EIPs/EIPS/eip-1702.md
Wei Tang 5ebc5df9ec Automatically merged updates to draft EIP(s) 1702 (#2131)
Hi, I'm a bot! This change was automatically merged because:

 - It only modifies existing Draft or Last Call EIP(s)
 - The PR was approved or written by at least one author of each modified EIP
 - The build is passing
2019-06-21 15:10:05 +12:00

11 KiB

eip title author discussions-to status type category created
1702 Generalized Account Versioning Scheme Wei Tang (@sorpaas) https://github.com/sorpaas/EIPs/issues/2 Draft Standards Track Core 2017-12-30

Simple Summary

Introduce account versioning for smart contracts so upgrading the VM or introducing new VMs can be easier.

Abstract

This defines a method of hard forking while maintaining the exact functionality of existing account by allowing multiple versions of the virtual machines to execute in the same block. This is also useful to define future account state structures when we introduce the on-chain WebAssembly virtual machine.

Motivation

By allowing account versioning, we can execute different virtual machine for contracts created at different times. This allows breaking features to be implemented while making sure existing contracts work as expected.

Note that this specification might not apply to all hard forks. We have emergency hard forks in the past due to network attacks. Whether they should maintain existing account compatibility should be evaluated in individual basis. If the attack can only be executed once against some particular contracts, then the scheme defined here might still be applicable. Otherwise, having a plain emergency hard fork might still be a good idea.

Specification

Account State

Re-define account state stored in the world state trie to have 5 items: nonce, balance, storageRoot, codeHash, and version. The newly added field version is a 256-bit integer. When version is zero, the account is RLP-encoded with the first 4 items. When version is not zero, the account is RLP-encoded with 5 items.

Contract Execution

When fetching an account code from state, we always fetch the associated version field together. We refer to this as the code's version below. The code of the account is always executed in the code's version.

In particular, this means that for DELEGATECALL and CALLCODE, the version of the execution call frame is the same as delegating/receiving contract's version.

Contract Deployment

In Ethereum, a contract has a deployment method, either by a contract creation transaction, or by another contract. If we regard this deployment method a contract's parent, then we find them forming a family of contracts, with the root being a contract creation transaction.

We let a family of contracts to always have the same version. That is, CREATE and CREATE2 will always deploy contract that has the same version as the code's version.

Validation

A new phrase, validation is added to contract deployment (by CREATE / CREATE2 opcodes, or by contract creation transaction). When version is 0, the phrase does nothing and always succeeds. Future VM versions can define additional validation that has to be passed.

If the validation phrase fails, deployment does not proceed and return out-of-gas.

Contract Creation Transaction

Define LATEST_VERSION in a hard fork to be the latest supported VM version. A contract creation transaction is always executed in LATEST_VERSION (which means the code's version is LATEST_VERSION), and deploys contracts of LATEST_VERSION. Before a contract creation transaction is executed, run validation on the contract creation code. If it does not pass, return out-of-gas.

Precompiled Contract and Externally-owned Address

Precompiled contracts and externally-owned addresses do not have version. If a message-call transaction or CALL / CALLCODE / STATICCALL / DELEGATECALL touches a new externally-owned address or a non-existing precompiled contract address, it is always created with version field being 0.

Alternative Specification

The above "Specification" section is commonly known as EIP-1702 variant I, below we define an alternative design, commonly known as EIP-1702 variant II.

Applies all sections in "Specification" except "Contract Deployment", and change it as below.

Contract Deployment

This provides an alternative design that allows CREATE, CREATE2 and contract creation transaction to deploy contract whose version are different.

The client maintains a mapping V of currently supported version prefix (for example, \0asm) to version number. All version prefixes have the invariant that given any prefix in mapping a and b, a is not b's prefix. Version numbers in V cannot be zero.

For contract deployment transaction, run the following additional steps.

  • Check that the code starts with an prefix in V, with version number.
  • Use version's validation procedure to validate the whole code (with prefix).
  • Execute the code with the code's version being version.
  • Apply the deployment routine defined as below.

Apply the following cause on contract deployment for all CREATE, CREATE2 and contract deployment transaction's deployment phrase.

  • If the code's version is zero, then CREATE and CREATE2 will always deploy contract with version zero.
  • If the code's version is not zero, do the following checks and operations, and return out-of-gas if any of it fails:
    • Check that the code starts with an prefix in V, with version number.
    • Use version's validation procedure to validate the whole code (with prefix).
    • Deploy the contract with version.

Extensions

In relation to the above "Specification" section (EIP-1702 variant I), we have defined the base account versioning layer. The base account versioning layer is already useful by itself and can handle most EVM improvements. Below we define two specifications that can be deployed separately, which improves functionality of variant I.

Extending Contract Creation Transaction

The base account versioning layer only allows contract of the newest version to be deployed via contract creation transaction. This is a reasonable assumption for current Ethereum network, because most of new features added to EVM are additions, and developers almost never want to deploy contracts that are not of the newest version. In this section, we provide an extension to allow multiple versions of contracts to be deployed via contract creation transaction.

Add an additional field version (256-bit integer) in contract creation transaction. So it becomes nonce, gasprice, startgas, to, value, data, v, r, s, version. When signing or recovering, sign ten items, with v, r, s as defined by EIP-155.

The transaction would be executed with the code's version in version supplied, and deploys contract of version. If version is not supported or validation does not pass, return out-of-gas.

Extending CREATE and CREATE2

The base account versioning layer only allows contracts of the same version to be deployed through CREATE and CREATE2. In this section, we provide an extension to allow different versions of contracts to be deployed via them, by providing two new opcodes, VCREATE and VCREATE2.

Define two new opcodes VCREATE and VCREATE2 at 0xf6 and 0xf7 respectively. VCREATE takes 4 stack arguments (version, value, input offset, input size), and VCREATE2 takes 5 stack arguments (version, endowment, memory_start, memory_length, salt). Note that except the stack item version, other arguments are the same as CREATE and CREATE2.

The two new opcodes behave identically to CREATE and CREATE2, except that it deploys contracts with version specified by stack item version.

The network at all times maintains a constant list within the client of all deployable versions (which can be different from supported versions). Upon VCREATE and VCREATE2, if the specified version is not on the list of deployable versions, return out-of-gas.

Usage Template

This section defines how other EIPs might use this account versioning EIP. Note that currently we only define the usage template for base layer.

Account versioning is usually applied directly to a hard fork meta EIP. EIPs in the hard fork are grouped by the virtual machine type, for example, EVM and eWASM. For each of them, we define:

  • Version: a non-zero integer less than 2^256 that uniquely identifies this version. Note that it does not need to be sequential.
  • Parent version: the base that all new features derived from. With parent version of 0 we define the base to be legacy VM. Note that once a version other than 0 is defined, the legacy VM's feature set must be frozen. When defining an entirely new VM (such as eWASM), parent version does not apply.
  • Features: all additional features that are enabled upon this version.

Rationale

This introduces account versioning via a new RLP item in account state. The first design above gets account versioning by making the contract family always have the same version. In this way, versions are only needed to be provided by contract creation transaction, and there is no restrictions on formats of code for any version. If we want to support multiple newest VMs (for example, EVM and WebAssembly running together), then this requires alternative design in contract creation transaction section

The second design above requires new versions of VMs follow a formatting -- that it always has a prefix. In this way, the version can be derived from the prefix, thus allowing a contract family to have multiple versions. It also makes it so that we can pin contract creation transaction using only one VM version, and it can deploy other VM versions.

Alternatively, account versioning can also be done through:

  • EIP-1707 and EIP-1712: This makes an account's versioning soly dependent on its code header prefix. If with only EIP-1707, it is not possible to certify any code is valid, because current VM allows treating code as data. This can be fixed by EIP-1712, but the drawback is that it's potentially backward incompatible.
  • EIP-1891: Instead of writing version field into account RLP state, we write it in a separate contract. This can accomplish the same thing as this EIP and potentially reduces code complexity, but the drawback is that every code execution will require an additional trie traversal, which impacts performance.

Backwards Compatibility

Account versioning is fully backwards compatible, and it does not change how current contracts are executed.

Discussions

Performance

Currently nearly all full node implementations uses config parameters to decide which virtual machine version to use. Switching virtual machine version is simply an operation that changes a pointer using a different set of config parameters. As a result, this scheme has nearly zero impact to performance.

WebAssembly

This scheme can also be helpful when we deploy on-chain WebAssembly virtual machine. In that case, WASM contracts and EVM contracts can co-exist and the execution boundary and interaction model are clearly defined as above.

Test Cases and Implementations

To be added.