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模式

图(1) 在Fabric v1.4.0里,启用7052端口

    目录结构如下:

图(2) fabric-samples/chaincode-docker-devmode/chaincode目录结构

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。

图(3) 启动链名监听的端口为7052

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"]}'

    效果如下:

图(4) 查到weight = 80kg

10、交易查询

10.1 查看当前通道myc的区块高度

peer channel getinfo -c myc

    得到通道myc的区块高度为height = 3

图(4) 查看当前通道myc的区块高度

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
图(5) 添加FABBRIC_CFG_PATH到/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

图(6) 查看tx_id

副录

    //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语言版本)的主要内容,如果未能解决你的问题,请参考以下文章

fabric contract链码案例

Fabric链码测试方法(go语言单元测试/性能测试)

fabric go语言链码打包并在其他Peer节点部署

Hyperledger Fabric 链码 类型

运行链码问题

Fabric1.4 编写链码