New design for EIP-1702

This commit is contained in:
Wei Tang 2019-04-02 15:35:24 +02:00
parent 54a1eb3fdc
commit 5b57472484
1 changed files with 85 additions and 23 deletions

View File

@ -11,57 +11,119 @@ created: 2017-12-30
## 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.
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.
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.
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
After the first hard fork using this scheme, newly created account state stored in the world state trie is changed to become a five-item RLP encoding: `nonce`, `balance`, `storageRoot`, `codeHash` and `version`. The `version` field defines that when a contract call transaction or `CALL` opcode is executed against this contract, which version of the virtual machine should be used. Four-item RLP encoding account state are considered to have the `version` 0.
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.
`CREATE`/`CREATE2` opcode, the contract creation transaction, and a normal call which initialize a new account, would only deploy accounts of the newest version.
### Contract Deployment
The behavior of `CALLCODE` and `DELEGATECALL` are not affected by the hard fork -- they would fetch the contract code (even if the contract is deployed in a newer version) and still use the virtual machine version defined in the current contract (or in the case of within the input of contract creation transaction or `CREATE` opcode, the newest virtual machine version) to execute the code.
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.
If a message call transaction creates a new account, the newly created account will have the newest account version number in the network.
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`.
Precompiled contracts, once created, use the given account version. If the account does not exist yet, newest version of the VM is used (as in the standard message call transaction). This is made so that VMs do not need full permission of the state trie but only with in the smart contract boundary. It is expected that once a precompiled contract is created, its behavior will not change.
### Validation
### Gas Boundary
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.
With the boundary between the VM and the blockchain, we don't consider there're additional gas cost involved. VM used defines the gas table applied. As examples, intrinsic gas only applies to actual transactions so only the newest ones are used. `CALL` and `CREATE` will use the old gas cost as the opcode gas cost, and once switched to the new VM, use the new gas cost from there.
If the validation phrase fails, deployment does not proceed and return
out-of-gas.
## Example
### Contract Execution
Consider we would like to have the REVERT opcode hard fork in Ethereum Classic using this scheme. After the hard fork, we have:
VM version used in contract execution is determined via calling
`address` (`I_a` in yellow paper). Precompiled contract does not have
version.
* Existing accounts are still of four-item RLP encoding. When a transaction has `to` field pointing to them, they're executed using version 0 of EVM. REVERT is the same as INVALID and consumes all the gases.
* A new contract creation transaction is executed using version 1 virtual machine, and would only create version 1 account on the blockchain. When executing them, it uses version 1 of EVM and REVERT uses the new behavior.
* When a version 0 account issues a CALL to version 1 account, sub-execution of the version 1 account uses the version 1 virtual machine.
* When a version 1 account issues a CALL to version 0 account, sub-execution of the version 0 account uses the version 0 vritual machine.
* When a version 0 account issues a CREATE, it always uses the newest version of the virtual machine, so it only creates version 1 new accounts.
### 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 for Contract Creation Transaction
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.
## Discussions
### Performance
Currently nearly all full node implementations uses config parameters to decide which virtual machine version to use. Switching vitual 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.
Currently nearly all full node implementations uses config parameters
to decide which virtual machine version to use. Switching vitual
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.
### Smart Contract Boundary and Formal Verification
Many current efforts are on-going for getting smart contracts formally verified. However, for any hard fork that introduces new opcodes or change behaviors of existing opcodes would break the verification of an existing contract has previously be formally verified. Using the scheme described here, we define the boundary of how a smart contract interacts with the blockchain and it might help the formal verification efforts:
Many current efforts are on-going for getting smart contracts formally
verified. However, for any hard fork that introduces new opcodes or
change behaviors of existing opcodes would break the verification of
an existing contract has previously be formally verified. Using the
scheme described here, we define the boundary of how a smart contract
interacts with the blockchain and it might help the formal
verification efforts:
* A smart contract has only immutable access to information of blockchain account balances and codes.
* A smart contract has only immutable access to information of
blockchain account balances and codes.
* A smart contract has only immutable access of block information.
* A smart contract or a contract creation transaction can modify only its own storage and codes.
* A smart contract can only interact with the blockchain in a mutable way using `CALL` or `CREATE`.
* A smart contract or a contract creation transaction can modify only
its own storage and codes.
* A smart contract can only interact with the blockchain in a mutable
way using `CALL` or `CREATE`.
### 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.
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.