【论文研读】-Defining the Ethereum Virtual Machine for Interactive Theorem Provers

背景

代码链接

Interactive Theorem Provers

交互式定理证明涉及使用计算证明助手来验证数学声明是否正确,或验证硬件和软件设计是否符合其正式规范。

文章摘要

以太坊中的智能合约由以太坊虚拟机 (EVM) 执行。我们在 Lem (我们的 EVM 定义是用 Lem 编写的,可以翻译成流行的交互式定理证明器 Coq 、Isabelle/HOL和 HOL4) 中定义了 EVM,这是一种可以为一些交互式定理证明器编译的语言。我们针对以太坊实现的标准测试套件测试了我们的定义。使用我们的定义,我们在交互式定理证明器 Isabelle/HOL 中证明了以太坊智能合约的一些安全属性。据我们所知,我们的定义是第一个实现所有指令的智能合约验证的正式 EVM 定义。我们的定义可以作为进一步分析和生成以太坊智能合约的基础。

文章贡献

  • 我们给出了智能合约执行与世界其他地方之间接口的正式规范;
  • 我们以可移植到不同交互式定理证明器(Isabelle/HOL 和 HOL4)和编程语言 OCaml 的方式定义 EVM,在此期间,我们发现规范和实现之间存在一些细微差别);
  • 我们针对 VM 测试套件测试了 EVM 定义的可执行部分,该套件验证了现有的以太坊节点实现;
  • 我们在测试套件中发现了未搜索的极端案例;
  • 我们使用我们的 EVM 定义来证明以太坊智能合约的不变量和安全属性。

目标和选择

背景: Solidity有丰富的语法,但是不规范。并且EVM有着一些缺点:

  • 大部分不会去读EVM字节码
  • EVM引入新的框架会导致EVM本身框架出现模糊。
  • EVM框架的简单导致也只有2000行正式的定义。

下图是2000行定义中部分节选:
在这里插入图片描述

目标

使用测试套件来测试EVM的定义,并且我们打算将我们的 EVM 定义作为智能合约验证的基础。

选择

会选择Lem框架来设计和定义编程语言的工具。
我们没有在一个特定的定理证明器中指定 EVM,而是选择了一个称为 Lem的框架,因为 Lem 中的定义可以翻译成一些流行的定理证明器:Coq 、Isabelle/HOL和 HOL4。一种潜在的替代方案是 K 框架 。 K-Framework 是专门为定义编程语言而设计的工具。我们选择 Lem 及其翻译目标是因为他们拥有更大的用户群和更长的历史。

EVM简介

EVM初始状态

在 EVM 中,除了几个全局参数外,大多数状态都存储在帐户中。 EVM 具有从地址(160 位字)到帐户状态的部分映射。
在这里插入图片描述

账户状态包含代码、存储、随机数和余额。代码是一个字节序列。存储是从机器字(EVM 机器字有 256 位)到机器字的映射。 256位字节码如下:
在这里插入图片描述

nonce 是一个不断增长的机器词。余额也是一个机器字,代表一些可转让的价值,可以作为运行 EVM 的费用支付。当代码不为空时,代码控制账户;这样的账户称为合约。否则,账户由地址对应的私钥持有者控制;这样的帐户称为外部帐户。该代码在存在时对一系列指令进行编码。除了包含立即值的 PUSH 指令外,指令都被编码在一个字节中。

EVM状态转换

外部账户可以通过创建合约或调用账户来发起交易。一旦事务启动,EVM 的整个状态转换都是确定性的。我们不描述由外部帐户创建的合约,因为创建后的合约状态是公开可检查的。
在这里插入图片描述

外部账户和合约都可以调用一个账户。当一个账户调用一个账户时,调用伴随着转账的余额、gas和数据。转移的余额存入被调用的帐户。 gas 调节调用期间的资源消耗。当被调用账户是外部账户时,会发生简单的余额转账。否则,当被调用账户为合约时,余额转账后,被调用合约的代码被执行。代码执行可以改变执行帐户的存储。执行可以读取所有帐户的余额和代码。

代码执行的资源消耗受启动外部帐户支付的 gas 量的限制。执行一条指令会消耗一些气体。当gas耗尽时,执行失败(out-of-gas failure)。此类故障会恢复当前调用期间执行的所有状态更改,但 gas 消耗除外。

一笔交易属于一个区块。块是以太坊节点之间的协议单位。 EVM 具有读取块号和一些先前块的加密哈希值的特殊指令。由于一个块指定了​​前一个块而不是唯一的后继块,因此网络中的块通常形成一棵树,但就 EVM 的状态而言,树中只有一个分支很重要。因此,我们可以将 EVM 视为顺序执行的机器。

合约调用接口

系统和环境的边界

为什么要分清系统和环境的边界: 调用不受信任的外部合约可能会引发一系列意外的风险和错误。外部调用可能在其合约和它所依赖的其他合约内执行恶意代码。因此,每一个外部调用都会有潜在的安全威胁,尽可能的从你的智能合约内移除外部调用。当无法完全去除外部调用时,可以使用这一章节其他部分提供的建议来尽量减少风险。
在这里插入图片描述
回调时,系统边界的不同选择。上图描述相同的情况,但是在系统和环境当中却有着不同的边界:

  • (a) 当系统就是我们合约的时候,重入(the reentrant call)就是系统的一部分。
  • (b) 当系统是我们合约单次的调用,重入就是环境的一部分。

两者都是合理的,但我们选择 (b) 是因为它与程序语法相匹配,其中 CALL 指令之后是同一消息调用中的下一个操作,而不是重入调用中的下一个操作。

部署以太坊虚拟机的输入和输出

合约和环境之间的交互总是从环境调用合约开始。环境可以使用以下信息调用合约:
在这里插入图片描述
整个语法定义了一个包含七个字段的记录类型。 call_env 的值由七个值组成,每个值都可以在一个字段名称下访问。字段名称为斜体。每个字段名称都用关联值的类型进行注释。 w256 表示 256 位机器字的类型和地址 160 位机器字。 list_byte 是字节列表的类型。箭头类型地址 → w256 是总函数的​​类型,它接受一个地址并返回一个 w256。这个定义不仅对推理 EVM 字节码很有用,而且对设计将被编译成 EVM 的高级语言也很有用。任何语言编写的以太坊合约都需要结合上述数据。

这部分的详细内容请看代码

环境的可能动作由以下变体类型环境动作描述,其值可以是标签 EnvironmentCall 加上 call env 的值,标签 EnvironmentRet 加上返回结果的值,或标签 EnvironmentFail。自动理解不同标签的值是不同的。这个定义描述了以太坊合约可能发生的一切。如果我们检查了这些案例,我们已经列举了所有的可能性。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个定义描述了以太坊合约可以做的所有事情。当为以太坊设计高级语言时,希望该语言能够引起所有这些动作。此外,由于输入-输出接口是在交互式定理证明器中定义的,因此可以在指定以太坊智能合约的逻辑公式中对动作进行普遍 (∀) 或存在性 (∃) 量化。

方案介绍

形式化确定性合约执行(系统内部合约)

EVM 正在执行一个帐户的代码,EVM 可以访问堆栈、内存、内存使用计数器、存储、程序计数器、所有帐户的余额、调用者、当前调用发送的值、发送的数据当前调用、为恢复而保留的初始状态、发起交易的外部账户、所有地址上的代码、当前区块、剩余气体、账户的存在以及已触及的存储索引列表。除了最后一个之外,所有东西都是 EVM 执行所必需的。最后一块备用在测试时枚举所有存储索引。

这些数据被打包成一个记录: 定义变量
在这里插入图片描述
在这里插入图片描述
定义函数:
在这里插入图片描述
在这里插入图片描述
主要用VM测试套件来测试上述定义的安全性(命令是否可以终止):我们的 Lem 定义可以提取到 OCaml 和 Isabelle/HOL 中。我们针对标准 VM 测试套件测试了 OCaml 提取。使用 Isabelle/HOL 提取,我们证明了一些字节码的安全属性。在这个图中,VM 测试套件和其他 EVM 实现不是我们的贡献。

形式化非确定性环境(系统外部环境)

我们将非确定性环境定义为类型的前置状态(account state * program result)和类型的后置状态(account state * variable ctx) 之间的二元关系。

主要考虑在执行智能合约过程中balance和gas之间的转换关系。

总结

我们定义了 EVM,以便交互式定理证明者可以推理以太坊智能合约。我们的 EVM 定义包含所有指令。我们在 Isabelle/HOL 中使用了我们的 EVM 定义,并证明了在存在可重入性的情况下以太坊合约的安全属性和不变量。作为副作用,我们在规范中发现了几个问题;我们要求对黄皮书进行 11 次修复。我们在模型中发现了 13 个 VM 测试套件没有触及的代码路径。我们证明了正式的可执行规范对于验证智能合约、测试规范以及测量虚拟机测试的代码覆盖率是有效的。我们希望我们的开发能够成为更复杂的智能合约验证框架和from/to EVM 字节码的经过验证的编译器的基础。

猜你喜欢

转载自blog.csdn.net/qq_40500099/article/details/126868066