From b2735dcd138ae14e4d7acd5e9194e392daaa4486 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2020 17:20:19 +0100 Subject: [PATCH 1/6] build(deps): bump actions/cache from v2.1.2 to v2.1.3 (#591) Bumps [actions/cache](https://github.com/actions/cache) from v2.1.2 to v2.1.3. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.2...0781355a23dac32fd3bac414512f4b903437991a) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-sims.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-sims.yml b/.github/workflows/release-sims.yml index 450a831dc..d3bd27297 100644 --- a/.github/workflows/release-sims.yml +++ b/.github/workflows/release-sims.yml @@ -23,7 +23,7 @@ jobs: - name: install runsim run: | export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 - - uses: actions/cache@v2.1.2 + - uses: actions/cache@v2.1.3 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary @@ -33,7 +33,7 @@ jobs: needs: install-runsim steps: - uses: actions/checkout@v2 - - uses: actions/cache@v2.1.2 + - uses: actions/cache@v2.1.3 with: path: ~/go/bin key: ${{ runner.os }}-go-runsim-binary From 71090323c0256e9d6bb38edc3c548b009b17657c Mon Sep 17 00:00:00 2001 From: Daniel Choi Date: Thu, 12 Nov 2020 11:42:24 -0800 Subject: [PATCH 2/6] docs: updates (#590) * gas docs * add period * pending state docs * format * fix links * add more to pendingstate docs * add more to gas docs * add hardspoon doc * minor fix to pendingstate doc * note on rlp encoding * usecase doc * update encoding doc * gas docs * hard spoon and reorder * fix links * encoding * pending state * final touches * update intro * use cases and resources * typo Co-authored-by: Federico Kunze Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- docs/basics/README.md | 1 + docs/basics/accounts.md | 2 +- docs/basics/gas.md | 81 +++++++++++++++++++++++++++++++++---- docs/basics/hard_spoon.md | 17 ++++++++ docs/basics/json_rpc.md | 2 +- docs/basics/transactions.md | 4 +- docs/core/README.md | 1 + docs/core/encoding.md | 69 +++++++++---------------------- docs/core/pending_state.md | 49 ++++++++++++++++++++++ docs/intro/README.md | 1 + docs/intro/overview.md | 32 +++++++++------ docs/intro/resources.md | 6 ++- docs/intro/use_cases.md | 50 +++++++++++++++++++++++ 13 files changed, 238 insertions(+), 77 deletions(-) create mode 100644 docs/basics/hard_spoon.md create mode 100644 docs/core/pending_state.md create mode 100644 docs/intro/use_cases.md diff --git a/docs/basics/README.md b/docs/basics/README.md index ff42b3b60..1d49ee839 100644 --- a/docs/basics/README.md +++ b/docs/basics/README.md @@ -13,5 +13,6 @@ This repository contains reference documentation on the basic concepts of Etherm 3. [Lifecycle of a transaction](./transactions.md) 4. [Photon](./photon.md) 5. [JSON-RPC Server](./json_rpc.md) +6. [Hard Spoon](./hard_spoon.md) After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material. diff --git a/docs/basics/accounts.md b/docs/basics/accounts.md index faf4df84d..6143702e4 100644 --- a/docs/basics/accounts.md +++ b/docs/basics/accounts.md @@ -73,7 +73,7 @@ curl -X GET "/auth/accounts/eth1f8rqrfwut7ngkxwth0gt99h0lxnxsp09ngvzwl" The Cosmos SDK Keyring output (i.e `ethermintcli keys`) only supports addresses and public keys in Bech32 format. ::: -To retrieve the Ethereum hex address using Web3, use the JSON-RPC `eth_accounts` endpoint: +To retrieve the Ethereum hex address using Web3, use the JSON-RPC [`eth_accounts`](./json_rpc.md#eth-accounts) endpoint: ```bash # query against a local node diff --git a/docs/basics/gas.md b/docs/basics/gas.md index db43f387e..0a1775e45 100644 --- a/docs/basics/gas.md +++ b/docs/basics/gas.md @@ -6,23 +6,88 @@ order: 3 Learn about the differences between `Gas` and `Fees` in Ethereum and Cosmos. {synopsis} -## Introduction to `Gas` in the SDK +## Pre-requisite Readings - +- [Cosmos SDK Gas](https://docs.cosmos.network/master/basics/gas-fees.html) {prereq} +- [Ethereum Gas](https://ethereum.org/en/developers/docs/gas/) {prereq} + +The concept of Gas represents the amount of computational effort required to execute specific operations on the state machine. + +Gas was created on Ethereum to disallow the EVM (Ethereum Virtual Machine) from running infinite +loops by allocating a small amount of monetary value into the system. A unit of gas, usually in a +form as a fraction of the native coin, is consumed for every operation on the EVM and requires a +user to pay for these operations. These operations consist in state transitions such as sending a +transaction or calling a contract. + +Exactly like Ethereum, Cosmos utilizes the concept of gas and this is how Cosmos tracks the resource +usage of operations during execution. Operations on Cosmos are represented as read or writes done to the chain's store. + +In Cosmos, a fee is calculated and charged to the user during a message execution. This fee is +calculated from the sum of all gas consumed in an message execution: + +``` +fee = gas * gas price +``` + +In both networks, gas is used to make sure that operations do not require an excess amount of +computational power to complete and as a way to deter bad-acting users from spamming the network. + +## Cosmos SDK `Gas` + +In the Cosmos SDK, gas is tracked in the main `GasMeter` and the `BlockGasMeter`: + +- `GasMeter`: keeps track of the gas consumed during executions that lead to state transitions. It is reset on every transaction execution. +- `BlockGasMeter`: keeps track of the gas consumed in a block and enforces that the gas does not go over a predefined limit. This limit is defined in the Tendermint consensus parameters and can be changed via governance parameter change proposals. + +More information regarding gas in Cosmos SDK can be found [here](https://docs.cosmos.network/master/basics/gas-fees.html). ## Matching EVM Gas consumption - +Ethermint is an EVM-compatible chain that supports Ethereum Web3 tooling. For this reason, gas +consumption must be equitable in order to accurately calculate the state transition hashes and exact +the behaviour that would be seen on the main Ethereum network (main net). + +In Cosmos, there are types of operations that are not triggered by transactions that can also result in state transitions. Concrete examples are the `BeginBlock` and `EndBlock` operations and the `AnteHandler` checks, which might also read and write to the store before running the state transition from a transaction. + +### `BeginBlock` and `EndBlock` + +These operations are defined by the Tendermint Core's Application Blockchain Interface (ABCI) and are defined by each Cosmos SDK module. As their name suggest, they are executed at the beginning and at the end of each block processing respectively (i.e pre and post transaction execution). Since these operations are not reflected on Ethereum, to match the the gas consumption we reset the main `GasMeter` to 0 on Ethermint's EVM module. + +### `AnteHandler` + +The Cosmos SDK [`AnteHandler`](https://docs.cosmos.network/master/basics/gas-fees.html#antehandler) +performs basic checks prior to transaction execution. These checks are usually signature +verification, transaction field validation, transaction fees, etc. + +Because the gas calculated in Ethermint is done by the `IntrinsicGas` method from go-ethereum, a +special `AnteHandler` that is customized for EVM transaction fee verification is required. This +allows Ethermint to generate the expected gas costs for operations done in the network and scale the +gas costs as it would in the Ethereum network. + +## Gas Refunds -## Gas refunds +In Ethereum, gas can be specified prior to execution and the remaining gas will be refunded back to +the user if any gas is left over - should fail with out of gas if not enough gas was provided. In +Ethermint, the concept of gas refunds does not exist and the fees paid is not refunded in part back +to the user. The fees exacted on a transaction will be collected by the validator and no refunds are +issued. Thus, it is extremely important to use the correct gas. - +To prevent overspending on fees, providing the `--gas-adjustment` flag for a cosmos transactions +will determine the fees automatically. Also the `eth_estimateGas` rpc call can be used to manually +get the correct gas costs for a transaction. -## AnteHandler +## 0 Fee Transactions -The `AnteHandler` is a special `handler` that is run for every transaction during `CheckTx` and `DeliverTx`, before the `handler` of each `message` in the transaction. `AnteHandler`s have a different signature than `handler`s +In Cosmos, a minimum gas price is not enforced by the `AnteHandler` as the `min-gas-prices` is +checked against the local node/validator. In other words, the minimum fees accepted are determined +by the validators of the network, and each validator can specify a different value for their fees. +This potentially allows end users to submit 0 fee transactions if there is at least one single +validator that is willing to include transactions with `0` gas price in their blocks proposed. - +For this same reason, in Ethermint it is possible to send transactions with `0` fees for transaction +types other than the ones defined by the `evm` module. EVM module transactions cannot have `0` fees +as gas is required inherently by Ethereum. This check is done by the evm transactions +`ValidateBasic` function as well as on the custom `AnteHandler` defined by Ethermint. ## Next {hide} diff --git a/docs/basics/hard_spoon.md b/docs/basics/hard_spoon.md new file mode 100644 index 000000000..376eb1fde --- /dev/null +++ b/docs/basics/hard_spoon.md @@ -0,0 +1,17 @@ + + +# Hard Spoon + +Learn about Ethermint's Hard Spoon functionality. {synopsis} + +## Hard Spoon on Ethermint + +A [hard spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) is the migration of the snapshot of a target blockchain's state into a new chain. The state of a network can be imported (or "scooped") onto a separate chain to accurately replicate the account balances and other information from the state. + +Ethermint's Hard Spoon tool (currently under specification) is targeted to import the state of the Ethereum mainnet to replicate the state for the given accounts. This will allow anyone on the Ethereum network to import their contracts into Ethermint. + +## Next {hide} + +Learn about the [encoding](./../core/encoding.md) formats used on Ethermint {hide} diff --git a/docs/basics/json_rpc.md b/docs/basics/json_rpc.md index 58a5db0aa..99cfeb2fe 100644 --- a/docs/basics/json_rpc.md +++ b/docs/basics/json_rpc.md @@ -895,4 +895,4 @@ curl -X POST --data '{"jsonrpc":"2.0","method":"personal_ecRecover","params":["0 ## Next {hide} -Learn about the [encoding](./../core/encoding.md) formats used on Ethermint {hide} +Learn about the Ethermint [Hard Spoon](./hard_spoon.md) functionality {hide} diff --git a/docs/basics/transactions.md b/docs/basics/transactions.md index cdb425fa6..7af04cfc6 100644 --- a/docs/basics/transactions.md +++ b/docs/basics/transactions.md @@ -20,9 +20,9 @@ responsible for performing preliminary message execution business logic such as signature verification, etc. This is particular to Cosmos SDK routed transactions. Ethereum routed transactions will bypass this as the EVM handles the same business logic. -Ethereum routed transactions coming from a web3 source are expected to be RLP encoded, however all +Ethereum routed transactions coming from a Web3 source are expected to be [RLP](./../core/encoding.md#rlp) encoded, however all internal interaction between Ethermint and Tendermint will utilize one of the supported encoding -formats: Protobuf, Amino or Hybrid of the previous two. +formats: [Protobuf](./../core/encoding.md#protocol-buffers) and [Amino](./../core/encoding.md#amino). ## Transaction formats diff --git a/docs/core/README.md b/docs/core/README.md index 746c8fefa..48a74488c 100644 --- a/docs/core/README.md +++ b/docs/core/README.md @@ -9,5 +9,6 @@ parent: This repository contains reference documentation on the core concepts of Ethermint. 1. [Encoding](./encoding.md) +2. [Pending State](./pending_state.md) After reading the core concepts, head on to the [guides](../guides/README.md) to learn how to use Ethereum tooling with Ethermint. diff --git a/docs/core/encoding.md b/docs/core/encoding.md index 23c97091a..1c2c03f5b 100644 --- a/docs/core/encoding.md +++ b/docs/core/encoding.md @@ -4,70 +4,39 @@ order: 1 # Encoding -The `codec` is used everywhere in the Cosmos SDK to encode and decode structs and interfaces. The specific codec used in the Cosmos SDK is called `go-amino`. {synopsis} +Learn about the encoding formats used on Ethermint. {synopsis} ## Pre-requisite Readings - [Cosmos SDK Encoding](https://docs.cosmos.network/master/core/encoding.html) {prereq} +- [Ethereum RLP](https://eth.wiki/en/fundamentals/rlp) {prereq} ## Encoding Formats -The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) -and [Protocol Buffers](https://developers.google.com/protocol-buffers), where Amino -is an object encoding specification. It is a subset of Proto3 with an extension for -interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) -for more information on Proto3, which Amino is largely compatible with (but not with Proto2). +### Protocol Buffers -Due to Amino having significant performance drawbacks, being reflection-based, and -not having any meaningful cross-language/client support, Protocol Buffers, specifically -[gogoprotobuf](https://github.com/gogo/protobuf/), is being used in place of Amino. -Note, this process of using Protocol Buffers over Amino is still an ongoing process. - -Binary wire encoding of types in the Cosmos SDK can be broken down into two main -categories, client encoding and store encoding. Client encoding mainly revolves -around transaction processing and signing, whereas store encoding revolves around -types used in state-machine transitions and what is ultimately stored in the Merkle -tree. - -For store encoding, protobuf definitions can exist for any type and will typically -have an Amino-based "intermediary" type. Specifically, the protobuf-based type -definition is used for serialization and persistence, whereas the Amino-based type -is used for business logic in the state-machine where they may converted back-n-forth. -Note, the Amino-based types may slowly be phased-out in the future so developers -should take note to use the protobuf message definitions where possible. - -In the `codec` package, there exists two core interfaces, `Marshaler` and `ProtoMarshaler`, -where the former encapsulates the current Amino interface except it operates on -types implementing the latter instead of generic `interface{}` types. - -In addition, there exists three implementations of `Marshaler`. The first being -`AminoCodec`, where both binary and JSON serialization is handled via Amino. The -second being `ProtoCodec`, where both binary and JSON serialization is handled -via Protobuf. Finally, `HybridCodec`, a codec that utilizes Protobuf for binary -serialization and Amino for JSON serialization. The `HybridCodec` is typically -the codec that used in majority in situations as it's easier to use for client -and state serialization. - -This means that modules may use Amino or Protobuf encoding but the types must -implement `ProtoMarshaler`. If modules wish to avoid implementing this interface -for their types, they may use an Amino codec directly. +The Cosmos [Stargate](https://stargate.cosmos.network/) release introduces +[protobuf](https://developers.google.com/protocol-buffers) as the main encoding format for both +client and state serialization. All the EVM module structs that are used for state and clients +(transaction messages, genesis, query services, etc) will be implemented as protocol buffer messages. ### Amino -Every module uses an Amino codec to serialize types and interfaces. This codec typically -has types and interfaces registered in that module's domain only (e.g. messages), -but there are exceptions like `x/gov`. Each module exposes a `RegisterCodec` function -that allows a user to provide a codec and have all the types registered. An application -will call this method for each necessary module. - -### Protobuf +The Cosmos SDK also supports the legacy Amino encoding format for backwards compatibility with +previous versions, specially for client encoding. Ethermint will not support Amino in the EVM module +once the migration to SDK `v0.40` is finalized. - +### RLP -## RLP +Recursive Length Prefix ([RLP](https://eth.wiki/en/fundamentals/rlp)), is an encoding/decoding algorithm that serializes a message and +allows for quick reconstruction of encoded data. Ethermint uses RLP to encode/decode Ethereum +messages for JSON-RPC handling to conform messages to the proper Ethereum format. This allows +messages to be encoded and decoded in the exact format as Ethereum's. - +Each message type defined on the EVM module define the `EncodeRLP` and `DecodeRLP` methods which +implement the `rlp.Encoder` and `rlp.Decoder` interfaces respectively. The RLP encode method is used +to sign bytes and transactions in `RLPSignBytes` and `Sign`. ## Next {hide} -Learn how to deploy a Solidity smart contract on Ethermint using [Truffle](./../guides/truffle.md) {hide} +Learn how [pending state](./pending_state.md) is handled on Ethermint. {hide} diff --git a/docs/core/pending_state.md b/docs/core/pending_state.md new file mode 100644 index 000000000..bc8d2ca9c --- /dev/null +++ b/docs/core/pending_state.md @@ -0,0 +1,49 @@ + + +# Pending State + +Learn how Ethermint handles pending state queries. {synopsis} + +## Pre-requisite Readings + +- [Tendermint Mempool](https://docs.tendermint.com/master/tendermint-core/mempool.htm) {prereq} + +## Ethermint vs Ethereum + +In Ethereum, pending blocks are generated as they are queued for production by miners. These pending +blocks include pending transactions that are picked out by miners, based on the highest reward paid +in gas. This mechanism exists as block finality is not possible on the Ethereum network. Blocks are +committed with probabilistic finality, which means that transactions and blocks become less likely +to become reverted as more time (and blocks) passes. + +Ethermint is designed quite differently on this front as there is no concept of a "pending state". +Ethermint uses [Tendermint Core](https://docs.tendermint.com/) BFT consensus which provides instant +finality for transaction. For this reason, Etheremint does not require a pending state mechanism, as +all (if not most) of the transactions will be committed to the next block (avg. block time on Cosmos chains is ~8s). However, this causes a +few hiccups in terms of the Ethereum Web3-compatible queries that can be made to pending state. + +Another significant difference with Ethereum, is that blocks are produced by validators or block producers, who include transactions from their local mempool into blocks in a +first-in-first-out (FIFO) fashion. Transactions on Ethermint cannot be ordered or cherry picked out from the Tendermint node [mempool](https://docs.tendermint.com/master/tendermint-core/mempool.html#transaction-ordering). + +## Pending State Queries + +Ethermint will make queries which will account for any unconfirmed transactions present in a node's +transaction mempool. A pending state query made will be subjective and the query will be made on the +target node's mempool. Thus, the pending state will not be the same for the same query to two +different nodes. + +### RPC Calls on Pending Transactions + +- [`eth_getBalance`](./../basics/json_rpc.md#eth_getbalance) +- [`eth_getTransactionCount`](./../basics/json_rpc.md#eth-gettransactioncount) +- [`eth_getBlockTransactionCountByNumber`](./../basics/json_rpc.md#eth-getblocktransactioncountbynumber) +- [`eth_getBlockByNumber`](./../basics/json_rpc.md#eth-getblockbynumber) +- [`eth_getTransactionByHash`](./../basics/json_rpc.md#eth-gettransactionbyhash) +- [`eth_getTransactionByBlockNumberAndIndex`](./../basics/json_rpc.html#eth-gettransactionbyblockhashandindex) +- [`eth_sendTransaction`](./../basics/json_rpc.md#eth-sendtransaction) + +## Next {hide} + +Learn how to deploy a Solidity smart contract on Ethermint using [Truffle](./../guides/truffle.md) {hide} diff --git a/docs/intro/README.md b/docs/intro/README.md index 67e989089..a9457cc98 100644 --- a/docs/intro/README.md +++ b/docs/intro/README.md @@ -10,6 +10,7 @@ This folder contains introduction material for Ethermint. 1. [Overview](./overview.md) 1. [Architecture](./architecture.md) +1. [Use Cases](./use_cases.md) 1. [Resources](./resources.md) After reading the introduction material, head over to the [basics](../basics/README.md) to learn more. diff --git a/docs/intro/overview.md b/docs/intro/overview.md index 85c8015dd..8209decd0 100644 --- a/docs/intro/overview.md +++ b/docs/intro/overview.md @@ -4,15 +4,20 @@ order: 1 # High-level Overview +Learn about Ethermint and its primary features. {synopsis} + ## What is Ethermint Ethermint is a scalable, high-throughput Proof-of-Stake blockchain that is fully compatible and -interoperable with Ethereum. It's built using the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/) which runs on top of [Tendermint Core](https://github.com/tendermint/tendermint) consensus engine. +interoperable with Ethereum. It's built using the [Cosmos +SDK](https://github.com/cosmos/cosmos-sdk/) which runs on top of [Tendermint +Core](https://github.com/tendermint/tendermint) consensus engine. -Ethermint allows for running vanilla Ethereum as a [Cosmos](https://cosmos.network/) application-specific blockchain. This allows developers -to have all the desired features of Ethereum, while at the same time, benefit -from Tendermint’s PoS implementation. Also, because it is built on top of the -Cosmos SDK, it will be able to exchange value with the rest of the Cosmos Ecosystem through the Inter Blockchain Communication Protocol (IBC). +Ethermint allows for running vanilla Ethereum as a [Cosmos](https://cosmos.network/) +application-specific blockchain. This allows developers to have all the desired features of +Ethereum, while at the same time, benefit from Tendermint’s PoS implementation. Also, because it is +built on top of the Cosmos SDK, it will be able to exchange value with the rest of the Cosmos +Ecosystem through the Inter Blockchain Communication Protocol (IBC). ### Features @@ -20,20 +25,21 @@ Here’s a glance at some of the key features of Ethermint: * Web3 compatibility * High throughput via [Tendermint Core](https://github.com/tendermint/tendermint) -* Horizontal scalability via [IBC](https://github.com/cosmos/ics) +* Horizontal scalability via [IBC](https://cosmos.network/ibc) * Fast transaction finality -* [Hard Spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) +* [Hard Spoon](./../basics/hard_spoon.md) -Ethermint enables these key features through: +Ethermint enables these key features by: -* Implementing Tendermint Core's ABCI application interface to manage the blockchain -* Leveraging [modules](https://github.com/cosmos/cosmos-sdk/tree/master/x/) and other mechanisms implemented by the Cosmos SDK +* Implementing Tendermint Core's Application Blockchain Interface ([ABCI](https://docs.tendermint.com/master/spec/abci/)) to manage the blockchain +* Leveraging [modules](https://docs.cosmos.network/master/building-modules/intro.html) and other mechanisms implemented by the [Cosmos SDK](https://docs.cosmos.network/). * Utilizing [`geth`](https://github.com/ethereum/go-ethereum) as a library to avoid code reuse and improve maintainability. -* Exposing a fully compatible Web3 RPC layer for interacting with existing Ethereum clients and tooling (Metamask, Remix, Truffle, etc). +* Exposing a fully compatible Web3 [JSON-RPC](./../basic/json_rpc.md) layer for interacting with existing Ethereum clients and tooling ([Metamask](./../guides/metamask.md), [Remix](./../guides/remix.md), [Truffle](./../guides/truffle.md), etc). The sum of these features allows developers to leverage existing Ethereum ecosystem tooling and -software to seamlessly deploy smart contracts which interact with the rest of the Cosmos ecosystem! +software to seamlessly deploy smart contracts which interact with the rest of the Cosmos +[ecosystem](https://cosmos.network/ecosystem)! ## Next {hide} -Learn about Ethermint's [architecture](./architectures.md) {hide} +Learn about Ethermint's [architecture](./architecture.md) {hide} diff --git a/docs/intro/resources.md b/docs/intro/resources.md index 721f73faa..ed7c5a937 100644 --- a/docs/intro/resources.md +++ b/docs/intro/resources.md @@ -1,5 +1,5 @@ # Resources @@ -27,10 +27,12 @@ Learn about Ethermint with the list of official resources. {synopsis} Note: most of these articles are outdated as they refer to the previous Ethermint projects (linked below). They are listed here for reference only. ::: +- [Ethermint Supports Web3 Personal API - Colin Schwarz](https://blog.cosmos.network/ethermint-supports-web3-personal-api-556adf75c24e) +- [The Road to Ethermint - Colin Schwarz](https://blog.cosmos.network/the-road-to-ethermint-836c0745f535) - [A Beginners Guide to Ethermint - ICF](https://blog.cosmos.network/a-beginners-guide-to-ethermint-38ee15f8a6f4) - [Using Ethermint with Truffle - Billy Rennekamp](https://blog.cosmos.network/using-ethermint-with-truffle-984e6721e30d) - [Light Clients on Ethermint - ICF](https://blog.cosmos.network/light-clients-on-ethermint-9ae1f3c6c4f5) -- [Cosmos Fee Token – Introducing the Photon - ICF](https://blog.cosmos.network/cosmos-fee-token-introducing-the-photon-8a62b2f51aa) +- [Cosmos Fee Token – Introducing the Photon - ICF](https://blog.cosmos.network/cosmos-fee-token-introducing-the-photon-8a62b2f51aa) - [Introducing the Hard Spoon - Chjango Unchained](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) - [Ethermint and NFTs at EthDenver - Tendermint](https://blog.cosmos.network/ethermint-nfts-at-ethdenver-bf32766835b6) diff --git a/docs/intro/use_cases.md b/docs/intro/use_cases.md new file mode 100644 index 000000000..48a60dab7 --- /dev/null +++ b/docs/intro/use_cases.md @@ -0,0 +1,50 @@ + + +# Use Cases + +Check out the 2 use cases for the Ethermint project. {synopsis} + +## Ethermint chain + +The Ethermint blockchain provides Ethereum developers to deploy their smart contracts to the +Ethermint EVM and get the benefits of a fast-finality Proof-of-Stake (PoS) chain. Developers will +also benefit from highly-reliable clients from testnets can be used to test and deploy their +contracts. + +Ethermint will also offer built-in interoperability functionalities with other Cosmos and BFT chains by using [IBC](https://cosmos.network/ibc). Developers can also benefit from using a bridge network (like +[Chainbridge](https://github.com/ChainSafe/ChainBridge), or a [Peg +Zone](https://github.com/cosmos/peggy)) to enable interoperability between mainnet Ethereum and Ethermint. + +## EVM module dependency + +The EVM module ([x/evm](https://github.com/cosmos/ethermint/tree/development/x/evm)) packaged inside +Ethermint can be used separately as its own standalone module. This can be added as a dependency to +any Cosmos chain, which will allow for smart contract support. + +Importing EVM module can also enable use cases such as Proof-of-Authority +([PoA](https://en.wikipedia.org/wiki/Proof_of_authority)) chains for enterprise and consortium +projects. Every chain on Cosmos is an [application-specific +blockchain](https://docs.cosmos.network/master/intro/why-app-specific.html) that is customized for +the business logic defined by a single application. Thus, by using a predefined validator set and +the EVM module as a dependency, enables projects with fast finality, interoperability as well as +Proof-of-Stake (PoS) consensus. + +## Trade offs + +Either option above will allow for fast finality, using a PoS consensus engine. Using the EVM module +as a dependency will require the importing of the EVM and the maintaining of the chain (including +validator sets, code upgrades/conformance, community engagement, incentives, etc), thus it incurs on a +higher operation cost. The benefit of importing the EVM module to your chains is that it allows for +granular control over the network and chain specific configurations/features that may not be +available in the Ethermint chain such as developing a module or importing a third-party one. + +Using Ethermint chain will allow for the direct deployment of smart contracts to the Ethermint +network. Utilizing the Ethermint client will defer the chain maintenance to the Ethermint network +and allow for the participation in a more mature blockchain. The Ethermint client will also offer +(in the near future) IBC compatibility which allows for interoperability between different network. + +## Next {hide} + +Read the available Ethermint [resources](./resources.md) {hide} From 26b3af7a263daeab672d1e1c75855906b84780e4 Mon Sep 17 00:00:00 2001 From: Daniel Choi Date: Mon, 16 Nov 2020 01:13:51 -0800 Subject: [PATCH 3/6] fix erroring of docker-build (#598) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0e0e570c7..c33bed084 100644 --- a/Makefile +++ b/Makefile @@ -154,9 +154,9 @@ clean: docker-build: docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest - docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} + # docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} # update old container - docker rm ethermint + docker rm ethermint || true # create a new container from the latest image docker create --name ethermint -t -i cosmos/ethermint:latest ethermint # move the binaries to the ./build directory From 73e5eb5e98c6248e56a104ccbf7af21c842ec492 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Mon, 16 Nov 2020 14:09:40 +0100 Subject: [PATCH 4/6] ante: fix fee check (#597) * ante: fix fee check * changelog * comment --- CHANGELOG.md | 1 + app/ante/eth.go | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcd70e29c..2373a015a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ corresponding Ethereum API namespace: ### Bug Fixes +* (ante) [\#597](https://github.com/cosmos/ethermint/pull/597) Fix incorrect fee check on `AnteHandler`. * (evm) [\#583](https://github.com/cosmos/ethermint/pull/583) Fixes incorrect resetting of tx count and block bloom during `BeginBlock`, as well as gas consumption. * (crypto) [\#577](https://github.com/cosmos/ethermint/pull/577) Fix `BIP44HDPath` that did not prepend `m/` to the path. This now uses the `DefaultBaseDerivationPath` variable from go-ethereum to ensure addresses are consistent. diff --git a/app/ante/eth.go b/app/ante/eth.go index bad8243fa..2075235db 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -100,20 +100,21 @@ func (emfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula evmDenom := emfd.evmKeeper.GetParams(ctx).EvmDenom - // fee = GP * GL + // fee = gas price * gas limit fee := sdk.NewInt64DecCoin(evmDenom, msgEthTx.Fee().Int64()) minGasPrices := ctx.MinGasPrices() + minFees := minGasPrices.AmountOf(evmDenom).MulInt64(int64(msgEthTx.Data.GasLimit)) - // check that fee provided is greater than the minimum - // NOTE: we only check if aphotons are present in min gas prices. It is up to the + // check that fee provided is greater than the minimum defined by the validator node + // NOTE: we only check if the evm denom tokens are present in min gas prices. It is up to the // sender if they want to send additional fees in other denominations. var hasEnoughFees bool - if fee.Amount.GTE(minGasPrices.AmountOf(evmDenom)) { + if fee.Amount.GTE(minFees) { hasEnoughFees = true } - // reject transaction if minimum gas price is positive and the transaction does not + // reject transaction if minimum gas price is not zero and the transaction does not // meet the minimum fee if !ctx.MinGasPrices().IsZero() && !hasEnoughFees { return ctx, sdkerrors.Wrap( From 2796f55b023e4fa71e96a695d324dd8409d7ced3 Mon Sep 17 00:00:00 2001 From: noot <36753753+noot@users.noreply.github.com> Date: Mon, 16 Nov 2020 11:11:15 -0500 Subject: [PATCH 5/6] deps: update go-ethereum to v1.9.24 (#594) * update to go-ethereum v1.9.24 * go mod tidy * add YoloV2 to chain config * add accessList and implement csdb accessList funcs * cleanup * access list tests * changelog * add stateDB test * test Copy Co-authored-by: Federico Kunze --- CHANGELOG.md | 4 + go.mod | 2 +- go.sum | 12 +- x/evm/keeper/statedb_test.go | 2 +- x/evm/types/access_list.go | 134 +++++++++++++++++ x/evm/types/access_list_test.go | 242 +++++++++++++++++++++++++++++++ x/evm/types/chain_config.go | 10 +- x/evm/types/chain_config_test.go | 10 +- x/evm/types/journal.go | 33 +++++ x/evm/types/journal_test.go | 6 + x/evm/types/state_transition.go | 2 +- x/evm/types/statedb.go | 39 +++++ x/evm/types/statedb_test.go | 22 ++- 13 files changed, 501 insertions(+), 17 deletions(-) create mode 100644 x/evm/types/access_list.go create mode 100644 x/evm/types/access_list_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 2373a015a..555f61503 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,10 @@ corresponding Ethereum API namespace: * (evm) [\#588](https://github.com/cosmos/ethermint/pull/588) The EVM transaction CLI has been removed in favor of the JSON-RPC. +### Improvements + +* (deps) [\#594](https://github.com/cosmos/ethermint/pull/594) Bump go-ethereum version to [v1.9.24](https://github.com/ethereum/go-ethereum/releases/tag/v1.9.24) + ### Bug Fixes * (ante) [\#597](https://github.com/cosmos/ethermint/pull/597) Fix incorrect fee check on `AnteHandler`. diff --git a/go.mod b/go.mod index 00e3baf8e..e10abd2cd 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/cespare/cp v1.1.1 // indirect github.com/cosmos/cosmos-sdk v0.39.1 github.com/deckarep/golang-set v1.7.1 // indirect - github.com/ethereum/go-ethereum v1.9.21 + github.com/ethereum/go-ethereum v1.9.24 github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index e51704441..dccf44184 100644 --- a/go.sum +++ b/go.sum @@ -164,6 +164,7 @@ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbT github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -178,8 +179,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.9.5/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= -github.com/ethereum/go-ethereum v1.9.21 h1:8qRlhzrItnmUGdVlBzZLI2Tb46S0RdSNjFwICo781ws= -github.com/ethereum/go-ethereum v1.9.21/go.mod h1:RXAVzbGrSGmDkDnHymruTAIEjUR3E4TX0EOpaj702sI= +github.com/ethereum/go-ethereum v1.9.24 h1:6AK+ORt3EMDO+FTjzXy/AQwHMbu52J2nYHIjyQX9azQ= +github.com/ethereum/go-ethereum v1.9.24/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -264,6 +265,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -703,6 +706,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -716,10 +720,12 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -834,7 +840,9 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs= golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go index 1096a063e..ed2fbf5f0 100644 --- a/x/evm/keeper/statedb_test.go +++ b/x/evm/keeper/statedb_test.go @@ -62,7 +62,7 @@ func (suite *KeeperTestSuite) TestBloomFilter() { } else { // get logs bloom from the log bloomInt := ethtypes.LogsBloom(logs) - bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) + bloomFilter := ethtypes.BytesToBloom(bloomInt) suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) } diff --git a/x/evm/types/access_list.go b/x/evm/types/access_list.go new file mode 100644 index 000000000..8d7cf2835 --- /dev/null +++ b/x/evm/types/access_list.go @@ -0,0 +1,134 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/common" +) + +// accessList is copied from go-ethereum +// https://github.com/ethereum/go-ethereum/blob/cf856ea1ad96ac39ea477087822479b63417036a/core/state/access_list.go#L23 +type accessList struct { + addresses map[common.Address]int + slots []map[common.Hash]struct{} +} + +// ContainsAddress returns true if the address is in the access list. +func (al *accessList) ContainsAddress(address common.Address) bool { + _, ok := al.addresses[address] + return ok +} + +// Contains checks if a slot within an account is present in the access list, returning +// separate flags for the presence of the account and the slot respectively. +func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { + idx, ok := al.addresses[address] + if !ok { + // no such address (and hence zero slots) + return false, false + } + if idx == -1 { + // address yes, but no slots + return true, false + } + + if idx >= len(al.slots) { + // return in case of out-of-range + return true, false + } + + _, slotPresent = al.slots[idx][slot] + return true, slotPresent +} + +// newAccessList creates a new accessList. +func newAccessList() *accessList { + return &accessList{ + addresses: make(map[common.Address]int), + } +} + +// Copy creates an independent copy of an accessList. +func (al *accessList) Copy() *accessList { + cp := newAccessList() + for k, v := range al.addresses { + cp.addresses[k] = v + } + cp.slots = make([]map[common.Hash]struct{}, len(al.slots)) + for i, slotMap := range al.slots { + newSlotmap := make(map[common.Hash]struct{}, len(slotMap)) + for k := range slotMap { + newSlotmap[k] = struct{}{} + } + cp.slots[i] = newSlotmap + } + return cp +} + +// AddAddress adds an address to the access list, and returns 'true' if the operation +// caused a change (addr was not previously in the list). +func (al *accessList) AddAddress(address common.Address) bool { + if _, present := al.addresses[address]; present { + return false + } + al.addresses[address] = -1 + return true +} + +// AddSlot adds the specified (addr, slot) combo to the access list. +// Return values are: +// - address added +// - slot added +// For any 'true' value returned, a corresponding journal entry must be made. +func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { + idx, addrPresent := al.addresses[address] + if !addrPresent || idx == -1 { + // Address not present, or addr present but no slots there + al.addresses[address] = len(al.slots) + slotmap := map[common.Hash]struct{}{slot: {}} + al.slots = append(al.slots, slotmap) + return !addrPresent, true + } + + if idx >= len(al.slots) { + // return in case of out-of-range + return false, false + } + + // There is already an (address,slot) mapping + slotmap := al.slots[idx] + if _, ok := slotmap[slot]; !ok { + slotmap[slot] = struct{}{} + // Journal add slot change + return false, true + } + // No changes required + return false, false +} + +// DeleteSlot removes an (address, slot)-tuple from the access list. +// This operation needs to be performed in the same order as the addition happened. +// This method is meant to be used by the journal, which maintains ordering of +// operations. +func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { + idx, addrOk := al.addresses[address] + // There are two ways this can fail + if !addrOk { + panic("reverting slot change, address not present in list") + } + slotmap := al.slots[idx] + delete(slotmap, slot) + // If that was the last (first) slot, remove it + // Since additions and rollbacks are always performed in order, + // we can delete the item without worrying about screwing up later indices + if len(slotmap) == 0 { + al.slots = al.slots[:idx] + al.addresses[address] = -1 + } +} + +// DeleteAddress removes an address from the access list. This operation +// needs to be performed in the same order as the addition happened. +// This method is meant to be used by the journal, which maintains ordering of +// operations. +func (al *accessList) DeleteAddress(address common.Address) { + delete(al.addresses, address) +} diff --git a/x/evm/types/access_list_test.go b/x/evm/types/access_list_test.go new file mode 100644 index 000000000..700c94fc3 --- /dev/null +++ b/x/evm/types/access_list_test.go @@ -0,0 +1,242 @@ +package types + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + ethcmn "github.com/ethereum/go-ethereum/common" + + "github.com/cosmos/ethermint/crypto/ethsecp256k1" +) + +type AccessListTestSuite struct { + suite.Suite + + address ethcmn.Address + accessList *accessList +} + +func (suite *AccessListTestSuite) SetupTest() { + privkey, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + + suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) + suite.accessList = newAccessList() + suite.accessList.addresses[suite.address] = 1 +} + +func TestAccessListTestSuite(t *testing.T) { + suite.Run(t, new(AccessListTestSuite)) +} + +func (suite *AccessListTestSuite) TestContainsAddress() { + found := suite.accessList.ContainsAddress(suite.address) + suite.Require().True(found) +} + +func (suite *AccessListTestSuite) TestContains() { + testCases := []struct { + name string + malleate func() + expAddrPresent bool + expSlotPresent bool + }{ + {"out of range", func() {}, true, false}, + { + "address, no slots", + func() { + suite.accessList.addresses[suite.address] = -1 + }, true, false, + }, + { + "no address, no slots", + func() { + delete(suite.accessList.addresses, suite.address) + }, false, false, + }, + { + "address, slot not present", + func() { + suite.accessList.addresses[suite.address] = 0 + suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1) + }, true, false, + }, + { + "address, slots", + func() { + suite.accessList.addresses[suite.address] = 0 + suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1) + suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{}) + suite.accessList.slots[0][ethcmn.Hash{}] = struct{}{} + }, true, true, + }, + } + + for _, tc := range testCases { + tc.malleate() + + addrPresent, slotPresent := suite.accessList.Contains(suite.address, ethcmn.Hash{}) + + suite.Require().Equal(tc.expAddrPresent, addrPresent, tc.name) + suite.Require().Equal(tc.expSlotPresent, slotPresent, tc.name) + } +} + +func (suite *AccessListTestSuite) TestCopy() { + expAccessList := newAccessList() + + testCases := []struct { + name string + malleate func() + }{ + {"empty", func() { + expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0) + }}, + { + "single address", func() { + expAccessList = newAccessList() + expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0) + expAccessList.addresses[suite.address] = -1 + }, + }, + { + "single address, single slot", + func() { + expAccessList = newAccessList() + expAccessList.addresses[suite.address] = 0 + expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 1) + expAccessList.slots[0] = make(map[ethcmn.Hash]struct{}) + expAccessList.slots[0][ethcmn.Hash{}] = struct{}{} + }, + }, + { + "multiple addresses, single slot each", + func() { + expAccessList = newAccessList() + expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10) + for i := 0; i < 10; i++ { + expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i + expAccessList.slots[i] = make(map[ethcmn.Hash]struct{}) + expAccessList.slots[i][ethcmn.BytesToHash([]byte(fmt.Sprintf("%d", i)))] = struct{}{} + } + }, + }, + { + "multiple addresses, multiple slots each", + func() { + expAccessList = newAccessList() + expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10) + for i := 0; i < 10; i++ { + expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i + expAccessList.slots[i] = make(map[ethcmn.Hash]struct{}) + for j := 0; j < 10; j++ { + expAccessList.slots[i][ethcmn.BytesToHash([]byte(fmt.Sprintf("%d-%d", i, j)))] = struct{}{} + } + } + }, + }, + } + + for _, tc := range testCases { + tc.malleate() + + accessList := expAccessList.Copy() + suite.Require().EqualValues(expAccessList, accessList, tc.name) + } +} + +func (suite *AccessListTestSuite) TestAddAddress() { + testCases := []struct { + name string + address ethcmn.Address + ok bool + }{ + {"already present", suite.address, false}, + {"ok", ethcmn.Address{}, true}, + } + + for _, tc := range testCases { + ok := suite.accessList.AddAddress(tc.address) + suite.Require().Equal(tc.ok, ok, tc.name) + } +} + +func (suite *AccessListTestSuite) TestAddSlot() { + testCases := []struct { + name string + malleate func() + expAddrChange bool + expSlotChange bool + }{ + {"out of range", func() {}, false, false}, + { + "address not present added, slot added", + func() { + delete(suite.accessList.addresses, suite.address) + }, true, true, + }, + { + "address present, slot not present added", + func() { + suite.accessList.addresses[suite.address] = 0 + suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1) + suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{}) + }, false, true, + }, + { + "address present, slot present", + func() { + suite.accessList.addresses[suite.address] = 0 + suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1) + suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{}) + suite.accessList.slots[0][ethcmn.Hash{}] = struct{}{} + }, false, false, + }, + } + + for _, tc := range testCases { + tc.malleate() + + addrChange, slotChange := suite.accessList.AddSlot(suite.address, ethcmn.Hash{}) + suite.Require().Equal(tc.expAddrChange, addrChange, tc.name) + suite.Require().Equal(tc.expSlotChange, slotChange, tc.name) + } +} + +func (suite *AccessListTestSuite) TestDeleteSlot() { + testCases := []struct { + name string + malleate func() + expPanic bool + }{ + {"panics, out of range", func() {}, true}, + {"panics, address not found", func() { + delete(suite.accessList.addresses, suite.address) + }, true}, + { + "single slot present", + func() { + suite.accessList.addresses[suite.address] = 0 + suite.accessList.slots = make([]map[ethcmn.Hash]struct{}, 1) + suite.accessList.slots[0] = make(map[ethcmn.Hash]struct{}) + suite.accessList.slots[0][ethcmn.Hash{}] = struct{}{} + }, false, + }, + } + + for _, tc := range testCases { + tc.malleate() + + if tc.expPanic { + suite.Require().Panics(func() { + suite.accessList.DeleteSlot(suite.address, ethcmn.Hash{}) + }, tc.name) + } else { + suite.Require().NotPanics(func() { + suite.accessList.DeleteSlot(suite.address, ethcmn.Hash{}) + }, tc.name) + } + } +} diff --git a/x/evm/types/chain_config.go b/x/evm/types/chain_config.go index a923bc97a..e0f7e92b9 100644 --- a/x/evm/types/chain_config.go +++ b/x/evm/types/chain_config.go @@ -40,7 +40,7 @@ type ChainConfig struct { IstanbulBlock sdk.Int `json:"istanbul_block" yaml:"istanbul_block"` // Istanbul switch block (< 0 no fork, 0 = already on istanbul) MuirGlacierBlock sdk.Int `json:"muir_glacier_block" yaml:"muir_glacier_block"` // Eip-2384 (bomb delay) switch block (< 0 no fork, 0 = already activated) - YoloV1Block sdk.Int `json:"yoloV1_block" yaml:"yoloV1_block"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) + YoloV2Block sdk.Int `json:"yoloV2_block" yaml:"yoloV2_block"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) EWASMBlock sdk.Int `json:"ewasm_block" yaml:"ewasm_block"` // EWASM switch block (< 0 no fork, 0 = already activated) } @@ -61,7 +61,7 @@ func (cc ChainConfig) EthereumConfig(chainID *big.Int) *params.ChainConfig { PetersburgBlock: getBlockValue(cc.PetersburgBlock), IstanbulBlock: getBlockValue(cc.IstanbulBlock), MuirGlacierBlock: getBlockValue(cc.MuirGlacierBlock), - YoloV1Block: getBlockValue(cc.YoloV1Block), + YoloV2Block: getBlockValue(cc.YoloV2Block), EWASMBlock: getBlockValue(cc.EWASMBlock), } } @@ -87,7 +87,7 @@ func DefaultChainConfig() ChainConfig { PetersburgBlock: sdk.ZeroInt(), IstanbulBlock: sdk.NewInt(-1), MuirGlacierBlock: sdk.NewInt(-1), - YoloV1Block: sdk.NewInt(-1), + YoloV2Block: sdk.NewInt(-1), EWASMBlock: sdk.NewInt(-1), } } @@ -136,8 +136,8 @@ func (cc ChainConfig) Validate() error { if err := validateBlock(cc.MuirGlacierBlock); err != nil { return sdkerrors.Wrap(err, "muirGlacierBlock") } - if err := validateBlock(cc.YoloV1Block); err != nil { - return sdkerrors.Wrap(err, "yoloV1Block") + if err := validateBlock(cc.YoloV2Block); err != nil { + return sdkerrors.Wrap(err, "yoloV2Block") } if err := validateBlock(cc.EWASMBlock); err != nil { return sdkerrors.Wrap(err, "eWASMBlock") diff --git a/x/evm/types/chain_config_test.go b/x/evm/types/chain_config_test.go index 00b6d9489..1fdbcf875 100644 --- a/x/evm/types/chain_config_test.go +++ b/x/evm/types/chain_config_test.go @@ -33,7 +33,7 @@ func TestChainConfigValidate(t *testing.T) { PetersburgBlock: sdk.OneInt(), IstanbulBlock: sdk.OneInt(), MuirGlacierBlock: sdk.OneInt(), - YoloV1Block: sdk.OneInt(), + YoloV2Block: sdk.OneInt(), EWASMBlock: sdk.OneInt(), }, false, @@ -176,7 +176,7 @@ func TestChainConfigValidate(t *testing.T) { true, }, { - "invalid YoloV1Block", + "invalid YoloV2Block", ChainConfig{ HomesteadBlock: sdk.OneInt(), DAOForkBlock: sdk.OneInt(), @@ -189,7 +189,7 @@ func TestChainConfigValidate(t *testing.T) { PetersburgBlock: sdk.OneInt(), IstanbulBlock: sdk.OneInt(), MuirGlacierBlock: sdk.OneInt(), - YoloV1Block: sdk.Int{}, + YoloV2Block: sdk.Int{}, }, true, }, @@ -207,7 +207,7 @@ func TestChainConfigValidate(t *testing.T) { PetersburgBlock: sdk.OneInt(), IstanbulBlock: sdk.OneInt(), MuirGlacierBlock: sdk.OneInt(), - YoloV1Block: sdk.OneInt(), + YoloV2Block: sdk.OneInt(), EWASMBlock: sdk.Int{}, }, true, @@ -238,7 +238,7 @@ constantinople_block: "0" petersburg_block: "0" istanbul_block: "-1" muir_glacier_block: "-1" -yoloV1_block: "-1" +yoloV2_block: "-1" ewasm_block: "-1" ` require.Equal(t, configStr, DefaultChainConfig().String()) diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go index b2190759d..9a4a8d60a 100644 --- a/x/evm/types/journal.go +++ b/x/evm/types/journal.go @@ -186,10 +186,18 @@ type ( // prev bool // prevDirty bool } + accessListAddAccountChange struct { + address *ethcmn.Address + } + accessListAddSlotChange struct { + address *ethcmn.Address + slot *ethcmn.Hash + } ) func (ch createObjectChange) revert(s *CommitStateDB) { delete(s.stateObjectsDirty, *ch.account) + idx, exists := s.addressToObjectIndex[*ch.account] if !exists { // perform no-op @@ -342,3 +350,28 @@ func (ch addPreimageChange) revert(s *CommitStateDB) { func (ch addPreimageChange) dirtied() *ethcmn.Address { return nil } + +func (ch accessListAddAccountChange) revert(s *CommitStateDB) { + /* + One important invariant here, is that whenever a (addr, slot) is added, if the + addr is not already present, the add causes two journal entries: + - one for the address, + - one for the (address,slot) + Therefore, when unrolling the change, we can always blindly delete the + (addr) at this point, since no storage adds can remain when come upon + a single (addr) change. + */ + s.accessList.DeleteAddress(*ch.address) +} + +func (ch accessListAddAccountChange) dirtied() *ethcmn.Address { + return nil +} + +func (ch accessListAddSlotChange) revert(s *CommitStateDB) { + s.accessList.DeleteSlot(*ch.address, *ch.slot) +} + +func (ch accessListAddSlotChange) dirtied() *ethcmn.Address { + return nil +} diff --git a/x/evm/types/journal_test.go b/x/evm/types/journal_test.go index 11c4f89af..a07f6a9bf 100644 --- a/x/evm/types/journal_test.go +++ b/x/evm/types/journal_test.go @@ -220,6 +220,12 @@ func (suite *JournalTestSuite) TestJournal_append_revert() { txhash: ethcmn.BytesToHash([]byte("txhash")), }, }, + { + "accessListAddAccountChange", + accessListAddAccountChange{ + address: &suite.address, + }, + }, } var dirtyCount int for i, tc := range testCases { diff --git a/x/evm/types/state_transition.go b/x/evm/types/state_transition.go index 980bf53f4..9c203717c 100644 --- a/x/evm/types/state_transition.go +++ b/x/evm/types/state_transition.go @@ -159,7 +159,7 @@ func (st StateTransition) TransitionDb(ctx sdk.Context, config ChainConfig) (*Ex return nil, err } - bloomInt = ethtypes.LogsBloom(logs) + bloomInt = big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)) bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes()) } diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index 94fa5b8f5..688bb7c5a 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -77,6 +77,9 @@ type CommitStateDB struct { validRevisions []revision nextRevisionID int + // Per-transaction access list + accessList *accessList + // mutex for state deep copying lock sync.Mutex } @@ -100,6 +103,7 @@ func NewCommitStateDB( preimages: []preimageEntry{}, hashToPreimageIndex: make(map[ethcmn.Hash]int), journal: newJournal(), + accessList: newAccessList(), } } @@ -243,6 +247,41 @@ func (csdb *CommitStateDB) SubRefund(gas uint64) { csdb.refund -= gas } +// AddAddressToAccessList adds the given address to the access list +func (csdb *CommitStateDB) AddAddressToAccessList(addr ethcmn.Address) { + if csdb.accessList.AddAddress(addr) { + csdb.journal.append(accessListAddAccountChange{&addr}) + } +} + +// AddSlotToAccessList adds the given (address, slot)-tuple to the access list +func (csdb *CommitStateDB) AddSlotToAccessList(addr ethcmn.Address, slot ethcmn.Hash) { + addrMod, slotMod := csdb.accessList.AddSlot(addr, slot) + if addrMod { + // In practice, this should not happen, since there is no way to enter the + // scope of 'address' without having the 'address' become already added + // to the access list (via call-variant, create, etc). + // Better safe than sorry, though + csdb.journal.append(accessListAddAccountChange{&addr}) + } + if slotMod { + csdb.journal.append(accessListAddSlotChange{ + address: &addr, + slot: &slot, + }) + } +} + +// AddressInAccessList returns true if the given address is in the access list. +func (csdb *CommitStateDB) AddressInAccessList(addr ethcmn.Address) bool { + return csdb.accessList.ContainsAddress(addr) +} + +// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. +func (csdb *CommitStateDB) SlotInAccessList(addr ethcmn.Address, slot ethcmn.Hash) (bool, bool) { + return csdb.accessList.Contains(addr, slot) +} + // ---------------------------------------------------------------------------- // Getters // ---------------------------------------------------------------------------- diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go index f4418afe4..b0ccdeaac 100644 --- a/x/evm/types/statedb_test.go +++ b/x/evm/types/statedb_test.go @@ -113,8 +113,8 @@ func (suite *StateDBTestSuite) TestBloomFilter() { } } else { // get logs bloom from the log - bloomInt := ethtypes.LogsBloom(logs) - bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) + bloomBytes := ethtypes.LogsBloom(logs) + bloomFilter := ethtypes.BytesToBloom(bloomBytes) suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) } @@ -695,3 +695,21 @@ func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { storage = types.Storage{} } } + +func (suite *StateDBTestSuite) TestCommitStateDB_AccessList() { + addr := ethcmn.Address([20]byte{77}) + hash := ethcmn.Hash([32]byte{99}) + + suite.Require().False(suite.stateDB.AddressInAccessList(addr)) + + suite.stateDB.AddAddressToAccessList(addr) + suite.Require().True(suite.stateDB.AddressInAccessList(addr)) + addrIn, slotIn := suite.stateDB.SlotInAccessList(addr, hash) + suite.Require().True(addrIn) + suite.Require().False(slotIn) + + suite.stateDB.AddSlotToAccessList(addr, hash) + addrIn, slotIn = suite.stateDB.SlotInAccessList(addr, hash) + suite.Require().True(addrIn) + suite.Require().True(slotIn) +} From ab951e22d0454ba1a0fccc9362491843bf7d4e06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Nov 2020 11:23:11 +0100 Subject: [PATCH 6/6] build(deps): bump github.com/tendermint/tendermint from 0.33.7 to 0.33.9 (#602) Bumps [github.com/tendermint/tendermint](https://github.com/tendermint/tendermint) from 0.33.7 to 0.33.9. - [Release notes](https://github.com/tendermint/tendermint/releases) - [Changelog](https://github.com/tendermint/tendermint/blob/v0.33.9/CHANGELOG.md) - [Commits](https://github.com/tendermint/tendermint/compare/v0.33.7...v0.33.9) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index e10abd2cd..4c3b680f6 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/spf13/viper v1.7.1 github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 github.com/stretchr/testify v1.6.1 - github.com/tendermint/tendermint v0.33.7 + github.com/tendermint/tendermint v0.33.9 github.com/tendermint/tm-db v0.5.1 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 diff --git a/go.sum b/go.sum index dccf44184..d444c780b 100644 --- a/go.sum +++ b/go.sum @@ -643,6 +643,8 @@ github.com/tendermint/iavl v0.14.0/go.mod h1:QmfViflFiXzxKLQE4tAUuWQHq+RSuQFxabl github.com/tendermint/tendermint v0.33.5/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= github.com/tendermint/tendermint v0.33.7 h1:b5CQD8ggDtl4u0EbXzabi0MaOw9NrcXker6ijEkAE74= github.com/tendermint/tendermint v0.33.7/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= +github.com/tendermint/tendermint v0.33.9 h1:rRKIfu5qAXX5f9bwX1oUXSZz/ALFJjDuivhkbGUQxiU= +github.com/tendermint/tendermint v0.33.9/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=