什么是Chaincode(智能合约)?
chaincode是一个程序,它是使用Go语言编写的,最终在Java等其他编程语言中实现了指定的接口。chaincode运行在一个被背书peer进程独立出来的安全的Docker容器中。chaincode通过应用程序提交的事务初始化和管理账本状态。
chaincode通常处理被网络成员认可的业务逻辑,因此它被认为是一种“智能合约”。由chaincode创建的状态只作用于该chaincode,而不能通过另一个chaincode直接访问。但是,在同一个网络中,给定适当的权限,chaincode可以调用另一个chaincode来访问它的状态。
在下面的部分中,我们将通过区块链通信产品应用方案供应商诺亚的视角来探索chaincode。我们的兴趣将更加关注诺亚对于chaincode生命周期的操作;在区块链网络中,打包、安装、实例化和升级chaincode的过程,是chaincode的操作生命周期的一个功能。
智能合约生命周期
Hyperledger Fabric的API支持与区块链网络中的各个节点进行交互——peers、orderers和MSPs——它还允许其中一个在支持的背书节点上打包、安装、实例化和升级chaincode。尽管Hyperledger Fabric可以用来管理chaincode的生命周期,但它还是提供了特定语言的sdk抽象了Hyperledger Fabric API的细节,以促进应用程序的开发。另外,可以通过CLI直接访问Hyperledger Fabric API,我们将在此文档中使用它。
官方提供了四个命令来管理一个chaincode的生命周期: package、install、instantiate和upgrade。在未来的版本中,官方也正在考虑添加stop和start命令操作事务来禁用和重新启用chaincode,而不必实际卸载它。在成功安装并实例化了一个chaincode之后,chaincode就处于活跃中(正在运行),并且可以通过调用事务处理事务。在安装完毕后,也可以在任何时间都对chaincode进行升级。
Packaging(包)
chaincode包由3部分组成:
- chaincode由chaincode部署规范(ChaincodeDeploymentSpec)或CDS定义。CDS是根据代码和其他属性(如名称和版本)定义了chaincode包。
- 一个可选的实例化策略,它可以在策略上用相同的策略来描述,用于支持和在背书策略中描述。参考Hyperledger Fabric Endorsement policies——背书策略
- 由“拥有”chaincode的实体的一组签名。
这些签名有以下目的:
- 为了建立chaincode的所有权;
- 允许对包的内容进行验证;
- 允许检测包是否篡改
一个channel上的chaincode实例化事务的创建者是通过chaincode的实例化策略来验证的。
创建package(包)
打包chaincode有两种方法。一种是当想要一个chaincode拥有多个所有者时,需要使用多个身份标识为该chaincode签名。这个工作流程需要我们首先创建一个已签名的chaincode(一个签署的CDS),然后通过序列的方式将其传递给其他所有者来签署。
更简单的工作流程是正在发行安装事务的节点的身份签名时部署已签署的CDS。
首先将处理更复杂的情况。但是,如果不需要担心多个所有者,那么可以跳过下面的安装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参数。否则,这个进程会创建一个除了CDS实例化策略之外的已签署CDS。
-S参数使用在core.yaml中由localMspid属性值标识的MSP来指示该程序的签名。
-S参数是可选的。但是,如果一个包是在没有签名的情况下创建的,那么它就不能由任何其他所有者使用signpackage命令来签署。
-i参数是可选的,即指定chaincode实例化策略。实例化策略与背书策略具有相同的格式,并指定哪些id可以实例化chaincode。在上面的示例中,只允许使用OrgA的admin实例化链代码。如果没有提供策略,则使用默认策略,这将只允许peer中MSP的admin身份来实例化chaincode。
包签名(Package signing)
一个已经被签名的chaincode包在被创建时候可以交由其它所有者检查并签名,这个工作流程支持chaincode包带外签署。
ChaincodeDeploymentSpec视需要也许会有共同的全部所有者签名去创建一个SignedChaincodeDeploymentSpec(或SignedCDS),SignedCDS包含了3个元素:
- CDS包含了chaincode的源码、名称和版本信息。
- chaincode的实例化策略,即表示背书策略。
- chaincode的所有者列表,以背书的方式定义。
注意:当chaincode在某些channel上实例化时,此背书策略是由带外决定的,以提供适当的MSP原则。如果没有指定实例化策略,则默认策略是channel的任何MSP的admin。
每个所有者通过将其与所有者的身份(例如证书)相结合,并签署结合后的结果来为ChaincodeDeploymentSpec背书。
一个chaincode所有者可以使用下面的命令来签署一个以前创建的签名包:
peer chaincode signpackage ccpack.out signedccpack.out
ccpack.out和signedccpack.out分别是输入和输出包。signedccpack.out包含了使用本地MSP签名的包的附加签名。
安装chaincode
安装事务将chaincode的源代码打包成一种指定的格式,称为ChaincodeDeploymentSpec(chaincode部署规范或CDS),并将其安装到运行该chaincode的peer节点上。
注意:必须在channel中的每个背书节点上安装chaincode,以运行chaincode。
当安装API被简单地给出一个ChaincodeDeploymentSpec时,它将默认实例化策略,并包含一个空的所有者列表。
注意:chaincode只应该安装在对chaincode拥有的成员的背书peer节点上,以保护网络中其他成员的chaincode逻辑的机密性。那些没有chaincode的成员,不能成为chaincode交易的背书人;也就是说,它们不能执行chaincode。但是,它们仍然可以验证并将事务提交到账本上。
要安装一个chaincode,请将一个签署的提案发送到system chaincode(系统智能合约)其中被描述为生命周期系统智能合约( lifecycle system chaincode —— LSCC)的部分。例如,要安装使用CLI的简单资产chaincode中描述的sacc示例chaincode,该命令将如下所示:
peer chaincode install -n asset_mgmt -v 1.0 -p sacc
CLI容器内执行创建的SignedChaincodeDeploymentSpec sacc,并将其发送给本地peer,本地peer会调用LSCC上的安装方法。对-p选项的参数指定了chaincode的路径,它必须位于用户的GOPATH的源码树中,例如$GOPATH/src/sacc。有关命令选项的完整描述,后面将会讲到。
要注意一点,为了在peer上安装,签署的提案的签名必须来自peer的本地MSP管理员的一个签名。
chaincode实例化
实例化事务调用生命周期系统chaincode(LSCC)来创建和初始化一个channel上的chaincode。这是一个chaincode-channel绑定过程:chaincode可以绑定到任意数量的channel,并分别在每个channel上独立操作。换句话说,不管chaincode安装和实例化了多少个其他channel,状态都被隔离到一个事务提交的channel上。
实例化事务的创建者必须满足在SignedCDS中包含的chaincode的实例化策略,并且该创建者作为创建该channel配置信息的一部分,也必须是channel上的一个写入者。这对于channel的安全性来说是非常重要的,它可以防止恶意实体部署chaincode或欺骗成员在一个未绑定的channel上执行chaincode。
例如,默认的实例化策略是任何channel上的MSP管理员,因此一个chaincode实例化事务的创建者必须是channel管理员的成员。当事务提案到达背书人(节点)的时候,它将验证创建者的签名与实例化策略。并且在将其提交给账本之前,在事务验证期间再次执行此操作。
实例化事务还为channel上的chaincode设置了背书策略。背书策略描述了交易结果的认证要求,被该channel的所有成员所接受。
例如,使用CLI实例化sacc chaincode,并使用john和0初始化状态,命令将如下所示:
peer chaincode instantiate -n sacc -v 1.0 -c '{"Args":["john","0"]}' -P "OR ('Org1.member','Org2.member')"
注意:签注策略(CLI使用波兰表示法),它需要来自Org1或Org2的任意成员的支持,以支持所有的事务到sacc。也就是说,无论是Org1或Org2都必须签署在sacc上执行调用的结果,以使事务是有效的。
【波兰表示法(Polish notation,或波兰记法),是一种逻辑、算术和代数表示方法,其特点是操作符置于操作数的前面,因此也称做前缀表示法。如果操作符的元数(arity)是固定的,则语法上不需要括号仍然能被无歧义地解析。波兰记法是波兰数学家扬·武卡谢维奇1920年代引入的,用于简化命题逻辑。】
在chaincode成功实例化之后,chaincode在channel上进入活跃状态,并准备处理任何背书事务类型支持的事务协议。这些事务是并发处理的,因为它们到达了背书peer。
chaincode升级
任何时候,chaincode都可以通过更改其版本来进行升级,这是SignedCDS的一部分。其他部分,例如所有者和实例化策略是可选的。但是,chaincode的名称必须是相同的,否则,它将被视为完全不同的chaincode。
在升级之前,必须将chaincode的新版本安装在需要的背书peer上。升级是一个类似于实例化事务的事务,它将chaincode的新版本绑定到channel。其他channels所绑定的旧版本chaincode将会继续运行旧版本chaincode。换句话说,升级事务只会一次影响一个channel,即提交事务的channel。
注意:由于chaincode的多个版本可能同时处于活跃状态,所以升级过程不会自动删除旧版本,因此用户必须暂时管理这个版本。
与实例化事务有一个微妙的区别:升级事务是根据当前的chaincode实例化策略检查的,而不是新策略(如果指定的话)。这是为了确保在当前的实例化策略中指定的现有成员可以升级chaincode。
注意:在升级过程中,调用chaincode Init函数来执行任何与数据相关的更新或重新初始化它,因此在升级chaincode时必须注意避免重新设置状态。
停止和启动chaincode
注意,停止和启动生命周期事务还没有实现。但是,可以通过从每个背书人中删除chaincode容器和SignedCDS包来手动停止chaincode。这是通过在背书peer节点运行的每个主机或虚拟机上删除chaincode的容器来完成的,然后从每个背书peer节点上删除SignedCDS。
TODO-为了从peer节点删除CDS,首先需要进入peer节点的容器。我们确实需要提供一个能够执行此功能的实用程序脚本。
docker rm -f <container id> rm /var/hyperledger/production/chaincodes/<ccname>:<ccversion>
Stop在工作流程中是有用的,可以在控制方式上进行升级,在进行升级之前,可以在所有peer上停止一个chaincode。
CLI(客户端)
官方正在评估为Hyperledger Fabric peer二进制文件分发特定平台的二进制文件的需求。目前,可以简单地从运行的docker容器中调用命令。
要查看当前可用的CLI命令,请在运行的fabric-peer Docker容器中执行以下命令:
docker run -it hyperledger/fabric-peer bash
# peer chaincode --help
注意:可使用docker exec -it cli(容器名) bash命令进入cli
这显示了与下面示例相似的输出:
Usage: peer chaincode [command] Available Commands: install Package the specified chaincode into a deployment spec and save it on the peer's path. instantiate Deploy the specified chaincode to the network. invoke Invoke the specified chaincode. list Get the instantiated chaincodes on a channel or installed chaincodes on a peer. package Package the specified chaincode into a deployment spec. query Query using the specified chaincode. signpackage Sign the specified chaincode package upgrade Upgrade chaincode. Flags: --cafile string Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint -h, --help help for chaincode -o, --orderer string Ordering service endpoint --tls Use TLS when communicating with the orderer endpoint --transient string Transient map of arguments in JSON encoding
全局flags:--logging-level string
默认的日志记录level和overrides,参见core.yaml的完整语法。
–test.coverprofile string Done (default “coverage.cov”)
-v, –version
使用“peer chaincode [command] –help”获取更多关于命令的信息。
为了方便在脚本化的应用程序中使用,peer命令总是在发生命令失败时生成非零返回代码。
chaincode命令的例子:
peer chaincode install -n mycc -v 0 -p path/to/my/chaincode/v0 peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a", "b", "c"]}' -C mychannel peer chaincode install -n mycc -v 1 -p path/to/my/chaincode/v1 peer chaincode upgrade -n mycc -v 1 -c '{"Args":["d", "e", "f"]}' -C mychannel peer chaincode query -C mychannel -n mycc -c '{"Args":["query","e"]}' peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n mycc -c '{"Args":["invoke","a","b","10"]}'
系统智能合约(System chaincode)
系统chaincode具有相同的编程模型,除了它在peer进程中运行,而不是像普通的chaincode那样在一个单独的容器中运行。因此,系统chaincode被构建到peer的可执行文件中,并且不遵循上面描述的相同的生命周期。特别是安装、实例化和升级并不适用于系统chaincode。
系统chaincode的目的是为了在peer和chaincode之间减少gRPC的通信成本,并权衡管理的灵活性。例如,系统chaincode只能用peer二进制进行升级。它还必须注册一个固定的参数集,并且没有背书策略或背书策略功能。
系统chaincode用于Hyperledger Fabric以实现许多系统行为,使它们可以被系统集成商所取代或修改。
当前的系统chaincode列表:
- LSCC( Lifecycle system chaincode ):生命周期系统chaincode处理上面描述的生命周期请求。
- CSCC(Configuration system chaincode):配置系统chaincode在peer端处理channel配置。
- QSCC(Query system chaincode):查询系统chaincode提供了分类查询api,例如获取块和事务。
- ESCC(Endorsement system chaincode):背书系统chaincode通过签署事务提案响应来处理支持。
- VSCC(Validation system chaincode):验证系统chaincode处理事务验证,包括检查背书策略和多版本并发控制。
在修改或替换这些系统chaincode时必须注意,特别是LSCC、ESCC和VSCC,因为它们在主事务执行路径中。值得注意的是,当VSCC在将其提交到账本之前验证一个块,重要的是,channel中的所有peer节点都要计算相同的验证,以避免账本差异(非确定性)。因此,如果VSCC被修改或替换,就需要特别的处理和维护。