对于开发者来说,cell 编程模型无疑是 Nervos CKB 中最有趣的部分。在这里,我们先对 cell 模型作一个简短的描述:

  • Cell 是通用版的 UTXO
  • 一个 cell 是一个包含任意数据和可定制脚本的 UTXO
  • 当一笔交易尝试销毁或创建一个 cell,cell 中的脚本将被加载并执行,在执行脚本中返回任何错误,都将导致交易失败。

Cell 模型不同于账户模型:

  • 验证而非计算
  • 将数据存储在单独的 cell 中,而不是将数据存储在一个账户中

当你比较两个模型时,你会发现还有许多其他的不同之处,如果你对该话题感兴趣,可以在 talk.nervos.org 上找到更多关于 cell 模型 VS 账户模型的讨论。

Cell 模型中缺失的那一块

UTXO 是一个非常棒的模型,cell 模型继承了它的灵活性。我们(通过 cell 模型)可以发行 UDT(用户自定义代币,类似于 ERC20),存储链上资产,玩石头剪刀布游戏,或者与比特币实现原子交换等。Cell 模型可以实现很多人们最初认为不可能实现的事情。

不幸的是,有一些合约确实很难在 cell 模型上实现:

  • 投票
  • 众筹
  • 去中心化定价的虚拟机
  • ...

这些难题合约有一个共性,那就是需要共享状态。

在一个 UTXO 类的模型内,状态是自然分离的。

在 CKB 中,用户可以在独立的 cell 中进行投票。这样我们就需要一个链下的角色收集这些投票的 cells 并计算结果。

1.jpg

当我们只想「看到」结果时,它实现的非常好。但是我们不能在另一个合约中使用投票结果,比如一个基于投票的 DAO 合约。我们很难在一个链上合约中验证统计后的结果。所以我们必须证明每一个投票 cell 的存在,交易时就需要引用每一个投票的 cell,这可能非常昂贵。

2.jpg

再举个例子,我们来看一个众筹合约:

一个 cell 中有所有的众筹代币,用户可以支付 CKB 来获得相应数量的代币。

问题在于,当我们分割这个 cell,众筹 cell 的 outpoint 就被改变了;其他用户必须等到下一个区块才能看到新的 outpoint。所以在一个区块时间内,只有一个用户可以参与众筹,这是不可接受的。

与投票的例子类似,一个典型的解决方案就是引入一个链下角色,用户在个人的 cells 中发出众筹请求;然后链下角色需要收集这些 cells 并将结果放在一个 cell 中。

我们可以看到,由于在 cell 模型中,状态是自然分离的,因此我们必须依赖某个链下角色来收集状态。

这样的方法是可行的,但是仍然会有一些问题:

  • 如何有效地证明收集的结果
  • 如何保证在引入了链下角色后的去中心化
  • 用户如何与链下角色进行交互

当然,解决这些问题并不难;我们可以通过支付链下角色一些费用来激励他们;使用某种挑战机或者零知识证明(zk proof)来验证收集的结果;定义一些协议来规定和链下角色的交互。我们总能解决这些问题。

等等,我想要的只是一个投票合约。我为什么需要整这么多(复杂的)东西?

一个合约管理一切

可不是嘛!我们可不想为每一个合约都整这么多东西,我们只需要构建一次:

Godwoken 是一个建立在 CKB 上的基于账户模型的编程层,目标就是管理一切状态共享的合约。

Godwoken 由以下几个部分组成:

  • 主合约 —— 一个维护所有账户、所有区块的全局状态的类型脚本(layer 1.5 层)
  • 挑战合约 —— 一个处理挑战需求的类型脚本
  • 聚合器 —— 一个收集 layer 1.5 层交易的链下程序,并定期向主合约提交 layer 1.5 层的区块。
  • 验证器 —— 一个持续监控两个合约的链下程序。验证器在一个无效区块被提交时会发送一个挑战请求,在一个错误的挑战请求被提交时会发送一个无效的挑战请求。

你可能发现,这听起来像是最近非常流行的 rollup 的解决方法。是的,没错。但是我们关注的是聚合的问题,而非可扩展性的问题。Godwoken 提供了 CKB 基于账户模型的编程能力来解决聚合问题。

一些人将 Rollup 称之为 layer 1.5 层;也有一些人认为它是 layer 2 层;还有人认为它是 layer 1 层(根据信任级别)。本文将 Godwoken 称为 layer 1.5 层,以便将其与 layer 1 层的 CKB 区分开。

Godwoken 使用和原生 CKB 合约相同的技术栈。唯一的区别在于 Godwoken 合约是基于账户模型的;它将验证账户的状态而不是 cells 的状态。账户状态和 layer 1 层的 cells 之间的映射关系是由 Godwoken 的主合约处理的,对于 layer 1.5 层的合约来说,是透明的。

对于需要创建一个投票合约的开发者来说,只需要使用脚本简单地创建一个账户,这个脚本就会验证输入的数据和账户状态。

// pseudo code
fn verify_voting(i, votes) -> bool {
    state[i] += votes;
    merkle_root(state) == output_account_root
}

从上面的伪代码中,我们可以看到验证模型类似于 layer 1 层。

Godwoken 主合约使用了一个稀疏 merkle tree来存储所有的账户和账户状态。

因此,如果我们想要在 layer 1.5 层合约之间使用一个状态,我们可以简单的为这个状态生成一个 merkle proof,并在合约中验证 merkle proof。

如果我们想在 layer 1 层的合约中使用一个 layer 1.5 层的状态,我们可以在交易的 cell_deps 字段中引用 Godwoken 主合约 cell,并从 cell 中读取 Godwoken 中的全局状态来获得 merkle root,这样就可以验证状态和 merkle proof 了。

通过创建一个抽象的账户层,我们可以在 CKB 上通过最小的成本创建一个状态共享的合约。

在后续的文章中,我们将会讨论 Godwoken 中的详细信息,我们如何维护 layer 1.5 层的账户和合约,以及基于账户模型的合约是如何工作的。


NervosNetwork
241 声望870 粉丝

这是 Nervos 唯一官方 SF 号。在这里,你能了解到关于 Nervos 的一切,enjoy.