以太坊ewasm测试链下使用c语言编写ewasm合约
1、 合约的创建
a) 搭建开发环境(ubuntu18)
i. llvm安装
这个不要自己编译,耗时太久,虚拟机上可能编译不过,直接下载安装
打开网站llvm ,选择最新版安装,本文安装8.0
$ wget -c http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$ tar xJvf clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$ pwd
$ vim ~/.bashrc
export PATH=$PATH:..clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04/bin
$ source ~/.bashrc
$ clang --version
ii. wabt安装
git clone https://github.com/webassembly/wabt.git
mkdir wabt/build
cd wabt/build
cmake .. -DBUILD_TESTS=OFF
make -j4
iii. pywebassembly安装
git clone https://github.com/poemm/pywebassembly.git
iv. binaryen安装
git clone https://github.com/WebAssembly/binaryen.git
cd binaryen
mkdir build
cd build
cmake ..
make -j4
sudo make install
v. rust安装
curl https://sh.rustup.rs -sSf | sh
source $HOME/.cargo/env
cargo install chisel
b) 编写合约
i. 参考代码
C代码:https://github.com/poemm/C_ewasm_contracts
Rust代码:https://github.com/ewasm/rust-ewasm.git
Nim代码:https://github.com/status-im/nimplay
ii. 编写c代码
首先需要获取代码:
git clone https://github.com/poemm/C_ewasm_contracts.git
目的只是为了复制其中的几个文件
新建目录evmc,复制两个文件到这里
mkdir evmc
cd evmc
cp ../C_ewasm_contracts/src/ewasm.h .
cp ../C_ewasm_contracts/src/ewasm.syms .
在当前目录下编写hello.c:
#include "ewasm.h"
typedef unsigned char BYTE; // 8-bit byte
void main()
{
char hello[] = "hello world!";
BYTE buf[32];
memcpy(buf,hello,sizeof(hello));
finish((i32ptr*)buf,32);
}
由于以太坊环境无法printf,这里只能通过finish返回给调用者,其中的ewasm.h文件是从https://github.com/poemm/C_ewasm_contracts里面复制出来的
iii. 编译
clang -cc1 -O3 -emit-llvm -triple=wasm32-unknown-unknown-wasm hello.c -o hello.ll
opt -O3 hello.ll -o hello.ll
llc -O3 -filetype=obj hello.ll -o hello.o
wasm-ld hello.o -o hello.wasm --no-entry -allow-undefined-file=ewasm.syms -export=main
这个时候获取的wasm不符合以太坊ewasm的两个规则,只能导入ethereum库,只能导出main和malloc两个函数,因此无法使用,我们需要手动修改一下
iv. 修改wasm
先转换wasm为wast,修改后,再改为wasm
wasm-dis hello.wasm -o hello.wast
打开文件hello.wast:
修改:
(import "env" "finish" (func $finish (param i32 i32)))
为:
(import " ethereum" "finish" (func $finish (param i32 i32)))
找到:
(export "__heap_base" (global $global$1))
(export "__data_end" (global $global$2))
删除这两行
然后把wasm转换为wast
wasm-as hello.wast -o hello.wasm
这个过程太麻烦,写个脚本test.sh搞定:
wasmfile=$1
filename=${wasmfile%.*}
wastfile=${filename}".wast"
wasm-dis $wasmfile -o $wastfile
# generate file.wat with Wabt's wasm2wat, then:
sed -i -e 's/"env"/"ethereum"/g' $wastfile
sed -i -e 's/"_main"/"main"/g' $wastfile
sed -i '/_heap_base/d' $wastfile
sed -i '/_data_end/d' $wastfile
wasm-as $wastfile -o $wasmfile
调用:
./test.sh hello.wasm
c) 合约后期处理
这个时候的wasm还是不能直接使用,需要通过哨兵合约的检测,就必须使用chisel,编写chisel.yml,内容如下:
ewasm:
file: "hello.wasm"
output: "hello_deployer.wasm"
trimexports:
preset: "ewasm"
verifyimports:
preset: "ewasm"
verifyexports:
preset: "ewasm"
repack:
preset: "ewasm"
deployer:
preset: "memory"
然后执行:
chisel run
获得文件hello_deployer.wasm,这个文件就是用来发布的合约
d) 合约转化为16进制数据
执行命令:
hexdump -v -e '"" 1/1 "%02x" ""' hello_deployer.wasm
就可以转换成功
复制内容,制作发布合约的rpc:
{
"jsonrpc": "2.0",
"id": 171,
"method": "eth_sendTransaction",
"params": [
{
"data": "0x0061736d0100000001090260027f7f0060000002130108657468657265756d0666696e697368000003030200010503010001071102046d61696e0002066d656d6f727902000a0e0202000b0900410041b10110000b0bb801010041000bb1010061736d0100000001090260027f7f0060000002130108657468657265756d0666696e697368000003030201010405017001010105030100020615037f01419088040b7f00419088040b7f00418d080b071102066d656d6f72790200046d61696e00020a36020300010b3001017f230041206b2200240020004100290085083700052000410029008008370300200041201000200041206a24000b0b1401004180080b0d68656c6c6f20776f726c642100",
"from": "1AdrP7vxBYFVW27rrscPbwoziSr3WtPNin",
"gas": "0x47b760"
}
]
}
其中data数据就是hexdump得到的内容,注意前面多加了个0x