跳到主要内容

基于UTXO的智能合约

介绍什么是UTXO智能合约以及为什么UTXO合约可以极大提高性能。

UTXO模型简介

UTXO模型(UnspentTransactionOutput 未花费交易输出)是比特币的核心设计之一,相比于以太坊等链账户模型,它的优势在于可以实现大规模的并行处理。

账户模型是维护了一个个全局的状态(账户余额,合约数据信息等),在进行每一步交易之前,都需要对这个全局状态进行读写操作,类似于银行账户余额。在高并发的情况下,对全局状态的读写会导致资源竞争,为了确保安全,账户必须进行串行有序的操作,这也就是为什么账户模型存在一个nonce字段来标记交易的顺序,交易的有效性严格依赖这些顺序,交易之间也必须按顺序进行校验。

UTXO模型则更加类似于现金或者硬币,并没有一个全局的状态来记录每一个地址的余额,而是通过一个个未花费的交易输出(单独的带面值硬币)来表示它的归属关系。地址的余额是通过统计所有未花费的输出(UTXO)总面额来计算出来的。在进行交易的时候,只需要对这些未花费的交易输出进行操作,而不需要对全局状态进行读写,这样就可以实现大规模的并行处理,提高了整个系统的性能。用日常生活的例子来对比,utxo模型维护的状态实际上就是一大堆硬币,每个硬币上面标记了面值和归属(或者花费它的条件),每个硬币之间都是独立互不影响的,可以同时对任意数量的硬币进行操作,这也就带来了更高的并行处理能力。

下图表示了UTXO模型的基本结构:

img/utxo-presentation.png

每一笔交易都包括输入和输出,输入一定来自于前一个交易的输出。类比而言,每一个输入相当于将对应的UTXO销毁,每一个输出则是创造新的UTXO。在比特币中,每一笔交易都会消耗之前的UTXO,生成新的UTXO,UTXO能且只能被花费一次,一旦花费就不再是UTXO,也就不再参与计算余额,这样就保证了每一笔交易都是有效的,不会出现双花的情况。

注意:coinbase交易,也就是矿工挖矿获得收益的交易不会消耗UTXO,只会产生新的utxo,这也可以看作是铸币的过程。

从上面的图示可以看出,UTXO模型控制的最小单位不是交易,而是一个个的UTXO,理论上可以对任意数量的UTXO同时进行操作,这就是UTXO模型的并行处理能力的来源。为了突出UTXO的并行性,我们可以用几个简单的例子来说明:

  1. UTXO转账并行:我可以构造一个1输入,10000输出的交易,将一笔钱平均分给10000个人,使用1笔交易就可以完成,而账户模型完成同样的目标则需要10000笔有序的交易交易,前序转账未完成前不能进行下一笔。(UTXO并行转账)
  2. UTXO收款并行:我的地址可以同时接受10000笔转账,注意这里是同时,它们之间的顺序并不重要,谁先到谁后到都不影响最终结果,而账户模型则需要交易出现在区块中的顺序进行串行处理。(UTXO并行接收)

UTXO处理并行:节点软件或者矿工可以将交易池中的交易按照不同的规则(比如UTXO相关性)进行分组,然后由多个处理模块并行处理,这样可以通过横向拓展提高整个网络的处理能力,要知道能够进行横向拓展是无限扩容的必要条件,而账户模型则需要对每一笔交易进行有序处理,无法并行。(UTXO并行处理)

因此,MVC采用UTXO模型作为交易并行处理的根基,同时解决了UTXO模型的一些缺点,比如难以实现智能合约,难以维护全局状态等问题,使得MVC具备了高性能的基础。

使用UTXO模型进行标准的转账交易是非常简单的,但是要实现智能合约却受到很多限制,也存在很多技术难度:

  • UTXO模型因为不存在全局状态,进行类似以太坊智能合约的全局状态管理非常困难。
  • UTXO模型是纯函数式编程,需要一定的理论基础才可以掌握正确的编程模式。
  • BTC等公链限制了可以执行的脚本数量和类型,只允许几大标准类型的交易输出,导致智能合约的功能受到限制。
  • UTXO生态中缺乏完善的智能合约开发工具,开发者需要自己编写脚本并且手动构造交易,非常不友好。
  • UTXO合约由于其技术特点,存在很多编译期常量,在处理循环,递归等复杂逻辑时会遇到很多困难。

MVC充分考虑了UTXO模型的优势和缺点,在吸取以太坊和其他UTXO公链的经验的基础上,引入了BVM(Blockchain Virtual Machine)的概念和MetaTxid等技术来实现真正的纯一层UTXO智能合约。并且同sCrypt 团队深度合作,提供了更加友好的智能合约开发工具,大大降低了编写和部署BVM智能合约的门槛。

BVM(Bitcoin Virtual Machine)

BVM是基于比特币的脚本系统 进行了操作码恢复和功能扩展的虚拟机,它是MVC智能合约的执行引擎。比特币的脚本系统是一个双栈结构的执行器,包括输入栈和输出栈,使用类forth语言对栈进行任意操作,实现更高层次的逻辑。实际上,很多现代编程语言的执行结构很大程度上都要依赖执行栈,从原理上来说,你甚至可以通过栈式结构来实现任意复杂的程序逻辑(只要内存资源足够多)。可以说,栈式结构是构成现代编程语言的基础,拥有无限的潜力。

但由于BTC的限制,脚本系统只能进行简单的逻辑判断,可以使用的操作码也非常有限,无法实现复杂的智能合约逻辑。BVM在脚本系统的基础上进行了功能扩展,引入了更多的操作码 ,支持更多的数据类型,提供了更多的功能,使得MVC智能合约可以实现更多的复杂逻辑。

我们可以对BVM及其合约进行如下的定义和特点归纳:

  1. BVM是基于比特币脚本系统的操作码进行了功能扩展的虚拟机。
  2. BVM由输入栈和输出栈组成。输出栈可以看作智能合约的函数定义以及数据,输入栈可以看作智能合约的函数调用以及参数。
  3. BVM合约是纯函数式运算 ,具备函数式编程的原子性,无状态,无副作用,可并行执行等特点。
  4. BVM的合约计算的结果只包含TRUE或者FALSE,通过UTXO是否能够被解锁来判断合约是否执行成功。
  5. BVM的合约具备原子性,要么全部成功,要么全部失败,不会出现部分执行的情况。校验失败的合约不会消耗GAS费,因为会被看作非法交易,不会记录上链。

BVM

更多关于BVM的操作码和合约编程的详细容请参考后续章节

UTXO智能合约简介

UTXO智能合约就是将合约的逻辑和数据存储在UTXO中,将合约的调用和参数作为input来尝试解锁合约,通过BVM执行合约的逻辑,最终通过能否解锁(函数返回true或false)来达到控制合约状态的目的。这样的模式可能对于以太坊智能合约开发者来说有些陌生,但实际上结合函数式编程思想,配合一些概念的转换,UTXO智能合约也可以实现非常复杂的逻辑。

UTXO模型由于不存在全局状态,因此需要将合约的状态和逻辑存储在UTXO中,通过UTXO交易调用链的传递来进行状态的传递和转换,如下图所示:

UTXO state chain (UTXO合约状态链,图片取自sCrypt

每一次的UTXO交易都会消耗之前的UTXO,生成新的UTXO,通过这种方式可以实现合约的链式状态转移。而能否解锁UTXO也就对应着合约的执行结果是否允许状态进行转移,如果合约判断不允许修改状态(比如不允许转账,不允许修改数据等),则会返回false,UTXO不会被解锁,合约执行失败。

我们把合约看作对数据状态进行转移操作的状态机,那么这里可以看出UTXO合约和Account合约的区别:

account合约维护全局状态,一个交易可能导致EVM进行多次的状态转移,频繁地修改状态数据直到合约执行完成或者Gas消耗完。而UTXO合约的交易一个input合约调用只会触发一次状态转移,并且无论合约内部的逻辑多复杂,状态转移多少次,BVM只会将最终的状态转移结果记录在链上。

  • UTXO合约没有全局状态,只有一个个等待被执行的“函数”(UTXO)。需要转移状态,就要先找到这个状态所在的函数,通过函数调用来修改状态并且生成新的函数。这样的模式使得UTXO合约的状态转移更加清晰,更加容易理解。

由于UTXO合约不依赖外部状态,因此一次合约调用,无论调用多少次,它的结果必然是确定的,这也就给合约的分析和调试以及单元测试带来了巨大的便利。反观EVM合约,由于依赖全局状态,合约的执行结果很可能会受到外部环境的影响,导致合约的执行结果不确定(余额够是一个结果,不够又是另一个结果),这也是EVM合约的安全性和可预测性的一个重要问题。

当然,每次将状态传递下去也不是没有代价,在一些需要溯源的场景中,状态可能会随着传递链条的增大而增大,因为溯源需要校验的数据可能越来越多,状态本身会无限膨胀 。这个问题MVC也通过称为MetaTxid的技术,通过哈希和数据抽取等密码学的手段将状态膨胀的一大类问题彻底解决,这也是MVC智能合约区别于其他UTXO链的一个重要特点。

UTXO smart contract