github.com/lzy4123/fabric@v2.1.1+incompatible/internal/peer/lifecycle/chaincode/querycommitted.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package chaincode 8 9 import ( 10 "context" 11 "fmt" 12 "io" 13 "os" 14 "sort" 15 "strings" 16 17 "github.com/golang/protobuf/proto" 18 cb "github.com/hyperledger/fabric-protos-go/common" 19 pb "github.com/hyperledger/fabric-protos-go/peer" 20 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 21 "github.com/hyperledger/fabric/bccsp" 22 "github.com/hyperledger/fabric/protoutil" 23 "github.com/pkg/errors" 24 "github.com/spf13/cobra" 25 "github.com/spf13/viper" 26 ) 27 28 // CommittedQuerier holds the dependencies needed to query 29 // the committed chaincode definitions 30 type CommittedQuerier struct { 31 Command *cobra.Command 32 Input *CommittedQueryInput 33 EndorserClient EndorserClient 34 Signer Signer 35 Writer io.Writer 36 } 37 38 type CommittedQueryInput struct { 39 ChannelID string 40 Name string 41 OutputFormat string 42 } 43 44 // QueryCommittedCmd returns the cobra command for 45 // querying a committed chaincode definition given 46 // the chaincode name 47 func QueryCommittedCmd(c *CommittedQuerier, cryptoProvider bccsp.BCCSP) *cobra.Command { 48 chaincodeQueryCommittedCmd := &cobra.Command{ 49 Use: "querycommitted", 50 Short: "Query the committed chaincode definitions by channel on a peer.", 51 Long: "Query the committed chaincode definitions by channel on a peer. Optional: provide a chaincode name to query a specific definition.", 52 RunE: func(cmd *cobra.Command, args []string) error { 53 if c == nil { 54 ccInput := &ClientConnectionsInput{ 55 CommandName: cmd.Name(), 56 EndorserRequired: true, 57 ChannelID: channelID, 58 PeerAddresses: peerAddresses, 59 TLSRootCertFiles: tlsRootCertFiles, 60 ConnectionProfilePath: connectionProfilePath, 61 TLSEnabled: viper.GetBool("peer.tls.enabled"), 62 } 63 64 cc, err := NewClientConnections(ccInput, cryptoProvider) 65 if err != nil { 66 return err 67 } 68 69 cqInput := &CommittedQueryInput{ 70 ChannelID: channelID, 71 Name: chaincodeName, 72 OutputFormat: output, 73 } 74 75 c = &CommittedQuerier{ 76 Command: cmd, 77 EndorserClient: cc.EndorserClients[0], 78 Input: cqInput, 79 Signer: cc.Signer, 80 Writer: os.Stdout, 81 } 82 } 83 return c.Query() 84 }, 85 } 86 87 flagList := []string{ 88 "channelID", 89 "name", 90 "peerAddresses", 91 "tlsRootCertFiles", 92 "connectionProfile", 93 "output", 94 } 95 attachFlags(chaincodeQueryCommittedCmd, flagList) 96 97 return chaincodeQueryCommittedCmd 98 } 99 100 // Query returns the committed chaincode definition 101 // for a given channel and chaincode name 102 func (c *CommittedQuerier) Query() error { 103 if c.Command != nil { 104 // Parsing of the command line is done so silence cmd usage 105 c.Command.SilenceUsage = true 106 } 107 108 err := c.validateInput() 109 if err != nil { 110 return err 111 } 112 113 proposal, err := c.createProposal() 114 if err != nil { 115 return errors.WithMessage(err, "failed to create proposal") 116 } 117 118 signedProposal, err := signProposal(proposal, c.Signer) 119 if err != nil { 120 return errors.WithMessage(err, "failed to create signed proposal") 121 } 122 123 proposalResponse, err := c.EndorserClient.ProcessProposal(context.Background(), signedProposal) 124 if err != nil { 125 return errors.WithMessage(err, "failed to endorse proposal") 126 } 127 128 if proposalResponse == nil { 129 return errors.New("received nil proposal response") 130 } 131 132 if proposalResponse.Response == nil { 133 return errors.New("received proposal response with nil response") 134 } 135 136 if proposalResponse.Response.Status != int32(cb.Status_SUCCESS) { 137 return errors.Errorf("query failed with status: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message) 138 } 139 140 if strings.ToLower(c.Input.OutputFormat) == "json" { 141 return c.printResponseAsJSON(proposalResponse) 142 } 143 return c.printResponse(proposalResponse) 144 } 145 146 func (c *CommittedQuerier) printResponseAsJSON(proposalResponse *pb.ProposalResponse) error { 147 if c.Input.Name != "" { 148 return printResponseAsJSON(proposalResponse, &lb.QueryChaincodeDefinitionResult{}, c.Writer) 149 } 150 return printResponseAsJSON(proposalResponse, &lb.QueryChaincodeDefinitionsResult{}, c.Writer) 151 } 152 153 // printResponse prints the information included in the response 154 // from the server as human readable plain-text. 155 func (c *CommittedQuerier) printResponse(proposalResponse *pb.ProposalResponse) error { 156 if c.Input.Name != "" { 157 result := &lb.QueryChaincodeDefinitionResult{} 158 err := proto.Unmarshal(proposalResponse.Response.Payload, result) 159 if err != nil { 160 return errors.Wrap(err, "failed to unmarshal proposal response's response payload") 161 } 162 fmt.Fprintf(c.Writer, "Committed chaincode definition for chaincode '%s' on channel '%s':\n", c.Input.Name, c.Input.ChannelID) 163 c.printSingleChaincodeDefinition(result) 164 c.printApprovals(result) 165 166 return nil 167 } 168 169 result := &lb.QueryChaincodeDefinitionsResult{} 170 err := proto.Unmarshal(proposalResponse.Response.Payload, result) 171 if err != nil { 172 return errors.Wrap(err, "failed to unmarshal proposal response's response payload") 173 } 174 fmt.Fprintf(c.Writer, "Committed chaincode definitions on channel '%s':\n", c.Input.ChannelID) 175 for _, cd := range result.ChaincodeDefinitions { 176 fmt.Fprintf(c.Writer, "Name: %s, ", cd.Name) 177 c.printSingleChaincodeDefinition(cd) 178 fmt.Fprintf(c.Writer, "\n") 179 } 180 return nil 181 } 182 183 type ChaincodeDefinition interface { 184 GetVersion() string 185 GetSequence() int64 186 GetEndorsementPlugin() string 187 GetValidationPlugin() string 188 } 189 190 func (c *CommittedQuerier) printSingleChaincodeDefinition(cd ChaincodeDefinition) { 191 fmt.Fprintf(c.Writer, "Version: %s, Sequence: %d, Endorsement Plugin: %s, Validation Plugin: %s", cd.GetVersion(), cd.GetSequence(), cd.GetEndorsementPlugin(), cd.GetValidationPlugin()) 192 } 193 194 func (c *CommittedQuerier) printApprovals(qcdr *lb.QueryChaincodeDefinitionResult) { 195 orgs := []string{} 196 approved := qcdr.GetApprovals() 197 for org := range approved { 198 orgs = append(orgs, org) 199 } 200 sort.Strings(orgs) 201 202 approvals := "" 203 for _, org := range orgs { 204 approvals += fmt.Sprintf("%s: %t, ", org, approved[org]) 205 } 206 approvals = strings.TrimSuffix(approvals, ", ") 207 208 fmt.Fprintf(c.Writer, ", Approvals: [%s]\n", approvals) 209 } 210 211 func (c *CommittedQuerier) validateInput() error { 212 if c.Input.ChannelID == "" { 213 return errors.New("channel name must be specified") 214 } 215 216 return nil 217 } 218 219 func (c *CommittedQuerier) createProposal() (*pb.Proposal, error) { 220 var function string 221 var args proto.Message 222 223 if c.Input.Name != "" { 224 function = "QueryChaincodeDefinition" 225 args = &lb.QueryChaincodeDefinitionArgs{ 226 Name: c.Input.Name, 227 } 228 } else { 229 function = "QueryChaincodeDefinitions" 230 args = &lb.QueryChaincodeDefinitionsArgs{} 231 } 232 233 argsBytes, err := proto.Marshal(args) 234 if err != nil { 235 return nil, errors.Wrap(err, "failed to marshal args") 236 } 237 ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte(function), argsBytes}} 238 239 cis := &pb.ChaincodeInvocationSpec{ 240 ChaincodeSpec: &pb.ChaincodeSpec{ 241 ChaincodeId: &pb.ChaincodeID{Name: lifecycleName}, 242 Input: ccInput, 243 }, 244 } 245 246 signerSerialized, err := c.Signer.Serialize() 247 if err != nil { 248 return nil, errors.WithMessage(err, "failed to serialize identity") 249 } 250 251 proposal, _, err := protoutil.CreateProposalFromCIS(cb.HeaderType_ENDORSER_TRANSACTION, c.Input.ChannelID, cis, signerSerialized) 252 if err != nil { 253 return nil, errors.WithMessage(err, "failed to create ChaincodeInvocationSpec proposal") 254 } 255 256 return proposal, nil 257 }