evm: error and block hash map updates (#60)

* evm: error and block hash map updates

* evm: update tests
This commit is contained in:
Federico Kunze
2021-05-31 10:54:59 -04:00
committed by GitHub
parent 9a5654f70d
commit abcfc9a6ba
22 changed files with 395 additions and 864 deletions
+6 -5
View File
@@ -19,6 +19,7 @@ import (
// and resets the Bloom filter and the transaction count to 0.
func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)
k.WithContext(ctx)
if req.Header.Height < 1 {
return
@@ -27,12 +28,12 @@ func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
// Gas costs are handled within msg handler so costs should be ignored
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
// TODO: why do we have so many hash -> height mappings
k.SetBlockHash(ctx, req.Hash, req.Header.Height)
k.SetBlockHeightToHash(ctx, common.BytesToHash(req.Hash), req.Header.Height)
k.headerHash = common.BytesToHash(req.Hash)
// special setter for csdb
k.SetHeightHash(ctx, uint64(req.Header.Height), common.BytesToHash(req.Hash))
// set height -> hash
// TODO: prune
k.SetHeaderHash(ctx, req.Header.Height, k.headerHash)
}
// EndBlock updates the accounts and commits state objects to the KV Store, while
+40 -27
View File
@@ -9,7 +9,9 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
ethcmn "github.com/ethereum/go-ethereum/common"
@@ -120,6 +122,13 @@ func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ
)
}
if ethermint.IsEmptyHash(req.Key) {
return nil, status.Errorf(
codes.InvalidArgument,
types.ErrEmptyHash.Error(),
)
}
ctx := sdk.UnwrapSDKContext(c)
k.CommitStateDB.WithContext(ctx)
@@ -127,9 +136,16 @@ func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ
key := ethcmn.HexToHash(req.Key)
state := k.CommitStateDB.GetState(address, key)
stateHex := state.Hex()
if ethermint.IsEmptyHash(stateHex) {
return nil, status.Error(
codes.NotFound, "contract code not found for given address",
)
}
return &types.QueryStorageResponse{
Value: state.String(),
Value: stateHex,
}, nil
}
@@ -219,35 +235,12 @@ func (k Keeper) TxReceipt(c context.Context, req *types.QueryTxReceiptRequest) (
func (k Keeper) TxReceiptsByBlockHeight(c context.Context, _ *types.QueryTxReceiptsByBlockHeightRequest) (*types.QueryTxReceiptsByBlockHeightResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
receipts := k.GetTxReceiptsByBlockHeight(ctx, ctx.BlockHeight())
receipts := k.GetTxReceiptsByBlockHeight(ctx, uint64(ctx.BlockHeight()))
return &types.QueryTxReceiptsByBlockHeightResponse{
Receipts: receipts,
}, nil
}
// TxReceiptsByBlockHash implements the Query/TxReceiptsByBlockHash gRPC method
func (k Keeper) TxReceiptsByBlockHash(c context.Context, req *types.QueryTxReceiptsByBlockHashRequest) (*types.QueryTxReceiptsByBlockHashResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if ethermint.IsEmptyHash(req.Hash) {
return nil, status.Error(
codes.InvalidArgument,
types.ErrEmptyHash.Error(),
)
}
ctx := sdk.UnwrapSDKContext(c)
hash := ethcmn.HexToHash(req.Hash)
receipts := k.GetTxReceiptsByBlockHash(ctx, hash)
return &types.QueryTxReceiptsByBlockHashResponse{
Receipts: receipts,
}, nil
}
// BlockLogs implements the Query/BlockLogs gRPC method
func (k Keeper) BlockLogs(c context.Context, req *types.QueryBlockLogsRequest) (*types.QueryBlockLogsResponse, error) {
if req == nil {
@@ -263,10 +256,30 @@ func (k Keeper) BlockLogs(c context.Context, req *types.QueryBlockLogsRequest) (
ctx := sdk.UnwrapSDKContext(c)
txLogs := k.GetAllTxLogs(ctx)
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixLogs)
txLogs := []types.TransactionLogs{}
pageRes, err := query.FilteredPaginate(store, req.Pagination, func(_, value []byte, accumulate bool) (bool, error) {
var txLog types.TransactionLogs
k.cdc.MustUnmarshalBinaryBare(value, &txLog)
if len(txLog.Logs) > 0 && txLog.Logs[0].BlockHash == req.Hash {
if accumulate {
txLogs = append(txLogs, txLog)
}
return true, nil
}
return false, nil
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &types.QueryBlockLogsResponse{
TxLogs: txLogs,
TxLogs: txLogs,
Pagination: pageRes,
}, nil
}
+1 -71
View File
@@ -236,10 +236,8 @@ func (suite *KeeperTestSuite) TestQueryStorage() {
Address: suite.address.String(),
Key: ethcmn.Hash{}.String(),
}
exp := &types.QueryStorageResponse{Value: "0x0000000000000000000000000000000000000000000000000000000000000000"}
expValue = exp.Value
},
true,
false,
},
{
"success",
@@ -649,74 +647,6 @@ func (suite *KeeperTestSuite) TestQueryTxReceiptByBlockHeight() {
}
}
func (suite *KeeperTestSuite) TestQueryTxReceiptByBlockHash() {
var (
req *types.QueryTxReceiptsByBlockHashRequest
expRes *types.QueryTxReceiptsByBlockHashResponse
)
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{"empty block hash",
func() {
req = &types.QueryTxReceiptsByBlockHashRequest{
Hash: "",
}
},
false,
},
{"success",
func() {
hash := ethcmn.BytesToHash([]byte("thash"))
blockHash := ethcmn.BytesToHash([]byte("bhash"))
receipt := &types.TxReceipt{
Hash: hash.Hex(),
From: suite.address.Hex(),
BlockHeight: uint64(suite.ctx.BlockHeight()),
BlockHash: blockHash.Hex(),
}
suite.app.EvmKeeper.SetBlockHash(suite.ctx, blockHash.Bytes(), suite.ctx.BlockHeight())
suite.app.EvmKeeper.AddTxHashToBlock(suite.ctx, suite.ctx.BlockHeight(), hash)
suite.app.EvmKeeper.SetTxReceiptToHash(suite.ctx, hash, receipt)
req = &types.QueryTxReceiptsByBlockHashRequest{
Hash: blockHash.Hex(),
}
expRes = &types.QueryTxReceiptsByBlockHashResponse{
Receipts: []*types.TxReceipt{receipt},
}
},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset
tc.malleate()
ctx := sdk.WrapSDKContext(suite.ctx)
res, err := suite.queryClient.TxReceiptsByBlockHash(ctx, req)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expRes, res)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *KeeperTestSuite) TestQueryBlockBloom() {
var (
req *types.QueryBlockBloomRequest
+21 -42
View File
@@ -39,7 +39,9 @@ type Keeper struct {
ctx sdk.Context
// chain ID number obtained from the context's chain id
eip155ChainID *big.Int
debug bool
// TODO: deprecate
// Ethermint concrete implementation on the EVM StateDB interface
CommitStateDB *types.CommitStateDB
@@ -48,6 +50,9 @@ type Keeper struct {
// TODO: (@fedekunze) for how long should we persist the entries in the access list?
// same block (i.e Transient Store)? 2 or more (KVStore with module Parameter which resets the state after that window)?
accessList *types.AccessListMappings
// hash header for the current height. Reset during abci.RequestBeginBlock
headerHash common.Hash
}
// NewKeeper generates new evm module keeper
@@ -148,40 +153,24 @@ func (k Keeper) SetBlockBloomTransient(bloom *big.Int) {
// Block
// ----------------------------------------------------------------------------
// GetBlockHash gets block height from block consensus hash
func (k Keeper) GetBlockHashFromHeight(ctx sdk.Context, height int64) (common.Hash, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyBlockHeightHash(uint64(height)))
// GetHeaderHash gets the header hash from a given height
func (k Keeper) GetHeaderHash(ctx sdk.Context, height int64) common.Hash {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixHeightToHeaderHash)
key := sdk.Uint64ToBigEndian(uint64(height))
bz := store.Get(key)
if len(bz) == 0 {
return common.Hash{}, false
return common.Hash{}
}
return common.BytesToHash(bz), true
return common.BytesToHash(bz)
}
// SetBlockHash sets the mapping from block consensus hash to block height
func (k Keeper) SetBlockHash(ctx sdk.Context, hash []byte, height int64) {
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) {
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 common.Hash, height int64) {
store := ctx.KVStore(k.storeKey)
store.Set(types.KeyBlockHeightHash(uint64(height)), hash.Bytes())
// SetBlockHash sets the mapping from heigh -> header hash
func (k Keeper) SetHeaderHash(ctx sdk.Context, height int64, hash common.Hash) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixHeightToHeaderHash)
key := sdk.Uint64ToBigEndian(uint64(height))
store.Set(key, hash.Bytes())
}
// SetTxReceiptToHash sets the mapping from tx hash to tx receipt
@@ -266,8 +255,8 @@ func (k Keeper) AddTxHashToBlock(ctx sdk.Context, blockHeight int64, txHash comm
}
// GetTxsFromBlock returns list of tx hash in the block by height.
func (k Keeper) GetTxsFromBlock(ctx sdk.Context, blockHeight int64) []common.Hash {
key := types.KeyBlockHeightTxs(uint64(blockHeight))
func (k Keeper) GetTxsFromBlock(ctx sdk.Context, blockHeight uint64) []common.Hash {
key := types.KeyBlockHeightTxs(blockHeight)
store := ctx.KVStore(k.storeKey)
data := store.Get(key)
@@ -287,7 +276,7 @@ func (k Keeper) GetTxsFromBlock(ctx sdk.Context, blockHeight int64) []common.Has
}
// GetTxReceiptsByBlockHeight gets tx receipts by block height.
func (k Keeper) GetTxReceiptsByBlockHeight(ctx sdk.Context, blockHeight int64) []*types.TxReceipt {
func (k Keeper) GetTxReceiptsByBlockHeight(ctx sdk.Context, blockHeight uint64) []*types.TxReceipt {
txs := k.GetTxsFromBlock(ctx, blockHeight)
if len(txs) == 0 {
return nil
@@ -312,16 +301,6 @@ func (k Keeper) GetTxReceiptsByBlockHeight(ctx sdk.Context, blockHeight int64) [
return receipts
}
// GetTxReceiptsByBlockHash gets tx receipts by block hash.
func (k Keeper) GetTxReceiptsByBlockHash(ctx sdk.Context, hash common.Hash) []*types.TxReceipt {
blockHeight, ok := k.GetBlockHeightByHash(ctx, hash)
if !ok {
return nil
}
return k.GetTxReceiptsByBlockHeight(ctx, blockHeight)
}
// ----------------------------------------------------------------------------
// Log
// ----------------------------------------------------------------------------
+13 -16
View File
@@ -54,7 +54,6 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
txHash := tmtypes.Tx(ctx.TxBytes()).Hash()
ethHash := ethcmn.BytesToHash(txHash)
blockHash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
st := &types.StateTransition{
Message: ethMsg,
@@ -69,7 +68,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
// other nodes, causing a consensus error
if !st.Simulate {
// Prepare db for logs
k.CommitStateDB.Prepare(ethHash, blockHash, int(k.GetTxIndexTransient()))
k.CommitStateDB.Prepare(ethHash, k.headerHash, int(k.GetTxIndexTransient()))
k.IncreaseTxIndexTransient()
}
@@ -85,7 +84,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
From: sender.Hex(),
Data: msg.Data,
BlockHeight: uint64(ctx.BlockHeight()),
BlockHash: blockHash.Hex(),
BlockHash: k.headerHash.Hex(),
Result: &types.TxResult{
ContractAddress: executionResult.Response.ContractAddress,
Bloom: executionResult.Response.Bloom,
@@ -118,14 +117,13 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
panic(err)
}
blockHash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
k.SetTxReceiptToHash(ctx, ethHash, &types.TxReceipt{
Hash: ethHash.Hex(),
From: sender.Hex(),
Data: msg.Data,
Index: uint64(st.Csdb.TxIndex()),
BlockHeight: uint64(ctx.BlockHeight()),
BlockHash: blockHash.Hex(),
BlockHash: k.headerHash.Hex(),
Result: &types.TxResult{
ContractAddress: executionResult.Response.ContractAddress,
Bloom: executionResult.Response.Bloom,
@@ -154,12 +152,20 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
)
}()
attrs := []sdk.Attribute{
sdk.NewAttribute(sdk.AttributeKeyAmount, st.Message.Value().String()),
sdk.NewAttribute(types.AttributeKeyTxHash, ethcmn.BytesToHash(txHash).Hex()),
}
if len(msg.Data.To) > 0 {
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.To))
}
// emit events
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeEthereumTx,
sdk.NewAttribute(sdk.AttributeKeyAmount, st.Message.Value().String()),
sdk.NewAttribute(types.AttributeKeyTxHash, ethcmn.BytesToHash(txHash).Hex()),
attrs...,
),
sdk.NewEvent(
sdk.EventTypeMessage,
@@ -168,14 +174,5 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
),
})
if len(msg.Data.To) > 0 {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeEthereumTx,
sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.To),
),
)
}
return executionResult.Response, nil
}
+1 -9
View File
@@ -454,11 +454,6 @@ func (k *Keeper) Empty(addr common.Address) bool {
//
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
func (k *Keeper) PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses ethtypes.AccessList) {
// NOTE: only update the access list during DeliverTx
if k.ctx.IsCheckTx() || k.ctx.IsReCheckTx() {
return
}
k.AddAddressToAccessList(sender)
if dest != nil {
k.AddAddressToAccessList(*dest)
@@ -532,11 +527,8 @@ func (k *Keeper) RevertToSnapshot(_ int) {}
// to store.
func (k *Keeper) AddLog(log *ethtypes.Log) {
txHash := common.BytesToHash(tmtypes.Tx(k.ctx.TxBytes()).Hash())
blockHash, found := k.GetBlockHashFromHeight(k.ctx, k.ctx.BlockHeight())
if found {
log.BlockHash = blockHash
}
log.BlockHash = k.headerHash
log.TxHash = txHash
log.TxIndex = uint(k.GetTxIndexTransient())