前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >佛萨奇2.0系统丨佛萨奇2.0智能合约系统开发上线版丨佛萨奇2.0开发现成源码功能

佛萨奇2.0系统丨佛萨奇2.0智能合约系统开发上线版丨佛萨奇2.0开发现成源码功能

原创
作者头像
VX_I357O98O7I8
发布2022-12-15 15:27:17
4520
发布2022-12-15 15:27:17
举报
文章被收录于专栏:商业模式策划

7. 使用TinyGo进行智能合约开发

读者对象:本章节主要描述使用TinyGo进行ChainMaker合约编写的方法,主要面向于使用Go进行ChainMaker的合约开发的开发者。为了最小化wasm文件尺寸,使用的是TinyGO编译器。

注意:TinyGo 的功能在 ChainMaker V2.3.0 之后将不再更新。推荐使用 docker-go 进行 Golang 合约的开发。

7.1. 环境依赖

使用 TinyGo 开发用于 ChainMaker 的 wasm 合约,需要安装 TinyGo 编译器,同时注意以下几点:

  • TinyGo对wasm的支持不太完善,对内存逃逸分析、GC等方面有不足之处,比较容易造成栈溢出。在开发合约时,应尽可能减少循环、内存申请等业务逻辑,使变量的栈内存地址在64K以内,要求tinygo version >= 0.17.0,推荐使用0.17.0。
  • TinyGo对导入的包支持有限,请参考:https://tinygo.org/lang-support/stdlib/,对列表中显示已支持的包,实际测试发现支持的并不完整,会发生一些错误,需要在实际开发过程中进行测试检验。
  • TinyGo引擎不支持fmt和strconv包。

7.2. 编写TinyGo智能合约

7.2.1. 搭建开发环境

为了简化用户的配置,我们已将开发、编译环境做成镜像,放在 chainmakerofficial/chainmaker-go-contract:2.1.0 上,直接执行下列命令可以拉取镜像,构建开发环境:

代码语言:javascript
复制
    docker pull chainmakerofficial/chainmaker-go-contract:2.1.0
    docker run -it --name chainmaker-go-contract -v $WORK_DIR:/home chainmakerofficial/chainmaker-go-contract:2.1.0 bash

其中,$WORK_DIR 为本地工作目录,挂载到 docker 的 /home 下面。

7.2.2. 代码编写规则

代码入口

代码语言:javascript
复制
func main() { // sdk代码中,有且仅有一个main()方法
	// 空,不做任何事。仅用于对tinygo编译支持
}

对链暴露方法写法为:

  • //export upgrade
  • func method_name(): 不可带参数,无返回值
代码语言:javascript
复制
//export init_contract 表明对外暴露方法名称
func initContract() {

}

其中init_contract、upgrade方法必须有且对外暴露

  • init_contract:创建合约会执行该方法
  • upgrade: 升级合约会执行该方法
代码语言:javascript
复制
// 安装合约时会执行此方法,必须。ChainMaker不允许用户直接调用该方法。
//export init_contract
func initContract() {

}
// 升级合约时会执行此方法,必须。ChainMaker不允许用户直接调用该方法。
//export upgrade
func upgrade() {

}

7.2.3. 示例代码说明

7.2.3.1. 存证合约示例源码展示

实现功能:

1、存储文件哈希和文件名称和该交易的ID。

2、通过文件哈希查询该条记录

代码语言:javascript
复制
/*
Copyright (C) BABEC. All rights reserved.

SPDX-License-Identifier: Apache-2.0

一个 文件存证 的存取示例 fact

*/

package main

import (
  "chainmaker.org/contract-sdk-tinygo/sdk/convert"
)

// 安装合约时会执行此方法,必须
//export init_contract
func initContract() {
  // 此处可写安装合约的初始化逻辑

}

// 升级合约时会执行此方法,必须
//export upgrade
func upgrade() {
  // 此处可写升级合约的逻辑

}

// 存证对象
type Fact struct {
  fileHash string
  fileName string
  time     int32 // second
  ec       *EasyCodec
}

// 新建存证对象
func NewFact(fileHash string, fileName string, time int32) *Fact {
  fact := &Fact{
    fileHash: fileHash,
    fileName: fileName,
    time:     time,
  }
  return fact
}

// 获取序列化对象
func (f *Fact) getEasyCodec() *EasyCodec {
  if f.ec == nil {
    f.ec = NewEasyCodec()
    f.ec.AddString("fileHash", f.fileHash)
    f.ec.AddString("fileName", f.fileName)
    f.ec.AddInt32("time", f.time)
  }
  return f.ec
}

// 序列化为json字符串
func (f *Fact) toJson() string {
  return f.getEasyCodec().ToJson()
}

// 序列化为cmec编码
func (f *Fact) marshal() []byte {
  return f.getEasyCodec().Marshal()
}

// 反序列化cmec为存证对象
func unmarshalToFact(data []byte) *Fact {
  ec := NewEasyCodecWithBytes(data)
  fileHash, _ := ec.GetString("fileHash")
  fileName, _ := ec.GetString("fileName")
  time, _ := ec.GetInt32("time")

  fact := &Fact{
    fileHash: fileHash,
    fileName: fileName,
    time:     time,
    ec:       ec,
  }
  return fact
}

// 对外暴露 save 方法,供用户由 SDK 调用
//export save
func save() {
  // 获取上下文
  ctx := NewSimContext()

  // 获取参数
  fileHash, err1 := ctx.ArgString("file_hash")
  fileName, err2 := ctx.ArgString("file_name")
  timeStr, err3 := ctx.ArgString("time")

  if err1 != SUCCESS || err2 != SUCCESS || err3 != SUCCESS {
    ctx.Log("get arg fail.")
    ctx.ErrorResult("get arg fail.")
    return
  }

  time, err := convert.StringToInt32(timeStr)
  if err != nil {
    ctx.ErrorResult(err.Error())
    ctx.Log(err.Error())
    return
  }

  // 构建结构体
  fact := NewFact(fileHash, fileName, int32(time))

  // 序列化:两种方式
  jsonStr := fact.toJson()
  bytesData := fact.marshal()

  //发送事件
  ctx.EmitEvent("topic_vx", fact.fileHash, fact.fileName)

  // 存储数据
  ctx.PutState("fact_json", fact.fileHash, jsonStr)
  ctx.PutStateByte("fact_bytes", fact.fileHash, bytesData)

  // 记录日志
  ctx.Log("【save】 fileHash=" + fact.fileHash)
  ctx.Log("【save】 fileName=" + fact.fileName)
  // 返回结果
  ctx.SuccessResult(fact.fileName + fact.fileHash)
}

// 对外暴露 find_by_file_hash 方法,供用户由 SDK 调用
//export find_by_file_hash
func findByFileHash() {
  ctx := NewSimContext()
  // 获取参数
  fileHash, _ := ctx.ArgString("file_hash")
  // 查询Json
  if result, resultCode := ctx.GetStateByte("fact_json", fileHash); resultCode != SUCCESS {
    // 返回结果
    ctx.ErrorResult("failed to call get_state, only 64 letters and numbers are allowed. got key:" + "fact" + ", field:" + fileHash)
  } else {
    // 返回结果
    ctx.SuccessResultByte(result)
    // 记录日志
    ctx.Log("get val:" + string(result))
  }

  // 查询EcBytes
  if result, resultCode := ctx.GetStateByte("fact_bytes", fileHash); resultCode == SUCCESS {
    // 反序列化
    fact := unmarshalToFact(result)
    // 返回结果
    ctx.SuccessResult(fact.toJson())
    // 记录日志
    ctx.Log("get val:" + fact.toJson())
    ctx.Log("【find_by_file_hash】 fileHash=" + fact.fileHash)
    ctx.Log("【find_by_file_hash】 fileName=" + fact.fileName)
  }
}

func main() {

}

7.2.4. 合约SDK接口描述

长安链提供Tinygo合约与链交互的相关接口,写合约时可直接导入包,并进行引用,具体信息可参考文章末尾”接口描述章节”。

7.2.5. 编译合约

7.2.5.1. 使用Docker镜像搭建编译环境

ChainMaker官方已经将容器发布至 https://hub.docker.com/u/chainmakerofficial

拉取镜像

代码语言:javascript
复制
docker pull chainmakerofficial/chainmaker-go-contract:2.1.0

请指定你本机的工作目录$WORK_DIR,例如/data/workspace/contract,挂载到docker容器中以方便后续进行必要的一些文件拷贝

代码语言:javascript
复制
docker run -it --name chainmaker-go-contract -v $WORK_DIR:/home chainmakerofficial/chainmaker-go-contract:2.1.0 bash
# 或者先后台启动
docker run -d  --name chainmaker-go-contract -v $WORK_DIR:/home chainmakerofficial/chainmaker-go-contract:2.1.0 bash -c "while true; do echo hello world; sleep 5;done"
# 再进入容器
docker exec -it chainmaker-go-contract bash
7.2.5.2. 编译示例合约
代码语言:javascript
复制
cd /home/
# 解压缩合约SDK源码
tar xvf /data/contract_go_template.tar.gz
cd contract_tinygo
# 编译main.go合约
sh build.sh

生成合约的字节码文件在

代码语言:javascript
复制
/home/contract_tinygo/chainmaker-contract-go.wasm
7.2.5.3. 示例框架描述

解压缩contract_go_template.tar.gz后,文件描述如下:

代码语言:javascript
复制
/home/contract_tinygo# ls -l
total 64
-rw-rw-r-- 1 1000 1000    56 Jul  2 12:45 build.sh            	# 编译脚本
-rw-rw-r-- 1 1000 1000  4149 Jul  2 12:44 bulletproofs.go		# 合约SDK基于bulletproofs的范围证明接口实现
-rw-rw-r-- 1 1000 1000 18871 Jul  2 12:44 chainmaker.go			# 合约SDK主要接口及实现
-rw-rw-r-- 1 1000 1000  4221 Jul  2 12:44 chainmaker_rs.go		# 合约SDK sql接口实现
-rw-rw-r-- 1 1000 1000 11777 May 24 13:27 easycodec.go			# 序列化工具类
-rw-rw-r-- 1 1000 1000  3585 Jul  2 12:44 main.go				# 存证示例代码
-rwxr-xr-x 1 root root 65122 Jul  6 07:22 main.wasm				# 编译成功后的wasm文件
-rw-rw-r-- 1 1000 1000  1992 Jul  2 12:44 paillier.go 			# 合约SDK基于paillier的半同态运算接口实现
7.2.5.4. 编译说明

在ChainMaker IDE中集成了编译器,可以对合约进行编译。集成的编译器是 TinyGo。用户如果手工编译,需要将 SDK 和用户编写的智能合约放入同一个文件夹,并在此文件夹的当前路径执行如下编译命令:

代码语言:javascript
复制
tinygo build -no-debug -opt=s -o name.wasm -target wasm

命令中 “name.wasm” 为生成的WASM 字节码的文件名,由用户自行指定。

7.2.6. 部署调用合约

编译完成后,将得到一个.wasm格式的合约文件,可将之部署到指定到长安链上,完成合约部署。 部署合约的使用教程可详见:部署示例合约 => 使用CMC工具部署/调用合约。

7.3. 迭代器使用示例

代码语言:javascript
复制
//export test_kv_iterator
func howToUseIterator() {
	ctx := NewSimContext()
    // 构造数据
	ctx.PutState("key1", "field1", "val")
	ctx.PutState("key1", "field2", "val")
	ctx.PutState("key1", "field23", "val")
	ctx.PutState("key1", "field3", "val")
	// 使用迭代器,能查出来  field1,field2,field23 三条数据
	rs, code := ctx.NewIteratorWithField("key1", "field1", "field3")
	if code == SUCCESS {
		for rs.HasNext() {
			key, field, val, code := rs.Next()
			if code == SUCCESS {
				// do something
			} else {
				rs.Close()
				ctx.ErrorResult("err")
				return
			}
		}
		rs.Close()
	}

	ctx.PutState("key2", "field1", "val")
	ctx.PutState("key3", "field2", "val")
	ctx.PutState("key33", "field23", "val")
	ctx.PutState("key4", "field3", "val")
	// 能查出来 key2,key3,key33 三条数据
	ctx.NewIterator("key2", "key4")
	// 能查出来 key3,key33 两条数据
	ctx.NewIteratorPrefixWithKey("key3")
	// 能查出来  key1 field2,key1 field23 三条数据
	ctx.NewIteratorPrefixWithKeyField("key1", "field2")

	ctx.PutStateFromKey("key5", "val")
	ctx.PutStateFromKey("key56", "val")
	ctx.PutStateFromKey("key6", "val")
	// 能查出来 key5,key56 两条数据
	ctx.NewIterator("key5", "key6")

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 7. 使用TinyGo进行智能合约开发
    • 7.1. 环境依赖
      • 7.2. 编写TinyGo智能合约
        • 7.2.1. 搭建开发环境
        • 7.2.2. 代码编写规则
        • 7.2.3. 示例代码说明
        • 7.2.4. 合约SDK接口描述
        • 7.2.5. 编译合约
        • 7.2.6. 部署调用合约
      • 7.3. 迭代器使用示例
      相关产品与服务
      容器镜像服务
      容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档