wasm合约目前有两种不同的编写方式。
非ABI方式 合约接口的入参与返回需开发者自己实现打包与解包,且不支持结构体作为入参,无ABI文件。
ABI方式 实现入参与返回值的自动打包与解包,支持结构体入参,支持多个返回值,生成相关的ABI文件。
#ifdef __cplusplus
extern "C" {
#endif
const char* thunderchain_main(const char* action, const char *args) {
if (strcmp(action, "HelloThunder") == 0)
return "Hello Thunderchain";
}
#ifdef __cplusplus
}
#endif
其中thunderchain_main为wasm合约入口函数,函数原型如下:
const char* thunderchain_main(const char* action, const char *args);
C++代码中,thunderchain_main要用到extern "C"。其中
action为具体合约方法,args为合约参数。
非ABI方式,生成bytecode体积较小,但代码相对繁琐。
#include "tcmethod.h"
#include "tcapi.h"
class Hello : public TCBaseContract{
public:
const char* HelloThunder(){
TC_Payable(false);
return "Hello Thunderchain";
}
};
TC_ABI(Hello, (HelloThunder))
用于接收参数的Hello类需继承于TC_BaseContract,方法类的公有的成员函数可设置为合约方法。 TC_ABI宏用来声明合约类与合约方法,只能调用TC_ABI声明过的方法。
TC_ABI(<入口合约类名>, (<方法名>)(<方法名>)(<方法名>)...)。
TC_ABI宏只能使用一次。
ABI方式会生成abi文件,标明输入输出类型与方法,同时代码也相对简洁。 HelloThunderchain合约生成的abi如下,各字段与solidity ABI兼容。
{
"name":"HelloThunder",
"payable":false,
"inputs":[],
"outputs":[
{
"type":"const char *"
}
]
}
ABI方式仅支持C++语言
合约部署流程与solidity合约一致,具体见solidity部署合约。 wasm合约当前不支持构造函数入参,部署时需要将params设置为空。
创建合约时,wasm虚拟机会调用action = "Init"方法。 如需初始化函数,例子如下
#include "tcapi.h"//wasm api 头文件
#ifdef __cplusplus
extern "C" {
#endif
const char* thunderchain_main(const char* action, const char *arg) {
if(strcmp(action, "Init")==0){ //action = "Init",初始化函数
return "Init Success";
} else if (strcmp(action, "Hello")==0){ //action = "Hello"
return "Hello Thunderchain";
}
}
#ifdef __cplusplus
}
#endif
部署合约后,会执行如下代码。
return "Init Success";
调用合约时,禁止action = "init" or "Init"
目前仅支持使用Catalyst迅雷链合约IDE线上编译。 参考Catalyst使用指南
调用合约时,交易data的数据格式为<action>|<args>,action与args以"|"分割。 具体参考合约例子
以具体合约为例,该合约功能为写入/读取短码记录的URL。
合约数据存储采用Key-Value方式,相关api为TC_StorageSet/TC_StorageGet
#include"tcapi.h"
char* init(){return EMPTY_CSTRING;}
#ifdef __cplusplus
extern "C" {
#endif
char* thunderchain_main(char* action, char* args){
void* root = TC_JsonParse(args);
//初始化函数,可选
ACTION_FUNC("Init", init);
ACTION_CODE("setrecord", {
//解析入参
char* recordInfo = TC_JsonGetString(root, "recordInfo");
char* RCode = TC_JsonGetString(root, "RCode");
//数据存储
TC_StorageSet(RCode, recordInfo);
return EMPTY_CSTRING;
});
ACTION_CODE("getrecord", {
//解析入参
char* RCode = TC_JsonGetString(root, "RCode");
//数据读取,并返回
return TC_StorageGet(RCode);
});
}
#ifdef __cplusplus
}
#endif
ACTION_FUNC与ACTION_CODE宏定义如下
#define ACTION_FUNC(_a, _fn) do{\
if (strcmp(action, _a) == 0) {return _fn();}\
}while(0)
#define ACTION_CODE(_a, _code) do{\
if (strcmp(action, _a) == 0) {_code}\
}while(0)
调用setrecord函数,设置 fbac231bc2a 记录的URL为 blockchain.xunlei.com 其中action = setrecord,args = {"recodeInfo":"blockchain.xunlei.com","RCode":"fbac231bc2a"}
最终生成的data数据为 "setrecord|{"recodeInfo":"blockchain.xunlei.com","RCode":"fbac231bc2a"}" 具体交易params json如下,from/gas/gasPrice/value以实际交易为准
{
"from": "0x54fb1c7d0f011dd63b08f85ed7b518ab82028100",
"gas": "0x76c00",
"gasPrice": "0x9184e72a",
"data": "0x7365747265636F72647C7B227265636F6465496E666F223A226F70656E2E6F6E657468696E67636C6F75642E636F6D222C2252436F6465223A226662616332333162633261227D"
}
实际构造交易时,需将action+args构成的字符串转为十六进制(ascii转hex)
以golang为例
str:="f|{}" h := []byte(str) hexStr := fmt.Sprintf("0x%x", h) //hexStr = "0x667c7b7d" fmt.Println(hexStr)
调用getrecord读取数据,读取 fbac231bc2a 记录的URL,其中action为getrecord,args为{"RCode":"fbac231bc2a"} 最终生成的data段数据为 "getrecord|{"RCode":"fbac231bc2a"}" 具体交易params json如下
{
"from": "0x54fb1c7d0f011dd63b08f85ed7b518ab82028100",
"gas": "0x76c00",
"gasPrice": "0x9184e72a",
"data": "0x6765747265636F72647C7B2252436F6465223A226662616332333162633261227D"
}
catelyst中调用wasm合约时,填入Action以及Key-Value,catelyst会自动生成入参action、args。
MinAllocMemSize = 32//malloc内存最小粒度为32B
FixedStackIdx = 16*1024//栈大小为16k
MaxDataMemSize = 16*1024//Data段限制为16k
DefaultMinHeapMemSize = 64*1024
DefaultMaxHeapMemSize = 256*1024//默认堆大小最小为64k,最大为256k
浮点数(float/double)
operator new/delete
typeid/dynamic_cast(-fno-rtti)
try-catch(-fno-exceptions)
signal.h
math.h
locale.h
errno.h
uchar.h
time.h(Incomplete support)
rand
atomics
thread
random
为方便wasm合约可以方便的访问区块链相关信息,wasm虚拟机对外提供了Storage、Message、Event、Contract相关的接口。
C语言中address定义 typedef char* address;
在wasm合约中,需要合约显式的调用Storage接口,实现对数据的持久化存储与读取。
void TC_StorageSet(
const char *key,
const char *val
)
以字符串key为键,持久化存储数据val
参数
返回值 无
char* TC_StorageGet(
const char *key,
const char *val
)
以字符串key为键,查询对应的数据 参数
void TC_StorageDel(
const char *key
)
删除以字符串key为键的数据 参数
void TC_Transfer(
const address to,
const BigInt amount
)
当前合约账户给address地址转账amount数额 参数
返回值 无
char* TC_GetMsgData()
获取合约调用的输入数据 参数 无 返回值 合约输入数据,字符串表示
uint64 TC_GetMsgGas()
获取合约调用的gas上限 参数 无 返回值 合约调用的gas上限
address TC_GetMsgSender()
获取合约调用者 参数 无 返回值 合约调用者地址,16进制字符串表示
char* TC_GetMsgSign()
获取合约调用参数中的方法名字段 参数 无 返回值 合约调用参数中的方法名字段
char* TC_GetMsgValue()
获取合约调用者转账给合约的数额 参数 无 返回值 16进制字符串表示
uint64 TC_GetNumber()
获取区块链当前区块高度 参数 无 返回值 区块链当前区块高度
uint64 TC_GetGasLimit()
获取当前区块的gas上限 参数 无 返回值 当前区块的gas上限
char* TC_GetCoinbase()
获取区块链的coinbase账户地址 参数 无 返回值 区块链的coinbase账户地址,16进制字符串表示
char* TC_BlockHash(
uint64 blockNumber
)
获取指定高度的区块哈希 参数
uint64 TC_GetTxGasPrice()
获取合约调用的gas价格 参数 无 返回值 合约调用的gas价格
char* TC_GetTxOrigin()
获取执行的起始地址,即原始交易的sender 参数 无 返回值 区块链的账户地址,16进制字符串表示
uint64 TC_GasLeft()
获取合约当前剩余的gas 参数 无 返回值 合约当前剩余的gas
char* TC_GetBalance(
const char *address
)
获取指定账户address的余额 参数 无 返回值 address账户的当前余额
char* TC_GetSelfAddress()
获取合约自身地址 参数 无 返回值 16进制字符串表示的合约地址
char* TC_GetMsgTokenValue()
获取合约调用者转账给合约的token数额 参数 无 返回值 16进制字符串表示给合约的token数额
迅雷链事件机制相关函数
void TC_Notify(
const char *eventID,
const char *data
)
合约触发事件 参数
void TC_Log0(
const char *data
)
合约触发事件,无事件标识 参数
void TC_Log1(
const char* data,
const char* topic
)
合约触发事件,带一级事件主题标识 参数
void TC_Log2(
const char* data,
const char* topic1,
const char* topic2
)
合约触发事件,带两级主题标识 参数
void TC_Log3(
const char* data,
const char* topic1,
const char* topic2,
const char* topic3
)
合约触发事件,带三级主题标识 参数
void TC_Log4(
const char* data,
const char* topic1,
const char* topic2,
const char* topic3,
const char* topic4
)
合约触发事件,带四级主题标识 参数
char* TC_SelfDestruct(
const char *address
)
销毁合约,并将合约账户剩余的余额转给address账户 参数
char* TC_CallContract(
address contract,
const char* action,
const char* args
)
调用外部合约方法,语义与solidaty Call一致 参数
不支持转账操作
char* TC_DelegateCallContract(
address contract,
const char* action,
const char* args
)
调用外部合约方法,语义与solidaty DelegateCall一致 参数
不支持转账操作
void TC_Assert(
bool condition
)
断言判断,与C语言的assert()接口功能类似,如果输入的condition为false,则触发合约终止并回滚状态 参数
void TC_Require(
bool condition
)
与TC_Assert()接口一样 参数
void TC_RequireWithMsg(
bool condition,
const char *msg
)
与TC_Require()接口功能一样,若condition为false, msg参数将会打印到日志中,方便查看调试 参数
bool TC_IsHexAddress(
const char *address
)
判断输入的字符串是否为地址的16进制字符串表示 参数
void TC_Payable(
bool condition
)
设置当前接口是否可以转账,默认为true,可以转账;若condition为false,且合约调用者传递的msg.Value > 0, 则会抛出异常终止合约执行,并回滚状态 参数
注意:如果接口A TC_Payable=true 接口B Payable=false, 当msg.value > 0, 接口A调用接口B时,B接口会报错哦 如上情况下,建议对B接口进行封装,对外调用提供不可转账的,对内调用提供可转账的。
void TC_Prints(
const char *s
)
打印信息s到日志(非交易事件log)中,方便调试 参数
uint64 TC_Now()
返回当前区块的时间戳,以unix时间表示 参数 无 返回值 unix时间戳
int TC_CheckSign(
const char* pubkey,
const char* data,
const char* sig
)
检查是否为给定公钥pubkey对信息data创建的签名signature,签名算法为secp256k1 参数
迅雷链提供的token发行、余额、转账等函数
void TC_Issue(
const BigInt amount
)
发行token 参数
address TC_TokenAddress()
查询msg中token类型 参数 无 返回值 token类型
为方便编解码json对象,暂时由wasm虚拟机提供json相关的接口。
void* TC_JsonParse(
const char *s
)
解析输入字符串为json对象,并返回json对象的句柄 参数
void* TC_JsonNewObject()
创建json对象,返回对应的句柄 参数 无 返回值 新创建json对象的句柄
int TC_JsonGetInt(
void *root,
const char *key
)
在root指向的json对象中,获取指定key字段的整数值 参数
int64 TC_JsonGetInt64(
void *root,
const char *key
)
在root指向的json对象中,获取指定key字段的整数值 参数
char* TC_JsonGetString(
void *root,
const char *key
)
在root指向的json对象中,获取指定key字段的字符串数值 参数
address TC_JsonGetAddress(
void *root,
const char *key
)
在root指向的json对象中,获取指定key字段的地址数值 参数
BigInt TC_JsonGetBigInt(
void *root,
const char *key
)
在root指向的json对象中,获取指定key字段的BigInt数值 参数
void* TC_JsonGetObject(
void *root,
const char *key
)
在root指向的json对象中,获取指定key字段的子json对象 参数
void TC_JsonPutInt(
void *root,
const char *key,
int val
)
在root指向的json对象中,设置指定key字段的值为val 参数
void TC_JsonPutInt64(
void *root,
const char *key,
int64 val
)
在root指向的json对象中,设置指定key字段的值为val 参数
void TC_JsonPutString(
void *root,
const char *key,
const char* val
)
在root指向的json对象中,设置指定key字段的值为val 参数
void TC_JsonPutAddress(
void *root,
const char *key,
const char* val
)
在root指向的json对象中,设置指定key字段的值为val 参数
void TC_JsonPutBigInt(
void *root,
const char *key,
const char* val
)
在root指向的json对象中,设置指定key字段的值为val 参数
void TC_JsonPutObject(
void *root,
const char *key,
void* child
)
在root指向的json对象中,设置指定key字段的值为val 参数
char* TC_JsonToString(
void *root
)
把root句柄指向的json对象,转换为字符串 参数
wasm虚拟机底层替换的部分C标准库接口,适配wasm虚拟机底层内存模型。
兼容标准库接口函数原型
exit
void exit(int)
正常终止合约的执行并退出 参数 合约终止状态码(暂未使用,可忽略) 返回值 无
void abort()
异常终止合约的执行并退出,会引起合约状态的回滚 参数 无 返回值 无
void* malloc(size_t size)
分配指定大小的内存,并返回内存的起始地址 参数
void* calloc(
size_t count,
size_t size
)
分配nmemb个元素,每个元素的大小为size字节,并返回内存的起始地址 参数
void* realloc(
void *ptr,
size_t size
)
调整ptr指向内存的大小为size个字节,并返回新的内存起始地址 参数
void* realloc(
void *ptr,
size_t size
)
调整ptr指向内存的大小为size个字节,并返回新的内存起始地址 参数
void* memcpy(
void* dest,
const void* src,
size_t n
)
内存拷贝 参数
src和dst内存段不可以有重叠
void* memmove(
void* dest,
const void* src,
size_t n
)
内存拷贝 参数
dst和src内存区域可以重叠
void* memset(
void* dest,
int c,
size_t n
)
填充内存,设置为指定的数值 参数
char* strconcat(
const char* str1,
const char* str2
)
把s2字符串拼接到s1字符串后面 参数
!!strconcat返回的地址一定是新的地址。!! 不可用str1地址
char * itoa(
int n
)
32位整数转换为字符串 参数
wasm合约提供一些基础的签名算法接口。
char* TC_Ecrecover(
const char* hash,
const BigInt v,
const BigInt r,
const BigInt s
)
通过消息的hash和签名(v, r, s)恢复公钥
参数
返回值 以16进制格式输出公钥字符串
char* TC_Ripemd160(
const char* data
)
Ripemd160哈希运算
参数
char* TC_Sha256(
const char* data
)
Ripemd160哈希运算
参数
char* TC_Keccak256(
const char* data
)
参数
标准的C库不提供BigInt操作,考虑到区块链底层与余额有关的操作,都是以BigInt作为操作数,而第三方的C库与go本身的BigInt操作会存在不兼容的问题,因此统一由wasm虚拟机提供此类接口,以c字符串形式表示。
C语言中BigInt定义 typedef char* BigInt;
C函数文档
TC_BigIntAdd
BigInt TC_BigIntAdd( const BigInt a, const BigInt b )
BigInt加法运算,等价于(a + b) 参数
- a - BigInt数字
- b - BigInt数字 返回值 加法结果
BigInt TC_BigIntSub(
const BigInt a,
const BigInt b
)
BigInt减法运算,等价于(a - b) 参数
BigInt TC_BigIntMul(
const BigInt a,
const BigInt b
)
BigInt乘法运算,等价于(a * b) 参数
BigInt TC_BigIntDiv(
const BigInt a,
const BigInt b
)
BigInt除法运算,等价于(a / b) 参数
BigInt TC_BigIntMod(
const BigInt a,
const BigInt b
)
BigInt取模运算,等价于(a % b) 参数
int TC_BigIntCmp(
const char *a,
const char *b
)
BigInt大小比较 参数
int64 TC_BigIntToInt64(
const char *s
)
BigInt转换为64位整数,超过64位的数值将会被截断 参数