【Gas优化】GasChecker

💡 本次解读的文章是2020年发表于 IEEE Transactions on Emerging Topics in Computing 的智能合约 Gas 优化论文,论文总结了十种 Gas 低效(gas-inefficient)的编程模式,并提出了一种新的基于符号执行(SE)的方法 GasChecker 来检测智能合约中的字节码。另外,本文将 SE 并行化,将其裁剪为 MapReduce 编程模型,并提出了一种新的基于反馈的负载均衡策略,提高了方法的可扩展性。

1、本文贡献

本文从四个方面对 Under-optimized smart contracts devour your money [ 1 ] [1] [1] 这篇论文的工作进行了广泛的扩展,具体表现为如下四点贡献:

(1)提出十种 gas-inefficient (gas 低效)编程模式;
(2)实现 GasChecker,其基于 SE 来检测智能合约字节码中 gas 低效模式;
(3)设计了基于反馈的负载均衡策略(FBLB),提高了自动化分析资源利用率;
(4)将 GasChecker 应用于已部署的智能合约,并进行了大规模的实证研究。

2、十种 Gas 低效编程模式

P1: Opaque Predicate

“不透明的谓词”模式是指总是产生一种结果(True or False)的比较,这种模式可以通过删除比较以及不可行分支上的代码来实现优化。

P2: Dead Code

"死代码"模式是指运行时无法达到的代码,这种模式可以通过删除实际无法运行的代码来实现优化。

P1 与 P2 的区别

P1 与 P2 经常一起出现,但不完全相同,P1 是指一个总是产生相同结果的比较,而 P2 是实际中无法执行的代码。需要注意的是:1)P1 不一定会导致 P2;比如图中分支块3存在 P1 模式,即分支块3到分支块5是不可行分支,说明此处的判断结果永远只有一个,即指向分支块1,但是这里不存在 P2 模式,因为分支块5有可能被运行。2)P2 不一定由 P1 造成的;比如函数未被调用,或返回语句后存在代码。

P3: Expensive Operations in a Loop

"一个循环中进行昂贵操作"模式是指在循环代码片段中存在 Gas 消耗较大的操作,可以将昂贵的操作移到外循环,以此实现代码优化。但一般优化后的字节码大小会增加,从而增加部署智能合约的 Gas。

Example

从图 a 可知合约声明一个全局变量 num,与传统应用不同,智能合约中的全局变量存储在 storage 中,由于访问 storage 需要花费昂贵的 gas,因此,反复读写一个循环(第5行)需要消耗大量的 gas。相比之下,图 b 的合约将 num 读入一个局部变量 y,然后在循环中增加 y,最后将 y 写回 num,访问存储的次数从 2x (在循环的每一次迭代中, 一次读一次写)减少到2,减少了 gas 的消耗。

P4: Fusible Loops

“可熔循环”模式是指存在两个循环可以在不改变程序行为的前提下合并为一个循环,即存在可融合的循环。循环融合起到优化作用的原因是:1)字节码的缩减可以减少部署智能合约的 gas;2)执行指令的减少可以减少调用智能合约的 gas;3)可以对合并后的回路本身进行其他优化(如公共子表达式消除),从而进一步降低 gas 消耗。

P5: Repeated Computation in a Loop

“循环重复计算”模式是指在循环的每次迭代中产生相同结果的计算,这种模式可以通过将计算移到循环外,并使用一个临时变量来保持结果,从而优化合约。

P6: Unilateral Comparison in a Loop

"一次循环中的单边比较"模式是指在一个循环中进行比较,其结果是固定的,需要注意的是,这种模式与 P1 模式是不同的,因为在不同的语境下比较会产生不同的结果。这种模式可以通过将单边比较移至循环外并复制循环体进行优化。

P7: Redundant SSTORE

“冗余的SSTORE”模式是指定义(即 SSTORE)后从未使用过(例如 SLOAD)的 storage,SSTORE的 gas 消耗成本较高,且冗余的 SSTORE 操作会浪费磁盘空间和大量资金。需要注意的是,一个 storage 可以由一个事务写入,然后由后续事务读取,因此需要进行多路径分析(multi-path program analysis)来实现优化。

P8: SWAP1/DUP2/SWAP1

“SWAP1/DUP2/SWAP1”模式是指当前操作顺序存在多余的消耗,可以利用能达到同样效果的 DUP1/SWAP2 操作序列进行替换,这种替换将有利于减少 gas 的消耗。

P9: PUSHx/POP

“PUSHx/POP”模式是指依次顺序执行一次 PUSH 和一次 POP 操作后,栈中没有发生任何变化,因此,移除操作序列 PUSHx/POP 可以有效节省 gas 的消耗,从而实现优化。

P10: PUSH1/NOT

“PUSH1/NOT”模式是指依次顺序执行一次 PUSH 和一次 NOT 操作,这种方式可以通过直接 PUSH1 一个在非 gas 模式中的位非来代替非 PUSH1/NOT 操作,以此实现优化。

3、基于 SE 的 GasChecker

本文的 GasChecker 方法是基于符号执行(Symbolic Execution,SE)的,使用 SE 的原因是:

  • SE 是一种多路径程序分析技术,有利于 P7 的检测;
  • 基于理论证明器(Z3),SE 能判断两个表达式是否等价,有利于 P4 和 P5 的检测;
  • SE 能够确定路径可行性,有利于 P1、P2 和 P6 的检测;
  • SE 能够识别一些困扰标准编译器技术的控制流转移(control flow transfers)。

上图显示的是基于 SE 的 GasChecker 的结构图,其中包括:

  • 一个 Master:用于运行驱动程序(Driver procedure)
  • 多个Mapper:用于运行 Map 任务(Map tasks)
  • 多个Reducer:用于运行 Reduce 任务(Reduce tasks)

注意:Mappers 和 Reducers 统称 workers,且运行过程中至少存在一个 workers。

3.1 功能简介

(1)GasChecker 的功能

接收一个智能合约并生成一个报告,报告包含分析智能合约中发现的 gas-inefficient 模式的位置。

(2)Master 的功能

首先对字节码进行反汇编,并从中构建控制流图(CFG),之后,启动多个 Map 任务和 Reduce 任务,GasChecker 迭代运行 Map 任务和 Reduce 任务,直到所有程序路径被确定或超时。Master 利用 Reducer 收集运行信息(包括表达式的值、执行的基本块等),以此检测 gas-inefficient 模式和性能信息(例如状态恢复的时间消耗),基于此来动态改善负载均衡。

(3)Mappers 的功能

接收 Map 任务,Mappers 首先恢复状态,明确从哪条路径探索开始,并符号化地运行 CFG(如果有新任务产生,则继续运行)。之后将产生的新任务编码在位向量中,以便通过减少网络通信来提高并行度。另外,每个 Mapper 中的监视器记录运行时信息和性能信息。

(4)Reducers 的功能

接收 Reduce 任务,将 Map 任务输出的运行时信息(例如表达式的值,执行的基本块)和性能信息合并成 Master 可以轻松处理的表单,然后将结果返回给 Master。比如,当两个 Mapper 的任务输出分别为 ( P 1 , i n f o 1 ) (P1, info1) (P1,info1) ( P 2 , i n f o 2 ) (P2, info2) (P2,info2) 时,其中 P P P 表示编码路径前缀(encoded path prefix), i n f o info info 表示执行 SE 时收集到的信息,则 Reducer 将它们整合成 ( { P 1 , P 2 } , { i n f o 1 , i n f o 2 } ) (\{P1, P2\}, \{info1, info2\}) ({P1,P2},{info1,info2}) 并返回给 Master。Master 接收到后,将 { P 1 , P 2 } \{P1, P2\} {P1,P2} 用于产生新的 Map 任务,将 { i n f o 1 , i n f o 2 } \{info1, info2\} {info1,info2} 用于动态改进负载均衡。

3.2 具体实现

在具体实现上,本文省略了对标准 SE 引擎内部细节的介绍,即在整个 GasChecker 框架中,反编译部分、CFG 生成部分以及 SE block (符号化地执行基本块)这些部分将重用现有的序列 SE 工具 —— OYENTE [ 2 ] [2] [2],而不进行详细地描述,下面是论文对整个系统的核心运行要点进行说明和描述。

(1)任务编码与状态恢复

这部分主要在说明如何对 Map 任务进行表示,以及 Mapper 接收到 Map 任务后,如何基于当前任务选择未探索路径的开始位置。具体来说,在 Mapper 接收来自 Master 的 Map 任务之后,通过状态信息(包括所有的必要信息,如程序计数器,所有变量的符号表达式,执行分支的约束),恢复到某一路径的开始位置来执行,当遇到条件跳转(JUMPI)时,分叉新状态,选择未探索状态执行,在 Map 任务结束后输出此次执行中选择的未探索状态。

在这个过程中,考虑到网络资源和 worker 数量的可扩展性等问题,本文将 Map 任务编码成位向量表示,以位向量表示路径前缀,Mapper 根据路径前缀信息,恢复到此次符号执行的路径开始位置。比如,现在 Master 向 Mapper 发送了一个 Map 任务为 0101,则意味着 Mapper 需要在符号化执行时,将遇到的前四个分支设置为 False、True、False、True(状态恢复),并将第四个分支之后的路径作为开始路径,之后从这个开始路径位置执行本次的探索。

(2)Map 任务(Map Task)

Map 任务主要包含路径前缀的状态恢复和 CFG 探索外。在 CFG 探索的过程中,需要接受 Master 的一个参数 S N SN SN,该参数表示 Map 任务要执行的 EVM 操作的数量,通过改变 S N SN SN,Master 可以调整 Mapper 需要完成的工作量。因此,Map 任务的整体过程是,首先,运行一个监控器(monitor),用于收集运行时的信息和性能信息,并基于路径前缀进行状态恢复,之后开始路径探索,直到所有路径都被探索或达到指定数量 S N SN SN 的任务即可。另外,当达到一个条件跳转(即JUMPI)时,如果可以取一条分支,则会产生一个新的状态,并在路径条件中添加一个新的约束,存储在新的状态中。Mapper 将选择一个未被探索的状态用于后续的符号执行(执行新的基本块,即新任务)。这里的路径选择可以使用相关路径探索算法(例如,深度优先搜索、随机搜索等),都可以在 Select_state() 中实现,以确定状态的执行优先级,本文选择使用深度优先搜索。

注意:即使执行的操作数量超过指定的任务量,Mapper 也不会立即停止执行EVM操作,而是直到执行了终止智能合约的操作(例如,JUMPI 或 STOP),即需要确保每个基本块都被完整的执行。

(3)基于反馈的负载均衡策略(FBLB)

由于 Mapper 之间相互独立且不能相互通信,因此一个 Mapper 无法获得其他 Mapper 在当前迭代中产生的新任务(执行新的基本块),因此, MapReduce 模型(即使用 Mapper + Reducer)可能导致计算资源利用率低,换句话说,一个 Mapper 必须等到所有 Mapper 都完成工作,然后 Reducer 处理 Mapper 的输出并将合并后的数据返回给 Master。在这个过程中,如果一些 Mappers 需要长时间等待其他 Mappers 完成工作,那么资源利用率就会很低。

为了提高资源的利用,本文提出了基于反馈的负载均衡策略(feedbackbased load balancing strategy ,FBLB),其主要的思路是调整每次迭代后 Mappers 应该完成的工作量,这里论文提出了两个相关指标用于作为调整的依据和衡量标准,分别是:

Replay ratioworking ratio
状态恢复时间/运行Map任务时间运行任务时间/worker可用时间

明显地,Replay ratio 越低越好,而如果许多 workers 需要等待另一个 workers 完成工作,那么working ratio 就会很低,因此,FBLB 的出发点是尽可能地降低 Replay ratio,提高 working ratio。具体做法是通过动态调整 Mapper 需要的 S N SN SN(前文有提及)来实现。

S N SN SN 的增加会降低 Replay ratio,因为一个 Mapper 会用更多的时间执行更多的 EVM 操作。而改变 S N SN SN 也会影响 working ratio,但这种影响需要在运行时确定。例如,一个较大的 S N SN SN 意味着一个 Mapper 需要很长的时间来完成它的工作,因此其他 Mappers 可能会等待它,特别是在 Mappers 数量超过任务(也就是说,有完全没有任务的 Mapper)的情况下。然而, S N SN SN 越大,Mappers 可能产生的新任务越多,从而提高后续迭代的 working ratio。

FBLB 动态调整 S N SN SN 的具体过程是:首先,Gas Checker 根据 Mapper 监视器采集的性能数据计算第 i i i 次迭代的Replay ratio( r r i rr_{i} rri)和 working ratio( w r i wr_{i} wri)。当 r r i rr_{i} rri 大于阈值 t d td td 时,GasChecker 对 S N i SN_{i} SNi 进行增加得到 S N i + 1 SN_{i + 1} SNi+1。否则,GasChecker 将 S N SN SN 的调整视为一个搜索问题,并借鉴模拟退火算法 [ 3 ] [3] [3]的思想来逼近working ratio 的全局最优。特别地,如果 w r i > w r i − 1 wr_{i} \gt wr_{i-1} wri>wri1(说明前期调整提高了 working ratio),则接受之前的调整,并基于 S N i SN_{i} SNi 调整 S N i + 1 SN_{i+1} SNi+1。如果 w r i < w r i − 1 wr_{i} \lt wr_{i-1} wri<wri1 (说明前期调整降低了 working ratio),则接受概率定义为 e ( w r i − w r i − 1 ) / T e^{(wr_{i}-wr_{i-1})/T} e(wriwri1)/T 的调整,其中 T T T 为模拟退火算法中的温度。如果拒绝前面的调整,则基于 S N i − 1 SN_{i - 1} SNi1 来设置 S N i + 1 SN_{i + 1} SNi+1。由于模拟退火算法有接受“坏”调整的概率,因此该方法可以跳出局部最优。

(4)模式检测(Pattern Detection)

  • P1 模式
    在符号执行的过程中,GasChecker 记录了每个执行谓词的结果,因此,在符号执行之后,对那些谓词只有一个结果的,GasChecker 将谓词视为不透明的,即 P1 模式。

  • P2 模式
    在 CFG 构建之后,GasChecker 获得了智能合约的所有基本模块,在符号执行期间,GasChecker 记录所有执行的块,在符号执行之后,GasChecker 将属于智能合约但未被符号执行执行的基本块视为 P2模式。

  • P3 模式
    为了检测循环相关的模式(即P3 ~ P6),GasChecker 首先通过三个步骤识别循环:1)GasChecker 扫描 CFG,找到表示存在循环的后沿,并识别循环的入口块和出口块。这里定义两个块之间的距离为从一个块到另一个块的最少边数;2)对于每个块,GasChecker 计算其与入口区块的距离以及与出口区块的距离;3)循环判断,如果一个块距离出口块比距离进入块更近,则认为该块处于循环中,因为循环中的块要经过出口块才能到达进入块(这里注意 CFG 是有向图)。实现了循环识别后,GasChecker 在一个循环中扫描每个块,以检测昂贵的操作。目前 GasChecker 可以检测三种昂贵的操作是 SLOAD、SSTORE 和BALANCE。

  • P4 模式
    GasChecker 首先识别两个相邻的循环,然后计算它们的循环边界。GasChecker 认为如果两个相邻的回路边界相等,则它们是可熔的(如果回路边界是常数,很容易识别它们是否相等,如果它们的循环边界依赖于符号,我们使用符号执行来判断它们是否相等)。这里对易熔环的检测可能会引入假阳性,因为不考虑环的数据依赖性(例如,第二个循环读取第一个循环中设置的变量),但人工分析发现实际应用中假阳性率较低。

  • P5 模式
    GasChecker 分三步检测 P5 模式。首先,它通过上述方法检测循环;其次,对于循环中的每个计算,GasChecker 在符号执行期间记录其在循环的每个迭代中的结果。最后,在符号执行完成后,GasChecker 检查循环中的每个计算是否在每个迭代中产生了相同的结果。如果是的话,GasChecker 就会检测到循环中的重复计算。

  • P6 模式
    GasChecker 分三步检测 P6 模式。首先,它通过上述方法检测循环;其次,它记录了符号执行过程中环路内各分支的结果。最后,在符号执行后,如果满足以下两个条件,GasChecker 认为存在单边比较:1)比较产生的两个可行分支,即它不是不透明谓词模式;2)同一语境下(进入循环前的路径条件)的比较只产生一种结果。

  • P7 模式
    GasChecker 分两步检测 P7 模式。首先,在符号执行过程中,GasChecker 记录每条探测路径上发生的存储操作的位置,包括用于写入的 SSTORE 和读取(比如,SLOAD ,、BALANCE 、 EXTCODESIZE 、EXTCODECOPY)的操作;其次,在符号执行后,GasChecker 认为如果 SSTORE 写的位置永远不能在所有路径中读取则是冗余的。需要注意的是,如果一个SSTORE将存储值从非零设置为零则不是冗余的,因为这样的操作清除了存储 [ 4 ] [4] [4],此类 SSTORE 不浪费 gas。

  • P8 - P10 模式
    GasChecker 分两步检测 P8 - P10 模式。首先,GasChecker 记录符号执行期间每次执行的 EVM 操作;其次,在对每个程序路径进行探索后,GasChecker 检查记录的 EVM 操作是否包含这些操作指令序列。

4、参考文献

[ 1 ] [1] [1] T. Chen, X. Li, X. Luo, and X. Zhang, “Under-optimized smart contracts devour your money,” in SANER, 2017.
[ 2 ] [2] [2] L. Luu, D. H. Chu, H. Olickel, P. Saxena, and A. Hobor, “Making smart contracts smarter,” in CCS, 2016.
[ 3 ] [3] [3] (2017) Simulated annealing. [Online]. Available: https://en.wik ipedia.org/wiki/Simulated annealing
[ 4 ] [4] [4] G. Wood. (2017) Ethereum: A secure decentralised generalised transaction ledger. [Online]. Available: https://bravenewcoin.c om/assets/Whitepapers/Ethereum-A-Secure-Decentralised-Gen eralised-Transaction-Ledger-Yellow-Paper.pdf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值