Skip to content

Commit

Permalink
[FAB-17434] Add queryapproved command to query the details of the a…
Browse files Browse the repository at this point in the history
…pproved chaincode definition (#1243)

Currently, Fabric peer does not provide a function to query the details of
approved chaincode definitions.

On the other hand, Fabric admins will need to confirm/use the information
after approval for their operations.

This patch adds `queryapproved` command for querying the details of
the chaincode definition approved by the organization itself to peer CLI,
so that Fabric peer can provide a function to query them.

Signed-off-by: Tatsuya Sato <Tatsuya.Sato@hal.hitachi.com>
  • Loading branch information
satota2 authored May 20, 2020
1 parent 48768be commit 4454188
Show file tree
Hide file tree
Showing 3 changed files with 529 additions and 2 deletions.
5 changes: 3 additions & 2 deletions internal/peer/lifecycle/chaincode/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func Cmd(cryptoProvider bccsp.BCCSP) *cobra.Command {
chaincodeCmd.AddCommand(QueryInstalledCmd(nil, cryptoProvider))
chaincodeCmd.AddCommand(GetInstalledPackageCmd(nil, cryptoProvider))
chaincodeCmd.AddCommand(ApproveForMyOrgCmd(nil, cryptoProvider))
chaincodeCmd.AddCommand(QueryApprovedCmd(nil, cryptoProvider))
chaincodeCmd.AddCommand(CheckCommitReadinessCmd(nil, cryptoProvider))
chaincodeCmd.AddCommand(CommitCmd(nil, cryptoProvider))
chaincodeCmd.AddCommand(QueryCommittedCmd(nil, cryptoProvider))
Expand Down Expand Up @@ -80,8 +81,8 @@ var (

var chaincodeCmd = &cobra.Command{
Use: "chaincode",
Short: "Perform chaincode operations: package|install|queryinstalled|getinstalledpackage|approveformyorg|checkcommitreadiness|commit|querycommitted",
Long: "Perform chaincode operations: package|install|queryinstalled|getinstalledpackage|approveformyorg|checkcommitreadiness|commit|querycommitted",
Short: "Perform chaincode operations: package|install|queryinstalled|getinstalledpackage|approveformyorg|queryapproved|checkcommitreadiness|commit|querycommitted",
Long: "Perform chaincode operations: package|install|queryinstalled|getinstalledpackage|approveformyorg|queryapproved|checkcommitreadiness|commit|querycommitted",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
common.InitCmd(cmd, args)
common.SetOrdererEnv(cmd, args)
Expand Down
215 changes: 215 additions & 0 deletions internal/peer/lifecycle/chaincode/queryapproved.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
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: fmt.Sprintf("Query an org's approved chaincode definition from its peer."),
Long: fmt.Sprintf("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
}
Loading

0 comments on commit 4454188

Please sign in to comment.