区块链与状态爆炸

图片描述

若是 Layer 1 的关注点应该是状态而不是计算,在设计 Layer 1 区块链时,咱们就须要先理解什么是区块链的状态。理解了状态是什么,咱们才能理解状态爆炸是什么。git

状态

区块链网络中的每个全节点,在网络中运行一段时间以后都会在本地存储上留下一些数据,咱们能够按照历史和如今把它们分为两类:github

  • 历史——区块数据和交易数据都是历史,历史是从 Genesis 到达当前状态的路径。
  • 状态(即如今)——节点在处理完从 Genesis
    到当前高度的全部区块和交易后造成的最终结果。状态随着区块的增长一直处于变化之中,交易是形成变化的缘由。

共识协议的做用是经过一系列的消息交换,保证每个节点看到的当前状态是相同的,而实现这个目标的方式是保证每个节点看到的历史是相同的。只要历史相同(即全部交易的排序相同),处理交易的方式相同(把交易放在相同的肯定性虚拟机里面执行),最后看到的当前状态就是相同的。当咱们说「区块链具备不可篡改性」时,是指区块链历史不可篡改,相反,状态是一直在变化的。web

有趣的是,不一样的区块链保存历史和状态的方式不一样,其中的差别使得不一样的区块链造成了各自的特色。因为这篇文章讨论的话题是状态,而影响状态的历史数据主要是交易(而不是区块头),接下来的讨论历史的时候会侧重交易,忽略区块头。segmentfault

举个例子:Bitcoin 的历史和状态

Bitcoin 的状态,指的是 Bitcoin 帐本当前的样子。Bitcoin 的状态是由一个个 UTXO(还没有花费的交易输出)构成的,每一个 UTXO 表明了必定数量的 Bitcoin,每一个 UTXO 上面写了一个名字(scriptPubkey),记录这个 UTXO 的全部者是谁。若是要作一个比喻的话,Bitcoin 的当前状态是一个装满了金币的袋子,每一个金币上刻着全部者的名字。服务器

Bitcoin 的历史由一连串的交易构成,交易内部的主要结构是输入和输出。交易更改状态的方法是,把当前状态中包含的一些UTXO(交易输入引用的那些)标记为已花费,从 UTXO 集合中移出,而后把一些新的 UTXO(这个交易的输出)添加到 UTXO 集合里面去。网络

clipboard.png

能够看出,Bitcoin 交易的输出(TXO,Transaction Output)正是上面说的 UTXO,UTXO 只不过是一种处于特殊阶段(还没有花费)的 TXO。由于构成 Bitcoin 状态的组件(UTXO),同时也是构成交易的组件(TXO)。由此 Bitcoin 有一个奇妙的性质:任意时刻的状态都是历史的一个子集,历史和状态包含的数据类型是同一维度的。交易的历史(全部被打包的交易的集合,即全部产生过的 TXO 的集合)即状态的历史(每一个区块对应的 UTXO 集合的集合,也是全部产生过的 TXO 的集合),Bitcoin 的历史只包含交易。post

在 Bitcoin 网络中,每个区块,每个 UTXO 都要持续占用节点的存储空间。目前 Bitcoin 整个历史的大小(全部区块加起来的大小)大约是200G,而状态的大小只有大约 3G(由约 5000万个UTXO组成)。Bitcoin 经过对区块大小的限制很好的管理了历史的增加速度,因为其历史和状态之间的子集关系,状态数据大小必然远小于历史数据大小,所以状态增加也间接的受到区块大小的管理。性能

再举个例子:Ethereum 的历史和状态

Ethereum 的状态,也叫作「世界状态」,指的是 Ethereum 帐本当前的样子。Ethereum 的状态是由帐户构成的一棵 Merkle 树(帐户是叶子),帐户里面不只记录了余额(表明必定数量的 ether),还记录了合约的数据(例如每一只加密猫的数据)。Ethereum 的状态能够看做是一个大帐本,帐本的第一列是名字,第二列是余额,第三列是合约数据。区块链

Ethereum 的历史一样由交易构成,交易内部的主要结构是:google

  • to - 另外一个帐户,表明交易的发送对象
  • value - 交易携带的 ether 数量
  • data - 交易携带的任意信息

交易更改状态的方法是,EVM 找到交易发送的目标帐户:

1.根据交易的 value 计算目标帐户的新余额;
2.将交易携带的 data 做为参数传递给目标帐户的智能合约,运行智能合约的逻辑,在运行中可能会修改任意帐户的内部状态生成新的状态;
3.构造新的叶子存放新的状态,更新状态 Merkle 树。

clipboard.png

能够看出,Ethereum 的历史和交易结构与 Bitcoin 相比有很是大的不一样。Ethereum 的状态是由帐户构成的,而交易是由触发帐户变更的信息构成,状态和交易中记录的是彻底不一样类型的数据,两者之间没有超集和子集的关系,历史和状态所包含的数据类型是两个维度的,交易历史大小与状态大小之间没有必然的联系。交易修改状态后,不只会产生新的状态(图中实线框的叶子),并且会留下旧的状态(图中虚线框的叶子)成为历史状态,所以 Ethereum 的历史不只仅包含交易,还包含历史状态。由于历史和状态属于不一样的维度,Ethereum 区块头中不只仅包含交易的 merkle root,也须要显式包含状态的 merkle root。(思考题:EOS 使用了相似 Ethereum 的帐户模型,却没有在区块头中包含状态的 Merkle Tree Root,这是好仍是很差?)

Ethereum 中每个区块,每个帐户都会持续占用节点的存储空间。Ethereum 节点在同步的时候有多种模式,在 Archive 模式下全部的历史和状态都会保存下来,其中历史包括历史交易和历史状态,全部数据加起来的大小超过了 2TB;在 Default 模式下,历史状态会被裁剪掉,本地只保留历史交易和当前状态,全部数据加起来大约是 170G,其中交易历史大小是 150G,当前状态大小是 10G。Ethereum 中全部的开销管理都被统一到 gas 计费模型之下,交易的大小须要消耗对应的 gas,而每一条 EVM 指令消耗的 gas,不只考虑了计算开销,也将存储开销考虑在内。经过每一个区块的 gaslimit,间接限制了历史和状态的增加速度。

ps. 常见的一个误解是:Ethereum 的「区块链大小」已经超过 1T 了。从上面的分析咱们能够看到,「区块链大小」是一个很是模糊的定义,若是把历史状态算进去,它确实超过了,可是对于全节点来讲,把历史状态删掉没有任何问题,由于只要有 Genesis 和交易历史,任意时刻的历史状态均可以从新被计算出来(不考虑计算须要的时间)。真正有意义的数据,是全节点必须的数据的大小,Bitcoin 是 200G,Ethereum 是 170G,二者是基本相同的,并且在平均配置的云主机上都能装下,所以人们观察到的 Ethereum 全节点减小 并非因为存储增长致使的(根本缘由是同步时的计算开销,这里不展开了)。考虑到 Ethereum 的历史长度(当前区块的 timestamp 减去 genesis 的 timestamp)不到 Bitcoin 的一半,能够看出 Ethereum 的历史和状态大小增加更快。

The Tragedy of (Storage) Commons:区块链版本的公地悲剧
公地悲剧所指的是这样一种状况,有限的共享资源在不受任何使用限制的状况下会被人们过分消耗。区块链节点为保存历史和状态付出的存储,正是这样一种共享资源。

区块链节点为处理交易所花费的资源有三种,CPU、存储和网络带宽。CPU 和带宽都是每一个区块会刷新的资源,咱们能够认为每一个区块间隔内都有一样多的 CPU 和带宽可供使用,上个区块消耗掉的 CPU 和带宽不会让下个区块可用的 CPU 和带宽变少。对于可刷新的资源,咱们能够经过一次性支付的交易手续费来补偿节点。

与 CPU 和带宽不一样,存储是一种占用资源,在一个区块中被占用了的存储,除非使用者主动释放,不然没法在后面的区块中被其它使用者使用。节点须要为存储持续的付出成本,而使用者却不须要为存储持续的支付手续费(记住交易手续费只须要支付一次)。使用者只须要在往区块链写数据的时候支付一点点手续费,就能够永久使用一个可用性超过 Amazon S3 的存储,其无限大的永久存储成本须要区块链网络中的全部全节点来承担。

Ethereum 上因为各类 DApp 的存在,The Tragedy of (Storage) Commons 相对更加严重。例如,在区块 5700001(May 30, 2018)的时候,使用状态最多的 5 个合约是:

1.EtherDelta, 5.09%
2.IDEX, 4.17%
3.CryptoKitties, 3.05%
4.ENS, 1.92%
5.EOS Sale, 1.73%

比较有趣的是最后一个,EOS Sale。虽然 EOS 的众筹已经完成,EOS 代币已经在 EOS 链上流转,EOS 众筹的记录却永远留在了Ethereum 的节点上,消耗 Ethereum 全节点的存储资源。

能够看到,在缺少管理的状况下,区块链的存储资源会被有意或者无心的滥用。在一个设计合理的经济模型中,使用者必须承担存储占用的成本,这个成本不只仅与占用存储空间的大小成正比,还与占用时间的长度成正比。

状态爆炸

不管是历史仍是状态数据都会占用存储资源。经过上面对 Bitcoin 和 Ethereum 的分析(其余区块链的状态模型基本均可以概括为两者之一)能够看到,虽然它们对历史和状态的增加进行了管理,可是对历史和状态的总大小却没有任何控制,这些数据会持续无休止的累积下去,使得运行全节点须要的存储资源愈来愈大。提升全节点的运行门槛,使网络的去中心化程度愈来愈低,这是咱们不肯意看到的。

你也许会说,有没有可能硬件平均水平的提升会超过历史和状态的积累速度?个人回答是可能性很低:

clipboard.png

从这张图中咱们能够看到,随着 Ethereum 网络的发展,状态数据累积的数量呈指数式的增加。Bitcoin 的状态数据从 0 积累到 3G,用了 10 年;Ethereum 的状态数据从 0 积累到 10G,用了 4 年;而这是在咱们尚未解决 Scalability 问题,区块链仍然是小众技术的状况下的增加速度。当咱们解决了 Scalability 问题,区块链真正得到 mass adoption,DApp 和用户数量都爆炸式增加的时候,区块链历史和状态数据会以什么速度累积呢?

这就是状态爆炸问题,咱们把它归类为 post-scalability problem,由于它在解决 Scalability 问题以后会很是明显。咱们最先是在作许可链场景落地时注意到了这个问题,由于许可链的性能远高于公有链,恰好处于 post-scalability 的阶段。(思考题:许可链怎么解决状态爆炸问题?)

历史数据的累积相对容易处理,将来能够经过去中心化的 Checkpoint 或是零知识证实等技术来压缩,在那以前全节点甚至能够把历史直接丢掉,依然能够正常运行。状态数据的累积则麻烦许多,由于它是全节点运行必须的数据。

很多区块链项目已经看到了这个问题,并提出了一些解决方案。EOS RAM 是解决状态爆炸问题的一个有益尝试:RAM 表明了超级节点服务器可用的内存资源,不管是帐户、合约状态仍是代码,都须要占用必定的 RAM 才能运行。RAM 的设计也有不少问题,它须要经过内置的交易市场购买,不可转让,没法租用,将合约执行过程当中的短时间内存需求和合约状态的长期存储需求混在了一块儿,并且 RAM 的总量设定没有肯定的规则,更多取决于超级节点能够承受的硬件配置,而非共识空间的成本。

Ethereum 社区也看到了这个问题并提出了 Storage Rent 的方案:要求使用者为存储资源的使用预支付一笔租金,占用存储资源会持续消耗这笔租金,占用时间越长,使用者须要支付的租金越多。Storage Rent 方案存在两个问题:

1.预支付的租金终有一天会用完,这时候如何处理占用的状态?正是为解决这个问题,Storage Rent 须要诸如 resurrection 的机制来补充,增长了设计的复杂度,使智能合约的 immutability 大打折扣,也为使用体验带来了麻烦;

2.Ethereum 的状态模型是一种共享状态的模型,而不是 First-class State。以 ERC20 Token 为例,全部用户的资产记录都存放在单个 ERC20 合约的存储里面,在这种状况下,应该由谁来支付租金?

解决状态爆炸问题也是 Nervos CKB 的设计目标之一,为此 CKB 走了一条彻底不一样的、更为完全的变革之路。

原文连接:https://talk.nervos.org/t/top...

相关文章
相关标签/搜索