Fabric 智能合约代码解读

https://github.com/hyperledger/fabric-samples/blob/main/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go​github.com/hyperledger/fabric-samples/blob/main/asset-transfer-basic/chaincode-go/chaincode/smartcontract.go

随手记录下期末的链码复习,参考官方提供的代码和文档完成链码的编写和测试部署,能看懂示例代码,代码解释见书和相关注释,书(Go语言Hyperledger实战)很详细,不重复搬运内容。

此文件夹下主要有三个go文件,关注smartcontract.goassetTransfer.go即可,前者是智能合约文件使用函数编写了资产转移的相关逻辑,后者是启动智能合约,了解不同文件需要导入的包。完成以上两步,可以在终端使用命令行部署调用。

1.智能合约编写

smartcontract.go业务逻辑很简单,一个资产的增删改查,这里的区块链本质就是个状态数据库,即对数据库数据的增删改查,包括以下几点(正确处理数据以及调用对应的函数)

  • 导入包
  • ctx.GetSub.FUNCTION()
  • json的序列化
  • 错误处理

1.1 导包

关注ContractAPI,通过此包传递上下文参数以及结构体定义

import (
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

定义结构体SmartContract,此结构体内嵌了contractapi包的结构体。

type SmartContract struct {
	contractapi.Contract
}

并将此结构体与合约的函数绑定(继承公共属性方法)。

func (s *SmartContract)

1.2ctx.GetStub账本处理函数

所有函数的传参都会传递一个ctx上下文,它定义为一个关于交易的接口实例。

ctx contractapi.TransactionContextInterface

此交易接口定义如图,又内嵌了两个接口实例,我们主要用到第一个GetStub。

之后,通过此接口的GetStub()函数可以得到一个stub对象(ChaincodeStuInterface的实例对象),stub对象提供了若干函数支持对账本的操作,本代码主要用到PutState,GetState和DelState函数,即资产的写入,读取和删除。此接口下三个函数定义如图。

通过以上说明和阅读函数定义,正确写出函数的调用以及需要传入的参数和返回值,也就可以明白本智能合约八个函数的核心就是围绕这三个(读,写,删)函数。

//读取数据,输入资产数据的ID,返回相对应数据和错误信息
ctx.GetStub.GetState(key string) ([]byte,error)
//写入数据,输入当前资产ID和它对应的数据,返回错误信息
ctx.GetStub.PutState(key string, value []byte) error
//删除数据,输入资产ID,删除此ID对应数据,返回错误信息
ctx.GetStub.DelState(key string) error


//合约的最后一个函数GetAllAsset,还调用了该对象的查询方法,本合约就出现一次了解即可
ctx.GetStub.GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error)

1.3 (反)序列化JSON

因为这里的资产是定义为了一个结构体数组,数组里放了若干条数据,每个数据对应结构体定义的字段,但是9.2介绍的读(写)函数返回值(接收值)都是[]byte类型,所以要对数组做序列化处理,简单来说,写入资产前要变成json格式,读取资产要反序列化json为正常格式,主要用到两个函数,需要注意下第二个函数用法。

json.Marshal(v any) ([]byte, error)//写入数据前先序列化
json.Unmarshal(data []byte, v any) error//读出数据后要反序列化,这个处理后的data将指向v

将合约中的结构体数组作为参数,演示此过程

package main

import (
    "encoding/json"
    "fmt"
)

type Asset struct {
    AppraisedValue int    `json:"AppraisedValue"`
    Color          string `json:"Color"`
    ID             string `json:"ID"`
    Owner          string `json:"Owner"`
    Size           int    `json:"Size"`
}

func main() {
    assets := []Asset{
        {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
        {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
        {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
        {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
        {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
        {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
    }
    //定义一个数组接受反序列化结果
    var deserializedAssets []Asset
    
    //1.序列化,它将逐条输出JSON
    for _, asset := range assets {
        assetsJSON, err := json.Marshal(assets)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(asset.ID, assetsJSON)
        //关注这一句,将上面的JSON反序列化再填入数组
        err = json.Unmarshal(assetsJSON, &deserializedAssets)
    }
    //逐条输出数组内容,这就是反序列化结果
    for _, asset := range deserializedAssets {
        fmt.Println(asset)
    }
}

上半部分是部分JSON信息(序列化结果太长),下半部分是反序列化结果。

1.4 错误处理

上面一共主要介绍了五个函数(读,写,删,序列化,反序列化)的定义,基本都要接受一个错误信息,简单模仿书上写法即可,每个合约函数省去错误处理也没几行。经过以上几个介绍,可以看一个获取所有资产GetAllAssets()简单例子(书上并没有给出第八个函数解释,其它都有看书就行)。不详细介绍GetStateByRange函数,也是嵌套了好几层接口,返回个迭代器,然后从里面拿结果,在这里包括了关闭退出,循环检查等功能。

// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
    // range query with empty string for startKey and endKey does an
    // open-ended query of all assets in the chaincode namespace.
    //GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error)
    
    //传两个空的字符串,表示查询所有信息。
    resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
    if err != nil {
        return nil, err
    }
    //函数结束时退出迭代器
    defer resultsIterator.Close()
    //定义一个数组接受信息
    var assets []*Asset
    //循环检查是否还有数据
    for resultsIterator.HasNext() {
        //按顺序指向一下个值,这个值就是从账本(状态数据库)得到的JSON
        queryResponse, err := resultsIterator.Next()
        if err != nil {
            return nil, err
        }
        //一个中间状态,传递反序列化的值
        var asset Asset
        //反序列化值存入数组
        err = json.Unmarshal(queryResponse.Value, &asset)
        if err != nil {
            return nil, err
        }
        //将asset值给assets
        assets = append(assets, &asset)
    }
    //返回数组,此为查询结果
    return assets, nil
}

2.使用智能合约

assetTransfer.go主要是启动合约的,所以代码量不大,这里不对链码和智能合约概念作区分,链码更像是生产环境的智能合约合约,统一认为一个意思,

  • 导包
  • 创建链码对象的方法
  • 启动合约的方法

2.1 导包

借助log包记录日志输出,上一步说到的contractapi包,还有代码路径(chaincode包)。

import (
    "log"

    "github.com/hyperledger/fabric-contract-api-go/contractapi"
    "github.com/hyperledger/fabric-samples/asset-transfer-basic/chaincode-go/chaincode"
)

2.2 创建链码对象

使用此contractapi下面的NewChaincode方法获取链码对象,传入合约对象指针,返回链码对象和错误信息。

func NewChaincode(contracts ...ContractInterface) (*ContractChaincode, error)

这里的指针也就是上一步介绍的那个SmartContract结构体指针,它关联了整个合约文件

assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
	if err != nil {
		log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
	}

2.3 启动合约

得到链码对象,运行此对象关联的Strat方法即可启动智能合约。

func (cc *ContractChaincode) Start() error

这里启动了合约并进行了错误处理,并且err的声明写法更简洁。

if err := assetChaincode.Start(); err != nil {
		log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
	}
  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啊吧吧_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值