在庞大的区块链行业中,“世界状态”是时常为人所提起的技术名词。世界状态究竟是什么,不同项目的世界状态是如何设计与实现的,状态爆炸又该如何解决?
1. 什么是世界状态
区块链可以理解为一个分布式的状态机:所有节点从同一个创世状态开始,依次运行达成共识的区块内的交易,驱动各个节点的状态按照相同操作序列(增加,删除,修改)不断变化,实现所有节点在执行完相同编号区块内的交易后,状态完全一致,我们把这个状态称之为世界状态。
图1 区块链状态改变世界状态里面记录了各种信息,比如账户余额,智能合约字节码,各个智能合约自定义的数据(例如一个游戏DAPP的道具相关信息),链的配置参数等。世界状态表示的是当前状态,即记录的各状态数据的当前数值。为保证执行交易时能够快速地对世界状态进行更新,世界状态方案设计实现要考虑状态数据的快速查找以及高效更新。
2. 比特币的世界状态处理
比特币的世界状态数据存储于chainstate目录,采用LevelDB进行管理,主要存储当前还没有花费的所有交易输出以及交易的元数据信息,用来验证新传入的交易和块,存储这些数据的时候会做适当的压缩。LevelDB是一个可持久化的键值数据库,利用磁盘顺序写性能比随机写大很多的特性,采用LSM-Tree结构,将磁盘的随机写转化为顺序写,大大提高了写速度。LSM将树结构拆成一大一小两棵树,较小的一个常驻内存,较大的一个持久化到磁盘。写入操作会首先操作内存中的树,随着内存中树的不断变大,会触发与磁盘中树的归并操作,而归并操作本身仅有顺序写。Bitcoin的LevelDB存储了多种数据,其中最主要的为Coin数据,以 进程里的内存,可分为file-backed和anonymous两种:file-backed有文件对应,且数据可修改,比如程序TEXT段,文件共享内存且数据可修改等;anonymous内存里数据没有文件对应,或者对应文件里内容不可修改,比如堆,栈,共享库,静态数据区以及未初始化的数据区。chainbase使用memap,并设置MAP_SHARED标志,该模式是file-backed类型。系统将文件映射到进程的地址空间,直接访问内存实现对文件的读写操作。系统定期将脏数据写入磁盘,会占用CPU和IO,影响性能。chainbase大小可以超过物理内存,当访问数据时,系统通过缺页中断将数据加载到内存。虽然理论上chainbase可以无限大,但数据量超过物理内存则会导致频繁的缺页中断,使交易执行时间变大,严重影响系统TPS性能。 chainbase采用boost库中的multi_index_container数据结构,它是boost实现的一个多关键字索引容器,可以理解为数据库中的表。在链化未来的实现中,chainbase内核心数据表一共有二十多张(参见controller.cpp中add_indices函数)。与智能合约开发数据库操作相关的有两张表
table_id_multi_index和key_value_index,table_id_multi_index保存着所有智能合约中创建的表信息,key_value_index保存着所有智能合约中创建的表的数据记录。此外还有五张表(index64_index,index128_index,index256_index,index_double_index,index_long_double_index)用来存储二级索引数据,实现数据记录的灵活查询。 区块链网络中的全节点在运行一段时间后,会在本地存储产生越来越多的数据。本地存储的数据包括历史数据和世界状态数据:历史数据主要以区块的形式存储,区块内包含了所有的交易信息;世界状态数据为节点处理完从创世块开始到当前块高包含的所有交易形成的当前状态数据。无论是历史数据还是世界状态数据,都会随着系统的运行持续增长,使得运行全节点需要的存储资源越来越大。当前区块链的处理性能还比较低(比特币为7TPS,以太坊为15TPS,其他区块链一般为数千TPS),考虑到区块链处理性能获得大大提升后,存储增长问题会变得更严重,这就是状态爆炸问题。历史数据的积累相对比较容易解决,可以采取中心化压缩存储,或者利用Checkpoint机制,全节点可以不用存储某个时间点之前历史数据。对于世界状态数据的状态爆炸问题,目前主要有状态租赁方案,状态剪枝方案,“无状态节点”方案等解决思路。 状态租赁方案要求用户为存储的数据支付租金,该租金不仅考虑数据占用空间大小,而且与其在链上存储的时间成正比。通过让用户支付状态费用,可以推动不再使用的状态信息可以随着时间的推移而被删除,从而防止数据逐步增长。关于收费模式有不同的讨论,目前没有明确的结论。 状态剪枝方案是指全节点只存储近期的数据,比较老的历史数据将存储在其他地方,状态剪枝方案会影响那些依靠全节点索引和查询所有历史数据的DApp。状态剪枝方案不能从根本上解决问题,而且随着区块链应用越来越多,数据增加的速度也越来越快,由于存储空间不变,那么能被存储的近期数据时间也就会越来越短。3.
以太坊的世界状态处理
比特币的世界状态是通过网络中全局的未使用交易输出(UTXO)来描述的,而以太坊则利用账户概念来描述状态信息。账户状态包含四个属性,nonce,balance,storageRoot和codeHash。以太坊用stateObject来管理账户状态,账户以Address为唯一标示,其信息在相关交易的执行中被修改。所有账户对象逐一插入Merkle-PatricaTrie(MPT)结构里,形成stateTrie。区块头数据结构中的Root字段存储了stateTrie的root数值,即世界状态的哈希值。4. Ultrain世界状态处理
区块链的一个重要指标是交易处理能力TPS(Transaction Per
Second),交易处理过程会对世界状态做高速频繁读写,因此需要高性能数据库来管理世界状态。另外,交易执行对世界状态的修改,有可能因为共识协议要求进行回滚,所以需要数据库或应用层逻辑能支持数据修改回滚。比特币与以太坊所采用的键值数据库LevelDB或者RocksDB对简单Key/Value查询支持的比较好,但是对复杂查询逻辑支持需要应用层自行实现。Ultrain实现的开放联盟链(以下简称开放联盟链)实现时综合考虑上述原因,选择了开源的chainbase数据库进行世界状态管理。chainbase内存映射
chainbase状态回滚支持
chainbase支持undo session概念。当创建一个undo
session之后,会对chianbase的增加,删除,修改操作进行跟踪记录。如果需要回滚,则在undo的时候还原原始记录;如果不需要回滚,则在commit的时候丢弃保存的undo状态信息使修改变得不可逆。chainbase内每张数据表都有三个独立undo
stack,分别记录对该数据表所做的增加,删除,修改操作。每个Block和每个Transaction开始都会创建一个新的undo
cache条目;Transaction成功,就执行undo
stack融合操作,将修改并入Block的undo条目;交易失败则进行状态回滚,回退Transction对数据表的修改。chainbase多索引支持
5.
世界状态快照生成
开放联盟链为主侧链架构,节点会随机在侧链间进行调度,链间调度引入了一个问题:节点从一条侧链调度到另外一条侧链,相当于一个新节点加入了该侧链网络,那这个节点需要有与其他节点一致的世界状态,才能加入共识参与出块流程。构建世界状态,可以利用历史区块数据,依次执行区块内保存的交易,重构出世界状态;也可以利用保存好的世界状态快照文件,直接恢复到指定块的世界状态,再继续重放后续区块。第一种方式中,链运行越久,产生的数据越多,重放需要耗费的时间越久;第二种方式中,一方面需要考虑如何能够高效地生成世界状态快照文件,另一方面要提供一种机制让节点可以验证其获取的世界状态快照文件是正确的没有经过篡改的。如何高效地生成世界状态快照文件而不影响主线程正常处理交易,即不对TPS性能造成影响,Ultrain采用了cache机制,并使用单独的世界状态快照生成线程。如下图所示,类似chainbase的undo
stack机制,Ultrain增加了cache模块。每次交易对chainbase数据的修改,一方面会进入undo
stack用于支持状态回滚,另一方面会进入cache模块,用于给单独的世界状态快照生成线程处理。cache模块的处理逻辑与undo
stack不同的地方在于,cache会保留多个block数据块的修改,并不断进行融合,每隔一定间隔(比如每生产100个区块)生成一个新的条目,如下图中901~1000,表示为编号为901到编号为1000的区块之间的数据修改cache。6. 状态爆炸及解决思路
状态租赁方案
状态剪枝方案
“无状态节点”方案
在无状态节点方案中,全节点不需要存储区块链的状态,而只需要对状态进行短暂的承诺(Commitment)以验证交易,这需要利用到的密码学原理包括累加器和向量承诺(Accumulators
and Vector
Commitments)。有研究机构构建了基于比特币和以太坊的概念验证模型,但是从概念验证到真正的落地应用,还需要做大量的工作。针对状态爆炸问题,还有项目提出了一些新的思路,比如EOS内置交易市场,用户需要支付原生代币购买RAM进行世界状态存储;Nervos采用Cell模型,利用原生代币经济模型激励存储空间自由定价交易;Coda利用零知识证明的不断递归实现存储压缩技术等。Ultrain所采取的主侧链结构中,各个侧链的世界状态相互独立,可以理解为对世界状态的水平切分,缓解了单链结构中状态爆炸的问题。此外,Ultrain也在尝试节点状态压缩技术,即节点只保存部分世界状态数据,结合交易提供的状态数据完成交易执行。同时Ultrain也在积极探索“无状态节点”技术,争取早日实现该技术的工程落地。让信任计算赋能各行各业。