作者:Shreemoy Mishra

来源:https://medium.com/iovlabs-innovation-stories/a-primer-on-blockchain-storage-economics-b65b242c6ec0

译者注:该文作者来自 RSK 侧链团队,其中大部分内容都与比特币无关,而跟 RSK 以及 RSK 的借鉴对象以太坊有关。读者可以将此视为对以太坊和 RSK 的协议设计的介绍,并洞见比特币与以太坊这样的区块链在设计上的核心差异。

在以太坊上,合约的代码和状态都是存储在链上的;虽然它因此获得了合约可相互访问、调用的好处,却也产生了所谓的 “状态数据膨胀” 问题,即随着用户和合约数量的增加,状态数据的体积会不断增大,导致全节点的负担越来越重。

而比特币则一开始就禁止了合约的相互访问,并鼓励用户尽可能减少链上存储(比如 P2SH 格式,实际上就是用户的智能合约的承诺,在链上仅表现为一条哈希值,在合约实际使用时才曝光内容)。

这种差异,绝非仅仅只是数据存储的模式这么简单。协议设计上的取舍,是一环扣一环的。正如我在文章中的注说的那样,由于状态膨胀问题的存在,以太坊也不得不持续地调整 EVM 状态读写操作码的 gas 消耗量(名义定价),以免与实情偏离过大。但是,这也使得这种链的治理不可避免地趋向中心化。因为操作码的定价绝不是一个科学可以解决的争议。

实际上,我也对作者的最后一部分相当不满,他暗示了这个问题是一个可扩展性问题,即认为存储问题最终只影响用户的交易费,只要交易费能够靠一些方法降低,就不是问题。但实际上,这是个资源使用和定价的问题。当资源被不合理使用时,即使该资源再丰富,也一样是浪费。

作为比特币人,我们要做的是深入理解这些差异,如此才能在技术的发展中,为比特币的设计抉择做出持续而有力的辩护。

img

大部分的网页和手机应用都依赖于云存储解决方案。存储的成本由开发者和企业负担,具体的数额由依赖于多个因素的第三方定价方案决定,这些因素包括存储数据的量级、持续时间、带宽要求、时延、安全性、同步和分布式设计,等等。这些也是合同规定的内容,服务质量的保证。

但是在 免信任、点对点的区块链网络 (比如 比特币以太坊RSK)中,情形大不相同。本文将讨论在区块链中存储数据的一些基本的 工程-经济学。

存储区块 vs. 存储状态

在考虑像以太坊这样的区块链的存储成本时,我们需要区分历史 交易 数据(以 区块 的形式存储)和 状态数据。“状态” 指的是,在给定的任一时间点,区块链上账户的 余额智能合约的代码(即 去中心化应用 的引擎)以及为使用这些应用而存储在链上的数据的 “快照”。通常来说,状态指的是 当前的 状态(最新的状态)。因为区块链网络中 没有公开可参考的时钟,所以当我们要指代 以前 的状态时,通常会说那是 某个区块高度 的状态。

使用去中心化应用需要与智能合约交互。这个 “交互” 的含义是:

  • 一笔来自某个用户账户的交易……
  • 通常靠用户的密码学货币 钱包软件网页插件或手机应用)发起……
  • 发往某个智能合约 —— 由链上的 合约地址 来标识……
  • 带有一些请求合约来处理的 数据

大部分交易都包含读取和修改一些状态数据的操作,而这些变化必须在交互过程中 持久化。不再需要的合约以及合约专属数据可以从状态中移除。甚至有些项目会设立经济激励来鼓励用户这么做,以减少状态数据的体积。

任一时间点的状态,实际上都是该点以前所有历史交易的 净结果。一个新的区块链节点启动时,它要重新创建出区块链的 当前状态,办法就是从(更老的)对等节点那里下载区块并执行(即 重放并验证)从区块 0 开始的所有区块中的交易。这个初次同步的过程可能耗时数个小时甚至数天,取决于你的带宽和处理器性能。离线较长一段时间的节点,在重新上线时也要做类似的事情,但可以从断开的地方开始 —— 即,从它下线时候的状态快照开始。

不论是从头开始同步,还是就近追赶,重新执行所有前序区块的过程是 按顺序的 —— 比特币和 RSK 的区块链本质使我们无法并行执行交易(译者注:此说有误,作为区块链,当然只能按顺序执行区块;但像比特币这样的区块链,在执行一个区块时,其内部的交易大部分是可以并行执行的,因为比特币的状态是相互独立的,只有一笔交易的结果在该区块中继续被使用,才只能顺序执行,而这是少数情形,大部分情况下,是可以并行执行的。像以太坊这样的区块链,才大概率无法并行执行,因为其状态不是相互独立的,而是可以互相访问的)。尝试并行执行可能会失败,更糟糕的是 —— 可能导致状态变更与全网不一致。不管怎么说,区块链的用意正是为了让互不信任的对等节点 可以对历史交易的排序达成共识

区块和交易数据通常根据链的协议所指定的方式来编码,然后使用键值对数据库存储在硬盘中。不过,状态数据,要使用叫做 “tries” 的树状抽象数据结构来存储。以太坊使用每个节点可以有 16 个分支的树状结构(十六叉树),而 RSK 使用二叉树。两者的详细比较可见这篇文章。状态数据存储在树状结构的 叶子节点 中。用来存储专属于某个智能合约的数据的叶子节点叫做 “Storage Cells”。整个状态树可能以编码形式或扩展形式存储,不同客户端实现(例如 GethTurbo-GethRSKJ)各有不同。但最终来说,跟交易数据一样,它们是以键值对数据库来备份的。

以太坊的区块链 历史(区块、交易、交易收据)的累计体积超过 1 TB(译者注:不同的客户端实现取得的结果不同)。我的笔记本电脑的 512 GB 的固态硬盘根本就塞不下 —— 需要一块外置硬盘。以太坊的 当前状态 体积小一些,不到 100 GB。虽然我的电脑的内部硬盘可以塞得下,但它对于我的内存(8 GB)来说还是太大了。存储上的要求会对节点和网络的表现有很大的影响。RSK 区块链比以太坊更年轻,体积也更小。但即便如此,存储要求也不低。

存储的隐形和显性成本

在以太坊和 RSK 中,在区块链上存储数据的代价 —— 通过 交易费 的形式向 用户 收取 —— 几乎完全跟存储和修改状态有关,跟存储历史交易数据无关。当然,那些 运行 区块链的节点必须负担存储状态和存储(大得多的)历史两项成本。

没有状态数据,我们就无法处理交易。即使只是处理从 Marina 的账户向 Celia 的账户的一笔简单转账,我们也要知道 Marina 的 当前余额。她的过往交易的记录对此并没有直接的帮助。在以太坊这样的设定中,运行区块链客户端的节点必须保证尽可能多的状态可以在内存中可用。这跟比特币不同,比特币的交易是 使用前序交易作为输入 建构出来的!比特币中其实没有 “账户” 和 “余额” 的概念。实际上,有人认为,比特币中 根本就没有状态 —— 只有带有 “剩余资金” 的交易,即 未花过的交易输出(UTXO)。熟悉中本聪的白皮书的人可能会想起,避免比特币的 根本安全问题 —— 多重支付攻击 —— 的唯一办法就是 知晓所有的前序交易。其实,所有的区块链也都是如此。为了最大限度地安全性,运行客户端软件的全节点必须保存历史交易的完整集合。

随着用户、账户和合约数量的增加,在内存中存储所有的状态数据会变得不可能。想一想,现在的状态数据的体积是 50GB。假设我安排电脑的 5 GB 内存用于存储状态,也只够存储 10%。那么,90% 的时间,一些交易要执行的状态数据 都不在 内存中。我们需要在硬盘中查找和检索。相关的硬盘读写时延会降低处理速度,而且这种降速会随着状态数据的 持续增长 越演越烈。读写时延也可以成为恶意人士的 拒绝服务式攻击(DoS) 的目标。读取 受阻和交易 无法并行处理(至少现在是没办法)让事情变得更糟。

读写的时延是 存储区块链数据的一项隐性成本 —— 开发者可以使用工具来追踪这个性能成本。一些以太坊的开发者试图限制他们的智能合约访问过多的状态数据。他们想用以更高的手续费(额外的计算)换取更低的读写时延。不过,大部分开发者,要更为属性存储数据的 显性成本。如前所述,这些成本主要由状态数据决定 —— 跟用来存储过往交易的存储无关。

智能合约是在以太坊虚拟机(EVM)的环境下执行的。构成代码的单个指令叫做 EVM 操作码。每个 EVM 操作码都有计算开销,以 gas 为计量单位。一笔交易所需的所有操作的总(gas)开销决定了该交易的交易费。矿工(或者叫区块生产者)可以得到手续费作为运行计算的补偿。在广播一笔(得到密码学签名的)交易到网络中时,用户(即 交易的发送者)必须指定自己愿意制度多少手续费。这个叫做一笔交易的 gaslimit。在交易执行完之后,剩余的 gas (的经济价值)会返还给发送者(译者注:比如你愿意为一笔交易使用 100 gas,每 gas 愿意付 3 元,那么你在交易起始之前,必须保证账户里有 300 元;但交易实际上只花费了 80 gas,那么你只需付出 240 元,剩余的 60 元会留在你的账户里)。

存储或修改智能合约的存储节点(上文所谓的 Storage Cells)里的数据,需要使用一个叫做 SSTORE 的 EVM 操作码。每个 Storage cell 可以存储 32 字节的数据。而 SSTORE 的 gas 开销取决于 它的用途:是创建一个新的 cell、还是重设一个值,还是清空数据。

用来创建一个新的 cell 时,SSTORE 操作码要消耗 20000 gas。但用来删除数据(将一个非零的值变为零)时,可以返还 15000 gas。更新 以前存储的(非零)值为一个新的非零值,则需要消耗 5000 gas。

这些是 写入 数据到区块链状态中的成本 —— 那 读取 呢? SLOAD 操作码是用来从一个合约的 storage cell 中读取数据的,每次读取要消耗 200 gas。读取 一个账户的余额(使用 BALANCE )消耗 400 gas。

这些值是 RSK 区块链的设定。(译者注:作者的意思是,这不同于以太坊的设定。但还可以让读者联想到另外一个意思:以太坊操作码的 Gas 开销,包括作者在这里提到的几个,都已经改变(主要是上升)过多次了,其核心原因,在于随着状态数据体积的增大,节点读写状态数据的实际成本增加,Gas 开销作为名义成本也必须提高,否则将诱使用户更倾向于写入状态数据,甚至导致节点被 DoS 攻击。这是由 gas 这种一元的名义开销体系的本性所决定的,纵是 RSK ,也有可能在未来改变这些操作码的 gas 开销。)

当然,这些操作码的细节和 gas 开销对普通用户是不可见的 —— 他们只能发现,交易的手续费会因为操作的复杂性增加而增加 —— 更多交互就要花掉更多的手续费。

Gas 消耗量一开始是以太坊开发者根据不同操作的执行时间(例如以纳秒计)来校准的。这是 工程开销 或者叫计算资源开销。随着区块链软件以及硬件的发展,这些操作的工程成本会 偏离 最初的基准。因此,这些参考值需要不断地 重新校准。一个例子是以太坊最近接受的一个提案,EIP-1884,它改变了一些操作的 Gas 消耗量。例如,在以太坊中, SLOAD 现在要消耗 800 Gas(原来是 200 Gas),而检查余额现在需要 700 Gas(原来是 400)。(译者注:该文撰写于 2021 年 2 月,在 2021 年 4 月,以太坊 “柏林” 分叉又进一步提高了这些操作码的 gas 开销。)

从工程成本推理出实际的经济和业务成本,还需通过 “自由市场” 经济学。第一个元素就是 gasprice ,它是每单位 gas 以区块链原生的密码货币(比如 ETH 和 BTC)来计算的价格。第二个乘数更明显 —— 原生密码货币与法币(比如美元)的汇率。

事实是,ETH 和比特币的美元汇率每天都在变化,就像太阳升起一般。不过,有时候人们还是会惊讶于 gas price 的波动

Gas price 的变化是因为交易要争取进入的 区块空间有限。区块空间有限,是为了保证区块有规律生产且不会给节点施加太多的计算负担。不过,区块链协议不是直接限制交易的数量,区块的大小是以一个区块能消耗多少 gas 数量来表示的。当前,以太坊的区块 Gas 限制是 1250 万。(译者注:在翻译之时,比特币的区块大小以 vB(虚拟字节)或者叫重量来表达,比特币区块所包含交易的 vB 总计不能超过 400 万。)

但是,关键 在于,用户(即交易发送者)设定自己的 gas price 出价 —— 而区块生产者(矿工)根据 计算成本相同时用户出价高的 来排序、打包交易。

这种用户之间追求让自己的交易尽快打包到区块中的 “市场竞争” ,让 gas price 的决定 某种程度上类似于有限的区块空间的拍卖。以太坊生态有一个(备受争议的)提案主张改变 gas price 的产生方式。但那是另一个话题了。

欺骗市场:Gas 套利

为任何资源有限的市场 引入经济激励,都有可能带来 意料之外的副作用外部性。一个例子是 “固定的” 工程成本(以 gas 计)和 “变化的” 经济成本之间的互动:gas price 和汇率。

两个 EVM 操作码 —— SSTORESELF-DESTRUCT (删除一个已经部署的合约)—— 都可以获得 Gas 返还,因为它们从区块链状态中删除了已经没用的信息。这种返还其实是 gas 补贴,一开始的设计用意是鼓励开发者减少状态体积。这些返还不是以货币的形式支付的,它们更像是 优惠券,只能用来 部分抵消 交易手续费,并且只能在一笔交易执行的最终环节使用。

不过,一些用户还是利用了这种激励机制来实现一种 “gas 银行”(例如,gastoken)。他们 有策略地 在 gas price 较低的时候在链上存储状态,然后在 gas price 很高的时候删除(早先存储的)数据以获得 gas 返还并在交易中使用。这种行为叫做 “gas 套利”,并且它是不受人欢迎的,因为它会导致状态数据膨胀。在以太坊生态中,这种模式的猖獗使用已经导致有提案主张完全移除 Gas 返还机制。

(译者注:在 2021 年 8 月,以太坊的 “伦敦” 分叉已经极大地削弱了 Gas 返还机制,理由正是作者说的这个。Gas 返还机制非但没有起到激励减少状态数据的效果,反而加剧了状态数据的膨胀。)

存储成本核算

每一个运行区块链全节点的用户都必须承担存储成本 —— 包括带宽、存储设备和硬盘读写。交易 发送者 以交易费的一部分支付了存储成本。但另一方面,是区块链的 矿工 获得了一个区块相关的交易费。在 RSK 区块链上,交易费事他们 唯一 的收入来源。在以太坊上,矿工还可以获得区块奖励。甚至于,在以太坊上,最近交易费也已经成为了收入的主要来源。

那么 不挖矿 的全节点呢?他们无法获得任何交易费。

专业的节点运营者 —— 比如 密码学货币交易所商家断言机和其它服务提供商 —— 可以使用大量的内存和硬盘来运行节点。他们中的某一些甚至可以提供 存档 服务 —— 存储区块链在多个时间点的多个快照。这使得他们能 服务复杂的查询请求(比如查询一个账户的历史余额或义者合约的历史状态)。作为服务商,他们有收入流来负担这些运营成本。

而运行全节点的 个人 并不能得到 任何 补偿 —— 他们运行全节点可能是出于利他心或者安全考虑 —— 连接到一个全节点的钱包可以提供最高的安全性。减少存储成本为个人节点提供了最多好处,可以鼓励更多用户运行全节点。

以太坊的现状

DeFi 运动的早期支持者曾想象,那些创新会为 “大众” 提供极为便宜的价值转移和金融服务。然而,现在在以太坊上,即使只是原生货币的简单转账,也要花上 8 美元(更别说 token 转账了)。这是因为,虽然执行交易的工程成本没有改变许多,整个经济模型已经发生了剧烈改变。

现在 ETH 已接近 1800 美元,而 gasprice 在 200 gwei(1 gwei 为 1 ETH 的十亿分之一) —— 每个存储额外数据的 SSTORE 操作都要支付约 8 美元。存储 32 字节!就要 8 美元!修改现有的一个值(使用同一个操作码)要便宜 4 倍。修改 32 字节就要花费 2 美元不是什么值得高兴的事情。一笔 ERC20 的 token 转账包含一次余额检查(400 gas)、两次余额更新(每次 5000 gas)、基本的交易手续费(21000 gas),以及与该 token 所属的智能合约交互所需付出的额外费用。以 gas 计,这些操作总共要消耗 37000 gas —— 按当前的价格,约为 13 美元 —— 一次简单的 token 转账哦!

RSK 的现状

以当前的 gas price 计,在 RSK 上的简单转账要支付 0.05 美元的交易费。并不便宜 —— 但是,比特币的美元汇率已超过 40000,而 RSK 的原生通货是跟比特币 1:1 锚定的。不过,8 比 0.05,RSK 的支付比以太坊便宜 160 倍。

回到存储成本不提,在 RSK 上,一个 SSTORE 操作码也要 0.05 美元。RSK 与以太坊相比,跟去中心化应用交互的所有成本几乎都存在这种 1:160 的的成本优势。

RSK 社区有许多提议和研究项目来优化区块链存储的经济模型。举个例子,RSKIP-215 提议 使用 共识决定的状态检查点。一旦同步完成,节点就可以根据状态检查点来 修剪 旧的区块和交易。另一个提议 —— 状态访问租金 —— 研究了围绕 时间维度 来实现状态 访问手续费、激励用户善用存储资源的可能性。

在以太坊社区中,也有一些被归类为 可扩展性方案 的提议 —— 可以将成百上千笔交易下放到 二层网络 来执行。这可以极大地减少存储资源的使用,也可以降低交易费。这些方法在 RSK 社区中也有活跃的研究。除了这些举措,社区还在开发集成创新解决方案:利用像 IPFSSwarm 这样的技术,为 任意内容 提供受激励的、类似于 torrent 的去中心化存储 —— 例如,RIF Storage

(完)