比特币扩容方案:RGB

本文意图深入探讨各种技术概念并探索协议设计,同时我们欢迎读者主动参与为我们的内容提供反馈和观点。由于每位作者的专业兴趣点不同,每篇文章可能不会涵盖各个协议的全部细节,因此我们特别期待并欢迎读者的详细探讨并补充我们的内容。

请注意,虽然我们在准备这篇文章时已经尽力保证内容的准确性,但是因为诸多可能的因素,其中可能仍存在错误或不准确之处,我们非常乐于相关错漏接受各位的指正。这样的共享与学习过程可以帮助我们共同进步并推动技术的发展。

您可以访问 Exploring Bitcoin Scaling Solutions: RGBExploring Bitcoin Scaling Solutions: RGB ++ 获取英文版内容

我们将推出一系列关于比特币扩容解决方案的文章,这些文章将致力于简化复杂技术,并详细解释它们是如何使区块链运行得更快、更高效的。

它们将涵盖 Lightning Network、rollups 、sidechains 等多种技术,使您对每种技术的工作原理、在提速和降低成本方面的优势,以及如何进行扩容有更清楚的认识。本文将重点探讨基于 RGB 的比特币扩容方案。

RGB

概述与愿景

RGB 是一个建立在比特币区块链之上的智能合约平台,它通过客户端验证范式,实现了在比特币网络上运行智能合约的能力。RGB 的核心特点包括数据的链下存储、以及使用比特币脚本作为安全和所有权控制系统。该平台旨在提供一个超越传统以太坊类智能合约系统的解决方案,通过分离智能合约的发行者、所有者和状态演变的概念,为去中心化应用和数字资产管理提供了一个更加分层、可扩展、私密和安全的环境。

协议速览

我们在这部分会尽量避免在内容中包括协议的复杂的细节。使用尽量简单化的方式解释协议的工作方式。

基础工作方式

UTXO 全称为 Unspent Transaction Output,为比特币中的基础概念。不熟悉的朋友可以先阅读 wiki 条目。我们不在这里对 UTXO 进行详细解释。

RGB 作为智能合约平台,其整体的设计均与 UTXO 绑定。我们先以一个例子说明其工作原理。假定通过 RGB 发行了某种资产 100 枚,那么该资产会与某个 UTXO 绑定。有权花费该 UTXO 的用户能在花费 UTXO 时,才可以在交易中指定 RGB 资产如何转移(RGB 称之为所有权)。如下图

UTXO1 上绑定了 100 枚 RGB 资产。将 UTXO1 花费后,得到了 UTXO2 与 UTXO3(黑色箭头)。交易中同时包含了 RGB 资产如何转移的指示(红色箭头),将 100 分为了 30 与 70 两部分,前者与 UTXO3 绑定,后者与 UTXO4 绑定。

RGB 合约旨在实现通用的智能合约逻辑,因此合约逻辑与 UTXO 的转移是解耦的。我们可以看到,RGB 资产的转移不必与 UTXO 的花费绑定。

而因为 UTXO 只能被花费一次,所以与该 UTXO 绑定的 RGB 资产也只能被转移一次。如果比特币安全,UTXO 无法被双花,RGB 资产也无法被双花,从而保障了协议安全。

RGB 将这一设计称作一次性封条

如何在链上指示 RGB 操作

为了隐私性与智能合约的表达能力,RGB 没有简单地将明文写在比特币脚本中,而是在链上提交一个与操作相关的哈希值(术语称为“承诺”)。RGB 的协议参与方可以在链下验证链上的哈希值是否与该操作匹配。

我们在这里不讨论承诺是如何提交的。但就最终结论而言,链上总会出现一个相关的承诺值。

这种方式保障了 RGB 交易的隐私性,对于非 RGB 协议的参与方而言,它所能观察到的链上行为如下图:

可以看到,由于无法通过哈希值反过来获得原始的 RGB 操作,非参与方甚至无法得知 RGB 资产的存在。

同时由于链上仅包含了操作的哈希值,因此提交到链上的数据大小始终是恒定的,这使得链下的复杂操作成为了可能,同时也仅有常数级别的费用开销。

客户端验证

RGB 提交到链上的仅包含哈希,交易时验证哈希是否有效则是协议安全的基础。RGB 通过称之为“客户端验证(client-side validation,CSV)”的方式保证交易的有效性。RGB 相关的数据全部在链下由交易方保存,在交易时由交易方自行决定数据如何传递,在交易数据传递后交易方可以根据交易数据在本地客户端验证交易的有效性。

我们用示意图表明客户端验证做了什么(注意:为了理解的方便,该图没有精确地表示出 RGB 协议的所有细节)。

我们仍然使用上文的例子说明。Bob 是 UTXO1 的所有者,他需要将 70 枚 RGB 资产转移给 UTXO4 的持有者 Alice。

  • 在交易的过程中,产生了新增数据 data2,Alice 因此接收了 Bob 发送的所有数据 data0,data1 与 data2。
  • Alice 阅读获得的 RGB 数据 data0,data1,data2,得知了 RGB 资产的状态变化(蓝色虚线)。
  • Alice 会验证链上是否存在 RGB 数据(data2,data1…)的哈希(绿色虚线),以及相应交易是否被比特币确认。从而确保 RGB 数据的有效性。Alice 需要验证所有的 RGB 数据是否有效,即从 RGB 合约被创建,也就是 data0 对应的链上交易(图中未画出),到最新的交易。Bob 在 Alice 接收到资产时也进行过相同的验证。

客户端保障隐私性的方式非常极端。我们可以想见,即时是 RGB 合约的部署者(持有 data0),如果无法拿到 data2,他也无法知晓 Bob 与 Alice 间发生的这笔交易。

信任与安全

由于“安全”是一个比较重要的问题。在这里我们会从“信任”的角度分析用户需要信任的协议与应用中的各环,并给出分析。

如没有另外提及,我们认为协议中使用的经典密码算法是可信的(不考虑量子计算机)。

  • 用户需要信任比特币网络。
    • 比特币上的交易不会被双花。否则 RGB 资产也会被双花。
    • 比特币交易不会被审查。否则RGB交易可能无法上链。
  • 用户需要信任链下 RGB 的执行引擎(包括虚拟机与智能合约设计)。用户的 RGB 资产与 UTXO 绑定,但在链上仅有 RGB 操作的哈希提交,而没有与比特币脚本的交互,因此链上无法以任何方式确认 RGB 交易是否有效。而如果链下执行引擎出错(如执行引擎的代码错误,或合约的逻辑错误),将有可能导致链下产生无效的状态,或提交的哈希无法与 RGB 状态对应。这种情况下,一旦 UTXO 交易被花费,用户手中的状态无法被正确验证,将会导致资产丢失。

我们可以看到,RGB 协议并没有为用户引入过多的信任负担。用户可以几乎自行验证协议的每一个部分,确保协议在正确运作。但这同样也为用户的使用带来了负担。

杂项

虚拟机

RGB 实现了一个图灵完备的虚拟机 AluVM

可扩展性

每笔 RGB 交易与一笔比特币交易绑定,这限制了 RGB 的处理能力。但 Bifrost协议 也被提出,作为比特币闪电网络的扩展,使得 RGB 支付可以在闪电网络上进行。

TVL

由于 RGB 的隐私性,TVL 尚无可以统计的方法。

RGB 小结:优势与劣势

优势:

  • 隐私性:RGB 在链上仅提交哈希,以客户端验证的方式保证了交易的隐私性。除交易方外其他人无法获知交易详情。
  • (复杂合约的)链上交易费用:在链上仅提交交易哈希,链上所需的交易费用与合约逻辑无关。
  • 信任:协议中的额外信任需求极少,用户几乎可以验证协议中的一切。

劣势:

  • 数据保存:用户交易时需要保存有 RGB 数据,数据丢失等同于资产丢失。
  • 交易时需要多方交互。当某一方离线时,交易变得不可能。这也使得客户端的设计变得困难。
  • 验证成本高:每次交易时,客户端都要验证状态,当交易次数增加时,验证开销可能会变得极为庞大。此外这一验证过程也需要验证比特币主网数据,因此也会增加本地的存储开销(本地保存)或一定的网络开销(与 RPC 节点或相关服务方交互)。

RGB 的这些劣势加大了客户端构建的难度,也使得用户的学习门槛变得极高。

更多细节

这部分我们会讨论协议中重要性处于次位的内容。这部分会包含一些技术细节与讨论,供感兴趣的读者参考。

状态机复制与链下计算引擎

区块链的其中一个核心议题仍然是如何保证各个节点存储的账本一致。我们在这里先介绍比特币,甚至所有区块链都在应用的一个范式:通过状态机复制实现容错服务。这个范式被广泛地应用于分布式服务的构建,用于解决多个副本的一致性问题。到实际的应用场景中则例如分布式数据库,再如这里讨论的区块链,都在应用这套范式。而它的实现方式也非常简明:

  1. 所有副本以某一状态作为初始态。
  2. 状态转换:
    1. 在接收新的输入前,副本状态保持不变。
    2. 在状态 s 接收到输入 i 后,副本会转换为新状态 s’。且这一状态转换是确定性的:给定相同的副本状态 s 与输入 i,副本的新状态 s’ 是一致的。
  3. 通过这样的方法,只要副本具有相同的初始状态 s0,并保证每个副本的输入 i0,i1,i2…in 相同且顺序一致,那么最终所有副本的状态都能保持一致。

我们借用比特币的语境,重新阐释一下上面的解释:

这里我们使用“状态”代指比特币中的所有 UTXO

  1. 所有比特币客户端以创世区块作为初始状态。
  2. 状态转换:
    1. 在接收到新的交易(区块)前,比特币账本的状态保持不变。
    2. 接收到新交易(区块)后,账本会转换为新状态。且这一转换过程是确定性的,给定相同的账本状态与相同的交易(区块),新的账本状态是一致的。
  3. 通过这样的方法,只要所有比特币客户端都能以创世区块作为初始态,并以相同的顺序处理相同的交易(区块)i0,i1,i2…in,那么最终所有客户端所持有的账本状态是一致的。

而这里最核心的问题在于如何保证所有节点都能决定一批相同顺序的交易(区块),换言之,即共识,这也是区块链抵御双花攻击的基础。

RGB 这种链下计算引擎做的事情在于利用比特币的共识来维护交易顺序,从而避免双花。而状态与状态转换都在链下维护。这种范式的优点在于将执行层与共识层进行了解耦,使得不升级区块链便能扩展功能。但这也导致共识层在执行时不会进行逻辑验证,当链上提交的数据与链下的交易引擎逻辑不符时,交易也并不会被拒绝,因此容易导致丢失资产等情况。也有这样的观点:RGB 是“寄生”在了比特币上。RGB 同样也可以运行在其他 UTXO-based 的区块链上。

铭文、符文等协议也是类似的范式。它们甚至面临一个更严重的问题,那就是生态初期并无完全一致的标准,链下执行引擎(indexer)存在不同实现,执行结果难以验证。

承诺方案

承诺(commitment)方案是密码学中的概念。它允许某人在选择某值后将其隐藏起来时进行承诺,并可以在随后揭示已承诺值。

承诺方案有两个基本性质:

  • 承诺值所对应的原值只会有一个。在 RGB 中,链上的承诺只可能对应唯一的 RGB 操作。(如果无法保证,则存在可能的双花)。这个性质被称作 binding。
  • 他人无法根据承诺值算出所选择的值。在 RGB 中,看到链上承诺的他人无法知晓对应的 RGB 操作,保证了隐私性。这个性质也被称作 hiding。

哈希是构造承诺方案的常用工具。例如我们所熟知的 Merkel Tree 是一个承诺方案。在区块链的其他应用中,承诺也是常见的工具,例如 ENS 的两阶段注册中也用到了承诺,用于避免注册的域名被他人抢跑。

Proof of Publication

我们可能会意识到一个问题,在 RGB 的客户端验证中,交易的双方是不可能得知资产的全局状态的,例如上述的例子中,Bob 持有 100 枚资产,但全局可能存在 200 枚资产。在这种情况下,合约运行不会造成什么问题吗?

RGB 称这一问题的解决方案为 proof of publication。 其核心思想是分布式系统中状态验证不需要由所有参与方去全局执行,而只需涉及特定状态转换的各方进行验证。采用这种方法,状态转换并非发布到全局,而是保证相关参与方知晓后(如签名),被编码成一个简短的密码承诺。如下图,合约 G 的全局状态为所有红色与灰色的节点(代表某次历史交易),然而验证 P 的状态只需要与 P 相关的红色节点部分。

需要注意,这一设计也对智能合约的设计提出了要求。

相关资源

RGB++

概述与愿景

RGB++ 由 Nervos CKB 的 Cipher 提出。Nervos CKB 是一条基于 UTXO 模型(CKB 中称之为 Cell)的公链。RGB++ 是一个基于 RGB 的扩展协议,它通过将 RGB 的 UTXO 所有权映射到 Nervos CKB 的 Cell 上,并利用 CKB 链和 Bitcoin 链上的脚本约束来验证状态计算的正确性和变更所有权的有效性。RGB++ 希望解决原 RGB 协议在实际落地中的技术问题,将客户端的工作转由 CKB 链承担。

协议速览

我们在这部分会尽量避免在内容中包括协议的复杂的细节。使用尽量简单化的方式解释协议的工作方式。

工作原理

RGB++ 是 RGB 的拓展协议,因此我们推荐读者在阅读以下内容前,确保已经了解了 RGB 协议的工作模式。而 RGB++ 的核心在于将原有的 RGB 的客户端验证范式改为直接公开到 Nervos CKB 链上,并由 Nervos CKB 充当执行引擎。我们仍然复用在 RGB 协议中使用的例子进行解释,如下图(注意,该图仅用于示意 RGB++ 基础的工作方式,为了理解的方便存在一些不准确)。

图中 Bob 向 Alice 发送 70 枚 RGB 资产。与之前的客户端验证不同,此时所有的 RGB 数据都被提交到了 CKB 链上公开,同时 CKB 也会作为交易引擎执行并保存 RGB 合约状态。

  • Bob 可以直接通过 Nervos CKB 得知自己拥有的 UTXO1 上绑定了 100 枚 RGB 资产。
  • 当 Bob 向 A 发送资产时,他不必再将 data0 与 data1 也提交给 Alice,Alice 可以自行在链上读取当前的合约状态或历史数据。
  • 在比特币上提交承诺的交易(0x345AB…D7654)确认后,Bob 可以向 CKB 提交包含了 RGB 数据(data2)的交易,CKB 的节点会同时运行比特币的轻节点,确认比特币链上存在承诺。
  • 在交易被 CKB 执行后,Alice 能够通过 CKB 读取到自己持有的 UTXO4 上此刻被绑定了 70 枚 RGB 资产。

可以看到,原有 RGB 的客户端的绝大多数功能现在被 CKB 链承担了,用户端在 RGB++ 中不必承担繁重的数据储存与验证成本。而负面影响则是 RGB 协议的隐私性当前不复存在。

交易流程详解

RGB++ 协议的白皮书对交易的流程作了非常详尽的解释,这里我们借用原文的表述,结合上图解说。

  • 链外计算
    • Bob 选中下一次要使用的一次性密封条(即 btc 交易要花费的 UTXO),例如 UTXO1。
    • Bob 链外计算并生成一笔将要发送到 CKB 上的 RGB++ 交易: CKB_TX。CKB_TX 中包含 RGB 状态变更所需数据。
    • Bob 链外计算承诺值 commitment(0x345AB…D7654)
  • BTC 交易提交
    • Bob 生成并发送一笔比特币交易,花费 UTXO1,通过 OP_RETURN 加入上述的commitment(0x345AB…D7654)。
  • CKB 交易提交
    • Bob 发送 CKB_TX 到 CKB 链上
    • 用户最新状态由 CKB_TX.output.data 维护
    • 下一次状态变更(Alice)需要使用 UTXO4, CKB_TX.output
  • 链上验证
    • Bitcoin UTXO1 仅被花费一次。
    • CKB 通过 Bitcoin Light Node,验证承诺值在比特币链上,且花费了正确的 UTXO。
    • CKB 验证 CKB 上合约的状态转移符合预先约定的合约规则。

信任与安全

由于“安全”是一个比较重要的问题。在这里我们会从“信任”的角度分析用户需要信任的协议与应用中的各环,并给出分析。
如没有另外提及,我们认为协议中使用的经典密码算法是可信的(不考虑量子计算机)。

  • 用户需要信任比特币网络。
    • 比特币上的交易不会被双花。否则 RGB(++) 资产也会被双花。
    • 比特币交易不会被审查。否则 RGB(++) 交易可能无法上链。
  • 用户需要信任 CKB 的交易执行引擎。理由同 RGB。
  • 用户需要信任 CKB 能会访问 Bitcoin 近行交易验证。否则非法状态将有可能上链。
  • 用户需要信任 CKB 不会审查用户交易。如果用户交易被审查无法上链,将无法获得交易执行结果。但是此时也能够将协议由 RGB++ 回退到 RGB,重新启用客户端验证的范式,离线计算交易执行结果。
  • 用户需要一定程度信任 CKB 的一致性(一致性可以理解为被确认的交易是否会回滚)。
    • 对应比特币上的承诺,恶意的攻击者无法构造出另一笔 CKB 交易,包含不同的执行内容。用户不必担心 CKB 的一致性导致 RGB 资产被双花。
    • 但用户需要考虑到,如果 CKB 回滚可能导致 CKB 交易数据丢失,而用户未保存相应交易数据,将可能进而导致 RGB++ 数据永久丢失。

需要注意的是,上面的信任分析没有考虑 RGB++ 计划引入的扩展功能,例如交易折叠、global state 等功能。

RGB++ 小结:优势与劣势

RGB++ 是 RGB 的扩展,我们先分析 RGB++ 相比 RGB 的优劣。

优势:

  • 用户不必自行保存数据,而是由 CKB 保存。
  • 执行引擎的绝大部分职责由 CKB 担当,用户能够从 CKB 链上读取RGB 合约的最新状态,也不必每次从头运行 RGB 数据。
  • CKB 通过 Bitcoin 轻节点自动验证交易,用户不必自行验证。

劣势:

  • 交易不再具有隐私性。
  • 交易需要在 CKB 上执行。限制了 RGB++ 合约执行复杂交易的能力。同时需要根据交易的复杂性支付费用。
  • 需要引入额外的对 CKB 链的信任依赖。

RGB++ 并没有完全继承 RGB 的优点,它牺牲了 RGB 方案的隐私性,并需要用户的部分额外信任,但在此基础上大幅提升了 RGB 协议的可用性。

而作为比特币的扩容方案,RGB++ 有如下优势与劣势:

优势

  • 信任:RGB++ 中 CKB 链充当了比特币链下公共计算引擎的作用,负责验证与运行协议,而无需跨链桥。协议中引入的额外信任需求非常轻度,用户容易验证 CKB 链是否在作恶,同时也有着回退到客户端验证的选项。

劣势

  • 没有很好地解决可扩展性问题。(RGB++ Light Paper 中提出的交易折叠可以一定程度上缓解这个问题,感兴趣可以阅读后文)。

更多细节

这部分我们会讨论协议中重要性处于次位的内容。这部分会包含一些技术细节与讨论,供感兴趣的读者参考。

同构绑定

在以上部分,我们还没有讲解 Nervos CKB 的具体架构。其他区块链,如图灵完备的 EVM 兼容链从理论上也能做到同样的事情。在本文附录中对此有简要的讨论。

CKB 能充当 RGB 计算引擎,其最大的优势在于 CKB 中的 Cell 与比特币的 UTXO 是同构的,这使得 CKB 可以复用 RGB 协议中的诸多设计,如通过 proof-of-publication 的设计避免状态争抢,使得 RGB 的相关计算可以回退到链下进行,避免协议执行中对 CKB 链的过多依赖。

交易折叠

RGB++ 将有可能将比特币上的一个承诺值对多笔(而非单笔)CKB 交易进行承诺,从而实现类似 ROLLUP 的效果,提升可拓展性。

global state 与其带来的安全风险

RGB++ 中为了增强合约的表达能力,引入了 global-state cell,RGB++ 的白皮书“共享状态与无主合约”中提到了这一点:

考虑 CKB 上存在一个全局状态的 Cell,用来管理多用户共享的状态。…全局状态由无主合约管理,所谓无主合约指的是任何人在满足合约的约束前提下都可以对状态进行变更,而不要求指定的数字签名提供方进行变更。…

这里的平庸实现指的是 Global-state cell 有被其他用户占用的风险,这样 CKB TX 就会因为指定的 Global state utxo 不存在而无法成立。…

为了解决上述问题,我们引入了 Intent Cell 作为中介。用户将自己希望执行的动作确定性地写入 Intent Cell,后者则可以通过第三方聚合器的协作与全局状态 Cell 交互,批量将多方的 intent 进行计算,并将交互结果合并到标准的 shadow cell 上。

这一点其实带来了潜在的安全问题。这一问题来自于 RGB 的 proof-of-publication 设计。该设计使得只保证单一路径上状态转换的偏序,而非全部状态转换的全序,就能保证协议安全。

而一旦引入了 global-state 等设计,则会改变这一点,导致非同一路径上的交易 A 与 B 的公开顺序对执行结果造成影响,考虑下面描述的场景(图参考上图):

  1. 交易 A 与 B 的承诺都已经先后上链 BTC。
  2. 但交易 A 对应的 CKB 交易因为某些原因一直未能上链(例如 CKB 链上的审查攻击)。
  3. B 交易在执行后已经对 global state 进行了修改。
  4. 交易 A 上链 CKB 后 A 交易很可能产生不同的执行结果甚至导致执行失败,造成资产丢失等后果。

而白皮书中提出的 Intent Cell 方法并不足以解决这个问题。Intent Cell 能保证的是提交到 CKB 的交易能执行,但却无法保证 A 与 B 的执行结果与预期一致。在原始 RGB 方案中,只要客户端的正确构造了状态转换与承诺,合约就能按照预期输出结果。而 RGB++ 中,在比特币上先提交的承诺的有效性却反过来被 CKB 控制了,使得原有协议的 Layer1 安全性被降级。为 RGB 引入 global-state cell 这一设计很有可能从安全性的角度不是一个好想法。

相关资源

附录 讨论:EVM 中 RGB 执行引擎的伪代码构建

我们在文中提到,图灵完备的 EVM 也能作为 RGB 架构的执行引擎。下面通过 伪代码 实现了一个 demo 合约,用于在 EVM 链上部署一个类 RGB++ 的执行引擎。

BTC_STATE 是一个特殊实现的合约,我们假定它可以(通过一个轻节点)读取比特币链上已确认的信息。同时任何人都能手动通过 RGB_CONTRACT 的 openCommitment 将交易 data 公开,将 EVM 作为执行引擎,执行 RGB 交易。这里我们假定交易的承诺值是通过提交到链上的。

// BTC_STATE can read bitcoin confirmed state
contract BTC_STATE {
  stuct TXO {
    uint256 btcTxId;
    uint256 outputIndex;
  }
  // a bitcoin tx will have at most 1 OpReturn
  function readBitcoinOpReturn(uint256 btcTxId) returns (bytes);
  // or
  function readBitcoinTransactionOutputScript(TXO txo) returns(bytes);

  function isBitcoinTransactionOutputSpent(TXO txo) returns(boolean);
  // ...
  function readBitcoinOrdinalIndex(TXO txo, uint256 satoshiIndex) returns (uint256);
  // ...
  function readBitcoin...(uint256 btcTxId) returns (bytes);
}

contract RGB_CONTRACT {
  struct RGB_DATA {
    TXO input; // could be multiple, only for reference
    TXO output; // could be multiple, only for reference
    bytes payload;
  }
  mappinp(uint256 => bytes) utxoStatus;
  mappinp(uint256 => boolean) utxoPermission;
  function openCommitment(RGB_DATA calldata data, bytes32 salt) public {
    isValidOpen(data, salt);
    utxoPermission[calcTxoHash(data.input)] = false;
    utxoPermission[calcTxoHash(data.output)] = true;
    execute(data.payload);
  }
  function calcTxoHash(TXO txo) pure returns (uint256);
  function calcCommitment(RGB_DATA calldata data, bytes32 salt) pure returns (bytes32);
  function execute(bytes payload) internal;
  function isValidOpen(RGB_DATA calldata data, bytes32 salt) {
    require(utxoPermission[calcTxoHash(data.input)], "from utxo should have permission");
    require(
      calcCommitment(data, salt) == BTC_STATE.readBitcoinOpReturn(data.input.btcTxId)
    );
    _;
  }
}