laconicd/x/evm/keeper/keeper.go

367 lines
11 KiB
Go
Raw Normal View History

package keeper
import (
"math/big"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
2021-04-17 10:00:07 +00:00
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
2021-04-18 15:54:18 +00:00
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/ethermint/metrics"
"github.com/cosmos/ethermint/x/evm/types"
)
// Keeper wraps the CommitStateDB, allowing us to pass in SDK context while adhering
// to the StateDB interface.
type Keeper struct {
2021-04-17 10:00:07 +00:00
// Protobuf codec
cdc codec.BinaryMarshaler
// Store key required for the EVM Prefix KVStore. It is required by:
// - storing Account's Storage State
// - storing Account's Code
// - storing transaction Logs
// - storing block height -> bloom filter map. Needed for the Web3 API.
2021-04-18 15:54:18 +00:00
// - storing block hash -> block height map. Needed for the Web3 API. TODO: remove
storeKey sdk.StoreKey
2021-04-18 15:54:18 +00:00
accountKeeper types.AccountKeeper
2021-04-17 10:00:07 +00:00
bankKeeper types.BankKeeper
2021-04-18 15:54:18 +00:00
// Ethermint concrete implementation on the EVM StateDB interface
CommitStateDB *types.CommitStateDB
// Transaction counter in a block. Used on StateSB's Prepare function.
// It is reset to 0 every block on BeginBlock so there's no point in storing the counter
// on the KVStore or adding it as a field on the EVM genesis state.
TxCount int
Bloom *big.Int
2021-04-18 15:54:18 +00:00
// LogsCache keeps mapping of contract address -> eth logs emitted
// during EVM execution in the current block.
LogsCache map[common.Address][]*ethtypes.Log
svcTags metrics.Tags
}
// NewKeeper generates new evm module keeper
func NewKeeper(
2021-04-17 10:00:07 +00:00
cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, paramSpace paramtypes.Subspace,
ak types.AccountKeeper, bankKeeper types.BankKeeper,
) *Keeper {
// 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
return &Keeper{
2021-04-18 15:54:18 +00:00
svcTags: metrics.Tags{
"svc": "evm_k",
},
cdc: cdc,
accountKeeper: ak,
2021-04-17 10:00:07 +00:00
bankKeeper: bankKeeper,
storeKey: storeKey,
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak, bankKeeper),
TxCount: 0,
Bloom: big.NewInt(0),
2021-04-18 15:54:18 +00:00
LogsCache: map[common.Address][]*ethtypes.Log{},
}
}
// 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)
}
// ----------------------------------------------------------------------------
2021-04-18 15:54:18 +00:00
// Block bloom bits mapping functions
// Required by Web3 API.
// ----------------------------------------------------------------------------
2021-04-18 15:54:18 +00:00
// GetBlockBloom gets bloombits from block height
func (k Keeper) GetBlockBloom(ctx sdk.Context, height int64) (ethtypes.Bloom, bool) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
key := types.BloomKey(height)
has := store.Has(key)
if !has {
return ethtypes.Bloom{}, true // sometimes bloom not found, fix this
}
bz := store.Get(key)
return ethtypes.BytesToBloom(bz), true
}
// SetBlockBloom sets the mapping from block height to bloom bits
func (k Keeper) SetBlockBloom(ctx sdk.Context, height int64, bloom ethtypes.Bloom) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
key := types.BloomKey(height)
store.Set(key, bloom.Bytes())
}
// GetBlockHash gets block height from block consensus hash
func (k Keeper) GetBlockHashFromHeight(ctx sdk.Context, height int64) ([]byte, bool) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyBlockHeightHash(uint64(height)))
if len(bz) == 0 {
return common.Hash{}.Bytes(), false
}
return common.BytesToHash(bz).Bytes(), true
}
// SetBlockHash sets the mapping from block consensus hash to block height
func (k Keeper) SetBlockHash(ctx sdk.Context, hash []byte, height int64) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
bz := sdk.Uint64ToBigEndian(uint64(height))
store.Set(types.KeyBlockHash(common.BytesToHash(hash)), bz)
}
// GetBlockHash gets block height from block consensus hash
func (k Keeper) GetBlockHeightByHash(ctx sdk.Context, hash common.Hash) (int64, bool) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyBlockHash(hash))
if len(bz) == 0 {
return 0, false
}
height := sdk.BigEndianToUint64(bz)
return int64(height), true
}
// SetBlockHash sets the mapping from block consensus hash to block height
func (k Keeper) SetBlockHeightToHash(ctx sdk.Context, hash []byte, height int64) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
store.Set(types.KeyBlockHeightHash(uint64(height)), hash)
}
// SetTxReceiptToHash sets the mapping from tx hash to tx receipt
func (k Keeper) SetTxReceiptToHash(ctx sdk.Context, hash common.Hash, receipt *types.TxReceipt) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
data := k.cdc.MustMarshalBinaryBare(receipt)
store := ctx.KVStore(k.storeKey)
store.Set(types.KeyHashTxReceipt(hash), data)
}
// GetHeightHash returns the block header hash associated with a given block height and chain epoch number.
func (k Keeper) GetHeightHash(ctx sdk.Context, height uint64) common.Hash {
return k.CommitStateDB.WithContext(ctx).GetHeightHash(height)
}
// SetHeightHash sets the block header hash associated with a given height.
func (k Keeper) SetHeightHash(ctx sdk.Context, height uint64, hash common.Hash) {
k.CommitStateDB.WithContext(ctx).SetHeightHash(height, hash)
}
2021-04-18 15:54:18 +00:00
// GetTxReceiptFromHash gets tx receipt by tx hash.
func (k Keeper) GetTxReceiptFromHash(ctx sdk.Context, hash common.Hash) (*types.TxReceipt, bool) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
2021-04-18 15:54:18 +00:00
store := ctx.KVStore(k.storeKey)
data := store.Get(types.KeyHashTxReceipt(hash))
if data == nil || len(data) == 0 {
return nil, false
}
2021-04-18 15:54:18 +00:00
var receipt types.TxReceipt
k.cdc.MustUnmarshalBinaryBare(data, &receipt)
return &receipt, true
}
2021-04-18 15:54:18 +00:00
// AddTxHashToBlock stores tx hash in the list of tx for the block.
func (k Keeper) AddTxHashToBlock(ctx sdk.Context, blockHeight int64, txHash common.Hash) {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
key := types.KeyBlockHeightTxs(uint64(blockHeight))
list := types.BytesList{}
store := ctx.KVStore(k.storeKey)
data := store.Get(key)
if len(data) > 0 {
k.cdc.MustUnmarshalBinaryBare(data, &list)
}
list.Bytes = append(list.Bytes, txHash.Bytes())
data = k.cdc.MustMarshalBinaryBare(&list)
store.Set(key, data)
}
// GetTxsFromBlock returns list of tx hash in the block by height.
func (k Keeper) GetTxsFromBlock(ctx sdk.Context, blockHeight int64) []common.Hash {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
key := types.KeyBlockHeightTxs(uint64(blockHeight))
store := ctx.KVStore(k.storeKey)
data := store.Get(key)
if len(data) > 0 {
list := types.BytesList{}
k.cdc.MustUnmarshalBinaryBare(data, &list)
txs := make([]common.Hash, 0, len(list.Bytes))
for _, b := range list.Bytes {
txs = append(txs, common.BytesToHash(b))
}
return txs
}
return nil
}
// GetTxReceiptsByBlockHeight gets tx receipts by block height.
func (k Keeper) GetTxReceiptsByBlockHeight(ctx sdk.Context, blockHeight int64) []*types.TxReceipt {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
txs := k.GetTxsFromBlock(ctx, blockHeight)
if len(txs) == 0 {
return nil
}
store := ctx.KVStore(k.storeKey)
receipts := make([]*types.TxReceipt, 0, len(txs))
for idx, txHash := range txs {
data := store.Get(types.KeyHashTxReceipt(txHash))
if data == nil || len(data) == 0 {
continue
}
var receipt types.TxReceipt
k.cdc.MustUnmarshalBinaryBare(data, &receipt)
receipt.Index = uint64(idx)
receipts = append(receipts, &receipt)
}
return receipts
}
// GetTxReceiptsByBlockHash gets tx receipts by block hash.
func (k Keeper) GetTxReceiptsByBlockHash(ctx sdk.Context, hash common.Hash) []*types.TxReceipt {
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
blockHeight, ok := k.GetBlockHeightByHash(ctx, hash)
if !ok {
return nil
}
return k.GetTxReceiptsByBlockHeight(ctx, blockHeight)
}
// GetAllTxLogs return all the transaction logs from the store.
func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
2021-04-18 15:54:18 +00:00
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.KeyPrefixLogs)
defer iterator.Close()
txsLogs := []types.TransactionLogs{}
for ; iterator.Valid(); iterator.Next() {
2021-04-17 10:00:07 +00:00
var txLog types.TransactionLogs
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &txLog)
// add a new entry
txsLogs = append(txsLogs, txLog)
}
return txsLogs
}
// GetAccountStorage return state storage associated with an account
func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) {
2021-04-18 15:54:18 +00:00
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
storage := types.Storage{}
err := k.ForEachStorage(ctx, address, func(key, value common.Hash) bool {
storage = append(storage, types.NewState(key, value))
return false
})
if err != nil {
return types.Storage{}, err
}
return storage, nil
}
// GetChainConfig gets block height from block consensus hash
func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
2021-04-18 15:54:18 +00:00
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyPrefixChainConfig)
if len(bz) == 0 {
return types.ChainConfig{}, false
}
var config types.ChainConfig
k.cdc.MustUnmarshalBinaryBare(bz, &config)
return config, true
}
// SetChainConfig sets the mapping from block consensus hash to block height
func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) {
2021-04-18 15:54:18 +00:00
metrics.ReportFuncCall(k.svcTags)
doneFn := metrics.ReportFuncTiming(k.svcTags)
defer doneFn()
store := ctx.KVStore(k.storeKey)
2021-04-17 10:00:07 +00:00
bz := k.cdc.MustMarshalBinaryBare(&config)
store.Set(types.KeyPrefixChainConfig, bz)
}