从BTC到Sui、ADA与Nervos:UTXO模型及其相关扩展

作者:0xAyA

作为比特币的核心设计原则之一,UTXO模型在诞生之日起就成为了区块链领域中一种重要的技术范式。它在保障交易安全性和可追溯性方面发挥了重要作用,同时提供了传统账户余额模型以外的另一条道路。随着近些年区块链技术不断经历更新迭代,UTXO模型本身也在不断地演化与扩展(如eUTXO、cell、Strict access list等)。

本文以学习和了解UTXO模型为目的,用浅显易懂的方式,简单梳理从BTC到Sui、Cardano和Nervos、Fuel各自的UTXO模型及实现方式,使其更好理解。

首先,什么是UTXO?

可以通过一个例子理解UTXO模型:

假设有两个人,Alice和Bob,他们原本各有5块钱。之后,双方发生了冲突,Alice被Bob抢走了2块钱。二人最终持有的金钱数额如下图所示:

不难看出,Alice最终剩下3块钱,Bob最终持有7块钱。这种小学加减法一样的记账方式频繁出现在银行系统中,被称为**“账户/余额模型”**。其中,账户的余额作为单一的数值而存在。

如果用不同于账户模型的方式,比如UTXO表示Alice和Bob之间发生的财富转移,示意图则会变成不同的样子:

此时,Alice还是剩3块钱,Bob还是剩7块钱,但这7块钱并不是用一个单一数值表示的,而是被拆成了“5块钱”和“2块钱”。这种反常规的方法是不是让人感到不太习惯?这就是特殊的记账方式——UTXO 。

在这种记账方式下,每笔链上交易会表现为UTXO的变化与转移。比如,在上文提到的交易事件中,Alice最初拥有的“5块钱”作为输入参数,被标记为UXTO_0,之后会被销毁;同时,程序会生成“2块钱”(UTXO_1)和“3块钱”(UTXO_2)作为输出参数,UTXO_1将被转给Bob,UTXO_2将转回给Alice,ALice和Bob之间的财富转移以此完成。

实际上,在UTXO模型中,不存在“账户”和“余额”这两个明确的概念,***UTXO只是帮助交易执行的数据结构,它会记录自身代表的金额、与其相关的交易索引等信息。***每个UTXO都代表一个可以被使用但未被使用的交易输入,具有确定的所有者。当一笔交易发生时,可以将某些UTXO作为输入,将其销毁后会产生新的UTXO作为交易输出结果。

这就是Bitcoin的记账方式:每次交易都会有旧的UTXO被销毁,新的UTXO被产生。被销毁的UTXO总金额等于新造的UTXO金额(其中某部分是给矿工的手续费)。这样一来,没有人可以凭空增发资金。

UTXO模型和账户/余额模型的比较

假设有一批用户同时发起了大量交易请求,如果分别使用UTXO模型和账户/余额模型处理交易,情况会是怎样?

在账户/余额模型中,每个用户都拥有一个账户,其中记录着余额信息。有交易发生时,相应账户的余额要被更新,这涉及对其“读”和“写”的操作。可如果某两笔交易涉及同一个账户,往往会产生读写上的冲突,即状态争用,这是必须要避免的情况。

传统的数据库系统往往通过“锁”机制,解决对某部分数据的读写争用。在这种场景下,构成数据争用关系的多笔交易往往要排队,无法同时执行,这会使交易的处理效率下降。当有大量交易待处理时,上述情况可能会导致严重的性能瓶颈,彼此有数据争用关系的交易可能长时间处于等待状态,无法被快速处理。

相比于账户余额模型,比特币的UTXO模型可以更好的解决数据争用问题。因为在这种方式下,每笔交易的直接处理对象不再是某个“账户”,而是各个独立的UTXO。由于不同的UTXO互不干扰,比特币网络中每笔交易都是互不干扰的。因此,比特币网络节点在处理大量的待处理交易时,可以同时处理多笔交易,无需使用“锁”,这样可以大大提高系统的吞吐量和并发性能

此外,UTXO模型的加密钱包通常会在用户发起一笔交易后,生成一个新地址,这样可以实现隐私保护——要将交易和某个具体的人关联起来变得更为困难——相比之下,账户/余额模型由于使用固定的地址,更容易被关联性分析。

但UTXO也存在局限性,其设计初衷是实现简单的货币转移,不是处理复杂的业务逻辑,尽管可以用脚本语言进行一些简单的功能实现,如多签、时间锁等,但由于比特币的UTXO能记录的状态信息太简陋,使其在进行一些复杂操作时有心无力。

比特币UTXO的局限性间接推动了“以太坊”的诞生——Vitalik作为Bitcoin Magazine最早的撰稿人之一,对比特币的缺点十分了解。而账户/余额模型不仅更容易为大多数人所理解,还可以解决UXTO难以处理富状态应用的困境,正如他在“以太坊白皮书”中所说的:

UTXO 可以是已使用或未使用;用于保存任何其他内部状态的多阶段合约或脚本是没有机会出现的。这使得多阶段期权合约、去中心化交易报价或两阶段加密承诺协议(这是安全计算赏金所必需的)难以创建。这也意味着 UTXO 只能用于构建简单的一次性合约,而不是去中心化组织等更复杂的“有状态”合约,使得元协议难以实现。二进制状态加之价值盲点也意味着另一个重要应用 — 提款限制 — 是不可能实现的。

UXTO模型的应用、优化和扩展

在介绍各种对UXTO的应用和优化之前,首先要分析UTXO在保持其优势的同时有哪些提升点,简单总结为如下几点:

  1. 对UTXO所存储状态的意义进行抽象;

  2. 对状态的所有权进行抽象。

  3. 解决共享UTXO的状态争用问题。

在BTC中,状态唯一的意义就是代币数量,而所有权通常用公钥来定义,至于状态争用,BTC并不是为dapp而设计,所以也没有过多涉及。

Sui

Sui为开发人员提供了两种对象类型:OwnedObject和SharedObject,前者相当于UTXO(更具体来说是UTXO的增强版),后者相当于账户/余额模型,两者可以同时使用,此处引用Sui技术文档的解释:

一个Object可以被共享,这意味着任何人都可以读取或写入该Object。与可变的OwnedObject(只能有一个写入者)相比,SharedObject需要共识来对读取和写入进行排序。

在其他区块链中,每个Object都是共享的。然而,Sui编程人员通常可以选择使用OwnedObject、SharedObject或两者的组合来实现特定的用例。这个选择可能对性能、安全性和实现复杂性产生影响。

在Sui中,Owned Objects就类似于UTXO,只有它的所有者Owner能对其进行操作,且Object都有版本号,“一个object的某个版本只能被它的 owner 花销一次”,所以,“一个object的某个版本” 实质就相当于 UTXO。

至于状态争用的问题,则可以通过特殊处理(局部排序,和Fuel类似)SharedObject来实现。

Cardano

Cardano使用extended UTXO模型,缩写为eUTXO。eUTXO支持更高的可编程性,同时兼有比特币UTXO模型的优点。

在Cardano中,状态的意义通过脚本进一步得到扩展,而其状态的所有权则通过更一般化的方式进行定义,同时使用UTXO集来尽量避免出现状态争用问题。具体概括,eUTXO在两个方面有所加强:

  1. eUTXO模型中存在更一般化的地址,这些地址不仅仅可以基于公钥的哈希,还能基于任意逻辑定义在何种条件下可以花费eUTXO,即可以对状态的所属权进行编程。

  2. 除了地址和值之外,输出还可以携带(几乎)任意数据,即可以通过脚本对状态的意义进行编程。

具体而言,eUTXO允许用户将类似JSON格式的任意数据添加到UTXO中,该数据称为Datum。Datum 允许开发人员为脚本提供类似状态的功能,它与特定的 UTXO 相关联。

同时,Cardano上的交易可以携带与特定用户相关的参数,称为Redeemer。Redeemer允许交易发起者定义UTXO的使用方式,可以被dapp开发人员用于各种目的。

当一笔交易被验证时,验证脚本会使用Datum、Redeemer和包含交易数据的上下文进行操作,该脚本中会包含在满足条件时使用UTXO的逻辑。

需要注意的是,eUTXO仍然是通过脚本来完成拓展任务的,和传统意义上的“智能合约”有着很大的差别(创始人Charles Hoskinson认为实际的名字应该叫“可编程验证器”,但“智能合约”这个说法更容易被市场所接受)。

Nervos

在Nervos(即CKB)中,状态的意义由typescript抽象,而其状态的所有权由lockscript抽象,一个简单的UTXO优化模型——cell代码如下:

pub struct CellOutput {

pub capacity: Capacity,

pub data: Vec,

pub lock: Script,

pub type_: Option,

}

而对于状态争用问题,目前CKB推进研究的是Open Transaction,用户可以提出一个部分UTXO指明交易目的,然后由撮合者撮合成完整的交易。

Nervos的cell模型是UTXO的“一般化”版本,对其详细的科普Jan在Nervos论坛上如此解释:

Layer1的关注点在状态,以Layer1为设计目标的CKB设计的关注点很自然就是状态。Ethereum将交易历史和状态历史分为两个维度,区块和交易表达的是触发状态迁移的事件而不是状态本身,而Bitcoin协议中的交易和状态融合成了一个维度,交易即状态,状态即交易,正是一个以状态为核心的架构。

同时,CKB想要验证和长久保存的状态,不仅仅是简单的数字(nValue),而是任何人们认为有价值的、经过共识的数据。显然Bitcoin的交易输出结构满足不了这个需求,但是它已经给了我们足够的启发:只需要将nValue一般化,把它从一个存放整数的空间变成一个可以存放任意数据的空间,我们就得到了一个更加一般化的”CTxOut",或者叫Cell。

在Cell里面,nValue变成了capacity和data两个字段,这两个字段共同表示一块存储空间,capacity是一个整数,表示这块空间有多大(以字节数为单位),data则是保存状态的地方,可以写入任意的一段字节;scriptPubKey变成了lock,只是换了一个名字而已,表达的是这块共识空间的所有者是谁 - 只有能提供参数(例如签名)使得lock脚本成功执行的人,才能“更新”这个Cell中的状态。整个CellOutput占用的字节数必须小于等于capacity。CKB中存在着许许多多的Cells,所有这些Cell的集合形成了CKB完整的当前状态,在CKB的当前状态中存储的是任意的共同知识,不再仅仅是某一种数字货币。

交易依然表示状态的变化/迁移。状态的变化,或者说Cell内容的“更新”实际上也是通过销毁和创建来完成的(并不是真的去修改原有Cell中的内容)。每一笔交易实际上都会销毁一批Cells,同时创建一批新的Cells;新创造的Cells会有新的所有者,也会存放新的数据,但是被销毁的capacity总和,总是大于等于新创建的capacity总和,由此保证没有人可以随便增发capacity。因为capacity可以转让,无法增发,拥有capacity等于拥有相应数量的共识状态空间,capacity是CKB网络中的原生资产。Cell的销毁只是把它标记为“已销毁”,类似Bitcoin的UTXO从未花费变为已花费,并不是从区块链上删掉。每一个Cell只能被销毁一次,就像每一个UTXO只能被花费一次。

这样一个模型的特点是:

  1. 状态是第一性的;

  2. 所有者是状态的属性,每一份状态只有一个所有者;

  3. 状态不断的被销毁和创建;

所以说,Cell是UTXO的一般化(generalized)版本。

Fuel

Fuel采用了基于UTXO优化的Strict access list模型,这种模型定义了一种新的UTXO——合约UTXO。

正如上文所介绍过的,BTC中的UTXO只有两个属性:币的数量和所有者,而合约UTXO则提供了更多的基础属性,包括:币的数量、合约ID、合约代码哈希和存储根。

如果使用无状态执行模型,只有在合约UTXO中才需要合约代码哈希和存储根。在有状态执行模型中,合约UTXO可以省略这些字段,但需要单独的存储元素UTXO类型。UTXO ID(每个UTXO的唯一标识符,可以用作键值存储数据库中的键)是产生UTXO的输出点,或者其变体(例如,输出点及其字段的哈希)。

在这种模型中,合约UTXO和智能合约一样是任何人都可以调用的。

需要注意的是Fuel提供的是更为贴近智能合约的功能,而非脚本,而UTXO本身模型的限制使得基于VM去做应用时会有数不清的麻烦,最典型的就是UTXO的争用问题,一般来说有三种解决办法:一是在链下处理如Rollup;二是先提前做好额外的排序工作,Fuel采用的就是后者;三是刚刚在CKB部分提到的Open Transaction,即每个用户可以提部分交易,然后由撮合者(类似定序器),撮合成完整的交易,BTC与之相对应的解决方案为PBST。

结尾

通过梳理,了解了UTXO的基本原理,知道了其模型与ETH的账户/余额模型的优劣之处,并对UTXO概念及其相关扩展有了更加清晰的了解。

作为比特币的核心设计原则之一,UTXO模型在保障交易的安全性和可追溯性方面发挥了重要作用,随着区块链技术的不断发展,UTXO模型也在不断演化和扩展(如EUTXO、cell、Strict access list等),为数字资产的交易和管理提供了更多可能性,通过深入研究和理解UTXO概念及其相关扩展,可以更好地把握区块链技术的本质,并为未来的创新和应用打下更加坚实的基石。

Subscribe to 极客 Web3
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.