Skip to content
58 changes: 32 additions & 26 deletions EIPS/eip-7069.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
eip: 7069
title: Revamped CALL instructions
description: Introduce CALL2, DELEGATECALL2 and STATICCALL2 with simplified semantics
description: Introduce EXTCALL, EXTDELEGATECALL and EXTSTATICCALL with simplified semantics
author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Danno Ferrin (@shemnon), Andrei Maiboroda (@gumb0), Charles Cooper (@charles-cooper)
discussions-to: https://ethereum-magicians.org/t/eip-revamped-call-instructions/14432
status: Draft
Expand All @@ -13,7 +13,7 @@ requires: 150, 211, 214, 2929

## Abstract

Introduce three new call instructions, `CALL2`, `DELEGATECALL2` and `STATICCALL2`, with simplified semantics. Introduce another instruction, `RETURNDATALOAD` for loading a word from return data into stack. The existing call instructions remain unchanged.
Introduce three new call instructions, `EXTCALL`, `EXTDELEGATECALL` and `EXTSTATICCALL`, with simplified semantics. Introduce another instruction, `RETURNDATALOAD` for loading a word from return data into stack. The existing call instructions remain unchanged.

The new instructions do not allow specifying a gas limit, but rather rely on the "63/64th rule" ([EIP-150](./eip-150.md)) to limit gas. An important improvement is the rules around the "stipend" are simplified, and callers do not need to perform special calculation whether the value is sent or not.

Expand Down Expand Up @@ -50,31 +50,32 @@ Lastly, the introduction of the `RETURNDATA*` instructions ([EIP-211](./eip-211.

We introduce four new instructions:

- `CALL2` (`0xf8`) with arguments `(target_address, input_offset, input_size, value)`
- `DELEGATECALL2` (`0xf9`) with arguments `(target_address, input_offset, input_size)`
- `STATICCALL2` (`0xfb`) with arguments `(target_address, input_offset, input_size)`
- `EXTCALL` (`0xf8`) with arguments `(target_address, input_offset, input_size, value)`
- `EXTDELEGATECALL` (`0xf9`) with arguments `(target_address, input_offset, input_size)`
- `EXTSTATICCALL` (`0xfb`) with arguments `(target_address, input_offset, input_size)`
- `RETURNDATALOAD` (`0xf7`) with argument `offset`

Execution semantics of `*CALL2`:
Execution semantics of `EXT*CALL`:

1. Charge `WARM_STORAGE_READ_COST` (100) gas.
2. Pop required arguments from stack, fail with error on stack underflow.
4. If `value` is non-zero:
3a. Fail with error if the current frame is in `static-mode`.
3b. Fail with error if the balance of the current account is less than `value`.
3c. Charge `CALL_VALUE_COST` gas.
2. Pop required arguments from stack, halt with exceptional failure on stack underflow.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems at this point EIP tries to maintain compatibility with both legacy and EOF, so maybe

Suggested change
2. Pop required arguments from stack, halt with exceptional failure on stack underflow.
2. Pop required arguments from stack, halt with exceptional failure on stack underflow.
- *Note: When implemented in EOF, stack underflow check is done during stack validation, and runtime check is omitted.*

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is for this EIP to be fully legacy-complete and EOF-independent. EOF (Megaspec) lists the differences required it to be EOF-complete (which is lack of runtime stack underflow check and banning new opcodes in legacy).

Is there any other piece which is EOF-only in this EIP?

(this is just a question to your comment, I have no objections towards adding this note for completeness, and I'll add it)

3. If `value` is non-zero:
- Halt with exceptional failure if the current frame is in `static-mode`.
- Charge `CALL_VALUE_COST` gas.
4. Peform (and charge for) memory expansion using `[input_offset, input_size]`.
5. If `target_address` is not in the `warm_account_list`, charge `COLD_ACCOUNT_ACCESS - WARM_STORAGE_READ_COST` (2500) gas.
6. If `target_address` is not in the state and the call configuration would result in account creation, charge `ACCOUNT_CREATION_COST` (25000) gas.
- The only such case in this EIP is if `value` is non-zero.
7. Calculate the gas available to callee as caller's remaining gas reduced by `max(ceil(gas/64), MIN_RETAINED_GAS)` (`MIN_RETAINED_GAS` is 5000).
8. Fail with error if the gas available to callee at this point is less than `MIN_CALLEE_GAS` (2300).
9. Perform the call with the available gas and configuration.
10. Push a status code on the stack:
11a. `0` if the call was successful.
11b. `1` if the call has reverted.
11c. `2` if the call has failed.
11. Gas not used by the callee is returned to the caller.
7. Calculate the gas available to callee as caller's remaining gas reduced by `max(floor(gas/64), MIN_RETAINED_GAS)`.
8. Halt with exceptional failure if the gas available to callee at this point is less than `MIN_CALLEE_GAS`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this exceptional failure? Seems could be light failure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR only changes the language on this one. And I see it as a sensible failure mode - consistent with an OOG. But light failure also makes sense on 2nd thought. @chfast @axic @shemnon any more opinions on this, any objections to changing to light failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed as suggested here: 743a3fb

9. If `value` is non-zero, fail lightly if the balance of the current account is less than `value` (push `1` on the stack, only consume gas charged until this point).
10. Fail lightly if call stack depth equals `1024` (push `1` on the stack, only consume gas charged until this point).
11. Perform the call with the available gas and configuration.
12. Push a status code on the stack:
- `0` if the call was successful.
- `1` if the call has reverted (also can be pushed earlier in a light failure scenario).
- `2` if the call has failed.
13. Gas not used by the callee is returned to the caller.

Execution semantics of `RETURNDATALOAD`:

Expand Down Expand Up @@ -114,10 +115,13 @@ Before the 63/64th rule was introduced, it was required to calculate available g

We have changed the ruleset:

1. Removed the call depth check.
2. Use the 63/64th rule, but
2a. ensure that at least 5000 gas is retained prior to executing the callee,
2b. ensure that at least 2300 gas is available to the callee.
1. Use the 63/64th rule, but
- Ensure that at least `MIN_RETAINED_GAS` gas is retained prior to executing the callee,
- Ensure that at least `MIN_CALLEE_GAS` gas is available to the callee.

Removing the call stack depth check was initially considered, but this would be incompatible with the original `*CALL` instructions, as well as `CREATE*` instructions, which can be intertwined with the new `EXT*CALL` instructions in the call stack. As such, keepeing the call stack depth involves the least change for legacy code.

Also, we find the simple (as opposed to the complex 63/64th rule) hard cap reassuring, that the call stack depth is limited, in case the gas rules can be bypassed. Lastly, the amount of gas to reach depth of 1024 is huge, but not absurdly huge and we want to avoid constrain ourselves by relying this check on current gas limits.

### Output buffers

Expand All @@ -129,18 +133,20 @@ Current call instructions return a boolean value to signal success: 0 means fail

We change the value from boolean to a status code, where `0` signals success and thus it will be possible to introduce more non-success codes in the future, if desired.

Status code `1` is used for both reverts coming from the callee frame and light failures encountered in the execution of the instructions. The reason for combining them is keeping the semantics similar to the original CALLs - both scenario preserve unused gas and continue being indistinguishable to the caller.

### Parameter order

The order of parameters has been changed to move the `value` field to be the last. This allows the instructions to have identical encoding with the exception of the last parameter, and simplifies EVM and compiler implementations slightly.

### Opcode encoding

Instead of introducing three new `*CALL2` opcodes we have discussed a version with an immediate configuration byte (flags). There are two main disadvantages to this:
Instead of introducing three new `EXT*CALL` opcodes we have discussed a version with an immediate configuration byte (flags). There are two main disadvantages to this:

1. Some combination of flags may not be useful/be invalid, and this increases the testing/implementation surface.
2. The instruction could take variable number of stack items (i.e. `value` for `CALL2`) would be a brand new concept no other instruction has.
2. The instruction could take variable number of stack items (i.e. `value` for `EXTCALL`) would be a brand new concept no other instruction has.

It is also useful to have these as new opcodes instead of modifying the exiting CALL series inside of EOF. This creates an "escape hatch" in case gas observability needs to be restored to EOF contracts. This is done by adding the GAS and original CALL series opcodes to the valid EOF opcode list.
It is also useful to have these as new opcodes instead of modifying the existing CALL series inside of EOF. This creates an "escape hatch" in case gas observability needs to be restored to EOF contracts. This is done by adding the GAS and original CALL series opcodes to the valid EOF opcode list.

### `CALLCODE`

Expand Down