-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FAB-17434] Add
queryapproved
command to query the details of the a…
…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
Showing
3 changed files
with
529 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.