Nervos 是由秘猿科技发起的区块链项目。其特点是通过分层架构来解决目前区块链遇到的所谓不可能三角(可扩展、去中心化、安全)问题。目前多数号称3.0的区块链主要想从共识算法上对tps进行突破,但就目前来看,尚未出现可以媲美POW的算法,很多项目通过牺牲去中心化特性来提高tps,但这其实是有违区块的初忠,所以并不值得提倡。我们都知道分层架构是解决计算机复杂问题的有效手段。Nervos CKB(Common Knowledge Base,共公知识库)作为区块链中的Layer1,向其它 Layer2 网络提供数据、资产与身份服务的共同知识库。
有关CKB的其它介绍请参考其官网发布的白皮书。本文通过官方提供的示例讲解CKB交易转账,发行用户TOKEN的实现及相关原理。
CKB 核心数据结构
和比特币类似,CKB使用一种扩展的UTXO交易模型。CKB的核心数据结构如图1所示:
图 1. Nervos CKB 区块核心数据结构
针对图1,作一些重点的数据结构的介绍:
Block,Header中定义区块的一些基本信息,例如父块哈希值。commit_transactions是一个Transaction数组。
Transaction,主要包含输入CellInput和 CellOutput 两个数组,deps指向某一个交易输出,它在脚本验证时会使用到,后面详细介绍 。
Outpoint,指定了某一个 UTXO,其中hash是其所在的交易哈希值,index是其所在交易输出中的数组下标。
CellInput,previous_out指定了引用的 UTXO,unlock是一个脚本,用来解锁这个输入,如验证数字签名是否正确。
CellOutput,capacity作为 CKB 链的原生token,概念上类似于以太坊的 ETH,但是 CKB 目前是没有 gas 的概念,替代的,在 cell 中存储数据需要花费capacity。data是二进制数组,用来存储任意的数据。lock可以理解为指定了 cell 的所有者,实际上,它是由用户公钥以及解锁脚本一起生成的 sha3 哈希。
Script,reference指向已经存在于链上的一个Cell脚本,这个Cell 由上文提到的 deps 指定。binary是一个ELF类型的可执行程序,用于在 CKB-VM 中执行,reference跟binary应该是二选一的。signed_args是一个字符串数组,一般第一个元素是验证脚本,第二个元素是 用户的公钥 pubkey。args也是一个字符串数组,跟signed_args 一起作为参数传入验证脚本(传入时去掉了signed_args的第一个元素,因为它是验证脚本本身)。
CKB 虚拟机
CKB-VM 实现了一套基于 RISC-V 指令集的虚拟机,用来执行支持 RISC-V GCC 编译出来的ELF目标文件。在大家都准备支持 WASM 虚拟机的时候,CKB 选择的是另外一条路,可以说也是挺有个性的。
CKB-VM 通过系统调用的功能,实现脚本程序与链数据的交互。CKB-VM负责执行上文中提到的Script,在执行Script时,它会将Script中的 signed_args 以及 args 作为参数传递给脚本。脚本的可执行文件由两种方式提交,一种是存在于 binary 字段中,另外一种通过 reference 以及交易的deps 从链上的某一个块中获取,这些上文中的Script也已经有所介绍。来看段具体的实现源码:
以原生capacity cell 为例,在解锁一个input 时,args_source_entry 可执行文件会被 CKB-VM 加载,它实际上是一个 ruby 解释器,signed_args[0]是 bitcoin_unlock.rb, 会被 args_source_entry 加载执行,然后由 bitcoin_unlock.rb 负责对数字签名进行验证。
有关 args_source_entry 的核心代码如下:
CKB 节点部署
具体如何运行节点可参考官方库的 README.md 文件。这里提两个需要注意的地方:
default.json 文件中 miner.type_hash。这是由出块节点的公钥和解锁脚本参与计算得出的sha3哈希值,也即是下文中提到的矿工地址(通过miner.address 查看,如果不一致,替换掉default.json里面的值 )。
spec/dev.json 中 system_cells 添加的 cells/argv_source_entry。
Cell Capacity 转账
有三种方式可以获取 capacity:创世块 、出块奖励和转账。当然,转账本身是不会产生新的 capacity。下面基于 https://github.com/nervosnetwork/ckb-demo-ruby-sdk 中的示例, 详细分析 capacity 转账需要的步骤及产生的数据。
capacity cell 的类型根据 lock 来确定,它的解锁脚本如下,其中 VERIFY_SCRIPT 是 contracts/bitcoin_unlock.rb。
简单来说, cell 的类型由解锁脚本及用户公钥唯一确定。有了这个概念,我们接着往下看。
转账用户界面
第1,2 行通过不同私钥分别创建了两个叫 *miner *和alice的钱包。同时miner 也是出块结点。miner当前的余额是100000,是通过出块获取的奖励。alice的初始余额为0。第5行 admin 向 alice 发送了 12345 个 capacity。
转账交易构建
请注意,以下行为是在客户端执行。miner → alice
miner 根据 type_hash 调用 CKB 的 RPC 接口获取在自己的UTXOs,并找到满足 capacity 之和不少于12345 的 cells 作为 inputs。这里因为一个区块的奖励是 50000,所以只需要一个 cell 即可。
设置inputs的解锁脚本。
生成属于 alice 的 cell 作为 output,capacity 为12345。
如果inputs的capacity之和大于 12345 , 再生成一个属于自己的找零output2。
对inputs签名,生成tx:
转账交易验证
请注意,以下行为是在CKB节点执行。
capacity 检查。确保输入capacity 不小于 输出的 capacity。源码如下:
双花检查,确保交易没有被双花。源码如下:
脚本较验。运行input的解锁脚本,主要是验证数据签名,确保花费的是自己的 cell,源码如下:
安全性分析
UDT
UDT ( User Defined Token),通俗点来说,就是发币吧。由于 CKB 使用的是UTXO模型,相比 ERC20 余额查询方式, CKB UDT 的余额计算方式有很大的不同。官方提供了两种实现方式,一种是通过多个Cell记录,另一个是种仅使用一个Cell。和上文 capacity cell 类似,UDT 的解锁脚本定义如下,除了 UNLOCK_SCRIPT 不同,还多了一个 token_info.name:
创建UDT
创建UDT的接口如上,需要指明 cell 使用的容量, token名称以及token数量。 因此首先找到符合cell容量的一个或多个UTXOs作为交易的inputs,即所有UTXO的容量之和不小于capacity。并对每个input设置解锁脚本和参数。主要包含:
admin 找到符合 cell 容量的一个或多个UTXOs作为交易的inputs,并对其设置解锁脚本。
创建一个 output cell,lock 是 UDT 的 type_hash。 capacity 是接收的容量。token数量的16进制编码写入data中。
设置 output 脚本,ckb 在较验 output 时会运行它,它的主要功能为验证data及所有者。
对 inputs 和 outputs 打包生成交易并签名发送至CKB
转账
admin → alice
alice do:
alice 生成接收 token 数量为12345 的 cell 作为 output1,其 capacity 为3000;
alice在自己的UTXOs中找到满足output1中所需 capacity 的 cells 作为 inputs1 (sum_capacity(inputs) > 3000 + 10999);
如果需要找零 capacity,再生成一个找零的 output2
生成一个交易 partial_tx 并签名发送给 admin。
admin do:
接收到 alice 的 partial_tx,给里面的 input 解锁脚本添加参数,指明它生成的 output 是哪些,这是为了告诉解锁脚本在验证数字签名时使用哪些 output,没有这一点,验签不会通过。
根据 token 的 type_hash 找到满足 token 数量之和大于等于 amount的 UTXOs作为 inputs2
如果 inputs2 的 tokens amounts 大于12345 ,生成一个找零给自己的 token 的 output3,否则,
生成一个找零 capacity 的 output4
提交tx :
官方的 demo 中还提供了另个一种实现方式,每个账户仅用一个 cell 去存储 token , 想想我们上面的实现,它要把所有的UTXO 列出来相加才能计算 token 的余额。
ps. 由于CKB代码还在不断更新,本文的代码基于以下版本:https://github.com/nervosnetwork/ckb-demo-ruby-sdk/tree/eb4403e
https://github.com/nervosnetwork/ckb/tree/3abf2b1
参考:
[1] https://github.com/nervosnetwork/binary/blob/master/whitepaper/nervos-ckb.pdf
[2] https://github.com/nervosnetwork/ckb-demo-ruby-sdk/blob/eb4403ebee4c3c0f9acfd4c55efabf102b15f34f/README.md
领取专属 10元无门槛券
私享最新 技术干货