Hyperledger Fabric 链码

这篇具有很好参考价值的文章主要介绍了Hyperledger Fabric 链码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

懂哪写哪,随时补充

链码结构

链码API

链码在开发过程中需要实现链码接口,交易的类型决定了哪个接口函数将会被调用,链码的接口定义如下:

type Chaincode interface {
   Init(stub ChaincodeStubInterface) pb.Response
   Invoke(stub ChaincodeStubInterface) pb.Response
}

链码的基本结构

链码的必要结构如下:

package main

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

//声明一个结构体
type SimpleChaincode struct {}

//为结构体添加Init方法
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response{
  //在该方法中实现链码初始化或升级时的处理逻辑
  //编写时可灵活使用stub中的API
}

//为结构体添加Invoke方法
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response{
  //在该方法中实现链码运行中被调用或查询时的处理逻辑
  //编写时可灵活使用stub中的API
}

//主函数,需要调用shim.Start( )方法
func main() {
  err:=shim.Start(new(SimpleChaincode))
  if err != nil {
     fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}

链码开发API

shim.ChaincodeStubInterface接口

参数读取API

GetArgs() [][]byte
//以byte数组的数组的形式获得传入的参数列表

GetStringArgs() []string
//以字符串数组的形式获得传入的参数列表

GetFunctionAndParameters() (string, []string)
//将字符串数组的参数分为两部分,第一个参数作为被调用的函数名称,剩下的参数作为函数的执行参数(若交易只有一个参数,则为空列表)

GetArgsSlice() ([]byte, error)
//以byte切片的形式获得参数列表

eg:
function, args := stub.GetFunctionAndParameters()

应用开发案例

转账

1、Init方法

Init方法中,首先通过stub的GetFunctionAndParameters()方法提取本次调用的交易中所指定的参数:

_, args := stub.GetFunctionAndParameters()
//本例中,用下划线忽略了返回的function值,用args变量记录其他参数。

接下来检查args参数数量,必须为4,否则会通过shim.Error()函数创建并返回一个状态为ERROR的Response消息:

if len(args) != 4 {
   return shim.Error("Incorrect number of arguments. Expecting 4")
}

分别读取4个参数。设用该链码实现转账的两个实体分别为a和b,则A、Aval、B、Bval的值分别表示a的名称、a的初始余额、b的名称、b的初始余额:

A = args[0]
Aval, err = strconv.Atoi(args[1])//strconv.Atoi()将string类型转为int类型
if err != nil {
   return shim.Error("Expecting integer value for asset holding")
}
B = args[2]
Bval, err = strconv.Atoi(args[3])
if err != nil {
   return shim.Error("Expecting integer value for asset holding")
}

之后,最为关键的是将必要的状态值记录到分布式账本中。stub的PutState()函数可以尝试在账本中添加或更新一对键值(需要等待Committer节点验证通过,才真正写入账本得到确认)。

Pustate()方法格式为PutState(key string,value[]byte)error,其中key为键,类型是string;value为值,类型是字节数组。以下代码向账本中存入了两对键值,分别记录了a和b的余额:

err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
   return shim.Error(err.Error())
}

err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
//strconv.Itoa函数的参数是一个整型数字,它可以将数字转换成对应的字符串类型的数字。
if err != nil {
   return shim.Error(err.Error())
}

最后,通过shim.Success(nil)创建并返回状态为OK的Response消息。

2、Invoke方法

Invoke方法中,同样通过stub的GetFunctionAndParameters()方法提取本次调用的交易中所指定的参数:

unction, args := stub.GetFunctionAndParameters()

之后根据function值的不同,执行不同的分支处理逻辑。

本例在Invoke中实现了三个分支处理逻辑:query、invoke和delete。由代码可见,为每个分支的处理逻辑都编写了一个方法:

if function == "invoke" {
   return t.invoke(stub, args)
} else if function == "delete" {
   return t.delete(stub, args)
} else if function == "query" {
   return t.query(stub, args)
}

资产权属管理

链码代码可参考examples/chaincode/go/marbles02/marbles_chaincode.go。

1、链码结构:

package main

// 引入必要的包
import (
    "bytes"
    "encoding/json"
    "fmt"
    "strconv"
    "strings"
    "time"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    pb "github.com/hyperledger/fabric/protos/peer"
)

// 声明名为SimpleChaincode的结构体
type SimpleChaincode struct {
}

// 声明大理石(marble)结构体
type marble struct {
    ObjectType string `json:"docType"`
    Name       string `json:"name"`
    Color      string `json:"color"`
    Size       int    `json:"size"`
    Owner      string `json:"owner"`
}

// 主函数,需要调用shim.Start()方法
func main() {
    err := shim.Start(new(SimpleChaincode))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}

// 为SimpleChaincode添加Init方法
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    // 不做具体处理
    return shim.Success(nil)
}

// 为SimpleChaincode添加Invoke方法
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    function, args := stub.GetFunctionAndParameters()
    fmt.Println("invoke is running " + function)

    // 定位到不同的分支处理逻辑
    if function == "initMarble" {
        return t.initMarble(stub, args)
    } else if function == "transferMarble" {
        return t.transferMarble(stub, args)
    } else if function == "transferMarblesBasedOnColor" {
        return t.transferMarblesBasedOnColor(stub, args)
    } else if function == "delete" {
        return t.delete(stub, args)
    } else if function == "readMarble" {
        return t.readMarble(stub, args)
    } else if function == "queryMarblesByOwner" {
        return t.queryMarblesByOwner(stub, args)
    } else if function == "queryMarbles" {
        return t.queryMarbles(stub, args)
    } else if function == "getHistoryForMarble" {
        return t.getHistoryForMarble(stub, args)
    } else if function == "getMarblesByRange" {
        return t.getMarblesByRange(stub, args)
    }

    fmt.Println("invoke did not find func: " + function) // error
    return shim.Error("Received unknown function invocation")
}

在链码中,可以自定义结构体类型来表示一种资产,并设定资产的各种属性。本例中定义了大理石(marble)资产,其属性包括类型、名称、颜色、尺寸、拥有者。具体映射到代码中,对marble类型的声明如下:

type marble struct {
    ObjectType string `json:"docType"`
    Name       string `json:"name"`
    Color      string `json:"color"`
    Size       int    `json:"size"`
    Owner      string `json:"owner"`
}

2、Invoke方法

链码的Init方法中未进行任何处理,Invoke方法中则包含了9个分支方法。

Hyperledger Fabric 链码

Hyperledger Fabric 链码

下面对分支方法逐一进行介绍。

  1. initMarble方法

initMarble方法根据输入参数创建一个大理石,并写入账本。

方法接受4个参数,依次表示大理石名称、颜色、尺寸、拥有者名称。例如,如果调用链码时指定参数{“Args”: [“initMarble”,“marble1”,“blue”,“35”,“tom”]},则功能为创建并记录一个名称为marble1、蓝色、尺寸为 35的大理石,拥有者为tom。

读取参数后,首先使用stub.GetState()进行查重。如果同样名称的大理石在账本中已经存在,则返回error的Response:

// 检查大理石是否已经存在
marbleAsBytes, err := stub.GetState(marbleName)
if err != nil {
    return shim.Error("Failed to get marble: " + err.Error())
} else if marbleAsBytes != nil {
    fmt.Println("This marble already exists: " + marbleName)
    return shim.Error("This marble already exists: " + marbleName)
}

创建相应的marble类型变量,并用json.Marshal()方法将其序列化到JSON对象中。自定义类型的变量序列化之后才可以写入账本,同理,对于从账本中读取出的信息需要反序列化后才便于进行操作:

// 创建marble,并序列化为JSON对象
objectType := "marble"
marble := &marble{objectType, marbleName, color, size, owner}
marbleJSONasBytes, err := json.Marshal(marble)
if err != nil {
    return shim.Error(err.Error())
}

之后,用stub.PutState()将序列化后的内容写入账本,以大理石名称marbleName为键:

// 将marbleJSONasBytes存入状态
err = stub.PutState(marbleName, marbleJSONasBytes)
if err != nil {
    return shim.Error(err.Error())
}

在initMarble中,为了支持之后针对某一特定颜色的大理石进行范围查找,需要将该大理石的颜色与名称这两个属性组合起来创建一个复合键,并记录在账本中。这里,复合键的意义是将一部分属性也构造为了索引的一部分,使得针对这部分属性做查询时,可以直接根据索引返回查询结果,而不需要具体提取完整信息来作比对:

indexName := "color~name"
colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string
    {marble.Color, marble.Name})
if err != nil {
    return shim.Error(err.Error())
}

这里调用了stub的CreateCompositeKey方法来创建复合键。该方法格式为 CreateCompositeKey(objectType string,attributes[]string)(string,error),实际上会将objectType和attributes中的每个 string串联起来,中间用U+0000分割;同时在开头加上\x00,标明该键为复合键。

最后,以复合键为键,以0x00为值,将复合键记录入账本中:

value := []byte{0x00}
stub.PutState(colorNameIndexKey, value)
  1. readMarble方法

根据大理石名称,readMarble方法会在账本中查询并返回大理石信息。

方法接受1个参数,即大理石名称。例如,如果调用链码时指定参数{“Args”:[“readMarble”,“marble1”]},则功能为查找名称为marble1的大理石,如果找到,返回其信息:

valAsbytes, err := stub.GetState(name)
if err != nil {
    jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
    return shim.Error(jsonResp)
} else if valAsbytes == nil {
    jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
    return shim.Error(jsonResp)
}

return shim.Success(valAsbytes)
  1. delete方法

根据大理石名称,delete方法会在账本中删除大理石信息。

方法接受1个参数,即大理石名称。例如,如果调用链码时指定参数{“Args”:[“delete”,“marble1”]},则功能为删除名称为marble1的大理石的信息。

**除了删除以大理石名称为键的状态,还需删除该大理石的颜色与名称复合键。**所以方法中第一步需要读取该大理石的颜色:

var marbleJSON marble

valAsbytes, err := stub.GetState(marbleName)
if err != nil {
    jsonResp = "{\"Error\":\"Failed to get state for " + marbleName + "\"}"
    return shim.Error(jsonResp)
} else if valAsbytes == nil {
    jsonResp = "{\"Error\":\"Marble does not exist: " + marbleName + "\"}"
    return shim.Error(jsonResp)
}

err = json.Unmarshal([]byte(valAsbytes), &marbleJSON)
if err != nil {
    jsonResp = "{\"Error\":\"Failed to decode JSON of: " + marbleName + "\"}"
    return shim.Error(jsonResp)
}

其中用json.Unmarshal方法将从账本中读取到的值反序列化为marble类型变量marbleJSON。则大理石颜色为marbleJSON.Color。

删除以大理石名称为键的状态:

err = stub.DelState(marbleName)
if err != nil {
    return shim.Error("Failed to delete state:" + err.Error())
}

删除以大理石的颜色与名称复合键为键的状态:

indexName := "color~name"
colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name})
if err != nil {
    return shim.Error(err.Error())
}

err = stub.DelState(colorNameIndexKey)
if err != nil {
    return shim.Error("Failed to delete state:" + err.Error())
}
  1. transferMarble方法

transferMarble方法用于更改一个大理石的拥有者。

方法接受两个参数,依次为大理石名称和新拥有者名称。例如,如果调用链码时指定参数{“Args”:[“transferMarble”,“marble2”,“jerry”]},则功能是将名称为marble2的大理石的拥有者改为jerry。

首先用stub.GetState()方法从账本中取得信息,再用json.Unmarshal()方法将其反序列化为marble类型

marbleAsBytes, err := stub.GetState(marbleName)
if err != nil {
    return shim.Error("Failed to get marble:" + err.Error())
} else if marbleAsBytes == nil {
    return shim.Error("Marble does not exist")
}

marbleToTransfer := marble{}
err = json.Unmarshal(marbleAsBytes, &marbleToTransfer)
if err != nil {
    return shim.Error(err.Error())
}

更改大理石的拥有者:

marbleToTransfer.Owner = newOwner

最后将更改后的状态写入账本:

marbleJSONasBytes, _ := json.Marshal(marbleToTransfer)
err = stub.PutState(marbleName, marbleJSONasBytes)
if err != nil {
    return shim.Error(err.Error())
}
  1. getMarblesByRange方法

给定大理石名称的起始和终止,getMarblesByRange可以进行范围查询,返回所有名称在指定范围内的大理石信息。

方法接受两个参数,依次为字典序范围的起始(包括)终止(不包括)。例如,调用链码时可以指定参数**{“Args”:[“getMarblesByRange”,“marble1”,“marble3”]}**进行范围查询,返回查找到的结果的键值。

方法中调用了stub.GetStateByRange(startKey,endKey)进行范围查询,其返回结果是一个迭代器StateQueryIteratorInterface结构,可以按照字典序迭代每个键值对,最后需调用Close()方法关闭:

resultsIterator, err := stub.GetStateByRange(startKey, endKey)
if err != nil {
    return shim.Error(err.Error())
}
defer resultsIterator.Close()

通过迭代器的迭代构造出查询结果的JSON数组,最后通过shim.Success()方法来返回结果:

var buffer bytes.Buffer
buffer.WriteString("[")

bArrayMemberAlreadyWritten := false
for resultsIterator.HasNext() {
    queryResponse, err := resultsIterator.Next()
    if err != nil {
        return shim.Error(err.Error())
    }
    if bArrayMemberAlreadyWritten == true {
        buffer.WriteString(",")
    }
    buffer.WriteString("{\"Key\":")
    buffer.WriteString("\"")
    buffer.WriteString(queryResponse.Key)
    buffer.WriteString("\"" )

    buffer.WriteString(", \"Record\":")
    // 记录本身就是一个 JSON 对象
    buffer.WriteString(string(queryResponse.Value))
    buffer.WriteString("}")
    bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]")

fmt.Printf("- getMarblesByRange queryResult:\n%s\n", buffer.String())

return shim.Success(buffer.Bytes())

Go 语言拼接字符串有五种方法,分别是:使用+号拼接、使用 sprintf 拼接、使用 join 函数拼接、使用 buffer.WriteString 函数拼接、使用 buffer.Builder 拼接。

使用 join 函数拼接:

var str []string = []string{s1, s2}
s := strings.Join(str, "")

使用 buffer.WriteString 函数拼接:

var bt bytes.Buffer
bt.WriteString(s1)
bt.WriteString(s2)
//获得拼接后的字符串
s3 := bt.String()

使用 buffer.Builder 拼接:文章来源地址https://www.toymoban.com/news/detail-402072.html

var build strings.Builder
build.WriteString(s1)
build.WriteString(s2)
s3 := build.String()

Go言拼接字符串有五种方法,分别是:使用+号拼接、使用 sprintf 拼接、使用 join 函数拼接、使用 buffer.WriteString 函数拼接、使用 buffer.Builder 拼接。

使用 join 函数拼接:

var str []string = []string{s1, s2}
s := strings.Join(str, "")

使用 buffer.WriteString 函数拼接:

var bt bytes.Buffer
bt.WriteString(s1)
bt.WriteString(s2)
//获得拼接后的字符串
s3 := bt.String()

使用 buffer.Builder 拼接:

var build strings.Builder
build.WriteString(s1)
build.WriteString(s2)
s3 := build.String()

到了这里,关于Hyperledger Fabric 链码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • fabric2.2环境搭建,链码部署至通道

    一 Fabric-X86 1.查看虚拟机环境 显示X86架构 centos7发行版本 2.源码克隆 选定位置进行GitHub代码克隆,命令为 克隆成功后生成文件夹fabric,源码克隆成功 在fabric目录切换版本至V2.2.5 3.golang版本升级 根据要求部署fabric2.2.5版本,go的版本需要最低达到1.14.1版本 下载安装包 解压

    2024年02月07日
    浏览(34)
  • fabric1.4环境手动部署及链码测试

    先在fabric文件夹下建一个aberic文件夹,并将一些必要的配置文件文件放入 如图 然后根据crypto-config.yaml生成证书文件 ./bin/cryptogen generate --config=./crypto-config.yaml 接下来,使用configtxgen工具执行configtx.yaml文件以创建orderer Genesis block,在此之前需要为configtxgen工具指定configtx.yaml文件

    2024年02月10日
    浏览(28)
  • Fabric使用自己的链码进行测试-go语言

    书接前文 Fabric链码部署-go语言 通过上面这篇文章,你可以部署好自己的链码 (后面很多命令是否需要修改,都是根据上面这篇文章来的,如果零基础的话建议先看上面这篇) 就进行下一步 在测试网络上运行自己的链码 目录 1、导航到test-network目录 1.1 打开日志Logspout(可选

    2024年02月05日
    浏览(30)
  • Linux搭建Hyperledger Fabric区块链框架 - Hyperledger Fabric模型概念

    2015年,Linux基金会启动了Hyperledger项目,目标是发展跨行业的区块链技术。 Hyperledger Fabric是Hyperledger中的一个区块链项目,包含一个账本,使用智能合约并且是一个通过所有参与者管理交易的系统。 Hyperledger Fabric 是分布式账本解决方案的平台,以模块化架构为基础,支持不同

    2023年04月08日
    浏览(40)
  • 【Fabric学习】什么是HyperLedger Fabric?

    本文总结自 Fabric官方文档 ,描述了Fabric产生的背景、特性、主要组件。 区块链 是不可更改的交易账本,由同等节点(peer nodes)组成的分布式网络来维护。 比特币:第一个使用区块链的应用; 以太坊:引入 智能合约 来开发分布式应用。 二者都是 公有链 ( public / permissi

    2024年01月19日
    浏览(36)
  • 【Hyperledger Fabric 学习】运行一个Fabric应用

    中文网址:https://hyperledger-fabric.readthedocs.io/zh_CN/latest 英文网址:https://hyperledger-fabric.readthedocs.io/en/latest 一般情况英文网址的内容更全面,版本也比中文新。 本教程介绍了 Fabric 应用程序如何与已部署的区块链网络进行交互。本教程使用使用 Fabric Gateway 客户端 API 构建的示例

    2023年04月08日
    浏览(59)
  • 【fabric2.4】使用java sdk访问虚拟机里面的区块链网络上的链码

    链码中的函数名和参数名需要大写,不然是private无法访问 记录一下做实验写论文时,如何使用fabric2.4的java sdk 执行ccp-generate脚本,能够在指定路径下获取当前网络的配置文件,这是第一步 看看本机的IP地址和虚拟机的IP地址,互相ping一下看看能不能ping通 把一些需要的文件拷

    2024年02月15日
    浏览(31)
  • Hyperledger Fabric 环境搭建

    以 fabric:2.2.0 ; ca:1.4.7 为例 官网下载go Downloads - The Go Programming Language (google.cn) 没有问题就完成啦 官方安装脚本(修改过了): 两个简单脚本直接开启/关闭测试区块链: 开启: 关闭:

    2024年02月11日
    浏览(32)
  • Hyperledger Fabric架构设计

    目录 1、逻辑架构 答疑:什么叫做背书策略? 2、运行时架构         答疑:什么是锚节点? Hyperledger Fabric从1.X开始,在扩展性及安全性方面有了很大的提升,且新增了诸多特性。 多通道:支持多通道,提高隔离安全性。 可插拔的组件:支持共识组件、权限管理组件等可

    2024年02月11日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包