From eb048ef93c1df0c88555ec3406849edc31f7a809 Mon Sep 17 00:00:00 2001 From: jiangyaoguo Date: Thu, 1 Dec 2016 15:26:17 +0800 Subject: [PATCH] [FAB-1237] chaincode upgrade cli This commit can be divided into two parts. One implements chaincode upgrade cli. Another is a refactor of chaincode cmd package. The main idea is extract endorserClient/broadcastClient/signer to be holded by ChaincodeCmdFactory, then we can get clients needed from ChaincodeCmdFactory in cmd implements. Thus in unit-tests, we can construct mock clients for tests. If this refactor looks good, we can extend it to other cmd tests. Change-Id: I1a44e347009fc645b2e97cd6d65efde5fb229257 Signed-off-by: jiangyaoguo --- core/chaincode/exectransaction_test.go | 2 +- peer/chaincode/chaincode.go | 17 +- peer/chaincode/common.go | 59 +++--- peer/chaincode/deploy.go | 65 ++++--- peer/chaincode/invoke.go | 38 ++-- peer/chaincode/query.go | 38 ++-- peer/chaincode/upgrade.go | 122 +++++++++++++ peer/chaincode/upgrade_test.go | 170 ++++++++++++++++++ peer/common/common.go | 16 +- peer/common/mockclient.go | 58 ++++++ .../ordererclient.go} | 74 ++++---- peer/main.go | 2 +- protos/utils/proputils.go | 22 ++- 13 files changed, 554 insertions(+), 129 deletions(-) create mode 100644 peer/chaincode/upgrade.go create mode 100644 peer/chaincode/upgrade_test.go create mode 100644 peer/common/mockclient.go rename peer/{chaincode/noopsordererclient.go => common/ordererclient.go} (60%) diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index 45b8eaf82c2..13813f97925 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -136,7 +136,7 @@ func endTxSimulationCDS(chainID string, txid string, txsim ledger.TxSimulator, p return err } // get a proposal - we need it to get a transaction - prop, err := putils.CreateProposalFromCDS(txid, chainID, cds, ss) + prop, err := putils.CreateDeployProposalFromCDS(txid, chainID, cds, ss) if err != nil { return err } diff --git a/peer/chaincode/chaincode.go b/peer/chaincode/chaincode.go index 1629eaaedf8..3814b11305b 100755 --- a/peer/chaincode/chaincode.go +++ b/peer/chaincode/chaincode.go @@ -31,9 +31,8 @@ const ( var logger = logging.MustGetLogger("chaincodeCmd") -// Cmd returns the cobra command for Chaincode -func Cmd() *cobra.Command { - flags := chaincodeCmd.PersistentFlags() +func AddFlags(cmd *cobra.Command) { + flags := cmd.PersistentFlags() flags.StringVarP(&chaincodeLang, "lang", "l", "golang", fmt.Sprintf("Language the %s is written in", chainFuncName)) @@ -51,10 +50,16 @@ func Cmd() *cobra.Command { fmt.Sprint("Name of a custom ID generation algorithm (hashing and decoding) e.g. sha256base64")) flags.StringVarP(&chainID, "chainID", "C", util.GetTestChainID(), fmt.Sprint("The chain on which this command should be executed")) +} + +// Cmd returns the cobra command for Chaincode +func Cmd(cf *ChaincodeCmdFactory) *cobra.Command { + AddFlags(chaincodeCmd) - chaincodeCmd.AddCommand(deployCmd()) - chaincodeCmd.AddCommand(invokeCmd()) - chaincodeCmd.AddCommand(queryCmd()) + chaincodeCmd.AddCommand(deployCmd(cf)) + chaincodeCmd.AddCommand(invokeCmd(cf)) + chaincodeCmd.AddCommand(queryCmd(cf)) + chaincodeCmd.AddCommand(upgradeCmd(cf)) return chaincodeCmd } diff --git a/peer/chaincode/common.go b/peer/chaincode/common.go index 0ae36772156..08c0ed008e3 100755 --- a/peer/chaincode/common.go +++ b/peer/chaincode/common.go @@ -32,7 +32,6 @@ import ( "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/peer/common" "github.com/hyperledger/fabric/peer/util" - protcommon "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" putils "github.com/hyperledger/fabric/protos/utils" "github.com/spf13/cobra" @@ -155,7 +154,7 @@ func getChaincodeSpecification(cmd *cobra.Command) (*pb.ChaincodeSpec, error) { // // NOTE - Query will likely go away as all interactions with the endorser are // Proposal and ProposalResponses -func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err error) { +func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool, cf *ChaincodeCmdFactory) (err error) { spec, err := getChaincodeSpecification(cmd) if err != nil { return err @@ -167,19 +166,9 @@ func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err invocation.IdGenerationAlg = customIDGenAlg } - endorserClient, err := common.GetEndorserClient(cmd) + creator, err := cf.Signer.Serialize() if err != nil { - return fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err) - } - - signer, err := msp.GetLocalMSP().GetDefaultSigningIdentity() - if err != nil { - return fmt.Errorf("Error obtaining the default signing identity, err %s", err) - } - - creator, err := signer.Serialize() - if err != nil { - return fmt.Errorf("Error serializing the default signing identity, err %s", err) + return fmt.Errorf("Error serializing identity for %s: %s\n", cf.Signer.Identifier(), err) } uuid := cutil.GenerateUUID() @@ -191,13 +180,13 @@ func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err } var signedProp *pb.SignedProposal - signedProp, err = putils.GetSignedProposal(prop, signer) + signedProp, err = putils.GetSignedProposal(prop, cf.Signer) if err != nil { return fmt.Errorf("Error creating signed proposal %s: %s\n", chainFuncName, err) } var proposalResp *pb.ProposalResponse - proposalResp, err = endorserClient.ProcessProposal(context.Background(), signedProp) + proposalResp, err = cf.EndorserClient.ProcessProposal(context.Background(), signedProp) if err != nil { return fmt.Errorf("Error endorsing %s: %s\n", chainFuncName, err) } @@ -205,13 +194,13 @@ func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err if invoke { if proposalResp != nil { // assemble a signed transaction (it's an Envelope message) - env, err := putils.CreateSignedTx(prop, signer, proposalResp) + env, err := putils.CreateSignedTx(prop, cf.Signer, proposalResp) if err != nil { return fmt.Errorf("Could not assemble transaction, err %s", err) } // send the envelope for ordering - if err = sendTransaction(env); err != nil { + if err = cf.BroadcastClient.Send(env); err != nil { return fmt.Errorf("Error sending transaction %s: %s\n", chainFuncName, err) } } @@ -241,7 +230,6 @@ func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool) (err } func checkChaincodeCmdParams(cmd *cobra.Command) error { - //we need chaincode name for everything, including deploy if chaincodeName == common.UndefinedParamValue { return fmt.Errorf("Must supply value for %s name parameter.\n", chainFuncName) @@ -283,16 +271,33 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error { return nil } -//sendTransactions sends a serialize Envelop to the orderer -func sendTransaction(env *protcommon.Envelope) error { - var orderer string - if viper.GetBool("peer.committer.enabled") { - orderer = viper.GetString("peer.committer.ledger.orderer") +// ChaincodeCmdFactory holds the clients used by ChaincodeCmd +type ChaincodeCmdFactory struct { + EndorserClient pb.EndorserClient + Signer msp.SigningIdentity + BroadcastClient common.BroadcastClient +} + +// InitCmdFactory init the ChaincodeCmdFactory with default clients +func InitCmdFactory() (*ChaincodeCmdFactory, error) { + endorserClient, err := common.GetEndorserClient() + if err != nil { + return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err) } - if orderer == "" { - return nil + signer, err := common.GetDefaultSigner() + if err != nil { + return nil, fmt.Errorf("Error getting default signer: %s", err) + } + + broadcastClient, err := common.GetBroadcastClient() + if err != nil { + return nil, fmt.Errorf("Error getting broadcast client: %s", err) } - return Send(orderer, env) + return &ChaincodeCmdFactory{ + EndorserClient: endorserClient, + Signer: signer, + BroadcastClient: broadcastClient, + }, nil } diff --git a/peer/chaincode/deploy.go b/peer/chaincode/deploy.go index ff30af96e60..124931461ba 100755 --- a/peer/chaincode/deploy.go +++ b/peer/chaincode/deploy.go @@ -22,31 +22,31 @@ import ( "golang.org/x/net/context" "github.com/hyperledger/fabric/core/util" - "github.com/hyperledger/fabric/msp" - "github.com/hyperledger/fabric/peer/common" protcommon "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/spf13/cobra" ) -// Cmd returns the cobra command for Chaincode Deploy -func deployCmd() *cobra.Command { - return chaincodeDeployCmd -} +var chaincodeDeployCmd *cobra.Command + +// deployCmd returns the cobra command for Chaincode Deploy +func deployCmd(cf *ChaincodeCmdFactory) *cobra.Command { + chaincodeDeployCmd = &cobra.Command{ + Use: "deploy", + Short: fmt.Sprintf("Deploy the specified chaincode to the network."), + Long: fmt.Sprintf(`Deploy the specified chaincode to the network.`), + ValidArgs: []string{"1"}, + RunE: func(cmd *cobra.Command, args []string) error { + return chaincodeDeploy(cmd, args, cf) + }, + } -var chaincodeDeployCmd = &cobra.Command{ - Use: "deploy", - Short: fmt.Sprintf("Deploy the specified chaincode to the network."), - Long: fmt.Sprintf(`Deploy the specified chaincode to the network.`), - ValidArgs: []string{"1"}, - RunE: func(cmd *cobra.Command, args []string) error { - return chaincodeDeploy(cmd, args) - }, + return chaincodeDeployCmd } //deploy the command via Endorser -func deploy(cmd *cobra.Command) (*protcommon.Envelope, error) { +func deploy(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, error) { spec, err := getChaincodeSpecification(cmd) if err != nil { return nil, err @@ -57,41 +57,32 @@ func deploy(cmd *cobra.Command) (*protcommon.Envelope, error) { return nil, fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err) } - endorserClient, err := common.GetEndorserClient(cmd) - if err != nil { - return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err) - } - signer, err := msp.GetLocalMSP().GetDefaultSigningIdentity() - if err != nil { - return nil, fmt.Errorf("Error obtaining the default signing identity, err %s", err) - } - - creator, err := signer.Serialize() + creator, err := cf.Signer.Serialize() if err != nil { - return nil, fmt.Errorf("Error serializing the default signing identity, err %s", err) + return nil, fmt.Errorf("Error serializing identity for %s: %s\n", cf.Signer.Identifier(), err) } uuid := util.GenerateUUID() - prop, err := utils.CreateProposalFromCDS(uuid, chainID, cds, creator) + prop, err := utils.CreateDeployProposalFromCDS(uuid, chainID, cds, creator) if err != nil { return nil, fmt.Errorf("Error creating proposal %s: %s\n", chainFuncName, err) } var signedProp *pb.SignedProposal - signedProp, err = utils.GetSignedProposal(prop, signer) + signedProp, err = utils.GetSignedProposal(prop, cf.Signer) if err != nil { return nil, fmt.Errorf("Error creating signed proposal %s: %s\n", chainFuncName, err) } - proposalResponse, err := endorserClient.ProcessProposal(context.Background(), signedProp) + proposalResponse, err := cf.EndorserClient.ProcessProposal(context.Background(), signedProp) if err != nil { return nil, fmt.Errorf("Error endorsing %s: %s\n", chainFuncName, err) } if proposalResponse != nil { // assemble a signed transaction (it's an Envelope message) - env, err := utils.CreateSignedTx(prop, signer, proposalResponse) + env, err := utils.CreateSignedTx(prop, cf.Signer, proposalResponse) if err != nil { return nil, fmt.Errorf("Could not assemble transaction, err %s", err) } @@ -105,14 +96,22 @@ func deploy(cmd *cobra.Command) (*protcommon.Envelope, error) { // chaincodeDeploy deploys the chaincode. On success, the chaincode name // (hash) is printed to STDOUT for use by subsequent chaincode-related CLI // commands. -func chaincodeDeploy(cmd *cobra.Command, args []string) error { - env, err := deploy(cmd) +func chaincodeDeploy(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error { + var err error + if cf == nil { + cf, err = InitCmdFactory() + if err != nil { + return err + } + } + defer cf.BroadcastClient.Close() + env, err := deploy(cmd, cf) if err != nil { return err } if env != nil { - err = sendTransaction(env) + err = cf.BroadcastClient.Send(env) } return err diff --git a/peer/chaincode/invoke.go b/peer/chaincode/invoke.go index 7334a40e28b..1e2c6ad0d5e 100755 --- a/peer/chaincode/invoke.go +++ b/peer/chaincode/invoke.go @@ -22,20 +22,32 @@ import ( "github.com/spf13/cobra" ) -func invokeCmd() *cobra.Command { - return chaincodeInvokeCmd -} +var chaincodeInvokeCmd *cobra.Command + +// invokeCmd returns the cobra command for Chaincode Invoke +func invokeCmd(cf *ChaincodeCmdFactory) *cobra.Command { + chaincodeInvokeCmd = &cobra.Command{ + Use: "invoke", + Short: fmt.Sprintf("Invoke the specified %s.", chainFuncName), + Long: fmt.Sprintf(`Invoke the specified %s.`, chainFuncName), + ValidArgs: []string{"1"}, + RunE: func(cmd *cobra.Command, args []string) error { + return chaincodeInvoke(cmd, args, cf) + }, + } -var chaincodeInvokeCmd = &cobra.Command{ - Use: "invoke", - Short: fmt.Sprintf("Invoke the specified %s.", chainFuncName), - Long: fmt.Sprintf(`Invoke the specified %s.`, chainFuncName), - ValidArgs: []string{"1"}, - RunE: func(cmd *cobra.Command, args []string) error { - return chaincodeInvoke(cmd, args) - }, + return chaincodeInvokeCmd } -func chaincodeInvoke(cmd *cobra.Command, args []string) error { - return chaincodeInvokeOrQuery(cmd, args, true) +func chaincodeInvoke(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error { + var err error + if cf == nil { + cf, err = InitCmdFactory() + if err != nil { + return err + } + } + defer cf.BroadcastClient.Close() + + return chaincodeInvokeOrQuery(cmd, args, true, cf) } diff --git a/peer/chaincode/query.go b/peer/chaincode/query.go index 4bcb9db08b1..47ab272d329 100755 --- a/peer/chaincode/query.go +++ b/peer/chaincode/query.go @@ -22,7 +22,20 @@ import ( "github.com/spf13/cobra" ) -func queryCmd() *cobra.Command { +var chaincodeQueryCmd *cobra.Command + +// queryCmd returns the cobra command for Chaincode Query +func queryCmd(cf *ChaincodeCmdFactory) *cobra.Command { + chaincodeQueryCmd = &cobra.Command{ + Use: "query", + Short: fmt.Sprintf("Query using the specified %s.", chainFuncName), + Long: fmt.Sprintf(`Query using the specified %s.`, chainFuncName), + ValidArgs: []string{"1"}, + RunE: func(cmd *cobra.Command, args []string) error { + return chaincodeQuery(cmd, args, cf) + }, + } + chaincodeQueryCmd.Flags().BoolVarP(&chaincodeQueryRaw, "raw", "r", false, "If true, output the query value as raw bytes, otherwise format as a printable string") chaincodeQueryCmd.Flags().BoolVarP(&chaincodeQueryHex, "hex", "x", false, @@ -31,16 +44,15 @@ func queryCmd() *cobra.Command { return chaincodeQueryCmd } -var chaincodeQueryCmd = &cobra.Command{ - Use: "query", - Short: fmt.Sprintf("Query using the specified %s.", chainFuncName), - Long: fmt.Sprintf(`Query using the specified %s.`, chainFuncName), - ValidArgs: []string{"1"}, - RunE: func(cmd *cobra.Command, args []string) error { - return chaincodeQuery(cmd, args) - }, -} - -func chaincodeQuery(cmd *cobra.Command, args []string) error { - return chaincodeInvokeOrQuery(cmd, args, false) +func chaincodeQuery(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error { + var err error + if cf == nil { + cf, err = InitCmdFactory() + if err != nil { + return err + } + } + defer cf.BroadcastClient.Close() + + return chaincodeInvokeOrQuery(cmd, args, false, cf) } diff --git a/peer/chaincode/upgrade.go b/peer/chaincode/upgrade.go new file mode 100644 index 00000000000..9b213e7576f --- /dev/null +++ b/peer/chaincode/upgrade.go @@ -0,0 +1,122 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package chaincode + +import ( + "fmt" + + "golang.org/x/net/context" + + "github.com/hyperledger/fabric/core/util" + protcommon "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + "github.com/hyperledger/fabric/protos/utils" + "github.com/spf13/cobra" +) + +var chaincodeUpgradeCmd *cobra.Command + +// upgradeCmd returns the cobra command for Chaincode Upgrade +func upgradeCmd(cf *ChaincodeCmdFactory) *cobra.Command { + chaincodeUpgradeCmd = &cobra.Command{ + Use: "upgrade", + Short: fmt.Sprintf("Upgrade chaincode."), + Long: fmt.Sprintf(`Upgrade an existing chaincode with the specified one. The new chaincode will immediately replace the existing chaincode upon the transaction committed.`), + ValidArgs: []string{"1"}, + RunE: func(cmd *cobra.Command, args []string) error { + return chaincodeUpgrade(cmd, args, cf) + }, + } + + return chaincodeUpgradeCmd +} + +//upgrade the command via Endorser +func upgrade(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, error) { + spec, err := getChaincodeSpecification(cmd) + if err != nil { + return nil, err + } + + cds, err := getChaincodeBytes(spec) + if err != nil { + return nil, fmt.Errorf("Error getting chaincode code %s: %s", chainFuncName, err) + } + + creator, err := cf.Signer.Serialize() + if err != nil { + return nil, fmt.Errorf("Error serializing identity for %s: %s\n", cf.Signer.Identifier(), err) + } + + uuid := util.GenerateUUID() + + prop, err := utils.CreateUpgradeProposalFromCDS(uuid, chainID, cds, creator) + if err != nil { + return nil, fmt.Errorf("Error creating proposal %s: %s\n", chainFuncName, err) + } + logger.Debugf("Get upgrade proposal for chaincode <%v>", spec.ChaincodeID) + + var signedProp *pb.SignedProposal + signedProp, err = utils.GetSignedProposal(prop, cf.Signer) + if err != nil { + return nil, fmt.Errorf("Error creating signed proposal %s: %s\n", chainFuncName, err) + } + + proposalResponse, err := cf.EndorserClient.ProcessProposal(context.Background(), signedProp) + if err != nil { + return nil, fmt.Errorf("Error endorsing %s: %s\n", chainFuncName, err) + } + logger.Debugf("endorse upgrade proposal, get response <%v>", proposalResponse.Response) + + if proposalResponse != nil { + // assemble a signed transaction (it's an Envelope message) + env, err := utils.CreateSignedTx(prop, cf.Signer, proposalResponse) + if err != nil { + return nil, fmt.Errorf("Could not assemble transaction, err %s", err) + } + logger.Debug("Get Signed envelope") + return env, nil + } + + return nil, nil +} + +// chaincodeUpgrade upgrades the chaincode. On success, the new chaincode +// version is printed to STDOUT +func chaincodeUpgrade(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error { + var err error + if cf == nil { + cf, err = InitCmdFactory() + if err != nil { + return err + } + } + defer cf.BroadcastClient.Close() + + env, err := upgrade(cmd, cf) + if err != nil { + return err + } + + if env != nil { + logger.Debug("Send signed envelope to orderer") + err = cf.BroadcastClient.Send(env) + return err + } + + return nil +} diff --git a/peer/chaincode/upgrade_test.go b/peer/chaincode/upgrade_test.go new file mode 100644 index 00000000000..6883fd70af6 --- /dev/null +++ b/peer/chaincode/upgrade_test.go @@ -0,0 +1,170 @@ +/* + Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package chaincode + +import ( + "fmt" + "os" + "sync" + "testing" + + "github.com/hyperledger/fabric/core/config" + "github.com/hyperledger/fabric/peer/common" + pb "github.com/hyperledger/fabric/protos/peer" + // "github.com/hyperledger/fabric/protos/utils" + "github.com/hyperledger/fabric/core/crypto/primitives" +) + +var once sync.Once + +// InitMSP init MSP +func InitMSP() { + once.Do(initMSP) +} + +func initMSP() { + // TODO: determine the location of this config file + var alternativeCfgPath = os.Getenv("PEER_CFG_PATH") + var mspMgrConfigFile string + if alternativeCfgPath != "" { + mspMgrConfigFile = alternativeCfgPath + "/msp/peer-config.json" + } else if _, err := os.Stat("./peer-config.json"); err == nil { + mspMgrConfigFile = "./peer-config.json" + } else { + mspMgrConfigFile = os.Getenv("GOPATH") + "/src/github.com/hyperledger/fabric/msp/peer-config.json" + } + + err := config.SetupFakeMSPInfrastructureForTests(mspMgrConfigFile) + if err != nil { + panic(fmt.Errorf("Fatal error when reading MSP config file %s: err %s\n", mspMgrConfigFile, err)) + } +} + +func TestUpgradeCmd(t *testing.T) { + primitives.SetSecurityLevel("SHA2", 256) + InitMSP() + + signer, err := common.GetDefaultSigner() + if err != nil { + t.Fatalf("Get default signer error: %v", err) + } + + mockResponse := &pb.ProposalResponse{ + Response: &pb.Response{Status: 200}, + Endorsement: &pb.Endorsement{}, + } + + mockEndorerClient := common.GetMockEndorserClient(mockResponse, nil) + + mockBroadcastClient := common.GetMockBroadcastClient(nil) + + mockCF := &ChaincodeCmdFactory{ + EndorserClient: mockEndorerClient, + Signer: signer, + BroadcastClient: mockBroadcastClient, + } + + cmd := upgradeCmd(mockCF) + AddFlags(cmd) + + args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"} + cmd.SetArgs(args) + + if err := cmd.Execute(); err != nil { + t.Errorf("Run chaincode upgrade cmd error:%v", err) + } +} + +func TestUpgradeCmdEndorseFail(t *testing.T) { + primitives.SetSecurityLevel("SHA2", 256) + InitMSP() + + signer, err := common.GetDefaultSigner() + if err != nil { + t.Fatalf("Get default signer error: %v", err) + } + + errCode := int32(500) + errMsg := "upgrade error" + mockResponse := &pb.ProposalResponse{Response: &pb.Response{Status: errCode, Message: errMsg}} + + mockEndorerClient := common.GetMockEndorserClient(mockResponse, nil) + + mockBroadcastClient := common.GetMockBroadcastClient(nil) + + mockCF := &ChaincodeCmdFactory{ + EndorserClient: mockEndorerClient, + Signer: signer, + BroadcastClient: mockBroadcastClient, + } + + cmd := upgradeCmd(mockCF) + AddFlags(cmd) + + args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"} + cmd.SetArgs(args) + + expectErrMsg := fmt.Sprintf("Could not assemble transaction, err Proposal response was not successful, error code %d, msg %s", errCode, errMsg) + if err := cmd.Execute(); err == nil { + t.Errorf("Run chaincode upgrade cmd error:%v", err) + } else { + if err.Error() != expectErrMsg { + t.Errorf("Run chaincode upgrade cmd get unexpected error: %s", err.Error()) + } + } +} + +func TestUpgradeCmdSendTXFail(t *testing.T) { + primitives.SetSecurityLevel("SHA2", 256) + InitMSP() + + signer, err := common.GetDefaultSigner() + if err != nil { + t.Fatalf("Get default signer error: %v", err) + } + + mockResponse := &pb.ProposalResponse{ + Response: &pb.Response{Status: 200}, + Endorsement: &pb.Endorsement{}, + } + + mockEndorerClient := common.GetMockEndorserClient(mockResponse, nil) + + sendErr := fmt.Errorf("send tx failed") + mockBroadcastClient := common.GetMockBroadcastClient(sendErr) + + mockCF := &ChaincodeCmdFactory{ + EndorserClient: mockEndorerClient, + Signer: signer, + BroadcastClient: mockBroadcastClient, + } + + cmd := upgradeCmd(mockCF) + AddFlags(cmd) + + args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"} + cmd.SetArgs(args) + + expectErrMsg := sendErr.Error() + if err := cmd.Execute(); err == nil { + t.Errorf("Run chaincode upgrade cmd error:%v", err) + } else { + if err.Error() != expectErrMsg { + t.Errorf("Run chaincode upgrade cmd get unexpected error: %s", err.Error()) + } + } +} diff --git a/peer/common/common.go b/peer/common/common.go index c799d95075e..6d830f6eb3a 100755 --- a/peer/common/common.go +++ b/peer/common/common.go @@ -17,11 +17,13 @@ limitations under the License. package common import ( + "fmt" + "github.com/hyperledger/fabric/core/errors" "github.com/hyperledger/fabric/core/peer" "github.com/hyperledger/fabric/flogging" + "github.com/hyperledger/fabric/msp" pb "github.com/hyperledger/fabric/protos/peer" - "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -29,7 +31,7 @@ import ( const UndefinedParamValue = "" // GetEndorserClient returns a new endorser client connection for this peer -func GetEndorserClient(cmd *cobra.Command) (pb.EndorserClient, error) { +func GetEndorserClient() (pb.EndorserClient, error) { clientConn, err := peer.NewPeerClientConnection() if err != nil { err = errors.ErrorWithCallstack(errors.Peer, errors.PeerConnectionError, err.Error()) @@ -58,3 +60,13 @@ func SetErrorLoggingLevel() error { return err } + +// GetDefaultSigner return a default Signer(Default/PERR) for cli +func GetDefaultSigner() (msp.SigningIdentity, error) { + signer, err := msp.GetLocalMSP().GetDefaultSigningIdentity() + if err != nil { + return nil, fmt.Errorf("Error obtaining the default signing identity, err %s", err) + } + + return signer, err +} diff --git a/peer/common/mockclient.go b/peer/common/mockclient.go new file mode 100644 index 00000000000..992f440c469 --- /dev/null +++ b/peer/common/mockclient.go @@ -0,0 +1,58 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// GetMockEndorserClient return a endorser client return specified ProposalResponse and err(nil or error) +func GetMockEndorserClient(repponse *pb.ProposalResponse, err error) pb.EndorserClient { + return &mockEndorserClient{ + repponse: repponse, + err: err, + } +} + +type mockEndorserClient struct { + repponse *pb.ProposalResponse + err error +} + +func (m *mockEndorserClient) ProcessProposal(ctx context.Context, in *pb.SignedProposal, opts ...grpc.CallOption) (*pb.ProposalResponse, error) { + return m.repponse, m.err +} + +func GetMockBroadcastClient(err error) BroadcastClient { + return &mockBroadcastClient{err: err} +} + +// mockBroadcastClient return success immediately +type mockBroadcastClient struct { + err error +} + +func (m *mockBroadcastClient) Send(env *cb.Envelope) error { + return m.err +} + +func (m *mockBroadcastClient) Close() error { + return nil +} diff --git a/peer/chaincode/noopsordererclient.go b/peer/common/ordererclient.go similarity index 60% rename from peer/chaincode/noopsordererclient.go rename to peer/common/ordererclient.go index a976b80c9b2..fb941e3491c 100644 --- a/peer/chaincode/noopsordererclient.go +++ b/peer/common/ordererclient.go @@ -14,67 +14,81 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chaincode - -//--------!!!IMPORTANT!!-!!IMPORTANT!!-!!IMPORTANT!!--------- -// This Orderer client is based off fabric/orderer/sample_clients/ -// broadcast_timestamp/client.go -// It is temporary and will go away from CLI when SDK implements -// interactions for the V1 architecture -//------------------------------------------------------------- +package common + import ( "fmt" "time" cb "github.com/hyperledger/fabric/protos/common" ab "github.com/hyperledger/fabric/protos/orderer" + "github.com/spf13/viper" "golang.org/x/net/context" "google.golang.org/grpc" ) -type broadcastClient struct { - client ab.AtomicBroadcast_BroadcastClient +type BroadcastClient interface { + //Send data to orderer + Send(env *cb.Envelope) error + Close() error } -// newBroadcastClient creates a simple instance of the broadcastClient interface -func newBroadcastClient(client ab.AtomicBroadcast_BroadcastClient) *broadcastClient { - return &broadcastClient{client: client} +type broadcastClient struct { + conn *grpc.ClientConn + client ab.AtomicBroadcast_BroadcastClient } -func (s *broadcastClient) getAck() error { - msg, err := s.client.Recv() - if err != nil { - return err +// GetBroadcastClient creates a simple instance of the BroadcastClient interface +func GetBroadcastClient() (BroadcastClient, error) { + var orderer string + if viper.GetBool("peer.committer.enabled") { + orderer = viper.GetString("peer.committer.ledger.orderer") } - if msg.Status != cb.Status_SUCCESS { - return fmt.Errorf("Got unexpected status: %v", msg.Status) + + if orderer == "" { + return nil, fmt.Errorf("Can't get orderer address") } - return nil -} -//Send data to solo orderer -func Send(serverAddr string, env *cb.Envelope) error { var opts []grpc.DialOption opts = append(opts, grpc.WithInsecure()) opts = append(opts, grpc.WithTimeout(3*time.Second)) opts = append(opts, grpc.WithBlock()) - conn, err := grpc.Dial(serverAddr, opts...) - defer conn.Close() + conn, err := grpc.Dial(orderer, opts...) if err != nil { - return fmt.Errorf("Error connecting: %s", err) + return nil, fmt.Errorf("Error connecting: %s", err) } client, err := ab.NewAtomicBroadcastClient(conn).Broadcast(context.TODO()) if err != nil { - return fmt.Errorf("Error connecting: %s", err) + conn.Close() + return nil, fmt.Errorf("Error connecting: %s", err) } - s := newBroadcastClient(client) - if err = s.client.Send(env); err != nil { + return &broadcastClient{conn: conn, client: client}, nil +} + +func (s *broadcastClient) getAck() error { + msg, err := s.client.Recv() + if err != nil { + return err + } + if msg.Status != cb.Status_SUCCESS { + return fmt.Errorf("Got unexpected status: %v", msg.Status) + } + return nil +} + +//Send data to orderer +func (s *broadcastClient) Send(env *cb.Envelope) error { + if err := s.client.Send(env); err != nil { return fmt.Errorf("Could not send :%s)", err) } - err = s.getAck() + err := s.getAck() return err } + +func (s *broadcastClient) Close() error { + return s.conn.Close() +} diff --git a/peer/main.go b/peer/main.go index 079c693faa1..a46010ee0ac 100644 --- a/peer/main.go +++ b/peer/main.go @@ -108,7 +108,7 @@ func main() { mainCmd.AddCommand(version.Cmd()) mainCmd.AddCommand(node.Cmd()) - mainCmd.AddCommand(chaincode.Cmd()) + mainCmd.AddCommand(chaincode.Cmd(nil)) mainCmd.AddCommand(clilogging.Cmd()) runtime.GOMAXPROCS(viper.GetInt("peer.gomaxprocs")) diff --git a/protos/utils/proputils.go b/protos/utils/proputils.go index afc95f1fdac..5819f0c18fb 100644 --- a/protos/utils/proputils.go +++ b/protos/utils/proputils.go @@ -377,19 +377,35 @@ func CreateProposalFromCIS(txid string, chainID string, cis *peer.ChaincodeInvoc return CreateChaincodeProposal(txid, chainID, cis, creator) } -// CreateProposalFromCDS returns a proposal given a serialized identity and a ChaincodeDeploymentSpec -func CreateProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte) (*peer.Proposal, error) { +// CreateDeployProposalFromCDS returns a deploy proposal given a serialized identity and a ChaincodeDeploymentSpec +func CreateDeployProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte) (*peer.Proposal, error) { + return createProposalFromCDS(txid, chainID, cds, creator, true) +} + +// CreateUpgradeProposalFromCDS returns a upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec +func CreateUpgradeProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte) (*peer.Proposal, error) { + return createProposalFromCDS(txid, chainID, cds, creator, false) +} + +// createProposalFromCDS returns a deploy or upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec +func createProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, deploy bool) (*peer.Proposal, error) { b, err := proto.Marshal(cds) if err != nil { return nil, err } + var propType string + if deploy { + propType = "deploy" + } else { + propType = "upgrade" + } //wrap the deployment in an invocation spec to lccc... lcccSpec := &peer.ChaincodeInvocationSpec{ ChaincodeSpec: &peer.ChaincodeSpec{ Type: peer.ChaincodeSpec_GOLANG, ChaincodeID: &peer.ChaincodeID{Name: "lccc"}, - CtorMsg: &peer.ChaincodeInput{Args: [][]byte{[]byte("deploy"), []byte(chainID), b}}}} + CtorMsg: &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b}}}} //...and get the proposal for it return CreateProposalFromCIS(txid, chainID, lcccSpec, creator)