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:
parent
ef1bef16e5
commit
6e1c16627a
@ -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
77
x/evm/spec/01_concepts.md
Normal 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
81
x/evm/spec/02_state.md
Normal 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)
|
7
x/evm/spec/03_state_transitions.md
Normal file
7
x/evm/spec/03_state_transitions.md
Normal 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
32
x/evm/spec/04_messages.md
Normal 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
49
x/evm/spec/05_abci.md
Normal 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
20
x/evm/spec/06_events.md
Normal 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
54
x/evm/spec/07_params.md
Normal 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)
|
@ -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
|
||||
```
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user