http://www.7klian.com

CKB 剧本编程简介 第五讲:调试

pry(main)> tx.witnesses[0] = "0x"
#include <memory.h>#include "ckb_syscalls.h"
$ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:bionic-20191012 bash


$ cd ckb-standalone-debugger/bins
(gdb) n
pry(main)> carrot_cell_dep = CKB::Types::CellDep.new(out_point: CKB::Types::OutPoint.new(tx_hash: carrot_tx_hash, index: 0))
347bf730c08eb0aab7e56e0357945a4d6cee109a
$ ./target/release/ckb-debugger -g type -h 0xa8b79392c857e29cb283e452f2cd48a8e06c51af64be175e0fe0e2902c482837 -t duktape.json
此刻链上有了 carrot 的剧本,我们可以建设一笔生意业务来测试这个 carrot 剧本:
=> 2765824
(gdb) n

= undefined
利用 GDB 调试 C 措施
License GPLv3+: GNU GPL version 3 or later
(gdb)
$ ls
root@982d1e906b76:/code# make
在这里,我们将利用 ckb-duktape 来展示基于 JavaScript 的 REPL。可是请留意,这只是一个 demo 用来演示一下事情流程,没有任何对象阻止您将本身喜爱的动态语言(不管是 Ruby、Rython、Lisp 等等)移植到 CKB 中去,并为该语言启动 REPL。
}
请留意,你大概会获得和我这里纷歧样的哈希,这得看你利用的情况。

$ git clone https://github.com/nervosnetwork/ckb-duktape
· 首先我更新了这个剧本,让它可以兼容 ckb v0.23.0。在这个版本中,我们可以利用 ckb_load_cell_data 来获取 cell 的数据。
$2 = 0 '\000'
pry(main)> api = CKB::API.new
len = 6;
$ git clone https://github.com/nervosnetwork/ckb-system-script...
pry(main)> CKB::MockTransactionDumper.new(api, tx).write("duktape.json")

$ cd ckb-duktape
(gdb) n
$ git clone https://github.com/nervosnetwork/ckb-standalone-de...

duk> capacity_field = CKB.load_cell_by_field(0, 0, CKB.SOURCE.OUTPUT, CKB.CELL.CAPACITY)
7e2ad2d9ed6718360587f3762163229eccd2cf10
pry(main)> duktape_repl_type_script.compute_hash

root@66e3b39e0dfd:/code# riscv64-unknown-elf-gdb carrot
留意,你大概需要按照你的情况,调解 carrot 范例剧本的哈希可能 carrot.json 的路径。此刻让我们试试在一个差异的终端内通过 GDB 毗连调试器:
pry(main)> duktape_repl_data.bytesize
duk> foo(123)
11 len = 6;
= undefined
= undefined

首先,让我们实验编译 duktape:
12 memset(buffer, 0, 6);
此刻我们知道了错误的原因,,接下往复修复 carrot 剧本中的错误就很是简朴了。可是正如你看到的,我们设法从 CKB 上获取一笔错误生意业务在运行时的状态,然后通过 GDB(一个业界常见的东西)来对其举办调试。并且您在 GDB 上现有的事情流程和东西也可以在这里利用,是不是很棒?
pry(main)> tx.outputs[0].type = carrot_type_script
pry(main)> tx.cell_deps << carrot_cell_dep
(gdb) p cmp

if (cmp) {
0
= undefined
riscv64-unknown-elf-gcc build/repl.o build/duktape.o -o build/repl -lm -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-s
$ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:bionic-20191012 bash

duk> function buf2hex(buffer) { return Array.prototype.map.call(new Uint8Array(buffer), function(x) { return ('00' + x.toString(16)).slice(-2); }).join(''); }

}
=> "0xa8b79392c857e29cb283e452f2cd48a8e06c51af64be175e0fe0e2902c482837"
$ ./target/release/ckb-debugger -l 0.0.0.0:2000 -g type -h 0x039c2fba64f389575cdecff8173882b97be5f8d3bdb2bb0770d8a7e265b91933 -t carrot.json
For help, type "help".
19 if (cmp) {
For bug reporting instructions, please see:
= [object ArrayBuffer]
Copyright (C) 2019 Free Software Foundation, Inc.
Type "show copying" and "show warranty" for details.
pry(main)> tx = tx.sign(wallet.key, api.compute_transaction_hash(tx))
= a8b79392c857e29cb283e452f2cd48a8e06c51af64be175e0fe0e2902c482837
这里我们可以看到那边出问题了:buffer 中第一个字节的值是 0,这和 c 差异,因此我们的 buffer 和 carrot 差异。条件 if (cap) { 没有跳转到下一个轮回,而是跳到了 true 的环境,返回了 -1,表白与 carrot 匹配。呈现这样问题的原因是,当两个 buffers 相等的时候,memcmp 将会返回 0,当它们不相等的时候,将返回非零值。可是我们没有测试 memcmp 的返回值是否为 0,就直接在 if 条件中利用了它,这样 C 会把所有的非零值都视为 true,这里返回的 -99 就会被判定为 true。对付初学者而言,这是在 C 中会碰着的典范的错误,我但愿你不会再犯这样的错误。:)
unsigned char buffer[6];
本文成立在 ckb v0.23.0 之上。详细的,我在每个项目中利用的是如下版本的 commit:
riscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror duktape/duktape.c -c -o build/duktape.o
(gdb) n
您还可以利用与 CKB 相关的成果:



• ckb-sdk-ruby:
root@982d1e906b76:/code# exit
duk> buf2hex(hash)
int cmp = memcmp(buffer, "carrot", 6);
请留意,我们在这里获得的剧本哈希正是我们当前执行的范例剧本的哈希!这将证明 CKB 系统调试在这里是有效的,我们也可以实验更多有趣的对象:
root@982d1e906b76:/# cd /code
Type "apropos word" to search for commands related to "word"...

首先,让我们将这笔生意业务连同利用的情况,都转存到一个当地文件中:
carrot.c
size_t index = 0;

(gdb) n


假如你仔细查抄这笔生意业务,你会发此刻输出的 cell 中,并没有以 carrot 开头的数据。然而我们运行之后仍然是验证失败,这意味着我们的剧本必然存在 bug。先前,没什么此外步伐,你大概需要返归去查抄代码,但愿可以找到堕落的处所。但此刻没有这个须要了,你可以跳过这里的生意业务,然后将其输入到一个独立的 CKB 调试器开始调试它!


return 0;
pry(main)> tx.cell_deps << duktape_repl_cell_dep
Breakpoint 1 at 0x106b0: file carrot.c, line 6.
pry(main)> carrot_data_hash = CKB::Blake2b.hexdigest(carrot_data)



int main(int argc, char* argv[]) {

结论

duk> print(CKB.CELL.CAPACITY)

pry(main)> duktape_repl_cell_dep = CKB::Types::CellDep.new(out_point: CKB::Types::OutPoint.new(tx_hash: duktape_repl_tx_hash, index: 0))

return -1;
pry(main)> carrot_data.bytesize
(gdb) b main
• ckb-duktape:


pry(main)> wallet = CKB::Wallet.from_hex(api, "<your private key>")
pry(main)> wallet2 = CKB::Wallet.from_hex(api, CKB::Key.random_private_key)


你需要在这里生成 build/repl 二进制文件。和 carrot 的例子雷同,我们先将 duktape REPL 的二进制文件陈设在 CKB 上:
}
(gdb) n
(gdb) p buffer[0]
pry(main)> api = CKB::API.new
这里我举办了两处修改:
= 00e40b5402000000
pry(main)> CKB::MockTransactionDumper.new(api, tx).write("carrot.json")
pry(main)> tx = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(100), use_dep_group: false, fee: 5000)
root@66e3b39e0dfd:/# cd /code
= undefined
while (1) {
$ cp ckb-system-scripts/c/ckb_*.h ./

duk> buf2hex(capacity_field)
pry(main)> carrot_data = File.read("carrot")
18 int cmp = memcmp(buffer, "carrot", 6);
root@3efa454be9af:/code# riscv64-unknown-elf-gcc carrot.c -g -o carrot
pry(main)> duktape_repl_tx_hash = wallet.send_capacity(wallet2.address, CKB::Utils.byte_to_shannon(300000), CKB::Utils.bin_to_hex(duktape_repl_data), fee: 310000)
• ckb:
int ret;


基于 REPL 的开拓/调试
=> 19296

在这里你还需要跟踪 carrot 范例剧本的哈希:
Find the GDB manual and other documentation resources online at:

riscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror c/entry.c -c -o build/entry.o
然而,GDB 仅仅是现代软件开拓中的一部门。动态语言在很洪流平上占据了主导职位,许多措施员都利用基于 REPL 的开拓/调试事情流。这与编译语言中的 GDB 完全差异,根基上你需要的是一个运行的情况,你可以输入任何你想要与情况举办交互的代码,然后获得差异的功效。正如我们将在这里展示的,CKB 也会支持这种范例的开拓/调试事情流。: p

duk> function foo(a) { return a + 1; }
然后让我们把它转存到文件中,并查抄 duktape 范例剧本的哈希:
20 return -1;
与上面差异的是,我们不需要启动 GDB,而是可以直接启动措施:
root@3efa454be9af:/# cd /code
(gdb) c

.
此刻,让我们将剧本陈设到 CKB 上。保持 CKB 节点处于运行状态,并启动 Ruby SDK:
2
CKB 剧本调试的第一种方案,凡是合用于 C、Rust 等
编程语言。也许你已经习惯了写 C 的措施,而 GDB 也是你的好搭档。你想知道是不是可以用 GDB 来调试 C 措施,谜底虽然是:Yes!你必定可以通过 GDB 来调试用 C 编写的 CKB 剧本!让我来演示一下:
13 ret = ckb_load_cell_data(buffer, &len, 0, index, CKB_SOURCE_OUTPUT);
7 uint64_t len = 0;

riscv64-unknown-elf-gcc build/entry.o build/duktape.o -o build/duktape -lm -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,-s
和往常一样,我们利用官方的 toolchain 来将其编译成 RISC-V 的代码:
你可以看到一个 duk> 提示你输入 JS 代码!同样,假如碰着错误,请查抄是否需要变动范例剧本的哈希,可能利用正确的 duktape.json 路径。我们看到常见的 JS 代码可以在这里事情运行:
请留意,当我编译剧本的时候,我添加了 -g,以便生成调试信息,这在 GDB 中很是有用。对付实际利用的剧本,你老是但愿只管地完善它们来只管节减存储在链上的空间。
=> "0x039c2fba64f389575cdecff8173882b97be5f8d3bdb2bb0770d8a7e265b91933"
pry(main)> wallet2 = CKB::Wallet.from_hex(api, CKB::Key.random_private_key)
(gdb) target remote 192.168.1.230:2000
duk> print(CKB.SOURCE.OUTPUT)
首先,我们照旧用之前文章顶用到的关于 carrot 的例子:
Type "show configuration" for configuration details.
if (ret == CKB_INDEX_OUT_OF_BOUND) {
= undefined
pry(main)> api.send_transaction(tx)
(gdb) n
14 if (ret == CKB_INDEX_OUT_OF_BOUND) {

pry(main)> carrot_type_script.compute_hash

pry(main)> duktape_repl_data = File.read("build/repl")

我们还需要建设一笔包括 duktape 剧本的生意业务,我这里利用一个很是简朴的剧本,虽然你可以插手更多的数据,这样你就可以在 CKB 上玩起来了
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv64-unknown-elf".
pry(main)> tx.witnesses[0] = "0x"
这个 00e40b5402000000 大概在一开始看起来有点神秘,可是请留意 RISC-V 利用的是 little endian(低字节序),所以假如在这里我们将字节序列颠倒,我们将获得 00000002540be400,在十进制中正好是 10000000000。还要记着,在 CKB 中容量利用的单元是 shannons,所以 10000000000 正好是 100 个字节,这正是我们生成上面的生意业务时,想要发送的代币的数量!此刻你看到了如安在 duktape 情况中与 CKB 愉快地玩耍了 :)
.

There is NO WARRANTY, to the extent permitted by law.
CKB::RPCError: jsonrpc error: {:code=>-3, :message=>"Script(ValidationFailure(-1))"}

事实上,CKB 剧本事情的层级要比其他智能合约低许多,因此 CKB 的调试进程就显得相当神秘。在本文中,我们将展示如何调试 CKB 剧本。你会发明,其实调试 CKB 剧本和你日常调试措施并没有太大区别。
= 124
pry(main)> carrot_tx_hash = wallet.send_capacity(wallet2.address, CKB::Utils.byte_to_shannon(20000), CKB::Utils.bin_to_hex(carrot_data), fee: 21000)

(gdb) n

Reading symbols from carrot...

$ sudo docker run --rm -it -v `pwd`:/code nervos/ckb-riscv-gnu-toolchain:bionic-20191012 bash

duk> print(1 + 2)

}
duk> var hash = CKB.load_script_hash()
· 我还在这段代码中插手了一个小 bug,这样我们等会儿就可以举办调试的事情流程。假如你很是熟悉 C,你大概已经留意到了,虽然你没有在意到的话也完全不消担忧,稍后我会表明的。
index++;
$ ls
pry(main)> duktape_repl_data_hash = CKB::Blake2b.hexdigest(duktape_repl_data)

Remote debugging using 192.168.1.230:2000
uint64_t len = 0;
我们已经先容了两种差异的在 CKB 中调试的进程,你可以随意利用个中一种(可能两种)。我已经火烧眉毛地想看你们在 CKB 上玩出花来啦!

此刻,让我们来试试 ckb-standalone-debugger:

duk>



GNU gdb (GDB) 8.3.0.20190516-git
• ckb-standalone-debugger:


riscv64-unknown-elf-gcc -Os -DCKB_NO_MMU -D__riscv_soft_float -D__riscv_float_abi_soft -Iduktape -Ic -Wall -Werror c/repl.c -c -o build/repl.o
This is free software: you are free to change and redistribute it.
pry(main)> tx.outputs[0].type = duktape_repl_type_script
pry(main)> tx = wallet.generate_tx(wallet2.address, CKB::Utils.byte_to_shannon(100), use_dep_group: false, fee: 5000)
0x00000000000100c6 in _start ()
留意,这里的 192.168.1.230 是我的事情站在当地网络中的 IP 地点,你大概需要调解该地点,因为你的计较机大概是差异的 IP 地点。此刻我们可以试一下常见的 GDB 调试进程:

carrot.c ckb_consts.h ckb_syscalls.h ckb-system-scripts/

6 size_t index = 0;
pry(main)> wallet = CKB::Wallet.from_hex(api, "<your private key>")

ret = ckb_load_cell_data(buffer, &len, 0, index, CKB_SOURCE_OUTPUT);
pry(main)> carrot_type_script = CKB::Types::Script.new(code_hash: carrot_data_hash, args: "0x")
Breakpoint 1, main (argc=0, argv=0x400000) at carrot.c:6
$1 = -99
=> 283048
Continuing.
break;
3
2379e89ae285e4e639b961756c22d8e4fde4d6ab


pry(main)> duktape_repl_type_script = CKB::Types::Script.new(code_hash: duktape_repl_data_hash, args: "0x")
$ cargo build --release

memset(buffer, 0, 6);
18a89d8c69e173ad59ce3e3b3bf79b5d11c5f8f8
root@3efa454be9af:/code# exit

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。