引介 | EVM 字节码的默克尔化
作者:Sina Mahmoodi
编译:Unitimes_David
摘要:无状态客户端需要将区块中调用的智能合约代码作为区块见证的一部分进行发送。合约代码是导致无状态区块带宽开销的第二大因素。代码默克尔化被认为有助于降低该开销。本文详细解释了我们如何将合约代码分割成块,默克尔化这些块(笔者注:指的是让这些块组成一棵默克尔树)并仅传输交易执行所必须的块。根据对最近主网区块所做的实验,我们可以发现该方法总计节约了 40-60% 的代码传输量。
无状态区块很大
虽然未被深入研究过,代码默克尔化这个想法由来已久,其主要被用于代码解耦。然而,它最近因不同用途而重获新生,即减小无状态客户端的带宽需求(如本文[1])。如果你想知道无状态客户端[2]背后的动机是什么,我建议你看看最近这篇概要[3]或者 Alexey Akhunov 的文章[4](文中发布了其实验数据)。我不会在本文深入模型细节,但为了完整起见,我提供了相关细节的摘要(如果你熟悉的话,你可以直接跳到下一段)。
在无状态模型下,(至少有些)节点不需要存储状态,并依赖其它节点(如矿工)在区块中打包所有必要状态(包括合约代码)及证明这些状态有效性的默克尔证明。这意味着和原来相比大得多的网络带宽。Alexey Akhunov 与 turbo-geth[5] 团队一直在做测量历史主网区块的区块见证大小的实验。下面是最近 50,000 个区块的测量结果。红线跟踪在一个无状态区块中需要发送的合约代码量,其为区块见证大小的第二大来源。如果以太坊从当前的十六进制 trie 树迁移到二进制 trie 树的话,这些见证中的哈希部分将会缩减约3倍,从而使得合约代码成为见证大小的主要来源。
来自github[6]的数据。图表显示了50000个近期主网区块的无状态区块见证组成。这些值是以128个块为窗口的移动平均值。
无需发送完整代码
直观地,我们可以假设一个给定的交易将仅仅触及其调用合约的部分代码(如4个函数中的2个)。因此,我们的目标是把代码分割成块并在区块见证中发送给定交易所必须的块(加上块有效证明)。如果我们的假设是正确的而且交易确实仅使用了小部分合约字节码(剧透:早期数据表明情况确实如此),那么区块见证中的合约代码部分会显著减少。
为了确切地了解其原理,让我们想象一个正在部署的新合约。我们扫描合约代码并识别出基本块[7](见算法[8])(笔者注,基本块可以理解为按序执行的一段代码片段)。注意,客户端仅需为JUMPDEST分析作一次代码扫描,因此不会引入很高的开销。这些基本块有两个特征:
一个虚构字节码的基本块
每个基本块要么从索引0开始,要么从JUMPDEST开始。这是为了让无状态客户端能够安全地进行JUMPDEST分析(稍后将更为详细地介绍)。