《#诗盗#·草》:心怀蒙古大草原,脚踩都市小草团。心旷神怡思飞扬,每天冒死来上班。
注解
没错,“小草青青,脚下留情”这是反人类的标语,人类自古就喜欢踩草地。
可是人人都踩,草地就坏了鸭!草草这么可爱你怎么可以踩?
草,之所以被踩坏,是因为很多人焦虑总是走同一条最短路线。而稣不走寻常路,每天走不同曲线,踩不同的位置。草草这么口爱,稣就是要踩,只要不被人捅死就可以愉快地去上班。
《#诗盗#·草》:心怀蒙古大草原,脚踩都市小草团。心旷神怡思飞扬,每天冒死来上班。
没错,“小草青青,脚下留情”这是反人类的标语,人类自古就喜欢踩草地。
可是人人都踩,草地就坏了鸭!草草这么可爱你怎么可以踩?
草,之所以被踩坏,是因为很多人焦虑总是走同一条最短路线。而稣不走寻常路,每天走不同曲线,踩不同的位置。草草这么口爱,稣就是要踩,只要不被人捅死就可以愉快地去上班。
《#诗盗#·记一次见鬼吓醒》:鳏寡残废天不欢,绝症孤独神作伴。纵有二逼快乐多,情智双高命却短。
梦见两个过世的朋友,一个横结肠癌,一个胃癌……醒来又想起鳏寡效应……
愿科技进步,人间应该只有一种绝症,叫:活腻了。
《#诗盗#·大霾苍蝇》:碉堡千栋插云立,雾霾万里从天倾。晨跑打球不怕病?盖被睡眯保小命。
霹雳角色“大漠苍鹰”的诗号:
太行千仞插云立,黄河万里从天倾。冲霄岂为层嶂碍?击光翻浪任翱行。
《#诗盗#·黑客求道》:数十年来炼黑客,求知问心不可得。情劫歧途会有期,直见正道更无疑。
霹雳角色“剑禅觅心”的诗号:
数甲子来寻剑客,空劫佛尘会有期。求道觅心不可得,直见禅锋更无疑。
作者: UMU @ MEET.ONE 实验室
经典三步:
了解区块链基本概念,了解 EOS 基本情况;
看官方开发者文档;
开始愉快地写代码。
但是,有个很大的问题:开发语言居然是 C++!所以,鼓励师出场了……
不信?我们就来试一试:
1 | error: cannot use 'try' with exceptions disabled |
智能合约的编译目标是 WASM 文件,最终要在 WASM 的 VM 里运行,比如 wabt,这和常规情况下使用原生 C++ 开发可执行程序、静态库、动态库等,有很大不同。
受限部分包括:
语言特性。比如上面举例的 try。
可调用外部函数。比如 CRT 的 rand 函数,再比如您想用 socket 自由通信……没门。
内存访问。这个比较难解释,后面再说。
如果您学过 Golang、Python、nodejs、Java 或其它相近语言,转到智能合约开发,可以说是轻而易举。理由如下:
智能合约关注业务逻辑,和大部分脚本语言类似。
智能合约有很强的约束范围,API 很有限,不会要求记忆大量知识。举个例子,它可以使用 boost,但也只是子集,无法使用完全的 boost。
智能合约有很强的套路,代码是满满的既定格式。熟悉 Hello world,会用基本的命令行工具进行测试,最多只需要 2 天,就会发出“原来这么简单”的感叹。
如果您学过 Golang、Rust 可能会注意到,它们可以编译成 WASM 文件。比如 Golang 的编译命令为:
1 | GOOS=js GOARCH=wasm go build -o hello.wasm |
但是找个 Hello world 编译一下,您可能会哭,产生的 WASM 文件有 2.3MB,就获得一个打印信息……(EOS 基本概念:RAM 挺贵的。)
虽然现状是 C++ 一枝独秀,但未来可能会有人开发专门的编译器支持 Golang、Rust 等语言开发 EOS 智能合约。
VSCode 安装插件后可以直接打开 WASM 文件,显示 WAST 代码,比如我们随便打开一个 hello.wasm,滚动到末尾,可能会看到以下两行:
1 | (data (i32.const 8192) "read\00") |
下面我们写个 C++ 代码:
1 | const char* p = reinterpret_cast<const char*>(8192); |
以上代码,打印出 read
。如果把 8192
改为 8197
,则打印 get
;改为 8201
,打印 malloc_from_freed was designed to only be called after _heap was completely allocated
。
这个例子可能吓倒大家,特别交代下,一般开发中,较难遇到逆向……只是想说明 WASM 的内存管理和常规 C++ 开发的可执行程序是不同的,后者把指针指向 8192,是 Process Working Set 的地址,通常来说去读这么低的地址,后果极可能是读异常,挂掉。
划重点:虽然你用 C++ 写代码,但编译后是 WASM 二进制编码,运行时使用 VM,受控性很强,降低了开发难度,也杜绝很多安全问题。
为了讨好 Python 程序员,下面用 Python 来写个开平方运算,有这样的:
1 | import math |
也有这样的:
1 | import numpy |
他们有个共同点——很快……相对 C++ 写的!!有点难以理解?
Python 的 sqrt 函数,其实都是用 C 语言实现的,最终都是调用解释器里的本地代码,速度很快。
原生 C++ 写的本地程序,几乎肯定是比 Python 快的,但我们前面说过:智能合约的 C++ 不是常规的 C++,当它被编译成 WASM 后,我们去看 WAST 代码,会发现 sqrt 的实现整个被塞进 WASM 里,它最终要用 VM 来执行,当然没有 Python 解释器快了!
打开任意 WASM 文件,可以看到里面很多 (import
开头的行,这些都是原生 C++ 实现的 API,它们的执行速度就是本地代码的速度,对应官网 API 文档里的 API。
有前面的性能问题,我们不禁要问 EOS 为什么不多做点 API 来提高性能?这是因为维护少量 API 代价比较可控,数量一多就有版本问题,各节点可能因为版本不同步而无法达成共识。
另外,目前的 wabt 功能强大,性能也过得去,对于 sqrt 此类可能并不常用的数学函数,即使用原生 C++ 实现了,性能提升带来的好处,也无法平衡多版本可能带来的风险。
原则上,BP 之间快速达成共识,提升 TPS 才是更值得做的。
作者: UMU @ MEET.ONE 实验室
总结同步主网数据到 MongoDB 时的常用操作,大部分以 transaction_traces 表为例。
1 | read-mode = read-only |
从 https://eosnode.tools/blocks
下载最新 blocks data,以减少网络同步时间。
首次启动,应使用 --replay-blockchain
参数。
目前 nodeos 1.5+ 版本如果优雅退出,下次启动可以无需痛苦的 replay 过程,所以可以监控 nodeos 进程,如果退出就调用。
启动脚本 /home/ubuntu/shell/continue.sh:
1 | nohup /usr/local/eosio/bin/nodeos --config-dir /home/ubuntu/nodeos/config-dir --data-dir /home/ubuntu/nodeos/data-dir > /home/ubuntu/shell/`date +%Y-%m-%d_%H-%M`.log 2>&1 & |
守护脚本 /home/ubuntu/shell/autorun.sh:
1 | ps -C nodeos || /home/ubuntu/shell/continue.sh |
添加到计划任务,运行 sudo crontab -e
,输入下行并保存、退出:
1 | * * * * * /home/ubuntu/shell/autorun.sh |
nodeos 需要写入,使用有写入权限的 EOS 用户,其余情况使用只读权限的 EOSReader 用户,数据库安装之后就尽量不使用管理员用户。
1 | use EOS |
1 | use EOS |
作者: UMU @ MEET.ONE 实验室
当 MongoDB 因不可抗力故障,nodeos 重启后会丢失上次故障时正在插入的记录。
nodeos 会将插入语句连同错误原因等信息一起写入 log,这给了我们手动修复丢失的机会。下面以 transaction_traces 为例,介绍修复流程。
1 | grep 'mongo exception, trans_traces insert:' *.log > lost.txt |
1 | echo 'print("++++"); |
1 | nohup mongo mongodb://$user:$password@127.0.0.1:$port/admin lost.js > lost.log |
作者: UMU @ MEET.ONE 实验室
2018 年最后一个工作日,智能合约开发小哥哥遇到一个奇怪的现象:某个账号给我们合约转账,在 EOS 浏览器上都可以找到记录,但用 cleos get table
在合约的 RAM 里找却找不到!
了解具体情况后,注意到两个事实:
只有某个特定账号有问题,其它账号很正常。
那个有问题的账号是纯数字的。
这是 EOS 账号解析的问题,UMU 曾经给 EOS 提过一个相关的 issue:get_table_by_scope parameter lower_bound is NOT properly converted, cause enumeration dead loop #5824,里面有问题产生原因和解决方案。
eosio::name 本质是一个 uint64_t 数字的 base32 编码,编码形式是为了方便人类记忆。举个例子:
shengxiaokai
本质上是 14075216089888066784 (0xc3553675c6a40ce0)
cleos get table
在解析账号时,兼容了这两种表达形式,所以 14075216089888066784
和 shengxiaokai
是等价的。
但本身是纯数字的账号可就有歧义了,比如 313131313131
是当成一个 uint64_t 解释,还是当成 base32?很不巧,解析代码是优先当成 uint64_t 解释的。
给纯数字 EOS 账号加上个空格后缀,比如 111122223333
可以改为 "111122223333 "
。
作者: UMU @ MEET.ONE 实验室
很多 EOS 浏览器都只能显示别人给我抵押了多少 EOS,但不能看到是哪个账号帮我抵押的。
从 eosio.contracts/eosio.system/src/delegate_bandwidth.cpp
的 delegatebw
函数开始分析。
它调用了 changebw
,其中的查表操作是这样的:
1 | del_bandwidth_table del_tbl( _self, from.value ); |
scope 是 from,而 from 就是要求的未知项,直接粉碎我们用这路线继续求解的可能。
MEET.ONE 之前发布过几篇关于 MongoDB 插件的文章,这些积累为我们继续求解提供了很大便利。
直接在 Mongo Shell
里尝试:
1 | use EOS |
执行之后,找到一条 trx_id 为 9bd50c0fd6f0e1d0ed4c6f5c6f873a33976955ff9dae2ac3eb16cb7e9a44d106
的交易记录,显示 1freeaccount
为 shengxiaokai
抵押:
1 | { |
《#诗盗#·记一次危险性社交活动》:去年波荡吃火锅,今年当醉草消火。强京东哥薛定谔,有心怀春无处躲。
波荡:补东,即冬至要进补。
当醉:冬至。