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

Commit

Permalink
Problem: EVM keeper keeps intermediate states inside
Browse files Browse the repository at this point in the history
Closes: #664

Solution:
- move the states(ctxStack and stateErr) into struct `StateDB`
- `StateDB` is a temparory object which only lives in `ApplyMessage`

move keeper methods out of statedb

fix lint

refactor state_transition

fix unit tests

Update x/evm/keeper/statedb.go

fix tests

rename
  • Loading branch information
yihuang committed Nov 15, 2021
1 parent 32c905a commit ef96e84
Show file tree
Hide file tree
Showing 28 changed files with 931 additions and 725 deletions.
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.vmdb.AddBalance(addr, big.NewInt(10000000000))

testCases := []struct {
name string
Expand Down Expand Up @@ -238,7 +238,7 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
suite.vmdb.AddBalance(addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))

testCases := []struct {
name string
Expand Down
28 changes: 7 additions & 21 deletions app/ante/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,12 @@ import (

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

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 Expand Up @@ -124,7 +122,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() {
Expand Down Expand Up @@ -152,7 +149,7 @@ 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)
codeHash := avd.evmKeeper.GetCodeHash(ctx, fromAddr)
if codeHash != common.BytesToHash(evmtypes.EmptyCodeHash) {
return ctx, stacktrace.Propagate(sdkerrors.Wrapf(sdkerrors.ErrInvalidType,
"the sender is not EOA: address <%v>, codeHash <%s>", fromAddr, codeHash), "")
Expand All @@ -170,7 +167,6 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx

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

Expand Down Expand Up @@ -322,8 +318,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 @@ -345,8 +339,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)
feeMktParams := ctd.feemarketKeeper.GetParams(ctx)

Expand Down Expand Up @@ -382,11 +374,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 := evmtypes.NewStateDB(ctx, ctd.evmKeeper)
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, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "address %s", coreMsg.From()),
"failed to transfer %s using the EVM block context transfer function", coreMsg.Value(),
Expand All @@ -408,8 +401,6 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
}
}

ctd.evmKeeper.WithContext(ctx)

// set the original gas meter
return next(ctx, tx, simulate)
}
Expand Down Expand Up @@ -447,9 +438,6 @@ func (ald AccessListDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
return next(ctx, tx, simulate)
}

// setup the keeper context before setting the access list
ald.evmKeeper.WithContext(ctx)

for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
Expand All @@ -466,11 +454,9 @@ func (ald AccessListDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
return ctx, stacktrace.Propagate(err, "failed to unpack tx data")
}

ald.evmKeeper.PrepareAccessList(sender, txData.GetTo(), vm.ActivePrecompiles(rules), txData.GetAccessList())
ald.evmKeeper.PrepareAccessList(ctx, sender, txData.GetTo(), vm.ActivePrecompiles(rules), txData.GetAccessList())
}

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

Expand Down
22 changes: 11 additions & 11 deletions app/ante/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
tx,
func() {
// set not as an EOA
suite.app.EvmKeeper.SetCode(addr, []byte("1"))
suite.vmdb.SetCode(addr, []byte("1"))
},
true,
false,
Expand All @@ -97,7 +97,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
tx,
func() {
// reset back to EOA
suite.app.EvmKeeper.SetCode(addr, nil)
suite.vmdb.SetCode(addr, nil)
},
true,
false,
Expand All @@ -106,7 +106,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
"success new account",
tx,
func() {
suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
suite.vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
true,
Expand All @@ -118,7 +118,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))
suite.vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
true,
Expand Down Expand Up @@ -244,7 +244,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
suite.vmdb.AddBalance(addr, big.NewInt(1000000))
},
false, true,
},
Expand All @@ -255,7 +255,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

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

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

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

suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000))
},
Expand Down Expand Up @@ -334,7 +334,7 @@ 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))
suite.vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
},
Expand Down Expand Up @@ -383,7 +383,7 @@ func (suite AnteTestSuite) TestAccessListDecorator() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
suite.vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
},
Expand All @@ -394,7 +394,7 @@ func (suite AnteTestSuite) TestAccessListDecorator() {
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(1000000))
suite.vmdb.AddBalance(addr, big.NewInt(1000000))
},
true,
},
Expand Down Expand Up @@ -493,7 +493,7 @@ func (suite AnteTestSuite) TestEthIncrementSenderSequenceDecorator() {
txData, err := evmtypes.UnpackTxData(msg.Data)
suite.Require().NoError(err)

nonce := suite.app.EvmKeeper.GetNonce(addr)
nonce := suite.vmdb.GetNonce(addr)
if txData.GetTo() == nil {
suite.Require().Equal(txData.GetNonce(), nonce)
} else {
Expand Down
2 changes: 1 addition & 1 deletion app/ante/sigs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (suite AnteTestSuite) TestSignatures() {
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)

suite.app.EvmKeeper.AddBalance(addr, big.NewInt(10000000000))
suite.app.EvmKeeper.AddBalance(suite.ctx, addr, big.NewInt(10000000000))
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
3 changes: 3 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 (
"time"

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

"github.com/stretchr/testify/suite"

Expand Down Expand Up @@ -38,6 +39,7 @@ type AnteTestSuite struct {
anteHandler sdk.AnteHandler
ethSigner ethtypes.Signer
dynamicTxFee bool
vmdb vm.StateDB
}

func (suite *AnteTestSuite) SetupTest() {
Expand All @@ -58,6 +60,7 @@ func (suite *AnteTestSuite) SetupTest() {
suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(sdk.NewDecCoin(evmtypes.DefaultEVMDenom, sdk.OneInt())))
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1000000000000000000))
suite.app.EvmKeeper.WithChainID(suite.ctx)
suite.vmdb = evmtypes.NewStateDB(suite.ctx, suite.app.EvmKeeper)

infCtx := suite.ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
suite.app.AccountKeeper.SetParams(infCtx, authtypes.DefaultParams())
Expand Down
33 changes: 17 additions & 16 deletions tests/importer/importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

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

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
Expand Down Expand Up @@ -139,23 +140,23 @@ func (suite *ImporterTestSuite) TestImportBlocks() {
})
ctx := suite.app.NewContext(false, tmheader)
ctx = ctx.WithBlockHeight(tmheader.Height)
suite.app.EvmKeeper.WithContext(ctx)
vmdb := evmtypes.NewStateDB(ctx, suite.app.EvmKeeper)

if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
applyDAOHardFork(suite.app.EvmKeeper)
applyDAOHardFork(vmdb)
}

for _, tx := range block.Transactions() {

receipt, gas, err := applyTransaction(
chainConfig, chainContext, nil, gp, suite.app.EvmKeeper, header, tx, usedGas, vmConfig,
ctx, chainConfig, chainContext, nil, gp, suite.app.EvmKeeper, vmdb, header, tx, usedGas, vmConfig,
)
suite.Require().NoError(err, "failed to apply tx at block %d; tx: %X; gas %d; receipt:%v", block.NumberU64(), tx.Hash(), gas, receipt)
suite.Require().NotNil(receipt)
}

// apply mining rewards
accumulateRewards(chainConfig, suite.app.EvmKeeper, header, block.Uncles())
accumulateRewards(chainConfig, vmdb, header, block.Uncles())

// simulate BaseApp EndBlocker commitment
endBR := types.RequestEndBlock{Height: tmheader.Height}
Expand All @@ -173,7 +174,7 @@ func (suite *ImporterTestSuite) TestImportBlocks() {
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(
config *ethparams.ChainConfig, evmKeeper *evmkeeper.Keeper,
config *ethparams.ChainConfig, vmdb ethvm.StateDB,
header *ethtypes.Header, uncles []*ethtypes.Header,
) {
// select the correct block reward based on chain progression
Expand All @@ -191,12 +192,12 @@ func accumulateRewards(
r.Sub(r, header.Number)
r.Mul(r, blockReward)
r.Div(r, rewardBig8)
evmKeeper.AddBalance(uncle.Coinbase, r)
vmdb.AddBalance(uncle.Coinbase, r)
r.Div(blockReward, rewardBig32)
reward.Add(reward, r)
}

evmKeeper.AddBalance(header.Coinbase, reward)
vmdb.AddBalance(header.Coinbase, reward)
}

// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
Expand All @@ -205,15 +206,15 @@ func accumulateRewards(
// Code is pulled from go-ethereum 1.9 because the StateDB interface does not include the
// SetBalance function implementation
// Ref: https://github.com/ethereum/go-ethereum/blob/52f2461774bcb8cdd310f86b4bc501df5b783852/consensus/misc/dao.go#L74
func applyDAOHardFork(evmKeeper *evmkeeper.Keeper) {
func applyDAOHardFork(vmdb ethvm.StateDB) {
// Retrieve the contract to refund balances into
if !evmKeeper.Exist(ethparams.DAORefundContract) {
evmKeeper.CreateAccount(ethparams.DAORefundContract)
if !vmdb.Exist(ethparams.DAORefundContract) {
vmdb.CreateAccount(ethparams.DAORefundContract)
}

// Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range ethparams.DAODrainList() {
evmKeeper.AddBalance(ethparams.DAORefundContract, evmKeeper.GetBalance(addr))
vmdb.AddBalance(ethparams.DAORefundContract, vmdb.GetBalance(addr))
}
}

Expand All @@ -224,8 +225,8 @@ func applyDAOHardFork(evmKeeper *evmkeeper.Keeper) {
// Function is also pulled from go-ethereum 1.9 because of the incompatible usage
// Ref: https://github.com/ethereum/go-ethereum/blob/52f2461774bcb8cdd310f86b4bc501df5b783852/core/state_processor.go#L88
func applyTransaction(
config *ethparams.ChainConfig, bc ethcore.ChainContext, author *common.Address,
gp *ethcore.GasPool, evmKeeper *evmkeeper.Keeper, header *ethtypes.Header,
ctx sdk.Context, config *ethparams.ChainConfig, bc ethcore.ChainContext, author *common.Address,
gp *ethcore.GasPool, evmKeeper *evmkeeper.Keeper, vmdb ethvm.StateDB, header *ethtypes.Header,
tx *ethtypes.Transaction, usedGas *uint64, cfg ethvm.Config,
) (*ethtypes.Receipt, uint64, error) {
msg, err := tx.AsMessage(ethtypes.MakeSigner(config, header.Number), sdk.ZeroInt().BigInt())
Expand All @@ -239,7 +240,7 @@ func applyTransaction(

// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := ethvm.NewEVM(blockCtx, txCtx, evmKeeper, config, cfg)
vmenv := ethvm.NewEVM(blockCtx, txCtx, vmdb, config, cfg)

// Apply the transaction to the current state (included in the env)
execResult, err := ethcore.ApplyMessage(vmenv, msg, gp)
Expand All @@ -263,11 +264,11 @@ func applyTransaction(
}

// Set the receipt logs and create a bloom for filtering
receipt.Logs = evmKeeper.GetTxLogsTransient(tx.Hash())
receipt.Logs = evmKeeper.GetTxLogsTransient(ctx, tx.Hash())
receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt})
receipt.BlockHash = header.Hash()
receipt.BlockNumber = header.Number
receipt.TransactionIndex = uint(evmKeeper.GetTxIndexTransient())
receipt.TransactionIndex = uint(evmKeeper.GetTxIndexTransient(ctx))

return receipt, execResult.UsedGas, err
}
5 changes: 2 additions & 3 deletions testutil/network/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,9 @@ func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalance
}

func WriteFile(name string, dir string, contents []byte) error {
writePath := filepath.Join(dir)
file := filepath.Join(writePath, name)
file := filepath.Join(dir, name)

err := tmos.EnsureDir(writePath, 0o755)
err := tmos.EnsureDir(dir, 0o755)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit ef96e84

Please sign in to comment.