Hyperledger Fabric链码开发实践日志

1.链码结构总述

这里,链码的开发用的是Go语言,为此需要先简单学习一下Go语言,这是一门轻量级的语言,有意思的是它自带通道,可以并发,就很适合大型分布式系统的开发。
启动链码必须调用shim包中的Start函数,这个函数的参数是一个Chaincode接口类型,Chaincode这个接口类型中有两个方法分别是Init和Invoke,这是链码开发中极为重要的两个方法:

  • Init:在链码实例化或者升级的时候被调用,完成数据初始化;
  • Invoke:在更新或查询提案事务中分类帐本数据状态的时候被调用。

在实际开发中,需要定义一个结构体,重写Init和Invoke两个方法完成相关功能。下面具体看看一个链码必要的结构:

package main //所写的包的名称

import (
        "fmt"
        "github.com/hyperledger/fabric/core/chaincode/shim"
        "github.com/hyperledger/fabric/protos/peer"
)//引入必要的包

type HelloChaincode struct{
    
    }//定义一个结构体

func main() {
    
    
        err := shim.Start(new(HelloChaincode))
        if err != nil {
    
    
                fmt.Printf("链码启动失败: %v", err)
        }
}//主函数,调用shim.Start方发启动链码

func (t *HelloChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
    
    
       
}

func (t *HelloChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
    
            fun, args := stub.GetFunctionAndParameters()
       
}

2.熟悉链码相关API

主要是shim包提供的API,分为5类:

  1. 参数解析API:用来获取参数的
  2. 账本数据状态操作API:对账本数据查询、更新等
  3. 交易信息获取API:获取提交的交易信息
  4. 事件处理API:与事件处理相关
  5. 对私有数据操作的API:专门对私有数据操作

API的数量还是比较多的,大多是获取相关信息的,有时间可以仔细看看,初级阶段主要使用的几个:

  • GetFunctionAndParameters()(function string,params []string)返回被调用函数的名称以及参数列表
  • GetStringArgs()[]string 直接返回参数列表
  • GetState(key string)([]byte,error) 根据指定的key值查询数据状态
  • PutState(key string,value []byte)error 根据指定的key,将对应的value保存到帐本中

3.链码实现Hello World

3.1 链码开发

先写个hello world练练手哈。

  1. 进入chaincode目录下创建一个名为hello的文件夹,然后创建并编辑链码文件:
sudo mkdir hello && cd hello
sudo vim hello.go
  1. 导入链码依赖包:
package main

import (
			"fmt"
			"github.com/hyperledger/fabric/core/chaincode/shim"
			"github.com/hyperledger/fabric/protos/peer"
		)
  1. 编写主函数:
func main() {
    
    
			err := shim.Start(new(HelloChaincode))
			if err != nil {
    
    
				fmt.Printf("链码启动失败: %v", err)
			}
}
  1. 自定义结构体:
type HelloChaincode struct{
    
    }
  1. 重写Init方法,它的功能就是初始化数据状态,一个简单的逻辑步骤如下:
  • 获取参数并判断参数是否合法;
  • 调用PutState函数将状态写入帐本;
  • 是否有写入错误;
  • 最后调用Success函数返回成功状态。
func (t *HelloChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
    
    
		    fmt.Println("开始实例化链码")
			_, args := stub.GetFunctionAndParameters()
			if len(args) != 2 {
    
    
				return shim.Error("指定了错误的参数个数")
			}
			fmt.Println("保存数据")
			err := stub.PutState(args[0],[]byte(args[1]))
			if err !=nil{
    
    
				return shim.Error("保存数据时发生错误")
			}
			fmt.Println("实例化成功")
			return shim.Success(nil)
}

可以注意一下其中的GetFunctionAndParameters()函数,这里参数形式是:“Args”:[“init”,“Hello”,“Wzh”],函数获取了被调用的函数"init"和传入的参数"Hello",“Wzh”,因此写成_,args的形式,也可以换成GetStringArgs()函数直接获取后面的参数。

  1. 重写Invoke方法,它的简单逻辑是:
  • 获取参数并判断参数是否合法;
  • 利用key值获取状态;
  • 完成被调用函数的功能;
  • 返回数据状态或者成功状态。
func (t *HelloChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
    
    	fun, args := stub.GetFunctionAndParameters()
			if fun == "query"{
    
    
				return query(stub,args)
			}
			return shim.Error("非法操作,指定功能不能实现")
}

func query(stub shim.ChaincodeStubInterface,args  []string) peer.Response{
    
    
			if len(args) !=1{
    
    
				return shim.Error("指定的参数错误,必须且只能指定相应的Key")
			}
			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)
}

这一段代码非常简单明了,不多赘述了。

3.2 链码测试

又又又又到了链码测试的环节,具体过程在前面的日志中,下面就只附上代码了:

cd chaincode-docker-devmode
sudo docker-compose -f docker-compose-simple.yaml up -d

打开终端2窗口:

sudo docker exec -it chaincode bash
cd hello
go build
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=hellocc:0 ./hello

打开终端3窗口:

sudo docker exec -it cli bash、
peer chaincode install -p chaincodedev/chaincode/hello -n hellocc -v 0
peer chaincode instantiate -n hellocc -v 0 -c '{"Args":["init","Hello","Wzh"]}' -C myc
peer chaincode query -n hellocc  -c '{"Args":["query","Hello"]}' -C myc

最后查询应该能得到Wzh(即Hello键赋值的内容)

猜你喜欢

转载自blog.csdn.net/weixin_43107949/article/details/109350623