From 1bd5b2bb8e37729f13e819c291cbf0125366c488 Mon Sep 17 00:00:00 2001 From: "Binh Q. Nguyen" Date: Fri, 27 Jan 2017 14:04:53 -0500 Subject: [PATCH] [FAB-1790, FAB-1791] Chaincode calling chaincode This changeset enables chaincode calling chaincode on the same channel or different channel read-only. The called chaincode operates in the same transaction context of the caller chaincode. See the chaincode API for details. Change shim.InvokeChaincode API to accept a channel. Fix up the chaincode_example04 so that the current unit tests can exercise both query and invoke. Also update a number of files for better logging, including removing repeating log in loop and info log in heavy transaction path. Change-Id: I9b67b91311482d7c47cf66a0849c2f29594621f5 Signed-off-by: Binh Q. Nguyen --- core/chaincode/chaincode_support.go | 9 +- core/chaincode/exectransaction_test.go | 4 +- core/chaincode/handler.go | 182 ++++++++++++------ core/chaincode/shim/chaincode.go | 18 +- core/chaincode/shim/interfaces.go | 8 +- core/chaincode/shim/mockstub.go | 16 +- core/scc/lccc/lccc.go | 2 +- .../chaincode_example04.go | 32 ++- .../chaincode_example05.go | 8 +- examples/chaincode/go/passthru/passthru.go | 2 +- .../src/main/java/example/LinkExample.java | 6 +- gossip/state/state.go | 2 - msp/identities.go | 18 +- msp/mgmt/config.go | 22 +++ 14 files changed, 231 insertions(+), 98 deletions(-) diff --git a/core/chaincode/chaincode_support.go b/core/chaincode/chaincode_support.go index 6cded0eb330..a8c71e4392f 100644 --- a/core/chaincode/chaincode_support.go +++ b/core/chaincode/chaincode_support.go @@ -428,6 +428,9 @@ func (chaincodeSupport *ChaincodeSupport) Stop(context context.Context, cccid *c //stop the chaincode sir := container.StopImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, ChainID: cccid.ChainID, Version: cccid.Version}, Timeout: 0} + // The line below is left for debugging. It replaces the line above to keep + // the chaincode container around to give you a chance to get data + //sir := container.StopImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, ChainID: cccid.ChainID, Version: cccid.Version}, Timeout: 0, Dontremove: true} vmtype, _ := chaincodeSupport.getVMType(cds) @@ -490,7 +493,9 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid return cID, cMsg, err } if chrte.handler.isRunning() { - chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", canName) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", canName) + } chaincodeSupport.runningChaincodes.Unlock() return cID, cMsg, nil } @@ -662,7 +667,7 @@ func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, cccid *c return ccresp, err } -// Returns true if the peer was configured with development-mode enabled +// IsDevMode returns true if the peer was configured with development-mode enabled func IsDevMode() bool { mode := viper.GetString("chaincode.mode") diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index b24dafe0853..ee582b80fcc 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -519,7 +519,7 @@ func checkFinalState(cccid *ccprovider.CCContext) error { return fmt.Errorf("Error retrieving state from ledger for <%s>: %s", cName, resErr) } if Aval != 90 { - return fmt.Errorf("Incorrect result. Aval is wrong for <%s>", cName) + return fmt.Errorf("Incorrect result. Aval %d != 90 <%s>", Aval, cName) } resbytes, resErr = txsim.GetState(cccid.Name, "b") @@ -531,7 +531,7 @@ func checkFinalState(cccid *ccprovider.CCContext) error { return fmt.Errorf("Error retrieving state from ledger for <%s>: %s", cName, resErr) } if Bval != 210 { - return fmt.Errorf("Incorrect result. Bval is wrong for <%s>", cName) + return fmt.Errorf("Incorrect result. Bval %d != 210 <%s>", Bval, cName) } // Success diff --git a/core/chaincode/handler.go b/core/chaincode/handler.go index b1b7103b22f..68a0a367a9c 100644 --- a/core/chaincode/handler.go +++ b/core/chaincode/handler.go @@ -28,6 +28,7 @@ import ( "github.com/hyperledger/fabric/core/common/ccprovider" ccintf "github.com/hyperledger/fabric/core/container/ccintf" "github.com/hyperledger/fabric/core/ledger" + "github.com/hyperledger/fabric/core/peer" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/looplab/fsm" @@ -114,14 +115,18 @@ func shorttxid(txid string) string { //and suffix will just be absent (also note that LCCC reserves //"/:[]${}" as special chars mainly for such namespace uses) func (handler *Handler) decomposeRegisteredName(cid *pb.ChaincodeID) { - handler.ccCompParts = &ccParts{} - b := []byte(cid.Name) + handler.ccCompParts = chaincodeIDParts(cid.Name) +} + +func chaincodeIDParts(ccName string) *ccParts { + b := []byte(ccName) + p := &ccParts{} //compute suffix (ie, chain name) i := bytes.IndexByte(b, '/') if i >= 0 { if i < len(b)-1 { - handler.ccCompParts.suffix = string(b[i+1:]) + p.suffix = string(b[i+1:]) } b = b[:i] } @@ -130,14 +135,14 @@ func (handler *Handler) decomposeRegisteredName(cid *pb.ChaincodeID) { i = bytes.IndexByte(b, ':') if i >= 0 { if i < len(b)-1 { - handler.ccCompParts.version = string(b[i+1:]) + p.version = string(b[i+1:]) } b = b[:i] } + // remaining is the chaincode name + p.name = string(b) - handler.ccCompParts.name = string(b) - - return + return p } func (handler *Handler) getCCRootName() string { @@ -221,6 +226,16 @@ func (handler *Handler) deleteQueryIterator(txContext *transactionContext, txid delete(txContext.queryIteratorMap, txid) } +// Check if the transactor is allow to call this chaincode on this channel +func (handler *Handler) checkACL(proposal *pb.Proposal, calledCC *ccParts) *pb.ChaincodeMessage { + // TODO: Decide what to pass in to verify that this transactor can access this + // channel (chID) and chaincode (ccID). Very likely we need the signedProposal + // which contains the sig and creator cert + + // If error, return ChaincodeMessage with type ChaincodeMessage_ERROR + return nil +} + //THIS CAN BE REMOVED ONCE WE FULL SUPPORT (Invoke) CONFIDENTIALITY WITH CC-CALLING-CC //Only invocation are allowed func (handler *Handler) canCallChaincode(txid string, isQuery bool) *pb.ChaincodeMessage { @@ -564,41 +579,50 @@ func (handler *Handler) handleGetState(msg *pb.ChaincodeMessage) { } var serialSendMsg *pb.ChaincodeMessage + var txContext *transactionContext + txContext, serialSendMsg = handler.isValidTxSim(msg.Txid, + "[%s]No ledger context for GetState. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) defer func() { handler.deleteTXIDEntry(msg.Txid) - chaincodeLogger.Debugf("[%s]handleGetState serial send %s", shorttxid(serialSendMsg.Txid), serialSendMsg.Type) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s]handleGetState serial send %s", + shorttxid(serialSendMsg.Txid), serialSendMsg.Type) + } handler.serialSendAsync(serialSendMsg, nil) }() - key := string(msg.Payload) + if txContext == nil { + return + } - // Invoke ledger to get state + key := string(msg.Payload) chaincodeID := handler.getCCRootName() + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s] getting state for chaincode %s, key %s, channel %s", + shorttxid(msg.Txid), chaincodeID, key, txContext.chainID) + } var res []byte var err error - var txContext *transactionContext - - txContext, serialSendMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for GetState. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR) - if txContext == nil { - return - } - res, err = txContext.txsimulator.GetState(chaincodeID, key) if err != nil { // Send error msg back to chaincode. GetState will not trigger event payload := []byte(err.Error()) - chaincodeLogger.Errorf("[%s]Failed to get chaincode state(%s). Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR) + chaincodeLogger.Errorf("[%s]Failed to get chaincode state(%s). Sending %s", + shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR) serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} } else if res == nil { //The state object being requested does not exist - chaincodeLogger.Debugf("[%s]No state associated with key: %s. Sending %s with an empty payload", shorttxid(msg.Txid), key, pb.ChaincodeMessage_RESPONSE) + chaincodeLogger.Debugf("[%s]No state associated with key: %s. Sending %s with an empty payload", + shorttxid(msg.Txid), key, pb.ChaincodeMessage_RESPONSE) serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid} } else { // Send response msg back to chaincode. GetState will not trigger event - chaincodeLogger.Debugf("[%s]Got state. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_RESPONSE) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s]Got state. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_RESPONSE) + } serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid} } @@ -677,7 +701,7 @@ func (handler *Handler) handleRangeQueryState(msg *pb.ChaincodeMessage) { var i = uint32(0) var qresult ledger.QueryResult for ; i < maxRangeQueryStateLimit; i++ { - qresult, err := rangeIter.Next() + qresult, err = rangeIter.Next() if err != nil { chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR) return @@ -952,7 +976,7 @@ func (handler *Handler) handleExecuteQueryState(msg *pb.ChaincodeMessage) { var i = uint32(0) var qresult ledger.QueryResult for ; i < maxExecuteQueryStateLimit; i++ { - qresult, err := executeIter.Next() + qresult, err = executeIter.Next() if err != nil { chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR) return @@ -970,8 +994,9 @@ func (handler *Handler) handleExecuteQueryState(msg *pb.ChaincodeMessage) { handler.deleteQueryIterator(txContext, iterID) } + var payloadBytes []byte payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, ID: iterID} - payloadBytes, err := proto.Marshal(payload) + payloadBytes, err = proto.Marshal(payload) if err != nil { executeIter.Close() handler.deleteQueryIterator(txContext, iterID) @@ -1029,8 +1054,9 @@ func (handler *Handler) afterInvokeChaincode(e *fsm.Event, state string) { func (handler *Handler) enterBusyState(e *fsm.Event, state string) { go func() { msg, _ := e.Args[0].(*pb.ChaincodeMessage) - - chaincodeLogger.Debugf("[%s]state is %s", shorttxid(msg.Txid), state) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s]state is %s", shorttxid(msg.Txid), state) + } // Check if this is the unique request from this chaincode txid uniqueReq := handler.createTXIDEntry(msg.Txid) if !uniqueReq { @@ -1040,24 +1066,27 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { } var triggerNextStateMsg *pb.ChaincodeMessage + var txContext *transactionContext + txContext, triggerNextStateMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for %s. Sending %s", + shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_ERROR) defer func() { handler.deleteTXIDEntry(msg.Txid) - chaincodeLogger.Debugf("[%s]enterBusyState trigger event %s", shorttxid(triggerNextStateMsg.Txid), triggerNextStateMsg.Type) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s]enterBusyState trigger event %s", + shorttxid(triggerNextStateMsg.Txid), triggerNextStateMsg.Type) + } handler.triggerNextState(triggerNextStateMsg, true) }() - chaincodeID := handler.getCCRootName() - var err error - var res []byte - - var txContext *transactionContext - - txContext, triggerNextStateMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_ERROR) if txContext == nil { return } + chaincodeID := handler.getCCRootName() + var err error + var res []byte + if msg.Type.String() == pb.ChaincodeMessage_PUT_STATE.String() { putStateInfo := &pb.PutStateInfo{} unmarshalErr := proto.Unmarshal(msg.Payload, putStateInfo) @@ -1074,11 +1103,8 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { key := string(msg.Payload) err = txContext.txsimulator.DeleteState(chaincodeID, key) } else if msg.Type.String() == pb.ChaincodeMessage_INVOKE_CHAINCODE.String() { - //check and prohibit C-call-C for CONFIDENTIAL txs - chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid)) - - if triggerNextStateMsg = handler.canCallChaincode(msg.Txid, false); triggerNextStateMsg != nil { - return + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid)) } chaincodeSpec := &pb.ChaincodeSpec{} unmarshalErr := proto.Unmarshal(msg.Payload, chaincodeSpec) @@ -1089,31 +1115,76 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { return } - // Get the chaincodeID to invoke - calledCCName := chaincodeSpec.ChaincodeID.Name - chaincodeLogger.Debugf("[%s] C-call-C %s", shorttxid(msg.Txid), calledCCName) + // Get the chaincodeID to invoke. The chaincodeID to be called may + // contain composite info like "chaincode-name:version/channel-name" + // We are not using version now but default to the latest + calledCcParts := chaincodeIDParts(chaincodeSpec.ChaincodeID.Name) + chaincodeSpec.ChaincodeID.Name = calledCcParts.name + if calledCcParts.suffix == "" { + // use caller's channel as the called chaincode is in the same channel + calledCcParts.suffix = txContext.chainID + } + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s] C-call-C %s on channel %s", + shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix) + } + + triggerNextStateMsg = handler.checkACL(txContext.proposal, calledCcParts) + if triggerNextStateMsg != nil { + return + } + // Set up a new context for the called chaincode if on a different channel + // We grab the called channel's ledger simulator to hold the new state ctxt := context.Background() - ctxt = context.WithValue(ctxt, TXSimulatorKey, txContext.txsimulator) + txsim := txContext.txsimulator + if calledCcParts.suffix != txContext.chainID { + lgr := peer.GetLedger(calledCcParts.suffix) + if lgr == nil { + payload := "Failed to find ledger for called channel " + calledCcParts.suffix + triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, + Payload: []byte(payload), Txid: msg.Txid} + return + } + txsim2, err2 := lgr.NewTxSimulator() + if err2 != nil { + triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, + Payload: []byte(err2.Error()), Txid: msg.Txid} + return + } + defer txsim2.Done() + txsim = txsim2 + } + ctxt = context.WithValue(ctxt, TXSimulatorKey, txsim) - // Create the invocation spec - chaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec} + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s] calling lccc to get chaincode data for %s on channel %s", + shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix) + } - //Get the latest version of calledCCName - cd, err := GetChaincodeDataFromLCCC(ctxt, msg.Txid, txContext.proposal, txContext.chainID, calledCCName) + //Call LCCC to get the called chaincode artifacts + var cd *ccprovider.ChaincodeData + cd, err = GetChaincodeDataFromLCCC(ctxt, msg.Txid, txContext.proposal, calledCcParts.suffix, calledCcParts.name) 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) + 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} return } - cccid := ccprovider.NewCCContext(txContext.chainID, calledCCName, cd.Version, msg.Txid, false, txContext.proposal) + cccid := ccprovider.NewCCContext(calledCcParts.suffix, calledCcParts.name, cd.Version, msg.Txid, false, txContext.proposal) // Launch the new chaincode if not already running - _, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(ctxt, cccid, chaincodeInvocationSpec) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s] launching chaincode %s on channel %s", + shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix) + } + 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) + 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} return } @@ -1145,7 +1216,9 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) { } // Send response msg back to chaincode. - chaincodeLogger.Debugf("[%s]Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_RESPONSE) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s]Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_RESPONSE) + } triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid} }() } @@ -1297,11 +1370,12 @@ func (handler *Handler) sendExecuteMessage(ctxt context.Context, chainID string, if err != nil { return nil, err } - - chaincodeLogger.Debugf("[%s]Inside sendExecuteMessage. Message %s", shorttxid(msg.Txid), msg.Type.String()) + if chaincodeLogger.IsEnabledFor(logging.DEBUG) { + chaincodeLogger.Debugf("[%s]Inside sendExecuteMessage. Message %s", shorttxid(msg.Txid), msg.Type.String()) + } //if security is disabled the context elements will just be nil - if err := handler.setChaincodeProposal(prop, msg); err != nil { + if err = handler.setChaincodeProposal(prop, msg); err != nil { return nil, err } diff --git a/core/chaincode/shim/chaincode.go b/core/chaincode/shim/chaincode.go index c054a681d1c..b7b6301431d 100644 --- a/core/chaincode/shim/chaincode.go +++ b/core/chaincode/shim/chaincode.go @@ -273,6 +273,7 @@ func (stub *ChaincodeStub) init(handler *Handler, txid string, input *pb.Chainco stub.proposalContext = proposalContext } +// InitTestStub initializes an appropriate stub for testing chaincode func InitTestStub(funargs ...string) *ChaincodeStub { stub := ChaincodeStub{} allargs := util.ToChaincodeArgs(funargs...) @@ -281,6 +282,7 @@ func InitTestStub(funargs ...string) *ChaincodeStub { return &stub } +// GetTxID returns the transaction ID func (stub *ChaincodeStub) GetTxID() string { return stub.TxID } @@ -293,7 +295,11 @@ func (stub *ChaincodeStub) GetTxID() string { // InvokeChaincode locally calls the specified chaincode `Invoke` using the // same transaction context; that is, chaincode calling chaincode doesn't // create a new transaction message. -func (stub *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte) pb.Response { +func (stub *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response { + // Internally we handle chaincode name as a composite name + if channel != "" { + chaincodeName = chaincodeName + "/" + channel + } return stub.handler.handleInvokeChaincode(chaincodeName, args, stub.TxID) } @@ -350,8 +356,7 @@ func (stub *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInter } -//Given a list of attributes, CreateCompositeKey function combines these attributes -//to form a composite key. +//CreateCompositeKey combines the given attributes to form a composite key. func (stub *ChaincodeStub) CreateCompositeKey(objectType string, attributes []string) (string, error) { return createCompositeKey(stub, objectType, attributes) } @@ -367,8 +372,7 @@ func createCompositeKey(stub ChaincodeStubInterface, objectType string, attribut return string(compositeKey), nil } -//Given a composite key, SplitCompositeKey function splits the key into attributes -//on which the composite key was formed. +//SplitCompositeKey splits the key into attributes on which the composite key was formed. func (stub *ChaincodeStub) SplitCompositeKey(compositeKey string) (string, []string, error) { return splitCompositeKey(stub, compositeKey) } @@ -446,10 +450,12 @@ func (iter *StateQueryIterator) Close() error { return err } +// GetArgs returns the argument list func (stub *ChaincodeStub) GetArgs() [][]byte { return stub.args } +// GetStringArgs returns the arguments as array of strings func (stub *ChaincodeStub) GetStringArgs() []string { args := stub.GetArgs() strargs := make([]string, 0, len(args)) @@ -459,6 +465,8 @@ func (stub *ChaincodeStub) GetStringArgs() []string { return strargs } +// GetFunctionAndParameters returns the first arg as the function and the rest +// as argument string array func (stub *ChaincodeStub) GetFunctionAndParameters() (function string, params []string) { allargs := stub.GetStringArgs() function = "" diff --git a/core/chaincode/shim/interfaces.go b/core/chaincode/shim/interfaces.go index 37b43583300..f23223d84a6 100644 --- a/core/chaincode/shim/interfaces.go +++ b/core/chaincode/shim/interfaces.go @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Interfaces to allow testing of chaincode apps with mocked up stubs package shim import ( @@ -51,8 +50,11 @@ type ChaincodeStubInterface interface { // InvokeChaincode locally calls the specified chaincode `Invoke` using the // same transaction context; that is, chaincode calling chaincode doesn't - // create a new transaction message. - InvokeChaincode(chaincodeName string, args [][]byte) pb.Response + // create a new transaction message. If the called chaincode is on a different + // channel, only the Response is returned to the caller; any PutState calls + // will not have any effect on the ledger of the channel; effectively it is + // a `Query`. If `channel` is empty, the caller's channel is assumed. + InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response // GetState returns the byte array value specified by the `key`. GetState(key string) ([]byte, error) diff --git a/core/chaincode/shim/mockstub.go b/core/chaincode/shim/mockstub.go index e4a7edd1bbe..ee55d04b6e1 100644 --- a/core/chaincode/shim/mockstub.go +++ b/core/chaincode/shim/mockstub.go @@ -214,23 +214,27 @@ func (stub *MockStub) PartialCompositeKeyQuery(objectType string, attributes []s return partialCompositeKeyQuery(stub, objectType, attributes) } -//Given a list of attributes, CreateCompositeKey function combines these attributes +// CreateCompositeKey combines the list of attributes //to form a composite key. func (stub *MockStub) CreateCompositeKey(objectType string, attributes []string) (string, error) { return createCompositeKey(stub, objectType, attributes) } -//Given a composite key, SplitCompositeKey function splits the key into attributes -//on which the composite key was formed. +// SplitCompositeKey splits the composite key into attributes +// on which the composite key was formed. func (stub *MockStub) SplitCompositeKey(compositeKey string) (string, []string, error) { return splitCompositeKey(stub, compositeKey) } -// Invokes a peered chaincode. -// E.g. stub1.InvokeChaincode("stub2Hash", funcArgs) +// InvokeChaincode calls a peered chaincode. +// E.g. stub1.InvokeChaincode("stub2Hash", funcArgs, channel) // Before calling this make sure to create another MockStub stub2, call stub2.MockInit(uuid, func, args) // and register it with stub1 by calling stub1.MockPeerChaincode("stub2Hash", stub2) -func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte) pb.Response { +func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response { + // Internally we use chaincode name as a composite name + if channel != "" { + chaincodeName = chaincodeName + "/" + channel + } // TODO "args" here should possibly be a serialized pb.ChaincodeInput otherStub := stub.Invokables[chaincodeName] mockLogger.Debug("MockStub", stub.Name, "Invoking peer chaincode", otherStub.Name, args) diff --git a/core/scc/lccc/lccc.go b/core/scc/lccc/lccc.go index 225fab7f150..a934a6ed4fd 100644 --- a/core/scc/lccc/lccc.go +++ b/core/scc/lccc/lccc.go @@ -389,7 +389,7 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response cd, cdbytes, _ := lccc.getChaincode(stub, chain, ccname) if cd == nil || cdbytes == nil { - logger.Debug("ChaincodeID [%s/%s] does not exist", chain, ccname) + logger.Debugf("ChaincodeID: %s does not exist on channel: %s ", ccname, chain) return shim.Error(TXNotFoundErr(ccname + "/" + chain).Error()) } diff --git a/examples/chaincode/go/chaincode_example04/chaincode_example04.go b/examples/chaincode/go/chaincode_example04/chaincode_example04.go index 68a1e02bfce..56e0f38631c 100644 --- a/examples/chaincode/go/chaincode_example04/chaincode_example04.go +++ b/examples/chaincode/go/chaincode_example04/chaincode_example04.go @@ -81,7 +81,7 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string f := "invoke" invokeArgs := util.ToChaincodeArgs(f, "a", "b", "10") - response := stub.InvokeChaincode(chainCodeToCall, invokeArgs) + response := stub.InvokeChaincode(chainCodeToCall, invokeArgs, "") if response.Status != shim.OK { errStr := fmt.Sprintf("Failed to invoke chaincode. Got error: %s", string(response.Payload)) fmt.Printf(errStr) @@ -96,36 +96,54 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string return shim.Error(err.Error()) } - return shim.Success(nil) + return response } func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { var event string // Event entity var err error - if len(args) != 1 { + if len(args) < 1 { return shim.Error("Incorrect number of arguments. Expecting entity to query") } event = args[0] + var jsonResp string // Get the state from the ledger eventValbytes, err := stub.GetState(event) if err != nil { - jsonResp := "{\"Error\":\"Failed to get state for " + event + "\"}" + jsonResp = "{\"Error\":\"Failed to get state for " + event + "\"}" return shim.Error(jsonResp) } if eventValbytes == nil { - jsonResp := "{\"Error\":\"Nil value for " + event + "\"}" + jsonResp = "{\"Error\":\"Nil value for " + event + "\"}" return shim.Error(jsonResp) } - jsonResp := "{\"Name\":\"" + event + "\",\"Amount\":\"" + string(eventValbytes) + "\"}" - fmt.Printf("Query Response:%s\n", jsonResp) + if len(args) > 3 { + chainCodeToCall := args[1] + queryKey := args[2] + channel := args[3] + f := "query" + invokeArgs := util.ToChaincodeArgs(f, queryKey) + response := stub.InvokeChaincode(chainCodeToCall, invokeArgs, channel) + if response.Status != shim.OK { + errStr := fmt.Sprintf("Failed to invoke chaincode. Got error: %s", err.Error()) + fmt.Printf(errStr) + return shim.Error(errStr) + } + jsonResp = string(response.Payload) + } else { + jsonResp = "{\"Name\":\"" + event + "\",\"Amount\":\"" + string(eventValbytes) + "\"}" + } + fmt.Printf("Query Response: %s\n", jsonResp) + return shim.Success([]byte(jsonResp)) } +// Invoke is called by fabric to execute a transaction func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() if function == "invoke" { diff --git a/examples/chaincode/go/chaincode_example05/chaincode_example05.go b/examples/chaincode/go/chaincode_example05/chaincode_example05.go index fcd03a2f815..6578bd69c31 100644 --- a/examples/chaincode/go/chaincode_example05/chaincode_example05.go +++ b/examples/chaincode/go/chaincode_example05/chaincode_example05.go @@ -75,7 +75,7 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string // Query chaincode_example02 f := "query" queryArgs := util.ToChaincodeArgs(f, "a") - response := stub.InvokeChaincode(chaincodeURL, queryArgs) + response := stub.InvokeChaincode(chaincodeURL, queryArgs, "") if response.Status != shim.OK { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) fmt.Printf(errStr) @@ -89,7 +89,7 @@ func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string } queryArgs = util.ToChaincodeArgs(f, "b") - response = stub.InvokeChaincode(chaincodeURL, queryArgs) + response = stub.InvokeChaincode(chaincodeURL, queryArgs, "") if response.Status != shim.OK { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) fmt.Printf(errStr) @@ -130,7 +130,7 @@ func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) // Query chaincode_example02 f := "query" queryArgs := util.ToChaincodeArgs(f, "a") - response := stub.InvokeChaincode(chaincodeURL, queryArgs) + response := stub.InvokeChaincode(chaincodeURL, queryArgs, "") if response.Status != shim.OK { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) fmt.Printf(errStr) @@ -144,7 +144,7 @@ func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) } queryArgs = util.ToChaincodeArgs(f, "b") - response = stub.InvokeChaincode(chaincodeURL, queryArgs) + response = stub.InvokeChaincode(chaincodeURL, queryArgs, "") if response.Status != shim.OK { errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) fmt.Printf(errStr) diff --git a/examples/chaincode/go/passthru/passthru.go b/examples/chaincode/go/passthru/passthru.go index b797cd1b724..52750d79fd2 100644 --- a/examples/chaincode/go/passthru/passthru.go +++ b/examples/chaincode/go/passthru/passthru.go @@ -48,7 +48,7 @@ func (p *PassthruChaincode) iq(stub shim.ChaincodeStubInterface, function string } chaincodeID := function - return stub.InvokeChaincode(chaincodeID, util.ToChaincodeArgs(args...)) + return stub.InvokeChaincode(chaincodeID, util.ToChaincodeArgs(args...), "") } // Invoke passes through the invoke call diff --git a/examples/chaincode/java/LinkExample/src/main/java/example/LinkExample.java b/examples/chaincode/java/LinkExample/src/main/java/example/LinkExample.java index 9eec17898f8..bbbc5967b74 100644 --- a/examples/chaincode/java/LinkExample/src/main/java/example/LinkExample.java +++ b/examples/chaincode/java/LinkExample/src/main/java/example/LinkExample.java @@ -27,9 +27,9 @@ public class LinkExample extends ChaincodeBase { //Default name for map chaincode in dev mode - //Can be set to a hash location via init or setMap + //Can be set to a hash location via init or setMap private String mapChaincode = "map"; - + @Override public String run(ChaincodeStub stub, String function, String[] args) { switch (function) { @@ -38,7 +38,7 @@ public String run(ChaincodeStub stub, String function, String[] args) { mapChaincode = args[0]; break; case "put": - stub.invokeChaincode(mapChaincode, function, toByteStringList(args)); + stub.invokeChaincode(mapChaincode, function, toByteStringList(args), ""); default: break; } diff --git a/gossip/state/state.go b/gossip/state/state.go index 2d79d0a093b..4fe6a137dce 100644 --- a/gossip/state/state.go +++ b/gossip/state/state.go @@ -329,7 +329,6 @@ func (s *GossipStateProviderImpl) antiEntropy() { } } } - s.logger.Debug("[XXX]: Stateprovider stopped, stoping anti entropy procedure.") if current == max { continue @@ -337,7 +336,6 @@ func (s *GossipStateProviderImpl) antiEntropy() { s.requestBlocksInRange(uint64(current), uint64(max)) } - s.logger.Debug("[XXX]: Stateprovider stopped, stoping anti entropy procedure.") s.done.Done() } diff --git a/msp/identities.go b/msp/identities.go index 8608f08cfd6..2e4ed98500f 100644 --- a/msp/identities.go +++ b/msp/identities.go @@ -30,6 +30,7 @@ import ( "github.com/hyperledger/fabric/bccsp" "github.com/hyperledger/fabric/bccsp/signer" "github.com/hyperledger/fabric/protos/common" + "github.com/op/go-logging" ) type identity struct { @@ -81,7 +82,7 @@ func (id *identity) GetOrganizationUnits() string { // to determine whether this identity produced the // signature; it returns nil if so or an error otherwise func (id *identity) Verify(msg []byte, sig []byte) error { - mspLogger.Infof("Verifying signature") + // mspLogger.Infof("Verifying signature") // Compute Hash digest, err := id.msp.bccsp.Hash(msg, &bccsp.SHAOpts{}) @@ -89,10 +90,11 @@ func (id *identity) Verify(msg []byte, sig []byte) error { return fmt.Errorf("Failed computing digest [%s]", err) } - // Verify signature - mspLogger.Debugf("Verify: msg = %s", hex.Dump(msg)) - mspLogger.Debugf("Verify: digest = %s", hex.Dump(digest)) - mspLogger.Debugf("Verify: sig = %s", hex.Dump(sig)) + // TODO: Are these ok to log ? + if mspLogger.IsEnabledFor(logging.DEBUG) { + mspLogger.Debugf("Verify: digest = %s", hex.Dump(digest)) + mspLogger.Debugf("Verify: sig = %s", hex.Dump(sig)) + } valid, err := id.msp.bccsp.Verify(id.pk, sig, digest, nil) if err != nil { @@ -116,7 +118,7 @@ func (id *identity) VerifyAttributes(proof [][]byte, spec *AttributeProofSpec) e // Serialize returns a byte array representation of this identity func (id *identity) Serialize() ([]byte, error) { - mspLogger.Infof("Serializing identity %s", id.id) + // mspLogger.Infof("Serializing identity %s", id.id) pb := &pem.Block{Bytes: id.cert.Raw} pemBytes := pem.EncodeToMemory(pb) @@ -143,13 +145,13 @@ type signingidentity struct { } func newSigningIdentity(id *IdentityIdentifier, cert *x509.Certificate, pk bccsp.Key, signer *signer.CryptoSigner, msp *bccspmsp) SigningIdentity { - mspLogger.Infof("Creating signing identity instance for ID %s", id) + //mspLogger.Infof("Creating signing identity instance for ID %s", id) return &signingidentity{identity{id: id, cert: cert, pk: pk, msp: msp}, signer} } // Sign produces a signature over msg, signed by this instance func (id *signingidentity) Sign(msg []byte) ([]byte, error) { - mspLogger.Infof("Signing message") + //mspLogger.Infof("Signing message") // Compute Hash digest, err := id.msp.bccsp.Hash(msg, &bccsp.SHAOpts{}) diff --git a/msp/mgmt/config.go b/msp/mgmt/config.go index 48204cda7e7..68e0421b015 100644 --- a/msp/mgmt/config.go +++ b/msp/mgmt/config.go @@ -18,6 +18,8 @@ package mgmt import ( "fmt" + "os" + "path/filepath" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/common/util" @@ -36,11 +38,31 @@ func LoadLocalMsp(dir string) error { return GetLocalMSP().Setup(conf) } +func getConfigPath(dir string) (string, error) { + // Try to read the dir + if _, err := os.Stat(dir); err != nil { + cfg := os.Getenv("PEER_CFG_PATH") + if cfg != "" { + dir = filepath.Join(cfg, dir) + } else { + dir = filepath.Join(os.Getenv("GOPATH"), "/src/github.com/hyperledger/fabric/msp/sampleconfig/") + } + if _, err := os.Stat(dir); err != nil { + return "", err + } + } + return dir, nil +} + // FIXME: this is required for now because we need a local MSP // and also the MSP mgr for the test chain; as soon as the code // to setup chains is ready, the chain should be setup using // the method below and this method should disappear func LoadFakeSetupWithLocalMspAndTestChainMsp(dir string) error { + var err error + if dir, err = getConfigPath(dir); err != nil { + return err + } conf, err := msp.GetLocalMspConfig(dir) if err != nil { return err