HyperLeger Fabric开发(六)——HyperLeger Fabric智能合约(ChainCode)

一、链码(Chaincode)简介

1、链码简介

在Fabric中,智能合约也称为链码(chaincode),分为用户链码和系统链码。系统链码用来实现系统层面的功能,包括系统的配置,用户链码的部署、升级,用户交易的签名和验证策略等;用户链码用于实现用户的应用功能,开发者编写链码应用程序并将其部署到区块链网络上,终端用户通过与网络节点交互的客户端应用程序调用链码。
链码被编译成一个独立的应用程序,运行于隔离的Docker容器中,在链码部署的时候会自动生成链码的Docker镜像。
链码是访问账本的基本方法,一般是用Go等高级语言编写的、实现规定接口的代码。上层应用可以通过调用链码来初始化和管理账本的状态。只要有适当的权限,链码之间也可以互相调用。
链码(Chaincode)是一段由Go语言编写(支持其它编程语言,如Java,NodeJS)并能实现预定义接口的程序。链码运行在一个受保护的Docker容器当中,与背书节点的运行互相隔离。链码可通过客户端提交的交易对账本状态初始化并进行管理。
链码通常处理由网络中的成员一致认可的业务逻辑。链码创建的(账本)状态是与其它链码互相隔离的,因而不能被其它链码直接访问。如果在相同的Fabric网络中,链码在获取相应许可后可以调用其它链码来访问它的账本。
链码被部署在Fabric网络节点上,运行在Docker容器中,并通过gRPC协议与相应的Peer节点进行交互,以操作分布式账本中的数据。

2、链码的背书策略

背书策略是背书节点如何决策交易是否合法的条件。链码实例化时可指定背书策略,当记账节点接收到交易时,会获知相关链码信息,然后检查链码的背书策略,判断交易是否满足背书策略,若满足则标注交易为合法。
背书策略可分为主体Principal(P)和阈值Threshold(T)两部分,具体如下:
A、Principal指定由哪些成员进行背书。
B、Threshold接受两个输入,分别为阈值T和若干个P的集合n,只要交易中包含了n中t个成员的背书则认为交易合法。
背书策略可以指定某几个组织内的任意成员身份进行背书,或者要求至少有一个管理员身份进行背书等等。
T(1, ‘A’, ‘B’) 则需要A,B中任意成员背书。
T(1, ‘A’, T(2, ‘B’, ‘C’))则需要A成员背书或B,C成员同时背书。
目前客户端已经实现对背书策略的支持,可以通过-P来指定背书策略,结合AND、OR来组合成员,完成成员身份(admin、member)的集合。
-P OR ( 'Org1.admin' , AND ('Org2.member' , 'Org3.member') )
上述背书策略指定要么Org1的admin进行背书,或者Org2和Org3的成员同时进行背书,才满足背书策略。

3、系统链码

系统链码与用户链码有相同的编程模型,但系统链码运行在Peer节点,用户链码则在隔离的容器中运行。因此,系统链码内置为Peer节点的可执行文件中,不遵循用户链码的生命周期,安装、实例化、升级不适用于系统链码。
系统链码用于减少Peer节点与用户链码进行gRPC通信的开销,同时权衡管理的灵活性。系统链码只能通过Peer节点的二进制文件升级,必须通过一组固定的参数进行注册,但不具有背书策略。
 Hyperledger Fabric系统链码实现了一系列系统功能,以便系统集成人员能够根据需求对其进行修改与替换。
常见系统链码如下:
生命周期系统链码(LSCC ):处理生命周期管理。
配置系统链码(CSCC):处理在Peer节点上的通道配置。
查询系统链码(QSCC):提供账本的查询API,例如获取区块以及交易。
背书系统链码(ESCC):通过对交易提案响应进行签名来处理背书过程。
验证系统链码(VSCC):处理交易验证,包括检查背书策略以及多进程并发控制。
在修改或者替换系统链码(LSCC、ESCC、VSCC)时必须注意,因为系统链码在主交易执行的路径中。VSCC在将区块提交至账本前,所有在通道的Peer节点会计算相同的验证以避免账本分歧(不确定性)。如果VSCC被改变或者替换,需要特别小心。

二、链码功能

Peer节点主要功能是调用链码执行交易和记账,其中交易执行由背书节点的链码负责,记账功能由记账节点负责。
HyperLeger Fabric开发(六)——HyperLeger Fabric智能合约(链码)_第1张图片
链码与Peer节点的交互过程如下:
A、链码通过gRPC与Peer节点交互,当Peer节点收到客户端的交易提案请求后,会发送一个链码消息对象(包含交易提案信息、调用者信息)给对应的链码。
B、链码调用Invoke方法,通过发送获取数据(GetState)和写入数据(PutState)消息,向Peer节点获取账本状态信息和发送预提交状态。
C、链码发送模拟执行结果给Peer节点,Peer节点对交易提案和模拟执行结果进行背书签名。

三、链码管理

1、链码生命周期

Fabric提供了 package,install,instantiate和upgrade 4个命令管理链码生命周期。
HyperLeger Fabric开发(六)——HyperLeger Fabric智能合约(链码)_第2张图片
通过install安装链码,通过instantiate实例化链码,然后可以通过invoke、query调用链码和查询链码。
如果需要升级链码,则需要先install安装新版本的链码,通过upgrade升级链码。
在install安装链码前,可以通过package打包并签名生成打包文件,然后在通过install安装。
链码在成功install以及instantiate后,链码处于运行状态,能够通过Invoke方法来处理交易,后续能够对链码进行升级。
Hyperledger Fabric API允许与区块链网络中的各个节点(Peer,Order,MSP)进行交互,同时也允许在背书节点上package、install、instantiate以及upgrade链码。CLI可以直接访问Hyperledger Fabric API。
Hyperledger Fabric SDK抽象了Hyperledger Fabric API的细节,以辅助应用程序开发,当然也能用于管理链码生命周期。

2、打包链码

链码包由三个部分组成:
A、由ChaincodeDeploymentSpec(CDS)格式定义的链码。CDS根据代码及其它属性(如名称与版本)定义链码包;
B、一个可选的实例化策略,能够被用作背书的策略进行描述;
C、拥有链码的实体的一组签名。
其中,链码的签名主要目的如下:
A、建立链码的所有权;
B、允许验证链码包中的内容;
C、允许检测链码包是否被篡改。
通道上的链码的实例化交易的创建者能够被链码的实例化策略验证。
链码打包的方法由两种,一种是打包被多个所有者所拥有的链码,需要初始化创建一个被签名的链码包(SignedCDS),然后将其按顺序的传递给其它所有者进行签名;一种是打包单个所有者持有的链码。
创建一个被签名的链码包的命令如下:
peer chaincode package -n sacc -p chaincodedev/chaincode/sacc -v 0 -s -S -i "AND('OrgA.admin')" ccpack.out
-s选项创建一个能被多个所有者签名的链码包,而不是简单的创建一个原始的CDS。一旦-s被指定,如果其它所有者想要签名CDS,则-S选项必须被指定。否则,将会创建一个SignedCDS,除CDS外仅仅包括实例化策略。
-S选项使用被在core.yaml文件中定义的localMspid属性的值标识的MSP对链码包进行签名。
-S选项是可选的。如果创建了一个没有签名的链码包,不能被其它所有者使用signpackage命令进行签名。
-i选项是可选的,允许为链码指定实例化策略。实例化策略与背书策略有相同的格式,指定哪些身份能够实例化链码。本例中仅OrgA的admin能够实例化链码。如果没有实例化策略被指定,将会使用默认的策略,仅允许拥有Peer的MSP的管理员身份实例化链码。

3、签名链码

在创建阶段就被签名的链码包能够交给其它所有者进行检查与签名,支持带外对链码进行签名。
 ChaincodeDeploymentSpec可以选择由所有者集合进行签名,从而创建一个SignedChaincodeDeploymentSpec(SignedCDS)。SignedCDS包括3个部分:
A、CDS包括源代码,链码的名称与版本号;
B、链码的实例化策略,表示为背书策略;
C、链码所有者的列表,由背书策略定义。
当在某些通道上实例化链码时,背书策略是在带外确定的,用于提供合适的MSP主体。如果没指定实例化策略,则默认的策略就是通道的任何MSP管理员。
每一个链码的所有者通过将SignedCDS与链码所有者的身份(例如证书)组合并签署组合结果来背书ChaincodeDeploymentSpec。
一个链码的所有者能够对自己所创建的签名过的链码包进行签名,需要使用如下命令:
peer chaincode signpackage ccpack.out signedccpack.out
ccpack.out、signedccpack.out分别是输入与输出包。signedccpack.out包括一个对链码包附加的签名(通过local msp进行的签名)。

4、安装链码

安装交易将链码的源代码打包成ChaincodeDeploymentSpec(CDS)的规定的格式,然后安装到通道中的背书节点上。
当安装的链码包只包含一个ChaincodeDeploymentSpec时,将使用默认初始化策略并包括一个空的所有者列表。
链码应该仅仅被安装在链码所有者成员的背书节点上,用于实现链码对于网络中其它成员在逻辑上是隔离的。没有链码的Peer节点,不能成为链码交易的背书者,不能执行链码,但作为记账节点仍然能够验证与提交交易到账本上。
安装链码会发送一个SignedProposal到生命周期系统链码(LSCC) 。
使用CLI安装sacc链码的命令如下:
peer chaincode install -n sacc -v 1.0 -p sacc
-n选项指定链码实例名称
-v选项指定链码的版本
-p选项指定链码所在路径,必须在GOPATH路径下
CLI内部创建sacc的SignedChaincodeDeploymentSpec,然后将其发送给本地Peer节点,Peer节点会调用LSCC上的安装方法。为了在Peer节点上安装链码,SignedProposal的签名必须来自于Peer节点的本地MSP管理员之一。

5、实例化链码

实例化调用生命周期系统链码(LSCC)用于创建及初始化通道上的链码。链码能够被绑定到任意数量的通道,以及在每个通道上单独的操作。无论链码安装及实例化到多少个通道上,每个通道的状态都是隔离的。
实例化的创建者必须满足包含在SignedCDS内链码的实例化策略,而且还必须是通道的写入器(作为通道创建的一部分被配置)。可以防止部署链码的流氓实体或者欺骗者在未被绑定的通道上执行链码。
默认的实例化策略是任意的通道MSP的管理员,因此链码实例化交易的创建者必须是通道管理员之一。当交易提案到达背书节点后,背书节点会根据实例化策略验证创建者的签名。在提交实例化交易到账本前,在交易验证时再一次完成该操作。
实例化交易同样设置了通道上的链码的背书策略 。背书策略描述了交易被通道上成员接受的认证要求。
使用CLI去实例化sacc的链码并初始化状态为user1与0,命令如下:
peer chaincode instantiate -n sacc -v 1.0 -c '{"Args":["user1","0"]}' -P "OR ('Org1.member','Org2.member')"
-n选项指定链码实例名称
-v选项指定链码的版本
-c 选项指定链码的调用参数
-P选项指定链码的背书策略
背书策略表示,org1.member或者org2.member必须对调用使用sacc的交易进行签名,以保障交易是有效的。在成功实例化后,通道的链码进入激活状态,可以处理任意的交易提案。交易到达背书节点时,会同时被处理。

6、调用链码

调用链码:
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C mychannel -n sacc -c '{"Args":["invoke","user1","user2","10"]}'
查询链码
peer chaincode query -C mychannel -n sacc -c '{"Args":["query","user1"]}'

7、升级链码

链码的升级通过改变其版本号(作为SignedCDS的一部分)。SignedCDS另外的部分,如所有者及实例化策略都是可选的。然而,链码的名称必须是一致的,否则会被当做另外一个新的链码。
在升级前,必须将新版本的链码安装到有需求的背书节点上。升级也是一种交易,会把新版本的链码绑定到通道中。升级只能在一个时间点对一个通道产生影响,其它通道仍然运行旧版本的链码。
由于可能存在多个版本的链码同时存在,升级过程不会自动删除老版本俩马,用户必须手动操作删除过程。
升级与实例化transaction有一点不同的是:通过现有的chaincode实例化策略检查升级transaction,而不是用新的策略检查。这是为了确保只有当前实例化策略中指定的成员能够升级chaincode。
在升级期间,链码的Init函数也会被调用,执行有关升级的数据或者使用数据重新进行初始化,在升级链码的期间避免对状态进行重置。
安装新版本的链码
peer chaincode install -n sacc -v 1 -p path/to/my/chaincode/v1
upgrade升级链码
peer chaincode upgrade -n sacc -v 1 -c '{"Args":["d", "e", "f"]}' -C mychannel