diff --git a/common/mocks/peer/mockccstream.go b/common/mocks/peer/mockccstream.go new file mode 100644 index 00000000000..f30cf5cee63 --- /dev/null +++ b/common/mocks/peer/mockccstream.go @@ -0,0 +1,158 @@ +/* +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 peer + +import ( + "fmt" + + pb "github.com/hyperledger/fabric/protos/peer" +) + +//MockResponseSet is used for processing CC to Peer comm +//such as GET/PUT/DEL state. The MockResponse contains the +//response to be returned for each input received.from the +//CC. Every stub call will generate a response +type MockResponseSet struct { + //DoneFunc is invoked when all I/O is done for this + //response set + DoneFunc func(int, error) + + //ErrorFunc is invoked at any step when the input does not + //match the received message + ErrorFunc func(int, error) + + //Responses contained the expected received message (optional) + //and response to send (optional) + Responses []*MockResponse +} + +//MockResponse contains the expected received message (optional) +//and response to send (optional) +type MockResponse struct { + RecvMsg *pb.ChaincodeMessage + RespMsg *pb.ChaincodeMessage +} + +// MockCCComm implements the mock communication between chaincode and peer +// We'd need two MockCCComm for communication. The receiver and sender will +// be switched between the two. +type MockCCComm struct { + name string + bailOnError bool + sendOnRecv *pb.ChaincodeMessage + recvStream chan *pb.ChaincodeMessage + sendStream chan *pb.ChaincodeMessage + respIndex int + respSet *MockResponseSet +} + +//Send sends a message +func (s *MockCCComm) Send(msg *pb.ChaincodeMessage) error { + s.sendStream <- msg + return nil +} + +//Recv receives a message +func (s *MockCCComm) Recv() (*pb.ChaincodeMessage, error) { + msg := <-s.recvStream + return msg, nil +} + +//CloseSend closes send +func (s *MockCCComm) CloseSend() error { + return nil +} + +//GetRecvStream returns the recvStream +func (s *MockCCComm) GetRecvStream() chan *pb.ChaincodeMessage { + return s.recvStream +} + +//GetSendStream returns the sendStream +func (s *MockCCComm) GetSendStream() chan *pb.ChaincodeMessage { + return s.sendStream +} + +//Quit closes the channels...this will also close chaincode side +func (s *MockCCComm) Quit() { + if s.recvStream != nil { + close(s.recvStream) + s.recvStream = nil + } + + if s.sendStream != nil { + close(s.sendStream) + s.sendStream = nil + } +} + +//SetBailOnError will cause Run to return on any error +func (s *MockCCComm) SetBailOnError(b bool) { + s.bailOnError = b +} + +//SetResponses sets responses for an Init or Invoke +func (s *MockCCComm) SetResponses(respSet *MockResponseSet) { + s.respSet = respSet + s.respIndex = 0 +} + +//Run receives and sends indefinitely +func (s *MockCCComm) Run() error { + for { + msg, err := s.Recv() + + if err != nil { + return err + } + + if err = s.respond(msg); err != nil { + if s.bailOnError { + return err + } + } + } +} + +func (s *MockCCComm) respond(msg *pb.ChaincodeMessage) error { + var err error + if s.respIndex < len(s.respSet.Responses) { + mockResp := s.respSet.Responses[s.respIndex] + if mockResp.RecvMsg != nil { + if msg.Type != mockResp.RecvMsg.Type { + if s.respSet.ErrorFunc != nil { + s.respSet.ErrorFunc(s.respIndex, fmt.Errorf("Invalid message expected %d received %d", int32(mockResp.RecvMsg.Type), int32(msg.Type))) + s.respIndex = s.respIndex + 1 + return nil + } + } + } + + if mockResp.RespMsg != nil { + err = s.Send(mockResp.RespMsg) + } + + s.respIndex = s.respIndex + 1 + + if s.respIndex == len(s.respSet.Responses) { + if s.respSet.DoneFunc != nil { + s.respSet.DoneFunc(s.respIndex, nil) + } + } + } + return err +} diff --git a/common/mocks/peer/mockpeerccsupport.go b/common/mocks/peer/mockpeerccsupport.go new file mode 100644 index 00000000000..7ddd02cd178 --- /dev/null +++ b/common/mocks/peer/mockpeerccsupport.go @@ -0,0 +1,77 @@ +/* +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 peer + +import ( + "fmt" + + pb "github.com/hyperledger/fabric/protos/peer" +) + +//MockPeerCCSupport provides CC support for peer interfaces. +type MockPeerCCSupport struct { + ccStream map[string]*MockCCComm +} + +//NewMockPeerSupport getsa mock peer support +func NewMockPeerSupport() *MockPeerCCSupport { + return &MockPeerCCSupport{ccStream: make(map[string]*MockCCComm)} +} + +//AddCC adds a cc to the MockPeerCCSupport +func (mp *MockPeerCCSupport) AddCC(name string, recv chan *pb.ChaincodeMessage, send chan *pb.ChaincodeMessage) (*MockCCComm, error) { + if mp.ccStream[name] != nil { + return nil, fmt.Errorf("CC %s already added", name) + } + mcc := &MockCCComm{name: name, recvStream: recv, sendStream: send} + mp.ccStream[name] = mcc + return mcc, nil +} + +//GetCC gets a cc from the MockPeerCCSupport +func (mp *MockPeerCCSupport) GetCC(name string) (*MockCCComm, error) { + s := mp.ccStream[name] + if s == nil { + return nil, fmt.Errorf("CC %s not added", name) + } + return s, nil +} + +//GetCCMirror creates a MockCCStream with streans switched +func (mp *MockPeerCCSupport) GetCCMirror(name string) *MockCCComm { + s := mp.ccStream[name] + if s == nil { + return nil + } + + return &MockCCComm{name: name, recvStream: s.sendStream, sendStream: s.recvStream} +} + +//RemoveCC removes a cc +func (mp *MockPeerCCSupport) RemoveCC(name string) error { + if mp.ccStream[name] == nil { + return fmt.Errorf("CC %s not added", name) + } + delete(mp.ccStream, name) + return nil +} + +//RemoveAll removes all ccs +func (mp *MockPeerCCSupport) RemoveAll() error { + mp.ccStream = make(map[string]*MockCCComm) + return nil +} diff --git a/core/chaincode/shim/chaincode.go b/core/chaincode/shim/chaincode.go index 3662ba0e056..0c9ca7522f5 100644 --- a/core/chaincode/shim/chaincode.go +++ b/core/chaincode/shim/chaincode.go @@ -72,18 +72,15 @@ type ChaincodeStub struct { // Peer address derived from command line or env var var peerAddress string -// Start is the entry point for chaincodes bootstrap. It is not an API for -// chaincodes. -func Start(cc Chaincode) error { - // If Start() is called, we assume this is a standalone chaincode and set - // up formatted logging. - SetupChaincodeLogging() +//this separates the chaincode stream interface establishment +//so we can replace it with a mock peer stream +type peerStreamGetter func(name string) (PeerChaincodeStream, error) - err := factory.InitFactories(&factory.DefaultOpts) - if err != nil { - return fmt.Errorf("Internal error, BCCSP could not be initialized with default options: %s", err) - } +//UTs to setup mock peer stream getter +var streamGetter peerStreamGetter +//the non-mock user CC stream establishment func +func userChaincodeStreamGetter(name string) (PeerChaincodeStream, error) { flag.StringVar(&peerAddress, "peer.address", "", "peer address") flag.Parse() @@ -94,7 +91,7 @@ func Start(cc Chaincode) error { clientConn, err := newPeerClientConnection() if err != nil { chaincodeLogger.Errorf("Error trying to connect to local peer: %s", err) - return fmt.Errorf("Error trying to connect to local peer: %s", err) + return nil, fmt.Errorf("Error trying to connect to local peer: %s", err) } chaincodeLogger.Debugf("os.Args returns: %s", os.Args) @@ -104,13 +101,38 @@ func Start(cc Chaincode) error { // Establish stream with validating peer stream, err := chaincodeSupportClient.Register(context.Background()) if err != nil { - return fmt.Errorf("Error chatting with leader at address=%s: %s", getPeerAddress(), err) + return nil, fmt.Errorf("Error chatting with leader at address=%s: %s", getPeerAddress(), err) } + return stream, nil +} + +// chaincodes. +func Start(cc Chaincode) error { + // If Start() is called, we assume this is a standalone chaincode and set + // up formatted logging. + SetupChaincodeLogging() + chaincodename := viper.GetString("chaincode.id.name") if chaincodename == "" { return fmt.Errorf("Error chaincode id not provided") } + + err := factory.InitFactories(&factory.DefaultOpts) + if err != nil { + return fmt.Errorf("Internal error, BCCSP could not be initialized with default options: %s", err) + } + + //mock stream not set up ... get real stream + if streamGetter == nil { + streamGetter = userChaincodeStreamGetter + } + + stream, err := streamGetter(chaincodename) + if err != nil { + return err + } + err = chatWithPeer(chaincodename, stream, cc) return err @@ -187,8 +209,9 @@ func StartInProc(env []string, args []string, cc Chaincode, recv <-chan *pb.Chai if chaincodename == "" { return fmt.Errorf("Error chaincode id not provided") } - chaincodeLogger.Debugf("starting chat with peer using name=%s", chaincodename) + stream := newInProcStream(recv, send) + chaincodeLogger.Debugf("starting chat with peer using name=%s", chaincodename) err := chatWithPeer(chaincodename, stream, cc) return err } diff --git a/core/chaincode/shim/handler.go b/core/chaincode/shim/handler.go index c8ded45fb16..8fd807a6624 100644 --- a/core/chaincode/shim/handler.go +++ b/core/chaincode/shim/handler.go @@ -341,19 +341,6 @@ func (handler *Handler) beforeTransaction(e *fsm.Event) { } } -// afterCompleted will need to handle COMPLETED event by sending message to the peer -func (handler *Handler) afterCompleted(e *fsm.Event) { - msg, ok := e.Args[0].(*pb.ChaincodeMessage) - if !ok { - e.Cancel(fmt.Errorf("Received unexpected message type")) - return - } - chaincodeLogger.Debugf("[%s]sending COMPLETED to validator for tid", shorttxid(msg.Txid)) - if err := handler.serialSend(msg); err != nil { - e.Cancel(fmt.Errorf("send COMPLETED failed %s", err)) - } -} - // afterResponse is called to deliver a response or error to the chaincode stub. func (handler *Handler) afterResponse(e *fsm.Event) { msg, ok := e.Args[0].(*pb.ChaincodeMessage) diff --git a/core/chaincode/shim/mockstub_test.go b/core/chaincode/shim/mockstub_test.go index fe086e74944..6f511da9130 100644 --- a/core/chaincode/shim/mockstub_test.go +++ b/core/chaincode/shim/mockstub_test.go @@ -234,3 +234,39 @@ func TestGetTxTimestamp(t *testing.T) { stub.MockTransactionEnd("init") } + +//TestMockMock clearly cheating for coverage... but not. Mock should +//be tucked away under common/mocks package which is not +//included for coverage. Moving mockstub to another package +//will cause upheaval in other code best dealt with separately +//For now, call all the methods to get mock covered in this +//package +func TestMockMock(t *testing.T) { + stub := NewMockStub("MOCKMOCK", &shimTestCC{}) + stub.args = [][]byte{[]byte("a"), []byte("b")} + stub.MockInit("id", nil) + stub.GetArgs() + stub.GetStringArgs() + stub.GetFunctionAndParameters() + stub.GetTxID() + stub.MockInvoke("id", nil) + stub.MockInvokeWithSignedProposal("id", nil, nil) + stub.DelState("dummy") + stub.GetStateByRange("start", "end") + stub.GetQueryResult("q") + stub2 := NewMockStub("othercc", &shimTestCC{}) + stub.MockPeerChaincode("othercc/mychan", stub2) + stub.InvokeChaincode("othercc", nil, "mychan") + stub.GetCreator() + stub.GetTransient() + stub.GetBinding() + stub.GetSignedProposal() + stub.GetArgsSlice() + stub.SetEvent("e", nil) + stub.GetHistoryForKey("k") + iter := &MockStateRangeQueryIterator{} + iter.HasNext() + iter.Close() + getBytes("f", []string{"a", "b"}) + getFuncArgs([][]byte{[]byte("a")}) +} diff --git a/core/chaincode/shim/shim_test.go b/core/chaincode/shim/shim_test.go index f74b70d3d13..6aae2c1f81e 100644 --- a/core/chaincode/shim/shim_test.go +++ b/core/chaincode/shim/shim_test.go @@ -17,9 +17,18 @@ limitations under the License. package shim import ( + "bytes" "os" + "strconv" "strings" "testing" + "time" + + mockpeer "github.com/hyperledger/fabric/common/mocks/peer" + "github.com/hyperledger/fabric/common/util" + lproto "github.com/hyperledger/fabric/protos/ledger/queryresult" + pb "github.com/hyperledger/fabric/protos/peer" + "github.com/hyperledger/fabric/protos/utils" "github.com/hyperledger/fabric/common/flogging" "github.com/op/go-logging" @@ -27,6 +36,320 @@ import ( "github.com/stretchr/testify/assert" ) +// shimTestCC example simple Chaincode implementation +type shimTestCC struct { +} + +func (t *shimTestCC) Init(stub ChaincodeStubInterface) pb.Response { + _, args := stub.GetFunctionAndParameters() + var A, B string // Entities + var Aval, Bval int // Asset holdings + var err error + + if len(args) != 4 { + return Error("Incorrect number of arguments. Expecting 4") + } + + // Initialize the chaincode + A = args[0] + Aval, err = strconv.Atoi(args[1]) + if err != nil { + return Error("Expecting integer value for asset holding") + } + B = args[2] + Bval, err = strconv.Atoi(args[3]) + if err != nil { + return Error("Expecting integer value for asset holding") + } + + // Write the state to the ledger + err = stub.PutState(A, []byte(strconv.Itoa(Aval))) + if err != nil { + return Error(err.Error()) + } + + err = stub.PutState(B, []byte(strconv.Itoa(Bval))) + if err != nil { + return Error(err.Error()) + } + + return Success(nil) +} + +func (t *shimTestCC) Invoke(stub ChaincodeStubInterface) pb.Response { + function, args := stub.GetFunctionAndParameters() + if function == "invoke" { + // Make payment of X units from A to B + return t.invoke(stub, args) + } else if function == "delete" { + // Deletes an entity from its state + return t.delete(stub, args) + } else if function == "query" { + // the old "Query" is now implemtned in invoke + return t.query(stub, args) + } else if function == "cc2cc" { + return t.cc2cc(stub, args) + } else if function == "rangeq" { + return t.rangeq(stub, args) + } else if function == "historyq" { + return t.historyq(stub, args) + } else if function == "richq" { + return t.richq(stub, args) + } + + return Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") +} + +// Transaction makes payment of X units from A to B +func (t *shimTestCC) invoke(stub ChaincodeStubInterface, args []string) pb.Response { + var A, B string // Entities + var Aval, Bval int // Asset holdings + var X int // Transaction value + var err error + + if len(args) != 3 { + return Error("Incorrect number of arguments. Expecting 3") + } + + A = args[0] + B = args[1] + + // Get the state from the ledger + // TODO: will be nice to have a GetAllState call to ledger + Avalbytes, err := stub.GetState(A) + if err != nil { + return Error("Failed to get state") + } + if Avalbytes == nil { + return Error("Entity not found") + } + Aval, _ = strconv.Atoi(string(Avalbytes)) + + Bvalbytes, err := stub.GetState(B) + if err != nil { + return Error("Failed to get state") + } + if Bvalbytes == nil { + return Error("Entity not found") + } + Bval, _ = strconv.Atoi(string(Bvalbytes)) + + // Perform the execution + X, err = strconv.Atoi(args[2]) + if err != nil { + return Error("Invalid transaction amount, expecting a integer value") + } + Aval = Aval - X + Bval = Bval + X + + // Write the state back to the ledger + err = stub.PutState(A, []byte(strconv.Itoa(Aval))) + if err != nil { + return Error(err.Error()) + } + + err = stub.PutState(B, []byte(strconv.Itoa(Bval))) + if err != nil { + return Error(err.Error()) + } + + return Success(nil) +} + +// Deletes an entity from state +func (t *shimTestCC) delete(stub ChaincodeStubInterface, args []string) pb.Response { + if len(args) != 1 { + return Error("Incorrect number of arguments. Expecting 1") + } + + A := args[0] + + // Delete the key from the state in ledger + err := stub.DelState(A) + if err != nil { + return Error("Failed to delete state") + } + + return Success(nil) +} + +// query callback representing the query of a chaincode +func (t *shimTestCC) query(stub ChaincodeStubInterface, args []string) pb.Response { + var A string // Entities + var err error + + if len(args) != 1 { + return Error("Incorrect number of arguments. Expecting name of the person to query") + } + + A = args[0] + + // Get the state from the ledger + Avalbytes, err := stub.GetState(A) + if err != nil { + jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" + return Error(jsonResp) + } + + if Avalbytes == nil { + jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}" + return Error(jsonResp) + } + + return Success(Avalbytes) +} + +// ccc2cc call +func (t *shimTestCC) cc2cc(stub ChaincodeStubInterface, args []string) pb.Response { + if len(args) < 1 { + return Error("Invalid number of args for cc2cc. expecting at least 1") + } + return stub.InvokeChaincode(args[0], util.ToChaincodeArgs(args...), "") +} + +// rangeq calls range query +func (t *shimTestCC) rangeq(stub ChaincodeStubInterface, args []string) pb.Response { + if len(args) != 2 { + return Error("Incorrect number of arguments. Expecting keys for range query") + } + + A := args[0] + B := args[0] + + // Get the state from the ledger + resultsIterator, err := stub.GetStateByRange(A, B) + if err != nil { + return Error(err.Error()) + } + defer resultsIterator.Close() + + // buffer is a JSON array containing QueryResults + var buffer bytes.Buffer + buffer.WriteString("[") + + bArrayMemberAlreadyWritten := false + for resultsIterator.HasNext() { + queryResponse, err := resultsIterator.Next() + if err != nil { + return Error(err.Error()) + } + // Add a comma before array members, suppress it for the first array member + if bArrayMemberAlreadyWritten == true { + buffer.WriteString(",") + } + buffer.WriteString("{\"Key\":") + buffer.WriteString("\"") + buffer.WriteString(queryResponse.Key) + buffer.WriteString("\"") + + buffer.WriteString(", \"Record\":") + // Record is a JSON object, so we write as-is + buffer.WriteString(string(queryResponse.Value)) + buffer.WriteString("}") + bArrayMemberAlreadyWritten = true + } + buffer.WriteString("]") + + return Success(buffer.Bytes()) +} + +// richq calls tichq query +func (t *shimTestCC) richq(stub ChaincodeStubInterface, args []string) pb.Response { + if len(args) != 1 { + return Error("Incorrect number of arguments. Expecting keys for range query") + } + + query := args[0] + + // Get the state from the ledger + resultsIterator, err := stub.GetQueryResult(query) + if err != nil { + return Error(err.Error()) + } + defer resultsIterator.Close() + + // buffer is a JSON array containing QueryResults + var buffer bytes.Buffer + buffer.WriteString("[") + + bArrayMemberAlreadyWritten := false + for resultsIterator.HasNext() { + queryResponse, err := resultsIterator.Next() + if err != nil { + return Error(err.Error()) + } + // Add a comma before array members, suppress it for the first array member + if bArrayMemberAlreadyWritten == true { + buffer.WriteString(",") + } + buffer.WriteString("{\"Key\":") + buffer.WriteString("\"") + buffer.WriteString(queryResponse.Key) + buffer.WriteString("\"") + + buffer.WriteString(", \"Record\":") + // Record is a JSON object, so we write as-is + buffer.WriteString(string(queryResponse.Value)) + buffer.WriteString("}") + bArrayMemberAlreadyWritten = true + } + buffer.WriteString("]") + + return Success(buffer.Bytes()) +} + +// rangeq calls range query +func (t *shimTestCC) historyq(stub ChaincodeStubInterface, args []string) pb.Response { + if len(args) < 1 { + return Error("Incorrect number of arguments. Expecting 1") + } + + key := args[0] + + resultsIterator, err := stub.GetHistoryForKey(key) + if err != nil { + return Error(err.Error()) + } + defer resultsIterator.Close() + + var buffer bytes.Buffer + buffer.WriteString("[") + + bArrayMemberAlreadyWritten := false + for resultsIterator.HasNext() { + response, err := resultsIterator.Next() + if err != nil { + return Error(err.Error()) + } + // Add a comma before array members, suppress it for the first array member + if bArrayMemberAlreadyWritten == true { + buffer.WriteString(",") + } + buffer.WriteString("{\"TxId\":") + buffer.WriteString("\"") + buffer.WriteString(response.TxId) + buffer.WriteString("\"") + + buffer.WriteString(", \"Value\":") + if response.IsDelete { + buffer.WriteString("null") + } else { + buffer.WriteString(string(response.Value)) + } + + buffer.WriteString(", \"IsDelete\":") + buffer.WriteString("\"") + buffer.WriteString(strconv.FormatBool(response.IsDelete)) + buffer.WriteString("\"") + + buffer.WriteString("}") + bArrayMemberAlreadyWritten = true + } + buffer.WriteString("]") + + return Success(buffer.Bytes()) +} + // Test Go shim functionality that can be tested outside of a real chaincode // context. @@ -194,3 +517,384 @@ func TestSetupChaincodeLogging_shim(t *testing.T) { }) } } + +//store the stream CC mappings here +var mockPeerCCSupport = mockpeer.NewMockPeerSupport() + +func mockChaincodeStreamGetter(name string) (PeerChaincodeStream, error) { + return mockPeerCCSupport.GetCC(name) +} + +func setupcc(name string, cc Chaincode) *mockpeer.MockCCComm { + viper.Set("chaincode.id.name", name) + send := make(chan *pb.ChaincodeMessage) + recv := make(chan *pb.ChaincodeMessage) + mockPeerCCSupport.AddCC(name, recv, send) + return 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) + } + } +} + +//TestInvoke tests init and invoke along with many of the stub functions +//such as get/put/del/range... +func TestInvoke(t *testing.T) { + streamGetter = mockChaincodeStreamGetter + cc := &shimTestCC{} + //viper.Set("chaincode.logging.shim", "debug") + var err error + ccname := "shimTestCC" + peerSide := setupcc(ccname, cc) + defer mockPeerCCSupport.RemoveCC(ccname) + //start the shim+chaincode + go func() { + err = Start(cc) + }() + + 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_REGISTER}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}}}} + peerSide.SetResponses(respSet) + err = peerSide.Run() + }() + + //wait for init + processDone(t, done, false) + + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY, Txid: "1"}) + + ci := &pb.ChaincodeInput{[][]byte{[]byte("init"), []byte("A"), []byte("100"), []byte("B"), []byte("200")}} + payload := utils.MarshalOrPanic(ci) + respSet := &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "2"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "2"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "2"}, nil}}} + peerSide.SetResponses(respSet) + + //use the payload computed from prev init + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INIT, Payload: payload, Txid: "2"}) + + //wait for done + processDone(t, done, false) + + //good invoke + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Txid: "3"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: []byte("100"), Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Txid: "3"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: []byte("200"), Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "3"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "3"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "3"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "3"}) + + //wait for done + processDone(t, done, false) + + //bad put + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Txid: "3a"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: []byte("100"), Txid: "3a"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Txid: "3a"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: []byte("200"), Txid: "3a"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "3a"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Txid: "3a"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "3a"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "3a"}) + + //wait for done + processDone(t, done, false) + + //bad get + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Txid: "3b"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Txid: "3b"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "3b"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("invoke"), []byte("A"), []byte("B"), []byte("10")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "3b"}) + + //wait for done + processDone(t, done, false) + + //bad delete + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_DEL_STATE, Txid: "4"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Txid: "4"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "4"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("delete"), []byte("A")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "4"}) + + //wait for done + processDone(t, done, false) + + //good delete + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_DEL_STATE, Txid: "4a"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "4a"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "4a"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("delete"), []byte("A")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "4a"}) + + //wait for done + processDone(t, done, false) + + //bad invoke + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "5"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("badinvoke")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "5"}) + + //wait for done + processDone(t, done, false) + + //range query + + //create the response + rangeQueryResponse := &pb.QueryResponse{Results: []*pb.QueryResultBytes{ + &pb.QueryResultBytes{ResultBytes: utils.MarshalOrPanic(&lproto.KV{"getputcc", "A", []byte("100")})}, + &pb.QueryResultBytes{ResultBytes: utils.MarshalOrPanic(&lproto.KV{"getputcc", "B", []byte("200")})}}, + HasMore: true} + payload = utils.MarshalOrPanic(rangeQueryResponse) + + //create the next response + rangeQueryNext := &pb.QueryResponse{Results: nil, HasMore: false} + + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE_BY_RANGE, Txid: "6"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: payload, Txid: "6"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Txid: "6"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: utils.MarshalOrPanic(rangeQueryNext), Txid: "6"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Txid: "6"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "6"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "6"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("rangeq"), []byte("A"), []byte("B")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "6"}) + + //wait for done + processDone(t, done, false) + + //error range query + + //create the response + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE_BY_RANGE, Txid: "6a"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: "6a"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "6a"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("rangeq"), []byte("A"), []byte("B")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "6a"}) + + //wait for done + processDone(t, done, false) + + //history query + + //create the response + historyQueryResponse := &pb.QueryResponse{Results: []*pb.QueryResultBytes{ + &pb.QueryResultBytes{ResultBytes: utils.MarshalOrPanic(&lproto.KeyModification{TxId: "6", Value: []byte("100")})}}, + HasMore: true} + payload = utils.MarshalOrPanic(historyQueryResponse) + + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY, Txid: "7"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: payload, Txid: "7"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Txid: "7"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: utils.MarshalOrPanic(rangeQueryNext), Txid: "7"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Txid: "7"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "7"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "7"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("historyq"), []byte("A")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "7"}) + + //wait for done + processDone(t, done, false) + + //error history query + + //create the response + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY, Txid: "7a"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: "7a"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "7a"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("historyq"), []byte("A")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "7a"}) + + //wait for done + processDone(t, done, false) + + //query result + + //create the response + getQRResp := &pb.QueryResponse{Results: []*pb.QueryResultBytes{ + &pb.QueryResultBytes{ResultBytes: utils.MarshalOrPanic(&lproto.KV{"getputcc", "A", []byte("100")})}, + &pb.QueryResultBytes{ResultBytes: utils.MarshalOrPanic(&lproto.KV{"getputcc", "B", []byte("200")})}}, + HasMore: true} + payload = utils.MarshalOrPanic(getQRResp) + + //create the next response + rangeQueryNext = &pb.QueryResponse{Results: nil, HasMore: false} + + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_QUERY_RESULT, Txid: "8"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: payload, Txid: "8"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Txid: "8"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: utils.MarshalOrPanic(rangeQueryNext), Txid: "8"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Txid: "8"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "8"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "8"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("richq"), []byte("A")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "8"}) + + //wait for done + processDone(t, done, false) + + time.Sleep(1 * time.Second) + peerSide.Quit() +} + +func TestStartInProc(t *testing.T) { + streamGetter = mockChaincodeStreamGetter + cc := &shimTestCC{} + var err error + ccname := "shimTestCC" + peerSide := setupcc(ccname, cc) + defer mockPeerCCSupport.RemoveCC(ccname) + + done := setuperror() + + doneFunc := func(ind int, err error) { + done <- err + } + + //start the mock peer + go func() { + respSet := &mockpeer.MockResponseSet{doneFunc, nil, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}}}} + peerSide.SetResponses(respSet) + err = peerSide.Run() + }() + + //start the shim+chaincode + go func() { + err = StartInProc([]string{"CORE_CHAINCODE_ID_NAME=shimTestCC", "CORE_CHAINCODE_LOGGING_SHIM=debug"}, nil, cc, peerSide.GetSendStream(), peerSide.GetRecvStream()) + }() + + //wait for init + processDone(t, done, false) + + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY, Txid: "1"}) + + time.Sleep(1 * time.Second) + peerSide.Quit() +} + +func TestCC2CC(t *testing.T) { + streamGetter = mockChaincodeStreamGetter + cc := &shimTestCC{} + //viper.Set("chaincode.logging.shim", "debug") + var err error + ccname := "shimTestCC" + peerSide := setupcc(ccname, cc) + defer mockPeerCCSupport.RemoveCC(ccname) + //start the shim+chaincode + go func() { + err = Start(cc) + }() + + 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_REGISTER}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}}}} + peerSide.SetResponses(respSet) + err = peerSide.Run() + }() + + //wait for init + processDone(t, done, false) + + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY, Txid: "1"}) + + ci := &pb.ChaincodeInput{[][]byte{[]byte("init"), []byte("A"), []byte("100"), []byte("B"), []byte("200")}} + payload := utils.MarshalOrPanic(ci) + respSet := &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "2"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Txid: "2"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: "2"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "2"}, nil}}} + peerSide.SetResponses(respSet) + + //use the payload computed from prev init + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INIT, Payload: payload, Txid: "2"}) + + //wait for done + processDone(t, done, false) + + //cc2cc + innerResp := utils.MarshalOrPanic(&pb.Response{Status: OK, Payload: []byte("CC2CC rocks")}) + cc2ccresp := utils.MarshalOrPanic(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: innerResp}) + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Txid: "3"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: cc2ccresp, Txid: "3"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "3"}, nil}}} + peerSide.SetResponses(respSet) + + ci = &pb.ChaincodeInput{[][]byte{[]byte("cc2cc"), []byte("othercc"), []byte("arg1"), []byte("arg2")}} + payload = utils.MarshalOrPanic(ci) + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "3"}) + + //wait for done + processDone(t, done, false) + + //error response cc2cc + respSet = &mockpeer.MockResponseSet{errorFunc, errorFunc, []*mockpeer.MockResponse{ + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Txid: "4"}, &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: cc2ccresp, Txid: "4"}}, + &mockpeer.MockResponse{&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Txid: "4"}, nil}}} + peerSide.SetResponses(respSet) + + peerSide.Send(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_TRANSACTION, Payload: payload, Txid: "4"}) + + //wait for done + processDone(t, done, false) + + time.Sleep(1 * time.Second) + peerSide.Quit() +}