myvetools: 帮助开发者高效开发TS代码,快速实现智能合约测试

  • maxdeath
  • 更新于 2021-05-13 09:54
  • 阅读 3150

介绍一个自己开发的小工具 myvetools来帮助我能够比较高效地开发 TS 代码,用于智能合约的测试工作。

唯链雷神区块链是一个兼容 EVM 的公链系统。它自拥有一些独特的设计使其区别于其他公链系统,比如它允许每个交易能够执行多个任务,又比如每个交易都是通过一个唯一的32字节的 ID 来标识,而其他公链(比如以太坊)大多是通过账户加上当前发送交易数的组合来唯一标识一笔交易。虽然这些独特的设计能够简化系统设计并提升效率,它们也给开发者在合约开发测试上带来了不便,因为开发者不能够直接使用那些以太生态内的工具 (比如 Truffle)。作为一个开发者,我也遇到了同样的问题,所以我着手开发自己的工具 myvetools来帮助我能够比较高效地开发 TS 代码,用于智能合约的测试工作。

这个工具主要是在以下三个方面帮到开发者:

  1. 允许开发者比较容易地下载想要使用的 solidity 编译器版本;
  2. 能够让开发者直接在代码中直接编译 solidity 源代码;
  3. 基于 Connex 为开发者提供了更为方便的和区块链节点交互的接口。

工具安装


npm install myvetools

选择 Solidity 编译器版本


通过以下命令可以下载指定版本的 solidity 编译器,比如 0.7.0 版本:

./node_modules/.bin/solcver -d 0.7.0

通过以下命令使用特定版本的 solidity 编译器,比如 0.7.0 版本:

./node_modules/.bin/solcver -u 0.7.0

合约编译


以下代码片段展示了如何在代码中直接编译合约 A 的源文件,从而得到它的 ABI 和用于部署的合约执行代码。

import { compileContract } from 'myvetools/dist/util·s'

例子


我写了一个简单的例子(https://github.com/zzGHzz/myvetools-demo),为了读者能够更好地了解如何使用 myvetools 来写测试合约的代码。这篇文章的剩余部分将详细地介绍这个例子。

创建测试代码

你可以通过以下代码片段来创建你的测试代码。其中你需要通过设置变量 url 来链接一个指定的节点。当链接成功后,你会得到一个Connex 的对象,用于之后于区块链交互。

import { expect, assert } from 'chai'

你可以设置 url 为以下链接,来链接一个测试网节点:

const url = 'http://testnet.veblocks.net'

子句(Clause)与交易(Transaction)

对于不熟悉唯链的开发者,有一个很重要的概念他们需要理解。

在唯链雷神区块链系统里,每一笔交易都可以按顺序执行多个任务(比如,转代币或者是调用合约)。值得注意的是,这些任务都是由同一个账号发起,也就是这笔交易的发起者。每个任务在系统里都是由一个子句的实现的。

所以,如果要调用智能合约改变合约状态,我们需要做以下的步骤:

  1. 首先需要创建一个子句用于调用相关合约函数,
  2. 其次需要把该子句放进一个交易,
  3. 发送交易到区块链网络执行。

请务必把以上流程铭记在心,以便之后能够更好地理解下文要展示的代码片段。更多关于唯链交易模型的信息可以在https://docs.vechain.org/thor/learn/transaction-model.html找到。

  • 测试目标合约

合约 A 是我为了展示写的一个简单的合约,主要的功能是在合约内存储一个非负整数,可以通过 set 方法来更新。

pragma solidity ^0.7.0;

测试合约 A

我将会做以下事情来测试合约 A :

  1. 部署合约并把作为初始值传入 100
  2. 调用合约函数a 并检查返回值是不是等于 100
  3. 在同一个交易中调用合约函数 set 两次,先后更新合约内存储的整数为 200 和 300
  4. 验证调用合约产生的事件 SetA
  5. 调用合约函数a 并检查返回值是不是等于 300

请注意,完整的测试代码可以在https://github.com/zzGHzz/myvetools-demo/blob/main/test.ts找到。

  • 步骤一

首先我们为合约 A 创建一个 Contract 实例c :

import { Contract } from 'myvetools/dist/contract'

在以下的代码片段中,我们生成一个用于部署合约的子句,并且把这个子句放入一个交易,然后发送交易到连接的节点:

const clause1 = c.deploy(0, 100)

之后我们通过 getReceipt 得到交易的收据,并且检查该交易是不是被正确地执行了,或者说是交易没有被无效:

import { getReceipt } from 'myvetools/dist/connexUtils'

我们可以看到该交易是用其 ID 来标识,这个 ID 被存储在 txRep.txid 。之后我们需要从收据中得到创建的合约地址,并且记录到c 里:

if (receipt.outputs[0].contractAddress !== null) {
  • 步骤二
const callOut = await c.call('a')
  • 步骤三
const clause2 = c.send('set', 0, 200)

这里我们生成了两个子句来调用合约的 set 函数,并且把它们放入同一个交易中。需要注意的地方是,子句的执行顺序是按照它们在数组里的先后顺序。所以当执行完该交易时,合约中的整数先被设为 200,然后才是300。

  • 步骤四
import { decodeEvent } from 'myvetools/dist/connexUtils'

这里收据的 outputs 域中包含两个输出,分别对应于交易中的两个子句。

  • 步骤五
const callOut = await c.call('a')

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
maxdeath
maxdeath
代尔夫特理工大学 (TU Delft) · 区块链博士后 & 代尔夫特理工大学 (TU Delft) · 信息论博士