From fc95c06c5bdcfd3ca78f2970a5d4204700220ad6 Mon Sep 17 00:00:00 2001 From: jiangyaoguo Date: Fri, 21 Apr 2017 15:31:33 +0800 Subject: [PATCH] [FAB-3329] set chaincode version in ProposalResponse When endorser endorse proposal, set ChaincodeID field of ChaincodeAction with the ChaincodeID which was used to execute chaincode, especially the chaincode version. This CR add ChaincodeID to escc args and set it when endorsing proposal. Another CR will implement checking chaincode version of transaction matches latest chaincode version in commiiter side. Change-Id: I62d7a5d34e12ff9d3131aa52d1af428bb8d58f2e Signed-off-by: jiangyaoguo --- common/ledger/testutil/test_helper.go | 10 ++-- core/chaincode/exectransaction_test.go | 22 +++++--- core/common/validation/fullflow_test.go | 18 ++++--- core/endorser/endorser.go | 36 +++++++++---- core/ledger/kvledger/example/app.go | 6 ++- core/scc/escc/endorser_onevalidsignature.go | 42 +++++++++------ .../escc/endorser_onevalidsignature_test.go | 51 ++++++++++++++----- .../vscc/validator_onevalidsignature_test.go | 5 +- events/events_test.go | 3 +- events/producer/eventhelper.go | 2 +- protos/testutils/txtestutils.go | 14 ++--- protos/utils/commonutils.go | 12 +++++ protos/utils/proputils.go | 4 +- protos/utils/proputils_test.go | 9 +++- protos/utils/txutils.go | 4 +- 15 files changed, 165 insertions(+), 73 deletions(-) diff --git a/common/ledger/testutil/test_helper.go b/common/ledger/testutil/test_helper.go index 1199ff50266..274486c30f3 100644 --- a/common/ledger/testutil/test_helper.go +++ b/common/ledger/testutil/test_helper.go @@ -25,6 +25,7 @@ import ( "github.com/hyperledger/fabric/common/util" lutils "github.com/hyperledger/fabric/core/ledger/util" "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" ptestutils "github.com/hyperledger/fabric/protos/testutils" "github.com/hyperledger/fabric/protos/utils" @@ -75,15 +76,18 @@ func (bg *BlockGenerator) NextTestBlocks(numBlocks int) []*common.Block { // ConstructTransaction constructs a transaction for testing func ConstructTransaction(_ *testing.T, simulationResults []byte, sign bool) (*common.Envelope, string, error) { - ccName := "foo" + ccid := &pb.ChaincodeID{ + Name: "foo", + Version: "v1", + } //response := &pb.Response{Status: 200} var txID string var txEnv *common.Envelope var err error if sign { - txEnv, txID, err = ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GetTestChainID(), ccName, nil, simulationResults, nil, nil) + txEnv, txID, err = ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GetTestChainID(), ccid, nil, simulationResults, nil, nil) } else { - txEnv, txID, err = ptestutils.ConstructUnsingedTxEnv(util.GetTestChainID(), ccName, nil, simulationResults, nil, nil) + txEnv, txID, err = ptestutils.ConstructUnsingedTxEnv(util.GetTestChainID(), ccid, nil, simulationResults, nil, nil) } return txEnv, txID, err } diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index 04c4ed1fe87..97444d42b20 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -171,28 +171,36 @@ func endTxSimulationCDS(chainID string, _ string, txsim ledger.TxSimulator, payl if err != nil { return err } + + // get lscc ChaincodeID + lsccid := &pb.ChaincodeID{ + Name: "lscc", + Version: util.GetSysCCVersion(), + } + // get a proposal - we need it to get a transaction prop, _, err := putils.CreateDeployProposalFromCDS(chainID, cds, ss, nil, nil, nil) if err != nil { return err } - return endTxSimulation(chainID, txsim, payload, commit, prop, blockNumber) + return endTxSimulation(chainID, lsccid, txsim, payload, commit, prop, blockNumber) } -func endTxSimulationCIS(chainID string, _ string, txsim ledger.TxSimulator, payload []byte, commit bool, cis *pb.ChaincodeInvocationSpec, blockNumber uint64) error { +func endTxSimulationCIS(chainID string, ccid *pb.ChaincodeID, _ string, txsim ledger.TxSimulator, payload []byte, commit bool, cis *pb.ChaincodeInvocationSpec, blockNumber uint64) error { // get serialized version of the signer ss, err := signer.Serialize() if err != nil { return err } + // get a proposal - we need it to get a transaction prop, _, err := putils.CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, chainID, cis, ss) if err != nil { return err } - return endTxSimulation(chainID, txsim, payload, commit, prop, blockNumber) + return endTxSimulation(chainID, ccid, txsim, payload, commit, prop, blockNumber) } //getting a crash from ledger.Commit when doing concurrent invokes @@ -205,7 +213,7 @@ func endTxSimulationCIS(chainID string, _ string, txsim ledger.TxSimulator, payl //concurrently (100 concurrent invokes followed by 100 concurrent queries) var _commitLock_ sync.Mutex -func endTxSimulation(chainID string, txsim ledger.TxSimulator, _ []byte, commit bool, prop *pb.Proposal, blockNumber uint64) error { +func endTxSimulation(chainID string, ccid *pb.ChaincodeID, txsim ledger.TxSimulator, _ []byte, commit bool, prop *pb.Proposal, blockNumber uint64) error { txsim.Done() if lgr := peer.GetLedger(chainID); lgr != nil { if commit { @@ -218,7 +226,7 @@ func endTxSimulation(chainID string, txsim ledger.TxSimulator, _ []byte, commit } // assemble a (signed) proposal response message - resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, &pb.Response{Status: 200}, txSimulationResults, nil, nil, signer) + resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, &pb.Response{Status: 200}, txSimulationResults, nil, ccid, nil, signer) if err != nil { return err } @@ -355,10 +363,10 @@ func invokeWithVersion(ctx context.Context, chainID string, version string, spec //no error, lets try commit if err == nil { //capture returned error from commit - err = endTxSimulationCIS(chainID, uuid, txsim, []byte("invoke"), true, cdInvocationSpec, blockNumber) + err = endTxSimulationCIS(chainID, spec.ChaincodeId, uuid, txsim, []byte("invoke"), true, cdInvocationSpec, blockNumber) } else { //there was an error, just close simulation and return that - endTxSimulationCIS(chainID, uuid, txsim, []byte("invoke"), false, cdInvocationSpec, blockNumber) + endTxSimulationCIS(chainID, spec.ChaincodeId, uuid, txsim, []byte("invoke"), false, cdInvocationSpec, blockNumber) } }() diff --git a/core/common/validation/fullflow_test.go b/core/common/validation/fullflow_test.go index 27c80e084a0..33caa33005d 100644 --- a/core/common/validation/fullflow_test.go +++ b/core/common/validation/fullflow_test.go @@ -35,13 +35,17 @@ import ( func getProposal() (*peer.Proposal, error) { cis := &peer.ChaincodeInvocationSpec{ ChaincodeSpec: &peer.ChaincodeSpec{ - ChaincodeId: &peer.ChaincodeID{Name: "foo"}, + ChaincodeId: getChaincodeID(), Type: peer.ChaincodeSpec_GOLANG}} proposal, _, err := utils.CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, signerSerialized) return proposal, err } +func getChaincodeID() *peer.ChaincodeID { + return &peer.ChaincodeID{Name: "foo", Version: "v1"} +} + func TestGoodPath(t *testing.T) { // get a toy proposal prop, err := getProposal() @@ -68,7 +72,7 @@ func TestGoodPath(t *testing.T) { simRes := []byte("simulation_result") // endorse it to get a proposal response - presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer) + presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, getChaincodeID(), nil, signer) if err != nil { t.Fatalf("CreateProposalResponse failed, err %s", err) return @@ -202,7 +206,7 @@ func TestBadTx(t *testing.T) { simRes := []byte("simulation_result") // endorse it to get a proposal response - presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer) + presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, getChaincodeID(), nil, signer) if err != nil { t.Fatalf("CreateProposalResponse failed, err %s", err) return @@ -255,7 +259,7 @@ func Test2EndorsersAgree(t *testing.T) { simRes1 := []byte("simulation_result") // endorse it to get a proposal response - presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, nil, signer) + presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, getChaincodeID(), nil, signer) if err != nil { t.Fatalf("CreateProposalResponse failed, err %s", err) return @@ -265,7 +269,7 @@ func Test2EndorsersAgree(t *testing.T) { simRes2 := []byte("simulation_result") // endorse it to get a proposal response - presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, nil, signer) + presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, getChaincodeID(), nil, signer) if err != nil { t.Fatalf("CreateProposalResponse failed, err %s", err) return @@ -298,7 +302,7 @@ func Test2EndorsersDisagree(t *testing.T) { simRes1 := []byte("simulation_result1") // endorse it to get a proposal response - presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, nil, signer) + presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, getChaincodeID(), nil, signer) if err != nil { t.Fatalf("CreateProposalResponse failed, err %s", err) return @@ -308,7 +312,7 @@ func Test2EndorsersDisagree(t *testing.T) { simRes2 := []byte("simulation_result2") // endorse it to get a proposal response - presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, nil, signer) + presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, getChaincodeID(), nil, signer) if err != nil { t.Fatalf("CreateProposalResponse failed, err %s", err) return diff --git a/core/endorser/endorser.go b/core/endorser/endorser.go index e7a108acfca..a6e1e12faa2 100644 --- a/core/endorser/endorser.go +++ b/core/endorser/endorser.go @@ -210,18 +210,19 @@ func (e *Endorser) getCDSFromLSCC(ctx context.Context, chainID string, txid stri func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid string, signedProp *pb.SignedProposal, proposal *pb.Proposal, response *pb.Response, simRes []byte, event *pb.ChaincodeEvent, visibility []byte, ccid *pb.ChaincodeID, txsim ledger.TxSimulator, cd *ccprovider.ChaincodeData) (*pb.ProposalResponse, error) { endorserLogger.Debugf("endorseProposal starts for chainID %s, ccid %s", chainID, ccid) + isSysCC := cd == nil // 1) extract the name of the escc that is requested to endorse this chaincode var escc string //ie, not "lscc" or system chaincodes - if cd != nil { + if isSysCC { + // FIXME: getCDSFromLSCC seems to fail for lscc - not sure this is expected? + // TODO: who should endorse a call to LSCC? + escc = "escc" + } else { escc = cd.Escc if escc == "" { // this should never happen, LSCC always fills this field panic("No ESCC specified in ChaincodeData") } - } else { - // FIXME: getCDSFromLSCC seems to fail for lscc - not sure this is expected? - // TODO: who should endorse a call to LSCC? - escc = "escc" } endorserLogger.Debugf("endorseProposal info: escc for cid %s is %s", ccid, escc) @@ -241,16 +242,31 @@ func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid str return nil, fmt.Errorf("failed to marshal response bytes - %s", err) } + // set version of executing chaincode + if isSysCC { + // if we want to allow mixed fabric levels we should + // set syscc version to "" + ccid.Version = util.GetSysCCVersion() + } else { + ccid.Version = cd.Version + } + + ccidBytes, err := putils.Marshal(ccid) + if err != nil { + return nil, fmt.Errorf("failed to marshal ChaincodeID - %s", err) + } + // 3) call the ESCC we've identified // arguments: // args[0] - function name (not used now) // args[1] - serialized Header object // args[2] - serialized ChaincodeProposalPayload object - // args[3] - result of executing chaincode - // args[4] - binary blob of simulation results - // args[5] - serialized events - // args[6] - payloadVisibility - args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, resBytes, simRes, eventBytes, visibility} + // args[3] - ChaincodeID of executing chaincode + // args[4] - result of executing chaincode + // args[5] - binary blob of simulation results + // args[6] - serialized events + // args[7] - payloadVisibility + args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, resBytes, simRes, eventBytes, visibility} version := util.GetSysCCVersion() ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}} res, _, err := e.callChaincode(ctx, chainID, version, txid, signedProp, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim) diff --git a/core/ledger/kvledger/example/app.go b/core/ledger/kvledger/example/app.go index 87c168cba95..efbbd541b6d 100644 --- a/core/ledger/kvledger/example/app.go +++ b/core/ledger/kvledger/example/app.go @@ -115,8 +115,12 @@ func (app *App) QueryBalances(accounts []string) ([]int, error) { } func constructTransaction(simulationResults []byte) *common.Envelope { + ccid := &pb.ChaincodeID{ + Name: "foo", + Version: "v1", + } response := &pb.Response{Status: 200} - txEnv, _, _ := ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GetTestChainID(), "foo", response, simulationResults, nil, nil) + txEnv, _, _ := ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GetTestChainID(), ccid, response, simulationResults, nil, nil) return txEnv } diff --git a/core/scc/escc/endorser_onevalidsignature.go b/core/scc/escc/endorser_onevalidsignature.go index 6826fa3d6d7..2a6e06d418c 100644 --- a/core/scc/escc/endorser_onevalidsignature.go +++ b/core/scc/escc/endorser_onevalidsignature.go @@ -52,10 +52,12 @@ func (e *EndorserOneValidSignature) Init(stub shim.ChaincodeStubInterface) pb.Re // args[0] - function name (not used now) // args[1] - serialized Header object // args[2] - serialized ChaincodeProposalPayload object -// args[3] - result of executing chaincode -// args[4] - binary blob of simulation results -// args[5] - serialized events -// args[6] - payloadVisibility +// args[3] - ChaincodeID of executing chaincode +// args[4] - result of executing chaincode +// args[5] - binary blob of simulation results +// args[6] - serialized events +// args[7] - payloadVisibility + // // NOTE: this chaincode is meant to sign another chaincode's simulation // results. It should not manipulate state as any state change will be @@ -64,9 +66,9 @@ func (e *EndorserOneValidSignature) Init(stub shim.ChaincodeStubInterface) pb.Re // definition can't be a state change of our own. func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.Response { args := stub.GetArgs() - if len(args) < 5 { + if len(args) < 6 { return shim.Error(fmt.Sprintf("Incorrect number of arguments (expected a minimum of 5, provided %d)", len(args))) - } else if len(args) > 7 { + } else if len(args) > 8 { return shim.Error(fmt.Sprintf("Incorrect number of arguments (expected a maximum of 7, provided %d)", len(args))) } @@ -88,13 +90,23 @@ func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb. payl = args[2] + // handle ChaincodeID + if args[3] == nil { + return shim.Error("ChaincodeID is null") + } + + ccid, err := putils.UnmarshalChaincodeID(args[3]) + if err != nil { + return shim.Error(err.Error()) + } + // handle executing chaincode result // Status code < 500 can be endorsed - if args[3] == nil { + if args[4] == nil { return shim.Error("Response of chaincode executing is null") } - response, err := putils.GetResponse(args[3]) + response, err := putils.GetResponse(args[4]) if err != nil { return shim.Error(fmt.Sprintf("Failed to get Response of executing chaincode: %s", err.Error())) } @@ -105,18 +117,18 @@ func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb. // handle simulation results var results []byte - if args[4] == nil { + if args[5] == nil { return shim.Error("simulation results are null") } - results = args[4] + results = args[5] // Handle serialized events if they have been provided // they might be nil in case there's no events but there // is a visibility field specified as the next arg events := []byte("") - if len(args) > 5 && args[5] != nil { - events = args[5] + if len(args) > 6 && args[6] != nil { + events = args[6] } // Handle payload visibility (it's an optional argument) @@ -128,8 +140,8 @@ func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb. // mechanisms that shall be encoded in this field (and handled // appropriately by the peer) var visibility []byte - if len(args) > 6 { - visibility = args[6] + if len(args) > 7 { + visibility = args[7] } // obtain the default signing identity for this peer; it will be used to sign this proposal response @@ -144,7 +156,7 @@ func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb. } // obtain a proposal response - presp, err := utils.CreateProposalResponse(hdr, payl, response, results, events, visibility, signingEndorser) + presp, err := utils.CreateProposalResponse(hdr, payl, response, results, events, ccid, visibility, signingEndorser) if err != nil { return shim.Error(err.Error()) } diff --git a/core/scc/escc/endorser_onevalidsignature_test.go b/core/scc/escc/endorser_onevalidsignature_test.go index 05592bf433f..75cd313c0c9 100644 --- a/core/scc/escc/endorser_onevalidsignature_test.go +++ b/core/scc/escc/endorser_onevalidsignature_test.go @@ -22,6 +22,7 @@ import ( "os" "testing" + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/common/validation" @@ -50,6 +51,13 @@ func TestInvoke(t *testing.T) { failResponse := &pb.Response{Status: 500, Message: "error"} successRes, _ := putils.GetBytesResponse(successResponse) failRes, _ := putils.GetBytesResponse(failResponse) + ccid := &pb.ChaincodeID{Name: "foo", Version: "v1"} + ccidBytes, err := proto.Marshal(ccid) + if err != nil { + t.Fail() + t.Fatalf("couldn't marshal ChaincodeID: err %s", err) + return + } // Initialize ESCC supplying the identity of the signer args := [][]byte{[]byte("DEFAULT"), []byte("PEER")} @@ -82,36 +90,42 @@ func TestInvoke(t *testing.T) { t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) } + // Failed path: Not enough parameters + args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test"), []byte("test")} + if res := stub.MockInvoke("1", args); res.Status == shim.OK { + t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) + } + // Failed path: header is null - args = [][]byte{[]byte("test"), nil, []byte("test"), successRes, []byte("test")} + args = [][]byte{[]byte("test"), nil, []byte("test"), ccidBytes, successRes, []byte("test")} if res := stub.MockInvoke("1", args); res.Status == shim.OK { fmt.Println("Invoke", args, "failed", string(res.Message)) t.Fatalf("escc invoke should have failed with a null header. args: %v", args) } // Failed path: payload is null - args = [][]byte{[]byte("test"), []byte("test"), nil, successRes, []byte("test")} + args = [][]byte{[]byte("test"), []byte("test"), nil, ccidBytes, successRes, []byte("test")} if res := stub.MockInvoke("1", args); res.Status == shim.OK { fmt.Println("Invoke", args, "failed", string(res.Message)) t.Fatalf("escc invoke should have failed with a null payload. args: %v", args) } // Failed path: response is null - args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil, []byte("test")} + args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, nil, []byte("test")} if res := stub.MockInvoke("1", args); res.Status == shim.OK { fmt.Println("Invoke", args, "failed", string(res.Message)) t.Fatalf("escc invoke should have failed with a null response. args: %v", args) } // Failed path: action struct is null - args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), successRes, nil} + args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, successRes, nil} if res := stub.MockInvoke("1", args); res.Status == shim.OK { fmt.Println("Invoke", args, "failed", string(res.Message)) t.Fatalf("escc invoke should have failed with a null action struct. args: %v", args) } // Failed path: status code >=500 - args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), failRes, []byte("test")} + args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, failRes, []byte("test")} if res := stub.MockInvoke("1", args); res.Status == shim.OK { fmt.Println("Invoke", args, "failed", string(res.Message)) t.Fatalf("escc invoke should have failed with a null response. args: %v", args) @@ -119,7 +133,7 @@ func TestInvoke(t *testing.T) { // Successful path - create a proposal cs := &pb.ChaincodeSpec{ - ChaincodeId: &pb.ChaincodeID{Name: "foo"}, + ChaincodeId: ccid, Type: pb.ChaincodeSpec_GOLANG, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("some"), []byte("args")}}} @@ -149,7 +163,7 @@ func TestInvoke(t *testing.T) { // success test 1: invocation with mandatory args only simRes := []byte("simulation_result") - args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes} + args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes} res := stub.MockInvoke("1", args) if res.Status != shim.OK { t.Fail() @@ -157,7 +171,7 @@ func TestInvoke(t *testing.T) { return } - err = validateProposalResponse(res.Payload, proposal, nil, successResponse, simRes, nil) + err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, nil) if err != nil { t.Fail() t.Fatalf("%s", err) @@ -167,7 +181,7 @@ func TestInvoke(t *testing.T) { // success test 2: invocation with mandatory args + events events := []byte("events") - args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes, events} + args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events} res = stub.MockInvoke("1", args) if res.Status != shim.OK { t.Fail() @@ -175,7 +189,7 @@ func TestInvoke(t *testing.T) { return } - err = validateProposalResponse(res.Payload, proposal, nil, successResponse, simRes, events) + err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, events) if err != nil { t.Fail() t.Fatalf("%s", err) @@ -183,7 +197,7 @@ func TestInvoke(t *testing.T) { } // success test 3: invocation with mandatory args + events and visibility - args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes, events, nil} + args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events, nil} res = stub.MockInvoke("1", args) if res.Status != shim.OK { t.Fail() @@ -191,7 +205,7 @@ func TestInvoke(t *testing.T) { return } - err = validateProposalResponse(res.Payload, proposal, []byte{}, successResponse, simRes, events) + err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, []byte{}, successResponse, simRes, events) if err != nil { t.Fail() t.Fatalf("%s", err) @@ -199,7 +213,7 @@ func TestInvoke(t *testing.T) { } } -func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, visibility []byte, response *pb.Response, simRes []byte, events []byte) error { +func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, ccid *pb.ChaincodeID, visibility []byte, response *pb.Response, simRes []byte, events []byte) error { if visibility == nil { // TODO: set visibility to the default visibility mode once modes are defined } @@ -270,6 +284,17 @@ func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, visibility return errors.New("events do not match") } + // validate that the ChaincodeID match + if cact.ChaincodeId.Name != ccid.Name { + return errors.New("ChaincodeID name do not match") + } + if cact.ChaincodeId.Version != ccid.Version { + return errors.New("ChaincodeID version do not match") + } + if cact.ChaincodeId.Path != ccid.Path { + return errors.New("ChaincodeID path do not match") + } + // get the identity of the endorser endorser, err := mspmgmt.GetManagerForChain(util.GetTestChainID()).DeserializeIdentity(pResp.Endorsement.Endorser) if err != nil { diff --git a/core/scc/vscc/validator_onevalidsignature_test.go b/core/scc/vscc/validator_onevalidsignature_test.go index 6196d334d0f..c073ec2b64f 100644 --- a/core/scc/vscc/validator_onevalidsignature_test.go +++ b/core/scc/vscc/validator_onevalidsignature_test.go @@ -33,14 +33,15 @@ import ( ) func createTx() (*common.Envelope, error) { - cis := &peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{ChaincodeId: &peer.ChaincodeID{Name: "foo"}}} + ccid := &peer.ChaincodeID{Name: "foo", Version: "v1"} + cis := &peer.ChaincodeInvocationSpec{ChaincodeSpec: &peer.ChaincodeSpec{ChaincodeId: ccid}} prop, _, err := utils.CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sid) if err != nil { return nil, err } - presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, &peer.Response{Status: 200}, []byte("res"), nil, nil, id) + presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, &peer.Response{Status: 200}, []byte("res"), nil, ccid, nil, id) if err != nil { return nil, err } diff --git a/events/events_test.go b/events/events_test.go index c76fefc73c8..e805ab485a3 100644 --- a/events/events_test.go +++ b/events/events_test.go @@ -102,6 +102,7 @@ func createTestBlock(t *testing.T) *common.Block { taas := make([]*ehpb.TransactionAction, 1) taas[0] = taa tx := &ehpb.Transaction{Actions: taas} + ccid := &ehpb.ChaincodeID{Name: "ccid", Version: "v1"} events := &ehpb.ChaincodeEvent{ ChaincodeId: "ccid", @@ -116,7 +117,7 @@ func createTestBlock(t *testing.T) *common.Block { if err != nil { t.Fatalf("Failure while marshalling the ProposalResponsePayload") } - ccaPayload.Action.ProposalResponsePayload, err = utils.GetBytesProposalResponsePayload(pHashBytes, pResponse, results, eventBytes) + ccaPayload.Action.ProposalResponsePayload, err = utils.GetBytesProposalResponsePayload(pHashBytes, pResponse, results, eventBytes, ccid) if err != nil { t.Fatalf("Failure while marshalling the ProposalResponsePayload") } diff --git a/events/producer/eventhelper.go b/events/producer/eventhelper.go index 4a26bca36de..375d533540f 100644 --- a/events/producer/eventhelper.go +++ b/events/producer/eventhelper.go @@ -76,7 +76,7 @@ func SendProducerBlockEvent(block *common.Block) error { // Dropping the read write set may cause issues for security and // we will need to revist when event security is addressed caPayload.Results = nil - chaincodeActionPayload.Action.ProposalResponsePayload, err = utils.GetBytesProposalResponsePayload(propRespPayload.ProposalHash, caPayload.Response, caPayload.Results, caPayload.Events) + chaincodeActionPayload.Action.ProposalResponsePayload, err = utils.GetBytesProposalResponsePayload(propRespPayload.ProposalHash, caPayload.Response, caPayload.Results, caPayload.Events, caPayload.ChaincodeId) if err != nil { return fmt.Errorf("error marshalling tx proposal payload for block event: %s", err) } diff --git a/protos/testutils/txtestutils.go b/protos/testutils/txtestutils.go index 3ee7cc27b7b..a11eb734950 100644 --- a/protos/testutils/txtestutils.go +++ b/protos/testutils/txtestutils.go @@ -51,23 +51,23 @@ func init() { // ConstructSingedTxEnvWithDefaultSigner constructs a transaction envelop for tests with a default signer. // This method helps other modules to construct a transaction with supplied parameters -func ConstructSingedTxEnvWithDefaultSigner(chainID, ccName string, response *pb.Response, simulationResults []byte, events []byte, visibility []byte) (*common.Envelope, string, error) { - return ConstructSingedTxEnv(chainID, ccName, response, simulationResults, events, visibility, signer) +func ConstructSingedTxEnvWithDefaultSigner(chainID string, ccid *pb.ChaincodeID, response *pb.Response, simulationResults []byte, events []byte, visibility []byte) (*common.Envelope, string, error) { + return ConstructSingedTxEnv(chainID, ccid, response, simulationResults, events, visibility, signer) } // ConstructSingedTxEnv constructs a transaction envelop for tests -func ConstructSingedTxEnv(chainID string, ccName string, pResponse *pb.Response, simulationResults []byte, events []byte, visibility []byte, signer msp.SigningIdentity) (*common.Envelope, string, error) { +func ConstructSingedTxEnv(chainID string, ccid *pb.ChaincodeID, pResponse *pb.Response, simulationResults []byte, events []byte, visibility []byte, signer msp.SigningIdentity) (*common.Envelope, string, error) { ss, err := signer.Serialize() if err != nil { return nil, "", err } - prop, txid, err := putils.CreateChaincodeProposal(common.HeaderType_ENDORSER_TRANSACTION, chainID, &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: ccName}}}, ss) + prop, txid, err := putils.CreateChaincodeProposal(common.HeaderType_ENDORSER_TRANSACTION, chainID, &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: ccid}}, ss) if err != nil { return nil, "", err } - presp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, pResponse, simulationResults, nil, nil, signer) + presp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, pResponse, simulationResults, nil, ccid, nil, signer) if err != nil { return nil, "", err } @@ -83,11 +83,11 @@ var mspLcl msp.MSP var sigId msp.SigningIdentity // ConstructUnsingedTxEnv creates a Transaction envelope from given inputs -func ConstructUnsingedTxEnv(chainID string, ccName string, response *pb.Response, simulationResults []byte, events []byte, visibility []byte) (*common.Envelope, string, error) { +func ConstructUnsingedTxEnv(chainID string, ccid *pb.ChaincodeID, response *pb.Response, simulationResults []byte, events []byte, visibility []byte) (*common.Envelope, string, error) { if mspLcl == nil { mspLcl = msp.NewNoopMsp() sigId, _ = mspLcl.GetDefaultSigningIdentity() } - return ConstructSingedTxEnv(chainID, ccName, response, simulationResults, events, visibility, sigId) + return ConstructSingedTxEnv(chainID, ccid, response, simulationResults, events, visibility, sigId) } diff --git a/protos/utils/commonutils.go b/protos/utils/commonutils.go index 76aad6e8d28..d2d77067724 100644 --- a/protos/utils/commonutils.go +++ b/protos/utils/commonutils.go @@ -21,6 +21,7 @@ import ( "time" cb "github.com/hyperledger/fabric/protos/common" + pb "github.com/hyperledger/fabric/protos/peer" "errors" @@ -247,3 +248,14 @@ func UnmarshalChannelHeader(bytes []byte) (*cb.ChannelHeader, error) { return chdr, nil } + +// UnmarshalChaincodeID returns a ChaincodeID from bytes +func UnmarshalChaincodeID(bytes []byte) (*pb.ChaincodeID, error) { + ccid := &pb.ChaincodeID{} + err := proto.Unmarshal(bytes, ccid) + if err != nil { + return nil, fmt.Errorf("UnmarshalChaincodeID failed, err %s", err) + } + + return ccid, nil +} diff --git a/protos/utils/proputils.go b/protos/utils/proputils.go index 38ee4e810e1..5af105dddb5 100644 --- a/protos/utils/proputils.go +++ b/protos/utils/proputils.go @@ -382,8 +382,8 @@ func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.He } // GetBytesProposalResponsePayload gets proposal response payload -func GetBytesProposalResponsePayload(hash []byte, response *peer.Response, result []byte, event []byte) ([]byte, error) { - cAct := &peer.ChaincodeAction{Events: event, Results: result, Response: response} +func GetBytesProposalResponsePayload(hash []byte, response *peer.Response, result []byte, event []byte, ccid *peer.ChaincodeID) ([]byte, error) { + cAct := &peer.ChaincodeAction{Events: event, Results: result, Response: response, ChaincodeId: ccid} cActBytes, err := proto.Marshal(cAct) if err != nil { return nil, err diff --git a/protos/utils/proputils_test.go b/protos/utils/proputils_test.go index d12216bcf3d..104d6a75400 100644 --- a/protos/utils/proputils_test.go +++ b/protos/utils/proputils_test.go @@ -161,6 +161,10 @@ func TestProposalResponse(t *testing.T) { EventName: "EventName", Payload: []byte("EventPayload"), TxId: "TxID"} + ccid := &pb.ChaincodeID{ + Name: "ccid", + Version: "v1", + } pHashBytes := []byte("proposal_hash") pResponse := &pb.Response{Status: 200} @@ -172,7 +176,7 @@ func TestProposalResponse(t *testing.T) { } // get the bytes of the ProposalResponsePayload - prpBytes, err := utils.GetBytesProposalResponsePayload(pHashBytes, pResponse, results, eventBytes) + prpBytes, err := utils.GetBytesProposalResponsePayload(pHashBytes, pResponse, results, eventBytes, ccid) if err != nil { t.Fatalf("Failure while marshalling the ProposalResponsePayload") return @@ -250,8 +254,9 @@ func TestEnvelope(t *testing.T) { response := &pb.Response{Status: 200, Payload: []byte("payload")} result := []byte("res") + ccid := &pb.ChaincodeID{Name: "foo", Version: "v1"} - presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, result, nil, nil, signer) + presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, result, nil, ccid, nil, signer) if err != nil { t.Fatalf("Could not create proposal response, err %s\n", err) return diff --git a/protos/utils/txutils.go b/protos/utils/txutils.go index 92a20edfa6f..7316c94a661 100644 --- a/protos/utils/txutils.go +++ b/protos/utils/txutils.go @@ -210,7 +210,7 @@ func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps . } // CreateProposalResponse creates a proposal response. -func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) { +func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Response, results []byte, events []byte, ccid *peer.ChaincodeID, visibility []byte, signingEndorser msp.SigningIdentity) (*peer.ProposalResponse, error) { hdr, err := GetHeader(hdrbytes) if err != nil { return nil, err @@ -223,7 +223,7 @@ func CreateProposalResponse(hdrbytes []byte, payl []byte, response *peer.Respons } // get the bytes of the proposal response payload - we need to sign them - prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events) + prpBytes, err := GetBytesProposalResponsePayload(pHashBytes, response, results, events, ccid) if err != nil { return nil, errors.New("Failure while unmarshalling the ProposalResponsePayload") }