Qtum x86 虚拟机技术文档连载(二)

7 个月前 创建于

*点击阅读公众号原文

本篇为 Qtum x86 虚拟机的第二篇技术分析连载,本篇逐渐进入实际代码操作实操阶段,想要了解什么是虚拟机、Qtum x86 虚拟机独特优势是什么请戳下方标题或图片:

Qtum x86 虚拟机技术文档连载(一)

2018年5月23日晚,Qtum x86 虚拟机及 Qtum 企业版的开发进展发布会于韩国首尔顺利举行。Qtum 研发团队现场演示了用c语言编写的 “Hello world” 智能合约,从编写、编译、部署到调用的全套流程,这也是 Qtum 区块链(内部测试网络)上运行的首个用 C 语言编写的智能合约。

详情:Qtum X86虚拟机及Qtum企业版原型于韩国发布

支持C/C++/Rust/Swift的Qtum X86虚拟机如何打造更丰富的智能合约生态

每周项目进展报告都有涉及 Qtum x86 技术进展,本系列文章主要为整合与实践操作指导,帮助更多的主流语言开发者加入区块链开发的大潮中来。本篇连载文章主要为“Hello World”合约的创建和调试。

Hello World

工具链设置完毕之后,我们终于可以使用一个简单的 Hello World 合约对它进行测试了。

这是一个非常简单的 Hello World 合约源代码:

include

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.cOUTPUT = helloworld.elfLIBS =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 -fdata-sectionsLDFLAGS += -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 [email protected]

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 脚本”)。

qx86startqx86cli generate 600 # this will generate 600 blocks. This will start the chain and give us some coins to work withqx86deploy helloworld.qbitqx86cli 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 00qx86cli generate 1 #create one block to confirm the transaction

执行后,不会有 JSON 结果出现,但你可以通过 searchevents 查看。