github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+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  	}
   179  	return nil
   180  }
   181  
   182  type ChaincodeDefinition interface {
   183  	GetVersion() string
   184  	GetSequence() int64
   185  	GetEndorsementPlugin() string
   186  	GetValidationPlugin() string
   187  }
   188  
   189  func (c *CommittedQuerier) printSingleChaincodeDefinition(cd ChaincodeDefinition) {
   190  	fmt.Fprintf(c.Writer, "Version: %s, Sequence: %d, Endorsement Plugin: %s, Validation Plugin: %s", cd.GetVersion(), cd.GetSequence(), cd.GetEndorsementPlugin(), cd.GetValidationPlugin())
   191  }
   192  
   193  func (c *CommittedQuerier) printApprovals(qcdr *lb.QueryChaincodeDefinitionResult) {
   194  	orgs := []string{}
   195  	approved := qcdr.GetApprovals()
   196  	for org := range approved {
   197  		orgs = append(orgs, org)
   198  	}
   199  	sort.Strings(orgs)
   200  
   201  	approvals := ""
   202  	for _, org := range orgs {
   203  		approvals += fmt.Sprintf("%s: %t, ", org, approved[org])
   204  	}
   205  	approvals = strings.TrimSuffix(approvals, ", ")
   206  
   207  	fmt.Fprintf(c.Writer, ", Approvals: [%s]\n", approvals)
   208  }
   209  
   210  func (c *CommittedQuerier) validateInput() error {
   211  	if c.Input.ChannelID == "" {
   212  		return errors.New("channel name must be specified")
   213  	}
   214  
   215  	return nil
   216  }
   217  
   218  func (c *CommittedQuerier) createProposal() (*pb.Proposal, error) {
   219  	var function string
   220  	var args proto.Message
   221  
   222  	if c.Input.Name != "" {
   223  		function = "QueryChaincodeDefinition"
   224  		args = &lb.QueryChaincodeDefinitionArgs{
   225  			Name: c.Input.Name,
   226  		}
   227  	} else {
   228  		function = "QueryChaincodeDefinitions"
   229  		args = &lb.QueryChaincodeDefinitionsArgs{}
   230  	}
   231  
   232  	argsBytes, err := proto.Marshal(args)
   233  	if err != nil {
   234  		return nil, errors.Wrap(err, "failed to marshal args")
   235  	}
   236  	ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte(function), argsBytes}}
   237  
   238  	cis := &pb.ChaincodeInvocationSpec{
   239  		ChaincodeSpec: &pb.ChaincodeSpec{
   240  			ChaincodeId: &pb.ChaincodeID{Name: lifecycleName},
   241  			Input:       ccInput,
   242  		},
   243  	}
   244  
   245  	signerSerialized, err := c.Signer.Serialize()
   246  	if err != nil {
   247  		return nil, errors.WithMessage(err, "failed to serialize identity")
   248  	}
   249  
   250  	proposal, _, err := protoutil.CreateProposalFromCIS(cb.HeaderType_ENDORSER_TRANSACTION, c.Input.ChannelID, cis, signerSerialized)
   251  	if err != nil {
   252  		return nil, errors.WithMessage(err, "failed to create ChaincodeInvocationSpec proposal")
   253  	}
   254  
   255  	return proposal, nil
   256  }