Skip to content

Commit 1bd5b2b

Browse files
committed
[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 <[email protected]>
1 parent 36bbeb6 commit 1bd5b2b

File tree

14 files changed

+231
-98
lines changed

14 files changed

+231
-98
lines changed

core/chaincode/chaincode_support.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,9 @@ func (chaincodeSupport *ChaincodeSupport) Stop(context context.Context, cccid *c
428428

429429
//stop the chaincode
430430
sir := container.StopImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, ChainID: cccid.ChainID, Version: cccid.Version}, Timeout: 0}
431+
// The line below is left for debugging. It replaces the line above to keep
432+
// the chaincode container around to give you a chance to get data
433+
//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}
431434

432435
vmtype, _ := chaincodeSupport.getVMType(cds)
433436

@@ -490,7 +493,9 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid
490493
return cID, cMsg, err
491494
}
492495
if chrte.handler.isRunning() {
493-
chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", canName)
496+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
497+
chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", canName)
498+
}
494499
chaincodeSupport.runningChaincodes.Unlock()
495500
return cID, cMsg, nil
496501
}
@@ -662,7 +667,7 @@ func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, cccid *c
662667
return ccresp, err
663668
}
664669

665-
// Returns true if the peer was configured with development-mode enabled
670+
// IsDevMode returns true if the peer was configured with development-mode enabled
666671
func IsDevMode() bool {
667672
mode := viper.GetString("chaincode.mode")
668673

core/chaincode/exectransaction_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ func checkFinalState(cccid *ccprovider.CCContext) error {
519519
return fmt.Errorf("Error retrieving state from ledger for <%s>: %s", cName, resErr)
520520
}
521521
if Aval != 90 {
522-
return fmt.Errorf("Incorrect result. Aval is wrong for <%s>", cName)
522+
return fmt.Errorf("Incorrect result. Aval %d != 90 <%s>", Aval, cName)
523523
}
524524

525525
resbytes, resErr = txsim.GetState(cccid.Name, "b")
@@ -531,7 +531,7 @@ func checkFinalState(cccid *ccprovider.CCContext) error {
531531
return fmt.Errorf("Error retrieving state from ledger for <%s>: %s", cName, resErr)
532532
}
533533
if Bval != 210 {
534-
return fmt.Errorf("Incorrect result. Bval is wrong for <%s>", cName)
534+
return fmt.Errorf("Incorrect result. Bval %d != 210 <%s>", Bval, cName)
535535
}
536536

537537
// Success

core/chaincode/handler.go

+128-54
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/hyperledger/fabric/core/common/ccprovider"
2929
ccintf "github.com/hyperledger/fabric/core/container/ccintf"
3030
"github.com/hyperledger/fabric/core/ledger"
31+
"github.com/hyperledger/fabric/core/peer"
3132
pb "github.com/hyperledger/fabric/protos/peer"
3233
"github.com/hyperledger/fabric/protos/utils"
3334
"github.com/looplab/fsm"
@@ -114,14 +115,18 @@ func shorttxid(txid string) string {
114115
//and suffix will just be absent (also note that LCCC reserves
115116
//"/:[]${}" as special chars mainly for such namespace uses)
116117
func (handler *Handler) decomposeRegisteredName(cid *pb.ChaincodeID) {
117-
handler.ccCompParts = &ccParts{}
118-
b := []byte(cid.Name)
118+
handler.ccCompParts = chaincodeIDParts(cid.Name)
119+
}
120+
121+
func chaincodeIDParts(ccName string) *ccParts {
122+
b := []byte(ccName)
123+
p := &ccParts{}
119124

120125
//compute suffix (ie, chain name)
121126
i := bytes.IndexByte(b, '/')
122127
if i >= 0 {
123128
if i < len(b)-1 {
124-
handler.ccCompParts.suffix = string(b[i+1:])
129+
p.suffix = string(b[i+1:])
125130
}
126131
b = b[:i]
127132
}
@@ -130,14 +135,14 @@ func (handler *Handler) decomposeRegisteredName(cid *pb.ChaincodeID) {
130135
i = bytes.IndexByte(b, ':')
131136
if i >= 0 {
132137
if i < len(b)-1 {
133-
handler.ccCompParts.version = string(b[i+1:])
138+
p.version = string(b[i+1:])
134139
}
135140
b = b[:i]
136141
}
142+
// remaining is the chaincode name
143+
p.name = string(b)
137144

138-
handler.ccCompParts.name = string(b)
139-
140-
return
145+
return p
141146
}
142147

143148
func (handler *Handler) getCCRootName() string {
@@ -221,6 +226,16 @@ func (handler *Handler) deleteQueryIterator(txContext *transactionContext, txid
221226
delete(txContext.queryIteratorMap, txid)
222227
}
223228

229+
// Check if the transactor is allow to call this chaincode on this channel
230+
func (handler *Handler) checkACL(proposal *pb.Proposal, calledCC *ccParts) *pb.ChaincodeMessage {
231+
// TODO: Decide what to pass in to verify that this transactor can access this
232+
// channel (chID) and chaincode (ccID). Very likely we need the signedProposal
233+
// which contains the sig and creator cert
234+
235+
// If error, return ChaincodeMessage with type ChaincodeMessage_ERROR
236+
return nil
237+
}
238+
224239
//THIS CAN BE REMOVED ONCE WE FULL SUPPORT (Invoke) CONFIDENTIALITY WITH CC-CALLING-CC
225240
//Only invocation are allowed
226241
func (handler *Handler) canCallChaincode(txid string, isQuery bool) *pb.ChaincodeMessage {
@@ -564,41 +579,50 @@ func (handler *Handler) handleGetState(msg *pb.ChaincodeMessage) {
564579
}
565580

566581
var serialSendMsg *pb.ChaincodeMessage
582+
var txContext *transactionContext
583+
txContext, serialSendMsg = handler.isValidTxSim(msg.Txid,
584+
"[%s]No ledger context for GetState. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR)
567585

568586
defer func() {
569587
handler.deleteTXIDEntry(msg.Txid)
570-
chaincodeLogger.Debugf("[%s]handleGetState serial send %s", shorttxid(serialSendMsg.Txid), serialSendMsg.Type)
588+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
589+
chaincodeLogger.Debugf("[%s]handleGetState serial send %s",
590+
shorttxid(serialSendMsg.Txid), serialSendMsg.Type)
591+
}
571592
handler.serialSendAsync(serialSendMsg, nil)
572593
}()
573594

574-
key := string(msg.Payload)
595+
if txContext == nil {
596+
return
597+
}
575598

576-
// Invoke ledger to get state
599+
key := string(msg.Payload)
577600
chaincodeID := handler.getCCRootName()
601+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
602+
chaincodeLogger.Debugf("[%s] getting state for chaincode %s, key %s, channel %s",
603+
shorttxid(msg.Txid), chaincodeID, key, txContext.chainID)
604+
}
578605

579606
var res []byte
580607
var err error
581-
var txContext *transactionContext
582-
583-
txContext, serialSendMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for GetState. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR)
584-
if txContext == nil {
585-
return
586-
}
587-
588608
res, err = txContext.txsimulator.GetState(chaincodeID, key)
589609

590610
if err != nil {
591611
// Send error msg back to chaincode. GetState will not trigger event
592612
payload := []byte(err.Error())
593-
chaincodeLogger.Errorf("[%s]Failed to get chaincode state(%s). Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
613+
chaincodeLogger.Errorf("[%s]Failed to get chaincode state(%s). Sending %s",
614+
shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
594615
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
595616
} else if res == nil {
596617
//The state object being requested does not exist
597-
chaincodeLogger.Debugf("[%s]No state associated with key: %s. Sending %s with an empty payload", shorttxid(msg.Txid), key, pb.ChaincodeMessage_RESPONSE)
618+
chaincodeLogger.Debugf("[%s]No state associated with key: %s. Sending %s with an empty payload",
619+
shorttxid(msg.Txid), key, pb.ChaincodeMessage_RESPONSE)
598620
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid}
599621
} else {
600622
// Send response msg back to chaincode. GetState will not trigger event
601-
chaincodeLogger.Debugf("[%s]Got state. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_RESPONSE)
623+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
624+
chaincodeLogger.Debugf("[%s]Got state. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_RESPONSE)
625+
}
602626
serialSendMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid}
603627
}
604628

@@ -677,7 +701,7 @@ func (handler *Handler) handleRangeQueryState(msg *pb.ChaincodeMessage) {
677701
var i = uint32(0)
678702
var qresult ledger.QueryResult
679703
for ; i < maxRangeQueryStateLimit; i++ {
680-
qresult, err := rangeIter.Next()
704+
qresult, err = rangeIter.Next()
681705
if err != nil {
682706
chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR)
683707
return
@@ -952,7 +976,7 @@ func (handler *Handler) handleExecuteQueryState(msg *pb.ChaincodeMessage) {
952976
var i = uint32(0)
953977
var qresult ledger.QueryResult
954978
for ; i < maxExecuteQueryStateLimit; i++ {
955-
qresult, err := executeIter.Next()
979+
qresult, err = executeIter.Next()
956980
if err != nil {
957981
chaincodeLogger.Errorf("Failed to get query result from iterator. Sending %s", pb.ChaincodeMessage_ERROR)
958982
return
@@ -970,8 +994,9 @@ func (handler *Handler) handleExecuteQueryState(msg *pb.ChaincodeMessage) {
970994
handler.deleteQueryIterator(txContext, iterID)
971995
}
972996

997+
var payloadBytes []byte
973998
payload := &pb.QueryStateResponse{KeysAndValues: keysAndValues, HasMore: qresult != nil, ID: iterID}
974-
payloadBytes, err := proto.Marshal(payload)
999+
payloadBytes, err = proto.Marshal(payload)
9751000
if err != nil {
9761001
executeIter.Close()
9771002
handler.deleteQueryIterator(txContext, iterID)
@@ -1029,8 +1054,9 @@ func (handler *Handler) afterInvokeChaincode(e *fsm.Event, state string) {
10291054
func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
10301055
go func() {
10311056
msg, _ := e.Args[0].(*pb.ChaincodeMessage)
1032-
1033-
chaincodeLogger.Debugf("[%s]state is %s", shorttxid(msg.Txid), state)
1057+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1058+
chaincodeLogger.Debugf("[%s]state is %s", shorttxid(msg.Txid), state)
1059+
}
10341060
// Check if this is the unique request from this chaincode txid
10351061
uniqueReq := handler.createTXIDEntry(msg.Txid)
10361062
if !uniqueReq {
@@ -1040,24 +1066,27 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
10401066
}
10411067

10421068
var triggerNextStateMsg *pb.ChaincodeMessage
1069+
var txContext *transactionContext
1070+
txContext, triggerNextStateMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for %s. Sending %s",
1071+
shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_ERROR)
10431072

10441073
defer func() {
10451074
handler.deleteTXIDEntry(msg.Txid)
1046-
chaincodeLogger.Debugf("[%s]enterBusyState trigger event %s", shorttxid(triggerNextStateMsg.Txid), triggerNextStateMsg.Type)
1075+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1076+
chaincodeLogger.Debugf("[%s]enterBusyState trigger event %s",
1077+
shorttxid(triggerNextStateMsg.Txid), triggerNextStateMsg.Type)
1078+
}
10471079
handler.triggerNextState(triggerNextStateMsg, true)
10481080
}()
10491081

1050-
chaincodeID := handler.getCCRootName()
1051-
var err error
1052-
var res []byte
1053-
1054-
var txContext *transactionContext
1055-
1056-
txContext, triggerNextStateMsg = handler.isValidTxSim(msg.Txid, "[%s]No ledger context for %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_ERROR)
10571082
if txContext == nil {
10581083
return
10591084
}
10601085

1086+
chaincodeID := handler.getCCRootName()
1087+
var err error
1088+
var res []byte
1089+
10611090
if msg.Type.String() == pb.ChaincodeMessage_PUT_STATE.String() {
10621091
putStateInfo := &pb.PutStateInfo{}
10631092
unmarshalErr := proto.Unmarshal(msg.Payload, putStateInfo)
@@ -1074,11 +1103,8 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
10741103
key := string(msg.Payload)
10751104
err = txContext.txsimulator.DeleteState(chaincodeID, key)
10761105
} else if msg.Type.String() == pb.ChaincodeMessage_INVOKE_CHAINCODE.String() {
1077-
//check and prohibit C-call-C for CONFIDENTIAL txs
1078-
chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid))
1079-
1080-
if triggerNextStateMsg = handler.canCallChaincode(msg.Txid, false); triggerNextStateMsg != nil {
1081-
return
1106+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1107+
chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid))
10821108
}
10831109
chaincodeSpec := &pb.ChaincodeSpec{}
10841110
unmarshalErr := proto.Unmarshal(msg.Payload, chaincodeSpec)
@@ -1089,31 +1115,76 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
10891115
return
10901116
}
10911117

1092-
// Get the chaincodeID to invoke
1093-
calledCCName := chaincodeSpec.ChaincodeID.Name
1094-
chaincodeLogger.Debugf("[%s] C-call-C %s", shorttxid(msg.Txid), calledCCName)
1118+
// Get the chaincodeID to invoke. The chaincodeID to be called may
1119+
// contain composite info like "chaincode-name:version/channel-name"
1120+
// We are not using version now but default to the latest
1121+
calledCcParts := chaincodeIDParts(chaincodeSpec.ChaincodeID.Name)
1122+
chaincodeSpec.ChaincodeID.Name = calledCcParts.name
1123+
if calledCcParts.suffix == "" {
1124+
// use caller's channel as the called chaincode is in the same channel
1125+
calledCcParts.suffix = txContext.chainID
1126+
}
1127+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1128+
chaincodeLogger.Debugf("[%s] C-call-C %s on channel %s",
1129+
shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix)
1130+
}
1131+
1132+
triggerNextStateMsg = handler.checkACL(txContext.proposal, calledCcParts)
1133+
if triggerNextStateMsg != nil {
1134+
return
1135+
}
10951136

1137+
// Set up a new context for the called chaincode if on a different channel
1138+
// We grab the called channel's ledger simulator to hold the new state
10961139
ctxt := context.Background()
1097-
ctxt = context.WithValue(ctxt, TXSimulatorKey, txContext.txsimulator)
1140+
txsim := txContext.txsimulator
1141+
if calledCcParts.suffix != txContext.chainID {
1142+
lgr := peer.GetLedger(calledCcParts.suffix)
1143+
if lgr == nil {
1144+
payload := "Failed to find ledger for called channel " + calledCcParts.suffix
1145+
triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR,
1146+
Payload: []byte(payload), Txid: msg.Txid}
1147+
return
1148+
}
1149+
txsim2, err2 := lgr.NewTxSimulator()
1150+
if err2 != nil {
1151+
triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR,
1152+
Payload: []byte(err2.Error()), Txid: msg.Txid}
1153+
return
1154+
}
1155+
defer txsim2.Done()
1156+
txsim = txsim2
1157+
}
1158+
ctxt = context.WithValue(ctxt, TXSimulatorKey, txsim)
10981159

1099-
// Create the invocation spec
1100-
chaincodeInvocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec}
1160+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1161+
chaincodeLogger.Debugf("[%s] calling lccc to get chaincode data for %s on channel %s",
1162+
shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix)
1163+
}
11011164

1102-
//Get the latest version of calledCCName
1103-
cd, err := GetChaincodeDataFromLCCC(ctxt, msg.Txid, txContext.proposal, txContext.chainID, calledCCName)
1165+
//Call LCCC to get the called chaincode artifacts
1166+
var cd *ccprovider.ChaincodeData
1167+
cd, err = GetChaincodeDataFromLCCC(ctxt, msg.Txid, txContext.proposal, calledCcParts.suffix, calledCcParts.name)
11041168
if err != nil {
11051169
payload := []byte(err.Error())
1106-
chaincodeLogger.Debugf("[%s]Failed to get chaincoed data (%s) for invoked chaincode. Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
1170+
chaincodeLogger.Debugf("[%s]Failed to get chaincoed data (%s) for invoked chaincode. Sending %s",
1171+
shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
11071172
triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
11081173
return
11091174
}
1110-
cccid := ccprovider.NewCCContext(txContext.chainID, calledCCName, cd.Version, msg.Txid, false, txContext.proposal)
1175+
cccid := ccprovider.NewCCContext(calledCcParts.suffix, calledCcParts.name, cd.Version, msg.Txid, false, txContext.proposal)
11111176

11121177
// Launch the new chaincode if not already running
1113-
_, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(ctxt, cccid, chaincodeInvocationSpec)
1178+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1179+
chaincodeLogger.Debugf("[%s] launching chaincode %s on channel %s",
1180+
shorttxid(msg.Txid), calledCcParts.name, calledCcParts.suffix)
1181+
}
1182+
cciSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: chaincodeSpec}
1183+
_, chaincodeInput, launchErr := handler.chaincodeSupport.Launch(ctxt, cccid, cciSpec)
11141184
if launchErr != nil {
11151185
payload := []byte(launchErr.Error())
1116-
chaincodeLogger.Debugf("[%s]Failed to launch invoked chaincode. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR)
1186+
chaincodeLogger.Debugf("[%s]Failed to launch invoked chaincode. Sending %s",
1187+
shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR)
11171188
triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
11181189
return
11191190
}
@@ -1145,7 +1216,9 @@ func (handler *Handler) enterBusyState(e *fsm.Event, state string) {
11451216
}
11461217

11471218
// Send response msg back to chaincode.
1148-
chaincodeLogger.Debugf("[%s]Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_RESPONSE)
1219+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1220+
chaincodeLogger.Debugf("[%s]Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type.String(), pb.ChaincodeMessage_RESPONSE)
1221+
}
11491222
triggerNextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Payload: res, Txid: msg.Txid}
11501223
}()
11511224
}
@@ -1297,11 +1370,12 @@ func (handler *Handler) sendExecuteMessage(ctxt context.Context, chainID string,
12971370
if err != nil {
12981371
return nil, err
12991372
}
1300-
1301-
chaincodeLogger.Debugf("[%s]Inside sendExecuteMessage. Message %s", shorttxid(msg.Txid), msg.Type.String())
1373+
if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
1374+
chaincodeLogger.Debugf("[%s]Inside sendExecuteMessage. Message %s", shorttxid(msg.Txid), msg.Type.String())
1375+
}
13021376

13031377
//if security is disabled the context elements will just be nil
1304-
if err := handler.setChaincodeProposal(prop, msg); err != nil {
1378+
if err = handler.setChaincodeProposal(prop, msg); err != nil {
13051379
return nil, err
13061380
}
13071381

0 commit comments

Comments
 (0)