2020-03-09 13:17:23 +00:00
|
|
|
package keeper
|
2019-07-24 22:14:12 +00:00
|
|
|
|
|
|
|
import (
|
2020-06-04 10:40:21 +00:00
|
|
|
"math/big"
|
2019-09-27 14:08:45 +00:00
|
|
|
|
2019-07-25 20:38:55 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
2021-05-25 12:56:36 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
2019-07-24 22:14:12 +00:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2022-01-14 09:37:33 +00:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2021-04-17 10:00:07 +00:00
|
|
|
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
2020-08-23 21:41:54 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2021-10-22 17:21:03 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core"
|
2019-07-24 22:14:12 +00:00
|
|
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
2021-10-22 17:21:03 +00:00
|
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
2022-01-05 07:28:27 +00:00
|
|
|
"github.com/ethereum/go-ethereum/params"
|
2021-04-18 15:54:18 +00:00
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
|
|
|
2021-06-22 10:49:18 +00:00
|
|
|
ethermint "github.com/tharsis/ethermint/types"
|
2022-01-05 07:28:27 +00:00
|
|
|
"github.com/tharsis/ethermint/x/evm/statedb"
|
2021-06-22 10:49:18 +00:00
|
|
|
"github.com/tharsis/ethermint/x/evm/types"
|
2019-07-24 22:14:12 +00:00
|
|
|
)
|
|
|
|
|
2021-08-05 16:24:06 +00:00
|
|
|
// Keeper grants access to the EVM module state and implements the go-ethereum StateDB interface.
|
2019-07-24 22:14:12 +00:00
|
|
|
type Keeper struct {
|
2021-04-17 10:00:07 +00:00
|
|
|
// Protobuf codec
|
2021-06-30 09:35:11 +00:00
|
|
|
cdc codec.BinaryCodec
|
2020-06-04 10:40:21 +00:00
|
|
|
// Store key required for the EVM Prefix KVStore. It is required by:
|
2021-08-05 16:24:06 +00:00
|
|
|
// - storing account's Storage State
|
|
|
|
// - storing account's Code
|
2020-06-04 10:40:21 +00:00
|
|
|
// - storing transaction Logs
|
2021-08-05 16:24:06 +00:00
|
|
|
// - storing Bloom filters by block height. Needed for the Web3 API.
|
2020-09-02 19:41:05 +00:00
|
|
|
storeKey sdk.StoreKey
|
2021-04-18 15:54:18 +00:00
|
|
|
|
2021-05-25 12:56:36 +00:00
|
|
|
// key to access the transient store, which is reset on every block during Commit
|
|
|
|
transientKey sdk.StoreKey
|
|
|
|
|
2021-08-05 16:24:06 +00:00
|
|
|
// module specific parameter space that can be configured through governance
|
|
|
|
paramSpace paramtypes.Subspace
|
|
|
|
// access to account state
|
2020-12-15 18:43:06 +00:00
|
|
|
accountKeeper types.AccountKeeper
|
2021-08-05 16:24:06 +00:00
|
|
|
// update balance and accounting operations with coins
|
|
|
|
bankKeeper types.BankKeeper
|
|
|
|
// access historical headers for EVM state transition execution
|
2021-06-07 11:00:14 +00:00
|
|
|
stakingKeeper types.StakingKeeper
|
2021-10-04 14:58:06 +00:00
|
|
|
// fetch EIP1559 base fee and parameters
|
|
|
|
feeMarketKeeper types.FeeMarketKeeper
|
2021-04-18 15:54:18 +00:00
|
|
|
|
2021-05-31 09:05:32 +00:00
|
|
|
// chain ID number obtained from the context's chain id
|
|
|
|
eip155ChainID *big.Int
|
2021-08-16 09:45:10 +00:00
|
|
|
|
|
|
|
// Tracer used to collect execution traces from the EVM transaction execution
|
|
|
|
tracer string
|
2021-09-02 12:36:33 +00:00
|
|
|
|
|
|
|
// EVM Hooks for tx post-processing
|
|
|
|
hooks types.EvmHooks
|
2019-10-03 16:46:02 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 14:08:45 +00:00
|
|
|
// NewKeeper generates new evm module keeper
|
2020-04-01 18:49:21 +00:00
|
|
|
func NewKeeper(
|
2021-06-30 09:35:11 +00:00
|
|
|
cdc codec.BinaryCodec,
|
2021-06-29 17:02:21 +00:00
|
|
|
storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace,
|
2021-06-07 11:00:14 +00:00
|
|
|
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
|
2021-10-04 14:58:06 +00:00
|
|
|
fmk types.FeeMarketKeeper,
|
2021-11-16 08:57:03 +00:00
|
|
|
tracer string,
|
2021-01-07 11:55:01 +00:00
|
|
|
) *Keeper {
|
2021-06-29 17:02:21 +00:00
|
|
|
// ensure evm module account is set
|
|
|
|
if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
|
|
|
|
panic("the EVM module account has not been set")
|
|
|
|
}
|
|
|
|
|
2020-09-02 19:41:05 +00:00
|
|
|
// set KeyTable if it has not already been set
|
|
|
|
if !paramSpace.HasKeyTable() {
|
|
|
|
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
|
2021-01-07 11:55:01 +00:00
|
|
|
return &Keeper{
|
2021-10-04 14:58:06 +00:00
|
|
|
cdc: cdc,
|
|
|
|
paramSpace: paramSpace,
|
|
|
|
accountKeeper: ak,
|
|
|
|
bankKeeper: bankKeeper,
|
|
|
|
stakingKeeper: sk,
|
|
|
|
feeMarketKeeper: fmk,
|
|
|
|
storeKey: storeKey,
|
|
|
|
transientKey: transientKey,
|
|
|
|
tracer: tracer,
|
2019-09-27 14:08:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 03:36:30 +00:00
|
|
|
// Logger returns a module-specific logger.
|
|
|
|
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
2021-04-18 15:54:18 +00:00
|
|
|
return ctx.Logger().With("module", types.ModuleName)
|
2020-04-30 03:36:30 +00:00
|
|
|
}
|
|
|
|
|
2021-05-31 09:05:32 +00:00
|
|
|
// WithChainID sets the chain id to the local variable in the keeper
|
|
|
|
func (k *Keeper) WithChainID(ctx sdk.Context) {
|
|
|
|
chainID, err := ethermint.ParseChainID(ctx.ChainID())
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if k.eip155ChainID != nil && k.eip155ChainID.Cmp(chainID) != 0 {
|
|
|
|
panic("chain id already set")
|
|
|
|
}
|
|
|
|
|
|
|
|
k.eip155ChainID = chainID
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChainID returns the EIP155 chain ID for the EVM context
|
|
|
|
func (k Keeper) ChainID() *big.Int {
|
|
|
|
return k.eip155ChainID
|
|
|
|
}
|
|
|
|
|
2020-12-07 20:09:09 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
2021-05-25 12:56:36 +00:00
|
|
|
// Block Bloom
|
2021-04-18 15:54:18 +00:00
|
|
|
// Required by Web3 API.
|
2020-12-07 20:09:09 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2021-09-15 09:45:03 +00:00
|
|
|
// EmitBlockBloomEvent emit block bloom events
|
|
|
|
func (k Keeper) EmitBlockBloomEvent(ctx sdk.Context, bloom ethtypes.Bloom) {
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeBlockBloom,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyEthereumBloom, string(bloom.Bytes())),
|
|
|
|
),
|
|
|
|
)
|
2021-04-18 15:54:18 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 12:56:36 +00:00
|
|
|
// GetBlockBloomTransient returns bloom bytes for the current block height
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) GetBlockBloomTransient(ctx sdk.Context) *big.Int {
|
|
|
|
store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientBloom)
|
|
|
|
heightBz := sdk.Uint64ToBigEndian(uint64(ctx.BlockHeight()))
|
2021-07-12 10:25:15 +00:00
|
|
|
bz := store.Get(heightBz)
|
2021-05-25 12:56:36 +00:00
|
|
|
if len(bz) == 0 {
|
2021-06-30 07:37:03 +00:00
|
|
|
return big.NewInt(0)
|
2021-05-25 12:56:36 +00:00
|
|
|
}
|
|
|
|
|
2021-06-30 07:37:03 +00:00
|
|
|
return new(big.Int).SetBytes(bz)
|
2021-05-25 12:56:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetBlockBloomTransient sets the given bloom bytes to the transient store. This value is reset on
|
|
|
|
// every block.
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) SetBlockBloomTransient(ctx sdk.Context, bloom *big.Int) {
|
|
|
|
store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientBloom)
|
|
|
|
heightBz := sdk.Uint64ToBigEndian(uint64(ctx.BlockHeight()))
|
2021-07-12 10:25:15 +00:00
|
|
|
store.Set(heightBz, bloom.Bytes())
|
2021-05-25 12:56:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Tx
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2021-09-04 20:33:06 +00:00
|
|
|
// SetTxIndexTransient set the index of processing transaction
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) SetTxIndexTransient(ctx sdk.Context, index uint64) {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
2021-09-04 20:33:06 +00:00
|
|
|
store.Set(types.KeyPrefixTransientTxIndex, sdk.Uint64ToBigEndian(index))
|
|
|
|
}
|
|
|
|
|
2021-05-25 12:56:36 +00:00
|
|
|
// GetTxIndexTransient returns EVM transaction index on the current block.
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) GetTxIndexTransient(ctx sdk.Context) uint64 {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
2021-06-30 09:35:11 +00:00
|
|
|
bz := store.Get(types.KeyPrefixTransientTxIndex)
|
2021-05-25 12:56:36 +00:00
|
|
|
if len(bz) == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return sdk.BigEndianToUint64(bz)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Log
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2021-07-26 08:40:59 +00:00
|
|
|
// GetLogSizeTransient returns EVM log index on the current block.
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) GetLogSizeTransient(ctx sdk.Context) uint64 {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
2021-07-26 08:40:59 +00:00
|
|
|
bz := store.Get(types.KeyPrefixTransientLogSize)
|
|
|
|
if len(bz) == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return sdk.BigEndianToUint64(bz)
|
|
|
|
}
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// SetLogSizeTransient fetches the current EVM log index from the transient store, increases its
|
2021-07-26 08:40:59 +00:00
|
|
|
// value by one and then sets the new index back to the transient store.
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) SetLogSizeTransient(ctx sdk.Context, logSize uint64) {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
|
|
|
store.Set(types.KeyPrefixTransientLogSize, sdk.Uint64ToBigEndian(logSize))
|
2021-07-26 08:40:59 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 12:56:36 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Storage
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2020-08-23 21:41:54 +00:00
|
|
|
// GetAccountStorage return state storage associated with an account
|
2022-01-05 07:28:27 +00:00
|
|
|
func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) types.Storage {
|
2020-08-23 21:41:54 +00:00
|
|
|
storage := types.Storage{}
|
2021-01-06 20:56:40 +00:00
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
k.ForEachStorage(ctx, address, func(key, value common.Hash) bool {
|
2020-08-23 21:41:54 +00:00
|
|
|
storage = append(storage, types.NewState(key, value))
|
2021-11-30 10:34:33 +00:00
|
|
|
return true
|
2020-08-23 21:41:54 +00:00
|
|
|
})
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
return storage
|
2020-08-23 21:41:54 +00:00
|
|
|
}
|
2020-09-02 19:41:05 +00:00
|
|
|
|
2021-05-25 12:56:36 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Account
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// SetHooks sets the hooks for the EVM module
|
|
|
|
// It should be called only once during initialization, it panic if called more than once.
|
|
|
|
func (k *Keeper) SetHooks(eh types.EvmHooks) *Keeper {
|
|
|
|
if k.hooks != nil {
|
|
|
|
panic("cannot set evm hooks twice")
|
|
|
|
}
|
2021-05-25 12:56:36 +00:00
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
k.hooks = eh
|
|
|
|
return k
|
2021-05-25 12:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// PostTxProcessing delegate the call to the hooks. If no hook has been registered, this function returns with a `nil` error
|
2022-04-04 18:11:46 +00:00
|
|
|
func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
|
2022-01-05 07:28:27 +00:00
|
|
|
if k.hooks == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2022-04-04 18:11:46 +00:00
|
|
|
return k.hooks.PostTxProcessing(ctx, msg, receipt)
|
2020-09-02 19:41:05 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// Tracer return a default vm.Tracer based on current keeper state
|
2022-02-14 23:08:41 +00:00
|
|
|
func (k Keeper) Tracer(ctx sdk.Context, msg core.Message, ethCfg *params.ChainConfig) vm.EVMLogger {
|
2022-01-05 07:28:27 +00:00
|
|
|
return types.NewTracer(k.tracer, msg, ethCfg, ctx.BlockHeight())
|
|
|
|
}
|
2021-05-25 12:56:36 +00:00
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// GetAccountWithoutBalance load nonce and codehash without balance,
|
|
|
|
// more efficient in cases where balance is not needed.
|
2022-01-05 18:18:02 +00:00
|
|
|
func (k *Keeper) GetAccountWithoutBalance(ctx sdk.Context, addr common.Address) *statedb.Account {
|
2022-01-05 07:28:27 +00:00
|
|
|
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
|
|
|
acct := k.accountKeeper.GetAccount(ctx, cosmosAddr)
|
|
|
|
if acct == nil {
|
2022-01-05 18:18:02 +00:00
|
|
|
return nil
|
2022-01-05 07:28:27 +00:00
|
|
|
}
|
2021-06-29 17:02:21 +00:00
|
|
|
|
2022-01-05 18:18:02 +00:00
|
|
|
codeHash := types.EmptyCodeHash
|
|
|
|
ethAcct, ok := acct.(ethermint.EthAccountI)
|
|
|
|
if ok {
|
|
|
|
codeHash = ethAcct.GetCodeHash().Bytes()
|
2021-05-25 12:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
return &statedb.Account{
|
2022-01-05 18:18:02 +00:00
|
|
|
Nonce: acct.GetSequence(),
|
|
|
|
CodeHash: codeHash,
|
|
|
|
}
|
2021-05-25 12:56:36 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// GetAccountOrEmpty returns empty account if not exist, returns error if it's not `EthAccount`
|
2022-01-05 18:18:02 +00:00
|
|
|
func (k *Keeper) GetAccountOrEmpty(ctx sdk.Context, addr common.Address) statedb.Account {
|
|
|
|
acct := k.GetAccount(ctx, addr)
|
|
|
|
if acct != nil {
|
|
|
|
return *acct
|
2022-01-05 07:28:27 +00:00
|
|
|
}
|
2022-01-05 18:18:02 +00:00
|
|
|
|
|
|
|
// empty account
|
|
|
|
return statedb.Account{
|
|
|
|
Balance: new(big.Int),
|
|
|
|
CodeHash: types.EmptyCodeHash,
|
2022-01-05 07:28:27 +00:00
|
|
|
}
|
2020-09-02 19:41:05 +00:00
|
|
|
}
|
2021-09-02 12:36:33 +00:00
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// GetNonce returns the sequence number of an account, returns 0 if not exists.
|
|
|
|
func (k *Keeper) GetNonce(ctx sdk.Context, addr common.Address) uint64 {
|
|
|
|
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
|
|
|
acct := k.accountKeeper.GetAccount(ctx, cosmosAddr)
|
|
|
|
if acct == nil {
|
|
|
|
return 0
|
2021-09-02 12:36:33 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 18:18:02 +00:00
|
|
|
return acct.GetSequence()
|
2021-09-02 12:36:33 +00:00
|
|
|
}
|
2021-10-22 17:21:03 +00:00
|
|
|
|
2022-01-05 07:28:27 +00:00
|
|
|
// GetBalance load account's balance of gas token
|
|
|
|
func (k *Keeper) GetBalance(ctx sdk.Context, addr common.Address) *big.Int {
|
|
|
|
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
|
|
|
params := k.GetParams(ctx)
|
|
|
|
coin := k.bankKeeper.GetBalance(ctx, cosmosAddr, params.EvmDenom)
|
|
|
|
return coin.Amount.BigInt()
|
2021-10-22 17:21:03 +00:00
|
|
|
}
|
2021-12-28 07:59:28 +00:00
|
|
|
|
|
|
|
// BaseFee returns current base fee, return values:
|
|
|
|
// - `nil`: london hardfork not enabled.
|
|
|
|
// - `0`: london hardfork enabled but feemarket is not enabled.
|
|
|
|
// - `n`: both london hardfork and feemarket are enabled.
|
2022-04-30 16:11:28 +00:00
|
|
|
func (k Keeper) GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
2021-12-28 07:59:28 +00:00
|
|
|
if !types.IsLondon(ethCfg, ctx.BlockHeight()) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
|
|
|
if baseFee == nil {
|
|
|
|
// return 0 if feemarket not enabled.
|
|
|
|
baseFee = big.NewInt(0)
|
|
|
|
}
|
|
|
|
return baseFee
|
|
|
|
}
|
2022-01-14 09:37:33 +00:00
|
|
|
|
|
|
|
// ResetTransientGasUsed reset gas used to prepare for execution of current cosmos tx, called in ante handler.
|
|
|
|
func (k Keeper) ResetTransientGasUsed(ctx sdk.Context) {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
|
|
|
store.Delete(types.KeyPrefixTransientGasUsed)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTransientGasUsed returns the gas used by current cosmos tx.
|
|
|
|
func (k Keeper) GetTransientGasUsed(ctx sdk.Context) uint64 {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
|
|
|
bz := store.Get(types.KeyPrefixTransientGasUsed)
|
|
|
|
if len(bz) == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return sdk.BigEndianToUint64(bz)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetTransientGasUsed sets the gas used by current cosmos tx.
|
|
|
|
func (k Keeper) SetTransientGasUsed(ctx sdk.Context, gasUsed uint64) {
|
|
|
|
store := ctx.TransientStore(k.transientKey)
|
|
|
|
bz := sdk.Uint64ToBigEndian(gasUsed)
|
|
|
|
store.Set(types.KeyPrefixTransientGasUsed, bz)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddTransientGasUsed accumulate gas used by each eth msgs included in current cosmos tx.
|
|
|
|
func (k Keeper) AddTransientGasUsed(ctx sdk.Context, gasUsed uint64) (uint64, error) {
|
|
|
|
result := k.GetTransientGasUsed(ctx) + gasUsed
|
|
|
|
if result < gasUsed {
|
|
|
|
return 0, sdkerrors.Wrap(types.ErrGasOverflow, "transient gas used")
|
|
|
|
}
|
|
|
|
k.SetTransientGasUsed(ctx, result)
|
|
|
|
return result, nil
|
|
|
|
}
|