From c1f45f8b4caa4e12716863943a473dc4bdd6ec23 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 13 Feb 2017 12:07:09 +0100 Subject: [PATCH 01/15] Propose RETURNDATACOPY and RETURNDATASIZE. --- EIPS/returndatacopy.md | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 EIPS/returndatacopy.md diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md new file mode 100644 index 0000000000000..2d4c7e8d0d55a --- /dev/null +++ b/EIPS/returndatacopy.md @@ -0,0 +1,61 @@ +## Preamble + + EIP: + Title: New opcodes: RETURNDATASIZE and RETURNDATACOPY + Author: Christian Reitwiessner + Type: Standard Track + Category Core + Status: Draft + Created: 2017-02-13 + Requires: + Replaces: 5/8 + + +## Simple Summary + +A mechanism to allow returning arbitrary-length data inside the EVM has been requested for quite a while now. Existing proposals always had very intricate problems associated with charging gas. This proposal solves the same problem while at the same time, it has a very simple gas charging mechanism and reqires minimal changes to the call opcodes. Its workings are very similar to the way calldata is handled already: After a call, return data is kept inside a virtual buffer from which the caller can copy it (or parts thereof) into memory. At the next call, the buffer is overwritten. This mechanism is 100% backwards compatible. + +## Abstract + +Please see summary. + +## Motivation + +In some situations, it is vital for a function to be able to return data whose length cannot be anticipated before the call. In principle, this can be solved without alterations to the EVM, for example by splitting the call into two calls where the first is used to compute only the size. All of these mechanisms, though, are very expensive in at least some situations. A very useful example of such a worst-case situation is a generic forwarding contract: A contract that takes call data, potentially makes some checks and then forwards it as is to another contract. The return data should of course be transferred in a similar way to the original caller. Since the contract is generic and does not know about the contract it calls, there is no way to determine the size of the output without adapting the called contract accordingly or trying a logarithmic number of calls. + +Compiler implementors are advised to reserve a zero-length area for return data if the size of the return data is unknown before the call and then use `RETURNDATACOPY` in conjunction with `RETURNDATASIZE` to actually retrieve the data. + +Note that this proposal also makes the EIP that proposes to allow to return data in case of an intentional state reversion (EIP [206](https://github.com/ethereum/EIPs/pull/206)) much more useful. Since the size of the failure data might be larger than the regular return data (or even unknown), it is possible to retrieve the failure data after the CALL opcode has signalled a failure, even if the regular output area is not large enough to hold the data. + +## Specification + +Add two new opcodes: + +`RETURNDATASIZE`: `0xd` + +Pushes the size of the return data (or the failure return data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the previous call onto the stack. If there was no previous call, pushes zero. +Gas costs: 2 (same as `CALLDATASIZE`) + +`RETURNDATACOPY`: `0xe` + +This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data of the previous call. If the return data is accessed beyond its length, it is considered to be filled with zeros. If there was no previous call, copies zeros. +Gas costs: `3 + 3 * ceil(amount / 32)` (same as `CALLDATACOPY`) + +## Rationale + +Other solutions that would allow returning dynamic data were considered, but they all had to deduct the gas from the call opcode and thus were both complicated to implement and specify ([5/8](https://github.com/ethereum/EIPs/issues/8)). Since this proposal is very similar to the way calldata is handled, it fits nicely into the concept. Furthermore, the eWASM architecture already handles return data in exactly the same way. + +Note that the EVM implementation needs to keep the return data until the next call or the return from the current call. Since this resource was already paid for as part of the memory of the callee, it should not be a problem. Implementations may either choose to keep the full memory of the callee alive until the next call or copy only the return data to a special memory area. + +The number values of the opcodes were allocated in the same nibble block that also contains `CALLDATASIZE` and `CALLDATACOPY`. + +## Backwards Compatibility + +This proposal introduces two new opcodes and stays fully backwards compatible apart from that. + +## Test Cases + +## Implementation + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From d10be75eabedcebcc91f1e64916cf1ee4777fe19 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 25 Apr 2017 12:46:22 +0200 Subject: [PATCH 02/15] Assign number and add clarifictaions. --- EIPS/returndatacopy.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index 2d4c7e8d0d55a..66bbd3e17ddca 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -1,6 +1,6 @@ ## Preamble - EIP: + EIP: 211 Title: New opcodes: RETURNDATASIZE and RETURNDATACOPY Author: Christian Reitwiessner Type: Standard Track @@ -29,16 +29,21 @@ Note that this proposal also makes the EIP that proposes to allow to return data ## Specification -Add two new opcodes: +Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` is considered to return the empty buffer in the success case and the failure data in the failure case. + +As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. `RETURNDATASIZE`: `0xd` -Pushes the size of the return data (or the failure return data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the previous call onto the stack. If there was no previous call, pushes zero. +Pushes the size of the return data buffer onto the stack. Gas costs: 2 (same as `CALLDATASIZE`) `RETURNDATACOPY`: `0xe` -This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data of the previous call. If the return data is accessed beyond its length, it is considered to be filled with zeros. If there was no previous call, copies zeros. +This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. If the return data buffer is accessed beyond its size, it is considered to be filled with zeros. + +ALTERNATIVE: If the return data is accessed beyond its size, results in failure. + Gas costs: `3 + 3 * ceil(amount / 32)` (same as `CALLDATACOPY`) ## Rationale @@ -47,6 +52,8 @@ Other solutions that would allow returning dynamic data were considered, but the Note that the EVM implementation needs to keep the return data until the next call or the return from the current call. Since this resource was already paid for as part of the memory of the callee, it should not be a problem. Implementations may either choose to keep the full memory of the callee alive until the next call or copy only the return data to a special memory area. +Keeping the memory of the callee until the next call-like opcode does not increase the peak memory usage in the following sense: Any memory allocation in the caller's frame that happens after the return from the call can be moved before the call without a change in gas costs, but will add this allocation to the peak allocation. + The number values of the opcodes were allocated in the same nibble block that also contains `CALLDATASIZE` and `CALLDATACOPY`. ## Backwards Compatibility From e07cff53904063012eb77a577bc9ab595feacf93 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 18 May 2017 11:50:03 +0200 Subject: [PATCH 03/15] Change codes to 0x3d and 0x3e --- EIPS/returndatacopy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index 66bbd3e17ddca..07635c84d2f4e 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -33,12 +33,12 @@ Add two new opcodes and amend the semantics of any opcode that creates a new cal As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. -`RETURNDATASIZE`: `0xd` +`RETURNDATASIZE`: `0x3d` Pushes the size of the return data buffer onto the stack. Gas costs: 2 (same as `CALLDATASIZE`) -`RETURNDATACOPY`: `0xe` +`RETURNDATACOPY`: `0x3e` This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. If the return data buffer is accessed beyond its size, it is considered to be filled with zeros. From 71ace16fcd34e962096a49d7dc81af2315d5f199 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 18 May 2017 15:05:11 +0200 Subject: [PATCH 04/15] Added CREATE2 --- EIPS/returndatacopy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index 07635c84d2f4e..9689f459e8d69 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -29,7 +29,7 @@ Note that this proposal also makes the EIP that proposes to allow to return data ## Specification -Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` is considered to return the empty buffer in the success case and the failure data in the failure case. +Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. From 28f1709777cbd99dc96dcdbbef8c532674bfbc86 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Jun 2017 14:58:32 +0200 Subject: [PATCH 05/15] Fail on access beyond end. --- EIPS/returndatacopy.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index 9689f459e8d69..11c80fba689a5 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -40,9 +40,7 @@ Gas costs: 2 (same as `CALLDATASIZE`) `RETURNDATACOPY`: `0x3e` -This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. If the return data buffer is accessed beyond its size, it is considered to be filled with zeros. - -ALTERNATIVE: If the return data is accessed beyond its size, results in failure. +This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. Furthermore, accessing the return data buffer beyond its size results in a failure, i.e. if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition. Gas costs: `3 + 3 * ceil(amount / 32)` (same as `CALLDATACOPY`) From aeaffcca26fa688f5015da7ce059c886e90ef374 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Jun 2017 17:04:58 +0200 Subject: [PATCH 06/15] Clarified reading from the end. --- EIPS/returndatacopy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index 11c80fba689a5..1201df21e4954 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -40,7 +40,7 @@ Gas costs: 2 (same as `CALLDATASIZE`) `RETURNDATACOPY`: `0x3e` -This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. Furthermore, accessing the return data buffer beyond its size results in a failure, i.e. if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition. +This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. Furthermore, accessing the return data buffer beyond its size results in a failure, i.e. if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition. In particular, reading 0 bytes from the end of the buffer will read 0 bytes; reading 0 bytes from one-byte out of the buffer causes an exception. Gas costs: `3 + 3 * ceil(amount / 32)` (same as `CALLDATACOPY`) From e3dff831121549e850fa662a0e6944878dc1ce22 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 24 Aug 2017 12:16:06 +0200 Subject: [PATCH 07/15] Clarify return data buffer for failed calls. --- EIPS/returndatacopy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index 1201df21e4954..f8b940854b458 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -29,7 +29,7 @@ Note that this proposal also makes the EIP that proposes to allow to return data ## Specification -Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. +Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instanciate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. From f37874200ad28bcf0cc3b171242174bdcf62559d Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 17 Nov 2017 19:46:47 +0100 Subject: [PATCH 08/15] Fix a typo https://github.com/ethereum/EIPs/pull/211#pullrequestreview-58334736 --- EIPS/returndatacopy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/returndatacopy.md b/EIPS/returndatacopy.md index f8b940854b458..2a8a358d7e31c 100644 --- a/EIPS/returndatacopy.md +++ b/EIPS/returndatacopy.md @@ -29,7 +29,7 @@ Note that this proposal also makes the EIP that proposes to allow to return data ## Specification -Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instanciate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. +Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. From 2f1e498112ce6d8f92f02a91a9714f09495ebd76 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 17 Nov 2017 19:47:23 +0100 Subject: [PATCH 09/15] Move returndatacopy.md to eip-211.md --- EIPS/{returndatacopy.md => eip-211.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename EIPS/{returndatacopy.md => eip-211.md} (100%) diff --git a/EIPS/returndatacopy.md b/EIPS/eip-211.md similarity index 100% rename from EIPS/returndatacopy.md rename to EIPS/eip-211.md From 14dc97daad12e204259a31632e20de3c3f199008 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 17 Nov 2017 20:04:39 +0100 Subject: [PATCH 10/15] Fix another typo --- EIPS/eip-211.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index 2a8a358d7e31c..0711f0bad0bc6 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -13,7 +13,7 @@ ## Simple Summary -A mechanism to allow returning arbitrary-length data inside the EVM has been requested for quite a while now. Existing proposals always had very intricate problems associated with charging gas. This proposal solves the same problem while at the same time, it has a very simple gas charging mechanism and reqires minimal changes to the call opcodes. Its workings are very similar to the way calldata is handled already: After a call, return data is kept inside a virtual buffer from which the caller can copy it (or parts thereof) into memory. At the next call, the buffer is overwritten. This mechanism is 100% backwards compatible. +A mechanism to allow returning arbitrary-length data inside the EVM has been requested for quite a while now. Existing proposals always had very intricate problems associated with charging gas. This proposal solves the same problem while at the same time, it has a very simple gas charging mechanism and requires minimal changes to the call opcodes. Its workings are very similar to the way calldata is handled already: After a call, return data is kept inside a virtual buffer from which the caller can copy it (or parts thereof) into memory. At the next call, the buffer is overwritten. This mechanism is 100% backwards compatible. ## Abstract From 61a3f8292f9b8258415c3a72a2774f6380a80991 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 17 Nov 2017 20:04:50 +0100 Subject: [PATCH 11/15] Mention the Byzantium block number --- EIPS/eip-211.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index 0711f0bad0bc6..75b0e40f1cf5d 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -29,7 +29,7 @@ Note that this proposal also makes the EIP that proposes to allow to return data ## Specification -Add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. +If `block.number >= BYZANTIUM_FORK_BLKNUM`, add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. From c22ad19a7dc1f4a793363c414facc8a3f81fb43c Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 20 Nov 2017 12:13:52 +0100 Subject: [PATCH 12/15] Change how to refer to EIP-140 Following https://github.com/ethereum/EIPs/pull/211#discussion_r151791340 --- EIPS/eip-140.md | 1 + EIPS/eip-211.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 EIPS/eip-140.md diff --git a/EIPS/eip-140.md b/EIPS/eip-140.md new file mode 100644 index 0000000000000..c766244c556b3 --- /dev/null +++ b/EIPS/eip-140.md @@ -0,0 +1 @@ +Reserved for https://github.com/ethereum/EIPs/pull/206 diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index 75b0e40f1cf5d..2bfb9228b7460 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -25,11 +25,11 @@ In some situations, it is vital for a function to be able to return data whose l Compiler implementors are advised to reserve a zero-length area for return data if the size of the return data is unknown before the call and then use `RETURNDATACOPY` in conjunction with `RETURNDATASIZE` to actually retrieve the data. -Note that this proposal also makes the EIP that proposes to allow to return data in case of an intentional state reversion (EIP [206](https://github.com/ethereum/EIPs/pull/206)) much more useful. Since the size of the failure data might be larger than the regular return data (or even unknown), it is possible to retrieve the failure data after the CALL opcode has signalled a failure, even if the regular output area is not large enough to hold the data. +Note that this proposal also makes the EIP that proposes to allow to return data in case of an intentional state reversion ([EIP-140](./eip-140.md)) much more useful. Since the size of the failure data might be larger than the regular return data (or even unknown), it is possible to retrieve the failure data after the CALL opcode has signalled a failure, even if the regular output area is not large enough to hold the data. ## Specification -If `block.number >= BYZANTIUM_FORK_BLKNUM`, add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see EIP [206](https://github.com/ethereum/EIPs/pull/206)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. +If `block.number >= BYZANTIUM_FORK_BLKNUM`, add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see [EIP-140](./eip-140.md)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. From d534a1bdb4afd052de68a21d0af58eb611cc041b Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 20 Nov 2017 15:12:34 +0100 Subject: [PATCH 13/15] A colon cannot separate sentences --- EIPS/eip-211.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index 2bfb9228b7460..caf6bb4c27e77 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -13,7 +13,7 @@ ## Simple Summary -A mechanism to allow returning arbitrary-length data inside the EVM has been requested for quite a while now. Existing proposals always had very intricate problems associated with charging gas. This proposal solves the same problem while at the same time, it has a very simple gas charging mechanism and requires minimal changes to the call opcodes. Its workings are very similar to the way calldata is handled already: After a call, return data is kept inside a virtual buffer from which the caller can copy it (or parts thereof) into memory. At the next call, the buffer is overwritten. This mechanism is 100% backwards compatible. +A mechanism to allow returning arbitrary-length data inside the EVM has been requested for quite a while now. Existing proposals always had very intricate problems associated with charging gas. This proposal solves the same problem while at the same time, it has a very simple gas charging mechanism and requires minimal changes to the call opcodes. Its workings are very similar to the way calldata is handled already; after a call, return data is kept inside a virtual buffer from which the caller can copy it (or parts thereof) into memory. At the next call, the buffer is overwritten. This mechanism is 100% backwards compatible. ## Abstract @@ -21,7 +21,7 @@ Please see summary. ## Motivation -In some situations, it is vital for a function to be able to return data whose length cannot be anticipated before the call. In principle, this can be solved without alterations to the EVM, for example by splitting the call into two calls where the first is used to compute only the size. All of these mechanisms, though, are very expensive in at least some situations. A very useful example of such a worst-case situation is a generic forwarding contract: A contract that takes call data, potentially makes some checks and then forwards it as is to another contract. The return data should of course be transferred in a similar way to the original caller. Since the contract is generic and does not know about the contract it calls, there is no way to determine the size of the output without adapting the called contract accordingly or trying a logarithmic number of calls. +In some situations, it is vital for a function to be able to return data whose length cannot be anticipated before the call. In principle, this can be solved without alterations to the EVM, for example by splitting the call into two calls where the first is used to compute only the size. All of these mechanisms, though, are very expensive in at least some situations. A very useful example of such a worst-case situation is a generic forwarding contract; a contract that takes call data, potentially makes some checks and then forwards it as is to another contract. The return data should of course be transferred in a similar way to the original caller. Since the contract is generic and does not know about the contract it calls, there is no way to determine the size of the output without adapting the called contract accordingly or trying a logarithmic number of calls. Compiler implementors are advised to reserve a zero-length area for return data if the size of the return data is unknown before the call and then use `RETURNDATACOPY` in conjunction with `RETURNDATASIZE` to actually retrieve the data. @@ -50,7 +50,7 @@ Other solutions that would allow returning dynamic data were considered, but the Note that the EVM implementation needs to keep the return data until the next call or the return from the current call. Since this resource was already paid for as part of the memory of the callee, it should not be a problem. Implementations may either choose to keep the full memory of the callee alive until the next call or copy only the return data to a special memory area. -Keeping the memory of the callee until the next call-like opcode does not increase the peak memory usage in the following sense: Any memory allocation in the caller's frame that happens after the return from the call can be moved before the call without a change in gas costs, but will add this allocation to the peak allocation. +Keeping the memory of the callee until the next call-like opcode does not increase the peak memory usage in the following sense; any memory allocation in the caller's frame that happens after the return from the call can be moved before the call without a change in gas costs, but will add this allocation to the peak allocation. The number values of the opcodes were allocated in the same nibble block that also contains `CALLDATASIZE` and `CALLDATACOPY`. From 0fe1eb7c46663a74561f0920c352f7e15b4fd75f Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 20 Nov 2017 15:13:05 +0100 Subject: [PATCH 14/15] Status is now Final --- EIPS/eip-211.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index caf6bb4c27e77..22d89bb5ad99f 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -5,7 +5,7 @@ Author: Christian Reitwiessner Type: Standard Track Category Core - Status: Draft + Status: Final Created: 2017-02-13 Requires: Replaces: 5/8 From cbe317dcd650c15e431f85410e3cbeda3bd31dc1 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Fri, 1 Dec 2017 17:54:17 +0100 Subject: [PATCH 15/15] Rather pedantic edits --- EIPS/eip-211.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md index 22d89bb5ad99f..14f65f917b756 100644 --- a/EIPS/eip-211.md +++ b/EIPS/eip-211.md @@ -31,7 +31,7 @@ Note that this proposal also makes the EIP that proposes to allow to return data If `block.number >= BYZANTIUM_FORK_BLKNUM`, add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see [EIP-140](./eip-140.md)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. -As an optimization, it is possible to share the return data buffer across call frames because only one will be non-empty at any time. +As an optimization, it is possible to share the return data buffer across call frames because at most one will be non-empty at any time. `RETURNDATASIZE`: `0x3d` @@ -40,7 +40,7 @@ Gas costs: 2 (same as `CALLDATASIZE`) `RETURNDATACOPY`: `0x3e` -This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. Furthermore, accessing the return data buffer beyond its size results in a failure, i.e. if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition. In particular, reading 0 bytes from the end of the buffer will read 0 bytes; reading 0 bytes from one-byte out of the buffer causes an exception. +This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. Furthermore, accessing the return data buffer beyond its size results in a failure; i.e. if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition. In particular, reading 0 bytes from the end of the buffer will read 0 bytes; reading 0 bytes from one-byte out of the buffer causes an exception. Gas costs: `3 + 3 * ceil(amount / 32)` (same as `CALLDATACOPY`)