evm: benchmark state DB (#514)

* evm: benchmark state DB

* add more bech funcs

* rm func from interface
This commit is contained in:
Federico Kunze Küllmer 2021-08-31 14:50:31 +02:00 committed by GitHub
parent 9a8827e790
commit 87c4ea2dc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 264 additions and 85 deletions

View File

@ -26,7 +26,6 @@ type EVMKeeper interface {
GetParams(ctx sdk.Context) evmtypes.Params GetParams(ctx sdk.Context) evmtypes.Params
WithContext(ctx sdk.Context) WithContext(ctx sdk.Context)
ResetRefundTransient(ctx sdk.Context) ResetRefundTransient(ctx sdk.Context)
GetCoinbaseAddress() (common.Address, error)
NewEVM(msg core.Message, config *params.ChainConfig, params evmtypes.Params, coinbase common.Address, tracer vm.Tracer) *vm.EVM NewEVM(msg core.Message, config *params.ChainConfig, params evmtypes.Params, coinbase common.Address, tracer vm.Tracer) *vm.EVM
GetCodeHash(addr common.Address) common.Hash GetCodeHash(addr common.Address) common.Hash
} }

View File

@ -330,7 +330,7 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms
params := k.GetParams(ctx) params := k.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
coinbase, err := k.GetCoinbaseAddress() coinbase, err := k.GetCoinbaseAddress(ctx)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
@ -394,7 +394,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
params := k.GetParams(ctx) params := k.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
coinbase, err := k.GetCoinbaseAddress() coinbase, err := k.GetCoinbaseAddress(ctx)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }

View File

@ -69,39 +69,40 @@ func (k Keeper) VMConfig(msg core.Message, params types.Params, tracer vm.Tracer
func (k Keeper) GetHashFn() vm.GetHashFunc { func (k Keeper) GetHashFn() vm.GetHashFunc {
return func(height uint64) common.Hash { return func(height uint64) common.Hash {
h := int64(height) h := int64(height)
ctx := k.Ctx()
switch { switch {
case k.Ctx().BlockHeight() == h: case ctx.BlockHeight() == h:
// Case 1: The requested height matches the one from the context so we can retrieve the header // Case 1: The requested height matches the one from the context so we can retrieve the header
// hash directly from the context. // hash directly from the context.
// Note: The headerHash is only set at begin block, it will be nil in case of a query context // Note: The headerHash is only set at begin block, it will be nil in case of a query context
headerHash := k.Ctx().HeaderHash() headerHash := ctx.HeaderHash()
if len(headerHash) != 0 { if len(headerHash) != 0 {
return common.BytesToHash(headerHash) return common.BytesToHash(headerHash)
} }
// only recompute the hash if not set (eg: checkTxState) // only recompute the hash if not set (eg: checkTxState)
contextBlockHeader := k.Ctx().BlockHeader() contextBlockHeader := ctx.BlockHeader()
header, err := tmtypes.HeaderFromProto(&contextBlockHeader) header, err := tmtypes.HeaderFromProto(&contextBlockHeader)
if err != nil { if err != nil {
k.Logger(k.Ctx()).Error("failed to cast tendermint header from proto", "error", err) k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err)
return common.Hash{} return common.Hash{}
} }
headerHash = header.Hash() headerHash = header.Hash()
return common.BytesToHash(headerHash) return common.BytesToHash(headerHash)
case k.Ctx().BlockHeight() > h: case ctx.BlockHeight() > h:
// Case 2: if the chain is not the current height we need to retrieve the hash from the store for the // Case 2: if the chain is not the current height we need to retrieve the hash from the store for the
// current chain epoch. This only applies if the current height is greater than the requested height. // current chain epoch. This only applies if the current height is greater than the requested height.
histInfo, found := k.stakingKeeper.GetHistoricalInfo(k.Ctx(), h) histInfo, found := k.stakingKeeper.GetHistoricalInfo(ctx, h)
if !found { if !found {
k.Logger(k.Ctx()).Debug("historical info not found", "height", h) k.Logger(ctx).Debug("historical info not found", "height", h)
return common.Hash{} return common.Hash{}
} }
header, err := tmtypes.HeaderFromProto(&histInfo.Header) header, err := tmtypes.HeaderFromProto(&histInfo.Header)
if err != nil { if err != nil {
k.Logger(k.Ctx()).Error("failed to cast tendermint header from proto", "error", err) k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err)
return common.Hash{} return common.Hash{}
} }
@ -133,7 +134,8 @@ func (k Keeper) GetHashFn() vm.GetHashFunc {
func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumTxResponse, error) { func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumTxResponse, error) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.MetricKeyTransitionDB) defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.MetricKeyTransitionDB)
params := k.GetParams(k.Ctx()) ctx := k.Ctx()
params := k.GetParams(ctx)
// return error if contract creation or call are disabled through governance // return error if contract creation or call are disabled through governance
if !params.EnableCreate && tx.To() == nil { if !params.EnableCreate && tx.To() == nil {
@ -145,7 +147,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
// get the latest signer according to the chain rules from the config // get the latest signer according to the chain rules from the config
signer := ethtypes.MakeSigner(ethCfg, big.NewInt(k.Ctx().BlockHeight())) signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))
msg, err := tx.AsMessage(signer) msg, err := tx.AsMessage(signer)
if err != nil { if err != nil {
@ -153,13 +155,13 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
} }
// get the coinbase address from the block proposer // get the coinbase address from the block proposer
coinbase, err := k.GetCoinbaseAddress() coinbase, err := k.GetCoinbaseAddress(ctx)
if err != nil { if err != nil {
return nil, stacktrace.Propagate(err, "failed to obtain coinbase address") return nil, stacktrace.Propagate(err, "failed to obtain coinbase address")
} }
// create an ethereum EVM instance and run the message // create an ethereum EVM instance and run the message
tracer := types.NewTracer(k.tracer, msg, ethCfg, k.Ctx().BlockHeight(), k.debug) tracer := types.NewTracer(k.tracer, msg, ethCfg, ctx.BlockHeight(), k.debug)
evm := k.NewEVM(msg, ethCfg, params, coinbase, tracer) evm := k.NewEVM(msg, ethCfg, params, coinbase, tracer)
txHash := tx.Hash() txHash := tx.Hash()
@ -368,14 +370,15 @@ func (k *Keeper) RefundGas(msg core.Message, leftoverGas, refundQuotient uint64)
// 'gasUsed' // 'gasUsed'
func (k *Keeper) resetGasMeterAndConsumeGas(gasUsed uint64) { func (k *Keeper) resetGasMeterAndConsumeGas(gasUsed uint64) {
// reset the gas count // reset the gas count
k.Ctx().GasMeter().RefundGas(k.Ctx().GasMeter().GasConsumed(), "reset the gas count") ctx := k.Ctx()
k.Ctx().GasMeter().ConsumeGas(gasUsed, "apply evm transaction") ctx.GasMeter().RefundGas(ctx.GasMeter().GasConsumed(), "reset the gas count")
ctx.GasMeter().ConsumeGas(gasUsed, "apply evm transaction")
} }
// GetCoinbaseAddress returns the block proposer's validator operator address. // GetCoinbaseAddress returns the block proposer's validator operator address.
func (k Keeper) GetCoinbaseAddress() (common.Address, error) { func (k Keeper) GetCoinbaseAddress(ctx sdk.Context) (common.Address, error) {
consAddr := sdk.ConsAddress(k.Ctx().BlockHeader().ProposerAddress) consAddr := sdk.ConsAddress(ctx.BlockHeader().ProposerAddress)
validator, found := k.stakingKeeper.GetValidatorByConsAddr(k.Ctx(), consAddr) validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
if !found { if !found {
return common.Address{}, stacktrace.Propagate( return common.Address{}, stacktrace.Propagate(
sdkerrors.Wrap(stakingtypes.ErrNoValidatorFound, consAddr.String()), sdkerrors.Wrap(stakingtypes.ErrNoValidatorFound, consAddr.String()),

View File

@ -65,7 +65,7 @@ func (suite *KeeperTestSuite) TestGetCoinbaseAddress() {
tc.malleate() tc.malleate()
coinbase, err := suite.app.EvmKeeper.GetCoinbaseAddress() coinbase, err := suite.app.EvmKeeper.GetCoinbaseAddress(suite.ctx)
if tc.expPass { if tc.expPass {
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Require().Equal(valOpAddr, coinbase) suite.Require().Equal(valOpAddr, coinbase)

View File

@ -29,8 +29,8 @@ var _ vm.StateDB = &Keeper{}
// address. // address.
func (k *Keeper) CreateAccount(addr common.Address) { func (k *Keeper) CreateAccount(addr common.Address) {
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
ctx := k.Ctx()
account := k.accountKeeper.GetAccount(k.Ctx(), cosmosAddr) account := k.accountKeeper.GetAccount(ctx, cosmosAddr)
log := "" log := ""
if account == nil { if account == nil {
log = "account created" log = "account created"
@ -39,10 +39,10 @@ func (k *Keeper) CreateAccount(addr common.Address) {
k.ResetAccount(addr) k.ResetAccount(addr)
} }
account = k.accountKeeper.NewAccountWithAddress(k.Ctx(), cosmosAddr) account = k.accountKeeper.NewAccountWithAddress(ctx, cosmosAddr)
k.accountKeeper.SetAccount(k.Ctx(), account) k.accountKeeper.SetAccount(ctx, account)
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
log, log,
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -57,8 +57,10 @@ func (k *Keeper) CreateAccount(addr common.Address) {
// coins and transferring them to the address. The coin denomination is obtained // coins and transferring them to the address. The coin denomination is obtained
// from the module parameters. // from the module parameters.
func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) { func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
ctx := k.Ctx()
if amount.Sign() != 1 { if amount.Sign() != 1 {
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"ignored non-positive amount addition", "ignored non-positive amount addition",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"amount", amount.Int64(), "amount", amount.Int64(),
@ -67,12 +69,18 @@ func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
} }
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
params := k.GetParams(ctx)
params := k.GetParams(k.Ctx()) // Coin denom and amount already validated
coins := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(amount))} coins := sdk.Coins{
{
Denom: params.EvmDenom,
Amount: sdk.NewIntFromBigInt(amount),
},
}
if err := k.bankKeeper.MintCoins(k.Ctx(), types.ModuleName, coins); err != nil { if err := k.bankKeeper.MintCoins(ctx, types.ModuleName, coins); err != nil {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"failed to mint coins when adding balance", "failed to mint coins when adding balance",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -81,8 +89,8 @@ func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
return return
} }
if err := k.bankKeeper.SendCoinsFromModuleToAccount(k.Ctx(), types.ModuleName, cosmosAddr, coins); err != nil { if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, cosmosAddr, coins); err != nil {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"failed to send from module to account when adding balance", "failed to send from module to account when adding balance",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -91,7 +99,7 @@ func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
return return
} }
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"balance addition", "balance addition",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -103,8 +111,10 @@ func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
// from the module parameters. This function performs a no-op if the amount is negative // from the module parameters. This function performs a no-op if the amount is negative
// or the user doesn't have enough funds for the transfer. // or the user doesn't have enough funds for the transfer.
func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) { func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
ctx := k.Ctx()
if amount.Sign() != 1 { if amount.Sign() != 1 {
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"ignored non-positive amount addition", "ignored non-positive amount addition",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"amount", amount.Int64(), "amount", amount.Int64(),
@ -114,11 +124,18 @@ func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
params := k.GetParams(k.Ctx()) params := k.GetParams(ctx)
coins := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(amount))}
if err := k.bankKeeper.SendCoinsFromAccountToModule(k.Ctx(), cosmosAddr, types.ModuleName, coins); err != nil { // Coin denom and amount already validated
k.Logger(k.Ctx()).Debug( coins := sdk.Coins{
{
Denom: params.EvmDenom,
Amount: sdk.NewIntFromBigInt(amount),
},
}
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, cosmosAddr, types.ModuleName, coins); err != nil {
k.Logger(ctx).Debug(
"failed to send from account to module when subtracting balance", "failed to send from account to module when subtracting balance",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -128,8 +145,8 @@ func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
return return
} }
if err := k.bankKeeper.BurnCoins(k.Ctx(), types.ModuleName, coins); err != nil { if err := k.bankKeeper.BurnCoins(ctx, types.ModuleName, coins); err != nil {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"failed to burn coins when subtracting balance", "failed to burn coins when subtracting balance",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -138,7 +155,7 @@ func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
return return
} }
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"balance subtraction", "balance subtraction",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -148,9 +165,11 @@ func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
// GetBalance returns the EVM denomination balance of the provided address. The // GetBalance returns the EVM denomination balance of the provided address. The
// denomination is obtained from the module parameters. // denomination is obtained from the module parameters.
func (k *Keeper) GetBalance(addr common.Address) *big.Int { func (k *Keeper) GetBalance(addr common.Address) *big.Int {
ctx := k.Ctx()
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
params := k.GetParams(k.Ctx()) params := k.GetParams(ctx)
balance := k.bankKeeper.GetBalance(k.Ctx(), cosmosAddr, params.EvmDenom) balance := k.bankKeeper.GetBalance(ctx, cosmosAddr, params.EvmDenom)
return balance.Amount.BigInt() return balance.Amount.BigInt()
} }
@ -162,10 +181,12 @@ func (k *Keeper) GetBalance(addr common.Address) *big.Int {
// GetNonce retrieves the account with the given address and returns the tx // GetNonce retrieves the account with the given address and returns the tx
// sequence (i.e nonce). The function performs a no-op if the account is not found. // sequence (i.e nonce). The function performs a no-op if the account is not found.
func (k *Keeper) GetNonce(addr common.Address) uint64 { func (k *Keeper) GetNonce(addr common.Address) uint64 {
ctx := k.Ctx()
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
nonce, err := k.accountKeeper.GetSequence(k.Ctx(), cosmosAddr) nonce, err := k.accountKeeper.GetSequence(ctx, cosmosAddr)
if err != nil { if err != nil {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"account not found", "account not found",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -179,21 +200,23 @@ func (k *Keeper) GetNonce(addr common.Address) uint64 {
// SetNonce sets the given nonce as the sequence of the address' account. If the // SetNonce sets the given nonce as the sequence of the address' account. If the
// account doesn't exist, a new one will be created from the address. // account doesn't exist, a new one will be created from the address.
func (k *Keeper) SetNonce(addr common.Address, nonce uint64) { func (k *Keeper) SetNonce(addr common.Address, nonce uint64) {
ctx := k.Ctx()
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
account := k.accountKeeper.GetAccount(k.Ctx(), cosmosAddr) account := k.accountKeeper.GetAccount(ctx, cosmosAddr)
if account == nil { if account == nil {
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"account not found", "account not found",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
) )
// create address if it doesn't exist // create address if it doesn't exist
account = k.accountKeeper.NewAccountWithAddress(k.Ctx(), cosmosAddr) account = k.accountKeeper.NewAccountWithAddress(ctx, cosmosAddr)
} }
if err := account.SetSequence(nonce); err != nil { if err := account.SetSequence(nonce); err != nil {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"failed to set nonce", "failed to set nonce",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -204,9 +227,9 @@ func (k *Keeper) SetNonce(addr common.Address, nonce uint64) {
return return
} }
k.accountKeeper.SetAccount(k.Ctx(), account) k.accountKeeper.SetAccount(ctx, account)
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"nonce set", "nonce set",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -221,8 +244,10 @@ func (k *Keeper) SetNonce(addr common.Address, nonce uint64) {
// GetCodeHash fetches the account from the store and returns its code hash. If the account doesn't // GetCodeHash fetches the account from the store and returns its code hash. If the account doesn't
// exist or is not an EthAccount type, GetCodeHash returns the empty code hash value. // exist or is not an EthAccount type, GetCodeHash returns the empty code hash value.
func (k *Keeper) GetCodeHash(addr common.Address) common.Hash { func (k *Keeper) GetCodeHash(addr common.Address) common.Hash {
ctx := k.Ctx()
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
account := k.accountKeeper.GetAccount(k.Ctx(), cosmosAddr)
account := k.accountKeeper.GetAccount(ctx, cosmosAddr)
if account == nil { if account == nil {
return common.BytesToHash(types.EmptyCodeHash) return common.BytesToHash(types.EmptyCodeHash)
} }
@ -238,17 +263,18 @@ func (k *Keeper) GetCodeHash(addr common.Address) common.Hash {
// GetCode returns the code byte array associated with the given address. // GetCode returns the code byte array associated with the given address.
// If the code hash from the account is empty, this function returns nil. // If the code hash from the account is empty, this function returns nil.
func (k *Keeper) GetCode(addr common.Address) []byte { func (k *Keeper) GetCode(addr common.Address) []byte {
ctx := k.Ctx()
hash := k.GetCodeHash(addr) hash := k.GetCodeHash(addr)
if bytes.Equal(hash.Bytes(), common.BytesToHash(types.EmptyCodeHash).Bytes()) { if bytes.Equal(hash.Bytes(), common.BytesToHash(types.EmptyCodeHash).Bytes()) {
return nil return nil
} }
store := prefix.NewStore(k.Ctx().KVStore(k.storeKey), types.KeyPrefixCode) store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixCode)
code := store.Get(hash.Bytes()) code := store.Get(hash.Bytes())
if len(code) == 0 { if len(code) == 0 {
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"code not found", "code not found",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"code-hash", hash.Hex(), "code-hash", hash.Hex(),
@ -261,21 +287,23 @@ func (k *Keeper) GetCode(addr common.Address) []byte {
// SetCode stores the code byte array to the application KVStore and sets the // SetCode stores the code byte array to the application KVStore and sets the
// code hash to the given account. The code is deleted from the store if it is empty. // code hash to the given account. The code is deleted from the store if it is empty.
func (k *Keeper) SetCode(addr common.Address, code []byte) { func (k *Keeper) SetCode(addr common.Address, code []byte) {
ctx := k.Ctx()
if bytes.Equal(code, types.EmptyCodeHash) { if bytes.Equal(code, types.EmptyCodeHash) {
k.Logger(k.Ctx()).Debug("passed in EmptyCodeHash, but expected empty code") k.Logger(ctx).Debug("passed in EmptyCodeHash, but expected empty code")
} }
hash := crypto.Keccak256Hash(code) hash := crypto.Keccak256Hash(code)
// update account code hash // update account code hash
account := k.accountKeeper.GetAccount(k.Ctx(), addr.Bytes()) account := k.accountKeeper.GetAccount(ctx, addr.Bytes())
if account == nil { if account == nil {
account = k.accountKeeper.NewAccountWithAddress(k.Ctx(), addr.Bytes()) account = k.accountKeeper.NewAccountWithAddress(ctx, addr.Bytes())
k.accountKeeper.SetAccount(k.Ctx(), account) k.accountKeeper.SetAccount(ctx, account)
} }
ethAccount, isEthAccount := account.(*ethermint.EthAccount) ethAccount, isEthAccount := account.(*ethermint.EthAccount)
if !isEthAccount { if !isEthAccount {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"invalid account type", "invalid account type",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"code-hash", hash.Hex(), "code-hash", hash.Hex(),
@ -284,9 +312,9 @@ func (k *Keeper) SetCode(addr common.Address, code []byte) {
} }
ethAccount.CodeHash = hash.Hex() ethAccount.CodeHash = hash.Hex()
k.accountKeeper.SetAccount(k.Ctx(), ethAccount) k.accountKeeper.SetAccount(ctx, ethAccount)
store := prefix.NewStore(k.Ctx().KVStore(k.storeKey), types.KeyPrefixCode) store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixCode)
action := "updated" action := "updated"
@ -298,7 +326,7 @@ func (k *Keeper) SetCode(addr common.Address, code []byte) {
store.Set(hash.Bytes(), code) store.Set(hash.Bytes(), code)
} }
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
fmt.Sprintf("code %s", action), fmt.Sprintf("code %s", action),
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"code-hash", hash.Hex(), "code-hash", hash.Hex(),
@ -323,17 +351,19 @@ func (k *Keeper) GetCodeSize(addr common.Address) int {
// AddRefund adds the given amount of gas to the refund transient value. // AddRefund adds the given amount of gas to the refund transient value.
func (k *Keeper) AddRefund(gas uint64) { func (k *Keeper) AddRefund(gas uint64) {
ctx := k.Ctx()
refund := k.GetRefund() refund := k.GetRefund()
refund += gas refund += gas
store := k.Ctx().TransientStore(k.transientKey) store := ctx.TransientStore(k.transientKey)
store.Set(types.KeyPrefixTransientRefund, sdk.Uint64ToBigEndian(refund)) store.Set(types.KeyPrefixTransientRefund, sdk.Uint64ToBigEndian(refund))
} }
// SubRefund subtracts the given amount of gas from the transient refund value. This function // SubRefund subtracts the given amount of gas from the transient refund value. This function
// will panic if gas amount is greater than the stored refund. // will panic if gas amount is greater than the stored refund.
func (k *Keeper) SubRefund(gas uint64) { func (k *Keeper) SubRefund(gas uint64) {
ctx := k.Ctx()
refund := k.GetRefund() refund := k.GetRefund()
if gas > refund { if gas > refund {
@ -343,14 +373,15 @@ func (k *Keeper) SubRefund(gas uint64) {
refund -= gas refund -= gas
store := k.Ctx().TransientStore(k.transientKey) store := ctx.TransientStore(k.transientKey)
store.Set(types.KeyPrefixTransientRefund, sdk.Uint64ToBigEndian(refund)) store.Set(types.KeyPrefixTransientRefund, sdk.Uint64ToBigEndian(refund))
} }
// GetRefund returns the amount of gas available for return after the tx execution // GetRefund returns the amount of gas available for return after the tx execution
// finalizes. This value is reset to 0 on every transaction. // finalizes. This value is reset to 0 on every transaction.
func (k *Keeper) GetRefund() uint64 { func (k *Keeper) GetRefund() uint64 {
store := k.Ctx().TransientStore(k.transientKey) ctx := k.Ctx()
store := ctx.TransientStore(k.transientKey)
bz := store.Get(types.KeyPrefixTransientRefund) bz := store.Get(types.KeyPrefixTransientRefund)
if len(bz) == 0 { if len(bz) == 0 {
@ -385,13 +416,15 @@ func (k *Keeper) GetCommittedState(addr common.Address, hash common.Hash) common
// GetState returns the committed state for the given key hash, as all changes are committed directly // GetState returns the committed state for the given key hash, as all changes are committed directly
// to the KVStore. // to the KVStore.
func (k *Keeper) GetState(addr common.Address, hash common.Hash) common.Hash { func (k *Keeper) GetState(addr common.Address, hash common.Hash) common.Hash {
return doGetState(k.Ctx(), k.storeKey, addr, hash) ctx := k.Ctx()
return doGetState(ctx, k.storeKey, addr, hash)
} }
// SetState sets the given hashes (key, value) to the KVStore. If the value hash is empty, this // SetState sets the given hashes (key, value) to the KVStore. If the value hash is empty, this
// function deletes the key from the store. // function deletes the key from the store.
func (k *Keeper) SetState(addr common.Address, key, value common.Hash) { func (k *Keeper) SetState(addr common.Address, key, value common.Hash) {
store := prefix.NewStore(k.Ctx().KVStore(k.storeKey), types.AddressStoragePrefix(addr)) ctx := k.Ctx()
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr))
key = types.KeyAddressStorage(addr, key) key = types.KeyAddressStorage(addr, key)
action := "updated" action := "updated"
@ -402,7 +435,7 @@ func (k *Keeper) SetState(addr common.Address, key, value common.Hash) {
store.Set(key.Bytes(), value.Bytes()) store.Set(key.Bytes(), value.Bytes())
} }
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
fmt.Sprintf("state %s", action), fmt.Sprintf("state %s", action),
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"key", key.Hex(), "key", key.Hex(),
@ -416,6 +449,8 @@ func (k *Keeper) SetState(addr common.Address, key, value common.Hash) {
// Suicide marks the given account as suicided and clears the account balance of // Suicide marks the given account as suicided and clears the account balance of
// the EVM tokens. // the EVM tokens.
func (k *Keeper) Suicide(addr common.Address) bool { func (k *Keeper) Suicide(addr common.Address) bool {
ctx := k.Ctx()
prev := k.HasSuicided(addr) prev := k.HasSuicided(addr)
if prev { if prev {
return true return true
@ -425,7 +460,7 @@ func (k *Keeper) Suicide(addr common.Address) bool {
_, err := k.ClearBalance(cosmosAddr) _, err := k.ClearBalance(cosmosAddr)
if err != nil { if err != nil {
k.Logger(k.Ctx()).Error( k.Logger(ctx).Error(
"failed to subtract balance on suicide", "failed to subtract balance on suicide",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -436,12 +471,9 @@ func (k *Keeper) Suicide(addr common.Address) bool {
} }
// TODO: (@fedekunze) do we also need to delete the storage state and the code? // TODO: (@fedekunze) do we also need to delete the storage state and the code?
k.setSuicided(ctx, addr)
// Set a single byte to the transient store k.Logger(ctx).Debug(
store := prefix.NewStore(k.Ctx().TransientStore(k.transientKey), types.KeyPrefixTransientSuicided)
store.Set(addr.Bytes(), []byte{1})
k.Logger(k.Ctx()).Debug(
"account suicided", "account suicided",
"ethereum-address", addr.Hex(), "ethereum-address", addr.Hex(),
"cosmos-address", cosmosAddr.String(), "cosmos-address", cosmosAddr.String(),
@ -450,11 +482,19 @@ func (k *Keeper) Suicide(addr common.Address) bool {
return true return true
} }
// setSuicided sets a single byte to the transient store and marks the address as suicided
func (k Keeper) setSuicided(ctx sdk.Context, addr common.Address) {
store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientSuicided)
store.Set(addr.Bytes(), []byte{1})
}
// HasSuicided queries the transient store to check if the account has been marked as suicided in the // HasSuicided queries the transient store to check if the account has been marked as suicided in the
// current block. Accounts that are suicided will be returned as non-nil during queries and "cleared" // current block. Accounts that are suicided will be returned as non-nil during queries and "cleared"
// after the block has been committed. // after the block has been committed.
func (k *Keeper) HasSuicided(addr common.Address) bool { func (k *Keeper) HasSuicided(addr common.Address) bool {
store := prefix.NewStore(k.Ctx().TransientStore(k.transientKey), types.KeyPrefixTransientSuicided) ctx := k.Ctx()
store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientSuicided)
return store.Has(addr.Bytes()) return store.Has(addr.Bytes())
} }
@ -465,13 +505,14 @@ func (k *Keeper) HasSuicided(addr common.Address) bool {
// Exist returns true if the given account exists in store or if it has been // Exist returns true if the given account exists in store or if it has been
// marked as suicided in the transient store. // marked as suicided in the transient store.
func (k *Keeper) Exist(addr common.Address) bool { func (k *Keeper) Exist(addr common.Address) bool {
ctx := k.Ctx()
// return true if the account has suicided // return true if the account has suicided
if k.HasSuicided(addr) { if k.HasSuicided(addr) {
return true return true
} }
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
account := k.accountKeeper.GetAccount(k.Ctx(), cosmosAddr) account := k.accountKeeper.GetAccount(ctx, cosmosAddr)
return account != nil return account != nil
} }
@ -482,11 +523,12 @@ func (k *Keeper) Exist(addr common.Address) bool {
// //
// Non-ethereum accounts are considered not empty // Non-ethereum accounts are considered not empty
func (k *Keeper) Empty(addr common.Address) bool { func (k *Keeper) Empty(addr common.Address) bool {
ctx := k.Ctx()
nonce := uint64(0) nonce := uint64(0)
codeHash := types.EmptyCodeHash codeHash := types.EmptyCodeHash
cosmosAddr := sdk.AccAddress(addr.Bytes()) cosmosAddr := sdk.AccAddress(addr.Bytes())
account := k.accountKeeper.GetAccount(k.Ctx(), cosmosAddr) account := k.accountKeeper.GetAccount(ctx, cosmosAddr)
if account != nil { if account != nil {
nonce = account.GetSequence() nonce = account.GetSequence()
@ -537,7 +579,8 @@ func (k *Keeper) PrepareAccessList(sender common.Address, dest *common.Address,
// AddressInAccessList returns true if the address is registered on the transient store. // AddressInAccessList returns true if the address is registered on the transient store.
func (k *Keeper) AddressInAccessList(addr common.Address) bool { func (k *Keeper) AddressInAccessList(addr common.Address) bool {
ts := prefix.NewStore(k.Ctx().TransientStore(k.transientKey), types.KeyPrefixTransientAccessListAddress) ctx := k.Ctx()
ts := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListAddress)
return ts.Has(addr.Bytes()) return ts.Has(addr.Bytes())
} }
@ -550,7 +593,8 @@ func (k *Keeper) SlotInAccessList(addr common.Address, slot common.Hash) (addres
// addressSlotInAccessList returns true if the address's slot is registered on the transient store. // addressSlotInAccessList returns true if the address's slot is registered on the transient store.
func (k *Keeper) addressSlotInAccessList(addr common.Address, slot common.Hash) bool { func (k *Keeper) addressSlotInAccessList(addr common.Address, slot common.Hash) bool {
ts := prefix.NewStore(k.Ctx().TransientStore(k.transientKey), types.KeyPrefixTransientAccessListSlot) ctx := k.Ctx()
ts := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListSlot)
key := append(addr.Bytes(), slot.Bytes()...) key := append(addr.Bytes(), slot.Bytes()...)
return ts.Has(key) return ts.Has(key)
} }
@ -562,7 +606,8 @@ func (k *Keeper) AddAddressToAccessList(addr common.Address) {
return return
} }
ts := prefix.NewStore(k.Ctx().TransientStore(k.transientKey), types.KeyPrefixTransientAccessListAddress) ctx := k.Ctx()
ts := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListAddress)
ts.Set(addr.Bytes(), []byte{0x1}) ts.Set(addr.Bytes(), []byte{0x1})
} }
@ -574,7 +619,8 @@ func (k *Keeper) AddSlotToAccessList(addr common.Address, slot common.Hash) {
return return
} }
ts := prefix.NewStore(k.Ctx().TransientStore(k.transientKey), types.KeyPrefixTransientAccessListSlot) ctx := k.Ctx()
ts := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientAccessListSlot)
key := append(addr.Bytes(), slot.Bytes()...) key := append(addr.Bytes(), slot.Bytes()...)
ts.Set(key, []byte{0x1}) ts.Set(key, []byte{0x1})
} }
@ -601,7 +647,9 @@ func (k *Keeper) RevertToSnapshot(target int) {
// context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log // context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log
// to store. // to store.
func (k *Keeper) AddLog(log *ethtypes.Log) { func (k *Keeper) AddLog(log *ethtypes.Log) {
log.BlockHash = common.BytesToHash(k.Ctx().HeaderHash()) ctx := k.Ctx()
log.BlockHash = common.BytesToHash(ctx.HeaderHash())
log.TxIndex = uint(k.GetTxIndexTransient()) log.TxIndex = uint(k.GetTxIndexTransient())
log.TxHash = k.GetTxHashTransient() log.TxHash = k.GetTxHashTransient()
@ -609,7 +657,7 @@ func (k *Keeper) AddLog(log *ethtypes.Log) {
k.IncreaseLogSizeTransient() k.IncreaseLogSizeTransient()
k.SetLog(log) k.SetLog(log)
k.Logger(k.Ctx()).Debug( k.Logger(ctx).Debug(
"log added", "log added",
"tx-hash-ethereum", log.TxHash.Hex(), "tx-hash-ethereum", log.TxHash.Hex(),
"log-index", int(log.Index), "log-index", int(log.Index),
@ -632,7 +680,8 @@ func (k *Keeper) AddPreimage(_ common.Hash, _ []byte) {}
// ForEachStorage uses the store iterator to iterate over all the state keys and perform a callback // ForEachStorage uses the store iterator to iterate over all the state keys and perform a callback
// function on each of them. // function on each of them.
func (k *Keeper) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { func (k *Keeper) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
store := k.Ctx().KVStore(k.storeKey) ctx := k.Ctx()
store := ctx.KVStore(k.storeKey)
prefix := types.AddressStoragePrefix(addr) prefix := types.AddressStoragePrefix(addr)
iterator := sdk.KVStorePrefixIterator(store, prefix) iterator := sdk.KVStorePrefixIterator(store, prefix)

View File

@ -0,0 +1,128 @@
package keeper_test
import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/tharsis/ethermint/tests"
)
func BenchmarkCreateAccountNew(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
b.StopTimer()
addr := tests.GenerateAddress()
b.StartTimer()
suite.app.EvmKeeper.CreateAccount(addr)
}
}
func BenchmarkCreateAccountExisting(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
suite.app.EvmKeeper.CreateAccount(suite.address)
}
}
func BenchmarkAddBalance(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
amt := big.NewInt(10)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
suite.app.EvmKeeper.AddBalance(suite.address, amt)
}
}
func BenchmarkSetCode(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
hash := crypto.Keccak256Hash([]byte("code")).Bytes()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
suite.app.EvmKeeper.SetCode(suite.address, hash)
}
}
func BenchmarkSetState(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
hash := crypto.Keccak256Hash([]byte("topic")).Bytes()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
suite.app.EvmKeeper.SetCode(suite.address, hash)
}
}
func BenchmarkAddLog(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
topic := crypto.Keccak256Hash([]byte("topic"))
txHash := crypto.Keccak256Hash([]byte("tx_hash"))
blockHash := crypto.Keccak256Hash([]byte("block_hash"))
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
suite.app.EvmKeeper.AddLog(&ethtypes.Log{
Address: suite.address,
Topics: []common.Hash{topic},
Data: []byte("data"),
BlockNumber: 1,
TxHash: txHash,
TxIndex: 1,
BlockHash: blockHash,
Index: 1,
Removed: false,
})
}
}
func BenchmarkSnapshot(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
target := suite.app.EvmKeeper.Snapshot()
require.Equal(b, i, target)
}
for i := b.N - 1; i >= 0; i-- {
require.NotPanics(b, func() {
suite.app.EvmKeeper.RevertToSnapshot(i)
})
}
}