github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/peer/lifecycle/chaincode/approveformyorg.go (about)

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