Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
evm: refactor statedb implementation (#729)
Browse files Browse the repository at this point in the history
* initial statedb module

unit tests

unit tests

keeper implementation

extract TxConfig

remove unused code

* keeper integration

* fix unit tests

* Apply suggestions from code review

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* fixup! initial statedb module

* changelog

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
  • Loading branch information
yihuang and fedekunze authored Jan 5, 2022
1 parent e9f1ab6 commit ade8431
Show file tree
Hide file tree
Showing 38 changed files with 2,342 additions and 1,679 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (evm) Reject invalid `MsgEthereumTx` wrapping tx
* (evm) Fix `SelfDestruct` opcode by deleting account code and state.
* (feemarket) [tharsis#855](https://github.com/tharsis/ethermint/pull/855) consistent `BaseFee` check logic.
* (evm) [tharsis#729](https://github.com/tharsis/ethermint/pull/729) Refactor EVM StateDB implementation.

### Improvements

Expand Down
4 changes: 2 additions & 2 deletions app/ante/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(10000000000))
suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt(10000000000))

suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, big.NewInt(100))

Expand Down Expand Up @@ -577,7 +577,7 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.ctx = suite.ctx.WithIsCheckTx(tc.checkTx).WithIsReCheckTx(tc.reCheckTx)
suite.app.EvmKeeper.AddBalance(addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
_, err := suite.anteHandler(suite.ctx, tc.txFn(), false)
if tc.expPass {
suite.Require().NoError(err)
Expand Down
39 changes: 14 additions & 25 deletions app/ante/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

ethermint "github.com/tharsis/ethermint/types"
evmkeeper "github.com/tharsis/ethermint/x/evm/keeper"
"github.com/tharsis/ethermint/x/evm/statedb"
evmtypes "github.com/tharsis/ethermint/x/evm/types"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -95,9 +96,6 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
return next(ctx, tx, simulate)
}

avd.evmKeeper.WithContext(ctx)
evmDenom := avd.evmKeeper.GetParams(ctx).EvmDenom

for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
Expand All @@ -117,25 +115,25 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx

// check whether the sender address is EOA
fromAddr := common.BytesToAddress(from)
codeHash := avd.evmKeeper.GetCodeHash(fromAddr)
if codeHash != common.BytesToHash(evmtypes.EmptyCodeHash) {
acct, err := avd.evmKeeper.GetAccount(ctx, fromAddr)
if err != nil {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType,
"the sender is not EOA: address <%v>, codeHash <%s>", fromAddr, codeHash)
"the sender is not EthAccount: address %s", fromAddr)
}

acc := avd.ak.GetAccount(ctx, from)
if acc == nil {
acc = avd.ak.NewAccountWithAddress(ctx, from)
if acct == nil {
acc := avd.ak.NewAccountWithAddress(ctx, from)
avd.ak.SetAccount(ctx, acc)
acct = statedb.NewEmptyAccount()
} else if acct.IsContract() {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType,
"the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
}

if err := evmkeeper.CheckSenderBalance(ctx, avd.bankKeeper, from, txData, evmDenom); err != nil {
if err := evmkeeper.CheckSenderBalance(sdk.NewIntFromBigInt(acct.Balance), txData); err != nil {
return ctx, sdkerrors.Wrap(err, "failed to check sender balance")
}

}
// recover the original gas meter
avd.evmKeeper.WithContext(ctx)
return next(ctx, tx, simulate)
}

Expand Down Expand Up @@ -221,9 +219,6 @@ func NewEthGasConsumeDecorator(
// - user doesn't have enough balance to deduct the transaction fees (gas_limit * gas_price)
// - transaction or block gas meter runs out of gas
func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
// reset the refund gas value in the keeper for the current transaction
egcd.evmKeeper.ResetRefundTransient(ctx)

params := egcd.evmKeeper.GetParams(ctx)

ethCfg := params.ChainConfig.EthereumConfig(egcd.evmKeeper.ChainID())
Expand Down Expand Up @@ -278,8 +273,6 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
}

// we know that we have enough gas on the pool to cover the intrinsic gas
// set up the updated context to the evm Keeper
egcd.evmKeeper.WithContext(ctx)
return next(ctx, tx, simulate)
}

Expand All @@ -301,8 +294,6 @@ func NewCanTransferDecorator(evmKeeper EVMKeeper, fmk evmtypes.FeeMarketKeeper)
// AnteHandle creates an EVM from the message and calls the BlockContext CanTransfer function to
// see if the address can execute the transaction.
func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
ctd.evmKeeper.WithContext(ctx)

params := ctd.evmKeeper.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(ctd.evmKeeper.ChainID())
signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))
Expand Down Expand Up @@ -330,11 +321,12 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
CoinBase: common.Address{},
BaseFee: baseFee,
}
evm := ctd.evmKeeper.NewEVM(coreMsg, cfg, evmtypes.NewNoOpTracer())
stateDB := statedb.New(ctx, ctd.evmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(ctx.HeaderHash().Bytes())))
evm := ctd.evmKeeper.NewEVM(ctx, coreMsg, cfg, evmtypes.NewNoOpTracer(), stateDB)

// check that caller has enough balance to cover asset transfer for **topmost** call
// NOTE: here the gas consumed is from the context with the infinite gas meter
if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(ctd.evmKeeper, coreMsg.From(), coreMsg.Value()) {
if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrInsufficientFunds,
"failed to transfer %s from address %s using the EVM block context transfer function",
Expand All @@ -360,7 +352,6 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
}
}

// set the original gas meter
return next(ctx, tx, simulate)
}

Expand Down Expand Up @@ -422,8 +413,6 @@ func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu
return next(ctx, tx, simulate)
}

vbd.evmKeeper.WithContext(ctx)

err := tx.ValidateBasic()
// ErrNoSignatures is fine with eth tx
if err != nil && !errors.Is(err, sdkerrors.ErrNoSignatures) {
Expand Down
51 changes: 25 additions & 26 deletions app/ante/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/tharsis/ethermint/app/ante"
"github.com/tharsis/ethermint/tests"
"github.com/tharsis/ethermint/x/evm/statedb"
evmtypes "github.com/tharsis/ethermint/x/evm/types"

ethtypes "github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -65,6 +66,8 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil)
tx.From = addr.Hex()

var vmdb *statedb.StateDB

testCases := []struct {
name string
tx sdk.Tx
Expand All @@ -86,7 +89,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
tx,
func() {
// set not as an EOA
suite.app.EvmKeeper.SetCode(addr, []byte("1"))
vmdb.SetCode(addr, []byte("1"))
},
true,
false,
Expand All @@ -96,7 +99,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
tx,
func() {
// reset back to EOA
suite.app.EvmKeeper.SetCode(addr, nil)
vmdb.SetCode(addr, nil)
},
true,
false,
Expand All @@ -105,7 +108,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
"success new account",
tx,
func() {
suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
true,
Expand All @@ -117,7 +120,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
true,
Expand All @@ -126,7 +129,10 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {

for _, tc := range testCases {
suite.Run(tc.name, func() {
vmdb = suite.StateDB()
tc.malleate()
suite.Require().NoError(vmdb.Commit())

_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(tc.checkTx), tc.tx, false, nextFn)

if tc.expPass {
Expand Down Expand Up @@ -204,6 +210,8 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
tx2 := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000000, big.NewInt(1), nil, nil, nil, &ethtypes.AccessList{{Address: addr, StorageKeys: nil}})
tx2.From = addr.Hex()

var vmdb *statedb.StateDB

testCases := []struct {
name string
tx sdk.Tx
Expand All @@ -221,40 +229,28 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
{
"gas limit too low",
tx,
func() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
},
func() {},
false, false,
},
{
"not enough balance for fees",
tx2,
func() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
},
func() {},
false, false,
},
{
"not enough tx gas",
tx2,
func() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
vmdb.AddBalance(addr, big.NewInt(1000000))
},
false, true,
},
{
"not enough block gas",
tx2,
func() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
vmdb.AddBalance(addr, big.NewInt(1000000))

suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1))
},
Expand All @@ -264,10 +260,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
"success",
tx2,
func() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
vmdb.AddBalance(addr, big.NewInt(1000000))

suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000))
},
Expand All @@ -277,7 +270,9 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {

for _, tc := range testCases {
suite.Run(tc.name, func() {
vmdb = suite.StateDB()
tc.malleate()
suite.Require().NoError(vmdb.Commit())

if tc.expPanic {
suite.Require().Panics(func() {
Expand Down Expand Up @@ -331,6 +326,8 @@ func (suite AnteTestSuite) TestCanTransferDecorator() {
err := tx.Sign(suite.ethSigner, tests.NewSigner(privKey))
suite.Require().NoError(err)

var vmdb *statedb.StateDB

testCases := []struct {
name string
tx sdk.Tx
Expand All @@ -355,15 +352,17 @@ func (suite AnteTestSuite) TestCanTransferDecorator() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
},
}

for _, tc := range testCases {
suite.Run(tc.name, func() {
vmdb = suite.StateDB()
tc.malleate()
suite.Require().NoError(vmdb.Commit())

_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(true), tc.tx, false, nextFn)

Expand Down Expand Up @@ -458,7 +457,7 @@ func (suite AnteTestSuite) TestEthIncrementSenderSequenceDecorator() {
txData, err := evmtypes.UnpackTxData(msg.Data)
suite.Require().NoError(err)

nonce := suite.app.EvmKeeper.GetNonce(addr)
nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, addr)
suite.Require().Equal(txData.GetNonce()+1, nonce)
} else {
suite.Require().Error(err)
Expand Down
9 changes: 3 additions & 6 deletions app/ante/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,20 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
tx "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/tharsis/ethermint/x/evm/statedb"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
)

// EVMKeeper defines the expected keeper interface used on the Eth AnteHandler
type EVMKeeper interface {
vm.StateDB
statedb.Keeper

ChainID() *big.Int
GetParams(ctx sdk.Context) evmtypes.Params
WithContext(ctx sdk.Context)
ResetRefundTransient(ctx sdk.Context)
NewEVM(msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.Tracer) *vm.EVM
GetCodeHash(addr common.Address) common.Hash
NewEVM(ctx sdk.Context, msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.Tracer, stateDB vm.StateDB) *vm.EVM
DeductTxCostsFromUserBalance(
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
) (sdk.Coins, error)
Expand Down
9 changes: 5 additions & 4 deletions app/ante/sigs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"math/big"

"github.com/tharsis/ethermint/tests"
"github.com/tharsis/ethermint/x/evm/statedb"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
)

Expand All @@ -14,11 +15,11 @@ func (suite AnteTestSuite) TestSignatures() {
addr, privKey := tests.NewAddrKey()
to := tests.GenerateAddress()

acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
acc := statedb.NewEmptyAccount()
acc.Nonce = 1
acc.Balance = big.NewInt(10000000000)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(10000000000))
suite.app.EvmKeeper.SetAccount(suite.ctx, addr, *acc)
msgEthereumTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
msgEthereumTx.From = addr.Hex()

Expand Down
6 changes: 6 additions & 0 deletions app/ante/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"

"github.com/stretchr/testify/suite"
Expand All @@ -25,6 +26,7 @@ import (
ante "github.com/tharsis/ethermint/app/ante"
"github.com/tharsis/ethermint/encoding"
"github.com/tharsis/ethermint/tests"
"github.com/tharsis/ethermint/x/evm/statedb"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
feemarkettypes "github.com/tharsis/ethermint/x/feemarket/types"

Expand All @@ -43,6 +45,10 @@ type AnteTestSuite struct {
enableLondonHF bool
}

func (suite *AnteTestSuite) StateDB() *statedb.StateDB {
return statedb.New(suite.ctx, suite.app.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(suite.ctx.HeaderHash().Bytes())))
}

func (suite *AnteTestSuite) SetupTest() {
checkTx := false

Expand Down
Loading

0 comments on commit ade8431

Please sign in to comment.