想知道更多关于区块链技术知识,请百度【链客区块链技术问答社区】 链客,有问必答!
智能合约可以部署和运行在区块链环境中,由一段代码来描述相关的业务逻辑。部署后的智能合约在区块链中没法修改,智能合约的执行彻底由代码决定,不受人为因素的干扰。通常来讲,参与方经过智能合约规定各自权利和义务、触发合约的条件以及结果,一旦该智能合约在区块链环境中运行就能够得出客观、准确的结果。
在 Fabric 中,智能合约也称为链码(chaincode),分为用户链码和系统链码,一般指的是用户链码。链码是访问帐本的基本方法,通常是用Go等高级语言编写的、实现规定接口的代码。上层应用能够经过调用链码来初始化和管理帐本的状态。只要有适当的权限,链码之间也能够互相调用。git
链码实例化时可指定背书策略,当确认节点接收到交易时,节点获知相关链码信息,而后检查该链码的背书策略,判断交易是否知足背书策略,若知足则标注交易为合法。
背书策略可分为主体 principal(P )和阈值 threshold(T) 两部分,具体以下:
1)principal 指定由哪些成员进行背书。
2)threshold 接受两个输入,分别为阈值t和若干个P的集合n,只要交易中包含了 n 中 t 个成员的背书则认为交易合法。
例如:
T(1, ‘A’, ‘B’) 则须要 A,B 中任意成员背书。 T(1, ‘A’, T(2, ‘B’, ‘C’)) 则须要 A成员背书或 B,C 成员同时背书。github
链码的在开发过程当中须要实现链码接口,交易的类型决定了哪一个接口函数将会被调用,如 instantiate 和 upgrade 类型会调用链码的Init接口,而 invoke 类型的交易则调用了链码的 Invoke 接口。链码的接口定义以下:(本文来自公众号:亨利笔记)
type Chaincode interface {
Init(stub ChaincodeStubInterface) pb.Response
Invoke(stub ChaincodeStubInterface) pb.Response
}
下面经过一个例子讲解链码的开发流程,示例链码根据交易的类型建立键值对并记录到帐本中,或者根据键名到帐本中查找与之相对应的值。
请先确保 Go 语言环境已经安装而且正确设置 GOPATH 环境变量。
(1)建立链码存放目录
建立keyValueStore目录以存放链码,同时进入目录
mkdir $GOPATH/src/keyValueStore
cd $GOPATH/src/keyValueStore
建立并编辑链码文件 keyValueStore.go 。
(2)链码源代码分析
1)导入头文件。
链码必须依赖 chaincode shim 包和 peer protobuf 包,它们分别用于链码的控制与数据传输,其次定义 KeyValueStore 类型,做为 chaincode shim 的载体。
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
type KeyValueStore struct {
}
2)实现Init方法。
Init 方法经过 shim.ChaincodeStubInterface 接口来获取实例化链码交易的相关信息,该接口的 GetStringArgs 方法可获取交易传给链码的参数。链码实例化时接收key 和 value 两个参数,所以先对参数个数进行验证,若验证经过,则第一个和第二个参数分别做为 key 和 value 存入到帐本中。
把状态存入帐本须要借助 shim.ChaincodeStubInterface 接口 PutState 方法来完成,因为帐本中的数据都以键值对的形式储存,所以该方法也只接受 key,value两个参数,其中 value 为 byte 格式,里面还包含多个 json 格式的键值对。
因为执行结果须要以消息的形式返回给客户端,所以还须要把返回消息封装成 fabric/protos/peer 中 Response 格式。
值得注意的是,链码升级的时候都会调用 Init 方法,编写升级链码时应注意 Init 方法的实现,以免从新初始化或覆盖上一版本的帐本状态。docker
3)实现Invoke方法。
与Init方法相似,Invoke 方法经过 shim.ChaincodeStubInterface 的 GetFunctionAndParameters 方法来获取 invoke 交易的参数,其中返回的 fn 与 args 分别为交易调用的具体函数名以及相应参数,此时 Invoke 方法进一步判断fn的值以进行下一步操做(set或者get),并把操做结果存放在 result 变量中以返回操做结果。json
为了完成对帐本的读写,链码还须要实现如下两个方法:
set:把输入的键值对记录在帐本中
get:根据键读取帐本中与之对应的值
4)实现get和put方法。
正如前面所说,invoke 方法根据 fn 的值来执行相应的 get 或 put 函数,这两个函数也须要 shim.ChaincodeStubInterface 接口来访问帐本数据。bash
5)实现主函数main():
链码须要在main函数中调用shim.Start()方法用于链码的部署。网络
(3)测试链码
链码的测试须要经过完整的Fabric网络,使用官方提供的例子能够快速构建测试网络,从而简化链码的开发流程。这里介绍搭建测试网络的步骤:
1)安装示例代码库。
2)进入 fabric-samples 目录。
$ cd
$GOPATH/src/github.com/hyperledger/fabric-samples
3)把新编写的链码放入fabric-samples的chaincode目录下。
$ cp -r
$GOPATH/src/keyValueStore ./chaincode
4)进入chaincode-docker-devmode目录并启动网络,命令中会建立了一个名称为myc的通道。
$ cd chaincode-docker-devmode
$ docker-compose -f docker-compose-simple.yaml up -d
5)进入chaincode容器,编译并运行链码。
$ docker exec -it chaincode
$ cd keyValueStore && go build
$ export CORE_PEER_ADDRESS=peer:7051
$ export CORE_CHAINCODE_ID_NAME=mycc:0
$./keyValueStore
$ exit
6)进入CLI容器并初始化链码,链码ID为mycc,版本号为0,部署的通道名称是myc。
$ docker exec -it cli bash
$ peer chaincode install -p chaincodedev/chaincode/keyValueStore -n mycc -v 0
$ peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
7)Invoke和Query链码。
$ peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
$ peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
$ peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
正常状况下,两次 query 返回的结果分别为 10 和 20。
开发链码时能够经过上述过程进行测试,但需避免使用相同的链码 ID 以避免链码实例化失败。另外,对于链码升级来讲,链码的 ID 应该保持不变,同时新链码的版本号须要比先前实例化的版本高,并经过 upgrade 交易来更新链码在通道中的状态。
假设对链码 keyValueStore.go 进行了更改,并把最新的链码保存在$GOPATH/src/keyValueStoreNew 下,则升级链码的操做以下:
1)进入fabric-samples目录并拷贝最新链码到chaincode目录。
$ cd $GOPATH/src/fabric-samples
$ cp -r $GOPATH/src/keyValueStoreNew ./chaincode
2)进入chaincode容器,编译并运行更新后的链码。
$ docker exec -it chaincode bash
$ cd keyValueStoreNew && go build
$ export CORE_PEER_ADDRESS=peer:7051
$ export CORE_CHAINCODE_ID_NAME=mycc:1
$ ./keyValueStoreNew
$ exit
3)进入cli容器并升级链码。
$ docker exec -it cli bash
$ peer chaincode install -p chaincodedev/chaincode/keyValueStoreNew -n mycc -v 1
$ peer chaincode upgrade -n mycc -v 1 -c '{"Args":["a","10"]}' -C myc
到此升级链码完毕,能够对最新的链码mycc进行操做。函数