EIP-6888: EVM 级别的算术验证
    
    
      
        
       
    
    
      
        
       
    
  
  在 EVM 级别检查数学溢出和除零错误
| Authors | Renan Rodrigues de Souza (@RenanSouza2) | 
|---|---|
| Created | 2023-04-16 | 
| Discussion Link | https://ethereum-magicians.org/t/eip-math-checking/13846 | 
摘要
此 EIP 将算术检查添加到 EVM 算术运算,并添加一个新的操作码,以便在发生事件时有条件地跳转。检查列表包括溢出、除零错误。
动机
数学检查在智能合约项目中的重要性非常明显。它曾经是 OpenZeppelin 的一个库,后来被纳入 Solidity 的默认行为中。将其引入 EVM 级别可以兼顾 gas 效率和安全性。
规范
本文档中使用的关键词“必须 (MUST)”,“禁止 (MUST NOT)”,“需要 (REQUIRED)”,“应该 (SHALL)”,“不应该 (SHALL NOT)”,“推荐 (RECOMMENDED)”,“不推荐 (NOT RECOMMENDED)”,“可以 (MAY)”和“可选 (OPTIONAL)”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。
从 BLOCK_TIMESTAMP >= HARDFORK_TIMESTAMP 开始
常量
| Constant | Type | Value | 
|---|---|---|
HARDFORK_TIMESTAMP | 
      uint64 | 
      TBD | 
    
UINT_MAX | 
      uint256 | 
      2 ** 256 - 1 | 
    
INT_MIN | 
      int256 | 
      -(2**255) | 
    
标志
| Variable | Type | Initial Value | 
|---|---|---|
carry | 
      bool | 
      false | 
overflow | 
      bool | 
      false | 
在 EVM 状态中添加两个新标志:无符号警告 (carry) 和有符号警告 (overflow)。 这些标志的作用域与程序计数器相同。
定义
从现在开始,a,b和c引用数学运算中的参数,res引用输出。 仅当运算采用 3 个输入时才使用c。
函数 sign(x) 定义在 uint256 -> {NEGATIVE, ZERO, POSITIVE} 集合中
条件
在以下情况下,carry 标志必须 (MUST) 设置:
- 当操作码为 
ADD(0x01) 且res < a时 - 当操作码为 
MUL(0x02) 且a != 0 ∧ res / a != b时 - 当操作码为 
SUB(0x03) 且b > a时 - 当操作码为 
DIV(0x04) 或MOD(0x06) 且b == 0时 - 当操作码为 
ADDMOD(0x08) 且c == 0时 - 当操作码为 
MULMOD(0x08) 且c == 0时 - 当操作码为 
EXP(0x0A) 且a ** b > UINT_MAX时 - 当操作码为 
SHL(0x1b) 且res >> a != b时 
在以下情况下,overflow 标志必须 (MUST) 设置:
- 当操作码为 
ADD(0x01) 且a != 0 ∧ b != 0 ∧ sign(a) == sign(b) ∧ sign(a) != sign(res)时 - 当操作码为 
SUB(0x03) 且(a != 0 ∧ b != 0 ∧ sign(a) != sign(b) ∧ sign(a) != sign(res)) ∨ (a == 0 ^ b == INT_MIN)时 - 当操作码为 
MUL(0x02) 且(a == -1 ∧ b == INT_MIN) ∨ (a == INT_MIN ∧ b == -1) ∨ (a != 0 ∧ (res / a != b))(此处的/表示SDIV) 时 - 当操作码为 
SDIV(0x05) 或SMOD(0x06) 且b == 0 ∨ (a == INT_MIN ∧ b == -1)时 - 当操作码为 
SHL(0x1b) 且res >> a != b(此处的>>表示SAR) 时 
操作码
      
      
         JUMPC
      
      
    
从堆栈中使用一个参数,即可能的 pc 目标地址。
根据 carry 标志有条件地更改程序计数器。 J_JUMPC = carry ? µ_s[0] : µ_pc + 1
清除两个标志。 carry = overflow = false
      
      
         JUMPO
      
      
    
从堆栈中使用一个参数,即可能的 pc 目标地址。
根据 ovewflow 标志有条件地更改程序计数器。 J_JUMPO = carry ? µ_s[0] : µ_pc + 1
清除两个标志。 carry = overflow = false
gas
这两个指令的 gas 成本均为 G_high,与 JUMPI 相同。
理由
EVM 对负数使用二进制补码。 上面列出的操作码会触发一个或两个标志,具体取决于它们是否用于有符号和无符号数字。
为每个操作码描述的条件都考虑到了易于实施。 唯一的例外是 EXP,因为它很难给出一个简洁的测试,因为大多数其他测试都依赖于逆运算,并且没有原生的 LOG。 大多数 EXP 的内部实现都会使用 MUL,因此标志 carry 可以从该指令中得出,而不是从 overflow 得出。
两个标志同时清除,因为预期指令会在代码之间转换时使用,在这些代码中,数字被视为有符号或无符号。
向后兼容性
此 EIP 引入了一个新的操作码并改变了 EVM 行为。
测试用例
待定
参考实现
待定
安全注意事项
这是一种新的 EVM 行为,但是每个代码都将决定如何与之交互。
版权
通过 CC0 放弃版权及相关权利。
Citation
Please cite this document as:
Renan Rodrigues de Souza (@RenanSouza2), "EIP-6888: EVM 级别的算术验证 [DRAFT]," Ethereum Improvement Proposals, no. 6888, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6888.