Nervos-CKB 智能合约的产品性缺陷

链下结算,链上验证。链上合约难于实现复杂的功能,比如参数存储。

这一特点导致了 Nervos-CKB 上的 DApp 容易被人挑刺,不够去中心化。

为什么要在链上部署智能合约?

大部分的产品就是为了使用区块链的去中心化属性,使得所部署的应用是真正的DApp,无法被任何人控制,验证 与 结算,都发生在链上,只要开源的代码被阅读后没发现后门 bug,那么它的DApp属性是可靠的。至少会让知道的人心里,不会产生去中心化质疑。

如果在 CKB 上实现一个具备惩罚功能的 DApp,那么惩罚这部分功能是不能在合约实现的,因为惩罚的证据,依赖链下提供,而不能一开始就使用存储在链上的数据来综合分析。链下提供的数据,如果数据源都有问题,验证的过程公平有何意义?

要理解 CKB 的链上验证,可以理解这个例子:将参数 1 和 1 传输到链上的合约,判断是不是 1+1 = 2。正确返回 true,否则返回 false。

明显地,依赖这一验证特性,是可以实现一些更加复杂且有趣的功能的,比如验证某条其它链的签名是否正确。你使用 CKB 钱包,在 CKB 区块链上给一个 ETH 的钱包地址A转账,这笔转账存放在链上 cell 中,lock_script 明确了当有人提供某条签名信息,且被 CKB 某个功能合约以 ETH 的验签方式校验与地址A 相等,那么就证明签名信息提供者是有地址A对应的私钥的,他可以消费这个cell,即 output。

如此地,实现了弱意义上的 CKB 链上的跨链收款功能,注意此时所有事情依然是发送在 CKB 公链上,并没有说是在 ETH 链。

然而,这种跨链收款功能,在 ETH 上也是可以实现的。ETH 本身的智能合约功能已经很强大,在已支持的算法之内,实现一个链上验签的功能,是绰绰有余的。链下输入: {RawMsg}, {SignMsg},链上获取:{TargetAddress},执行验签,结束。

这样一对比,CKB 智能合约的优点貌似不在上面那点。目前我能想到的:

  1. 就是它的智能合约虚拟机CKB-VM 支持多语言编程了;
  2. 可以实现可更新合约合约部署后无法更新一直是区块链开发者的心头之痛。

上面两点确实是方便了开发者,但无法弥补产品多样性上的缺陷。

ETH 本身的智能合约功能已经很强大,实现一个链上验签的功能,是搓搓有余的。链下输入: {RawMsg}, {SignMsg},链上获取:{TargetAddress},执行验签,结束。

关于这个论点,想展开说一下。

在现代软件工程里,会有两件截然不同的事情:theoretically possible,与 practically feasible。理论上可行的事情,在现实角度中,并不一定能完全实现。因为落实到软件层面,有方方面面的取舍,比如硬件是否支持,功耗如何,运行时的效率如何,与其他软件是否良好的配合工作。所以我们需要 researcher 和 engineer 的完美配合,才能做出一个突破性的进展。

最近其实有一个很好的例子:有一个叫 jslinux: JSLinux 的项目,可以利用 JS 引擎,在浏览器里运行一个 x86 的虚拟机,可以跑起来很多实际的 OS。这种项目其实一直有,很多 demo 出来时,也非常惊艳。这里其实可以思考一个问题:苹果决定从 x86 迁移到 ARM 时,他们为什么不依赖于这样的项目呢?理论上运行在 m1 上的 Safari 也可以通过 jslinux 支持任意 x86 程序,为什么苹果还要费尽心思做 Rosetta 2,跟每一个 vendor 沟通把应用原生移植到 m1 上呢?

这里的原因就是:虽然理论上 jslinux 能用来支持 x86,但是实际的运行效率很差,导致于这并不是一个可以依赖的方向。对于 EVM 也是一样,EVM 本身的 opcode 设计与现在体系结构有着天壤之别,这样的结果就是很难保证一个 Solidity 程序的运行效率(包括运行时间与 cycle 消耗)与原生程序媲美的。其实这里同样有例子的: EVM 上的 blake2b 支持在 2016 年就已经有人提出,但是直到 3 年后的 2019 年,才通过 Istanbul hardfork 引入 Ethereum: EIPs/EIPS/eip-152.md at master · ethereum/EIPs · GitHub 。同时可以看到实现的方式是直接提供了一个 precompiled contract,并不是用 Solidity 直接实现 blake2b。其实这里就足够引起反思了:以太坊社区有这么多持续的资源投入,如果真的能在 EVM 上通过合约的形式提供一个 blake2b 方法,为什么还要等到三年后靠 hardfork 来实现呢?原因就是 Solidity 原生的实现 cycle 消耗过于大,以至于用 Solidity 做这样的一个合约并不实际。考虑一个简单的 hash function,已经很难在 EVM 里原生提供,验签的实现,难度会大得多。其实社区里也已经有一些想在 EVM 上原生提供椭圆曲线算法的支持: GitHub - witnet/elliptic-curve-solidity: Elliptic Curve arithmetic operations written in Solidity 但是大部分都不了了之,一个很重要的原因,就是 cycle 消耗实在是过大。这就是一个 theoretically possible but practically infeasible 的例子。

如果有兴趣跟进一下 ETH 2.0 的话,ETH 2.0 的 eWASM 也选择了一条有趣的道路:他们也并没有尝试原生直接在 WASM 上实现加密算法,而是选择了 5 个加密算法中使用的核心原语,通过 precompiled contract 的方式,提供这 5 个原语,再通过这 5 个原语,构建出他们想要的一些加密算法,其实可以想一想这里带来的暗示。即使是切换到 WASM 上,以太坊仍不觉得他们可以支持合约层面让用户提供任意的算法。同时我们可以再看看另一个使用 WASM 这个所谓的“灵活”虚拟机的选择:Polkadot 也是选择支持 ed25519 和 secp256k1,并没有赋予用户用自定义加密算法的能力。

其实在我个人看来,CKB 上智能合约带来的最本质的优点,并不是多语言支持,也不是和约部署后可以更新,甚至不是我们支持自定义的加密算法。我们想通过 CKB VM 来提供的,是在大家的原生 CPU 上,尽可能薄的一层抽象,通过这一层抽象带来区块链需要的确定性与安全性。同时投入相当多的精力,让这一层抽象做到足够透明,赋予 CKB VM 与原生 CPU 一样的能力。任何你能在原生 CPU 上所做的操作与优化,我们都希望在 CKB VM 上同样可以提供。这里的重点并不是 CKB VM 给智能合约带来了什么,而是 CKB VM 没有带走的可能性。一块原生 CPU 上全部的潜能,都留给合约开发者,才是我们想要实现的愿景。

今天读 hacker news 时看到了一个 quote,非常适合这里做结尾:

All problems in computer science can be solved by another level of indirection.
All performance problems can be solved by removing a layer of indirection.

7 Likes

可惜的是,CKB 把这层放在了链下。

对于链来说,这是它的特性,无可厚非。

不同的特性也导致了不同的结果,从一个 DApp 开发者的角度来看,目前在 CKB 上开发DApp 的局限性是很明显的了。从一个性能信仰者来看,真是极好的。

无论如何,有舍则必有得。

可以放在链下,和一定要放在链下是有区别的。CKB 其实选择的是前者,而不是后者。

我们设计是一个重要的考量就是对于千奇百怪的应用,并没有一个 one-size-fit-all 的解决方案。如果一个应用想选择最大程度的去中心化,在链上做完全的验证计算,其实在 CKB 模式下,同样可以实现。具体可以期待年底我们会发布的 polyjuice,这个正是以太坊,在链上做计算的模式。

CKB 推荐链下计算,链上验证,是因为对于很多应用,并不是所有的计算步骤,都一定要放在链上的。举个例子,Alice 在 CKB 中有 10 个 UDT cell,现在 Alice 想给 Bob 转 100 UDT,Alice 其实并不关心这 10 个 UDT cell 中,实际选了哪几个来给 Bob 支付,Alice 只关心我最后给 Bob 支付了 100 个 UDT,剩余的找零给我自己。进一步再举个例子,对于一个 UDT 发行者来说,他并不关心用户在使用 UDT 时,谁给谁做了多少转账,他只关心只有他自己,可以做增发操作,其他任何用户都不能做增发操作。对于这些场景来讲,可以精确的区分出链上要真正保证的逻辑,与链下并不那么重要的逻辑。从性能的角度,我们完全可以做拆分,只在链上跑真正关系安全,需要验证的逻辑。这是 CKB 提倡 “链上验证,链下计算” 的原因,但是这并不代表 CKB 上一定要这么做。觉得 CKB 这样会带来局限性的话,我会建议可以再深入的了解一下 CKB。

polyjuice 的介绍文档目前有公开的吗

多复杂的功能才算复杂,这个这个够复杂吗?第二个你能不能在 Ethereum 上写一个?"参数存储"难于实现从何谈起,你能给一个具体的例子吗?

看来你也同意验证和结算在链上就够了 :slight_smile:

如何验证链外数据的“正确性”是 oracle 问题,这和链下计算链上验证的架构是不同的东西,不能把两者混为一谈。无论是 Ethereum 还是 CKB, 都需要去解决 Oracle 的问题。无论是哪种 Oracle 方案,理论上在 Ethereum/CKB 上都能实现,实际上由于架构提供的编程能力不同,CKB 能做的事情更多。

事实上将计算和验证分为两层所能提供的计算能力,远远超过在 L1 所能达到的 Bounded Turing-complete.

这种“也是可以实现的”有点想当然了。注意 CKB 上能实现的不是你描述的简单的跨链收款智能合约,而是可以重用 Ethereum 基础设施例如 Metamask,不需要用户切换账户和私钥的能力。要做到这一点,不仅有上面 xuejie 指出的问题,Ethereum 账户模型不够抽象是另外一个困难。最简单的理解就是,Ethereum 上所有的交易都必须用 secp256k1 签名,与单一密码学算法是绑定的。如果 Ethereum 真的可以做到 CKB 能做的,现在被提上议事日程Account Abstraction 就完全是多余的工作。我不知道 AA 要真正部署还要多久,我知道的是 AA 想要做到的事情在 CKB 上今天就可以做到。

5 Likes

emmm,给你看个 demo:https://pay.lay2.dev,求 ETH 链上版本(你把合约写好,我们负责前端对接,做好了我们一起去找 V 领年度最佳奖)。

2 Likes

ETH虚拟机的问题学姐已经写了很多了,我提一下链下结算/链上验证和链上计算的一个差异,前者相当于定义一个函数 fn,校验 input 经过这个函数之后,和指定的 output 一致:

output == fn(input)

链下结算不再受限于这个函数的具体执行过程

举个简单例子,链上验证:你给我 N CKB,我给你 N * 3 SUDT,那么我们可以在链上写成: output == input * 3

而链下结算的函数,不会受限于这个函数的具体执行过程,你可以自由选择 N * 3 还是 N + N + N 甚至 N << 1 + N

4 Likes

我只想知道,如何在合约运行过程中存储数据。
如uniswap这样的DEX,都是需要运行中存储状态的,需要如何做?

不好意思,目前,你,是做不到的

你对链上验证有个误解。事实上链下计算、链上验证和去中心化完全无关。

简单解释下,我们假设有个智能合约 F: (input) -> output

在以太坊中这个 input 和 output 都是 account state tree。

在 CKB 上验证时可以采用一个最简单的方式,先计算 F(input), 再对比 F(input) == output。这样我们验证的结果和以太坊是完全一致的。因此 CKB 链上验证的计算能力至少等于链上计算。


事实上你的结论和事实是相反的,一般开发者用 CKB 开发会感到比以太坊更麻烦些(尤其是只接触过以太坊开发的开发者)。因为需要将验证和计算分离,相对的好处是底层的交易可以并行验证,而且开发者可以利用不同于计算的验证逻辑来大幅度的节省链上验证花销的 cycles。


这里只讨论验证模型,CKB-VM 的扩展能力是额外的优势。做合约模型上的对比时先不讨论
如果对验证模型不清楚,建议了解下 Bitcoin,对 Bitcoin 验证比较了解的话会更容易了解 CKB 为什么这么做。

没有明白你指的产品性缺陷是什么

1 Like

@jjy 参考这个产品的可能性