Fabric链码入门案例(go语言版本)
Posted sanqima
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Fabric链码入门案例(go语言版本)相关的知识,希望对你有一定的参考价值。
在Fabric中,新的链码类要重新实现Init()和Invoke()这2个方法。这里以fabone.go为例,Fabric版本为 v1.4.0,进行说明。
Fabric GitHub官网
Fabric v1.4.0源码下载
1、定义一个空类
type HelloChainCode struct {
}
2、重写Init()方法
实现链码的初始化功能,这里指定为初始化 <k,v> 键值对。
func (t *HelloChainCode) Init(stub shim.ChaincodeStubInterface) peer.Response {
fmt.Println("begin init...")
_,args := stub.GetFunctionAndParameters()
if len(args) != 2 {
return shim.Error("must have two params")
}
//save data
err := stub.PutState(args[0],[]byte(args[1]))
if err != nil {
return shim.Error("PutState() is error")
}
fmt.Println("init success!")
return shim.Success(nil)
}
3、重写Invoke()方法
a) 实现调用链码功能,供peer节点调用,通过Invoke,将调用方法指向query()、setkey()。当然也可以指向其他方法,比如 delete()、getTxID()等等。
func (t *HelloChainCode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
callStr,args := stub.GetFunctionAndParameters()
if callStr == "setkey" {
return t.setkey(stub)
} else if callStr == "query" {
return t.query(stub,args)
}
jsonResp := "{\\"Error\\":\\"Nil,It has no func: "+ callStr + "\\"}"
return shim.Error(jsonResp)
}
b) Invoke指向的query()方法
func (t *HelloChainCode) query(stub shim.ChaincodeStubInterface, args[] string) peer.Response {
if len(args) != 1 {
return shim.Error("params length must equal 1")
}
result,err := stub.GetState(args[0])
if err != nil {
return shim.Error("GetState() is failed")
}
if result == nil {
strRes := string(result[:])
jsonResp := "{\\"Error\\":\\"Nil,It has no data for:" + strRes + "\\"}"
return shim.Error(jsonResp)
}
return shim.Success(result)
}
c) Invoke指向的setkey()方法
func (t *HelloChainCode) setkey(stub shim.ChaincodeStubInterface) peer.Response {
fmt.Println("begin set...")
_,args := stub.GetFunctionAndParameters()
if len(args) != 2 {
return shim.Error("must have two params")
}
_,err := stub.GetState(args[0])
if err != nil {
return shim.Error("This key"+args[0]+" is not existed!")
}
//set data
err = stub.PutState(args[0],[]byte(args[1]))
if err != nil {
return shim.Error("PutState() is error")
}
fmt.Println("set success!")
return shim.Success(nil)
}
4、完整代码
a) fabone.go 完整代码
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
"fmt"
)
type HelloChainCode struct {
}
func (t *HelloChainCode) Init(stub shim.ChaincodeStubInterface) peer.Response {
fmt.Println("begin init...")
_,args := stub.GetFunctionAndParameters()
if len(args) != 2 {
return shim.Error("must have two params")
}
//save data
err := stub.PutState(args[0],[]byte(args[1]))
if err != nil {
return shim.Error("PutState() is error")
}
fmt.Println("init success!")
return shim.Success(nil)
}
func (t *HelloChainCode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
callStr,args := stub.GetFunctionAndParameters()
if callStr == "setkey" {
return t.setkey(stub)
} else if callStr == "query" {
return t.query(stub,args)
}
jsonResp := "{\\"Error\\":\\"Nil,It has no func: "+ callStr + "\\"}"
return shim.Error(jsonResp)
}
func (t *HelloChainCode) query(stub shim.ChaincodeStubInterface, args[] string) peer.Response {
if len(args) != 1 {
return shim.Error("params length must equal 1")
}
result,err := stub.GetState(args[0])
if err != nil {
return shim.Error("GetState() is failed")
}
if result == nil {
strRes := string(result[:])
jsonResp := "{\\"Error\\":\\"Nil,It has no data for:" + strRes + "\\"}"
return shim.Error(jsonResp)
}
return shim.Success(result)
}
func (t *HelloChainCode) setkey(stub shim.ChaincodeStubInterface) peer.Response {
fmt.Println("begin set...")
_,args := stub.GetFunctionAndParameters()
if len(args) != 2 {
return shim.Error("must have two params")
}
_,err := stub.GetState(args[0])
if err != nil {
return shim.Error("This key"+args[0]+" is not existed!")
}
//set data
err = stub.PutState(args[0],[]byte(args[1]))
if err != nil {
return shim.Error("PutState() is error")
}
fmt.Println("set success!")
return shim.Success(nil)
}
func main() {
err := shim.Start(new(HelloChainCode))
if err != nil {
fmt.Printf("it start failed!")
}
}
b)进入fabric-samples/chaincode-docker-devmode/目录,新建一个文件夹名称为fabone,并把fabone.go放到该目录下。
cd fabric-samples/chaincode-docker-devmode/
mkdir fabone
cp fabone.go /fabone
5、修改配置文件
a) 在fabric-samples/chaincode-docker-devmode/目录里,
将docker-compose-simple.yaml复制一份,改名为docker-compose.yaml,同时,用"./chaincode" 替换"./…/chaincode",命令依次如下:
cd fabric-samples/chaincode-docker-devmode/
cp docker-compose-simple.yaml docker-compose.yaml
sed -i "s|./../chaincode|./chaincode|g" docker-compose.yaml
b) 在docker-compose.yaml里添加一个7052端口,并打开dev模式
目录结构如下:
6、初始化链码
a) 启动docker服务
docker-compose up -d
b)打开一个新的终端2,依次输入如下命令,进入chaincode容器,编译fabone.go源文件
## 进入fabone.go所在的目录
cd fabric-samples/chaincode-docker-devmode/fabone
sudo docker exec -it chaincode bash
go build fabone.go
c) 启动链码
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=fabone:1.0 ./fabone
该语句的含义是,在7052端口,启动一个fabone链码,版本为1.0。
7、安装和实例化链码
a) 在打开一个新的终端3,进入cli容器
sudo docker exec -it cli bash
b) 安装链码
将 fabone 1.0链码,安装到节点里。
peer chaincode install -v 1.0 -n fabone -p chaincodedev/chaincode/fabone
c) 实例化链码
初始化 weight = 60kg的一个<k,v>键值对,并把它保存到myc通道里。
peer chaincode instantiate -C myc -n fabone -v 1.0 -c '{"Args":["init","weight","60kg"]}'
8、调用链码
调用setkey(),把weight改成80kg,命令如下:
peer chaincode invoke -C myc -n fabone -c '{"Args":["setkey","weight","80kg"]}'
打印status=200,说明链码调用setkey()函数成功。
9、查询链码里的key-value
查询key(“weight”)对应的状态数据
peer chaincode query -C myc -n fabone -c '{"Args":["query","weight"]}'
效果如下:
10、交易查询
10.1 查看当前通道myc的区块高度
peer channel getinfo -c myc
得到通道myc的区块高度为height = 3
10.2 导出区块
a)重新打开一个新的终端4,依次输入如下命令:
cd /usr/local/gocode/src/github.com/hyperledger/fabric-samples/bin
mkdir myblock
cd ..
chmod -cR 777 myblock/
sudo docker cp cli:/opt/gopath/src/chaincodedev/one.block /usr/local/gocode/src/github.com/hyperledger/fabric-samples/bin/myblock
b) 设置FABRIC_CFG_PATH环境变量
#修改/etc/profile文件
vim /etc/profile
#添加FABRIC_CFG_PATH环境变量
#fabric
export FABRIC_CFG_PATH=/usr/local/gocode/src/github.com/hyperledger/fabric-samples/basic-network
## 使能环境
source /etc/profile
c) 使用configtxgen工具导出区块
configtxgen -inspectBlock ./myblock/one.block > ./myblock/one.json
d) 查看one.json的txID
cat myblock/one.json |grep -A 3 "tx_id"
得到tx_id = 9573bde3b0677f29c6ab5cb7952125f95da69f2aac9f9c32a6d10b067878cef1
副录
//one.json 区块数据
{
"data": {
"data": [
{
"payload": {
"data": {
"actions": [
{
"header": {
"creator": {
"id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNOakNDQWQyZ0F3SUJBZ0lSQU1uZjkvZG1WOVJ2Q0NWdzlwWlFVZlV3Q2dZSUtvWkl6ajBFQXdJd2dZRXgKQ3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhFdzFUWVc0ZwpSbkpoYm1OcGMyTnZNUmt3RndZRFZRUUtFeEJ2Y21jeExtVjRZVzF3YkdVdVkyOXRNUXd3Q2dZRFZRUUxFd05EClQxQXhIREFhQmdOVkJBTVRFMk5oTG05eVp6RXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1UY3hNVEV5TVRNME1URXgKV2hjTk1qY3hNVEV3TVRNME1URXhXakJwTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtRMkZzYVdadgpjbTVwWVRFV01CUUdBMVVFQnhNTlUyRnVJRVp5WVc1amFYTmpiekVNTUFvR0ExVUVDeE1EUTA5UU1SOHdIUVlEClZRUURFeFp3WldWeU1DNXZjbWN4TG1WNFlXMXdiR1V1WTI5dE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMEQKQVFjRFFnQUVaOFM0VjcxT0JKcHlNSVZaZHdZZEZYQWNrSXRycHZTckNmMEhRZzQwV1c5WFNvT09PNzZJK1VtZgpFa21UbElKWFA3L0F5UlJTUlUzOG9JOEl2dHU0TTZOTk1Fc3dEZ1lEVlIwUEFRSC9CQVFEQWdlQU1Bd0dBMVVkCkV3RUIvd1FDTUFBd0t3WURWUjBqQkNRd0lvQWdpbk9SSWhuUEVGWlVoWG02ZVdCa203SzdaYzhSNC96N0xXNEgKb3NzRGxDc3dDZ1lJS29aSXpqMEVBd0lEUndBd1JBSWdWaWtJVVp6Z2Z1RnNHTFFIV0pVVkpDVTdwRGFFVGthegpQekZnc0NpTHhVQUNJQ2d6SllsVzdudlp4UDdiNnRiZXUzdDhtcmhNWFFzOTU2bUQ0K0JvS3VOSQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==",
"mspid": "DEFAULT"
},
"nonce": "/n5TtJl5QryN4z5T7qa8SL0M14otTlu0"
},
"payload": {
"action": {
"endorsements": [
{
"endorser": "CgdERUZBVUxUEroGLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNOakNDQWQyZ0F3SUJBZ0lSQU1uZjkvZG1WOVJ2Q0NWdzlwWlFVZlV3Q2dZSUtvWkl6ajBFQXdJd2dZRXgKQ3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhFdzFUWVc0ZwpSbkpoYm1OcGMyTnZNUmt3RndZRFZRUUtFeEJ2Y21jeExtVjRZVzF3YkdVdVkyOXRNUXd3Q2dZRFZRUUxFd05EClQxQXhIREFhQmdOVkJBTVRFMk5oTG05eVp6RXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1UY3hNVEV5TVRNME1URXgKV2hjTk1qY3hNVEV3TVRNME1URXhXakJwTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtRMkZzYVdadgpjbTVwWVRFV01CUUdBMVVFQnhNTlUyRnVJRVp5WVc1amFYTmpiekVNTUFvR0ExVUVDeE1EUTA5UU1SOHdIUVlEClZRUURFeFp3WldWeU1DNXZjbWN4TG1WNFlXMXdiR1V1WTI5dE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMEQKQVFjRFFnQUVaOFM0VjcxT0JKcHlNSVZaZHdZZEZYQWNrSXRycHZTckNmMEhRZzQwV1c5WFNvT09PNzZJK1VtZgpFa21UbElKWFA3L0F5UlJTUlUzOG9JOEl2dHU0TTZOTk1Fc3dEZ1lEVlIwUEFRSC9CQVFEQWdlQU1Bd0dBMVVkCkV3RUIvd1FDTUFBd0t3WURWUjBqQkNRd0lvQWdpbk9SSWhuUEVGWlVoWG02ZVdCa203SzdaYzhSNC96N0xXNEgKb3NzRGxDc3dDZ1lJS29aSXpqMEVBd0lEUndBd1JBSWdWaWtJVVp6Z2Z1RnNHTFFIV0pVVkpDVTdwRGFFVGthegpQekZnc0NpTHhVQUNJQ2d6SllsVzdudlp4UDdiNnRiZXUzdDhtcmhNWFFzOTU2bUQ0K0JvS3VOSQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==",
"signature": "MEUCIQCQzgUxtNyYVwhYan6IMSQl0z61ModWm16l+l1pkf/XggIgP8N+pKigKTzubun8o/wZx02R7Mj/f1IOGgVw1MqK/zE="
}
],
"proposal_response_payload": {
"extension": {
"chaincode_id": {
"name": "fabone",
"path": "",
"version": "1.0"
},
"events": null,
"response": {
"message": "",
"payload": null,
"status": 200
},
"results": {
"data_model": "KV",
"ns_rwset": [
{
"collection_hashed_rwset": [],
"namespace": "fabone",
"rwset": {
"metadata_writes": [],
"range_queries_info": [],
"reads": [
{
"key": "weight",
"version": {
"block_num": "1",
"tx_num": "0"
}
}
],
"writes": [
{
"is_delete": false,
"key": "weight",
"value": "ODBrZw=="
}
]
}
},
{
"collection_hashed_rwset": [],
"namespace": "lscc",
"rwset": {
"metadata_writes": [],
"range_queries_info": [],
"reads": [
{
"key": "fabone",
"version": {
"block_num": "1",
"tx_num": "0"
}
}
],
"writes": []
}
}
]
},
"token_expectation": null
},
"proposal_hash": "cwzYy2afp1nVfaDFuFfJYUGC/HSOTHj9TdNBMvGlc9I="
}
},
"chaincode_proposal_payload": {
"TransientMap": {},
"input": {
"chaincode_spec": {
"chaincode_id": {
"name": "fabone",
"path": "",
"version": ""
},
"input": {
"args": [
"c2V0a2V5",
"d2VpZ2h0",
"ODBrZw=="
],
"decorations": {}
},
"timeout": 0,
"type": "GOLANG"
}
}
}
}
}
]
},
"header": {
"channel_header": {
"channel_id": "myc",
"epoch": "0",
"extension": "EggSBmZhYm9uZQ==",
"timestamp": "2021-07-30T00:38:53.450469289Z",
"tls_cert_hash": null,
"tx_id": "9573bde3b0677f29c6ab5cb7952125f95da69f2aac9f9c32a6d10b067878cef1",
"type": 3,
"version": 0
},
"signature_header": {
"creator": {
"id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNOakNDQWQyZ0F3SUJBZ0lSQU1uZjkvZG1WOVJ2Q0NWdzlwWlFVZlV3Q2dZSUtvWkl6ajBFQXdJd2dZRXgKQ3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhFdzFUWVc0ZwpSbkpoYm1OcGMyTnZNUmt3RndZRFZRUUtFeEJ2Y21jeExtVjRZVzF3YkdVdVkyOXRNUXd3Q2dZRFZRUUxFd05EClQxQXhIREFhQmdOVkJBTVRFMk5oTG05eVp6RXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1UY3hNVEV5TVRNME1URXgKV2hjTk1qY3hNVEV3TVRNME1URXhXakJwTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNCTUtRMkZzYVdadgpjbTVwWVRFV01CUUdBMVVFQnhNTlUyRnVJRVp5WVc1amFYTmpiekVNTUFvR0ExVUVDeE1EUTA5UU1SOHdIUVlEClZRUURFeFp3WldWeU1DNXZjbWN4TG1WNFlXMXdiR1V1WTI5dE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMEQKQVFjRFFnQUVaOFM0VjcxT0JKcHlNSVZaZHdZZEZYQWNrSXRycHZTckNmMEhRZzQwV1c5WFNvT09PNzZJK1VtZgpFa21UbElKWFA3L0F5UlJTUlUzOG9JOEl2dHU0TTZOTk1Fc3dEZ1lEVlIwUEFRSC9CQVFEQWdlQU1Bd0dBMVVkCkV3RUIvd1FDTUFBd0t3WURWUjBqQkNRd0lvQWdpbk9SSWhuUEVGWlVoWG02ZVdCa203SzdaYzhSNC96N0xXNEgKb3NzRGxDc3dDZ1lJS29aSXpqMEVBd0lEUndBd1JBSWdWaWtJVVp6Z2Z1RnNHTFFIV0pVVkpDVTdwRGFFVGthegpQekZnc0NpTHhVQUNJQ2d6SllsVzdudlp4UDdiNnRiZXUzdDhtcmhNWFFzOTU2bUQ0K0JvS3VOSQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==",
"mspid": "DEFAULT"
},
"nonce": "/n5TtJl5QryN4z5T7qa8SL0M14otTlu0"
}
}
},
"signature": "MEQCIG17Wq1iZsDcnKqgxaccsz2O26jJP1J+eIC4/fq9TBIsAiBj4RV/gDmYDDD5s7iy0l+y/i+dA+sVT8dF1oy1B8HzzA=="
}
]
},
"header": {
"data_hash": "U5Fms2VccR9eDTHkwbE2usd8KRpcSLcK1XKtyOScHbg=",
"number": "2",
"previous_hash": "QIs/gsEKWtVr1q9sLKcXLmgLtRw+le2h9DA1lwjdBVA="
},
"metadata": {
"metadata": [
"Eq4HCuMGCsYGCgdERUZBVUxUEroGLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNOakNDQWQyZ0F3SUJBZ0lSQU1uZjkvZG1WOVJ2Q0NWdzlwWlFVZlV3Q2dZSUtvWkl6ajBFQXdJd2dZRXgKQ3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGhNUll3RkFZRFZRUUhFdzFUWVc0ZwpSbkpoYm1OcGMyTnZNUmt3RndZRFZRUUtFeEJ2Y21jeExtVjRZVzF3YkdVdVkyO以上是关于Fabric链码入门案例(go语言版本)的主要内容,如果未能解决你的问题,请参考以下文章