我们已经在学习过了理论知识,下面让我们进入动手实践阶段。
亲自上手摸摸 CKB 这条链,
才能更好体会到前面的理论知识。
我们已经在云端运行了一条测试链, 并预先生成了一些账户地址,方便这次教程的使用。
试试点击下面的按钮,查看这条链最新的区块。
Fetch Blocks
接下来我们准备了 3 个钱包。
试试看把鼠标移到钱包上,打开钱包。
☠️ 仅作为演示用途,请勿在正式场合或主网下使用这些钱包
钱包 1
mainet: ckb1qyqy84gfm9ljvqr69p0njfqullx5zy2hr9kqjynwlg
testnet: ckt1qyqy84gfm9ljvqr69p0njfqullx5zy2hr9kq0pd3n5
lock_arg: 0x43d509d97f26007a285f39241cffcd411157196c
private_key: 0xdd50cac37ec6dd12539a968c1a2cbedda75bd8724f7bcad486548eaabb87fc8b
钱包 2
mainet: ckb1qyqf22qfzaer95xm5d2m5km0f6k288x9warqwjwke8
testnet: ckt1qyqf22qfzaer95xm5d2m5km0f6k288x9warqnhsf4m
lock_arg: 0x952809177232d0dba355ba5b6f4eaca39cc57746
private_key: 0x6cd5e7be2f6504aa5ae7c0c04178d8f47b7cfc63b71d95d9e6282f5b090431bf
钱包 3
mainet: ckb1qyqtxvjgczx9tmtrd5hsp5r96g37cxsdxvaqp6culx
testnet: ckt1qyqtxvjgczx9tmtrd5hsp5r96g37cxsdxvaqulxrn6
lock_arg: 0xb33248c08c55ed636d2f00d065d223ec1a0d333a
private_key: 0x8ca6891c2386d71a6206f1d56888b48a4ee335ca8b03ed04ac31b143c0d8c609
每个钱包有四条信息,其含义如下:
在本次教程中,这 3 个钱包将被我们用于发送交易、部署合约等各种用途。
现在,你可以通过下面的按钮,选择其中任意一个钱包,查看这个钱包相关的 Cell 和交易。
点击上面任意一个 Cell 或者任意一笔交易,你会看到 JSON 格式的详细信息。
当我们在说,一个钱包拥有多少 CKB (原生代币)的时候,我们其实指的是,这个钱包能够解锁的所有的 live Cell 的 capacity 之和,也是这个钱包在链上占有的总存储空间。
现在,钱包 1 是云端这一条测试链默认的矿工地址。
也就是说,钱包 1 将源源不断收到来自挖矿所获得的出块奖励。所以你会看到钱包 1 查找出来的 live Cell 是最多的。钱包 2 和钱包 3 只有很少、甚至还没有 live cell。
目前,测试链只有一个矿工。
最后,我们还需要知道这条测试链的配置信息。
{
"PREFIX": "ckt",
"SCRIPTS": {
"SECP256K1_BLAKE160": {
"CODE_HASH": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
"HASH_TYPE": "type",
"TX_HASH": "0x4f1097802dc6fe19b942f1c2e8e52d564ee35899e4aef308101c86c49bc1f471",
"INDEX": "0x0",
"DEP_TYPE": "dep_group",
"SHORT_ID": 0
},
"SECP256K1_BLAKE160_MULTISIG": {
"CODE_HASH": "0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8",
"HASH_TYPE": "type",
"TX_HASH": "0x4f1097802dc6fe19b942f1c2e8e52d564ee35899e4aef308101c86c49bc1f471",
"INDEX": "0x1",
"DEP_TYPE": "dep_group",
"SHORT_ID": 1
},
"DAO": {
"CODE_HASH": "0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e",
"HASH_TYPE": "type",
"TX_HASH": "0x075c01b717840fd37fcf4a2f1b1bcdbf7c26f9c4781855bb772ac9d1a57eb2f0",
"INDEX": "0x2",
"DEP_TYPE": "code"
}
}
}
prefix:ckt
表明这条链是测试链,而不是主网。
scripts
里代表的则是链内置的智能合约,也就是 type 和 lock 可以利用的系统内置的一些锁。
每条 CKB 链都会预先在创世块部署几个系统内置的智能合约,上面显示了3个系统合约的具体信息。
好了,这就是我们所有必须了解的信息了。
接下来,我们将开始构建并发送第一笔交易!
我们将在本节内容学会并亲自动手完成一笔最基础的转账交易。
在开始之前,让我们在黑板上再次重写一遍:
一笔CKB的交易,无非是消费一些已有的 live cell,去创造出另一些新的 live cell。
同时,因为 CKB 采用的是“链下计算、链上确认”的设计,所以我们在转账的时候,甚至可以采用手动拼贴交易的方式,去完成一笔转账操作。
只要我们事先拟好交易的内容(也就是注明这笔交易要消费哪些cell、创造出什么样的新cell),然后用相应的私钥对交易进行签名, 当这笔交易被提交到链上,只要验证通过、签名有效,那么交易就会被打包完成。
这种手动拼贴交易的方式意味着什么呢?
在 CKB 这种体系下,其实我们人肉组成了一个 layer2 网络。
想象现在你有一位朋友住在亚马逊丛林里,他独自生活,身边只有一台离线的电脑,没有网络,与世隔绝。
某天下午他打猎归来,突然想起来还欠你一点钱,他打开电脑,想转给你 1 万个 CKB 还债。
尽管没有网络,他还是把转账交易的内容写在了一张纸上,然后在电脑里输入自己的私钥,计算出了这笔转账相应的签名,最后把签名也附在了纸上。
过了半个月,当有信差来访时,他托人把这张纸邮寄到中国,又过了半个月,信纸终于送到了你的手上。
你看着信纸,交易确实指明创造 1 万 CKB 的 cell 给你,你决定把这笔交易提交到 CKB 主网上。 主网验证附上的签名有效,于是交易完成,你的账户里多了一万个CKB,债务两清了。
你和亚马逊丛林的朋友共同组成了一个包含 2 个节点的 layer2 网络,虽然这个网络的吞吐量只有 1 笔交易/每月。
尽管 CKB 目前已经有了各种各样的工具,帮助你自动构建交易、完成转账、部署合约,等等, 但接下来,我们还是会延续亚马逊朋友的这种方法,来实现一笔普通的转账交易。
目的是使用手动拼接交易的方式,让你更深刻的理解 CKB cell 的工作原理。
我们将使用 JSON 格式来手动拼接交易。
下面是钱包 1 的 4 个 live cell,直接把 cell 拖到下面的框中,看看自动生成的 input 是什么样子的。
你可能看到了,input 中的 cell 是以 previous_output
的形式出现的, 传入的是 tx_hash 和 index 组成的 outpoint,相当于是对 cell 的一个索引,或者像 cell 的一个指针,通过 outpoint 我们找到想要消费的 cell。
inputs 中还有一个字段叫 since
,它是用来控制时间的,我们暂且不必管它。
除了inputs,还有一个字段叫 cell_deps
,它是一笔交易中需要依赖的 cell, 也是以 outpoint 这种索引结果出现的。
什么是需要依赖的 cell 呢?
比如在普通的转账交易中,lock 锁需要用到固定的加密算法 SECP256K1_BLAKE160,也就是系统内置的一个智能合约, 这个加密算法的代码存放在某个 cell 中,就需要在 cell_deps 中引用进来, 这样 CKB-VM 虚拟机才能知道从哪里载入代码进行运算。
通过上文测试链的配置信息,我们很容易找到 cell_deps 中需要传入的参数。
接下来我们再使用另一个工具,看看生成的output、以及一笔完整的交易长什么样子。
同样,把钱包 1 的 cell 拖到 input 中。
output 方框内将马上自动生成一个相同大小的新 cell。
点击 output 中的设置按钮,可以对新生成的 cell 进行重新分配,包括生成几个 cell、设置每个新 cell 的大小,设置每个 cell 的解锁地址,等等。
output 占用的 capacity 空间必须小于 input,二者的差值即为矿工能挣到的手续费。
设置完成后,点击“生成交易”的按钮,就可以看到这笔交易的 JSON 是什么样子了。
你应该注意到了,交易中的 outputs
把新生成的 cell 的信息都写出来了,包括 capactiy 大小、lock 锁等信息。
但 output 中的 cell 并没有指明 data 的信息,相反,data 被统一挪到了outputs_data
字段中,按顺序对应 outputs 中的 cell。
这样做也是出于性能优化角度来设计的。
最后,一笔完整的交易还包括 version
和 header_deps
两个字段。 前者为版本信息,目前固定设置为 0x0 ,后者暂时不用管,放空就行。
一笔转账拼好之后,需要用相应的私钥,对这笔交易进行签名,表明我们确实是 cell 的主人,有权对这些 cell 执行操作。
签名将被放入一个新的名为 witnesses 的字段中,作为交易的证明。
到这里你已经完整了解了一笔交易的过程,我们马上开始动手发交易。
现在,把下面白框中的空白交易填满。
把它当作一次练习,自己用手动的方式填写一笔转账交易。
你可能需要用到查找钱包对应的 live Cell、查看链配置信息(用来填写 cell_deps)、16 进制与 10 进制互相转换这些功能,它们在工具箱中都可以找到。
点击? Nervos 的图拍呢 ,即可打开工具箱。
{
version: "0x0",
cell_deps: [
{
out_point: {
tx_hash: "将此处补充完整",
index: "将此处补充完整",
},
dep_type: "将此处补充完整",
},
],
header_deps: [],
inputs: [
{
since: "0x0",
previous_output: {
tx_hash: "将此处补充完整",
index: "将此处补充完整",
},
},
],
outputs: [
{
capacity: "将此处补充完整",
lock: {
code_hash: "将此处补充完整",
hash_type: "将此处补充完整",
args: "将此处补充完整",
},
},
],
outputs_data: ["0x"],
witnesses: ["0x"]
}
保存
填写完成后,点击保存按钮。
好了,到这里你已经手动把交易全部填好了。
这时我们已经可以为这笔交易生成一个独一无二的哈希了,也就是 tx_hash 已经可以提前确定出来。
点击下面的按钮,试试生成生成交易的哈希。
尽管这笔交易已经可以提前生成 tx_hash,但它现在仍然是一笔 raw_tx。raw_tx 跟 tx 最大的不同是, tx 会在 witnesses 字段中放入交易的签名。
事实上,你可以在 witnesses 里放入任何你需要的参数或者证明。而且因为它是一个数组,还可以放入多个证明。 但因为现在我们在尝试的是系统内建的转账交易, 这种交易互相约定会在每一组 witnesses 的第一个位置,放入这样一个结构:
{
"lock": "证明",
"input_type": "证明",
"output_type": "证明"
}
这一个结构被称为 WitnessArgs。不同的锁会从 WitnessArgs 不同的字段中读取自己需要的签名。 其中,lock 字段是 input 使用到的 lock 锁需要验证的签名。 在我们现在要实验的普通转账交易中,就是 SECP256K1_BLAKE160 算法需要验证的签名。
input_type 和 output_type 则是 input 和 output 中 type 锁需要验证的签名,暂时不必管它。
签名是一个比较繁琐的过程。更详细的技术细节在这里:How to sign transaction
现在,为了完成签名,让我们首先为这笔交易生成一个待签名的 message。
有了 message,以及钱包里的私钥,我们就可以计算出签名了。
把生成的签名填入下面的输入框中,点击按钮,以 witnessArgs 的形式做一遍序列化:
现在我们可以完善原本的交易了,把序列化好的签名放进 witnesses 字段里:
注意看下,交易成功上链后返回的 tx_hash,是不是和之前事先生成的那个 tx_hash 一模一样?
CKB 的确定性诚不欺我。
现在,你可以通过下面的按钮,看看刚才我们发送的交易是不是真的在链上了。如果提示 tx_status: pending , 则表明交易还在pending,稍后重试就可以了。
最后恭喜你,成功完成了第一小节的内容~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!