在 CKB 上如何用最简单的方法构造验证交易

你知道如何在 CKB 上构建验证交易吗?今天,CKB 开发者 luochao 就来和大家聊一聊如何用最常见最简单的方式在 CKB 上构建验证交易,快来查看吧。

1. Cell Collet

CKB,任何交易必须包含至少一条 input 和一条 output。为了构造交易,首先需要定位 input,我们将其称为「cell collect」。

我们知道 CKB 与 UTXO 模型非常类似,这意味着如果没有任何预缓存,就无法得知任何地址的当前状态。组成当前状态的信息可能分散在链上不同的 cell 中。可参考 Cell model 了解 CKB 的数据模型。有两种方法可进行 cell collect:

a.使用 ckb indexer 服务

这里介绍一种标识指定地址的信息(如 live_cell 数量、交易数量、总容量)的简单方法,而不需要任何其他操作。请注意,由于基于 CKB 节点的 indexer 函数,此方法只适用于基础用法。另外此方法也不支持查找更详细的信息(例如,无法定位合约 cell),而且还将消耗 CKB 节点本身的资源。

不过抛开这些缺点,让我们从这个方法开始,以更好地理解单元收集是如何工作的。CKB 节点中默认关闭此功能。要打开它,请按如下所示手动更改配置文件并重新启动 CKB 节点:

在 mckb.toml 中更改以下配置:

[rpc]





# List of API modules: [“Net”, “Pool”, “Miner”, “Chain”, “Stats”, “Subscription”, “Indexer”, “Experiment”]


modules = [“Net”, “Pool”, “Miner”, “Chain”, “Stats”, “Subscription”, “Experiment”]

可看出 indexer 服务默认没有启动。要打开该功能,请向数组 modules 中添加「Indexer」。

modules = [“Net”, “Pool”, “Miner”, “Chain”, “Stats”, “Subscription”, “Experiment”, “Indexer”]

使用 ckb run -C<path>  重启 CKB 节点后,通过 RPC 方法 index_lock_hash 注册需索引的地址。请注意:index_from 参数控制索引的起始点,null 值从 chain tip(当前块)开始索引,而 0 值从 genesis 块开始索引。

现在,等待重新构建 index 服务,然后使用 index 服务的 RPC 接口来查看相应地址的 live_cell/transaction/capacity 值。

如果不再需要 index 服务,可以通过 RPC 注销监视列表,关闭服务,并从 ckb.toml 中的数组中删除「Indexer」。

b.创建你自己的 Cell 收集服务

创建你自己的 Cell 收集服务有什么好处?

我们都知道,交易包括创建和销毁任意数量的 cell,这是交易的最简单定义。由于 cell 使用的灵活性,任何有意义的数据都可以存储在 cell 的 data 字段中,各种类型的合约都可以用 type script 表示。因此,每个用户或用例可能对 cell 收集有不同的需求。

实际上有许多问题有待探索,每个问题根据不同的用例都可能有不同的答案:

· cell 收集的顺序是什么?先进先出,按大小顺序,最佳模型等等


· 可以使用哪种 cell?特定的幸运数字或任意的


· 是否有任何 cell data 或 type 需要特殊处理?


· 选择 cell 后是否需要过滤/确认?

CKB 包含的 index 服务不能很好地满足这些需求,并且它不能灵活适配未来可能增加的额外需求。最有效的方法是自己构建 cell 收集功能。

如何做?

当一个新的区块上链时,区块中作为 input 的 cell 必须从 live cell 集中移除,而区块中的 output 必须添加到 live cell 集中。由于在 PoW 区块链中经常发生短分叉,当分叉使以前接收的区块无效时,必须回滚该块的 input 和 output 以更改。缓存设计可有助于加速同步,例如,缓存链中的最后 N 个块并删除这些块中使用的 live cell。

2. 构造交易

现在我们可以开始构建交易了。开始之前你需要了解一些背景知识,包括构建witness,交易费的计算和一些小技巧。

构造交易

所有类型都基于 molecule(一个序列化系统)进行序列化,核心结构体是:

table RawTransaction {


  version:        Uint32,


  cell_deps:      CellDepVec,


  header_deps:    Byte32Vec,


  inputs:         CellInputVec,


  outputs:        CellOutputVec,


  outputs_data:   BytesVec,


}

table Transaction {


  raw:            RawTransaction,


  witnesses:      BytesVec,


}

cell_deps 和 input 是一系列指向链上 live cell 的指针。不同之处在于,dep 是一个引用(只读),在交易中使用 input。index 结构为:

struct OutPoint {


  tx_hash:        Byte32,


  index:          Uint32,


}

struct CellDep {


  out_point:      OutPoint,


  dep_type:       byte,


}

tx_hash 和 outputs_index 用于定位 cell。 dep 有一个额外的 dep_type 字段,用于表示 cell 中的 data 是 code 还是 dep_group。(请注意,可以使用 dep_group 功能组合多个 cell 中的代码)

· 用 0 表示 code ,这意味着可以直接使用cell data


· 用 1 表示 dep group,这意味着这个 cell 中的 data 是一个重定向字段,需要解析 n 个 cell(此处不允许递归),dep group data 用 dVector OutPointVec <OutPoint> 列出所有需要的要点。

例如:你可以查看默认的 lock cell,并使用 dep group 功能将 secp256k1 库分成两个 cell 来存储乘法表和代码。区块大小是有限的,如果数据太大不能放入一个块中,它可以存储在单独的 cell 中并在运行时一起加载。

outputs 和 outputs_data 是两个一对一的列表。output 中只有容量和 type/lock script。输出数据放在与索引对应的 outputs_data 中。

header_dep 是过去区块头哈希的列表,这些区块头的头数据可以通过 CKB scripts 访问。

既然已经解释了交易的简单结构,现在让我们探索稍微复杂一点的结构:

script

table Script {


  code_hash:      Byte32,


  hash_type:      byte,


  args:           Bytes,


}

code_hash 和 hash_type 用于指定 lock cell,args 是 lock script 所需的参数。hash_type 有两个值:





· 「data」用 0 表示,code_hash 意味着,lock cell的 data hash


· 「type」用 1 表示,code_hash 意味着 lock cell 的 type script hash

很容易将 hash_type 理解为 code,但是 type 有什么用呢?CKB 的默认 lock 是按 type 索引的。这对合约开发者意味着什么?

你可以看到的第 0 个区块的第二个输出的 type script,这是一个 TypeID script,这意味着,如果您发布的库也绑定这个 typeid script,它将生成一个唯一的 id(code hash)用于索引数据,你可以持续更新这个库的内容而不改变typeid。引用库的合约不会因库更改而无效。这是更新链上库的解决方案。

witness

现在,所有 RawTransacion 字段已经设置完成,来看 witness 字段。这个字段是为了确保交易不能篡改其他交易,并且这个字段还允许包含一些合约可能需要的临时变量。它由一系列的 witness 组成:

table WitnessArgs {


  lock:                   BytesOpt,          // Lock args


  input_type:             BytesOpt,          // Type args for input


  output_type:            BytesOpt,          // Type args for output


}

最初,input 需要一个 witness 来验证,但这是低效的,所以当 VM 验证交易时,使用 script group,将交易脚本中一致的 input/output  cell 合并到一个 script group 中,并且只执行一次验证。

script group 的优点是提高了验证速度,减少了验证的消耗,减小了 witness 的大小。相应的缺点是生成 witness 的规则要复杂得多。

witness 是所有东西的签名:包括 tx_hash、长度和 witness 值、input_group中其他定位的 witness的长度和值。

当 witness 尚未生成时,如何签名 witness?ckb lock 的方案是设置所有 0 的签名进行签名,然后覆盖witness。

因为签名消息的长度只能是 65 个字符,所以我们首先对要签名的内容进行 blake2b-hash,然后对 hash 进行签名。

具体的签名过程和关于如何安排不同脚本组的见证的约定可参考:如何对交易签名 url.cn/5yfunBR

样式和交易费

通过以上过程,我们得到了一个完整的交易结构。此时,如果需要接受最低费用的交易,我们需要对现有的交易做一些回归测试和修改。

如何估算交易费?

交易费是序列化交易(molecule)的大小和执行的指令的消耗的总和。默认的大小单位是 1,000 shannons / KB(千字节)。(Shannon 是 1/100,000,000 CKByte)

然而,矿工可以修改这个默认单元。如果需要查看实时事务费用估计,可以使用 hestimate_fee_rate 通过 RPC 进行查看。

如果需要花费最低的费用,可以不断地调整 input capacity 和 output capacity之间的差异,然后使用二分查找方法重新生成交易,直到满意为止。


关键词: CKB  witness  

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/28208

联系我们

aliyinhang@gmail.com