3. 用go编写简单的fabric链码

    科技2025-04-23  13

    系列文章目录

    Fabric2.2在Linux上的部署记录Fabric2.2测试网络(test-network)的使用记录用go编写简单的fabric链码将fabric链码部署到测试网络的记录利用fabric-gateway-java连接并调用fabric链码

    文章目录

    系列文章目录前言一、链码是什么?二、编写简单的链码(go版本)2.1 前期准备2.2 创建链码目录2.3 创建模块和源文件2.4 导入API包和自定义SmartContract2.5 定义资产结构2.6 初始化账本2.7 编写新增资产函数2.8 编写读取资产函数2.9 编写更新资产函数2.10 编写删除资产函数2.11 编写判断资产存在函数2.12 编写资产转移函数2.13 编写查询所有资产函数2.14 编写主函数 三、管理链码依赖总结

    前言

    在上一篇文章中,对fabric2.2的测试网络进行了部署和简单的调用了官方提供的链码样例。在此基础上,本文尝试根据官网文档编写go语言版本的链码。

    一、链码是什么?

    根据官方文档的描述,链码是用go、node.js或java编写的程序,可实现规定的接口。类似于EOS的智能合约,我们可以通过调用链码更新和查询账本内容。

    二、编写简单的链码(go版本)

    2.1 前期准备

    确保已经安装go并进行了正确的配置。因网络原因,可配置代理如下:

    go env -w GOPROXY=https://goproxy.io,direct

    本文基于go1.14进行实践。

    2.2 创建链码目录

    mkdir atcc && cd atcc

    2.3 创建模块和源文件

    go mod init atcc # 生成go.mod文件 touch atcc.go

    2.4 导入API包和自定义SmartContract

    package main import ( "encoding/json" "fmt" "log" "github.com/hyperledger/fabric-contract-api-go/contractapi" ) // SmartContract provides functions for managing an Asset type SmartContract struct { contractapi.Contract }

    2.5 定义资产结构

    // Asset describes basic details of what makes up a simple asset type Asset struct { ID string `json:"ID"` Owner string `json:"owner"` Value int `json:"Value"` }

    2.6 初始化账本

    // InitLedger adds a base set of assets to the ledger func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { assets := []Asset{ {ID: "asset1", Owner: "ZhangSan", Value: 300}, {ID: "asset2", Owner: "LiSi", Value: 400}, {ID: "asset3", Owner: "Klay", Value: 500}, } for _, asset := range assets { assetJSON, err := json.Marshal(asset) if err != nil { return err } err = ctx.GetStub().PutState(asset.ID, assetJSON) if err != nil { return fmt.Errorf("failed to put to world state. %v", err) } } return nil }

    2.7 编写新增资产函数

    // CreateAsset issues a new asset to the world state with given details. func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, owner string, Value int) error { exists, err := s.AssetExists(ctx, id) if err != nil { return err } if exists { return fmt.Errorf("the asset %s already exists", id) } asset := Asset{ ID: id, Owner: owner, Value: Value, } assetJSON, err := json.Marshal(asset) if err != nil { return err } return ctx.GetStub().PutState(id, assetJSON) }

    2.8 编写读取资产函数

    // ReadAsset returns the asset stored in the world state with given id. func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) { assetJSON, err := ctx.GetStub().GetState(id) if err != nil { return nil, fmt.Errorf("failed to read from world state: %v", err) } if assetJSON == nil { return nil, fmt.Errorf("the asset %s does not exist", id) } var asset Asset err = json.Unmarshal(assetJSON, &asset) if err != nil { return nil, err } return &asset, nil }

    2.9 编写更新资产函数

    // UpdateAsset updates an existing asset in the world state with provided parameters. func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, owner string, Value int) error { exists, err := s.AssetExists(ctx, id) if err != nil { return err } if !exists { return fmt.Errorf("the asset %s does not exist", id) } // overwriting original asset with new asset asset := Asset{ ID: id, Owner: owner, Value: Value, } assetJSON, err := json.Marshal(asset) if err != nil { return err } return ctx.GetStub().PutState(id, assetJSON) }

    2.10 编写删除资产函数

    // DeleteAsset deletes an given asset from the world state. func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error { exists, err := s.AssetExists(ctx, id) if err != nil { return err } if !exists { return fmt.Errorf("the asset %s does not exist", id) } return ctx.GetStub().DelState(id) }

    2.11 编写判断资产存在函数

    // AssetExists returns true when asset with given ID exists in world state func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) { assetJSON, err := ctx.GetStub().GetState(id) if err != nil { return false, fmt.Errorf("failed to read from world state: %v", err) } return assetJSON != nil, nil }

    2.12 编写资产转移函数

    // TransferAsset updates the owner field of asset with given id in world state. func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error { asset, err := s.ReadAsset(ctx, id) if err != nil { return err } asset.Owner = newOwner assetJSON, err := json.Marshal(asset) if err != nil { return err } return ctx.GetStub().PutState(id, assetJSON) }

    2.13 编写查询所有资产函数

    // 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. resultsIterator, err := ctx.GetStub().GetStateByRange("", "") if err != nil { return nil, err } defer resultsIterator.Close() var assets []*Asset for resultsIterator.HasNext() { 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 } assets = append(assets, &asset) } return assets, nil }

    2.14 编写主函数

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

    将以上内容整合到一起,就编写出了简单的链码,能实现对自定义资产结构的增删查改等功能。

    三、管理链码依赖

    在将依赖部署到网络上前,需要将链码的相关依赖包含在软件包中,最简单的方法就是利用go mod 将相关依赖下载到本地。

    go mod tidy #增加缺失的包,移除没用的包 go mod vendor #将依赖包复制到项目下的 vendor 目录

    总结

    本文对go版本的链码编写进行了简单的介绍,具体介绍可参考官方文档。接下来会介绍将编写的链码部署到网络上的步骤。以上内容如有不正,请多多指教。

    Processed: 0.013, SQL: 8