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

本文详细介绍了如何在Linux环境下部署Fabric2.2,并通过Go语言编写和管理链码,包括创建链码目录、设置依赖、定义资产结构、实现增删查改功能,以及链码的初始化和主函数。同时,文章还提到了链码依赖的管理方法,为后续链码部署打下基础。

系列文章目录

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

前言

在上一篇文章中,对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版本的链码编写进行了简单的介绍,具体介绍可参考官方文档。接下来会介绍将编写的链码部署到网络上的步骤。以上内容如有不正,请多多指教。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值