evm: module specification (#538)

* evm: module specification

* params and events

* readme and messages

* minor updates

* concepts

* genesis state concept

* begin and end block

* update parameters and genesis

* state objects

* state table

* use permalink

* init and export genesis

* update abci

* extra eips param

* review comments

* precision

* link to photon doc
This commit is contained in:
Federico Kunze 2020-12-09 16:11:15 -03:00 committed by GitHub
parent ef1bef16e5
commit 6e1c16627a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 375 additions and 14 deletions

View File

@ -7,9 +7,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/cosmos/ethermint/x/evm/types"
)
// BeginBlock sets the block hash -> block height map for the previous block height
@ -22,7 +21,12 @@ func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
// Gas costs are handled within msg handler so costs should be ignored
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
k.SetBlockHash(ctx, req.Header.LastBlockId.GetHash(), req.Header.GetHeight()-1)
// Set the hash -> height and height -> hash mapping.
hash := req.Header.LastBlockId.GetHash()
height := req.Header.GetHeight() - 1
k.SetHeightHash(ctx, uint64(height), common.BytesToHash(hash))
k.SetBlockHash(ctx, hash, height)
// reset counters that are used on CommitStateDB.Prepare
k.Bloom = big.NewInt(0)
@ -37,13 +41,6 @@ func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.Valid
// Gas costs are handled within msg handler so costs should be ignored
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
// Set the hash for the current height.
// NOTE: we set the hash here instead of on BeginBlock in order to set the final block prior to
// an upgrade. If we set it on BeginBlock the last block from prior to the upgrade wouldn't be
// included on the store.
hash := types.HashFromContext(ctx)
k.SetHeightHash(ctx, uint64(ctx.BlockHeight()), hash)
// Update account balances before committing other parts of state
k.UpdateAccounts(ctx)

77
x/evm/spec/01_concepts.md Normal file
View File

@ -0,0 +1,77 @@
<!--
order: 1
-->
# Concepts
## EVM
The Ethereum Virtual Machine [EVM](https://ethereum.org/en/developers/docs/evm/) is a state machine
that provides the necessary tools to run or create a contract on a given state.
## State DB
The `StateDB` interface from geth represents an EVM database for full state querying of both
contracts and accounts. The concrete type that fulfills this interface on Ethermint is the
`CommitStateDB`.
## State Object
A `stateObject` represents an Ethereum account which is being modified.
The usage pattern is as follows:
* First you need to obtain a state object.
* Account values can be accessed and modified through the object.
## Genesis State
The `x/evm` module `GenesisState` defines the state necessary for initializing the chain from a previous exported height.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/genesis.go#L14-L20
### Genesis Accounts
The `GenesisAccount` type corresponds to an adaptation of the Ethereum `GenesisAccount` type. Its
main difference is that the one on Ethermint uses a custom `Storage` type that uses a slice instead
of maps for the evm `State` (due to non-determinism), and that it doesn't contain the private key
field.
It is also important to note that since the `auth` and `bank` SDK modules manage the accounts and
balance state, the `Address` must correspond to an `EthAccount` that is stored in the `auth`'s
module `AccountKeeper` and the balance must match the balance of the `EvmDenom` token denomination
defined on the `GenesisState`'s `Param`. The values for the address and the balance amount maintain
the same format as the ones from the SDK to make manual inspections easier on the genesis.json.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/genesis.go#L22-L30
### Transaction Logs
On every Ethermint transaction, its result contains the Ethereum `Log`s from the state machine
execution that are used by the JSON-RPC Web3 server for for filter querying. Since Cosmos upgrades
don't persist the transactions on the blockchain state, we need to persist the logs the EVM module
state to prevent the queries from failing.
`TxsLogs` is the field that contains all the transaction logs that need to be persisted after an
upgrade. It uses an array instead of a map to ensure determinism on the iteration.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/logs.go#L12-L18
### Chain Config
The `ChainConfig` is a custom type that contains the same fields as the go-ethereum `ChainConfig`
parameters, but using `sdk.Int` types instead of `*big.Int`. It also defines additional YAML tags
for pretty printing.
The `ChainConfig` type is not a configurable SDK `Param` since the SDK does not allow for validation
against a previous stored parameter values or `Context` fields. Since most of this type's fields
rely on the block height value, this limitation prevents the validation of of potential new
parameter values against the current block height (eg: to prevent updating the config block values
to a past block).
If you want to update the config values, use an software upgrade procedure.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/chain_config.go#L16-L45
### Params
See the [params](07_params.md) document for further information about parameters.

81
x/evm/spec/02_state.md Normal file
View File

@ -0,0 +1,81 @@
<!--
order: 2
-->
# State
The `x/evm` module keeps the following objects in state:
| | Key | Value |
|-----------------|---------------------------------------------------|---------------------------|
| Block Height | `[]byte{1} + []byte(block.Hash)` | `BigEndian(block.Height)` |
| Bloom | `[]byte{2} + []byte(block.Height)` | `[]byte(Bloom)` |
| Tx Logs | `[]byte{3} + []byte(tx.Hash)` | `amino([]Log)` |
| Account Code | `[]byte{4} + []byte(code.Hash)` | `[]byte(Code)` |
| Account Storage | `[]byte{5} + []byte(address) + []byte(state.Key)` | `[]byte(state.Value)` |
| Chain Config | `[]byte{6}` | `amino(ChainConfig)` |
## `CommitStateDB`
`StateDB`s within the ethereum protocol are used to store anything within the IAVL tree. `StateDB`s
take care of caching and storing nested states. It's the general query interface to retrieve
contracts and accounts
The Ethermint `CommitStateDB` is a concrete type that implements the EVM `StateDB` interface.
Instead of using a trie and database for querying and persistence, the `CommitStateDB` uses
`KVStores` (key-value stores) and Cosmos SDK `Keeper`s to facilitate state transitions.
The `CommitStateDB` contains a store key that allows the DB to write to a concrete subtree of the
multistore that is only accessible to the EVM module.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/statedb.go#L33-L85
The functionalities provided by the Ethermint `StateDB` are:
* CRUD of `stateObject`s and accounts:
* Balance
* Code
* Nonce
* State
* EVM module parameter getter and setter
* State transition logic
* Preparation: transaction index and hash, block hash
* CRUD of transaction logs
* Aggregate queries
* Snapshot state
* Identify current state with a revision
* Revert state to a given revision
* State transition and persistence
* Preparation: tx and block context
* Commit state objects
* Finalise state objects
* Export state for upgrades
* Auxiliary functions
* Copy state
* Reset state
## State Objects
State objects are used by the VM which is unable to deal with database-level errors. Any error that occurs during a database read is memoized here and will eventually be returned by `StateDB.Commit`.
The Ethermint `stateObject` is a concrete type that mimics the functionality from the `go-ethereum`
private `stateObject` type. It keeps track of the interim values for the contract bytecode, storage
state and balance of an `EthAccount`.
The storage entries (original and "dirty") for each state object are represented as slices instead
of maps since latter can cause non-deterministic block app hashes, which result in the chain
halting.
When a `stateObject` is committed during `EndBlock`. It sets sets the account contract code to store, as well as the dirty storage state. The account's nonce and the account balance are updated by calling the `auth` and `bank` module setter functions, respectively.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/state_object.go#L49-L81
The functionalities provided by the Ethermint `stateObject` are:
* Storage state getter and setter (temporary)
* Contract bytecode getter and setter (temporary)
* Balance getter and setter (temporary)
* Balance accounting (temporary)
* Account nonce and address getter and setter (temporary)
* Auxiliary functions: copy, RLP encoding, empty
* Commit state object (final)

View File

@ -0,0 +1,7 @@
<!--
order: 3
-->
# State Transitions
<!-- define state transitions for accounts and storage -->

32
x/evm/spec/04_messages.md Normal file
View File

@ -0,0 +1,32 @@
<!--
order: 4
-->
# Messages
## MsgEthereumTx
An EVM state transition can be achieved by using the `MsgEthereumTx`. This message encapsulates an
Ethereum transaction as an SDK message and contains the necessary transaction data fields.
One remark about the `MsgEthereumTx` is that it implements both the [`sdk.Msg`](https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/types/tx_msg.go#L7-L29) and [`sdk.Tx`](https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/types/tx_msg.go#L33-L41)
interfaces (generally SDK messages only implement the former, while the latter is a group of
messages bundled together). The reason of this, is because the `MsgEthereumTx` must not be included in a [`auth.StdTx`](https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/x/auth/types/stdtx.go#L23-L30) (SDK's standard transaction type) as it performs gas and fee checks using the Ethereum logic from Geth instead of the Cosmos SDK checks done on the `auth` module `AnteHandler`.
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/msg.go#L117-L124
+++ https://github.com/cosmos/ethermint/blob/v0.3.1/x/evm/types/tx_data.go#L12-L29
This message validation is expected to fail if:
- `Data.Price` (i.e gas price) is ≤ 0.
- `Data.Amount` is negative
The transaction execution is expected to fail if:
- Any of the custom `AnteHandler` Ethereum decorators checks fail:
- Minimum gas amount requirements for transaction
- Tx sender account doesn't exist or hasn't enough balance for fees
- Account sequence doesn't match the transaction `Data.AccountNonce`
- Message signature verification fails
- EVM contract creation (i.e `evm.Create`) fails, or `evm.Call` fails

49
x/evm/spec/05_abci.md Normal file
View File

@ -0,0 +1,49 @@
<!--
order: 5
-->
# ABCI
## InitGenesis
`InitGenesis` initializes the EVM module genesis state by setting the `GenesisState` fields to the
store. In particular it sets the parameters, configuration, accounts and transaction logs.
The function also performs the invariant that the EVM balance from the `GenesisAccount` matches the
balance amount from the `EthAccount` as defined on the `auth` module.
## ExportGenesis
The `ExportGenesis` ABCI function exports the genesis state of the EVM module. In particular, it
retrieves all the accounts with their bytecode, balance and storage, the transaction logs, and the
EVM parameters and chain configuration.
## BeginBlock
The EVM module `BeginBlock` logic is executed prior to handling the state transitions from the
transactions. The main objective of this function is to:
* Set the block header hash mappings to the module state (`hash -> height` and `height -> hash`).
This workaround is due to the fact that until the `v0.34.0` Tendermint version it wasn't possible
to query and subscribe to a block by hash.
* Reset bloom filter and block transaction count. These variables, which are fields of the EVM
`Keeper`, are updated on every EVM transaction.
## EndBlock
The EVM module `EndBlock` logic occurs after executing all the state transitions from the
transactions. The main objective of this function is to:
* Update the accounts. This operation retrieves the current account and balance values for each
state object and updates the account represented on the stateObject with the given values. This is
done since the account might have been updated by transactions other than the ones defined by the
`x/evm` module, such as bank send or IBC transfers.
* Commit dirty state objects and delete empty ones from the store. This operation writes the
contract code to the key value store in the case of contracts and updates the account's balance,
which is set to the the bank module's `Keeper`.
* Clear account cache. This clears cache of state objects to handle account changes outside of the
EVM.
* Store the block bloom to state. This is due for Web3 compatibility as the Ethereum headers contain
this type as a field. The Ethermint RPC uses this query to construct an Ethereum Header from a
Tendermint Header.

20
x/evm/spec/06_events.md Normal file
View File

@ -0,0 +1,20 @@
<!--
order: 6
-->
# Events
The EVM module emits the Cosmos SDK [events](./../../../docs/quickstart/events.md#sdk-and-tendermint-events) after a state execution. It can be expected that the type `message`, with an
attribute key of `action` will represent the first event for each message being processed as emitted
by the Cosmos SDK's `Baseapp` (i.e the the basic application that implements Tendermint Core's ABCI
interface).
## MsgEthereumTx
| Type | Attribute Key | Attribute Value |
|----------|---------------|-----------------|
| ethereum | `"amount"` | `{amount}` |
| ethereum | `"recipient"` | `{eth_address}` |
| message | `"sender"` | `{eth_address}` |
| message | `"action"` | `"ethereum"` |
| message | `"module"` | `"evm"` |

54
x/evm/spec/07_params.md Normal file
View File

@ -0,0 +1,54 @@
<!--
order: 7
-->
# Parameters
The evm module contains the following parameters:
| Key | Type | Default Value |
|----------------|--------|---------------|
| `EVMDenom` | string | `"aphoton"` |
| `EnableCreate` | bool | `true` |
| `EnableCall` | bool | `true` |
| `ExtraEIPs` | []int | TBD |
## EVM denom
The evm denomination parameter defines the token denomination used on the EVM state transitions and
gas consumption for EVM messages.
The EVM Denom is used on the following cases:
* `AnteHandler`: for calculating sufficient balance to pay for gas cost or transaction fees.
* `journal`: to revert certain state executions (`balanceChange` and `suicideChange`).
* `stateObject`: to track the `evm_denom` balance of the object account.
* `CommitStateDB`: to update account balance from an existing state object.
For example, on Ethereum, the `evm_denom` would be `ETH`. In the case of Ethermint, the default denomination is the [atto photon](./../../../docs/basics/photon.md). In terms of precision, the `PHOTON` and `ETH` share the same value, _i.e_ `1 PHOTON = 10^18 atto photon` and `1 ETH = 10^18 wei`.
::: danger
SDK applications that want to import the EVM module as a dependency will need to set their own `evm_denom` (i.e not `"aphoton"`).
:::
## Enable Create
The enable create parameter toggles state transitions that use the `vm.Create` function. When the
parameter is disabled, it will prevent all contract creation functionality.
## Enable Transfer
The enable transfer toggles state transitions that use the `vm.Call` function. When the parameter is
disabled, it will prevent transfers between accounts and executing a smart contract call.
## Extra EIPs
The extra EIPs parameter defines the set of activateable Ethereum Improvement Proposals ([EIPs](https://ethereum.org/en/eips/))
on the Ethereum VM `Config` that apply custom jump tables.
The supported activateable EIPS are:
* [EIP 1344](https://eips.ethereum.org/EIPS/eip-1344)
* [EIP 1884](https://eips.ethereum.org/EIPS/eip-1884)
* [EIP 2200](https://eips.ethereum.org/EIPS/eip-2200)
* [EIP 2315](https://eips.ethereum.org/EIPS/eip-2315)
* [EIP 2929](https://eips.ethereum.org/EIPS/eip-2929)

View File

@ -9,8 +9,52 @@ parent:
## Abstract
<!-- TODO: -->
This document defines the specification of the Ethereum Virtual Machine (EVM) as a Cosmos SDK module.
## Content
## Contents
<!-- TODO: -->
1. **[Concepts](01_concepts.md)**
2. **[State](02_state.md)**
3. **[State Transitions](03_state_transitions.md)**
4. **[Messages](04_messages.md)**
5. **[ABCI](05_abci.md)**
6. **[Events](06_events.md)**
7. **[Parameters](07_params.md)**
## Module Architecture
> **NOTE:**: If you're not familiar with the overall module structure from
the SDK modules, please check this [document](https://docs.cosmos.network/master/building-modules/structure.html) as
prerequisite reading.
```shell
evm/
├── client
│ └── cli
│ ├── query.go # CLI query commands for the module
   └── tx.go # CLI transaction commands for the module
├── keeper
│ ├── keeper.go # ABCI BeginBlock and EndBlock logic
│ ├── keeper.go # Store keeper that handles the business logic of the module and has access to a specific subtree of the state tree.
│ ├── params.go # Parameter getter and setter
│ ├── querier.go # State query functions
│ └── statedb.go # Functions from types/statedb with a passed in sdk.Context
├── types
│   ├── chain_config.go
│   ├── codec.go # Type registration for encoding
│   ├── errors.go # Module-specific errors
│   ├── events.go # Events exposed to the Tendermint PubSub/Websocket
│   ├── genesis.go # Genesis state for the module
│   ├── journal.go # Ethereum Journal of state transitions
│   ├── keys.go # Store keys and utility functions
│   ├── logs.go # Types for persisting Ethereum tx logs on state after chain upgrades
│   ├── msg.go # EVM module transaction messages
│   ├── params.go # Module parameters that can be customized with governance parameter change proposals
│   ├── state_object.go # EVM state object
│   ├── statedb.go # Implementation of the StateDb interface
│   ├── storage.go # Implementation of the Ethereum state storage map using arrays to prevent non-determinism
│   └── tx_data.go # Ethereum transaction data types
├── genesis.go # ABCI InitGenesis and ExportGenesis functionality
├── handler.go # Message routing
└── module.go # Module setup for the module manager
```

View File

@ -32,7 +32,7 @@ type revision struct {
// CommitStateDB implements the Geth state.StateDB interface. Instead of using
// a trie and database for querying and persistence, the Keeper uses KVStores
// and an account mapper is used to facilitate state transitions.
// and an AccountKeeper to facilitate state transitions.
//
// TODO: This implementation is subject to change in regards to its statefull
// manner. In otherwords, how this relates to the keeper in this module.