go_study/fabric-main/internal/peer/lifecycle/chaincode/queryapproved.go

216 lines
6.2 KiB
Go

/*
Copyright Hitachi America, Ltd. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package chaincode
import (
"context"
"fmt"
"io"
"os"
"strings"
"github.com/golang/protobuf/proto"
cb "github.com/hyperledger/fabric-protos-go/common"
pb "github.com/hyperledger/fabric-protos-go/peer"
lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// ApprovedQuerier holds the dependencies needed to query
// the approved chaincode definition for the organization
type ApprovedQuerier struct {
Command *cobra.Command
EndorserClient EndorserClient
Input *ApprovedQueryInput
Signer Signer
Writer io.Writer
}
type ApprovedQueryInput struct {
ChannelID string
Name string
Sequence int64
OutputFormat string
}
// QueryApprovedCmd returns the cobra command for
// querying the approved chaincode definition for the organization
func QueryApprovedCmd(a *ApprovedQuerier, cryptoProvider bccsp.BCCSP) *cobra.Command {
chaincodeQueryApprovedCmd := &cobra.Command{
Use: "queryapproved",
Short: "Query an org's approved chaincode definition from its peer.",
Long: "Query an organization's approved chaincode definition from its peer.",
RunE: func(cmd *cobra.Command, args []string) error {
if a == nil {
ccInput := &ClientConnectionsInput{
CommandName: cmd.Name(),
EndorserRequired: true,
ChannelID: channelID,
PeerAddresses: peerAddresses,
TLSRootCertFiles: tlsRootCertFiles,
ConnectionProfilePath: connectionProfilePath,
TLSEnabled: viper.GetBool("peer.tls.enabled"),
}
cc, err := NewClientConnections(ccInput, cryptoProvider)
if err != nil {
return err
}
aqInput := &ApprovedQueryInput{
ChannelID: channelID,
Name: chaincodeName,
Sequence: int64(sequence),
OutputFormat: output,
}
a = &ApprovedQuerier{
Command: cmd,
EndorserClient: cc.EndorserClients[0],
Input: aqInput,
Signer: cc.Signer,
Writer: os.Stdout,
}
}
return a.Query()
},
}
flagList := []string{
"channelID",
"name",
"sequence",
"peerAddresses",
"tlsRootCertFiles",
"connectionProfile",
"output",
}
attachFlags(chaincodeQueryApprovedCmd, flagList)
return chaincodeQueryApprovedCmd
}
// Query returns the approved chaincode definition
// for a given channel and chaincode name
func (a *ApprovedQuerier) Query() error {
if a.Command != nil {
// Parsing of the command line is done so silence cmd usage
a.Command.SilenceUsage = true
}
err := a.validateInput()
if err != nil {
return err
}
proposal, err := a.createProposal()
if err != nil {
return errors.WithMessage(err, "failed to create proposal")
}
signedProposal, err := signProposal(proposal, a.Signer)
if err != nil {
return errors.WithMessage(err, "failed to create signed proposal")
}
proposalResponse, err := a.EndorserClient.ProcessProposal(context.Background(), signedProposal)
if err != nil {
return errors.WithMessage(err, "failed to endorse proposal")
}
if proposalResponse == nil {
return errors.New("received nil proposal response")
}
if proposalResponse.Response == nil {
return errors.New("received proposal response with nil response")
}
if proposalResponse.Response.Status != int32(cb.Status_SUCCESS) {
return errors.Errorf("query failed with status: %d - %s", proposalResponse.Response.Status, proposalResponse.Response.Message)
}
if strings.ToLower(a.Input.OutputFormat) == "json" {
return printResponseAsJSON(proposalResponse, &lb.QueryApprovedChaincodeDefinitionResult{}, a.Writer)
}
return a.printResponse(proposalResponse)
}
// printResponse prints the information included in the response
// from the server as human readable plain-text.
func (a *ApprovedQuerier) printResponse(proposalResponse *pb.ProposalResponse) error {
result := &lb.QueryApprovedChaincodeDefinitionResult{}
err := proto.Unmarshal(proposalResponse.Response.Payload, result)
if err != nil {
return errors.Wrap(err, "failed to unmarshal proposal response's response payload")
}
fmt.Fprintf(a.Writer, "Approved chaincode definition for chaincode '%s' on channel '%s':\n", a.Input.Name, a.Input.ChannelID)
var packageID string
if result.Source != nil {
switch source := result.Source.Type.(type) {
case *lb.ChaincodeSource_LocalPackage:
packageID = source.LocalPackage.PackageId
case *lb.ChaincodeSource_Unavailable_:
}
}
fmt.Fprintf(a.Writer, "sequence: %d, version: %s, init-required: %t, package-id: %s, endorsement plugin: %s, validation plugin: %s\n",
result.Sequence, result.Version, result.InitRequired, packageID, result.EndorsementPlugin, result.ValidationPlugin)
return nil
}
func (a *ApprovedQuerier) validateInput() error {
if a.Input.ChannelID == "" {
return errors.New("The required parameter 'channelID' is empty. Rerun the command with -C flag")
}
if a.Input.Name == "" {
return errors.New("The required parameter 'name' is empty. Rerun the command with -n flag")
}
return nil
}
func (a *ApprovedQuerier) createProposal() (*pb.Proposal, error) {
var function string
var args proto.Message
function = "QueryApprovedChaincodeDefinition"
args = &lb.QueryApprovedChaincodeDefinitionArgs{
Name: a.Input.Name,
Sequence: a.Input.Sequence,
}
argsBytes, err := proto.Marshal(args)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal args")
}
ccInput := &pb.ChaincodeInput{Args: [][]byte{[]byte(function), argsBytes}}
cis := &pb.ChaincodeInvocationSpec{
ChaincodeSpec: &pb.ChaincodeSpec{
ChaincodeId: &pb.ChaincodeID{Name: lifecycleName},
Input: ccInput,
},
}
signerSerialized, err := a.Signer.Serialize()
if err != nil {
return nil, errors.WithMessage(err, "failed to serialize identity")
}
proposal, _, err := protoutil.CreateProposalFromCIS(cb.HeaderType_ENDORSER_TRANSACTION, a.Input.ChannelID, cis, signerSerialized)
if err != nil {
return nil, errors.WithMessage(err, "failed to create ChaincodeInvocationSpec proposal")
}
return proposal, nil
}