版权声明:本博客系作者(hanxiaodong/kevin)原创,转载请声明出处。 https://blog.csdn.net/weixin_44676392/article/details/87938415
目标
- 使用链码相关的API实现一个简单的 Hello World 入门应用
- 使用开发测试模式测试 Hello World 应用
任务实现
前面我们已经接触了与链码相关的内容,下面我们根据已掌握的链码知识实现一个简单的链码应用。该应用需求较为简单:链码在实例化时向账本保存一个初始数据,key 为 Hello, value 为 World,然后用户发出查询请求,可以根据 key 查询到相应的 value。
5.3.1 链码开发
-
创建文件夹
进入
fabric-samples/chaincode/
目录下并创建一个名为hello
的文件夹$ cd hyfa/fabric-samples/chaincode $ sudo mkdir hello $ cd hello
-
创建并编辑链码文件
$ sudo vim hello.go
-
导入链码依赖包
// hanxiaodong // QQ群(专业Fabric交流群):862733552 package main import ( "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" "fmt" )
-
编写主函数
func main() { err := shim.Start(new(HelloChaincode)) if err != nil { fmt.Printf("链码启动失败: %v", err) } }
-
自定义结构体
type HelloChaincode struct { }
-
实现 Chaincode 接口
实现
Chaincode
接口必须重写 Init 与 Invoke 两个方法。Init 函数:初始化数据状态
- 获取参数并判断参数长度是否为2
- 参数: Key, Value
- 调用 PutState 函数将状态写入账本中
- 如果有错误, 则返回
- 打印输出提示信息
- 返回成功
具体实现代码如下:
// 实例化/升级链码时被自动调用 // -c '{"Args":["Hello","World"]' func (t *HelloChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { fmt.Println("开始实例化链码....") // 获取参数 //args := stub.GetStringArgs() _, args := stub.GetFunctionAndParameters() // 判断参数长度是否为2个 if len(args) != 2 { return shim.Error("指定了错误的参数个数") } fmt.Println("保存数据......") // 通过调用PutState方法将数据保存在账本中 err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error("保存数据时发生错误...") } fmt.Println("实例化链码成功") return shim.Success(nil) }
Invoke 函数
-
获取参数并判断长度是否为1
-
利用第1个参数获取对应状态 GetState(key)
-
如果有错误则返回
-
如果返回值为空则返回错误
-
返回成功状态
具体实现代码如下:
- 获取参数并判断参数长度是否为2
// 对账本数据进行操作时被自动调用(query, invoke)
func (t *HelloChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// 获取调用链码时传递的参数内容(包括要调用的函数名及参数)
fun, args := stub.GetFunctionAndParameters()
// 客户意图
if fun == "query"{
return query(stub, args)
}
return shim.Error("非法操作, 指定功能不能实现")
}
-
实现查询函数
函数名称为 query,具体实现如下:
func query(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) }
5.3.2 链码测试
-
启动网络
进入
fabric-samples/chaincode-docker-devmode/
目录$ cd ../chaincode-docker-devmode/
-
构建并启动链码
2.1 打开一个新的终端2,进入 chaincode 容器:
$ sudo docker exec -it chaincode bash
2.2 编译链码
# cd hello # go build
2.3 启动链码
# CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=hellocc:0 ./hello
命令执行后输出如下:
[shim] SetupChaincodeLogging -> INFO 001 Chaincode log level not provided; defaulting to: INFO [shim] SetupChaincodeLogging -> INFO 002 Chaincode (build level: ) starting up ...
-
测试:
3.1 打开一个新的终端3,进入 cli 容器
$ sudo docker exec -it cli bash
3.2 安装链码
# peer chaincode install -p chaincodedev/chaincode/hello -n hellocc -v 0
3.3 实例化链码
# peer chaincode instantiate -n hellocc -v 0 -c '{"Args":["init", "Hello","World"]}' -C myc
3.4 调用链码
根据指定的 key (“Hello”)查询对应的状态数据
# peer chaincode query -n hellocc -c '{"Args":["query","Hello"]}' -C myc
返回查询结果:
World
FAQ
-
在调用链码时将 query 换为 invoke 可以吗?
可以将 query 替换为 invoke 操作,但是两个命令的执行流程也不同,而且执行后可以从终端的输出中看出,返回的查询结果显示的内容是一串数字,无法确定其正确性。