系列文章目录
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
touch atcc.go
2.4 导入API包和自定义SmartContract
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract
struct {
contractapi
.Contract
}
2.5 定义资产结构
type Asset
struct {
ID
string `json:"ID"`
Owner
string `json:"owner"`
Value
int `json:"Value"`
}
2.6 初始化账本
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 编写新增资产函数
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 编写读取资产函数
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 编写更新资产函数
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
)
}
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 编写删除资产函数
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 编写判断资产存在函数
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 编写资产转移函数
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 编写查询所有资产函数
func (s
*SmartContract
) GetAllAssets(ctx contractapi
.TransactionContextInterface
) ([]*Asset
, error) {
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版本的链码编写进行了简单的介绍,具体介绍可参考官方文档。接下来会介绍将编写的链码部署到网络上的步骤。以上内容如有不正,请多多指教。