---
title: xuperchain 试图用 UTXO 追踪合约状态
date: '2019-05-28 18:06:36'
draft: false
summary: xuperchain 把合约状态拆成按 key 追踪的版本变化，用预执行和冲突检测来绕开传统 global state trie 的一部分成本。
slug: xuperchain-utxo-contract-state-tracking
syndication:
- platform: Weibo
  url: https://weibo.com/1648815335/HwiJhfqpB
tags:
- blockchain
- utxo
- smart-contract
- xuperchain
topics:
- blockchain
type: post
---

百度开源了一个区块链项目 `xuperchain`。我当时下了源码看了一下，觉得它里面有几个设计挺有意思。

Ethereum 为了支持链上合约执行，选择了 `Account` 模式，而不是 `UTXO`。原因也比较直接：合约执行时，需要先加载合约当前状态，执行后再更新状态。用户在构造交易的时候，并不能准确知道交易执行后的最终状态是什么，所以很难像 `UTXO` 那样提前把输入输出关系表达清楚。于是 Ethereum 用的是 `Account + Global State Trie` 这一套状态追踪方式。

`xuperchain` 的思路不太一样。它尝试用 `UTXO` 的方式来追踪链上合约状态。它的交易里有两类 `input`：

- 一类追踪资产
- 一类追踪合约里 `key-value` 的变化

比如一个 `counter` 合约第一次调用时，对应的 `input.refTxid` 可以为空；第二次调用时，`counter` 这个 key 的 input 就会引用上一次交易。也就是说，它不是把合约状态看成一个整体，而是把状态拆成很多可以单独追踪版本变化的 key。

这套设计自然会带来一个问题：客户端怎么知道这次交易到底会改哪些 key，应该如何构造交易？

`xuperchain` 的做法是提供一层预执行。客户端先请求节点预执行合约，拿到输入输出，再按这个结果构造正式交易并提交上链。

另一个问题是冲突控制。比如两个交易同时调用 `increase counter`，如果它们都引用了同一个旧状态，就会发生冲突。所以它不是按“整个合约状态”做冲突追踪，而是按 key 粒度追踪。这样可以显著降低冲突概率，这个思路其实很像数据库里的乐观锁。

这套机制的一个直接好处是：链的状态可以被拆成一组 key-value 的版本变化追踪，从而省掉 `Global State Trie` 的一部分构建和维护成本，理论上对性能是有帮助的。

它还会反过来影响智能合约虚拟机的设计。虚拟机上下文后面接的是一个多版本 key-value 数据库，合约通过 `GetObject` / `PutObject` 这类接口来声明自己读写了哪些 key，以及这些 key 的最新值。这样一来，合约更像是在验证交易，而不是每次都要从头重新执行一遍才能恢复最终状态。如果信任区块中的交易数据，最终状态理论上是可以通过交易直接构造出来的。

它的合约支持 `Wasm`，当时有 `C++` 和 `Golang` 两种语言。`Golang Wasm` 那时候还不支持直接 `export` 方法给外部调用，所以它用了一个 `driver` 模式，在 `main` 里通过反射分发具体方法。代价也很明显：`Golang Wasm` 生成的文件还是太大，一个简单的 `counter` 合约都能编译出 `5MB` 以上。

所以我对它的印象是：它不是沿着 Ethereum 的默认路径往前走，而是在尝试回答另一个问题，能不能继续保留 `UTXO` 式的追踪优势，同时把合约状态表达出来。这条路约束更多，但也很值得看。

<!-- WEIBO_MEDIA_START -->
## 原微博中的媒体

![](./weibo-4376984953676451-1.jpg)

![](./weibo-4376984953676451-2.jpg)

![](./weibo-4376984953676451-3.jpg)
<!-- WEIBO_MEDIA_END -->
