github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/internal/peer/lifecycle/chaincode/queryapproved.go (about)

     1  /*
     2  Copyright Hitachi America, Ltd. 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  	"strings"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	cb "github.com/hyperledger/fabric-protos-go/common"
    18  	pb "github.com/hyperledger/fabric-protos-go/peer"
    19  	lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle"
    20  	"github.com/osdi23p228/fabric/bccsp"
    21  	"github.com/osdi23p228/fabric/protoutil"
    22  	"github.com/pkg/errors"
    23  	"github.com/spf13/cobra"
    24  	"github.com/spf13/viper"
    25  )
    26  
    27  // ApprovedQuerier holds the dependencies needed to query
    28  // the approved chaincode definition for the organization
    29  type ApprovedQuerier struct {
    30  	Command        *cobra.Command
    31  	EndorserClient EndorserClient
    32  	Input          *ApprovedQueryInput
    33  	Signer         Signer
    34  	Writer         io.Writer
    35  }
    36  
    37  type ApprovedQueryInput struct {
    38  	ChannelID    string
    39  	Name         string
    40  	Sequence     int64
    41  	OutputFormat string
    42  }
    43  
    44  // QueryApprovedCmd returns the cobra command for
    45  // querying the approved chaincode definition for the organization
    46  func QueryApprovedCmd(a *ApprovedQuerier, cryptoProvider bccsp.BCCSP) *cobra.Command {
    47  	chaincodeQueryApprovedCmd := &cobra.Command{
    48  		Use:   "queryapproved",
    49  		Short: "Query an org's approved chaincode definition from its peer.",
    50  		Long:  "Query an organization's approved chaincode definition from its peer.",
    51  		RunE: func(cmd *cobra.Command, args []string) error {
    52  			if a == nil {
    53  				ccInput := &ClientConnectionsInput{
    54  					CommandName:           cmd.Name(),
    55  					EndorserRequired:      true,
    56  					ChannelID:             channelID,
    57  					PeerAddresses:         peerAddresses,
    58  					TLSRootCertFiles:      tlsRootCertFiles,
    59  					ConnectionProfilePath: connectionProfilePath,
    60  					TLSEnabled:            viper.GetBool("peer.tls.enabled"),
    61  				}
    62  
    63  				cc, err := NewClientConnections(ccInput, cryptoProvider)
    64  				if err != nil {
    65  					return err
    66  				}
    67  
    68  				aqInput := &ApprovedQueryInput{
    69  					ChannelID:    channelID,
    70  					Name:         chaincodeName,
    71  					Sequence:     int64(sequence),
    72  					OutputFormat: output,
    73  				}
    74  
    75  				a = &ApprovedQuerier{
    76  					Command:        cmd,
    77  					EndorserClient: cc.EndorserClients[0],
    78  					Input:          aqInput,
    79  					Signer:         cc.Signer,
    80  					Writer:         os.Stdout,
    81  				}
    82  			}
    83  			return a.Query()
    84  		},
    85  	}
    86  	flagList := []string{
    87  		"channelID",
    88  		"name",
    89  		"sequence",
    90  		"peerAddresses",
    91  		"tlsRootCertFiles",
    92  		"connectionProfile",
    93  		"output",
    94  	}
    95  	attachFlags(chaincodeQueryApprovedCmd, flagList)
    96  
    97  	return chaincodeQueryApprovedCmd
    98  }
    99  
   100  // Query returns the approved chaincode definition
   101  // for a given channel and chaincode name
   102  func (a *ApprovedQuerier) Query() error {
   103  	if a.Command != nil {
   104  		// Parsing of the command line is done so silence cmd usage
   105  		a.Command.SilenceUsage = true
   106  	}
   107  
   108  	err := a.validateInput()
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	proposal, err := a.createProposal()
   114  	if err != nil {
   115  		return errors.WithMessage(err, "failed to create proposal")
   116  	}
   117  
   118  	signedProposal, err := signProposal(proposal, a.Signer)
   119  	if err != nil {
   120  		return errors.WithMessage(err, "failed to create signed proposal")
   121  	}
   122  
   123  	proposalResponse, err := a.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(a.Input.OutputFormat) == "json" {
   141  		return printResponseAsJSON(proposalResponse, &lb.QueryApprovedChaincodeDefinitionResult{}, a.Writer)
   142  	}
   143  	return a.printResponse(proposalResponse)
   144  }
   145  
   146  // printResponse prints the information included in the response
   147  // from the server as human readable plain-text.
   148  func (a *ApprovedQuerier) printResponse(proposalResponse *pb.ProposalResponse) error {
   149  	result := &lb.QueryApprovedChaincodeDefinitionResult{}
   150  	err := proto.Unmarshal(proposalResponse.Response.Payload, result)
   151  	if err != nil {
   152  		return errors.Wrap(err, "failed to unmarshal proposal response's response payload")
   153  	}
   154  	fmt.Fprintf(a.Writer, "Approved chaincode definition for chaincode '%s' on channel '%s':\n", a.Input.Name, a.Input.ChannelID)
   155  
   156  	var packageID string
   157  	if result.Source != nil {
   158  		switch source := result.Source.Type.(type) {
   159  		case *lb.ChaincodeSource_LocalPackage:
   160  			packageID = source.LocalPackage.PackageId
   161  		case *lb.ChaincodeSource_Unavailable_:
   162  		}
   163  	}
   164  	fmt.Fprintf(a.Writer, "sequence: %d, version: %s, init-required: %t, package-id: %s, endorsement plugin: %s, validation plugin: %s\n",
   165  		result.Sequence, result.Version, result.InitRequired, packageID, result.EndorsementPlugin, result.ValidationPlugin)
   166  	return nil
   167  }
   168  
   169  func (a *ApprovedQuerier) validateInput() error {
   170  	if a.Input.ChannelID == "" {
   171  		return errors.New("The required parameter 'channelID' is empty. Rerun the command with -C flag")
   172  	}
   173  
   174  	if a.Input.Name == "" {
   175  		return errors.New("The required parameter 'name' is empty. Rerun the command with -n flag")
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  func (a *ApprovedQuerier) createProposal() (*pb.Proposal, error) {
   182  	var function string
   183  	var args proto.Message
   184  
   185  	function = "QueryApprovedChaincodeDefinition"
   186  	args = &lb.QueryApprovedChaincodeDefinitionArgs{
   187  		Name:     a.Input.Name,
   188  		Sequence: a.Input.Sequence,
   189  	}
   190  
   191  	argsBytes, err := proto.Marshal(args)
   192  	if err != nil {
   193  		return nil, errors.Wrap(err, "failed to marshal args")
   194  	}
   195  	ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte(function), argsBytes}}
   196  
   197  	cis := &pb.ChaincodeInvocationSpec{
   198  		ChaincodeSpec: &pb.ChaincodeSpec{
   199  			ChaincodeId: &pb.ChaincodeID{Name: lifecycleName},
   200  			Input:       ccInput,
   201  		},
   202  	}
   203  
   204  	signerSerialized, err := a.Signer.Serialize()
   205  	if err != nil {
   206  		return nil, errors.WithMessage(err, "failed to serialize identity")
   207  	}
   208  
   209  	proposal, _, err := protoutil.CreateProposalFromCIS(cb.HeaderType_ENDORSER_TRANSACTION, a.Input.ChannelID, cis, signerSerialized)
   210  	if err != nil {
   211  		return nil, errors.WithMessage(err, "failed to create ChaincodeInvocationSpec proposal")
   212  	}
   213  
   214  	return proposal, nil
   215  }