diff --git a/peer/channel/channel.go b/peer/channel/channel.go index 277bb34a000..51685f028fb 100644 --- a/peer/channel/channel.go +++ b/peer/channel/channel.go @@ -34,8 +34,8 @@ import ( const ( channelFuncName = "channel" - shortDes = "Operate a channel: create|fetch|join|list|update." - longDes = "Operate a channel: create|fetch|join|list|update." + shortDes = "Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo." + longDes = "Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo." ) var logger = flogging.MustGetLogger("channelCmd") @@ -74,6 +74,7 @@ func Cmd(cf *ChannelCmdFactory) *cobra.Command { channelCmd.AddCommand(listCmd(cf)) channelCmd.AddCommand(updateCmd(cf)) channelCmd.AddCommand(signconfigtxCmd(cf)) + channelCmd.AddCommand(getinfoCmd(cf)) return channelCmd } diff --git a/peer/channel/getinfo.go b/peer/channel/getinfo.go new file mode 100644 index 00000000000..835e25b1b70 --- /dev/null +++ b/peer/channel/getinfo.go @@ -0,0 +1,113 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package channel + +import ( + "fmt" + + "github.com/pkg/errors" + + "encoding/json" + + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/core/scc/qscc" + "github.com/hyperledger/fabric/peer/common" + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + "github.com/hyperledger/fabric/protos/utils" + "github.com/spf13/cobra" + "golang.org/x/net/context" +) + +func getinfoCmd(cf *ChannelCmdFactory) *cobra.Command { + getinfoCmd := &cobra.Command{ + Use: "getinfo", + Short: "get blockchain information of a specified channel.", + Long: "get blockchain information of a specified channel. Requires '-c'.", + RunE: func(cmd *cobra.Command, args []string) error { + return getinfo(cf) + }, + } + flagList := []string{ + "channelID", + } + attachFlags(getinfoCmd, flagList) + + return getinfoCmd +} +func (cc *endorserClient) getBlockChainInfo() (*cb.BlockchainInfo, error) { + var err error + + invocation := &pb.ChaincodeInvocationSpec{ + ChaincodeSpec: &pb.ChaincodeSpec{ + Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), + ChaincodeId: &pb.ChaincodeID{Name: "qscc"}, + Input: &pb.ChaincodeInput{Args: [][]byte{[]byte(qscc.GetChainInfo), []byte(chainID)}}, + }, + } + + var prop *pb.Proposal + c, _ := cc.cf.Signer.Serialize() + prop, _, err = utils.CreateProposalFromCIS(cb.HeaderType_ENDORSER_TRANSACTION, "", invocation, c) + if err != nil { + return nil, errors.WithMessage(err, "cannot create proposal") + } + + var signedProp *pb.SignedProposal + signedProp, err = utils.GetSignedProposal(prop, cc.cf.Signer) + if err != nil { + return nil, errors.WithMessage(err, "cannot create signed proposal") + } + + proposalResp, err := cc.cf.EndorserClient.ProcessProposal(context.Background(), signedProp) + if err != nil { + return nil, errors.WithMessage(err, "failed sending proposal") + } + + if proposalResp.Response == nil || proposalResp.Response.Status != 200 { + return nil, errors.Errorf("received bad response, status %s", proposalResp.Response.Status) + } + + blockChainInfo := &cb.BlockchainInfo{} + err = proto.Unmarshal(proposalResp.Response.Payload, blockChainInfo) + if err != nil { + return nil, errors.Wrap(err, "cannot read qscc response") + } + + return blockChainInfo, nil + +} + +func getinfo(cf *ChannelCmdFactory) error { + //the global chainID filled by the "-c" command + if chainID == common.UndefinedParamValue { + return errors.New("Must supply channel ID") + } + + var err error + if cf == nil { + cf, err = InitCmdFactory(EndorserRequired, OrdererNotRequired) + if err != nil { + return err + } + } + + client := &endorserClient{cf} + + blockChainInfo, err := client.getBlockChainInfo() + if err != nil { + return err + } + jsonBytes, err := json.Marshal(blockChainInfo) + if err != nil { + return err + } + + fmt.Printf("Blockchain info: %s\n", string(jsonBytes)) + + return nil +} diff --git a/peer/channel/getinfo_test.go b/peer/channel/getinfo_test.go new file mode 100644 index 00000000000..20e8896f5a2 --- /dev/null +++ b/peer/channel/getinfo_test.go @@ -0,0 +1,77 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package channel + +import ( + "testing" + + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/peer/common" + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + "github.com/stretchr/testify/assert" +) + +func TestGetChannelInfo(t *testing.T) { + InitMSP() + resetFlags() + + mockBlockchainInfo := &cb.BlockchainInfo{ + Height: 1, + CurrentBlockHash: []byte("CurrentBlockHash"), + PreviousBlockHash: []byte("PreviousBlockHash"), + } + mockPayload, err := proto.Marshal(mockBlockchainInfo) + assert.NoError(t, err) + + mockResponse := &pb.ProposalResponse{ + Response: &pb.Response{ + Status: 200, + Payload: mockPayload, + }, + Endorsement: &pb.Endorsement{}, + } + + signer, err := common.GetDefaultSigner() + assert.NoError(t, err) + + mockCF := &ChannelCmdFactory{ + EndorserClient: common.GetMockEndorserClient(mockResponse, nil), + BroadcastFactory: mockBroadcastClientFactory, + Signer: signer, + } + + cmd := getinfoCmd(mockCF) + AddFlags(cmd) + + args := []string{"-c", mockChannel} + cmd.SetArgs(args) + + assert.NoError(t, cmd.Execute()) +} + +func TestGetChannelInfoMissingChannelID(t *testing.T) { + InitMSP() + resetFlags() + + signer, err := common.GetDefaultSigner() + if err != nil { + t.Fatalf("Get default signer error: %v", err) + } + + mockCF := &ChannelCmdFactory{ + Signer: signer, + } + + cmd := getinfoCmd(mockCF) + + AddFlags(cmd) + + cmd.SetArgs([]string{}) + + assert.Error(t, cmd.Execute()) +}