Alert Source Discuss

EIP-: 摘要

Authors

摘要

该 EIP 使得调用返回字符串和其他动态大小数组的函数成为可能。 目前,当从以太坊虚拟机内部调用另一个合约/函数时, 必须预先指定输出的大小。当然,可以给出更大的 大小,但是也必须为未写入的内存支付 gas,这使得返回 动态大小的数据既昂贵又不灵活,以至于实际上无法使用。

本 EIP 中提出的解决方案是仅对在 CALL 返回时实际写入的内存 收取 gas 费用。

规范

CALLCALLCODEDELEGATECALL (以下称为 CALL*) 的 gas 和内存语义 以以下方式更改(CREATE 不写入内存,因此不受影响):

假设 CALL* 的参数为 gas, address, value, input_start, input_size, output_start, output_size, 那么,在操作码开始时,增长内存的 gas 仅对 input_start + input_size 收取,而不对 output_start + output_size 收取。

如果被调用合约返回大小为 n 的数据,则调用合约的内存将增长到 output_start + min(output_size, n) (并且调用合约为此支付 gas),并且输出将写入区域 [output_start, output_start + min(n, output_size))

调用合约可能会在操作码的开始和操作码的结束时耗尽 gas。

调用后,MSIZE 操作码应返回内存实际增长到的大小。

动机

一般来说,为调用的输出保留一定的内存区域是一个好习惯, 因为让子例程写入内存中的任意区域可能很危险。另一方面,通常很难在执行调用之前知道调用的输出大小: 数据可能位于另一个合约的存储中,该存储通常无法访问并且 确定其大小将需要另一次调用该合约。

此外,为实际未写入的内存区域收取 gas 费用是不必要的。

本提案试图解决这两个问题:调用者可以选择在其内存区域的末尾提供一个巨大的 内存区域。被调用者可以通过返回“写入”它,并且 调用者仅为实际写入的内存区域付费。

这使得以非常灵活的方式返回动态数据(如字符串和动态大小的数组)成为可能。 甚至可以确定返回数据的大小: 如果调用者使用 output_start = MSIZEoutput_size = =2**256-1,则实际写入的 内存区域是 (output_start, MSIZE) (此处,调用后评估的 MSIZE)。 这很重要,因为它允许“代理”合约 调用它们不知道其接口的其他合约并仅返回其输出, 即,它们既转发输入又转发输出。为此,重要的是调用者 (1) 不需要事先知道输出的大小,并且 (2) 可以在调用后确定 输出的大小。

理由

这种处理问题的方式需要对以太坊虚拟机进行最小的更改。 实现类似目标的其他方法会更改操作码本身或 其参数的数量。另一种可能性是仅在 output_size 等于 2**256-1 时才更改 gas 机制。由于实现中的主要困难是在 CALL 周围的代码中的两个点必须扩大内存, 因此这不会简化。

在早期阶段,有人建议还在堆栈上添加返回数据的大小, 但是上面描述的 MSIZE 机制应该足够了,并且具有更好的 向后兼容性。

一些评论可在 https://github.com/ethereum/EIPs/issues/8 上找到

向后兼容性

此提案更改了合约的语义,因为合约可以访问 gas 计数器 和内存大小。

另一方面,由于 以下原因,现有合约不太可能受到此更改的影响:

Gas:

VM 不会收取比以前更多的 gas。通常,合约的编写方式 使得它们的语义不会因使用更少的 gas 而改变。如果使用更多的 gas,则合约 可能会耗尽 gas(如果它们对子调用所需的 gas 执行严格的估计)。在这里, 合约可能只会向其调用者返回更多的 gas。

内存大小:

MSIZE 操作码通常用于在先前未使用的位置分配内存。 语义的更改会以两种方式影响现有合约:

  1. 分配的内存中的重叠。通过使用 CALL,合约可能想要分配 一定大小的内存,即使该内存未被被调用合约写入。 随后使用 MSIZE 分配内存可能会与此切片重叠,该切片 现在比更改前更小。但是,存在此类合约的可能性不大。

  2. 内存地址更改。更一般而言,如果使用 MSIZE 分配内存,则 更改后,内存中对象的地址将有所不同。但是,合约 都应该以某种方式编写,使得内存中的对象是 可重定位的, 即,它们在内存中的绝对位置以及它们相对于其他 对象的位置无关紧要。当然,数组并非如此,但是它们 是在单个分配中分配的,而不是通过中间的 CALL 分配的。

实现

VM 的实现者应注意不要在调用结束之前以及在检查了足够的 gas 仍然可用之后才增加内存。EIP 的典型用途包括为输出“保留” 2**256-1 字节的内存。

Python 实现:

old: http://vitalik.ca/files/old.py new: http://vitalik.ca/files/new.py

Citation

Please cite this document as:

, "EIP-: 摘要," Ethereum Improvement Proposals, no. , . [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-.