github.com/anjalikarhana/fabric@v2.1.1+incompatible/internal/peer/lifecycle/chaincode/checkcommitreadiness.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/internal/pkg/identity"
    23  	"github.com/hyperledger/fabric/protoutil"
    24  	"github.com/pkg/errors"
    25  	"github.com/spf13/cobra"
    26  	"github.com/spf13/viper"
    27  )
    28  
    29  // CommitReadinessChecker holds the dependencies needed to
    30  // check whether a chaincode definition is ready to be committed
    31  // on a channel (i.e. has approvals from enough organizations to satisfy
    32  // the lifecycle endorsement policy) and receive the list of orgs that
    33  // have approved the definition.
    34  type CommitReadinessChecker struct {
    35  	Command        *cobra.Command
    36  	EndorserClient pb.EndorserClient
    37  	Input          *CommitReadinessCheckInput
    38  	Signer         identity.SignerSerializer
    39  	Writer         io.Writer
    40  }
    41  
    42  // CommitReadinessCheckInput holds all of the input parameters for checking
    43  // whether a chaincode definition is ready to be committed ValidationParameter
    44  // bytes is the (marshalled) endorsement policy when using the default
    45  // endorsement and validation plugins.
    46  type CommitReadinessCheckInput struct {
    47  	ChannelID                string
    48  	Name                     string
    49  	Version                  string
    50  	PackageID                string
    51  	Sequence                 int64
    52  	EndorsementPlugin        string
    53  	ValidationPlugin         string
    54  	ValidationParameterBytes []byte
    55  	CollectionConfigPackage  *pb.CollectionConfigPackage
    56  	InitRequired             bool
    57  	PeerAddresses            []string
    58  	TxID                     string
    59  	OutputFormat             string
    60  }
    61  
    62  // Validate the input for a CheckCommitReadiness proposal
    63  func (c *CommitReadinessCheckInput) Validate() error {
    64  	if c.ChannelID == "" {
    65  		return errors.New("The required parameter 'channelID' is empty. Rerun the command with -C flag")
    66  	}
    67  
    68  	if c.Name == "" {
    69  		return errors.New("The required parameter 'name' is empty. Rerun the command with -n flag")
    70  	}
    71  
    72  	if c.Version == "" {
    73  		return errors.New("The required parameter 'version' is empty. Rerun the command with -v flag")
    74  	}
    75  
    76  	if c.Sequence == 0 {
    77  		return errors.New("The required parameter 'sequence' is empty. Rerun the command with --sequence flag")
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  // CheckCommitReadinessCmd returns the cobra command for the
    84  // CheckCommitReadiness lifecycle operation
    85  func CheckCommitReadinessCmd(c *CommitReadinessChecker, cryptoProvider bccsp.BCCSP) *cobra.Command {
    86  	chaincodeCheckCommitReadinessCmd := &cobra.Command{
    87  		Use:   "checkcommitreadiness",
    88  		Short: fmt.Sprintf("Check whether a chaincode definition is ready to be committed on a channel."),
    89  		Long:  fmt.Sprintf("Check whether a chaincode definition is ready to be committed on a channel."),
    90  		RunE: func(cmd *cobra.Command, args []string) error {
    91  			if c == nil {
    92  				// set input from CLI flags
    93  				input, err := c.createInput()
    94  				if err != nil {
    95  					return err
    96  				}
    97  
    98  				ccInput := &ClientConnectionsInput{
    99  					CommandName:           cmd.Name(),
   100  					EndorserRequired:      true,
   101  					ChannelID:             channelID,
   102  					PeerAddresses:         peerAddresses,
   103  					TLSRootCertFiles:      tlsRootCertFiles,
   104  					ConnectionProfilePath: connectionProfilePath,
   105  					TLSEnabled:            viper.GetBool("peer.tls.enabled"),
   106  				}
   107  
   108  				cc, err := NewClientConnections(ccInput, cryptoProvider)
   109  				if err != nil {
   110  					return err
   111  				}
   112  
   113  				c = &CommitReadinessChecker{
   114  					Command:        cmd,
   115  					Input:          input,
   116  					EndorserClient: cc.EndorserClients[0],
   117  					Signer:         cc.Signer,
   118  					Writer:         os.Stdout,
   119  				}
   120  			}
   121  
   122  			return c.ReadinessCheck()
   123  		},
   124  	}
   125  	flagList := []string{
   126  		"channelID",
   127  		"name",
   128  		"version",
   129  		"sequence",
   130  		"endorsement-plugin",
   131  		"validation-plugin",
   132  		"signature-policy",
   133  		"channel-config-policy",
   134  		"init-required",
   135  		"collections-config",
   136  		"peerAddresses",
   137  		"tlsRootCertFiles",
   138  		"connectionProfile",
   139  		"output",
   140  	}
   141  	attachFlags(chaincodeCheckCommitReadinessCmd, flagList)
   142  
   143  	return chaincodeCheckCommitReadinessCmd
   144  }
   145  
   146  // ReadinessCheck submits a CheckCommitReadiness proposal
   147  // and prints the result.
   148  func (c *CommitReadinessChecker) ReadinessCheck() error {
   149  	err := c.Input.Validate()
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	if c.Command != nil {
   155  		// Parsing of the command line is done so silence cmd usage
   156  		c.Command.SilenceUsage = true
   157  	}
   158  
   159  	proposal, err := c.createProposal(c.Input.TxID)
   160  	if err != nil {
   161  		return errors.WithMessage(err, "failed to create proposal")
   162  	}
   163  
   164  	signedProposal, err := signProposal(proposal, c.Signer)
   165  	if err != nil {
   166  		return errors.WithMessage(err, "failed to create signed proposal")
   167  	}
   168  
   169  	// checkcommitreadiness currently only supports a single peer
   170  	proposalResponse, err := c.EndorserClient.ProcessProposal(context.Background(), signedProposal)
   171  	if err != nil {
   172  		return errors.WithMessage(err, "failed to endorse proposal")
   173  	}
   174  
   175  	if proposalResponse == nil {
   176  		return errors.New("received nil proposal response")
   177  	}
   178  
   179  	if proposalResponse.Response == nil {
   180  		return errors.New("received proposal response with nil response")
   181  	}
   182  
   183  	if proposalResponse.Response.Status != int32(cb.Status_SUCCESS) {
   184  		return errors.Errorf("query failed with status: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message)
   185  	}
   186  
   187  	if strings.ToLower(c.Input.OutputFormat) == "json" {
   188  		return printResponseAsJSON(proposalResponse, &lb.CheckCommitReadinessResult{}, c.Writer)
   189  	}
   190  	return c.printResponse(proposalResponse)
   191  }
   192  
   193  // printResponse prints the information included in the response
   194  // from the server as human readable plain-text.
   195  func (c *CommitReadinessChecker) printResponse(proposalResponse *pb.ProposalResponse) error {
   196  	result := &lb.CheckCommitReadinessResult{}
   197  	err := proto.Unmarshal(proposalResponse.Response.Payload, result)
   198  	if err != nil {
   199  		return errors.Wrap(err, "failed to unmarshal proposal response's response payload")
   200  	}
   201  
   202  	orgs := []string{}
   203  	for org := range result.Approvals {
   204  		orgs = append(orgs, org)
   205  	}
   206  	sort.Strings(orgs)
   207  
   208  	fmt.Fprintf(c.Writer, "Chaincode definition for chaincode '%s', version '%s', sequence '%d' on channel '%s' approval status by org:\n", c.Input.Name, c.Input.Version, c.Input.Sequence, c.Input.ChannelID)
   209  	for _, org := range orgs {
   210  		fmt.Fprintf(c.Writer, "%s: %t\n", org, result.Approvals[org])
   211  	}
   212  
   213  	return nil
   214  }
   215  
   216  // setInput creates the input struct based on the CLI flags
   217  func (c *CommitReadinessChecker) createInput() (*CommitReadinessCheckInput, error) {
   218  	policyBytes, err := createPolicyBytes(signaturePolicy, channelConfigPolicy)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	ccp, err := createCollectionConfigPackage(collectionsConfigFile)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  
   228  	input := &CommitReadinessCheckInput{
   229  		ChannelID:                channelID,
   230  		Name:                     chaincodeName,
   231  		Version:                  chaincodeVersion,
   232  		PackageID:                packageID,
   233  		Sequence:                 int64(sequence),
   234  		EndorsementPlugin:        endorsementPlugin,
   235  		ValidationPlugin:         validationPlugin,
   236  		ValidationParameterBytes: policyBytes,
   237  		InitRequired:             initRequired,
   238  		CollectionConfigPackage:  ccp,
   239  		PeerAddresses:            peerAddresses,
   240  		OutputFormat:             output,
   241  	}
   242  
   243  	return input, nil
   244  }
   245  
   246  func (c *CommitReadinessChecker) createProposal(inputTxID string) (*pb.Proposal, error) {
   247  	args := &lb.CheckCommitReadinessArgs{
   248  		Name:                c.Input.Name,
   249  		Version:             c.Input.Version,
   250  		Sequence:            c.Input.Sequence,
   251  		EndorsementPlugin:   c.Input.EndorsementPlugin,
   252  		ValidationPlugin:    c.Input.ValidationPlugin,
   253  		ValidationParameter: c.Input.ValidationParameterBytes,
   254  		InitRequired:        c.Input.InitRequired,
   255  		Collections:         c.Input.CollectionConfigPackage,
   256  	}
   257  
   258  	argsBytes, err := proto.Marshal(args)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte(checkCommitReadinessFuncName), argsBytes}}
   263  
   264  	cis := &pb.ChaincodeInvocationSpec{
   265  		ChaincodeSpec: &pb.ChaincodeSpec{
   266  			ChaincodeId: &pb.ChaincodeID{Name: lifecycleName},
   267  			Input:       ccInput,
   268  		},
   269  	}
   270  
   271  	creatorBytes, err := c.Signer.Serialize()
   272  	if err != nil {
   273  		return nil, errors.WithMessage(err, "failed to serialize identity")
   274  	}
   275  
   276  	proposal, _, err := protoutil.CreateChaincodeProposalWithTxIDAndTransient(cb.HeaderType_ENDORSER_TRANSACTION, c.Input.ChannelID, cis, creatorBytes, inputTxID, nil)
   277  	if err != nil {
   278  		return nil, errors.WithMessage(err, "failed to create ChaincodeInvocationSpec proposal")
   279  	}
   280  
   281  	return proposal, nil
   282  }