github.com/lzy4123/fabric@v2.1.1+incompatible/internal/peer/lifecycle/chaincode/approveformyorg.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 "crypto/tls" 12 "fmt" 13 "time" 14 15 "github.com/golang/protobuf/proto" 16 cb "github.com/hyperledger/fabric-protos-go/common" 17 pb "github.com/hyperledger/fabric-protos-go/peer" 18 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 19 "github.com/hyperledger/fabric/bccsp" 20 "github.com/hyperledger/fabric/internal/peer/chaincode" 21 "github.com/hyperledger/fabric/internal/peer/common" 22 "github.com/hyperledger/fabric/protoutil" 23 "github.com/pkg/errors" 24 "github.com/spf13/cobra" 25 "github.com/spf13/viper" 26 ) 27 28 // ApproverForMyOrg holds the dependencies needed to approve 29 // a chaincode definition for an organization 30 type ApproverForMyOrg struct { 31 Certificate tls.Certificate 32 Command *cobra.Command 33 BroadcastClient common.BroadcastClient 34 DeliverClients []pb.DeliverClient 35 EndorserClients []EndorserClient 36 Input *ApproveForMyOrgInput 37 Signer Signer 38 } 39 40 // ApproveForMyOrgInput holds all of the input parameters for approving a 41 // chaincode definition for an organization. ValidationParameter bytes is 42 // the (marshalled) endorsement policy when using the default endorsement 43 // and validation plugins 44 type ApproveForMyOrgInput struct { 45 ChannelID string 46 Name string 47 Version string 48 PackageID string 49 Sequence int64 50 EndorsementPlugin string 51 ValidationPlugin string 52 ValidationParameterBytes []byte 53 CollectionConfigPackage *pb.CollectionConfigPackage 54 InitRequired bool 55 PeerAddresses []string 56 WaitForEvent bool 57 WaitForEventTimeout time.Duration 58 TxID string 59 } 60 61 // Validate the input for an ApproveChaincodeDefinitionForMyOrg proposal 62 func (a *ApproveForMyOrgInput) Validate() error { 63 if a.ChannelID == "" { 64 return errors.New("The required parameter 'channelID' is empty. Rerun the command with -C flag") 65 } 66 67 if a.Name == "" { 68 return errors.New("The required parameter 'name' is empty. Rerun the command with -n flag") 69 } 70 71 if a.Version == "" { 72 return errors.New("The required parameter 'version' is empty. Rerun the command with -v flag") 73 } 74 75 if a.Sequence == 0 { 76 return errors.New("The required parameter 'sequence' is empty. Rerun the command with --sequence flag") 77 } 78 79 return nil 80 } 81 82 // ApproveForMyOrgCmd returns the cobra command for chaincode ApproveForMyOrg 83 func ApproveForMyOrgCmd(a *ApproverForMyOrg, cryptoProvider bccsp.BCCSP) *cobra.Command { 84 chaincodeApproveForMyOrgCmd := &cobra.Command{ 85 Use: "approveformyorg", 86 Short: fmt.Sprintf("Approve the chaincode definition for my org."), 87 Long: fmt.Sprintf("Approve the chaincode definition for my organization."), 88 RunE: func(cmd *cobra.Command, args []string) error { 89 if a == nil { 90 // set input from CLI flags 91 input, err := a.createInput() 92 if err != nil { 93 return err 94 } 95 96 ccInput := &ClientConnectionsInput{ 97 CommandName: cmd.Name(), 98 EndorserRequired: true, 99 OrdererRequired: true, 100 ChannelID: channelID, 101 PeerAddresses: peerAddresses, 102 TLSRootCertFiles: tlsRootCertFiles, 103 ConnectionProfilePath: connectionProfilePath, 104 TLSEnabled: viper.GetBool("peer.tls.enabled"), 105 } 106 107 cc, err := NewClientConnections(ccInput, cryptoProvider) 108 if err != nil { 109 return err 110 } 111 112 endorserClients := make([]EndorserClient, len(cc.EndorserClients)) 113 for i, e := range cc.EndorserClients { 114 endorserClients[i] = e 115 } 116 117 a = &ApproverForMyOrg{ 118 Command: cmd, 119 Input: input, 120 Certificate: cc.Certificate, 121 BroadcastClient: cc.BroadcastClient, 122 DeliverClients: cc.DeliverClients, 123 EndorserClients: endorserClients, 124 Signer: cc.Signer, 125 } 126 } 127 return a.Approve() 128 }, 129 } 130 flagList := []string{ 131 "channelID", 132 "name", 133 "version", 134 "package-id", 135 "sequence", 136 "endorsement-plugin", 137 "validation-plugin", 138 "signature-policy", 139 "channel-config-policy", 140 "init-required", 141 "collections-config", 142 "peerAddresses", 143 "tlsRootCertFiles", 144 "connectionProfile", 145 "waitForEvent", 146 "waitForEventTimeout", 147 } 148 attachFlags(chaincodeApproveForMyOrgCmd, flagList) 149 150 return chaincodeApproveForMyOrgCmd 151 } 152 153 // Approve submits a ApproveChaincodeDefinitionForMyOrg 154 // proposal 155 func (a *ApproverForMyOrg) Approve() error { 156 err := a.Input.Validate() 157 if err != nil { 158 return err 159 } 160 161 if a.Command != nil { 162 // Parsing of the command line is done so silence cmd usage 163 a.Command.SilenceUsage = true 164 } 165 166 proposal, txID, err := a.createProposal(a.Input.TxID) 167 if err != nil { 168 return errors.WithMessage(err, "failed to create proposal") 169 } 170 171 signedProposal, err := signProposal(proposal, a.Signer) 172 if err != nil { 173 return errors.WithMessage(err, "failed to create signed proposal") 174 } 175 176 var responses []*pb.ProposalResponse 177 for _, endorser := range a.EndorserClients { 178 proposalResponse, err := endorser.ProcessProposal(context.Background(), signedProposal) 179 if err != nil { 180 return errors.WithMessage(err, "failed to endorse proposal") 181 } 182 responses = append(responses, proposalResponse) 183 } 184 185 if len(responses) == 0 { 186 // this should only be empty due to a programming bug 187 return errors.New("no proposal responses received") 188 } 189 190 // all responses will be checked when the signed transaction is created. 191 // for now, just set this so we check the first response's status 192 proposalResponse := responses[0] 193 194 if proposalResponse == nil { 195 return errors.New("received nil proposal response") 196 } 197 198 if proposalResponse.Response == nil { 199 return errors.Errorf("received proposal response with nil response") 200 } 201 202 if proposalResponse.Response.Status != int32(cb.Status_SUCCESS) { 203 return errors.Errorf("proposal failed with status: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message) 204 } 205 // assemble a signed transaction (it's an Envelope message) 206 env, err := protoutil.CreateSignedTx(proposal, a.Signer, responses...) 207 if err != nil { 208 return errors.WithMessage(err, "failed to create signed transaction") 209 } 210 var dg *chaincode.DeliverGroup 211 var ctx context.Context 212 if a.Input.WaitForEvent { 213 var cancelFunc context.CancelFunc 214 ctx, cancelFunc = context.WithTimeout(context.Background(), a.Input.WaitForEventTimeout) 215 defer cancelFunc() 216 217 dg = chaincode.NewDeliverGroup( 218 a.DeliverClients, 219 a.Input.PeerAddresses, 220 a.Signer, 221 a.Certificate, 222 a.Input.ChannelID, 223 txID, 224 ) 225 // connect to deliver service on all peers 226 err := dg.Connect(ctx) 227 if err != nil { 228 return err 229 } 230 } 231 232 if err = a.BroadcastClient.Send(env); err != nil { 233 return errors.WithMessage(err, "failed to send transaction") 234 } 235 236 if dg != nil && ctx != nil { 237 // wait for event that contains the txID from all peers 238 err = dg.Wait(ctx) 239 if err != nil { 240 return err 241 } 242 } 243 244 return err 245 } 246 247 // createInput creates the input struct based on the CLI flags 248 func (a *ApproverForMyOrg) createInput() (*ApproveForMyOrgInput, error) { 249 policyBytes, err := createPolicyBytes(signaturePolicy, channelConfigPolicy) 250 if err != nil { 251 return nil, err 252 } 253 254 ccp, err := createCollectionConfigPackage(collectionsConfigFile) 255 if err != nil { 256 return nil, err 257 } 258 259 input := &ApproveForMyOrgInput{ 260 ChannelID: channelID, 261 Name: chaincodeName, 262 Version: chaincodeVersion, 263 PackageID: packageID, 264 Sequence: int64(sequence), 265 EndorsementPlugin: endorsementPlugin, 266 ValidationPlugin: validationPlugin, 267 ValidationParameterBytes: policyBytes, 268 InitRequired: initRequired, 269 CollectionConfigPackage: ccp, 270 PeerAddresses: peerAddresses, 271 WaitForEvent: waitForEvent, 272 WaitForEventTimeout: waitForEventTimeout, 273 } 274 275 return input, nil 276 } 277 278 func (a *ApproverForMyOrg) createProposal(inputTxID string) (proposal *pb.Proposal, txID string, err error) { 279 if a.Signer == nil { 280 return nil, "", errors.New("nil signer provided") 281 } 282 283 var ccsrc *lb.ChaincodeSource 284 if a.Input.PackageID != "" { 285 ccsrc = &lb.ChaincodeSource{ 286 Type: &lb.ChaincodeSource_LocalPackage{ 287 LocalPackage: &lb.ChaincodeSource_Local{ 288 PackageId: a.Input.PackageID, 289 }, 290 }, 291 } 292 } else { 293 ccsrc = &lb.ChaincodeSource{ 294 Type: &lb.ChaincodeSource_Unavailable_{ 295 Unavailable: &lb.ChaincodeSource_Unavailable{}, 296 }, 297 } 298 } 299 300 args := &lb.ApproveChaincodeDefinitionForMyOrgArgs{ 301 Name: a.Input.Name, 302 Version: a.Input.Version, 303 Sequence: a.Input.Sequence, 304 EndorsementPlugin: a.Input.EndorsementPlugin, 305 ValidationPlugin: a.Input.ValidationPlugin, 306 ValidationParameter: a.Input.ValidationParameterBytes, 307 InitRequired: a.Input.InitRequired, 308 Collections: a.Input.CollectionConfigPackage, 309 Source: ccsrc, 310 } 311 312 argsBytes, err := proto.Marshal(args) 313 if err != nil { 314 return nil, "", err 315 } 316 ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte(approveFuncName), argsBytes}} 317 318 cis := &pb.ChaincodeInvocationSpec{ 319 ChaincodeSpec: &pb.ChaincodeSpec{ 320 ChaincodeId: &pb.ChaincodeID{Name: lifecycleName}, 321 Input: ccInput, 322 }, 323 } 324 325 creatorBytes, err := a.Signer.Serialize() 326 if err != nil { 327 return nil, "", errors.WithMessage(err, "failed to serialize identity") 328 } 329 330 proposal, txID, err = protoutil.CreateChaincodeProposalWithTxIDAndTransient(cb.HeaderType_ENDORSER_TRANSACTION, a.Input.ChannelID, cis, creatorBytes, inputTxID, nil) 331 if err != nil { 332 return nil, "", errors.WithMessage(err, "failed to create ChaincodeInvocationSpec proposal") 333 } 334 335 return proposal, txID, nil 336 }