【转载请标明出处】:https://blog.csdn.net/qq_25870633/article/details/81751101
之前的文章中我们有讲过了fabric的一些核心的配置文件的选项说明,讲过fabric 的网络启动的步骤,那么,我们会在这篇文章中讲述如何的编写链码,及本地调试链码,及发布链码调用链码等等操作!
其中,一个最基本的空链码结构为如下所示:
package myChainCode
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
"fmt"
)
type FirstChainCode struct {
}
/**
实例化/升级链码时调用的方法
*/
func (self *FirstChainCode)Init(stub shim.ChaincodeStubInterface) peer.Response {
return peer.Response{}
}
/**
客户端/cli使用Query或者Invoke的方式调用链码时调用的方法
*/
func (self *FirstChainCode)Invoke(stub shim.ChaincodeStubInterface) peer.Response {
return peer.Response{}
}
func main() {
if err := shim.Start(new(FirstChainCode)); nil != err {
fmt.Printf("实例化链码失败,err := %n", err.Error())
}
}
先来讲解下链码文件的结构:
首先,上述是每个链码文件都需要实现 ChainCode 接口
在源码包的 github.com/hyperledger/fabric/core/chaincode/shim/interfaces.go 中的 ChainCode 接口为:
package shim
import (
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/hyperledger/fabric/protos/ledger/queryresult"
pb "github.com/hyperledger/fabric/protos/peer"
)
// Chaincode interface must be implemented by all chaincodes. The fabric runs
// the transactions by calling these functions as specified.
type Chaincode interface {
// Init is called during Instantiate transaction after the chaincode container
// has been established for the first time, allowing the chaincode to
// initialize its internal data
Init(stub ChaincodeStubInterface) pb.Response
// Invoke is called to update or query the ledger in a proposal transaction.
// Updated state variables are not committed to the ledger until the
// transaction is committed.
Invoke(stub ChaincodeStubInterface) pb.Response
}
其中,
1、当链码收到 实例化 (instantiate) 或者 升级 (upgrade) 类型交易时,Init 方法会被调用;
2、当链码收到 调用 (invoke) 或者 查询 (query)类型交易时, Invoke 方法会被调用;
下面我们在看一个稍微完善点的链码:
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
"fmt"
)
type SimpleChaincode struct {
}
/**
实例化/升级链码时被自动调用
-c '{"Args":["Hello","World"]'
*/
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
// 获取参数
//args := stub.GetStringArgs()
_, args := stub.GetFunctionAndParameters()
// 判断参数长度是否为2个
if len(args) != 2 {
return shim.Error("指定了错误的参数个数")
}
// 通过调用PutState方法将数据保存在账本中
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error("保存数据时发生错误...")
}
return shim.Success("链码实例化成功~")
}
/**
对账本数据进行操作时被自动调用(query, invoke)
peer chaincode query -n hello -C myc -c '{"Args":["queryData","Hello"]}'
*/
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// 获取调用链码时传递的参数内容(包括要调用的函数名及参数)
fun, args := stub.GetFunctionAndParameters()
if fun == "queryData" {
return queryData(stub, args)
}
return shim.Error("非法操作, 指定功能不能实现")
}
/**
自定义方法
*/
func queryData(stub shim.ChaincodeStubInterface, args []string) peer.Response {
// 检查传递的参数个数是否为1外
if len(args) != 1 {
return shim.Error("只能指定相应的Key")
}
// 根据指定的Key调用GetState方法查询数据
result, err := stub.GetState(args[0])
if err != nil {
return shim.Error("根据指定的 " + args[0] + " 查询数据时发生错误")
}
if result == nil {
return shim.Error("根据指定的 " + args[0] + " 没有查询到相应的数据")
}
// 返回查询结果
return shim.Success(result)
}
func main() {
if err := shim.Start(new(SimpleChaincode)); nil != err {
fmt.Printf("链码启动失败: %v", err)
}
}
以上就是我们的链码的编写,其中【stub shim.ChaincodeStubInterface 为我们提供了操作 账本的API,具体请参考相关资料】下面我们来查看链码的本地调试及 远端的安装部署
首先,我们要本地调试的话需要具备可以启动简单的本地fabric网络的配置,我这里就直接用了fabric-sample提供的 chaincode 目录来存放本地链码,使用 chaincode-docker-devmode 目录来运行链码,【为什么这么做呢,首先,我们在Hyperledger fabric的简单入门(一)fabric-samples的下载及自动启动网络脚本演示 一文中就有说明了 chaincode 和 chaincode-docker-devmode 的作用,且在 chaincode-docker-devmode 中的 docker-compose-simple.yaml 中就能看到,该本地测试环境所引用的链码是 挂载了 chaincode 到容器的】
步骤:
1、先进入 chaincode-docker-devmode 启动本地测试网络 :docker-compose -f docker-compose-simple.yaml up
2、进入对应的链码所在目录【当然进入到chaincode容器中的对应挂载的目录也是可以的】,如:/fabric-samples/chaincode/sacc 执行go build 把链码编译成go的可执行文件【或者直接把可执行文件拖到这里面来】;
3、在 链码容器 【注意: 一定是去到容器里面启动链码哦】的 对应的链码的可执行文件所在的目录,如:/opt/gopath/src/chaincode/sacc
这时候我们需要手动启动链码:CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc 【其中,CORE_PEER_ADDRESS 和 docker-compose-simple.yaml 中peer 的CORE_PEER_ADDRESS 端口不一样,为什么是这样,请查看之前的Hyperledger fabric的简单入门(三)fabric主要配置文件细讲 中的 peer 的配置文件 core.yaml 讲解自明 】
启动链码显示:
链码启动成功!【当然,正式环境链码的启动是有Endorser节点来启动链码容器启动的】
升级链码,在没有关闭本地网络的情况下,升级链码需要,把更改的的执行文件替换(当然不替换也是可以的需要不同名) 且在链码容器中启动链码时,指定 新的版本号【一般是网上叠加的版本号】和指定新的执行文件:
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:2 ./sacc
OK,这时候我们就可以,启动一个客户端来调用对应的链码了,步骤和正常的链码调用一直,如:
安装 -> 实例化 /升级 -> 调用
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
OK,以上就是我们在本地环境测试链码的方式,正式环境是如何操作链码的,请参考【Hyperledger fabric的简单入门(二)单机演示fabric网络启动全过程】最后面操作链码那部分!