github.com/anjalikarhana/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  }