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