用户链码对应用开发者来说十分重要,它提供了基于区块链分布式账本的状态处理逻辑,基于它可以开发出多种复杂的应用。
在超级账本Fabric项目中,用户可以使用Go语言来开发链码,未来还将支持包括Java、JavaScript在内的多种高级语言。
用户链码相关的代码都在core/chaincode路径下。其中core/chaincode/shim包中的代码主要是供链码容器侧调用使用,其他代码主要是Peer侧使用。
12.5.1 基本结构
Fabric中为链码提供了很好的封装支持,用户编写链码十分简单。
下面给出了链码的典型结构,用户只需要关注到Init()和Invoke()函数的实现上,在其中利用shim.ChaincodeStubInterface结构,实现跟账本的交互逻辑:
package main
import (
"errors"
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
type DemoChaincode struct { }
func (t *DemoChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
// more logics using stub here
return stub.Success(nil)
}
func (t *DemoChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response
// more logics using stub here
return stub.Success(nil)
}
func main() {
err := shim.Start(new(DemoChaincode))
if err != nil {
fmt.Printf("Error starting DemoChaincode: %s", err)
}
}
用户链码支持install、instantiate、invoke、query、upgrade、package、signpackage等操作,其生命周期被生命周期管理系统链码(Lifecycle System Chaincode,LSCC)进行管理。
12.5.2 链码与Peer的交互过程
用户链码目前运行在Docker容器中,跟Peer节点之间通过gRPC通道进行通信,双方通过ChaincodeMessage消息进行交互。
ChaincodeMessage消息结构如图12-22所示,其中,Type为消息的类型,TxId为关联的交易的ID,Payload中则存储消息内容。
图12-22 ChaincodeMessage消息结构
消息类型包括REGISTER、REGISTERED、INIT、READY、TRANSACTION、 COMPLETED、ERROR、GET_STATE、PUT_STATE、DEL_STATE、INVOKE_CHAINCODE、RESPONSE、 GET_STATE_BY_RANGE、GET_QUERY_RESULT、QUERY_STATE_NEXT、QUERY_STATE_CLOSE、 KEEPALIVE、GET_HISTORY_FOR_KEY等19种消息,这些消息负责整个用户链码的完整生命周期。
用户链码容器和所属Peer节点之间的主要交互过程如图12-23所示。
典型情况下,链码自注册到Peer开始,一直到被调用过程,主要步骤大致如下:
1)用户链码调用shim.Start()方法后,首先会向Peer发送ChaincodeMessage_REGISTER消息尝试进行注册。之后开始等待接收来自Peer的消息。此时状态为初始的created。
2)Peer收到来自链码容器的ChaincodeMessage_REGISTER消息,注册到本地的一个 handler结构,返回ChaincodeMessage_REGISTERED消息给链码容器。更新状态为established,之后自动发出 ChaincodeMessage_READY消息给链码侧,更新状态为ready。
3)链码侧收到ChaincodeMessage_REGISTERED消息后,不进行任何操作,注册成功。更新状态为established。收到ChaincodeMessage_READY消息后更新状态为ready。
4)Peer侧发出ChaincodeMessage_INIT消息给链码容器,对链码进行进行初始化。
5)链码容器收到ChaincodeMessage_INIT消息,调用用户链码代码Init()方法进行初始化,成功后,返回ChaincodeMessage_COMPLETED消息。此时,链码容器可以被调用了。
6)链码被调用时,Peer发出ChaincodeMessage_TRANSACTION消息给链码。
图12-23 链码消息交互
7)链码收到ChaincodeMessage_TRANSACTION消息,会调用Invoke()方法,根据Invoke方法中用户实现的逻辑,可以发出以下消息给Peer侧:
·ChaincodeMessage_GET_HISTORY_FOR_KEY
·ChaincodeMessage_GET_QUERY_RESULT
·ChaincodeMessage_GET_STATE
·ChaincodeMessage_GET_STATE_BY_RANGE
·ChaincodeMessage_QUERY_STATE_CLOSE
·ChaincodeMessage_QUERY_STATE_NEXT
·ChaincodeMessage_INVOKE_CHAINCODE
Peer侧收到这些消息,进行相应的处理,并回复ChaincodeMessage_RESPONSE消息。最后,链码侧会回复调用完成的消息ChaincodeMessage_COMPLETE给Peer侧。
8)在上述过程中,Peer和链码侧还会定期的发送haincodeMessage_KEEPALIVE消息给对方,以确保彼此在线。
12.5.3 链码处理状态机
无论是链码容器侧还是Peer侧,都是根据收到的消息触发处理逻辑,然后进入下一个状态,十分适合采用有限状态机 (Finite State Machine,FSM)结构进行描述。Fabric项目采用了github.com/looplab/fsm包实现了基于状态机的链码逻辑处理。
1.链码容器侧处理
链码侧主要实现了Handler结构体来处理消息,并通过各种handleXXX方法具体实现来自Chaincode接口中定义的各种对账本的操作:
Handler结构体实现代码在core/chaincode/shim/handler.go文件,主要定义如下:
type Handler struct {
sync.RWMutex
// shim to peer grpc serializer. User only in serialSend
serialLock sync.Mutex
To string
ChatStream PeerChaincodeStream
FSM *fsm.FSM
cc Chaincode
// Multiple queries (and one transaction) with different txids can be executing
in parallel for this chaincode
// responseChannel is the channel on which responses are communicated by the
shim to the chaincodeStub.
responseChannel map[string]chan pb.ChaincodeMessage
nextState chan *nextStateInfo
}
Handler结构体的主要成员包括:
·ChatStream:跟Peer进行通信的gRPC流;
·FSM:最重要的事件处理状态机,根据收到不同事件调用不同方法;
·cc:所面向的链码;
·responseChannel:本地chan,字典结构,key是TxID,value里面可以放上一些消息,供调用者后面使用;
·nextState:本地chan,可以存放下一步要进行的操作和数据。
链码侧对链码消息处理的状态机如图12-24所示,最关键的是在ready状态下处理来自Peer的各种消息。
2.Peer侧状态机
Peer侧也通过了一个Handler结构体来处理链码容器侧过来的各种消息。该结构体的定义在core/chaincode/handler.go文件中,定义代码如下所示:
type Handler struct {
sync.RWMutex
// peer to shim grpc serializer. User only in serialSend
serialLock sync.Mutex
ChatStream ccintf.ChaincodeStream
FSM *fsm.FSM
ChaincodeID *pb.ChaincodeID
ccInstance *sysccprovider.ChaincodeInstance
chaincodeSupport *ChaincodeSupport
registered bool
readyNotify chan bool
// Map of tx txid to either invoke tx. Each tx will be
// added prior to execute and remove when done execute
txCtxs map[string]*transactionContext
txidMap map[string]bool
// used to do Send after making sure the state transition is complete
nextState chan *nextStateInfo
policyChecker policy.PolicyChecker
}
图12-24 链码侧状态机
Handler结构体的主要成员包括:
·ChatStream:跟链码侧进行通信的gRPC通道;
·FSM:核心的事件处理状态机,根据收到不同事件调用不同方法;
·ChaincodeID:所关联的链码ID;
·ccInstance:代表链码实例;
·chaincodeSupport:提供对链码的执行、注册、启动、停止等操作;
·registered:是否已注册;
·readyNotify:本地chan,通知就绪状态;
·txCtxs:字典结构,存储交易ID到交易的映射;
·nextState:本地chan,存储状态切换的相关数据;
·policyChecker:根据通道策略,对签名进行检查等。
Peer侧对链码消息处理的状态机如图12-25所示,最主要的是在ready状态下处理来自链码侧的各种消息。
图12-25 Peer侧状态机
来源:我是码农,转载请保留出处和链接!
本文链接:http://www.54manong.com/?id=901