Alert Source Discuss
Standards Track: Core

EIP-211: 新操作码: RETURNDATASIZE 和 RETURNDATACOPY

Authors Christian Reitwiessner <chris@ethereum.org>
Created 2017-02-13

简单总结

允许在 EVM 内部返回任意长度数据的机制已经被请求了很长时间。 现有的提案总是存在与 gas 收费相关的非常复杂的问题。 该提案解决了相同的问题,同时,它具有非常简单的 gas 收费机制,并且只需要对调用操作码进行最小的更改。 它的工作方式与现在处理 calldata 的方式非常相似; 在调用之后,返回数据保存在一个虚拟缓冲区中,调用者可以从中将数据(或其部分)复制到内存中。 在下一次调用时,缓冲区将被覆盖。 这种机制是 100% 向后兼容的。

摘要

请参阅总结。

动机

在某些情况下,函数能够返回在调用之前无法预测长度的数据至关重要。 原则上,这可以在不对 EVM 进行修改的情况下解决,例如,将调用拆分为两个调用,其中第一个调用仅用于计算大小。 然而,所有这些机制在至少某些情况下都非常昂贵。 这种最坏情况的一个非常有用的例子是通用转发合约; 该合约接收调用数据,可能会进行一些检查,然后将其按原样转发到另一个合约。 当然,返回数据应该以类似于原始调用者的方式传输。 由于合约是通用的并且不知道它调用的合约,因此如果没有相应地调整被调用合约或尝试对数数量的调用,则无法确定输出的大小。

建议编译器实现者为返回数据保留一个零长度区域,如果在调用之前返回数据的大小未知,然后结合使用 RETURNDATACOPYRETURNDATASIZE 来实际检索数据。

请注意,此提案还使得允许在有意状态恢复的情况下返回数据的 EIP (EIP-140) 更有用。 由于故障数据的大小可能大于常规返回数据(甚至未知),因此即使常规输出区域不足以容纳数据,也可以在 CALL 操作码发出故障信号后检索故障数据。

规范

如果 block.number >= BYZANTIUM_FORK_BLKNUM,则添加两个新的操作码,并修改任何创建新调用帧的操作码(如 CALLCREATEDELEGATECALL 等),以下简称为类调用操作码的语义。 假设 EVM(更具体地说是:EVM 调用帧)具有一个新的可变大小的内部缓冲区,称为返回数据缓冲区。 为每个新的调用帧创建一个空的缓冲区。 执行任何类调用操作码时,缓冲区将被清除(其大小设置为零)。 在执行类调用操作码之后,调用的完整返回数据(或故障数据,请参阅 EIP-140)存储在返回数据缓冲区(调用者)中,并且相应地更改其大小。 作为例外,CREATECREATE2 被认为在成功情况下返回空缓冲区,在失败情况下返回失败数据。 如果执行了类调用操作码,但实际上没有实例化调用帧(例如,由于值传输的资金不足或被调用的合约不存在),则返回数据缓冲区为空。

作为一种优化,可以在调用帧之间共享返回数据缓冲区,因为在任何时候最多只有一个缓冲区是非空的。

RETURNDATASIZE: 0x3d

将返回数据缓冲区的大小推送到堆栈上。 Gas 消耗: 2 (与 CALLDATASIZE 相同)

RETURNDATACOPY: 0x3e

此操作码具有与 CALLDATACOPY 类似的语义,但它不是从调用数据复制数据,而是从返回数据缓冲区复制数据。 此外,访问超出其大小的返回数据缓冲区会导致失败; 也就是说,如果 start + length 溢出或导致的值大于 RETURNDATASIZE,则当前调用将因 out-of-gas 条件而停止。 特别是,从缓冲区末尾读取 0 个字节将读取 0 个字节; 从缓冲区外的一个字节读取 0 个字节会导致异常。

Gas 消耗: 3 + 3 * ceil(amount / 32) (与 CALLDATACOPY 相同)

理由

考虑了其他允许返回动态数据的解决方案,但它们都必须从 call 操作码中扣除 gas,因此实现和规范都很复杂 (5/8)。 由于此提案与处理 calldata 的方式非常相似,因此它非常适合该概念。 此外,eWASM 架构已经以完全相同的方式处理返回数据。

请注意,EVM 实现需要保留返回数据,直到下一次调用或从当前调用返回。 由于此资源已作为被调用者内存的一部分支付,因此不应成为问题。 实现可以选择保持被调用者的完整内存处于活动状态,直到下一次调用,或者仅将返回数据复制到特殊内存区域。

在以下意义上,保持被调用者的内存直到下一个类调用操作码不会增加峰值内存使用量; 在调用者的帧中,在从调用返回之后发生的任何内存分配都可以在调用之前移动,而 gas 成本没有变化,但会将此分配添加到峰值分配中。

操作码的数值分配在包含 CALLDATASIZECALLDATACOPY 的相同半字节块中。

向后兼容性

此提案引入了两个新的操作码,除此之外,它保持完全向后兼容。

测试用例

实现

版权

CC0 下放弃版权和相关权利。

Citation

Please cite this document as:

Christian Reitwiessner <chris@ethereum.org>, "EIP-211: 新操作码: RETURNDATASIZE 和 RETURNDATACOPY," Ethereum Improvement Proposals, no. 211, February 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-211.