Hyperledger Fabric(4)链码ChainCode
智能合约,是一个抽象的概念,智能合约的历史可以追溯到 1990s 年代。它是由尼克萨博(Nick Szabo)提出的理念,几乎与互联网同龄。
我们这里所说的智能合约只狭义的指区块链中。它能够部署和运行在区块链环境中,由一段代码来描述相关的业务逻辑。部署后的智能合约在区块链中无法修改,智能合约的执行完全由代码决定,不受人为因素的干扰。一般来说,参与方通过智能合约规定各自权利和义务、触发合约的条件以及结果,一旦该智能合约在区块链环境中运行就可以得出客观、准确的结果。
什么是ChainCode
ChainCode(链码)是智能合约在Fabric区块链网络的实现形式。分为用户链码和系统链码,通常指的是用户链码。链码是访问账本的基本方法,一般是用Go等高级语言编写的、实现规定接口的代码。上层应用可以通过调用链码来初始化和管理账本的状态。只要有适当的权限,链码之间也可以互相调用。
链码被部署在Fabric网络节点上,运行在隔离沙盒(目前为Docker容器)中,并通过gRPC协议与相应的Peer节点进行交互,以操作分布式账本中的数据。
启动Fabric网络后,可以通过命令行或SDK进行链码操作,验证网络运行是否正常。
它扮演的角色如下图所示:
应用程序通过向区块链网络发送交易来调用智能合约,从而操作账本中的状态。
ChainCode的运行方式
在Fabric中交易的处理过程,客户端将提案首先发送到背书节点,背书节点检提案的合法性。如果合法的话,背书节点将通过交易所属的链码临时执行一个交易,并执行背书节点在本地持有的状态副本。
Chaincode应该仅仅被安装于chaincode所有者的背书节点上,链码运行在节点上的沙盒(Docker容器)中,并通过gRPC协议与相应的Peer节点进行交互,以使该chaincode逻辑对整个网络的其他成员保密。
请务必在一条channel上每一个要运行你chaincode的背书节点上安装你的chaincode,其他没有chaincode的成员将无权成为chaincode影响下的交易的认证节点(endorser)。也就是说,他们不能执行chaincode。不过,他们仍可以验证交易并提交到账本上。
ChainCode要在区块链网络中运行,需要经过链码安装和链码实例化两个步骤。
多次安装,一次实例化
在一个区块链子链中,该网络是由“1账本+1通道+N个peer节点”组成。如果我们要手动来搭建Fabric网络的话,即通过命令行的形式来进行ChainCode的安装与实例化。我们需要多次install,一次instance。也就是说,对于整个Fabric网络来说,假设有X个背书节点,那么,我们需要给每个背书节点安装ChainCode,但是在整个网络搭建过程中只需要instance ChainCode一次。因为install 针对的是背书节点,instance 针对的是通道。
install 链码的对象是背书节点,主要目的是方便背书节点对运行链码,对交易进行模拟。
instance 链码的对象是channel,主要目的是为了将安装过的链码在指定通道上进行实例化调用,在节点上创建容器启动,并执行初始化操作。实例化的过程中,需要指定背书策略,来确定通道上哪些节点执行的交易才能添加到账本中。
安装的过程其实就是对指定的代码进行编译打包,并把打包好的文件发送到Peer,等待接下来的实例化。下面链码的生命周期就是指这个过程。
实例化链上代码主要是在Peer所在的机器上对前面安装好的链上代码进行包装,生成对应Channel的Docker镜像和Docker容器。并且在实例化时我们可以指定背书策略。
- Chaincode运行在一个受保护的Docker容器当中,与背书节点的运行互相隔离。
- Chaincode可通过应用提交的交易对账本状态初始化并进行管理。
ChainCode的生命周期
Hyperledger Fabric API提供了四个管理chaincode生命周期的命令:
- package
- install
- Instantiate
- Upgrade
在未来的版本中,正考虑添加stop和start交易的指令,以便能方便地停止与重启chaincode,而不用非要真正卸载它才行。在一个区块链网络中,将打包、安装、实例化和升级chaincode的过程作为一种可操作的chaincode生命周期函数进行调用。
在这里,我们简单讨论package,signpackage,install,instantiate,upgrade,stop&start这几个生命周期。具体如下图所示:
1)Packaging | 打包
在区块链中,将链码安装到Peer节点,与我们安装普通程序不一样。Fabric对链码的安装有严格的验证和安全机制。
安装好的链码,谁有资格实例化?又该使用什么样的背书策略来调用ChainCode呢?
这一切,都需要在安装之前规定好。我们把这些附加信息+ChainCode源码统称为ChainCode包。
一般而言,ChainCode包由以下三个部分组成:
- chaincode本身,其由ChaincodeDeploymentSpec(Chaincode部署规范)来定义。ChaincodeDeploymentSpec,以下简称CDS。CDS是根据代码及一些其他属性(名称,版本等)来定义chaincode。
- 一个可选的实例化策略,该策略可被背书策略描述。
- 一组表示chaincode所有权的签名。
打包的过程,就是将这三部分信息打包成一个整体。一般来说,打包chaincode有两种方式:
- 第一种是当你想要让chaincode有多个所有者的时候,此时就需要让chaincode包被多个所有者签名。这种情况下需要我们创建一个被签名的chaincode包(SignedCDS),这个包依次被每个所有者签名。
- 另一种就比较简单了,让chaincode有一个所有者的时候,此时只要被一个人签名即可。
要创建一个签名过的chaincode包,请用下面的指令:
peer chaincode package -n mycc -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -v 0 -s -S -i "AND('OrgA.admin')" ccpack.out
- -s:选项创建了一个可被多个所有者签名的包,而非简单地创建一个CDS。如果使用-s,那么当其他所有者要签名的时候,-S也必须同时使用。否则,该过程将创建一个仅包含实例化策略的签名chaincode包(SignedCDS)。
- -S选项可以使在core.yaml文件中被localMspid相关属性值定义好的MSP对包进行签名。该选项是可选的。不过,如果我们创建了一个没有签名的包,那么它就不能被任何其他所有者用signpackage指令进行签名。
- -i选项也是可选的,它允许我们为chaincode指定实例化策略。实例化策略与背书策略格式相同,它指明谁可以实例化chaincode。
2)signpackage | 签名并打包
一个在创建时就被签名的chaincode包可以交给其他所有者进行检查与签名。具体的工作流程支持通道外对chaincode包签名。
ChaincodeDeploymentSpec可以选择被全部所有者签名并创建一个SignedChaincodeDeploymentSpec(SignedCDS),SignedCDS包含三个部分:
- CDS包含chaincode的源码、名称与版本
- 一个chaincode实例化策略,其表示为背书策略
- chaincode所有者的列表,由Endorsement定义
一个chaincode所有者可以对一个之前创建好的带签名的包进行签名,具体使用如下指令:
peer chaincode signpackage ccpack.out signedccpack.out
指令中的ccpack.out和signedccpack.out分别是输入与输出包。
signedccpack.out则包含一个用本地MSP对包进行的附加签名。
3)install | 安装
install的过程会将chaincode的源码以一种被称ChaincodeDeploymentSpec(CDS)的规定格式打包,并把它安装在一个将要运行该chaincode的peer节点上。
在Fabric网络中,我们需要在一个channel上每一个要运行你chaincode的背书节点上安装你的chaincode。
Chaincode应该仅仅被安装于chaincode所有者的背书节点上,以使该chaincode逻辑对整个网络的其他成员保密。其他没有chaincode的成员将无权成为chaincode影响下的交易的认证节点(endorser)。对于认证节点来说,他们不能执行chaincode。不过,他们仍可以验证交易并提交到账本上。
下面安装chaincode,使用CLI安装一个存放在sacc目录下的chaincode时,命令如下
peer chaincode install -n asset_mgmt -v 1.0 -p sacc
-n:name 对链码进行命令
-v: version 指定chaincode的版本
-p: path 指明chainCode的路径
在CLI内部会为sacc创建SignedChaincodeDeploymentSpec,并将其发送到本地peer节点。这些节点会调用LSCC上的Install方法。
4)instantiate | 实例化chaincode
实例化ChainCode过程会调用生命周期系统chaincode(LSCC)在一个channel上创建并初始化一段chaincode。实例化过程,会生成对应Channel的Docker镜像和Docker容器。并且在实例化时我们可以指定背书策略。
当然,一个实例化交易的创建者必须符合在SignedCDS中chaincode的实例化策略,且必须充当channel的写入器(这会成为channel创建配置的一部分),这对于channel的安全至关重要。
使用CLI去实例化 名为mycc 的chaincode,指令具体如下:
peer chaincode instantiate -n mycc -v 1.0 -c '{"Args":["john","0"]}' -P "OR ('Org1.member','Org2.member')"
-n 实例化链码的名称
-c --ctor,链码的具体执行参数,为json格式
-P --Policy 指定实例化策略
5)upgrade | 升级chaincode
一段chaincode可以通过更改它的版本(SignedCDS的一部分)来随时进行更新。至于SignedCDS的其他部分,比如所有者及实例化策略,都是可选的。不过,chaincode的名称必须一致,否则它会被当做完全不同的另一段chaincode。
在升级之前,chaincode的新版本必须安装在需要它的背书节点上。升级是一个类似于实例化交易的交易,它会将新版本的chaincode与channel绑定。其他与旧版本绑定的channel则仍旧运行旧版本的chaincode。换句话说,升级交易只会一次影响一个提交它的channel。
6)stop&star | 停止与启动
注意,停止与启动生命周期交易的功能还没实现。不过,你可以通过移除chaincode容器以及从每个背书节点删除SignedCDS包来停止chaincode。具体而言,就是删除所有主机或虚拟机上peer节点运行于其中的chaincode的容器,随后从每个背书节点删除SignedCDS。
chainCode的状态总结
在上述的过程中,ChainCode的状态有以下几种。这里做一个小总结:
CDS-ChainCodeSpec 包含链码源码,名称,版本等信息
SignedCDS-SignedChaincodeDeploymentSpec ,包含 CDS、一个chaincode实例化策略,其表示为背书策略、chaincode所有者的列表,由Endorsement定义。
ChainCode的种类
在Fabric中,链码可以分为两种,系统chaincode与用户chaincode。
1)系统ChainCode
系统链码则负责Fabric节点自身的处理逻辑,包括系统配置、背书、校验等工作。这些处理过程最初通过硬编码(Hard-Coded)的方式固化在系统中。Fabric通过系统链码的形式来实现,运行在Peer主进程内,兼顾了逻辑实现和管理的灵活性,以及通信的性能。
例如:一个系统chaincode只能通过peer节点的二进制文件升级。同时,系统chaincode只能以一组编译好的特定的参数进行注册,且不具有背书策略相关功能。
以下是系统chaincode的表格:
Fabric五大类型系统链码。系统链码目前仅支持Go语言,在Peer节点启动时会自动完成注册和部署,以进程内逻辑形式跟主进程进行交互。
2)用户chaincode
用户链码对应用开发者来说十分重要,它提供了基于区块链分布式账本的状态处理逻辑,基于它可以开发出多种复杂的应用。
在超级账本Fabric项目中,用户可以使用Go语言来开发链码,未来还将支持包括Java、JavaScript在内的多种高级语言。
用户链码相关的代码都在core/chaincode路径下。其中core/chaincode/shim包中的代码主要是供链码容器侧调用使用,其他代码主要是Peer侧使用。
3)两者的比较
相同点
系统chaincode与用户chaincode两种的编程模型相同,
不同点
系统chaincode运行于peer节点内 而 用户chaincode运行在一个隔离的容器中。因此,系统chaincode在节点内构建且不遵循上文描述的chaincode生命周期。安装,实例化,升级这三项操作不适用于系统chaincode。
链码的工作流程
我们知道,在Fabric中,链码运行在节点上的沙盒(Docker容器)中,被调用时的基本工作流程如上图所示。
- 首先,用户通过客户端(SDK或CLI),向Fabric的背书节点(endorser)发出调用链码的交易提案(proposal)。
- 然后,节点对提案进行包括ACL权限检查在内的各种检验,通过后则创建模拟执行这一交易的环境。
- 接着,背书节点和链码容器之间通过gRPC消息来交互,模拟执行交易并给出背书结论。
- 最后,客户端收到足够的背书节点的支持后,便可以将这笔交易发送给排序节点(orderer)进行排序,并最终写入区块链。
链码容器的shim层是节点与链码交互的中间层。当链码的代码逻辑需要读写账本时,链码会通过shim层发送相应操作类型的ChaincodeMessage给节点,节点本地操作账本后返回响应消息。在上面的流程中,出现了一个重要的部分,shim层。这里需要再次强调一下,链码容器的shim层是节点与链码交互的中间层。
《区块链核心技术与应用》
《区块链开发实战》
等