2018年5月23日晚,Qtum x86虚拟机及Qtum企业版的开发进展发布会于韩国首尔顺利举行。Qtum研发团队现场演示了用c语言编写的“Hello world”智能合约,从编写、编译、部署到调用的全套流程,这也是Qtum区块链(内部测试网络)上运行的首个用 C 语言编写的智能合约。
详情:
支持C/C++/Rust/Swift的Qtum X86虚拟机如何打造更丰富的智能合约生态
每周项目进展报告都有涉及 Qtum x86 技术进展,本系列文章主要为整合与实践操作指导,帮助更多的主流语言开发者加入区块链开发的大潮中来。本篇连载文章主要为“Hello World”合约的创建和调试。
Hello World
工具链设置完毕之后,我们终于可以使用一个简单的 Hello World 合约对它进行测试了。
这是一个非常简单的 Hello World 合约源代码:
#include <qtum.h>
int onCreate(){
qtumEventStringString("Hello World", "Contract creation");
return 0;
}
int main(){
qtumEventStringString("Hello World", "Execution Success!");
return 0;
}
在这里 onCreate 函数在最初使用 createcontract 或 qx86deploy 在区块链上部署智能合约时被调用。之后无论是执行 callcontract 、sendtocontract 或是其他合约调用时,都将调用 main 函数 。
qtumEventStringString 函数可在执行时生成一个新事件(event),StringString 后缀意味着事件的 “Key”和“Value” 都是一串字符。还有类似qtumEventStringInt64 这样的函数,用于指定的64位整数“Key”而不是字符串。使用 qx86cli searchevents 我们可以对该事件进行检索。
这个智能合约的编译过程稍微复杂一些。我们使用工具链docker 容器来进行实际的处理。这能简化在任何操作系统上编译 qtumx86 智能合约的过程,而不仅限于 Linux 或 OSX。
首先,我们编写一个 makefile。这个 makefile 将在 docker容器上执行,以确保所有 Qtum 工具都可用,下面是 hello world 的模板:
# files to be built
# fill this in as a template
HDRS =
C_SRC = helloworld.c
OUTPUT = helloworld.elf
LIBS =
BYTECODE = helloworld.qbit
C_OBJS = $(subst .c,.o,$(C_SRC))
#these default flags will just remove dead code and give warnings
CFLAGS += -Wall -ffunction-sections -f>LDFLAGS += -Wl,--gc-section
default: $(BYTECODE)
$(BYTECODE): $(OUTPUT)
x86testbench -assemble -raw $(OUTPUT) > $(BYTECODE)
$(OUTPUT): $(C_OBJS)
$(CC) $(LDFLAGS) -o $(OUTPUT) $(C_OBJS) $(LIBS)
$(C_OBJS): $(HDRS) $(C_SRC)
$(CC) $(CFLAGS) -c $*.c -o $@
clean:
rm -f $(C_OBJS) $(OUTPUT) $(BYTECODE)
我们把它的变量分解如下:
HDRS =
这是为了跟踪合约中的任何头文件。虽然头文件不是直接编译的,但是跟踪这些文件,可以在对头文件进行更改时重新编译代码。
C_SRC = helloworld.c
这些是要编译的C文件,可以通过在附加文件名后面加上空格分隔符将其扩展到多个文件。
OUTPUT = helloworld.elf
这是GCC编译器的输出,是一个标准的ELF文件,非常适合于后编译分析。大多数现有的逆向工程和分析工具都支持这种格式。因此,如果需要对编译器的二进制文件进行分析,那么可以使用这个文件。
LIBS =
这项操作是为了向合约中添加静态库。
BYTECODE = helloworld.qbit
这是实际用于部署在 Qtum 区块链上的字节码文件。它由 ELF 文件简化而来, 非常容易解析和使用,但由于存在较多限制,暂时没有工具支持。当使用 qx86deploy 将其部署到区块链时,将使用 Qtum RPC 命令 “createcontract” ,参数是这个文件,它被转储到十六进制字符串中。
关于修改 Makefile,对于大多数简单的合约,这就足够了。对于涉及许多库等的更复杂的文件,使用它来编写自定义 makefile 可能更理想。为了真正部署此合约,你可以在本地 shell 中使用此脚本(假设你已从工具链设置中检索了 helpers.sh 脚本”)。
qx86start
qx86cli generate 600 # this will generate 600 blocks. This will start the chain and give us some coins to work with
qx86deploy helloworld.qbit
qx86cli generate 1 # generate 1 block so that we can mine our contract and ensure its processed
在这步操作之后你会看到以下输出:
Jordans-MacBook-Pro:helloworld earlz$ qx86deploy helloworld.qbit
{
"txid": "02b28cfcc7d1fdcb5963ee2ff9024a045ac6fb4d8d66683b027e47fd5973ed0b",
"sender": "qUJfCTe5LacTUBjC7djrEfoMtfZGXD7J4L",
"hash160": "75e089080145b639bf496e27cc3ed655e13377d0",
"hexaddress": "85840fafe5b51343f58259de8b48fe6b001cce57",
"address": "xLUbyAmNUTRxLt67x5gDtDxBzWpLrH92Zn"
}
Jordans-MacBook-Pro:helloworld earlz$ qx86cli generate 1
[
"300b9d8d9e5d05c22d09ff91335766e559af21ab70ee6f37daf280220eecc06d"
]
许多不同的字段将从部署命令中返回,其中很重要的一个是带有 xLUbyAmNUTRxLt67x5gDtDxBzWpLrH92Zn 值的address,所以会根据区块链上实际情况的不同而改变。txid 字段表示的是合约所属的交易ID 。
接下来你可以继续以下操作:
qx86cli searchevents
之后将会出现一组相当大的 JSON 输出。无论合约的执行是否成功,每一份合约的执行都有一份 JSON 输出。下面是 JSON 中各字段的含义:
block hash——执行合约时的区块哈希
tx-hash——待定
tx-n——待定
address——被执行的根合约地址,可能是执行createcontract 或是调用 sendtocontract 时的地址
used-gas——执行合约时所花费的 gas
sender-refund——退还给 sender 的 QTUM 数量
如果合约执行中出现的错误或恢复,所有在执行中发送的 QTUM 将被退还给 sender
status——合约执行状态的描述字符串
status-code——合约执行状态的状态码
transfer-txid——待定
commit-state——显示该合约执行是否已经是提交状态
deltas——显示该合约执行过程中发生的所有状态变化。它会试图自动解析字符串或数字,并以更友好的方式显示
deltas-raw——与 deltas 一样,但总是以原始十六进制形式显示
modified-balances——显示由于该合约的执行引起余额变动的所有地址的余额
spent-vins——显示所有该合约执行时消耗的 UTXO
events——显示实际执行该合约时发生的事件,是一个 key-value 键值对
calls——显示其他合约执行结果的递归结构。在整个合约执行过程中,每个合约调用都有一个结果。
你将在 JSON 输出的底部看到如下内容:
"modified-balances": {
},
"spent-vins": [
]
},
"events": {
"Hello World": "Contract creation"
},
"calls": [
]
}
]
可以看到 hello world 合约已经生成。
为了调用该合约以及对它进行测试(这里请将地址换成你自己的),执行以下操作:
qx86cli callcontract xLUbyAmNUTRxLt67x5gDtDxBzWpLrH92Zn 00
这将立即返回 JSON 结果。callcontract 只是在本地调用合约,并不创建区块链交易。因此,没有与 callcontract 相关的费用产生。在 JSON 结果中,你现在应该能在 events 数组中看到"Hello World": "Execution Success!"
如要在区块链上中实际交易中执行同样的操作,你可以根据以下步骤:
qx86cli sendtocontract xLUbyAmNUTRxLt67x5gDtDxBzWpLrH92Zn 00
qx86cli generate 1 #create one block to confirm the transaction
执行后,不会有 JSON 结果出现,但你可以通过 searchevents 查看。