【我的区块链之路】- Hyperledger fabric的简单入门(四)链码的编写及调试

【转载请标明出处】:https://blog.csdn.net/qq_25870633/article/details/81751101     

      之前的文章中我们有讲过了fabric的一些核心的配置文件的选项说明,讲过fabric 的网络启动的步骤,那么,我们会在这篇文章中讲述如何的编写链码,及本地调试链码,及发布链码调用链码等等操作!

注意】首先,我们在编写链码之前需要下载几个依赖库:

1、github.com/hyperledger/fabric/core/chaincode/shim

2、github.com/hyperledger/fabric/protos/peer

其中,一个最基本的空链码结构为如下所示:

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
 

【注意】本地环境中默认通道为 myc;且本地环境安装及实例化/升级等都不需要一大堆 --tls --msp 等等参数

OK,以上就是我们在本地环境测试链码的方式,正式环境是如何操作链码的,请参考【Hyperledger fabric的简单入门(二)单机演示fabric网络启动全过程】最后面操作链码那部分!

猜你喜欢

转载自blog.csdn.net/qq_25870633/article/details/81751101