Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

Commit

Permalink
evm: module specification (#538)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
fedekunze authored Dec 9, 2020
1 parent ef1bef1 commit 6e1c166
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 14 deletions.
17 changes: 7 additions & 10 deletions x/evm/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)

Expand Down
77 changes: 77 additions & 0 deletions x/evm/spec/01_concepts.md
Original file line number Diff line number Diff line change
@@ -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 changes: 81 additions & 0 deletions x/evm/spec/02_state.md
Original file line number Diff line number Diff line change
@@ -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 changes: 7 additions & 0 deletions x/evm/spec/03_state_transitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!--
order: 3
-->

# State Transitions

<!-- define state transitions for accounts and storage -->
32 changes: 32 additions & 0 deletions x/evm/spec/04_messages.md
Original file line number Diff line number Diff line change
@@ -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 changes: 49 additions & 0 deletions x/evm/spec/05_abci.md
Original file line number Diff line number Diff line change
@@ -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 changes: 20 additions & 0 deletions x/evm/spec/06_events.md
Original file line number Diff line number Diff line change
@@ -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 changes: 54 additions & 0 deletions x/evm/spec/07_params.md
Original file line number Diff line number Diff line change
@@ -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)
Loading

0 comments on commit 6e1c166

Please sign in to comment.