一文读懂代码默克尔化的最优分块大小
摘要
本文将介绍以下内容:
一个基于 TurboGeth 的操作码追踪器能够以每秒 28 个区块的速度处理并导出 EVM 层面的事务详情。
一个脚本可以根据事务执行的基本块计算出固定大小分块的默克尔化结果。
ConsenSys 的 TeamX 团队正在研究其它主题,因此我们希望这些工具能够对社区有用。
默克尔化:问题陈述
无状态以太坊的基本思路之一是使用了状态见证数据(witeness)。见证数据会为客户端提供足够的状态用以执行区块,无需更多状态信息。
见证消息需要尽可能小,因此减少见证消息的大小是当务之急。见证消息的大小主要受到合约代码的影响。这就是我们采用代码默克尔化的原因:使用多种不同的策略将合约代码拆分成较小的部分,这样就可以只将必要的部分包括在见证消息内。
这些代码块仍需由无状态客户端验证,因此它们会附带结构化数据(默克尔证明),让客户端能够确保其收到的代码与全状态中包含的代码相同。
合约行为、分块策略、默克尔证明和见证消息大小之间的相互作用让人难以决定什么是代码默克尔化的最佳方法。
在本文中,我们将探索最简单的分块策略:固定大小分块。所谓的固定大小分块,就是将合约拆分成固定大小的部分,其中唯一的变量就是分块大小。
鉴于 EVM 行为的特殊性,以及其它文章中的相关讨论(见文末《通过 EVM 代码默克尔化缩减见证数据大小》),这些分块可能还需要一些元数据,以便无状态客户端辨别分块中的一些字节码是否存在于完整合约的 PUSHDATA 内。
结果
请注意:开销结果是非常不可靠的。Python 工具存在缺陷,并且正在改进中。
我们已经在超过 50 万个主网交易区块(从第 900 万个区块向后)的痕迹上运行了我们自己的默克尔化脚本。我们得出的主要结论是:
现实世界交易的基本块(basic block)的,统计分布严重偏向小型基本块。
分块之间的距离越近,证明哈希的分摊程度越高/构建多重证明(multiproof)所必需的哈希值就越少。
以下开销数据都是在 50 万个区块上运行默克尔化流程得出的结果,但是在 1000 个区块上运行下来的结果相同,并且稳定在 ±1% 的范围内。
默克尔化开销取决于分块中包含的合约字节(即使这些合约字节没有使用),以及这些分块的多重证明的哈希值所使用的字节:
工具
基于 TurboGeth 的操作码追踪器
我们使用 Go 语言创建了一个基于 TurboGeth 的追踪器,该追踪器能够从 EVM 内的交易执行中提取任意跟踪数据。截至发稿时,TurboGeth 每秒可(在 AWSi3.xlarge 类型的实例上)处理超过 28 个区块;我们的追踪器显示了如何在收集跟踪数据时对其进行处理,并以最低的延迟提取这些数据,这主要取决于待导出数据的数量。这就意味着,在大约 12 小时内可以。
鉴于我们的追踪器与 TurboGeth 紧密连接,如果 TurboGeth 继续加快速度,我们的追踪器也会自动加快速度。
举一个具体的例子,探测 EVM 运行的连续代码段(即,基本块)以及将这些数据导出成记录各种元数据的 JSON 文件是以每秒 20 个区块以上的速度完成的。
这个速度,再加上部分流程是在一开始就完成的,减少了 “为以防万一” 而收集额外数据的需求。这样可以简化后续流程,并极大提高研究项目迭代的灵活性。
我们认为这部分代码依然是有用的,但是对于其它项目来说,自定义流程类型和导出数据类型是一个很好的切入点。追踪器已经整合进了 TurboGeth。
之前的方法由于数据收集缓慢,需要为中间步骤存储大量数据,从而产生使用门槛。相比之下,基于 TurboGeth 的追踪器能够有效降低准入壁垒。我们希望它可以促进以太坊研究项目更快速迭代,同时也让其它团队能够使用其结果。
当然了,我们希望你能仔细检查我们的结果。
实现主网交易痕迹默克尔化的脚本
我们编写了一个工具,使用操作码追踪器的输出和分块大小来计算每个区块的默克尔化代码见证数据,以此收集每笔交易、每个区块、每批交易和全局层面的统计数据。这些统计数据是我们了解现实世界交易的运行时特征的关键。
32 字节分块的输出如下所示:
Block 9904995: segs=816 median=14 2▁▃▁▃▅▄▆█▂▂▄▂▄26 (+29% more)Block 9904995: overhead=42.4% exec=12K merklization= 16.6 K = 15.6 + 1.0 KBlock 9904996: segs=8177 median=18 2▁▂▁▄█▄▆▄▃▅▃▃▄▁▂▂▂34 (+24% more)Block 9904996: overhead=34.1% exec=101K merklization= 136.0 K = 135.1 + 0.9 KBlock 9904997: segs=14765 median=14 2▁▂▂▄█▃▆▄▃▃▂▄▂26 (+25% more)Block 9904997: overhead=31.5% exec=106K merklization= 139.8 K = 139.0 + 0.8 KBlock 9904998: segs=20107 median=14 2▁▁▂▄▅▄█▃▃▄▂▂▃26 (+27% more)Block 9904998: overhead=36.4% exec=76K merklization= 103.8 K = 103.0 + 0.8 KBlock 9904999: segs=10617 median=12 2▁▂▃▅▆▅█▄▃▃▂22 (+29% more)Block 9904999: overhead=37.4% exec=59K merklization= 81.4 K = 80.8 + 0.7 Kfile /data/traces2/segments-9904000.json.gz: overhead=32.1% exec=74493K merklization=98368.4K = 97481.3 K chunks + 887.1 K hashes segment sizes:median=14 2▁▁▂▆█▆█▆▄▄▃▃▃26 (+28% more)running total: blocks=905000 overhead=30.8% exec=65191590K merklization=85278335.0K = 84535640.5 K chunks + 742694.5 K hashes estimated median:15.0该脚本假定的是普通的默克尔树,其参数数量和哈希大小都是可配置的,但是没有黄皮书中所定义的特殊节点(分支节点、扩展节点等)。
很期待看到社区将如何使用这些工具!
致谢
非常感谢 AlexeyAkhunov 及 TurboGeth 团队中其他成员的远见卓识和辛勤付出。没有他们,这一切都是不可能发生的!
微信扫描关注公众号,及时掌握新动向
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场