diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index 896a6657..8c8495a8 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -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) diff --git a/x/evm/spec/01_concepts.md b/x/evm/spec/01_concepts.md new file mode 100644 index 00000000..4ac6be4f --- /dev/null +++ b/x/evm/spec/01_concepts.md @@ -0,0 +1,77 @@ + + +# 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. diff --git a/x/evm/spec/02_state.md b/x/evm/spec/02_state.md new file mode 100644 index 00000000..5fc15d5c --- /dev/null +++ b/x/evm/spec/02_state.md @@ -0,0 +1,81 @@ + + +# 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) diff --git a/x/evm/spec/03_state_transitions.md b/x/evm/spec/03_state_transitions.md new file mode 100644 index 00000000..58c6c178 --- /dev/null +++ b/x/evm/spec/03_state_transitions.md @@ -0,0 +1,7 @@ + + +# State Transitions + + \ No newline at end of file diff --git a/x/evm/spec/04_messages.md b/x/evm/spec/04_messages.md new file mode 100644 index 00000000..d31efae8 --- /dev/null +++ b/x/evm/spec/04_messages.md @@ -0,0 +1,32 @@ + + +# 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 diff --git a/x/evm/spec/05_abci.md b/x/evm/spec/05_abci.md new file mode 100644 index 00000000..4f2db279 --- /dev/null +++ b/x/evm/spec/05_abci.md @@ -0,0 +1,49 @@ + + +# 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. diff --git a/x/evm/spec/06_events.md b/x/evm/spec/06_events.md new file mode 100644 index 00000000..7fbb807c --- /dev/null +++ b/x/evm/spec/06_events.md @@ -0,0 +1,20 @@ + + +# 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"` | diff --git a/x/evm/spec/07_params.md b/x/evm/spec/07_params.md new file mode 100644 index 00000000..85a02354 --- /dev/null +++ b/x/evm/spec/07_params.md @@ -0,0 +1,54 @@ + + +# 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) diff --git a/x/evm/spec/README.md b/x/evm/spec/README.md index fc9247e5..272704a6 100644 --- a/x/evm/spec/README.md +++ b/x/evm/spec/README.md @@ -9,8 +9,52 @@ parent: ## Abstract - +This document defines the specification of the Ethereum Virtual Machine (EVM) as a Cosmos SDK module. -## Content +## Contents - \ No newline at end of file +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 +``` diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index ac867142..b3fcd588 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -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.