作为区块链应用开发人员,智能合约的开发是必不可少的,在Hyperledger Fabric中,智能合约又称为链玛(chiancode)。

 

链码的概念

链码又称之为链上代码,一般由开发人员使用Golang(java或nodejs)编写,提供分布式账本的状态处理逻辑。链码被部署在Hyperledger Fabric的网络节点上,能够独立运行在安全且受保护的Docker容器中,以gRPC协议与相应的Peer节点进行通信,操作分布式账本中的数据。在Fabric中,链码分为系统链码用户链码

系统链码

系统链码负责Fabric节点自身的处理逻辑,包括系统配置、背书、校验等工作。目前,系统链码只支持Golang语言。Peer节点启动的同时,也会注册和部署相应系统链码。这里,我们开发人员不需要关心太多有关系统链码的编写。

用户链码

与系统链码不同,用户链码是由开发人员根据不同场景需求以及成员制定的相关规则,可以使用Golang、java或nodejs来编写。

用户链码是Fabric应用程序中占据着重要地位。它向下可以对账本数据进行操作,向上可以给企业级应用程序提供api接口。所以,一个没有链码的企业级应用程序,不能称为区块链应用程序。

链码的生命周期管理

链码在编写完成之后,还需要经过一系列的操作才能应用在Fabric网络中,这一系列操作由以下5个命令来进行:

  1. install: 将已编写的链码安装在指定的peer节点中。
  2. instantiate: 对已安装的链码进行实例化。
  3. upgrade: 对已有的链码进行升级。链代码可以在安装后根据具体需求的变化进行升级。
  4. package: 对指定的链码进行打包
  5. singnpackage: 对已打包的文件进行签名。

install、instantiate、upgrade这3个操作只适用于用户链码,不适用于系统链码。链码在经过安装和初始化之后便于运行了起来,之后才可以调用链码。

链码的编写

开放基于Hyperledger Fabric的分布式账本应用程序,编写链码是一重要环节。Hyperledger Fabric提供了有关链码编写的SDK,

  • NodeJs SDK: https://github.com/hyperledger/fabric-sdk-node
  • Java SDK: https://github.com/hyperledger/fabric-sdk-java
  • Python SDK: https://github.com/hyperledger/fabric-sdk-py
  • Go SDK: https://github.com/hyperledger/fabric-sdk-go

这里我们将使用golang的SDK进行了链码的开发,在编写之前,需要将与Hyperledger Fabric Go相关的API下载至本地系统中。

下载命令如下:

$ go get -u github.com/hyperledger/fabric/core/chaincode/shim
$ go get -u github.com/hyperledger/fabric/protos/peer

还可以将整个fabric的SDK下载到本地,这种情况需要将git分支切换到release-1.2版本,master分支不包含shim包

链码接口

启动链码需要调用shim包的Start函数,Start函数调用时需要传递一个类型为Chaincode的参数,Chaincode是一个接口类型,该接口包含两个未实现方法:Init与Invoke。
 

type Chaincode interface{
    Init(stub ChaincodeStubInterface) peer.Response
    Invoke(stub ChaincodeStubInterface) peer.Response
}

编写链码其实就是实现Chaincode中的Init和Invoke方法。在链码被初始化或者升级(instantiate或upgrade)时会调用Init方法,在被调用或者查询(invoke或query)时会调用Invoke方法

链码结构

shim包为链码提供了用来访问/操作数据状态、事物上下文和调用其他链代码的相关API;peer包提供了链码执行后的响应信息。开发链码需要引入shim包和peer包:

package main

import (
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
)

//结构体
type SimpleChaincode struct{}

//结构体实现Chaincode的Init方法
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
	//实现链码初始化或者升级时的处理逻辑
}

//结构体实现Chaincode的Invoke方法
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
	//实现链码运行时被调用或者查询时的处理逻辑
}

//主函数,每个链码都需要有个主函数,固定写法
func main(){
	err := shim.Start(new(SimpleChaincode))
	if err != nil{
		fmt.Println("启动SimpleChaincode时发生错误")
	}

}

需要注意的是: 

  1. 因为链码是一个可独立运行的应用程序,所以必须声明在一个main包中并提供相应的main函数作为应用入口。
  2. 链码可以是一个工程(多个go文件,多个包),这个go工程需要有一个main函数入口。

这里不再赘述shim包下的API了,有兴趣的读者可以查阅官方文档。