--- eip: 1702 title: Generalized Account Versioning Scheme author: Wei Tang (@sorpaas) discussions-to: https://github.com/sorpaas/EIPs/issues/2 status: Draft type: Standards Track category: Core created: 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 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 calling `address`. #### Alternative Design 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. Apply the following cause on contract deployment for all `CREATE`, `CREATE2` and contract deployment transaction. * If the `version` of caller (determined by `I_a`) is zero, then `CREATE` and `CREATE2` will always deploy contract with version zero. * If the `version` of caller (determined by `I_a`) 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`. ### 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 Execution VM version used in contract execution is determined via calling `address` (`I_a` in yellow paper). ### 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`. Before a contract creation transaction is executed, run *validation* on the contract creation code. If it does not pass, return out-of-gas. #### Alternative Design This provides an alternative design that allows contract to be created in multiple versions. 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 in `version` supplied. If `version` is not supported or *validation* 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`. ## 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.