每个实例化之后的链码都会以容器的形式启动起来,下面举一个byfn.sh
启动的例子:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS
a908ea5fa0ee dev-peer1.org2.example.com-mycc-1.0-26c2ef32838554aac4f7ad6f100aca865e87959c9a126e86d764c8d01f8346ab "chaincode -peer.add…" 42 hours ago Up 42 hours dev-peer1.org2.example.com-mycc-1.0
9495cf13bc81 dev-peer0.org1.example.com-mycc-1.0-384f11f484b9302df90b453200cfb25174305fce8f53f4e94d45ee3b6cab0ce9 "chaincode -peer.add…" 42 hours ago Up 42 hours dev-peer0.org1.example.com-mycc-1.0
d7d5c7912f71 dev-peer0.org2.example.com-mycc-1.0-15b571b3ce849066b7ec74497da3b27e54e0df1345daff3951b94245ce09c42b "chaincode -peer.add…" 42 hours ago Up 42 hours dev-peer0.org2.example.com-mycc-1.0
容器启动以后执行的命令是:
chaincode -peer.address [peer地址]
该命令会执行链码的main
函数,一般来说链码的main
函数都是下面的样子:
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
主要是执行了shim.Start()
方法,这是链码容器启动的关键函数,今天就来解析一个这个函数到底做了一下什么事。
梳理一下链码启动的整个过程:
peer
节点执行ProcessProposal()
chaincode -peer.address [peer地址]
main
函数main
函数中调用shim.Start()
方法完成链码的启动看看这个方法具体做了什么吧,在core/chaincode/shim/chaincode.go
的132行
// chaincodes.
func Start(cc Chaincode) error {
// 做一个链码日志的设置
SetupChaincodeLogging()
// 从环境变量中获取chaincodeName,如下
// CORE_CHAINCODE_ID_NAME=mycc:1.0
chaincodename := viper.GetString("chaincode.id.name")
if chaincodename == "" {
return errors.New("error chaincode id not provided")
}
// 创建一个工厂
err := factory.InitFactories(factory.GetDefaultOpts())
if err != nil {
return errors.WithMessage(err, "internal error, BCCSP could not be initialized with default options")
}
// 设置streamGetter为userChaincodeStreamGetter
// 即将用户链码以流的方式读取进来
if streamGetter == nil {
streamGetter = userChaincodeStreamGetter
}
// 执行上面设置的streamGetter,参数传一个chaincodename
stream, err := streamGetter(chaincodename)
if err != nil {
return err
}
// 这个函数很重要,与peer节点聊天,即与peer节点通信
// 链码容器通过这个函数与安装到的peer节点进行通信
err = chatWithPeer(chaincodename, stream, cc)
return err
}
Start()
方法主体逻辑很简单,它由几个重要的函数构成:
SetupChaincodeLogging()
,设置日志InitFactories()
,创建工厂userChaincodeStreamGetter()
,将用户链码以流的方式拿到chatWithPeer()
,与peer节点通信设置日志的函数我们就不看了,主要是从环境变量中获取一些日志设置。下面就来看下剩下的三个函数究竟做了什么
先看下这个函数的参数GetDefaultOpts()
,在bccsp/factory/opts.go
的20行:
// GetDefaultOpts offers a default implementation for Opts
// returns a new instance every time
func GetDefaultOpts() *FactoryOpts {
return &FactoryOpts{
ProviderName: "SW",
SwOpts: &SwOpts{
HashFamily: "SHA2",
SecLevel: 256,
Ephemeral: true,
},
}
}
它返回了一个FactoryOpts
对象实例,可以看到该对象主要是一些加密有关的字段,这里不做展开了。这样可以猜想,InitFactories()
方法,也是做了一些链码设置加密操作的一些内容,这里就不展开看了,有兴趣的可以去看下,代码在bccsp/factory/nopkcs11.go
接下来就是userChaincodeStreamGetter()
方法了,在core/chaincode/shim/chaincode.go
的82行:
//the non-mock user CC stream establishment func
func userChaincodeStreamGetter(name string) (PeerChaincodeStream, error) {
// 这一行就是读取链码容器启动执行的命令的那个参数的,拿到peer地址
flag.StringVar(&peerAddress, "peer.address", "", "peer address")
if viper.GetBool("peer.tls.enabled") {
// 下面是一些有关tls的几个操作
// 获取tls密钥地址,在用户安装链码的时候指定
keyPath := viper.GetString("tls.client.key.path")
// 获取tls证书地址
certPath := viper.GetString("tls.client.cert.path")
// 读取秘钥数据
data, err1 := ioutil.ReadFile(keyPath)
if err1 != nil {
err1 = errors.Wrap(err1, fmt.Sprintf("error trying to read file content %s", keyPath))
chaincodeLogger.Errorf("%+v", err1)
return nil, err1
}
key = string(data)
// 读取证书数据
data, err1 = ioutil.ReadFile(certPath)
if err1 != nil {
err1 = errors.Wrap(err1, fmt.Sprintf("error trying to read file content %s", certPath))
chaincodeLogger.Errorf("%+v", err1)
return nil, err1
}
cert = string(data)
}
flag.Parse()
chaincodeLogger.Debugf("Peer address: %s", getPeerAddress())
// 与peer节点建立连接
clientConn, err := newPeerClientConnection()
if err != nil {
err = errors.Wrap(err, "error trying to connect to local peer")
chaincodeLogger.Errorf("%+v", err)
return nil, err
}
来看下newPeerClientConnection()
方法,在core/chaincode/shim/chaincode.go
的308行:
func newPeerClientConnection() (*grpc.ClientConn, error) {
// 拿到peerAddress,那是一个全局变量,如果是空会从环境变量中拿
// 如果还是拿不到就会报错
var peerAddress = getPeerAddress()
// 设置一些与peer节点的keepalive参数
kaOpts := &comm.KeepaliveOptions{
ClientInterval: time.Duration(1) * time.Minute,
ClientTimeout: time.Duration(20) * time.Second,
}
// 根据是否设置了tls,传入不同的参数,执行NewClientConnectionWithAddres方法
if viper.GetBool("peer.tls.enabled") {
return comm.NewClientConnectionWithAddress(peerAddress, true, true,
comm.InitTLSForShim(key, cert), kaOpts)
}
return comm.NewClientConnectionWithAddress(peerAddress, true, false, nil, kaOpts)
}
来看下NewClientConnectionWithAddres()
方法,在core/comm/connection.go
的206行:
// NewClientConnectionWithAddress Returns a new grpc.ClientConn to the given address
func NewClientConnectionWithAddress(peerAddress string, block bool, tslEnabled bool,
creds credentials.TransportCredentials, ka *KeepaliveOptions) (*grpc.ClientConn, error) {
// 参数:1.peer地址 2.是否阻塞等待dial返回 3.是否开启tls 4.tls配置(不开则传nil) 5.keepalive参数
// 创建一个grpc的拨号参数
var opts []grpc.DialOption
// 设置keepalive部分参数
if ka != nil {
opts = ClientKeepaliveOptions(ka)
} else {
// set to the default options
opts = ClientKeepaliveOptions(DefaultKeepaliveOptions)
}
// 设置tls部分参数
if tslEnabled {
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithInsecure())
}
// 设置block参数
if block {
opts = append(opts, grpc.WithBlock())
}
// 设置最大接收消息大小,最大发送消息大小
opts = append(opts, grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(MaxRecvMsgSize),
grpc.MaxCallSendMsgSize(MaxSendMsgSize),
))
// 设置一个超时上下文
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
// grpc拨号连接
conn, err := grpc.DialContext(ctx, peerAddress, opts...)
if err != nil {
return nil, err
}
// 返回连接
return conn, err
}
回到userChaincodeStreamGetter()
中:
// 刚刚看到这里
clientConn, err := newPeerClientConnection()
if err != nil {
err = errors.Wrap(err, "error trying to connect to local peer")
chaincodeLogger.Errorf("%+v", err)
return nil, err
}
chaincodeLogger.Debugf("os.Args returns: %s", os.Args)
// 创建chaincodeSupport客户端,就一个字段clientConn
// chaincodeSupport服务在peer端有启动
chaincodeSupportClient := pb.NewChaincodeSupportClient(clientConn)
// 注册chaincodeSupport客户端
stream, err := chaincodeSupportClient.Register(context.Background())
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("error chatting with leader at address=%s", getPeerAddress()))
}
return stream, nil
}
chaincodeSupportClient.Register()
部分源代码在protos/peer/chaincode_shim.pb.go
的1016行:
type ChaincodeSupportClient interface {
Register(ctx context.Context, opts ...grpc.CallOption) (ChaincodeSupport_RegisterClient, error)
}
type chaincodeSupportClient struct {
cc *grpc.ClientConn
}
func NewChaincodeSupportClient(cc *grpc.ClientConn) ChaincodeSupportClient {
return &chaincodeSupportClient{cc}
}
func (c *chaincodeSupportClient) Register(ctx context.Context, opts ...grpc.CallOption) (ChaincodeSupport_RegisterClient, error) {
stream, err := c.cc.NewStream(ctx, &_ChaincodeSupport_serviceDesc.Streams[0], "/protos.ChaincodeSupport/Register", opts...)
if err != nil {
return nil, err
}
x := &chaincodeSupportRegisterClient{stream}
return x, nil
}
type ChaincodeSupport_RegisterClient interface {
Send(*ChaincodeMessage) error
Recv() (*ChaincodeMessage, error)
grpc.ClientStream
}
type chaincodeSupportRegisterClient struct {
grpc.ClientStream
}
chaincodeSupportRegisterClient
发送和接收的消息类型是ChaincodeMessage
,这里提一下这个消息类型,后续会经常看到这个类型,它是链码容器与peer
节点传输的消息类型。
userChaincodeStreamGetter()
方法到这里就结束了。
最后一个方法了,这个方法是一个大头,userChaincodeStreamGetter()
方法已经完成了与peer
节点的通信了,它返回了一个stram
对象,是一个接口PeerChaincodeStream
,只提供了三个方法:
// PeerChaincodeStream interface for stream between Peer and chaincode instance.
type PeerChaincodeStream interface {
Send(*pb.ChaincodeMessage) error
Recv() (*pb.ChaincodeMessage, error)
CloseSend() error
}
将stream
传给chatWithPeer
作为参数
if streamGetter == nil {
streamGetter = userChaincodeStreamGetter
}
stream, err := streamGetter(chaincodename)
if err != nil {
return err
}
// cc即为链码对象,实现了Init和Invoke接口
err = chatWithPeer(chaincodename, stream, cc)
看下chatWithPeer()
吧,在core/chaincode/shim/chaincode.go
322行:
func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) error {
// Create the shim handler responsible for all control logic
handler := newChaincodeHandler(stream, cc)
defer stream.CloseSend()
第一步就是创建一个ChaincodeHandler
对象,这是一个很重要的对象,链码容器与peer
节点的交互本质上就是两边的ChaincodeHandler
在通过GRPC
通信不断地发送和介绍消息。看下newChaincodeHandler()
方法,在core/chaincode/shim/handler.go
的166行:
// Handler handler implementation for shim side of chaincode.
type Handler struct {
// 需要锁来保护链码,防止peer的并发请求
sync.Mutex
// 序列化锁,在serialSend时用
serialLock sync.Mutex
To string
// grpc流,就是userChaincodeStreamGetter()创建的那个stream
ChatStream PeerChaincodeStream
// 链码对象
cc Chaincode
// handler状态,有created,ready,established三种状态
state state
// 链码信息响应通道
responseChannel map[string]chan pb.ChaincodeMessage
}
// NewChaincodeHandler returns a new instance of the shim side handler.
func newChaincodeHandler(peerChatStream PeerChaincodeStream, chaincode Chaincode) *Handler {
v := &Handler{
ChatStream: peerChatStream,
cc: chaincode,
}
v.responseChannel = make(map[string]chan pb.ChaincodeMessage)
v.state = created
return v
}
回到chatWithPeer()
:
// ....
// Send the ChaincodeID during register.
// 创建ChaincodeID对象
chaincodeID := &pb.ChaincodeID{Name: chaincodename}
payload, err := proto.Marshal(chaincodeID)
if err != nil {
return errors.Wrap(err, "error marshalling chaincodeID during chaincode registration")
}
// Register on the stream
chaincodeLogger.Debugf("Registering.. sending %s", pb.ChaincodeMessage_REGISTER)
// 这里就开始发送第一个流信息了
// 类型是ChaincodeMessage_REGISTER,payload主要就是包含chaincodeName
// 主要是用于注册,serialSend函数很简单,主要就是调用stream流的send方法发送消息
if err = handler.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER, Payload: payload}); err != nil {
return errors.WithMessage(err, "error sending chaincode REGISTER")
}
// holds return values from gRPC Recv below
type recvMsg struct {
msg *pb.ChaincodeMessage
err error
}
msgAvail := make(chan *recvMsg, 1)
errc := make(chan error)
// 这个函数就是从grpc中接收对端消息了
receiveMessage := func() {
in, err := stream.Recv()
msgAvail <- &recvMsg{in, err}
}
// 下面这块逻辑就是死循环不断地从grpc中接收消息
go receiveMessage()
for {
select {
case rmsg := <-msgAvail:
// 从msgAvail中拿取接收到的消息
switch {
case rmsg.err == io.EOF:
// 接收到EOF表示对端关闭了,这边就退出结束并退出
err = errors.Wrapf(rmsg.err, "received EOF, ending chaincode stream")
chaincodeLogger.Debugf("%+v", err)
return err
case rmsg.err != nil:
// err不为空表示接收出错了,返回err
err := errors.Wrap(rmsg.err, "receive failed")
chaincodeLogger.Errorf("Received error from server, ending chaincode stream: %+v", err)
return err
case rmsg.msg == nil:
// 接收到空的msg了,返回err
err := errors.New("received nil message, ending chaincode stream")
chaincodeLogger.Debugf("%+v", err)
return err
default:
// 默认情况是正常接收
chaincodeLogger.Debugf("[%s]Received message %s from peer", shorttxid(rmsg.msg.Txid), rmsg.msg.Type)
// 处理对端发过来的消息
err := handler.handleMessage(rmsg.msg, errc)
if err != nil {
err = errors.WithMessage(err, "error handling message")
return err
}
// 处理完以后重新起一个协程去监听,这里是不是搞一个协程池好一点?
go receiveMessage()
// 回到主循环继续从grpc中接收消息
}
// 如果handleMessage后续发送过程出错了,会往errc中发送err
// 可以看到handler.handleMessage第二个参数是errc
case sendErr := <-errc:
if sendErr != nil {
err := errors.Wrap(sendErr, "error sending")
return err
}
}
}
}
chatWithPeer()
方法到这里就结束了,主要就是不断地发送消息,接收消息,处理接收到的消息,继续发送消息的过程,这也就是我之前讲为什么链码与peer
节点交互的过程本质上是两个ChaincodeHandler
之间在不断收发处理消息的过程,这个消息的类型就是ChaincodeMessage
类型。先来简单看下这个类型:
type ChaincodeMessage struct {
// 消息类型
Type ChaincodeMessage_Type
// 时间戳
Timestamp *timestamp.Timestamp
// payload
Payload []byte
// 交易ID
Txid string
// 签名提案
Proposal *SignedProposal
// 链码事件
ChaincodeEvent *ChaincodeEvent
// channel id
ChannelId string
XXX_NoUnkeyedLiteral struct{}
XXX_unrecognized []byte
XXX_sizecache int32
}
因此,到这里,后续的工作就是分析链码容器与peer
节点两侧的ChaincodeHandler
具体是怎么处理消息的就好了。
chatWithPeer()
中,链码侧发送了第一个消息给peer
节点,类型为ChaincodeMessage_REGISTER
,下面看下peer
节点这边做了什么
peer
节点的handler
的具体实现在core/chaincode/handler.go
中,注意链码侧的handler
实现在core/chaincode/shim/handler.go
中。具体的处理流程函数在402行ProcessStream()
中。
我们暂时不关心
peer
节点中的handler
流程是如何起来的。不过我追踪了一下这个函数的调用,好像也是在处理链码实例化提案的时候起来了。也就是说,在
peer
节点执行链码实例化的过程中,不仅仅启动了链码容器,也启动了本地与链码容器进行通信的handler
,都是在执行ChaincodeSupport.Launch()
方法的时候启动的。
上述结论不一定是对的,但是我们暂时不关心,后续有机会可以解析一下这块的逻辑,总之我们找到了peer
节点的handler
的处理流程,下面就先来看一下吧:
func (h *Handler) ProcessStream(stream ccintf.ChaincodeStream) error {
// 执行完之后注销
defer h.deregister()
h.mutex.Lock()
h.streamDoneChan = make(chan struct{})
h.mutex.Unlock()
defer close(h.streamDoneChan)
h.chatStream = stream
h.errChan = make(chan error, 1)
// keepalive通道
var keepaliveCh <-chan time.Time
if h.Keepalive != 0 {
ticker := time.NewTicker(h.Keepalive)
defer ticker.Stop()
keepaliveCh = ticker.C
}
// ===============================
// 下面这块几乎和链码侧的处理流程一模一样
type recvMsg struct {
msg *pb.ChaincodeMessage
err error
}
msgAvail := make(chan *recvMsg, 1)
receiveMessage := func() {
in, err := h.chatStream.Recv()
msgAvail <- &recvMsg{in, err}
}
go receiveMessage()
for {
select {
case rmsg := <-msgAvail:
switch {
// Defer the deregistering of the this handler.
case rmsg.err == io.EOF:
chaincodeLogger.Debugf("received EOF, ending chaincode support stream: %s", rmsg.err)
return rmsg.err
case rmsg.err != nil:
err := errors.Wrap(rmsg.err, "receive failed")
chaincodeLogger.Errorf("handling chaincode support stream: %+v", err)
return err
case rmsg.msg == nil:
err := errors.New("received nil message, ending chaincode support stream")
chaincodeLogger.Debugf("%+v", err)
return err
default:
err := h.handleMessage(rmsg.msg)
if err != nil {
err = errors.WithMessage(err, "error handling message, ending stream")
chaincodeLogger.Errorf("[%s] %+v", shorttxid(rmsg.msg.Txid), err)
return err
}
go receiveMessage()
}
case sendErr := <-h.errChan:
err := errors.Wrapf(sendErr, "received error while sending message, ending chaincode support stream")
chaincodeLogger.Errorf("%s", err)
return err
case <-keepaliveCh:
// 发送一个keepalive消息
h.serialSendAsync(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_KEEPALIVE})
continue
}
}
}
大体上和链码侧的处理逻辑几乎相同,最终也是走到了handleMessage()
方法中,看下这个方法,在core/chaincode/handler.go
的178行:
// handleMessage is called by ProcessStream to dispatch messages.
func (h *Handler) handleMessage(msg *pb.ChaincodeMessage) error {
chaincodeLogger.Debugf("[%s] Fabric side handling ChaincodeMessage of type: %s in state %s", shorttxid(msg.Txid), msg.Type, h.state)
// 判断是不是心跳消息,如果是就什么都不做
if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
return nil
}
// 这里的state的赋值和链码侧的不一样
// 链码侧的赋值用的string类型,而peer这边用的int类型
// handler在创建的时候没有给state赋值,state就是默认零值0
// 而Created状态对应的就是0,Established是1,Ready是2
switch h.state {
case Created:
// 因此,在peerHandler接收到第一条消息时,会走这个case
return h.handleMessageCreatedState(msg)
case Ready:
return h.handleMessageReadyState(msg)
default:
return errors.Errorf("handle message: invalid state %s for transaction %s", h.state, msg.Txid)
}
}
看下handleMessageCreatedState()
方法,因为走的是这个case,在core/chaincode/handler.go
的195行:
func (h *Handler) handleMessageCreatedState(msg *pb.ChaincodeMessage) error {
switch msg.Type {
// 之前在看链码侧时,链码侧发送的第一个消息的类型的就是ChaincodeMessage_REGISTER
case pb.ChaincodeMessage_REGISTER:
h.HandleRegister(msg)
default:
// handleMessageCreatedState只接收ChaincodeMessage_REGISTER这个消息类型
return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in created state", msg.Txid, msg.Type)
}
return nil
}
看下HandleRegister()
方法,因为走的是这个case,在core/chaincode/handler.go
的508行:
// handleRegister is invoked when chaincode tries to register.
func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
chaincodeLogger.Debugf("Received %s in state %s", msg.Type, h.state)
// 从消息中获取payload,主要就是链码ID
chaincodeID := &pb.ChaincodeID{}
err := proto.Unmarshal(msg.Payload, chaincodeID)
if err != nil {
chaincodeLogger.Errorf("Error in received %s, could NOT unmarshal registration info: %s", pb.ChaincodeMessage_REGISTER, err)
return
}
// Now register with the chaincodeSupport
h.chaincodeID = chaincodeID
// 将链码注册到peer节点上
err = h.Registry.Register(h)
if err != nil {
// 该函数在注册成功后发送,如果err不为空会报错
h.notifyRegistry(err)
return
}
// 解析链码名字,包括链码name,version,以及链码id
h.ccInstance = ParseName(h.chaincodeID.Name)
chaincodeLogger.Debugf("Got %s for chaincodeID = %s, sending back %s", pb.ChaincodeMessage_REGISTER, chaincodeID, pb.ChaincodeMessage_REGISTERED)
// serialSend顺序发送数据,即往链码侧发送消息了
// 消息类型为ChaincodeMessage_REGISTERED标识已经注册
if err := h.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}); err != nil {
chaincodeLogger.Errorf("error sending %s: %s", pb.ChaincodeMessage_REGISTERED, err)
h.notifyRegistry(err)
return
}
// 将当前handler的状态改为Established
h.state = Established
chaincodeLogger.Debugf("Changed state to established for %+v", h.chaincodeID)
// 这个函数一会看一下,它最终还会将handler的状态更新为ready
h.notifyRegistry(nil)
}
上述流程很简单,主要就是将链码注册到peer
节点上,并发送ChaincodeMessage_REGISTERED
消息给链码侧,同时更新handler
的状态为Established
,最后调用h.notifyRegistry(nil)
将handler
的状态置为Ready
。
有两个方法看一下,一个是Register()
方法用于注册:
func (r *HandlerRegistry) Register(h *Handler) error {
r.mutex.Lock()
defer r.mutex.Unlock()
key := h.chaincodeID.Name
if r.handlers[key] != nil {
chaincodeLogger.Debugf("duplicate registered handler(key:%s) return error", key)
return errors.Errorf("duplicate chaincodeID: %s", h.chaincodeID.Name)
}
// This chaincode was not launched by the peer but is attempting
// to register. Only allowed in development mode.
if r.launching[key] == nil && !r.allowUnsolicitedRegistration {
return errors.Errorf("peer will not accept external chaincode connection %v (except in dev mode)", h.chaincodeID.Name)
}
// 将handler注册到map中,key就是chaincodeID
r.handlers[key] = h
chaincodeLogger.Debugf("registered handler complete for chaincode %s", key)
return nil
}
还有一个是notifyRegistry()
方法:
// notifyRegistry will send ready on registration success and
// update the launch state of the chaincode in the handler registry.
func (h *Handler) notifyRegistry(err error) {
if err == nil {
err = h.sendReady()
}
if err != nil {
h.Registry.Failed(h.chaincodeID.Name, err)
chaincodeLogger.Errorf("failed to start %s", h.chaincodeID)
return
}
h.Registry.Ready(h.chaincodeID.Name)
}
// sendReady sends READY to chaincode serially (just like REGISTER)
func (h *Handler) sendReady() error {
chaincodeLogger.Debugf("sending READY for chaincode %+v", h.chaincodeID)
ccMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY}
// if error in sending tear down the h
if err := h.serialSend(ccMsg); err != nil {
chaincodeLogger.Errorf("error sending READY (%s) for chaincode %+v", err, h.chaincodeID)
return err
}
h.state = Ready
chaincodeLogger.Debugf("Changed to state ready for chaincode %+v", h.chaincodeID)
return nil
}
因此,在HandleRegister()
方法最后一行执行h.notifyRegistry(nil)
以后,因为err==nil
,会执行sendReady()
方法,并最终再发送一个ChaincodeMessage_READY
消息给链码侧,同时将handler
自身的state
改为Ready
。
如果err!=nil
,会输出一个错误日志,表示这次失败。
到这里peer
节点handler
的处理流程就执行完了,到最后发送了两个消息给链码侧:
peer
节点接收到链码侧发送来的ChaincodeMessage_REGISTER
以后,响应一个ChaincodeMessage_REGISTERED
消息,更新handler
的状态为Established
。ChaincodeMessage_READY
消息给链码侧,更新handler
的状态为Ready
我们回到链码侧的handleMessage()
方法:
func (handler *Handler) handleMessage(msg *pb.ChaincodeMessage, errc chan error) error {
if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
chaincodeLogger.Debug("Sending KEEPALIVE response")
// 链码侧接收到keepalive以后,与peer节点不同,他会回应一个消息给peer节点
// 而peer节点收到keepalive以后是什么都不做,因为它有一个定时器在定期发送keepalive
// 这里采用异步发送的方式,不关心是否出错,因为可能下次keepalive能接收到
handler.serialSendAs而peer节点收到keepalive以后是什么都不做ync(msg, nil)
return nil
}
chaincodeLogger.Debugf("[%s] Handling ChaincodeMessage of type: %s(state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)
var err error
// 判断handelr的状态
switch handler.state {
case ready:
err = handler.handleReady(msg, errc)
case established:
err = handler.handleEstablished(msg, errc)
case created:
// 链码侧的handler在创建的时候,state就是created
// 直到接收到消息之前,该状态一直都是created,因此走这个case
err = handler.handleCreated(msg, errc)
default:
err = errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}
if err != nil {
// 如果err不为空,表示处理出错
// 则会发送一个ChaincodeMessage_ERROR消息给peer节点,并返回错误
payload := []byte(err.Error())
errorMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
handler.serialSend(errorMsg)
return err
}
return nil
}
看下handleCreated()
方法,在core/chaincode/shim/handler.go
的820行:
//handle created state
func (handler *Handler) handleCreated(msg *pb.ChaincodeMessage, errc chan error) error {
if msg.Type == pb.ChaincodeMessage_REGISTERED {
// peer节点发送来的消息类型就是ChaincodeMessage_REGISTERED
handler.state = established
return nil
}
return errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}
这里peer
节点发送来的消息类型就是ChaincodeMessage_REGISTERED
类型(peer
节点发送的第一条消息),链码侧收到以后,将状态改为established
。
之后,链码侧会收到peer
节点发送来的第二条消息,因为当前链码侧handler
状态为established
,因此走handleEstablished()
这个方法,在core/chaincode/shim/handler.go
的811行:
//handle established state
func (handler *Handler) handleEstablished(msg *pb.ChaincodeMessage, errc chan error) error {
if msg.Type == pb.ChaincodeMessage_READY {
handler.state = ready
return nil
}
return errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}
这里peer
节点发送来的消息类型就是ChaincodeMessage_READY
类型(peer
节点发送的第二条消息),链码侧收到以后,将状态改为ready
。
至此到这里,链码侧与peer
节点的handler
的状态就都是ready
了
不妨再看看,链码侧与peer
节点的handler
的handleReady
都接受哪些类型。
代码在core/chaincode/shim/handler.go
的774行
//handle ready state
func (handler *Handler) handleReady(msg *pb.ChaincodeMessage, errc chan error) error {
switch msg.Type {
case pb.ChaincodeMessage_RESPONSE:
if err := handler.sendChannel(msg); err != nil {
chaincodeLogger.Errorf("[%s] error sending %s (state:%s): %s", shorttxid(msg.Txid), msg.Type, handler.state, err)
return err
}
chaincodeLogger.Debugf("[%s] Received %s, communicated (state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)
return nil
case pb.ChaincodeMessage_ERROR:
if err := handler.sendChannel(msg); err != nil {
chaincodeLogger.Errorf("[%s] error sending %s (state:%s): %s", shorttxid(msg.Txid), msg.Type, handler.state, err)
}
chaincodeLogger.Debugf("[%s] Error Received %s, communicated (state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)
//we don't return error on ERROR
return nil
case pb.ChaincodeMessage_INIT:
chaincodeLogger.Debugf("[%s] Received %s, initializing chaincode", shorttxid(msg.Txid), msg.Type)
// Call the chaincode's Run function to initialize
handler.handleInit(msg, errc)
return nil
case pb.ChaincodeMessage_TRANSACTION:
chaincodeLogger.Debugf("[%s] Received %s, invoking transaction on chaincode(state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)
// Call the chaincode's Run function to invoke transaction
handler.handleTransaction(msg, errc)
return nil
}
return errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}
主要处理四种类型:
ChaincodeMessage_RESPONSE
ChaincodeMessage_ERROR
ChaincodeMessage_INIT
ChaincodeMessage_TRANSACTION
前两个主要是往handler
的responseChannel
发送数据,发送 response 或者是 error
ChaincodeMessage_INIT
主要是处理实例化的,实例化的时候,peer
侧会发送ChaincodeMessage_INIT
类型的消息过来,最终会调用链码的Init
方法
ChaincodeMessage_TRANSACTION
主要是处理链码调用的,最终会调用链码的Invoke
方法。
看下handleInit()
方法,在core/chaincode/shim/handler.go
的177行:
// handleInit handles request to initialize chaincode.
func (handler *Handler) handleInit(msg *pb.ChaincodeMessage, errc chan error) {
// The defer followed by triggering a go routine dance is needed to ensure that the previous state transition
// is completed before the next one is triggered. The previous state transition is deemed complete only when
// the beforeInit function is exited. Interesting bug fix!!
go func() {
var nextStateMsg *pb.ChaincodeMessage
defer func() {
// 异步地发送处理结果给peer节点
// nextStateMsg再之后的处理流程中会被复制,包括出错或是成功
handler.triggerNextState(nextStateMsg, errc)
}()
// 错误处理函数
errFunc := func(err error, payload []byte, ce *pb.ChaincodeEvent, errFmt string, args ...interface{}) *pb.ChaincodeMessage {
if err != nil {
// Send ERROR message to chaincode support and change state
if payload == nil {
payload = []byte(err.Error())
}
chaincodeLogger.Errorf(errFmt, args...)
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: ce, ChannelId: msg.ChannelId}
}
return nil
}
// 获取peer节点传过来的参数,是一个chaincodeInput结构体
input := &pb.ChaincodeInput{}
unmarshalErr := proto.Unmarshal(msg.Payload, input)
if nextStateMsg = errFunc(unmarshalErr, nil, nil, "[%s] Incorrect payload format. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
return
}
// Call chaincode's Run
// Create the ChaincodeStub which the chaincode can use to callback
// 创建stub对象
stub := new(ChaincodeStub)
// 执行stub的初始化,将提案中的信息抽取出来赋值到这个对象中
err := stub.init(handler, msg.ChannelId, msg.Txid, input, msg.Proposal)
if nextStateMsg = errFunc(err, nil, stub.chaincodeEvent, "[%s] Init get error response. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
return
}
// 执行链码的Init方法
res := handler.cc.Init(stub)
chaincodeLogger.Debugf("[%s] Init get response status: %d", shorttxid(msg.Txid), res.Status)
if res.Status >= ERROR {
// 出错处理
err = errors.New(res.Message)
if nextStateMsg = errFunc(err, []byte(res.Message), stub.chaincodeEvent, "[%s] Init get error response. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
return
}
}
resBytes, err := proto.Marshal(&res)
if err != nil {
payload := []byte(err.Error())
chaincodeLogger.Errorf("[%s] Init marshal response error [%s]. Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent}
return
}
// Send COMPLETED message to chaincode support and change state
// 设置ChaincodeMessage_COMPLETED消息,表示完成
// 最终会调用defer中的triggerNextState方法异步发送给peer
nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelId}
chaincodeLogger.Debugf("[%s] Init succeeded. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_COMPLETED)
}()
}
看下handleTransaction()
方法,在core/chaincode/shim/handler.go
的238行:
// handleTransaction Handles request to execute a transaction.
func (handler *Handler) handleTransaction(msg *pb.ChaincodeMessage, errc chan error) {
go func() {
//better not be nil
var nextStateMsg *pb.ChaincodeMessage
defer func() {
handler.triggerNextState(nextStateMsg, errc)
}()
errFunc := func(err error, ce *pb.ChaincodeEvent, errStr string, args ...interface{}) *pb.ChaincodeMessage {
if err != nil {
payload := []byte(err.Error())
chaincodeLogger.Errorf(errStr, args...)
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: ce, ChannelId: msg.ChannelId}
}
return nil
}
// 获取用户传递的参数,由peer传过来
input := &pb.ChaincodeInput{}
unmarshalErr := proto.Unmarshal(msg.Payload, input)
if nextStateMsg = errFunc(unmarshalErr, nil, "[%s] Incorrect payload format. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
return
}
// Call chaincode's Run
// Create the ChaincodeStub which the chaincode can use to callback
stub := new(ChaincodeStub)
err := stub.init(handler, msg.ChannelId, msg.Txid, input, msg.Proposal)
if nextStateMsg = errFunc(err, stub.chaincodeEvent, "[%s] Transaction execution failed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
return
}
// 调用链码的Invoke
res := handler.cc.Invoke(stub)
// Endorser will handle error contained in Response.
resBytes, err := proto.Marshal(&res)
if nextStateMsg = errFunc(err, stub.chaincodeEvent, "[%s] Transaction execution failed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
return
}
// Send COMPLETED message to chaincode support and change state
chaincodeLogger.Debugf("[%s] Transaction completed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_COMPLETED)
nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelId}
}()
}
上述两个方法处理逻辑基本相同,梳理一下流程:
Init
或者是Invoke
方法ChaincodeMessage_COMPLETED
消息给peer
节点下面再回过头来看下处理ChaincodeMessage_RESPONSE
和ChaincodeMessage_ERROR
的流程。看下sendChannel()
方法,在core/chaincode/shim/handler.go
的110行:
func (handler *Handler) sendChannel(msg *pb.ChaincodeMessage) error {
handler.Lock()
defer handler.Unlock()
// responseChannel map为空则报错
if handler.responseChannel == nil {
return errors.Errorf("[%s] Cannot send message response channel", shorttxid(msg.Txid))
}
// 以 ChannelId+Txid 作为map的key
txCtxID := handler.getTxCtxId(msg.ChannelId, msg.Txid)
// 如果 map[key] 对应的channel为 nil,则报错
if handler.responseChannel[txCtxID] == nil {
return errors.Errorf("[%s] sendChannel does not exist", shorttxid(msg.Txid))
}
chaincodeLogger.Debugf("[%s] before send", shorttxid(msg.Txid))
// 往对应的channel中发送数据
handler.responseChannel[txCtxID] <- *msg
chaincodeLogger.Debugf("[%s] after send", shorttxid(msg.Txid))
return nil
}
我们想要知道responseChannel
中对应的 key,value
是在哪里复制,追踪了一下是在createChannel()
方法中,在core/chaincode/shim/handler.go
的95行:
func (handler *Handler) createChannel(channelID, txid string) (chan pb.ChaincodeMessage, error) {
handler.Lock()
defer handler.Unlock()
// responseChannel map为空则报错
if handler.responseChannel == nil {
return nil, errors.Errorf("[%s] cannot create response channel", shorttxid(txid))
}
// 以 ChannelId+Txid 作为map的key
txCtxID := handler.getTxCtxId(channelID, txid)
// 如果key对应的value已经存在则报错
if handler.responseChannel[txCtxID] != nil {
return nil, errors.Errorf("[%s] channel exists", shorttxid(txCtxID))
}
// 创建channel
c := make(chan pb.ChaincodeMessage)
// 赋值
handler.responseChannel[txCtxID] = c
// 返回相应的通道
return c, nil
}
看下这个函数在哪里被调用了,有好几处地方被调用了,挑一处比较重要的方法callPeerWithChaincodeMsg()
,在core/chaincode/shim/handler.go
的289行:
// callPeerWithChaincodeMsg sends a chaincode message (for e.g., GetState along with the key) to the peer for a given txid
// and receives the response.
func (handler *Handler) callPeerWithChaincodeMsg(msg *pb.ChaincodeMessage, channelID, txid string) (pb.ChaincodeMessage, error) {
// Create the channel on which to communicate the response from the peer
var respChan chan pb.ChaincodeMessage
var err error
// 这里调用了createChannel,创建了channel
if respChan, err = handler.createChannel(channelID, txid); err != nil {
return pb.ChaincodeMessage{}, err
}
// 执行完以后删除相应的通道
defer handler.deleteChannel(channelID, txid)
// 看下这个函数
return handler.sendReceive(msg, respChan)
}
其中有个sendReceive()
方法很重要,这个我们等下看,先看下callPeerWithChaincodeMsg()
方法在哪被调用了,我这里贴一张图过来
随便找一处调用看一看,追溯回去以后发现,其实都是ChaincodeStub
的一些接口方法,比如GetState(),PutState()
等等。而这些方法一般在Inovke
或Init
调用。
到这里就破案了,最后还有一个sendReceive()
方法再看下,在core/chaincode/shim/handler.go
的129行:
//sends a message and selects
func (handler *Handler) sendReceive(msg *pb.ChaincodeMessage, c chan pb.ChaincodeMessage) (pb.ChaincodeMessage, error) {
errc := make(chan error, 1)
// 异步发送消息,例如在GetState()方法调用好以后
// 生成一个消息,类型为ChaincodeMessage_GET_STATE
handler.serialSendAsync(msg, errc)
//the serialsend above will send an err or nil
//the select filters that first error(or nil)
//and continues to wait for the response
//it is possible that the response triggers first
//in which case the errc obviously worked and is
//ignored
for {
select {
case err := <-errc:
if err == nil {
continue
}
//would have been logged, return false
return pb.ChaincodeMessage{}, err
case outmsg, val := <-c:
// c就是传进来的respchan,是在createChannel时创建的
if !val {
return pb.ChaincodeMessage{}, errors.New("unexpected failure on receive")
}
return outmsg, nil
}
}
}
OK,到这里以后整个处理逻辑就通了,以invoke
为例:
peer
节点发送ChaincodeMessage_TRANSACTION
消息给链码侧handleTransaction()
方法,并最终调用链码的Invoke()
方法Invoke()
中没有调用其他操作账本的方法,例如GetState(),PutState()
等等,那么handleTransaction()
最终会返回一个ChaincodeMessage_COMPLETED
到peer
节点createChannel()
GetState()
方法类型为ChaincodeMessage_GET_STATE
),发送到peer
节点,由peer
节点再做相应的处理peer
节点处理完以后再发送一个ChaincodeMessage_RESPONSE
类型的消息给链码侧handleTransaction()
中,再返回一个ChaincodeMessage_COMPLETED
到peer
节点链码侧的分析就到这里了,下面看看peer
节点如何处理handleReady
的
代码在core/chaincode/handler.go
的205行:
func (h *Handler) handleMessageReadyState(msg *pb.ChaincodeMessage) error {
switch msg.Type {
// 如果是ChaincodeMessage_COMPLETED和ChaincodeMessage_ERROR类型则发送通知,这个方法一会再看
case pb.ChaincodeMessage_COMPLETED, pb.ChaincodeMessage_ERROR:
h.Notify(msg)
// 根据不同的消息类型做相应的处理逻辑
case pb.ChaincodeMessage_PUT_STATE:
go h.HandleTransaction(msg, h.HandlePutState)
case pb.ChaincodeMessage_DEL_STATE:
go h.HandleTransaction(msg, h.HandleDelState)
case pb.ChaincodeMessage_INVOKE_CHAINCODE:
go h.HandleTransaction(msg, h.HandleInvokeChaincode)
case pb.ChaincodeMessage_GET_STATE:
go h.HandleTransaction(msg, h.HandleGetState)
case pb.ChaincodeMessage_GET_STATE_BY_RANGE:
go h.HandleTransaction(msg, h.HandleGetStateByRange)
case pb.ChaincodeMessage_GET_QUERY_RESULT:
go h.HandleTransaction(msg, h.HandleGetQueryResult)
case pb.ChaincodeMessage_GET_HISTORY_FOR_KEY:
go h.HandleTransaction(msg, h.HandleGetHistoryForKey)
case pb.ChaincodeMessage_QUERY_STATE_NEXT:
go h.HandleTransaction(msg, h.HandleQueryStateNext)
case pb.ChaincodeMessage_QUERY_STATE_CLOSE:
go h.HandleTransaction(msg, h.HandleQueryStateClose)
case pb.ChaincodeMessage_GET_PRIVATE_DATA_HASH:
go h.HandleTransaction(msg, h.HandleGetPrivateDataHash)
case pb.ChaincodeMessage_GET_STATE_METADATA:
go h.HandleTransaction(msg, h.HandleGetStateMetadata)
case pb.ChaincodeMessage_PUT_STATE_METADATA:
go h.HandleTransaction(msg, h.HandlePutStateMetadata)
default:
return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in ready state", msg.Txid, msg.Type)
}
return nil
}
根据不同的消息类型,最终它们都执行了HandleTransaction()
方法,第一个参数是msg,第二个参数是指定的处理方法,各不相同。那么下面来看下HandleTransaction()
方法,在core/chaincode/handler.go
的251行:
// HandleTransaction is a middleware function that obtains and verifies a transaction
// context prior to forwarding the message to the provided delegate. Response messages
// returned by the delegate are sent to the chat stream. Any errors returned by the
// delegate are packaged as chaincode error messages.
func (h *Handler) HandleTransaction(msg *pb.ChaincodeMessage, delegate handleFunc) {
chaincodeLogger.Debugf("[%s] handling %s from chaincode", shorttxid(msg.Txid), msg.Type.String())
if !h.registerTxid(msg) {
// 防止重复的TXID
return
}
startTime := time.Now()
var txContext *TransactionContext
var err error
// 获取交易上下文
// 对ChaincodeMessage_INVOKE_CHAINCODE单独处理,保证统一上下文
if msg.Type == pb.ChaincodeMessage_INVOKE_CHAINCODE {
txContext, err = h.getTxContextForInvoke(msg.ChannelId, msg.Txid, msg.Payload, "")
} else {
txContext, err = h.isValidTxSim(msg.ChannelId, msg.Txid, "no ledger context")
}
chaincodeName := h.chaincodeID.Name + ":" + h.chaincodeID.Version
meterLabels := []string{
"type", msg.Type.String(),
"channel", msg.ChannelId,
"chaincode", chaincodeName,
}
// 指标相关设置
h.Metrics.ShimRequestsReceived.With(meterLabels...).Add(1)
var resp *pb.ChaincodeMessage
if err == nil {
// 根据不同的类型做不同的处理操作,得到resp
resp, err = delegate(msg, txContext)
}
if err != nil {
// 出错返回ChaincodeMessage_ERROR错误消息
err = errors.Wrapf(err, "%s failed: transaction ID: %s", msg.Type, msg.Txid)
chaincodeLogger.Errorf("[%s] Failed to handle %s. error: %+v", shorttxid(msg.Txid), msg.Type, err)
resp = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChannelId: msg.ChannelId}
}
chaincodeLogger.Debugf("[%s] Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type, resp.Type)
// 这里删除,对应最开始registerTxid中的ADD
h.ActiveTransactions.Remove(msg.ChannelId, msg.Txid)
// 异步发送响应
h.serialSendAsync(resp)
meterLabels = append(meterLabels, "success", strconv.FormatBool(resp.Type != pb.ChaincodeMessage_ERROR))
// 指标相关设置
h.Metrics.ShimRequestDuration.With(meterLabels...).Observe(time.Since(startTime).Seconds())
h.Metrics.ShimRequestsCompleted.With(meterLabels...).Add(1)
}
HandleTransaction()
方法主要是获取当前交易的上下文,然后执行相应的消息类型的方法做相应的处理操作,并最终返回一个ChaincodeMessage_RESPONSE
类型的消息给链码侧。
再来看下Notify()
方法,在core/chaincode/handler.go
的544行:
func (h *Handler) Notify(msg *pb.ChaincodeMessage) {
// 获取交易上下文
tctx := h.TXContexts.Get(msg.ChannelId, msg.Txid)
if tctx == nil {
chaincodeLogger.Debugf("notifier Txid:%s, channelID:%s does not exist for handling message %s", msg.Txid, msg.ChannelId, msg.Type)
return
}
chaincodeLogger.Debugf("[%s] notifying Txid:%s, channelID:%s", shorttxid(msg.Txid), msg.Txid, msg.ChannelId)
// 往交易上下文的ResponseNotifier中发送消息
tctx.ResponseNotifier <- msg
tctx.CloseQueryIterators()
}
主要起的是一个通知的作用,看下ResponseNotifier
这个变量在哪用到了,追溯到了handler.Execute()
方法,在core/chaincode/handler.go
的1240行:
func (h *Handler) Execute(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, msg *pb.ChaincodeMessage, timeout time.Duration) (*pb.ChaincodeMessage, error) {
chaincodeLogger.Debugf("Entry")
defer chaincodeLogger.Debugf("Exit")
txParams.CollectionStore = h.getCollectionStore(msg.ChannelId)
txParams.IsInitTransaction = (msg.Type == pb.ChaincodeMessage_INIT)
txctx, err := h.TXContexts.Create(txParams)
if err != nil {
return nil, err
}
defer h.TXContexts.Delete(msg.ChannelId, msg.Txid)
if err := h.setChaincodeProposal(txParams.SignedProp, txParams.Proposal, msg); err != nil {
return nil, err
}
h.serialSendAsync(msg)
var ccresp *pb.ChaincodeMessage
select {
// 这里用到了,从这里拿响应
case ccresp = <-txctx.ResponseNotifier:
// response is sent to user or calling chaincode. ChaincodeMessage_ERROR
// are typically treated as error
case <-time.After(timeout):
err = errors.New("timeout expired while executing transaction")
ccName := cccid.Name + ":" + cccid.Version
h.Metrics.ExecuteTimeouts.With("chaincode", ccName).Add(1)
case <-h.streamDone():
err = errors.New("chaincode stream terminated")
}
return ccresp, err
}
而如果我们追踪这个Exucte()
方法,可以追溯到模拟提案中的callChaincode()
方法,这个在之前写的文章Hyperledger Fabric从源码分析背书提案过程中有提到。如果能够追溯到这的朋友,恭喜你已经把整个背书模拟提案的流程基本上都搞清楚了!!!!
总结一下吧,总结一下整体的模拟提案的流程,真的是太不容易了!!!
这里假设链码已经安装了,我们从实例化开始
peer
节点收到提案后,执行模拟提案peer
节点判断链码容器是否启动,如果没有启动,则启动链码容器,调用shim.Start()
方法,并最终建立起链码侧与peer
节点两端的handler
的GRPC
通信。
REGISTER
消息给peer
节点,当前链码侧状态为created
peer
节点当前状态为created
,收到REGISTER
消息以后,将其注册到peer
节点的handler
上,发送REGISTERED
消息给链码侧,同时更新peer
节点状态为Established
peer
节点再发送Ready
消息给链码侧,同时更新peer
节点状态为Ready
REGISTERED
消息后,更新链码侧状态为Established
Ready
消息后,更新链码侧状态为Ready
Ready
状态,互相通信peer
节点判断提案是lscc
的deploy
请求,则发送INIT
消息给链码侧INIT
消息以后,调用链码的Init()
方法,并返回COMPLETED
消息peer
节点收到COMPLETED
消息以后,执行Notify()
方法,往交易上下文的响应通道中发送响应数据callChaincode()
方法内部会最终会调用到一个Execute()
方法,监听这个通道,并拿到响应数据看了好多天的源码,能把自己看的解析心得分享出来很开心,希望大家看了解析之后也能对Fabric
模拟提案的整体过程有更深入的了解