forked from cerc-io/laconicd-deprecated
evm: error and block hash map updates (#60)
* evm: error and block hash map updates * evm: update tests
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user