首页 > 军事 >9188彩票更改银行卡|解析CKB交易结构 | 火星技术帖

9188彩票更改银行卡|解析CKB交易结构 | 火星技术帖

发布时间:2020-01-11 17:49:40
[摘要] 在撰写本文时,对应的 ckb 版本是 v0.25.0,在未来的版本中交易结构还可能有所变动。上图是关于交易结构的概览。此交易销毁了 inputs中的 cell,同时创建了在outputs中的 cell。ckb 主链将交易打包成块。交易 tx1 要比 tx2 早,也比 tx3 早。在所有先前的交易中,一个可用的 cell 会以输出而非输入的形式出现。一个交易只能以可用的 cell 作为输入。交易哈希

9188彩票更改银行卡|解析CKB交易结构 | 火星技术帖

9188彩票更改银行卡,免责声明:本文旨在传递更多市场信息,不构成任何投资建议。文章仅代表作者观点,不代表火星财经官方立场。

小编:记得关注哦

来源:nervosnetwork

作者:ian,nervos 工程师,专注于系统设计和客户端实现,前 hooya game cto,前 groupon 软件工程师。原文链接:https://github.com/nervosnetwork/rfcs/blob/transaction-structure/rfcs/0022-transaction-structure/0022-transaction-structure.md

翻译 & 校对:williams & kelly

这篇文章分成两个部分。第一个部分包含了核心的交易特征,而第二部分介绍一些扩展内容。在撰写本文时,对应的 ckb 版本是 v0.25.0,在未来的版本中交易结构还可能有所变动。您可以点击阅读原文,查看最新版本。

上图是关于交易结构的概览。有别于逐字逐句的解释各个名词,我将会介绍 ckb 转账能够提供的各种特殊结构,以及这些名词在其中的具体意思。

part i:核心特征

价值储存

ckb 采用的是 utxo 模型。一笔交易销毁了一些在先前交易下创建的输出(作为输入),并且创建一些新的输出,我们在 ckb 中将此交易输出称做一个 cell。因此在这里的 cell 和交易输出是可以替换的。

下图显示了在此层中会出现的专有名词。

此交易销毁了 inputs中的 cell,同时创建了在outputs中的 cell。

ckb 主链将交易打包成块。我们可以在区块中利用从零(也就是创世区块)开始递增的非负整数(编号),作为区块编号来关联链上的区块。在区块中的交易也是按照顺序排列的。我们可以说编号较小的区块是较早(old)的区块,如果一个交易在较早的区块上,或者它所在的区块的位置早于其它区块,那么它也会是比较早的交易。在下面的示例中,区块 i 比区块 i+1 早。交易 tx1 要比 tx2 早,也比 tx3 早。

在所有先前的交易中,一个可用(live)的 cell 会以输出而非输入的形式出现。一个被销毁(dead)的 cell 代表它是以输入的形式在其它较早的交易中被使用过。一个交易只能以可用的 cell 作为输入。

我们可以从除了 witnesses之外的所有交易字段计算交易的哈希。关于如何计算交易哈希的更多信息,可以参阅附录 a。

交易哈希是独一无二的。因为一个 cell 总是被一个交易创建出来,而每个新的 cell 在交易输出的数组中都有他自己的位置,所以我们可以通过交易哈希以及输出索引去指向一个 cell。outpoint结构是一种引用类型。交易在输入时会使用outpoint来指向先前被创建的 cell,而非嵌入其中。

cell 将 ckb 代币存储在字段 capacity中。一个交易不能够凭空铸造 capacity,所以交易必将符合以下规则:

在输入中每个 cell 容量的总和要大于等于在输出中每个 cell 容量的总和。

矿工可以收取这两者之间的价差做为手续费。

如果你熟悉比特币,那么就会发现在价值储存层都是相似的,但是比特币缺乏锁定脚本来保护交易输出的所有权。ckb 正好有这个特征,但是在我们探讨这个话题之前,让我们先来谈谈 cell data 和 code locating 层吧,这是任何 ckb 中脚本特征的依据。

cell data

除了能够存储价值通证以外,ckb cell 还能储存任意数据。

字段 outputs_data是输出的并行数组。在outputs中第 i 个 cell 的数据对应的是outputs_data中的第 i 项。

cell 中的capacity不只代表通证的数量,也代表能够存储数据的限制。这也是它如此命名的原因,因为它代表 cell 的存储容量。

capacity 不仅能用于存储数据,它还需要涵盖 cell 中的所有字段,包括 data、lock、type以及capacity本身。

计算占用容量的规范请参考:

https://github.com/nervosnetwork/ckb/wiki/occupied-capacity

交易势必会创建一个占用容量小于(输入) cell 容量的输出 cell。

cell 中有两个字段的类型是 script。ckb-vm 会运行所有输入 cell 中的lock脚本,还会运行所有输入和输出 cell 中的type脚本。

我们区分了代码和脚本这两种术语:

脚本并没有直接包含代码。看看下面的脚本结构。现在我们可以忽略哈希类型的 type以及args字段。

当 ckb-vm 需要运行一个脚本时,它必须要先找到它的代码。字段 code_hash和hash_type就是用来查看代码的。

在 ckb 中,脚本代码会被编译成 risc-v 二进制文件。这个二进制文件是以数据的形式存储在 cell 中的。当 hash_type是数据时,脚本会被定位在一个数据哈希和脚本的code_hash相等的 cell 中。cell 数据哈希,如其名所示,是从 cell 的数据中算出来的(详见附录 a)。在交易中的范围是有限制的,脚本只能从cell_deps中找到一个匹配的 cell。

下图将解释 ckb 如何找到相应的脚本代码。

如果你想使用 ckb 中的脚本,那么应该遵循代码定位的规则:

把你的代码编译成 risc-v 二进制文件。你可以在建构系统 cell 代码的仓库中找到一些案例:

https://github.com/nervosnetwork/ckb-system-scripts

通过一笔交易,创建一个将二进制文件作为数据的 cell,并将交易发到链上。

建构一个脚本结构,其 hash_type是「数据」,code_hash只是

在 cell_deps的 cell 必定是可用的,就像是 inputs一样。但有别于 inputs,一个只在cell_deps中被使用的 cell 不会被认为是被销毁的。

下面两个章节我们将讨论脚本如何在交易中用于锁定 cell,以及如何建立 cell 上的合约。

每个 cell 都有一个锁定脚本。当交易中的 cell 被以输入的形式使用时,锁定脚本必须执行。当脚本只出现在输出时, 则不需要显示在 cell_deps中相应的代码中。交易只有在所有的输入中锁定脚本都正常(执行并)退出时才有效。因为脚本在输入上运行,所以它可以扮演锁的角色来控制谁可以解锁或者销毁 cell,以及花费储存在 cell 中的容量。

以下是一个总是可以正常(执行并)退出的锁脚本的代码示例。如果使用这段代码作为锁脚本,那么任何人都可以销毁这个 cell。

最主流的锁定数字资产的方式是用非对称加密创建的数字签名。

这个签名演算法有两个要求:

在 ckb 中,公钥指纹可以在脚本结构的 args字段中被存储,同时这个签名可以在交易的witnesses字段中被存储。我使用「可以」是因为这只是一个我推荐的方式,并且只用在默认的 secp256k1 锁定脚本中:https://github.com/nervosnetwork/ckb-system-scripts/blob/master/c/secp256k1_blake160_sighash_all.c

脚本代码可以读取交易的任何一部分。所以锁定脚本可以选择不同的协定,例如,储存公钥的信息在 cell 数据中。然而,如果所有锁定脚本都跟随推荐的协定,他就可以简化创建交易的应用程序,像是钱包。

让我们看一下脚本代码是如何被定位和载入的,以及代码如何访问输入、脚本参数(script args)和 witnesses。

首先,请注意 ckb 并不会在逐个输入之间运行锁定脚本。首先它会先按锁定脚本进行分组,并且只运行一次相同的锁定脚本。ckb 会按照这三个步骤运行:脚本分组(script grouping),代码定位(code locating)以及运行。

上图展示了第一步中的两个步骤:

目前 ckb 可以载入脚本代码的二进制文件并通过 entry 函数开始运行代码。脚本可以通过 syscall 的 ckb_load_script进行自读取:

许多 ckb 的 syscall 都被设计为从交易中读取数据。这些 syscall 用有一个指明哪里可以读取数据的参数。例如,载入第一个 witness:

第一个参数控制哪里可以存储读取的数据,以及有多少 byte 已被读取。在接下来的章节中我们先忽略它。

第五个参数是数据源。ckb_source_input 代表从交易输入中读取,第四个参数 0 则是输入数组的索引。ckb_source_input 也用于读取witnesses。

记住当我们通过锁脚本将输入进行分组时,我们已经保存了输入的索引。这个信息用于为分组创建虚拟的 witness 和输入数组。这段代码可以通过一个特殊的数据源 ckb_source_group_input,利用虚拟数组中的索引去读取输入或 witness。读取 witness 时,通过 ckb_source_group_input只读取拥有相同位置并具有特定输入的 witness。

所有读取和输入相关数据的 syscall,都可以使用 ckb_source_group_input 以及在虚拟输入数组中的索引,例如 ckb_load_cell_* 的 syscall 系列。

类型脚本和锁定脚本很相似,但有两点不同:

虽然我们只能在 cell 中维持一种脚本,但我们不会想要在一个单一的脚本中扰乱(脚本)不同的职责。

锁定脚本只为输出执行,所以他的首要任务是保护 cell。只有所有者可以以输入的形式使用 cell,以及花费储存于其中的通证。

类型脚本的目的是在 cell 上建立合约。当你得到一个特殊类型的合约时,你可以确定 cell 已经在特定代码中通过验证。同时这个代码也会在 cell 被销毁时被执行。类型脚本的典型情况是用户自定义的 token,这种类型脚本必须在输出上运行,所以通证的发行必须被授权。

在输入上运行类型脚本对合约而言非常重要;例如一个让用户可以在线下抵押 ckb 来租用资产的合约,如果这个类型脚本不在输入上运行,用户可以在没有权限的情况下从合约中取回 ckb,只需销毁这个 cell 并将容量转移到一个没有类型脚本的新 cell 上即可。

这个运行类型脚本的步骤和锁定脚本也很相似,除了:

像 ckb_source_group_input,有一个特殊的数据源ckb_source_group_output可以将索引用于脚本组的虚拟输出数组中。

part i 交易结构概述

part ii:扩展

在第一部分,我介绍了交易提供的核心特征。在这个部分,我将介绍一些 ckb 不需要他们也能运行的延伸套件的特征,但这些套件可以使 cell 模型变的更好。

下图是在这个部分会出现的新字段(标有黄色底)的总览。

dep group 是一个捆绑许多 cell 作为其成员的 cell。当一个 dep group 的 cell 在 cell_deps中被使用时,它的效果和添加全部的成员到cell_deps中是一样的。

dep group 在 cell 数据中存储了一系列的 outpoint清单。每一个outpoint都指向其中一个群体的成员。

celldep结构中有个叫dep_type的字段,可以用于区分直接提供代码的普通 cell,和在cell_deps中扩展至其成员的 dep group。

dep group 会在定位和运行节点之前被扩展,只有被扩展的 cell_deps才是可见的。

在 v0.19.0 版中,锁定脚本 secp256k1 被分成 code cell 和 data cell。code cell 通过 cell_deps载入 data cell。所以如果一个交易要解锁一个被 secp256k1 锁定的 cell,那么他一定要在cell_deps加上两个 cell。在 dep group 中,交易就只需要 dep group 即可。

我们分离 secp256k1 cell 有两个原因。

在第一部分的锁定脚本中,我描述了一个脚本如何通过 cell 的数据哈希来定位它的代码。一旦一个 cell 被创建,那么相关联的脚本代码就不会被改变,因为要找到一个有相同的哈希的另一段代码是不可能的。

脚本有另一个 hash_type 的选项,type。

当脚本使用了 hash type 的 type,它会匹配相等于 code_hash的类型脚本哈希的 cell。类型脚本的哈希是从 cell 的type字段中计算出来的(详见附录 a)。

现在,如果 cell 用一个通过类型脚本哈希来定位代码的脚本,并通过创建一个具有相同类型脚本的新 cell,那么我们就可以升级代码了。新的 cell 已经有更新的脚本,在 dep_cells中增加了新 cell 的交易将会使用这个新的版本。

然而,这只解决了一个问题。如果一个作恶者创建一个拥有同种类型脚本的 cell,但使用伪造的代码作为数据,那么这还是不安全的。作恶者可以通过使用假的 cell 作为 dep 来绕开脚本验证。下一章将描述解决第二个问题的脚本。

因为被类型脚本哈希引用的代码是可以被改变的,所以你必须信任脚本作者使用的这种类型脚本。虽然使用哪个版本取决于哪一个 cell 在 dep_cells的交易中被添加。用户总是可以在签署交易之前检查代码。但是,如果脚本用于解锁 cell,那么签名检查甚至是可以被略过的。

我们选择 cell 类型脚本哈希的理由是用来支持可更新的脚本。如果作恶者想要创建具有特化的形态脚本的 cell,那么交易必须被类型脚本的代码验证。

type id 就是这种类型的类型脚本。如其名所示,他确保了类型脚本的独特性。

这个特征包括了多种类型脚本,所以我会用不同专有名词去区分他们:

从 part i 的类型脚本中我们知道,类型脚本会先将输入与输出聚集。换句话说,如果一个类型脚本是 type id,那么在同个群组的输入与输出都有同样的 type id。

type id 的代码在验证的就是在任何代码中,至少都会有一组输出和一组输入。根据输入与输出的数量,一个交易允许有多种 type id 群组,type id 群组被分成以下三种不同的类型:

上图的交易中有三种交易 id 群组

在 type id creation group 中,在 args中的唯一参数,是在群组中这个交易的第一个cellinput结构的哈希,还有 cell 的输出索引。例如,在群组 g3 中,id3 是一个在tx.inputs[0]和 0(cell3 在 tx.outputs 的索引)上的哈希。

这里有两种可以透过特定的 type id 来创建新 cell 的方法:

我们假设方法 2 是唯一一个创建相等于既有 type id 的新 cell 的方法。这个方法需要原有的所有者的授权。

type id 的代码只能通过 ckb-vm 的代码实行,但我们会选择在 ckb 的节点上以一个特殊的系统脚本来实行它,因为如果我们以后想升级 type id 的代码,就必须要通过一个递归依赖的类型脚本代码,来将自己作为类型脚本。

type id 的代码 cell 使用了一个特殊的类型脚本哈希,也就是文本 type_id。

header deps 可以让脚本来读取区块头。这个功能有一定的限制以确保交易被确定。

我们只会在所有交易中的脚本已经有确定的结果时,才会说一笔交易已经确定了。

header deps 允许脚本去读取其哈希已经列在 header_deps中的区块头。还有另一个先决条件,是交易只能在所有列在header_deps的区块都已经在链上时(叔块除外),才可以被添加到链上。

有两种方式可以利用 ckb_load_header系统调用来载入脚本中的区块头:

第二种载入区块头的方法有另一个好处是,脚本会知道 cell 位于被载入的区块中。dao 取出交易借此来获取存储容量的区块号。

下面是上图中 loading header 的一些例子。

字段 since可以避免交易在特定时间前被挖出。详见 since rfc。https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0017-tx-valid-since/0017-tx-valid-since.md

字段 version是预留在未来使用的。在当前版本中,它必须等于0。

在此系统中有两个特殊的交易。

第一个是 cellbase,这是每个区块中的第一个交易。cellbase 中只有一个仿真的输入。在这个仿真的交易中,previous_outpoint不会关联人其他的 cell,而是会设置一个特殊值。since必须设置为区块编号。

cellbase 的输出是链上给早区块的奖励和交易费。

cellbase 是特殊的,因为他的输出容量并不来自于输出。

另一个特殊的交易是 dao 的取款交易。它也有部分的输出容量并非来自于输入。这部分是在 dao 中锁定 cell 所获得的收益。ckb 会通过检查是否有使用 dao 作为类型脚本的输入来识别 dao 的取款交易。

附录a:计算多种哈希

加密原语

blake256

ckb 使用 blake2b 作为默认的哈希演算法。我们用 blake256 来表示一个函数,用个人的「ckb-default-hash」来获取 blake2b 哈希的前 256 位。

交易哈希是 blake256(tx_hash_digest(tx)),其中tx_hash_digest是将交易中的所有字段(不包括 witnesses)序列化为二进制的方法。序列化规范还没有进行最终的确定,现在,您可以通过 rpc_compute_transaction_hash获取交易哈希。

cell 的数据哈希只是 blake256(data)。

脚本哈希是 blake256(serialize(script)),serialize将脚本结构转换成二进制块。序列化规范还没有进行最终的确定,现在你可以通过 rpc_compute_script_hash来获取脚本哈希。



焦点

推荐

最新

精选

© Copyright 2018-2019 broganganley.com 庄坞汀潭门户网站 Inc. All Rights Reserved.