接下来分析lscc的执行过程,在这之前,先看下其初始化注册过程。在peer/node/start.go#Serve
里registerChaincodeSupport(ccSrv, ccEndpoint, ca, aclProvider)
。进入方法,主要是scc.CreateSysCCs(ccp, sccp, aclProvider)
,就是引入了scc/importsysccs.go#builtInSystemChaincodes
里定义的SCC,即cscc
,lscc
和gscc
(包含chaincode的Name,Path代码地址,Chaincode对象,acl,policy等)。然后逐个执行sccp.RegisterSysCC(cc)
注册SCC,实际上就是加入SystemChaincodeProvider.Registrar.typeRegistry
(map[string]*inprocContainer)里,key是chaincode的Name,这个后面初始化时候会使用到。
这里完成注册后,在serve()
里稍后的位置执行sccp.DeploySysCCs("", ccp)
进行部署。第一个参数是channelId为“”,说明这些scc是chainless的,可以供所有的channel使用。进入方法,首先构造cds&pb.ChaincodeDeploymentSpec{ExecEnv: pb.ChaincodeDeploymentSpec_SYSTEM, ChaincodeSpec: spec}
.这里注意到,部署类型是ChaincodeDeploymentSpec_SYSTEM,回顾上节acc类型是ChaincodeDeploymentSpec_DOCKER,说明SCC是部署在peer内的,而acc是部署在另外新的chaincode docker容器里。然后执行ccprov.Execute(ctxt, cccid, chaincodeDeploymentSpec)
,最终实际上又回到前面介绍过的ChaincodeSupport.Execute
方法上。后面的执行与acc是一致的,所区别的在于launch。区别于acc使用的是dockercontroller/DockerVM#start
,scc使用的是inproccontroller.InprocVM#start
.首先vm.registry.typeRegistry[path]
,这个在前面已经注册了可以获取出来。然后开始真正的launch。
1 | func (ipc *inprocContainer) launchInProc(ctxt context.Context, id string, args []string, env []string, ccSupport ccintf.CCSupport) error { |
这里主要有两个goroutine,分别处理chaincode shim和peer的handle stream。同时,有两个channel,peerRcvCCSend
,ccRcvPeerSend
,可以从名字判断出消息的流向。
启动第一个goroutineshim.StartInProc
,向chaincodeSupport
发送注册。用于这里的chatWithPeer
跟acc介绍的是同一个都在chaincode.go
下,在这里面构造了该scc的handler(设置inProcStream
这个stream)发送了&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER, Payload: payload})
这个消息。这个类比于acc里在chaincode容器的handler。
启动第二个goroutineccSupport.HandleChaincodeStream(ctxt, inprocStream)
,用于处理注册请求,这个类比于acc里的peer的handler,所不同的是,acc里的stream是grpc server stream,是由grpc server构建的,而这里则是封装了peerRcvCCSend
,ccRcvPeerSend
这两个channel。总而言之,在acc内,新chaincode容器内通过grpc方式与peer通信,而scc由于都处在同一个peer容器内,是通过go channel的方法实现。其他的主要流程都是一致的。
第三部分的select-case
则是在处理chaincode退出的情况。这个方法在外层InprocVM#Start
是go routine启动的,最外层等待的是select-case
等待launchState.Done()
channel的返回,这个跟acc是一致的。
一路返回到core/chaincode/chaincode_support.go#Invoke
方法,在执行完Launch
后,最后执行ChaincodeSupport#execute
.在前面instantiate(3)里介绍过execute方法,这里直接看发送的ChaincodeMessage
。在chaincode_support.go#Invoke
里指定部署ChaincodeDeploymentSpec
的ChaincodeMessage_Type
为pb.ChaincodeMessage_INIT
,由chaincode/handler.go#serialSendAsync
,对端的shim/handler.go#handleMessage
。前面初始化完成后,两端handler状态都为ready,select-case
进入hanlder.state=ready
,ChaincodeMessageType=ChaincodeMessage_INIT
执行到#handleInit
.
1 | func (handler *Handler) handleInit(msg *pb.ChaincodeMessage, errc chan error) { |
在这里,主要是
stub.init
构建stub,这个桩的概念在远程调用里常见。然后执行handler.cc.Init(stub)
。这里看lscc.go#Init
.对于lscc(大多数scc)来说,初始化并不需要进行特殊处理,因此这里只是简单返回shim.Success
的resp.在handleInit
里,defer func
将结果返回,这里如果执行成功,返回pb.ChaincodeMessage_COMPLETED
,否则返回pb.ChaincodeMessage_ERROR
.同样的,在chaincode/handler.go#handleMessage
里对pb.ChaincodeMessage_COMPLETED, pb.ChaincodeMessage_ERROR
这两个调用chaincode shim的回复消息调用handler.Notify(msg)
,即tctx.ResponseNotifier <- msg
.这个在前面的instantiate(3)小节提过,异步调用的错误以及结果都发送到txctx.ResponseNotifie这个chan内再做后续的处理。至此,完成lscc的初始化和注册,后面再介绍acc的初始化的rpc过程。
命名缩写参考
cc : chaincode
scc : system chaincode
ccp, ccprov : chaincode provider
sccp : system chaincode provider
cccid : ccprovider.CCContext