How chain code is executed
First of all, it is executed via endorser ProcessProposal service. The endorser will then try to invoke the chaincode, inccluding:
- Start the CC container if need
- Set up the stream handler registration
- Pass MSG with Transaction
- Handle the response from CC, until Completed indication received
- Notify endorser the job done. 30s timeout by default
endorser ProcessProposal
endorser will try to invoke chaincode by calling callChaincode(), which will then invoke chaincode for execution.
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).Invoke at chaincode_support.go:197
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).Execute at chaincode_support.go:155
github.com/hyperledger/fabric/core/endorser.(*SupportImpl).Execute at support.go:126
github.com/hyperledger/fabric/core/endorser.(*Endorser).callChaincode at endorser.go:119
github.com/hyperledger/fabric/core/endorser.(*Endorser).SimulateProposal at endorser.go:187
github.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposalSuccessfullyOrError at endorser.go:397
github.com/hyperledger/fabric/core/endorser.(*Endorser).ProcessProposal at endorser.go:340
github.com/hyperledger/fabric/core/handlers/auth/filter.(*expirationCheckFilter).ProcessProposal at expiration.go:61
github.com/hyperledger/fabric/core/handlers/auth/filter.(*filter).ProcessProposal at filter.go:32
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-protos-go/peer._Endorser_ProcessProposal_Handler.func1 at peer.pb.go:107
github.com/hyperledger/fabric/internal/peer/node.unaryGrpcLimiter.func1 at grpc_limiters.go:51
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1.1.1 at chain.go:25
github.com/hyperledger/fabric/common/grpclogging.UnaryServerInterceptor.func1 at server.go:92
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1.1.1 at chain.go:25
github.com/hyperledger/fabric/common/grpcmetrics.UnaryServerInterceptor.func1 at interceptor.go:31
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1.1.1 at chain.go:25
github.com/hyperledger/fabric/vendor/github.com/grpc-ecosystem/go-grpc-middleware.ChainUnaryServer.func1 at chain.go:34
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-protos-go/peer._Endorser_ProcessProposal_Handler at peer.pb.go:109
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).processUnaryRPC at server.go:995
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).handleStream at server.go:1275
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1 at server.go:710
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1 at server.go:708
Launch CC
Look into chaincode.Invoke, we can see it will launch the chaincode if it is not running.
// Invoke will invoke chaincode and return the message containing the response.
// The chaincode will be launched if it is not already running.
func (cs *ChaincodeSupport) Invoke(txParams *ccprovider.TransactionParams, chaincodeName string, input *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
ccid, cctype, err := cs.CheckInvocation(txParams, chaincodeName, input)
if err != nil {
return nil, errors.WithMessage(err, "invalid invocation")
}
h, err := cs.Launch(ccid)
if err != nil {
return nil, err
}
return cs.execute(cctype, txParams, chaincodeName, input, h)
}
Check the launching, it will try CC server model, or start client model.
func (r *RuntimeLauncher) Launch(ccid string, streamHandler extcc.StreamHandler) error {
var startFailCh chan error
var timeoutCh <-chan time.Time
startTime := time.Now()
launchState, alreadyStarted := r.Registry.Launching(ccid)
if !alreadyStarted {
startFailCh = make(chan error, 1)
timeoutCh = time.NewTimer(r.StartupTimeout).C
go func() {
// go through the build process to obtain connecion information
ccservinfo, err := r.Runtime.Build(ccid)
if err != nil {
startFailCh <- errors.WithMessage(err, "error building chaincode")
return
}
// chaincode server model indicated... proceed to connect to CC
if ccservinfo != nil {
if err = r.ConnectionHandler.Stream(ccid, ccservinfo, streamHandler); err != nil {
startFailCh <- errors.WithMessagef(err, "connection to %s failed", ccid)
return
}
launchState.Notify(errors.Errorf("connection to %s terminated", ccid))
return
}
// default peer-as-server model... compute connection information for CC callback
// and proceed to launch chaincode
ccinfo, err := r.ChaincodeClientInfo(ccid)
if err != nil {
startFailCh <- errors.WithMessage(err, "could not get connection info")
return
}
if ccinfo == nil {
startFailCh <- errors.New("could not get connection info")
return
}
if err = r.Runtime.Start(ccid, ccinfo); err != nil {
startFailCh <- errors.WithMessage(err, "error starting container")
return
}
exitCode, err := r.Runtime.Wait(ccid)
if err != nil {
launchState.Notify(errors.Wrap(err, "failed to wait on container exit"))
}
launchState.Notify(errors.Errorf("container exited with %d", exitCode))
}()
}
var err error
select {
case <-launchState.Done():
err = errors.WithMessage(launchState.Err(), "chaincode registration failed")
case err = <-startFailCh:
launchState.Notify(err)
r.Metrics.LaunchFailures.With("chaincode", ccid).Add(1)
case <-timeoutCh:
err = errors.Errorf("timeout expired while starting chaincode %s for transaction", ccid)
launchState.Notify(err)
r.Metrics.LaunchTimeouts.With("chaincode", ccid).Add(1)
}
success := true
if err != nil && !alreadyStarted {
success = false
chaincodeLogger.Debugf("stopping due to error while launching: %+v", err)
defer r.Registry.Deregister(ccid)
}
r.Metrics.LaunchDuration.With(
"chaincode", ccid,
"success", strconv.FormatBool(success),
).Observe(time.Since(startTime).Seconds())
chaincodeLogger.Debug("launch complete")
return err
}
When CC is launched, it will be started with argument ‘chaincode -peer.address 1.2.3.4:7052’, which indicates CC in Container should set up a gRPC connection to endorser immediately.
CC register
Therefore, peer side will get the gRPC from chaincode container. In this case, the handler is newly created, so its state is ‘Created’, and the first message is ChaincodeMessage_REGISTER. It will eventaully call HandleRegister() to register the CC. As a result, Message ChaincodeMessage_REGISTERED and ChaincodeMessage_READY wil be sent back to CC.
//ChaincodeSupport Handler state:
const (
Created State = iota
Established
Ready
)
// Message Types
const (
ChaincodeMessage_UNDEFINED ChaincodeMessage_Type = 0
ChaincodeMessage_REGISTER ChaincodeMessage_Type = 1
ChaincodeMessage_REGISTERED ChaincodeMessage_Type = 2
ChaincodeMessage_INIT ChaincodeMessage_Type = 3
ChaincodeMessage_READY ChaincodeMessage_Type = 4
ChaincodeMessage_TRANSACTION ChaincodeMessage_Type = 5
ChaincodeMessage_COMPLETED ChaincodeMessage_Type = 6
ChaincodeMessage_ERROR ChaincodeMessage_Type = 7
ChaincodeMessage_GET_STATE ChaincodeMessage_Type = 8
ChaincodeMessage_PUT_STATE ChaincodeMessage_Type = 9
ChaincodeMessage_DEL_STATE ChaincodeMessage_Type = 10
ChaincodeMessage_INVOKE_CHAINCODE ChaincodeMessage_Type = 11
ChaincodeMessage_RESPONSE ChaincodeMessage_Type = 13
ChaincodeMessage_GET_STATE_BY_RANGE ChaincodeMessage_Type = 14
ChaincodeMessage_GET_QUERY_RESULT ChaincodeMessage_Type = 15
ChaincodeMessage_QUERY_STATE_NEXT ChaincodeMessage_Type = 16
ChaincodeMessage_QUERY_STATE_CLOSE ChaincodeMessage_Type = 17
ChaincodeMessage_KEEPALIVE ChaincodeMessage_Type = 18
ChaincodeMessage_GET_HISTORY_FOR_KEY ChaincodeMessage_Type = 19
ChaincodeMessage_GET_STATE_METADATA ChaincodeMessage_Type = 20
ChaincodeMessage_PUT_STATE_METADATA ChaincodeMessage_Type = 21
ChaincodeMessage_GET_PRIVATE_DATA_HASH ChaincodeMessage_Type = 22
)
github.com/hyperledger/fabric/core/chaincode.(*Handler).HandleRegister at handler.go:486
github.com/hyperledger/fabric/core/chaincode.(*Handler).handleMessageCreatedState at handler.go:167
github.com/hyperledger/fabric/core/chaincode.(*Handler).handleMessage at handler.go:156
github.com/hyperledger/fabric/core/chaincode.(*Handler).ProcessStream at handler.go:403
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).HandleChaincodeStream at chaincode_support.go:125
github.com/hyperledger/fabric/core/chaincode.(*ChaincodeSupport).Register at chaincode_support.go:130
github.com/hyperledger/fabric/vendor/github.com/hyperledger/fabric-protos-go/peer._ChaincodeSupport_Register_Handler at chaincode_shim.pb.go:1169
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).processStreamingRPC at server.go:1199
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).handleStream at server.go:1279
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1 at server.go:710
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/hyperledger/fabric/vendor/google.golang.org/grpc.(*Server).serveStreams.func1 at server.go:708
Chaincode invocation
When CC is ready, ChaincodeMessage_TRANSACTION will be sent to CC container side.
CC as client: