From 6860586c8bdba3b04a440e39c85a26f8412f5aec Mon Sep 17 00:00:00 2001 From: Srinivasan Muralidharan Date: Mon, 29 May 2017 23:44:49 -0400 Subject: [PATCH] [FAB-4208] proper UT for chaincode framework Replaces the heavyweight chaincode framework tests that need a full fledged peer and container env for testing with a lightweight UT tests using a mock environment. The CC side and some of the ledger interfaces are mocked. Also disables other tests in the framework so the community can get a feel for the coverage and speedup . renamed cc_test.go to chaincode_support_test.go . add some minor tests . added some error refactoring in handler.go Change-Id: I462f05f7a78b7b2cfbb20138a20c5c0ee45a9be4 Signed-off-by: Srinivasan Muralidharan --- common/mocks/peer/mockccstream.go | 18 +- core/chaincode/chaincode_support_test.go | 674 +++++++++++++++++++++++ core/chaincode/exectransaction_test.go | 21 +- core/chaincode/handler.go | 199 +++---- core/chaincode/multichains_test.go | 1 + core/chaincode/systemchaincode_test.go | 2 + core/chaincode/upgrade_test.go | 2 + 7 files changed, 801 insertions(+), 116 deletions(-) create mode 100644 core/chaincode/chaincode_support_test.go diff --git a/common/mocks/peer/mockccstream.go b/common/mocks/peer/mockccstream.go index f30cf5cee63..36e2684f476 100644 --- a/common/mocks/peer/mockccstream.go +++ b/common/mocks/peer/mockccstream.go @@ -44,7 +44,7 @@ type MockResponseSet struct { //and response to send (optional) type MockResponse struct { RecvMsg *pb.ChaincodeMessage - RespMsg *pb.ChaincodeMessage + RespMsg interface{} } // MockCCComm implements the mock communication between chaincode and peer @@ -60,6 +60,10 @@ type MockCCComm struct { respSet *MockResponseSet } +func (s *MockCCComm) SetName(newname string) { + s.name = newname +} + //Send sends a message func (s *MockCCComm) Send(msg *pb.ChaincodeMessage) error { s.sendStream <- msg @@ -143,7 +147,17 @@ func (s *MockCCComm) respond(msg *pb.ChaincodeMessage) error { } if mockResp.RespMsg != nil { - err = s.Send(mockResp.RespMsg) + var ccMsg *pb.ChaincodeMessage + if ccMsg, _ = mockResp.RespMsg.(*pb.ChaincodeMessage); ccMsg == nil { + if ccMsgFunc, ok := mockResp.RespMsg.(func(*pb.ChaincodeMessage) *pb.ChaincodeMessage); ok && ccMsgFunc != nil { + ccMsg = ccMsgFunc(msg) + } + } + + if ccMsg == nil { + panic("----no pb.ChaincodeMessage---") + } + err = s.Send(ccMsg) } s.respIndex = s.respIndex + 1 diff --git a/core/chaincode/chaincode_support_test.go b/core/chaincode/chaincode_support_test.go new file mode 100644 index 00000000000..5e68abaceb9 --- /dev/null +++ b/core/chaincode/chaincode_support_test.go @@ -0,0 +1,674 @@ +/* +Copyright IBM Corp. 2017 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 ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/common/flogging" + commonledger "github.com/hyperledger/fabric/common/ledger" + mocklgr "github.com/hyperledger/fabric/common/mocks/ledger" + mockpeer "github.com/hyperledger/fabric/common/mocks/peer" + "github.com/hyperledger/fabric/common/util" + "github.com/hyperledger/fabric/core/chaincode/shim" + "github.com/hyperledger/fabric/core/common/ccprovider" + "github.com/hyperledger/fabric/core/config" + "github.com/hyperledger/fabric/core/ledger" + "github.com/hyperledger/fabric/core/ledger/ledgermgmt" + "github.com/hyperledger/fabric/core/peer" + "github.com/hyperledger/fabric/core/policy" + "github.com/hyperledger/fabric/core/scc" + plgr "github.com/hyperledger/fabric/protos/ledger/queryresult" + pb "github.com/hyperledger/fabric/protos/peer" + putils "github.com/hyperledger/fabric/protos/utils" + "golang.org/x/net/context" + + mspmgmt "github.com/hyperledger/fabric/msp/mgmt" +) + +var globalBlockNum map[string]uint64 + +type mockResultsIterator struct { + current int + kvs []*plgr.KV +} + +func (mri *mockResultsIterator) Next() (commonledger.QueryResult, error) { + if mri.current == len(mri.kvs) { + return nil, nil + } + kv := mri.kvs[mri.current] + mri.current = mri.current + 1 + + return kv, nil +} + +func (mri *mockResultsIterator) Close() { + mri.current = len(mri.kvs) +} + +type mockExecQuerySimulator struct { + txsim ledger.TxSimulator + mocklgr.MockQueryExecutor + resultsIter map[string]map[string]*mockResultsIterator +} + +func (meqe *mockExecQuerySimulator) GetHistoryForKey(namespace, query string) (commonledger.ResultsIterator, error) { + return meqe.commonQuery(namespace, query) +} + +func (meqe *mockExecQuerySimulator) ExecuteQuery(namespace, query string) (commonledger.ResultsIterator, error) { + return meqe.commonQuery(namespace, query) +} + +func (meqe *mockExecQuerySimulator) commonQuery(namespace, query string) (commonledger.ResultsIterator, error) { + if meqe.resultsIter == nil { + return nil, fmt.Errorf("query executor not initialized") + } + nsiter := meqe.resultsIter[namespace] + if nsiter == nil { + return nil, fmt.Errorf("namespace %v not found for %s", namespace, query) + } + iter := nsiter[query] + if iter == nil { + fmt.Printf("iter not found for query %s\n", query) + } + return iter, nil +} + +func (meqe *mockExecQuerySimulator) SetState(namespace string, key string, value []byte) error { + if meqe.txsim == nil { + return fmt.Errorf("SetState txsimulator not initialed") + } + return meqe.txsim.SetState(namespace, key, value) +} + +func (meqe *mockExecQuerySimulator) DeleteState(namespace string, key string) error { + if meqe.txsim == nil { + return fmt.Errorf("SetState txsimulator not initialed") + } + return meqe.txsim.DeleteState(namespace, key) +} + +func (meqe *mockExecQuerySimulator) SetStateMultipleKeys(namespace string, kvs map[string][]byte) error { + if meqe.txsim == nil { + return fmt.Errorf("SetState txsimulator not initialed") + } + return meqe.txsim.SetStateMultipleKeys(namespace, kvs) +} + +func (meqe *mockExecQuerySimulator) ExecuteUpdate(query string) error { + if meqe.txsim == nil { + return fmt.Errorf("SetState txsimulator not initialed") + } + return meqe.txsim.ExecuteUpdate(query) +} + +func (meqe *mockExecQuerySimulator) GetTxSimulationResults() ([]byte, error) { + if meqe.txsim == nil { + return nil, fmt.Errorf("SetState txsimulator not initialed") + } + return meqe.txsim.GetTxSimulationResults() +} + +//initialize peer and start up. If security==enabled, login as vp +func initMockPeer(chainIDs ...string) error { + peer.MockInitialize() + + mspGetter := func(cid string) []string { + return []string{"DEFAULT"} + } + + peer.MockSetMSPIDGetter(mspGetter) + + getPeerEndpoint := func() (*pb.PeerEndpoint, error) { + return &pb.PeerEndpoint{Id: &pb.PeerID{Name: "testpeer"}}, nil + } + + ccStartupTimeout := time.Duration(2) * time.Second + NewChaincodeSupport(getPeerEndpoint, false, ccStartupTimeout) + theChaincodeSupport.executetimeout = time.Duration(1) * time.Second + + // Mock policy checker + policy.RegisterPolicyCheckerFactory(&mockPolicyCheckerFactory{}) + + scc.RegisterSysCCs() + + globalBlockNum = make(map[string]uint64, len(chainIDs)) + for _, id := range chainIDs { + scc.DeDeploySysCCs(id) + if err := peer.MockCreateChain(id); err != nil { + return err + } + scc.DeploySysCCs(id) + // any chain other than the default testchainid does not have a MSP set up -> create one + if id != util.GetTestChainID() { + mspmgmt.XXXSetMSPManager(id, mspmgmt.GetManagerForChain(util.GetTestChainID())) + } + globalBlockNum[id] = 1 + } + + return nil +} + +func finitMockPeer(chainIDs ...string) { + for _, c := range chainIDs { + scc.DeDeploySysCCs(c) + if lgr := peer.GetLedger(c); lgr != nil { + lgr.Close() + } + } + ledgermgmt.CleanupTestEnv() + ledgerPath := config.GetPath("peer.fileSystemPath") + os.RemoveAll(ledgerPath) + os.RemoveAll(filepath.Join(os.TempDir(), "hyperledger")) +} + +//store the stream CC mappings here +var mockPeerCCSupport = mockpeer.NewMockPeerSupport() + +func mockChaincodeStreamGetter(name string) (shim.PeerChaincodeStream, error) { + return mockPeerCCSupport.GetCC(name) +} + +func setupcc(name string) (*mockpeer.MockCCComm, *mockpeer.MockCCComm) { + send := make(chan *pb.ChaincodeMessage) + recv := make(chan *pb.ChaincodeMessage) + peerSide, _ := mockPeerCCSupport.AddCC(name, recv, send) + peerSide.SetName("peer") + return peerSide, mockPeerCCSupport.GetCCMirror(name) +} + +//assign this to done and failNow and keep using them +func setuperror() chan error { + return make(chan error) +} + +func processDone(t *testing.T, done chan error, expecterr bool) { + err := <-done + if expecterr != (err != nil) { + if err == nil { + t.Fatalf("Expected error but got success") + } else { + t.Fatalf("Expected success but got error %s", err) + } + } +} + +func startTx(t *testing.T, chainID string, cis *pb.ChaincodeInvocationSpec) (context.Context, ledger.TxSimulator, *pb.SignedProposal, *pb.Proposal) { + ctxt := context.Background() + + creator := []byte([]byte("Alice")) + sprop, prop := putils.MockSignedEndorserProposalOrPanic(chainID, cis.ChaincodeSpec, creator, []byte("msg1")) + var txsim ledger.TxSimulator + var err error + if ctxt, txsim, err = startTxSimulation(ctxt, chainID); err != nil { + t.Fatalf("getting txsimulator failed %s", err) + } + return ctxt, txsim, sprop, prop +} + +func endTx(t *testing.T, cccid *ccprovider.CCContext, txsim ledger.TxSimulator, cis *pb.ChaincodeInvocationSpec) { + if err := endTxSimulationCIS(cccid.ChainID, cis.ChaincodeSpec.ChaincodeId, cccid.TxID, txsim, []byte("invoke"), true, cis, globalBlockNum[cccid.ChainID]); err != nil { + t.Fatalf("simulation failed with error %s", err) + } + globalBlockNum[cccid.ChainID] = globalBlockNum[cccid.ChainID] + 1 +} + +func execCC(t *testing.T, ctxt context.Context, ccSide *mockpeer.MockCCComm, cccid *ccprovider.CCContext, waitForERROR bool, expectExecErr bool, done chan error, cis *pb.ChaincodeInvocationSpec, respSet *mockpeer.MockResponseSet) error { + ccSide.SetResponses(respSet) + + _, _, err := ExecuteWithErrorFilter(ctxt, cccid, cis) + + if err == nil && expectExecErr { + t.Fatalf("expected error but succeeded") + } else if err != nil && !expectExecErr { + t.Fatalf("exec failed with %s", err) + } + + //wait + processDone(t, done, waitForERROR) + + return nil +} + +//initialize cc support env and startup the chaincode +func startCC(t *testing.T, ccname string) (*mockpeer.MockCCComm, *mockpeer.MockCCComm) { + peerSide, ccSide := setupcc(ccname) + defer mockPeerCCSupport.RemoveCC(ccname) + theChaincodeSupport.userRunsCC = true + flogging.SetModuleLevel("chaincode", "debug") + //register peer side with ccsupport + go func() { + theChaincodeSupport.HandleChaincodeStream(context.Background(), peerSide) + }() + + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + //start the mock peer + go func() { + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}, nil}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY}, nil}}} + ccSide.SetResponses(respSet) + ccSide.Run() + }() + + ccSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER, Payload: putils.MarshalOrPanic(&pb.ChaincodeID{Name: ccname + ":0"}), Txid: "0"}) + + //wait for init + processDone(t, done, false) + + return peerSide, ccSide +} + +func getTarGZ(t *testing.T, name string, contents []byte) []byte { + startTime := time.Now() + inputbuf := bytes.NewBuffer(nil) + gw := gzip.NewWriter(inputbuf) + tr := tar.NewWriter(gw) + size := int64(len(contents)) + + tr.WriteHeader(&tar.Header{Name: name, Size: size, ModTime: startTime, AccessTime: startTime, ChangeTime: startTime}) + tr.Write(contents) + tr.Close() + gw.Close() + ioutil.WriteFile("/tmp/t.gz", inputbuf.Bytes(), 0644) + return inputbuf.Bytes() +} + +// Deploy a chaincode - i.e., build and initialize. +func deployCC(t *testing.T, ctx context.Context, cccid *ccprovider.CCContext, spec *pb.ChaincodeSpec) { + // First build and get the deployment spec + code := getTarGZ(t, "src/dummy/dummy.go", []byte("code")) + cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: code} + + //ignore existence errors + ccprovider.PutChaincodeIntoFS(cds) + + b := putils.MarshalOrPanic(cds) + + sysCCVers := util.GetSysCCVersion() + + //wrap the deployment in an invocation spec to lscc... + lsccSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: "lscc", Version: sysCCVers}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("deploy"), []byte(cccid.ChainID), b}}}} + + sprop, prop := putils.MockSignedEndorserProposal2OrPanic(cccid.ChainID, lsccSpec.ChaincodeSpec, signer) + lsccid := ccprovider.NewCCContext(cccid.ChainID, lsccSpec.ChaincodeSpec.ChaincodeId.Name, sysCCVers, cccid.TxID, true, sprop, prop) + + //write to lscc + if _, _, err := ExecuteWithErrorFilter(ctx, lsccid, lsccSpec); err != nil { + t.Fatalf("Error deploying chaincode %v (err: %s)", cccid, err) + } +} + +func initializeCC(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCComm) error { + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + chaincodeID := &pb.ChaincodeID{Name: ccname, Version: "0"} + ci := &pb.ChaincodeInput{[][]byte{[]byte("init"), []byte("A"), []byte("100"), []byte("B"), []byte("200")}} + cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + ctxt, txsim, sprop, prop := startTx(t, chainID, cis) + + //bad txid in response (should be "1"), should fail + resp := &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("init succeeded")}), Txid: "unknowntxid"}} + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{resp}} + + cccid := ccprovider.NewCCContext(chainID, ccname, "0", "1", false, sprop, prop) + execCC(t, ctxt, ccSide, cccid, false, true, done, cis, respSet) + + //set the right TxID in response now + resp.RespMsg.(*pb.ChaincodeMessage).Txid = "1" + + badcccid := ccprovider.NewCCContext(chainID, ccname, "unknownver", "1", false, sprop, prop) + execCC(t, ctxt, ccSide, badcccid, false, true, done, cis, respSet) + + //---------try a successful init at last------- + //everything lined up + // correct registered chaincode version + // matching txid + // txsim context + // full response + // correct block number for ending sim + + respSet = &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Payload: putils.MarshalOrPanic(&pb.PutStateInfo{Key: "A", Value: []byte("100")}), Txid: "1"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Payload: putils.MarshalOrPanic(&pb.PutStateInfo{Key: "B", Value: []byte("200")}), Txid: "1"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), ChaincodeEvent: &pb.ChaincodeEvent{ChaincodeId: ccname}, Txid: "1"}}}} + + cccid.Version = "1" + execCC(t, ctxt, ccSide, cccid, false, false, done, cis, respSet) + + endTx(t, cccid, txsim, cis) + + return nil +} + +func invokeCC(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCComm) error { + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + chaincodeID := &pb.ChaincodeID{Name: ccname, Version: "0"} + ci := &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + ctxt, txsim, sprop, prop := startTx(t, chainID, cis) + + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Payload: []byte("A"), Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Payload: []byte("B"), Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Payload: putils.MarshalOrPanic(&pb.PutStateInfo{Key: "A", Value: []byte("90")}), Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Payload: putils.MarshalOrPanic(&pb.PutStateInfo{Key: "B", Value: []byte("210")}), Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Payload: putils.MarshalOrPanic(&pb.PutStateInfo{Key: "TODEL", Value: []byte("-to-be-deleted-")}), Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), Txid: "2"}}}} + + cccid := ccprovider.NewCCContext(chainID, ccname, "0", "2", false, sprop, prop) + execCC(t, ctxt, ccSide, cccid, false, false, done, cis, respSet) + + //delete the extra var + respSet = &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Payload: []byte("TODEL"), Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_DEL_STATE, Payload: []byte("TODEL"), Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), Txid: "3"}}}} + + cccid.TxID = "3" + execCC(t, ctxt, ccSide, cccid, false, false, done, cis, respSet) + + //get the extra var and delete it + //NOTE- we are calling ExecuteWithErrorFilter which returns error if chaincode returns ERROR response + respSet = &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Payload: []byte("TODEL"), Txid: "4"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.ERROR, Message: "variable not found"}), Txid: "4"}}}} + + cccid.TxID = "4" + execCC(t, ctxt, ccSide, cccid, false, true, done, cis, respSet) + + endTx(t, cccid, txsim, cis) + + return nil +} + +func getQueryStateByRange(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCComm) error { + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + chaincodeID := &pb.ChaincodeID{Name: ccname, Version: "0"} + ci := &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + ctxt, txsim, sprop, prop := startTx(t, chainID, cis) + + //create the response + queryStateNextFunc := func(reqMsg *pb.ChaincodeMessage) *pb.ChaincodeMessage { + qr := &pb.QueryResponse{} + proto.Unmarshal(reqMsg.Payload, qr) + return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Payload: putils.MarshalOrPanic(&pb.QueryStateNext{Id: qr.Id}), Txid: "5"} + } + queryStateCloseFunc := func(reqMsg *pb.ChaincodeMessage) *pb.ChaincodeMessage { + qr := &pb.QueryResponse{} + proto.Unmarshal(reqMsg.Payload, qr) + return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Payload: putils.MarshalOrPanic(&pb.QueryStateClose{Id: qr.Id}), Txid: "5"} + } + + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE_BY_RANGE, Payload: putils.MarshalOrPanic(&pb.GetStateByRange{StartKey: "A", EndKey: "B"}), Txid: "5"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, queryStateNextFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR}, queryStateCloseFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), Txid: "5"}}}} + + cccid := ccprovider.NewCCContext(chainID, ccname, "0", "5", false, sprop, prop) + execCC(t, ctxt, ccSide, cccid, false, false, done, cis, respSet) + + endTx(t, cccid, txsim, cis) + + return nil +} + +func cc2cc(t *testing.T, chainID, chainID2, ccname string, ccSide *mockpeer.MockCCComm) error { + calledCC := "calledCC" + //starts and registers the CC + _, calledCCSide := startCC(t, calledCC) + if calledCCSide == nil { + t.Fatalf("start up failed for called CC") + } + defer calledCCSide.Quit() + + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + chaincodeID := &pb.ChaincodeID{Name: calledCC, Version: "0"} + ci := &pb.ChaincodeInput{[][]byte{[]byte("deploycc")}} + cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + //first deploy the new cc to LSCC + ctxt, txsim, sprop, prop := startTx(t, chainID, cis) + cccid := ccprovider.NewCCContext(chainID, calledCC, "0", "6", false, sprop, prop) + + deployCC(t, ctxt, cccid, cis.ChaincodeSpec) + + //commit + endTx(t, cccid, txsim, cis) + + //now do the cc2cc + chaincodeID = &pb.ChaincodeID{Name: ccname, Version: "0"} + ci = &pb.ChaincodeInput{[][]byte{[]byte("invokecc")}} + cis = &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + ctxt, txsim, sprop, prop = startTx(t, chainID, cis) + + if _, _, err := ccprovider.GetChaincodeProvider().GetCCValidationInfoFromLSCC(ctxt, "getccdata", sprop, prop, chainID, calledCC); err != nil { + t.Fatalf("Could not get chaincode data from lscc for %s", calledCC) + } + + sysCCVers := util.GetSysCCVersion() + //call a callable system CC, a regular cc, a regular cc on a different chain and an uncallable system cc and expect an error inthe last one + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Payload: putils.MarshalOrPanic(&pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "lscc:" + sysCCVers}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte{}}}}), Txid: "7"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Payload: putils.MarshalOrPanic(&pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "calledCC:0/" + chainID}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte{}}}}), Txid: "7"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Payload: putils.MarshalOrPanic(&pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "calledCC:0/" + chainID2}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte{}}}}), Txid: "7"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Payload: putils.MarshalOrPanic(&pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "vscc:" + sysCCVers}, Input: &pb.ChaincodeInput{Args: [][]byte{[]byte{}}}}), Txid: "7"}}}} + + respSet2 := &mockpeer.MockResponseSet{nil, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), Txid: "7"}}}} + calledCCSide.SetResponses(respSet2) + + cccid = ccprovider.NewCCContext(chainID, ccname, "0", "7", false, sprop, prop) + + execCC(t, ctxt, ccSide, cccid, false, true, done, cis, respSet) + + endTx(t, cccid, txsim, cis) + + return nil +} + +func getQueryResult(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCComm) error { + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + chaincodeID := &pb.ChaincodeID{Name: ccname, Version: "0"} + ci := &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + ctxt, txsim, sprop, prop := startTx(t, chainID, cis) + + kvs := make([]*plgr.KV, 1000) + for i := 0; i < 1000; i++ { + kvs[i] = &plgr.KV{chainID, fmt.Sprintf("%d", i), []byte(fmt.Sprintf("%d", i))} + } + + queryExec := &mockExecQuerySimulator{resultsIter: make(map[string]map[string]*mockResultsIterator)} + queryExec.resultsIter[ccname] = map[string]*mockResultsIterator{"goodquery": &mockResultsIterator{kvs: kvs}} + + queryExec.txsim = ctxt.Value(TXSimulatorKey).(ledger.TxSimulator) + ctxt = context.WithValue(ctxt, TXSimulatorKey, queryExec) + + //create the response + queryStateNextFunc := func(reqMsg *pb.ChaincodeMessage) *pb.ChaincodeMessage { + qr := &pb.QueryResponse{} + proto.Unmarshal(reqMsg.Payload, qr) + return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Payload: putils.MarshalOrPanic(&pb.QueryStateNext{Id: qr.Id}), Txid: "8"} + } + queryStateCloseFunc := func(reqMsg *pb.ChaincodeMessage) *pb.ChaincodeMessage { + qr := &pb.QueryResponse{} + proto.Unmarshal(reqMsg.Payload, qr) + return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Payload: putils.MarshalOrPanic(&pb.QueryStateClose{Id: qr.Id}), Txid: "8"} + } + + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_QUERY_RESULT, Payload: putils.MarshalOrPanic(&pb.GetQueryResult{Query: "goodquery"}), Txid: "8"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, queryStateNextFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, queryStateNextFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR}, queryStateCloseFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), Txid: "8"}}}} + + cccid := ccprovider.NewCCContext(chainID, ccname, "0", "8", false, sprop, prop) + execCC(t, ctxt, ccSide, cccid, false, false, done, cis, respSet) + + endTx(t, cccid, txsim, cis) + + return nil +} + +func getHistory(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCComm) error { + done := setuperror() + + errorFunc := func(ind int, err error) { + done <- err + } + + chaincodeID := &pb.ChaincodeID{Name: ccname, Version: "0"} + ci := &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeId: chaincodeID, Input: ci}} + + ctxt, txsim, sprop, prop := startTx(t, chainID, cis) + + kvs := make([]*plgr.KV, 1000) + for i := 0; i < 1000; i++ { + kvs[i] = &plgr.KV{chainID, fmt.Sprintf("%d", i), []byte(fmt.Sprintf("%d", i))} + } + + queryExec := &mockExecQuerySimulator{resultsIter: make(map[string]map[string]*mockResultsIterator)} + queryExec.resultsIter[ccname] = map[string]*mockResultsIterator{"goodquery": &mockResultsIterator{kvs: kvs}} + + queryExec.txsim = ctxt.Value(TXSimulatorKey).(ledger.TxSimulator) + ctxt = context.WithValue(ctxt, TXSimulatorKey, queryExec) + + //create the response + queryStateNextFunc := func(reqMsg *pb.ChaincodeMessage) *pb.ChaincodeMessage { + qr := &pb.QueryResponse{} + proto.Unmarshal(reqMsg.Payload, qr) + return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Payload: putils.MarshalOrPanic(&pb.QueryStateNext{Id: qr.Id}), Txid: "8"} + } + queryStateCloseFunc := func(reqMsg *pb.ChaincodeMessage) *pb.ChaincodeMessage { + qr := &pb.QueryResponse{} + proto.Unmarshal(reqMsg.Payload, qr) + return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Payload: putils.MarshalOrPanic(&pb.QueryStateClose{Id: qr.Id}), Txid: "8"} + } + + respSet := &mockpeer.MockResponseSet{errorFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY, Payload: putils.MarshalOrPanic(&pb.GetQueryResult{Query: "goodquery"}), Txid: "8"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, queryStateNextFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, queryStateNextFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR}, queryStateCloseFunc}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: putils.MarshalOrPanic(&pb.Response{Status: shim.OK, Payload: []byte("OK")}), Txid: "8"}}}} + + cccid := ccprovider.NewCCContext(chainID, ccname, "0", "8", false, sprop, prop) + execCC(t, ctxt, ccSide, cccid, false, false, done, cis, respSet) + + endTx(t, cccid, txsim, cis) + + return nil +} + +func TestCCFramework(t *testing.T) { + //register 2 channels + chainID := "mockchainid" + chainID2 := "secondchain" + if err := initMockPeer(chainID, chainID2); err != nil { + t.Fatalf("%s", err) + } + defer finitMockPeer(chainID, chainID2) + + //create a chaincode + ccname := "shimTestCC" + + //starts and registers the CC + _, ccSide := startCC(t, ccname) + if ccSide == nil { + t.Fatalf("start up failed") + } + + //call's init and does some PUT (after doing some negative testing) + initializeCC(t, chainID, ccname, ccSide) + + //chaincode support should not allow dups + if err := theChaincodeSupport.registerHandler(&Handler{ChaincodeID: &pb.ChaincodeID{Name: ccname + ":0"}}); err == nil { + t.Fatalf("expected re-register to fail") + } else if err, _ := err.(*DuplicateChaincodeHandlerError); err == nil { + t.Fatalf("expected DuplicateChaincodeHandlerError") + } + + //call's init and does some PUT (after doing some negative testing) + initializeCC(t, chainID2, ccname, ccSide) + + //call's invoke and do some GET + invokeCC(t, chainID, ccname, ccSide) + + //call's query state range + getQueryStateByRange(t, chainID, ccname, ccSide) + + //call's cc2cc (variation with syscc calls) + cc2cc(t, chainID, chainID2, ccname, ccSide) + + //call's query result + getQueryResult(t, chainID, ccname, ccSide) + + //call's history result + getHistory(t, chainID, ccname, ccSide) + + ccSide.Quit() +} diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index ae83850ab2a..d1f7971f4e2 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -62,6 +62,15 @@ import ( "google.golang.org/grpc/credentials" ) +var runTests bool + +func testForSkip(t *testing.T) { + //run tests + if !runTests { + t.SkipNow() + } +} + //initialize peer and start up. If security==enabled, login as vp func initPeer(chainIDs ...string) (net.Listener, error) { //start clean @@ -796,6 +805,7 @@ func TestGopathExecuteDeployTransaction(t *testing.T) { } func TestExecuteInvokeTransaction(t *testing.T) { + testForSkip(t) testCases := []struct { chaincodeType pb.ChaincodeSpec_Type @@ -847,6 +857,8 @@ func TestExecuteInvokeTransaction(t *testing.T) { // Test the execution of an invalid transaction. func TestExecuteInvokeInvalidTransaction(t *testing.T) { + testForSkip(t) + chainID := util.GetTestChainID() lis, err := initPeer(chainID) @@ -892,6 +904,7 @@ type tcicTc struct { // Test the execution of a chaincode that invokes another chaincode. func TestChaincodeInvokeChaincode(t *testing.T) { + testForSkip(t) channel := util.GetTestChainID() channel2 := channel + "2" lis, err := initPeer(channel, channel2) @@ -965,6 +978,7 @@ func stopChaincode(ctx context.Context, chaincodeCtx *ccprovider.CCContext) { // Test the execution of a chaincode that invokes another chaincode with wrong parameters. Should receive error from // from the called chaincode func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) { + testForSkip(t) chainID := util.GetTestChainID() lis, err := initPeer(chainID) @@ -1057,6 +1071,7 @@ func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) { // Test the invocation of a transaction. func TestQueries(t *testing.T) { + testForSkip(t) chainID := util.GetTestChainID() @@ -1394,7 +1409,7 @@ func TestQueries(t *testing.T) { } func TestGetEvent(t *testing.T) { - + testForSkip(t) testCases := []struct { chaincodeType pb.ChaincodeSpec_Type chaincodePath string @@ -1479,6 +1494,7 @@ func TestGetEvent(t *testing.T) { // Test the execution of a chaincode that queries another chaincode // example02 implements "query" as a function in Invoke. example05 calls example02 func TestChaincodeQueryChaincodeUsingInvoke(t *testing.T) { + testForSkip(t) //this is essentially same as the ChaincodeInvokeChaincode now that //we don't distinguish between Invoke and Query (there's no separate "Query") t.Skip() @@ -1603,6 +1619,7 @@ func TestChaincodeQueryChaincodeUsingInvoke(t *testing.T) { // test that invoking a security-sensitive system chaincode fails func TestChaincodeInvokesForbiddenSystemChaincode(t *testing.T) { + testForSkip(t) chainID := util.GetTestChainID() lis, err := initPeer(chainID) @@ -1658,6 +1675,7 @@ func TestChaincodeInvokesForbiddenSystemChaincode(t *testing.T) { // Test the execution of a chaincode that invokes system chaincode // uses the "pthru" chaincode to query "lscc" for the "pthru" chaincode func TestChaincodeInvokesSystemChaincode(t *testing.T) { + testForSkip(t) chainID := util.GetTestChainID() lis, err := initPeer(chainID) @@ -1721,6 +1739,7 @@ func TestChaincodeInvokesSystemChaincode(t *testing.T) { } func TestChaincodeInitializeInitError(t *testing.T) { + testForSkip(t) testCases := []struct { name string chaincodeType pb.ChaincodeSpec_Type diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index 81b4e6c7a3a..74d21abc773 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -715,12 +715,19 @@ func (handler *Handler) handleGetStateByRange(msg *pb.ChaincodeMessage) { } chaincodeID := handler.getCCRootName() - rangeIter, err := txContext.txsimulator.GetStateRangeScanIterator(chaincodeID, getStateByRange.StartKey, getStateByRange.EndKey) - if err != nil { - // Send error msg back to chaincode. GetState will not trigger event + errHandler := func(err error, iter commonledger.ResultsIterator, errFmt string, errArgs ...interface{}) { + if iter != nil { + iter.Close() + handler.deleteQueryIterator(txContext, iterID) + } payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get ledger scan iterator. Sending %s", pb.ChaincodeMessage_ERROR) + chaincodeLogger.Errorf(errFmt, errArgs) serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + } + + rangeIter, err := txContext.txsimulator.GetStateRangeScanIterator(chaincodeID, getStateByRange.StartKey, getStateByRange.EndKey) + if err != nil { + errHandler(err, nil, "Failed to get ledger scan iterator. Sending %s", pb.ChaincodeMessage_ERROR) return } @@ -728,23 +735,14 @@ func (handler *Handler) handleGetStateByRange(msg *pb.ChaincodeMessage) { var payload *pb.QueryResponse payload, err = getQueryResponse(handler, txContext, rangeIter, iterID) if err != nil { - rangeIter.Close() - handler.deleteQueryIterator(txContext, iterID) - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler(err, rangeIter, "Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) return } var payloadBytes []byte payloadBytes, err = proto.Marshal(payload) if err != nil { - // Send error msg back to chaincode. GetState will not trigger event - rangeIter.Close() - handler.deleteQueryIterator(txContext, iterID) - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to marshal response. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler(err, rangeIter, "Failed to marshal response. Sending %s", pb.ChaincodeMessage_ERROR) return } chaincodeLogger.Debugf("Got keys and values. Sending %s", pb.ChaincodeMessage_RESPONSE) @@ -829,48 +827,47 @@ func (handler *Handler) handleQueryStateNext(msg *pb.ChaincodeMessage) { handler.serialSendAsync(serialSendMsg, nil) }() - queryStateNext := &pb.QueryStateNext{} + var txContext *transactionContext + var queryStateNext *pb.QueryStateNext + + errHandler := func(payload []byte, iter commonledger.ResultsIterator, errFmt string, errArgs ...interface{}) { + if iter != nil { + iter.Close() + handler.deleteQueryIterator(txContext, queryStateNext.Id) + } + chaincodeLogger.Errorf(errFmt, errArgs) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + } + + queryStateNext = &pb.QueryStateNext{} + unmarshalErr := proto.Unmarshal(msg.Payload, queryStateNext) if unmarshalErr != nil { - payload := []byte(unmarshalErr.Error()) - chaincodeLogger.Errorf("Failed to unmarshall state next query request. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(unmarshalErr.Error()), nil, "Failed to unmarshall state next query request. Sending %s", pb.ChaincodeMessage_ERROR) return } - txContext := handler.getTxContext(msg.Txid) + txContext = handler.getTxContext(msg.Txid) if txContext == nil { - payload := []byte("transaction context not found (timed out ?)") - chaincodeLogger.Errorf("[%s]Failed to get transaction context. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte("transaction context not found (timed out ?)"), nil, "[%s]Failed to get transaction context. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) return } queryIter := handler.getQueryIterator(txContext, queryStateNext.Id) if queryIter == nil { - payload := []byte("query iterator not found") - chaincodeLogger.Errorf("query iterator not found. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte("query iterator not found"), nil, "query iterator not found. Sending %s", pb.ChaincodeMessage_ERROR) return } payload, err := getQueryResponse(handler, txContext, queryIter, queryStateNext.Id) if err != nil { - queryIter.Close() - handler.deleteQueryIterator(txContext, queryStateNext.Id) - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), queryIter, "Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) return } payloadBytes, err := proto.Marshal(payload) if err != nil { - queryIter.Close() - handler.deleteQueryIterator(txContext, queryStateNext.Id) - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to marshal response. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), queryIter, "Failed to marshal response. Sending %s", pb.ChaincodeMessage_ERROR) return } chaincodeLogger.Debugf("Got keys and values. Sending %s", pb.ChaincodeMessage_RESPONSE) @@ -915,20 +912,21 @@ func (handler *Handler) handleQueryStateClose(msg *pb.ChaincodeMessage) { handler.serialSendAsync(serialSendMsg, nil) }() + errHandler := func(payload []byte, errFmt string, errArgs ...interface{}) { + chaincodeLogger.Errorf(errFmt, errArgs) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + } + queryStateClose := &pb.QueryStateClose{} unmarshalErr := proto.Unmarshal(msg.Payload, queryStateClose) if unmarshalErr != nil { - payload := []byte(unmarshalErr.Error()) - chaincodeLogger.Errorf("Failed to unmarshall state query close request. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(unmarshalErr.Error()), "Failed to unmarshall state query close request. Sending %s", pb.ChaincodeMessage_ERROR) return } txContext := handler.getTxContext(msg.Txid) if txContext == nil { - payload := []byte("transaction context not found (timed out ?)") - chaincodeLogger.Errorf("[%s]Failed to get transaction context. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte("transaction context not found (timed out ?)"), "[%s]Failed to get transaction context. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) return } @@ -941,11 +939,7 @@ func (handler *Handler) handleQueryStateClose(msg *pb.ChaincodeMessage) { payload := &pb.QueryResponse{HasMore: false, Id: queryStateClose.Id} payloadBytes, err := proto.Marshal(payload) if err != nil { - - // Send error msg back to chaincode. GetState will not trigger event - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed marshall resopnse. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), "Failed marshall resopnse. Sending %s", pb.ChaincodeMessage_ERROR) return } @@ -991,18 +985,26 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) { handler.serialSendAsync(serialSendMsg, nil) }() + var txContext *transactionContext + var iterID string + + errHandler := func(payload []byte, iter commonledger.ResultsIterator, errFmt string, errArgs ...interface{}) { + if iter != nil { + iter.Close() + handler.deleteQueryIterator(txContext, iterID) + } + chaincodeLogger.Errorf(errFmt, errArgs) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + } + getQueryResult := &pb.GetQueryResult{} unmarshalErr := proto.Unmarshal(msg.Payload, getQueryResult) if unmarshalErr != nil { - payload := []byte(unmarshalErr.Error()) - chaincodeLogger.Errorf("Failed to unmarshall query request. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(unmarshalErr.Error()), nil, "Failed to unmarshall query request. Sending %s", pb.ChaincodeMessage_ERROR) return } - iterID := util.GenerateUUID() - - var txContext *transactionContext + iterID = util.GenerateUUID() txContext, serialSendMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for GetQueryResult. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) if txContext == nil { @@ -1013,10 +1015,7 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) { executeIter, err := txContext.txsimulator.ExecuteQuery(chaincodeID, getQueryResult.Query) if err != nil { - // Send error msg back to chaincode. GetState will not trigger event - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get ledger query iterator. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), nil, "Failed to get ledger query iterator. Sending %s", pb.ChaincodeMessage_ERROR) return } @@ -1024,24 +1023,14 @@ func (handler *Handler) handleGetQueryResult(msg *pb.ChaincodeMessage) { var payload *pb.QueryResponse payload, err = getQueryResponse(handler, txContext, executeIter, iterID) if err != nil { - executeIter.Close() - handler.deleteQueryIterator(txContext, iterID) - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), executeIter, "Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) return } var payloadBytes []byte payloadBytes, err = proto.Marshal(payload) if err != nil { - executeIter.Close() - handler.deleteQueryIterator(txContext, iterID) - - // Send error msg back to chaincode. GetState will not trigger event - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed marshall response. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), executeIter, "Failed marshall response. Sending %s", pb.ChaincodeMessage_ERROR) return } @@ -1087,18 +1076,26 @@ func (handler *Handler) handleGetHistoryForKey(msg *pb.ChaincodeMessage) { handler.serialSendAsync(serialSendMsg, nil) }() + var iterID string + var txContext *transactionContext + + errHandler := func(payload []byte, iter commonledger.ResultsIterator, errFmt string, errArgs ...interface{}) { + if iter != nil { + iter.Close() + handler.deleteQueryIterator(txContext, iterID) + } + chaincodeLogger.Errorf(errFmt, errArgs) + serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + } + getHistoryForKey := &pb.GetHistoryForKey{} unmarshalErr := proto.Unmarshal(msg.Payload, getHistoryForKey) if unmarshalErr != nil { - payload := []byte(unmarshalErr.Error()) - chaincodeLogger.Errorf("Failed to unmarshall query request. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(unmarshalErr.Error()), nil, "Failed to unmarshall query request. Sending %s", pb.ChaincodeMessage_ERROR) return } - iterID := util.GenerateUUID() - - var txContext *transactionContext + iterID = util.GenerateUUID() txContext, serialSendMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for GetHistoryForKey. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) if txContext == nil { @@ -1108,10 +1105,7 @@ func (handler *Handler) handleGetHistoryForKey(msg *pb.ChaincodeMessage) { historyIter, err := txContext.historyQueryExecutor.GetHistoryForKey(chaincodeID, getHistoryForKey.Key) if err != nil { - // Send error msg back to chaincode. GetState will not trigger event - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get ledger history iterator. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), nil, "Failed to get ledger history iterator. Sending %s", pb.ChaincodeMessage_ERROR) return } @@ -1121,24 +1115,14 @@ func (handler *Handler) handleGetHistoryForKey(msg *pb.ChaincodeMessage) { payload, err = getQueryResponse(handler, txContext, historyIter, iterID) if err != nil { - historyIter.Close() - handler.deleteQueryIterator(txContext, iterID) - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), historyIter, "Failed to get query result. Sending %s", pb.ChaincodeMessage_ERROR) return } var payloadBytes []byte payloadBytes, err = proto.Marshal(payload) if err != nil { - historyIter.Close() - handler.deleteQueryIterator(txContext, iterID) - - // Send error msg back to chaincode. GetState will not trigger event - payload := []byte(err.Error()) - chaincodeLogger.Errorf("Failed marshal response. Sending %s", pb.ChaincodeMessage_ERROR) - serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), historyIter, "Failed marshal response. Sending %s", pb.ChaincodeMessage_ERROR) return } @@ -1181,6 +1165,11 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { return } + errHandler := func(payload []byte, errFmt string, errArgs ...interface{}) { + chaincodeLogger.Errorf(errFmt, errArgs) + triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + } + chaincodeID := handler.getCCRootName() var err error var res []byte @@ -1189,9 +1178,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { putStateInfo := &pb.PutStateInfo{} unmarshalErr := proto.Unmarshal(msg.Payload, putStateInfo) if unmarshalErr != nil { - payload := []byte(unmarshalErr.Error()) - chaincodeLogger.Debugf("[%s]Unable to decipher payload. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) - triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(unmarshalErr.Error()), "[%s]Unable to decipher payload. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) return } @@ -1207,9 +1194,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { chaincodeSpec := &pb.ChaincodeSpec{} unmarshalErr := proto.Unmarshal(msg.Payload, chaincodeSpec) if unmarshalErr != nil { - payload := []byte(unmarshalErr.Error()) - chaincodeLogger.Debugf("[%s]Unable to decipher payload. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) - triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(unmarshalErr.Error()), "[%s]Unable to decipher payload. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) return } @@ -1229,10 +1214,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { err := handler.checkACL(txContext.signedProp, txContext.proposal, calledCcIns) if err != nil { - chaincodeLogger.Errorf("[%s] C-call-C %s on channel %s failed check ACL [%v]: [%s]", - shorttxid(msg.Txid), calledCcIns.ChaincodeName, calledCcIns.ChainID, txContext.signedProp, err) - triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, - Payload: []byte(err.Error()), Txid: msg.Txid} + errHandler([]byte(err.Error()), "[%s] C-call-C %s on channel %s failed check ACL [%v]: [%s]", shorttxid(msg.Txid), calledCcIns.ChaincodeName, calledCcIns.ChainID, txContext.signedProp, err) return } @@ -1277,10 +1259,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { //Call LSCC to get the called chaincode artifacts cd, err = GetChaincodeDataFromLSCC(ctxt, msg.Txid, txContext.signedProp, txContext.proposal, calledCcIns.ChainID, calledCcIns.ChaincodeName) if err != nil { - payload := []byte(err.Error()) - chaincodeLogger.Debugf("[%s]Failed to get chaincoed data (%s) for invoked chaincode. Sending %s", - shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR) - triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), "[%s]Failed to get chaincoed data (%s) for invoked chaincode. Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR) return } } else { @@ -1298,10 +1277,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { cciSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec} _, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(ctxt, cccid, cciSpec) if launchErr != nil { - payload := []byte(launchErr.Error()) - chaincodeLogger.Debugf("[%s]Failed to launch invoked chaincode. Sending %s", - shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) - triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(launchErr.Error()), "[%s]Failed to launch invoked chaincode. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) return } @@ -1324,10 +1300,7 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { } if err != nil { - // Send error msg back to chaincode and trigger event - payload := []byte(err.Error()) - chaincodeLogger.Errorf("[%s]Failed to handle %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_ERROR) - triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + errHandler([]byte(err.Error()), "[%s]Failed to handle %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_ERROR) return } diff --git a/core/chaincode/multichains_test.go b/core/chaincode/multichains_test.go index 6090f453993..0c15bed16b1 100644 --- a/core/chaincode/multichains_test.go +++ b/core/chaincode/multichains_test.go @@ -26,6 +26,7 @@ import ( ) func TestExecuteInvokeOnManyChains(t *testing.T) { + testForSkip(t) //lets use 2 chains to test multi chains chains := []string{"chain1", "chain2"} lis, err := initPeer(chains...) diff --git a/core/chaincode/systemchaincode_test.go b/core/chaincode/systemchaincode_test.go index b3fb19cf42e..8cc3a7064c3 100644 --- a/core/chaincode/systemchaincode_test.go +++ b/core/chaincode/systemchaincode_test.go @@ -140,6 +140,7 @@ func deploySampleSysCC(t *testing.T, ctxt context.Context, chainID string) error // Test deploy of a transaction. func TestExecuteDeploySysChaincode(t *testing.T) { + testForSkip(t) sysccinfo, lis, err := initSysCCTests() if err != nil { t.Fail() @@ -171,6 +172,7 @@ func TestExecuteDeploySysChaincode(t *testing.T) { // Test multichains func TestMultichains(t *testing.T) { + testForSkip(t) sysccinfo, lis, err := initSysCCTests() if err != nil { t.Fail() diff --git a/core/chaincode/upgrade_test.go b/core/chaincode/upgrade_test.go index 6dd9faadb00..bfc1081fcb8 100644 --- a/core/chaincode/upgrade_test.go +++ b/core/chaincode/upgrade_test.go @@ -126,6 +126,7 @@ func upgrade2(ctx context.Context, cccid *ccprovider.CCContext, // re-initializtion of the same chaincode "mycc" // upgrade when "mycc" is up and running (test version based namespace) func TestUpgradeCC(t *testing.T) { + testForSkip(t) chainID := util.GetTestChainID() lis, err := initPeer(chainID) @@ -219,6 +220,7 @@ func TestUpgradeCC(t *testing.T) { // upgrade to exampl02 when "mycc" is not deployed // look for "not found" failure func TestInvalUpgradeCC(t *testing.T) { + testForSkip(t) chainID := util.GetTestChainID() lis, err := initPeer(chainID)