forked from cerc-io/laconicd-deprecated
additions
This commit is contained in:
+12
-8
@@ -3,29 +3,29 @@ package keeper
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/cosmos/ethermint/metrics"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// BeginBlock sets the block height -> header hash map for the previous block height
|
||||
// BeginBlock sets the block hash -> block height map for the previous block height
|
||||
// and resets the Bloom filter and the transaction count to 0.
|
||||
func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||
if req.Header.LastBlockId.GetHash() == nil || req.Header.GetHeight() < 1 {
|
||||
if req.Header.Height < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Gas costs are handled within msg handler so costs should be ignored
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
// Set the hash -> height and height -> hash mapping.
|
||||
currentHash := req.Hash
|
||||
height := req.Header.GetHeight()
|
||||
k.SetBlockHash(ctx, req.Hash, req.Header.Height)
|
||||
k.SetBlockHeightToHash(ctx, req.Hash, req.Header.Height)
|
||||
|
||||
k.SetHeightHash(ctx, uint64(height), common.BytesToHash(hash))
|
||||
// special setter for csdb
|
||||
k.SetHeightHash(ctx, uint64(req.Header.Height), common.BytesToHash(req.Hash))
|
||||
|
||||
// reset counters that are used on CommitStateDB.Prepare
|
||||
k.Bloom = big.NewInt(0)
|
||||
@@ -37,6 +37,10 @@ func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||
// the store. The EVM end block logic doesn't update the validator set, thus it returns
|
||||
// an empty slice.
|
||||
func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
// Gas costs are handled within msg handler so costs should be ignored
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestBeginBlock() {
|
||||
// update the counters
|
||||
suite.app.EvmKeeper.Bloom.SetInt64(10)
|
||||
suite.app.EvmKeeper.TxCount = 10
|
||||
|
||||
suite.app.EvmKeeper.BeginBlock(suite.ctx, abci.RequestBeginBlock{})
|
||||
suite.Require().NotZero(suite.app.EvmKeeper.Bloom.Int64())
|
||||
suite.Require().NotZero(suite.app.EvmKeeper.TxCount)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestEndBlock() {
|
||||
// update the counters
|
||||
suite.app.EvmKeeper.Bloom.SetInt64(10)
|
||||
|
||||
// set gas limit to 1 to ensure no gas is consumed during the operation
|
||||
initialConsumed := suite.ctx.GasMeter().GasConsumed()
|
||||
|
||||
_ = suite.app.EvmKeeper.EndBlock(suite.ctx, abci.RequestEndBlock{Height: 100})
|
||||
|
||||
suite.Require().Equal(int64(initialConsumed), int64(suite.ctx.GasMeter().GasConsumed()))
|
||||
|
||||
bloom, found := suite.app.EvmKeeper.GetBlockBloom(suite.ctx, 100)
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(int64(10), bloom.Big().Int64())
|
||||
|
||||
}
|
||||
+263
-32
@@ -2,14 +2,17 @@ package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/cosmos/ethermint/metrics"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
@@ -17,12 +20,18 @@ import (
|
||||
var _ types.QueryServer = Keeper{}
|
||||
|
||||
// Account implements the Query/Account gRPC method
|
||||
func (q Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*types.QueryAccountResponse, error) {
|
||||
func (k Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*types.QueryAccountResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if len(req.Address) == 0 {
|
||||
if types.IsZeroAddress(req.Address) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrZeroAddress.Error(),
|
||||
@@ -30,9 +39,10 @@ func (q Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
so := q.GetOrNewStateObject(ctx, ethcmn.HexToAddress(req.Address))
|
||||
so := k.GetOrNewStateObject(ctx, ethcmn.HexToAddress(req.Address))
|
||||
balance, err := ethermint.MarshalBigInt(so.Balance())
|
||||
if err != nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -43,13 +53,18 @@ func (q Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Balance implements the Query/Balance gRPC method
|
||||
func (q Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) {
|
||||
func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRequest) (*types.QueryCosmosAccountResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if len(req.Address) == 0 {
|
||||
if types.IsZeroAddress(req.Address) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrZeroAddress.Error(),
|
||||
@@ -58,9 +73,48 @@ func (q Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*typ
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
balanceInt := q.GetBalance(ctx, ethcmn.HexToAddress(req.Address))
|
||||
ethStr := req.Address
|
||||
ethAddr := ethcmn.FromHex(ethStr)
|
||||
|
||||
ethToCosmosAddr := sdk.AccAddress(ethAddr[:]).String()
|
||||
cosmosToEthAddr, _ := sdk.AccAddressFromBech32(ethToCosmosAddr)
|
||||
|
||||
acc := k.accountKeeper.GetAccount(ctx, cosmosToEthAddr)
|
||||
res := types.QueryCosmosAccountResponse{
|
||||
CosmosAddress: cosmosToEthAddr.String(),
|
||||
}
|
||||
if acc != nil {
|
||||
res.Sequence = acc.GetSequence()
|
||||
res.AccountNumber = acc.GetAccountNumber()
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// Balance implements the Query/Balance gRPC method
|
||||
func (k Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if types.IsZeroAddress(req.Address) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrZeroAddress.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
balanceInt := k.GetBalance(ctx, ethcmn.HexToAddress(req.Address))
|
||||
balance, err := ethermint.MarshalBigInt(balanceInt)
|
||||
if err != nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.Internal,
|
||||
"failed to marshal big.Int to string",
|
||||
@@ -73,31 +127,30 @@ func (q Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*typ
|
||||
}
|
||||
|
||||
// Storage implements the Query/Storage gRPC method
|
||||
func (q Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*types.QueryStorageResponse, error) {
|
||||
func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*types.QueryStorageResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if len(req.Address) == 0 {
|
||||
if types.IsZeroAddress(req.Address) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrZeroAddress.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
if len(req.Key) == 0 {
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrEmptyHash.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
address := ethcmn.HexToAddress(req.Address)
|
||||
key := ethcmn.HexToHash(req.Key)
|
||||
|
||||
state := q.GetState(ctx, address, key)
|
||||
state := k.GetState(ctx, address, key)
|
||||
|
||||
return &types.QueryStorageResponse{
|
||||
Value: state.String(),
|
||||
@@ -105,12 +158,18 @@ func (q Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ
|
||||
}
|
||||
|
||||
// Code implements the Query/Code gRPC method
|
||||
func (q Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.QueryCodeResponse, error) {
|
||||
func (k Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.QueryCodeResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if len(req.Address) == 0 {
|
||||
if types.IsZeroAddress(req.Address) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrZeroAddress.Error(),
|
||||
@@ -120,7 +179,7 @@ func (q Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.Que
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
address := ethcmn.HexToAddress(req.Address)
|
||||
code := q.GetCode(ctx, address)
|
||||
code := k.GetCode(ctx, address)
|
||||
|
||||
return &types.QueryCodeResponse{
|
||||
Code: code,
|
||||
@@ -128,12 +187,18 @@ func (q Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.Que
|
||||
}
|
||||
|
||||
// TxLogs implements the Query/TxLogs gRPC method
|
||||
func (q Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types.QueryTxLogsResponse, error) {
|
||||
func (k Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types.QueryTxLogsResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if types.IsEmptyHash(req.Hash) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrEmptyHash.Error(),
|
||||
@@ -143,8 +208,9 @@ func (q Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
hash := ethcmn.HexToHash(req.Hash)
|
||||
logs, err := q.GetLogs(ctx, hash)
|
||||
logs, err := k.GetLogs(ctx, hash)
|
||||
if err != nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.Internal,
|
||||
err.Error(),
|
||||
@@ -156,13 +222,19 @@ func (q Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BlockLogs implements the Query/BlockLogs gRPC method
|
||||
func (q Keeper) BlockLogs(c context.Context, req *types.QueryBlockLogsRequest) (*types.QueryBlockLogsResponse, error) {
|
||||
// TxReceipt implements the Query/TxReceipt gRPC method
|
||||
func (k Keeper) TxReceipt(c context.Context, req *types.QueryTxReceiptRequest) (*types.QueryTxReceiptResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if types.IsEmptyHash(req.Hash) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrEmptyHash.Error(),
|
||||
@@ -171,7 +243,90 @@ func (q Keeper) BlockLogs(c context.Context, req *types.QueryBlockLogsRequest) (
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
txLogs := q.GetAllTxLogs(ctx)
|
||||
hash := ethcmn.HexToHash(req.Hash)
|
||||
receipt, found := k.GetTxReceiptFromHash(ctx, hash)
|
||||
if !found {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.NotFound, types.ErrTxReceiptNotFound.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
return &types.QueryTxReceiptResponse{
|
||||
Receipt: receipt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TxReceiptsByBlockHeight implements the Query/TxReceiptsByBlockHeight gRPC method
|
||||
func (k Keeper) TxReceiptsByBlockHeight(c context.Context, req *types.QueryTxReceiptsByBlockHeightRequest) (*types.QueryTxReceiptsByBlockHeightResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
receipts := k.GetTxReceiptsByBlockHeight(ctx, req.Height)
|
||||
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) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if types.IsEmptyHash(req.Hash) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
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) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if types.IsEmptyHash(req.Hash) {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.InvalidArgument,
|
||||
types.ErrEmptyHash.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
txLogs := k.GetAllTxLogs(ctx)
|
||||
|
||||
return &types.QueryBlockLogsResponse{
|
||||
TxLogs: txLogs,
|
||||
@@ -179,14 +334,28 @@ func (q Keeper) BlockLogs(c context.Context, req *types.QueryBlockLogsRequest) (
|
||||
}
|
||||
|
||||
// BlockBloom implements the Query/BlockBloom gRPC method
|
||||
func (q Keeper) BlockBloom(c context.Context, _ *types.QueryBlockBloomRequest) (*types.QueryBlockBloomResponse, error) {
|
||||
func (k Keeper) BlockBloom(c context.Context, req *types.QueryBlockBloomRequest) (*types.QueryBlockBloomResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
// use block height provided through the gRPC header
|
||||
bloom, found := q.GetBlockBloom(ctx, ctx.BlockHeight())
|
||||
height := ctx.BlockHeight()
|
||||
if setHeight := req.Height; setHeight > 0 {
|
||||
height = setHeight
|
||||
}
|
||||
|
||||
bloom, found := k.GetBlockBloom(ctx, height)
|
||||
if !found {
|
||||
return nil, status.Errorf(
|
||||
codes.NotFound, "%s: height %d", types.ErrBloomNotFound.Error(), ctx.BlockHeight(),
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(
|
||||
codes.NotFound, types.ErrBloomNotFound.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -196,11 +365,73 @@ func (q Keeper) BlockBloom(c context.Context, _ *types.QueryBlockBloomRequest) (
|
||||
}
|
||||
|
||||
// Params implements the Query/Params gRPC method
|
||||
func (q Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
|
||||
func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
if req == nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
params := q.GetParams(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
|
||||
return &types.QueryParamsResponse{
|
||||
Params: params,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StaticCall implements Query/StaticCall gRPCP method
|
||||
func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest) (*types.QueryStaticCallResponse, error) {
|
||||
if req == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txHash := tmtypes.Tx(ctx.TxBytes()).Hash()
|
||||
ethHash := ethcmn.BytesToHash(txHash)
|
||||
|
||||
var recipient *ethcmn.Address
|
||||
if len(req.Address) > 0 {
|
||||
addr := ethcmn.HexToAddress(req.Address)
|
||||
recipient = &addr
|
||||
}
|
||||
|
||||
so := k.GetOrNewStateObject(ctx, *recipient)
|
||||
sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707")
|
||||
|
||||
st := &types.StateTransition{
|
||||
AccountNonce: so.Nonce(),
|
||||
Price: new(big.Int).SetBytes(big.NewInt(0).Bytes()),
|
||||
GasLimit: 100000000,
|
||||
Recipient: recipient,
|
||||
Amount: new(big.Int).SetBytes(big.NewInt(0).Bytes()),
|
||||
Payload: req.Input,
|
||||
Csdb: k.CommitStateDB.WithContext(ctx),
|
||||
ChainID: chainIDEpoch,
|
||||
TxHash: ðHash,
|
||||
Sender: sender,
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
}
|
||||
|
||||
config, found := k.GetChainConfig(ctx)
|
||||
if !found {
|
||||
return nil, types.ErrChainConfigNotFound
|
||||
}
|
||||
|
||||
ret, err := st.StaticCall(ctx, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.QueryStaticCallResponse{Data: ret}, nil
|
||||
}
|
||||
|
||||
@@ -1,499 +0,0 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryAccount() {
|
||||
var (
|
||||
req *types.QueryAccountRequest
|
||||
expAccount *types.QueryAccountResponse
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"zero address",
|
||||
func() {
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(0))
|
||||
expAccount = &types.QueryAccountResponse{
|
||||
Balance: "0",
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
Nonce: 0,
|
||||
}
|
||||
req = &types.QueryAccountRequest{
|
||||
Address: ethcmn.Address{}.String(),
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(100))
|
||||
expAccount = &types.QueryAccountResponse{
|
||||
Balance: "100",
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
Nonce: 0,
|
||||
}
|
||||
req = &types.QueryAccountRequest{
|
||||
Address: suite.address.String(),
|
||||
}
|
||||
},
|
||||
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.Account(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expAccount, res)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryBalance() {
|
||||
var (
|
||||
req *types.QueryBalanceRequest
|
||||
expBalance string
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"zero address",
|
||||
func() {
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(0))
|
||||
expBalance = "0"
|
||||
req = &types.QueryBalanceRequest{
|
||||
Address: ethcmn.Address{}.String(),
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(100))
|
||||
expBalance = "100"
|
||||
req = &types.QueryBalanceRequest{
|
||||
Address: suite.address.String(),
|
||||
}
|
||||
},
|
||||
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.Balance(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expBalance, res.Balance)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryStorage() {
|
||||
var (
|
||||
req *types.QueryStorageRequest
|
||||
expValue string
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"zero address",
|
||||
func() {
|
||||
req = &types.QueryStorageRequest{
|
||||
Address: ethcmn.Address{}.String(),
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{"empty hash",
|
||||
func() {
|
||||
req = &types.QueryStorageRequest{
|
||||
Address: suite.address.String(),
|
||||
Key: ethcmn.Hash{}.String(),
|
||||
}
|
||||
exp := &types.QueryStorageResponse{Value: "0x0000000000000000000000000000000000000000000000000000000000000000"}
|
||||
expValue = exp.Value
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
key := ethcmn.BytesToHash([]byte("key"))
|
||||
value := ethcmn.BytesToHash([]byte("value"))
|
||||
expValue = value.String()
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, key, value)
|
||||
req = &types.QueryStorageRequest{
|
||||
Address: suite.address.String(),
|
||||
Key: key.String(),
|
||||
}
|
||||
},
|
||||
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.Storage(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expValue, res.Value)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryCode() {
|
||||
var (
|
||||
req *types.QueryCodeRequest
|
||||
expCode []byte
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"zero address",
|
||||
func() {
|
||||
req = &types.QueryCodeRequest{
|
||||
Address: ethcmn.Address{}.String(),
|
||||
}
|
||||
exp := &types.QueryCodeResponse{}
|
||||
expCode = exp.Code
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
expCode = []byte("code")
|
||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, expCode)
|
||||
|
||||
req = &types.QueryCodeRequest{
|
||||
Address: suite.address.String(),
|
||||
}
|
||||
},
|
||||
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.Code(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expCode, res.Code)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryTxLogs() {
|
||||
var (
|
||||
req *types.QueryTxLogsRequest
|
||||
expLogs []*types.Log
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"empty hash",
|
||||
func() {
|
||||
req = &types.QueryTxLogsRequest{
|
||||
Hash: ethcmn.Hash{}.String(),
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{"logs not found",
|
||||
func() {
|
||||
hash := ethcmn.BytesToHash([]byte("hash"))
|
||||
req = &types.QueryTxLogsRequest{
|
||||
Hash: hash.String(),
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
hash := ethcmn.BytesToHash([]byte("tx_hash"))
|
||||
|
||||
expLogs = []*types.Log{
|
||||
{
|
||||
Address: suite.address.String(),
|
||||
Topics: []string{ethcmn.BytesToHash([]byte("topic")).String()},
|
||||
Data: []byte("data"),
|
||||
BlockNumber: 1,
|
||||
TxHash: hash.String(),
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.BytesToHash([]byte("block_hash")).String(),
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
}
|
||||
|
||||
suite.app.EvmKeeper.SetLogs(suite.ctx, hash, types.LogsToEthereum(expLogs))
|
||||
|
||||
req = &types.QueryTxLogsRequest{
|
||||
Hash: hash.String(),
|
||||
}
|
||||
},
|
||||
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.TxLogs(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expLogs, res.Logs)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryBlockLogs() {
|
||||
var (
|
||||
req *types.QueryBlockLogsRequest
|
||||
expLogs []types.TransactionLogs
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"empty hash",
|
||||
func() {
|
||||
req = &types.QueryBlockLogsRequest{
|
||||
Hash: ethcmn.Hash{}.String(),
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{"logs not found",
|
||||
func() {
|
||||
hash := ethcmn.BytesToHash([]byte("hash"))
|
||||
req = &types.QueryBlockLogsRequest{
|
||||
Hash: hash.String(),
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
|
||||
hash := ethcmn.BytesToHash([]byte("block_hash"))
|
||||
expLogs = []types.TransactionLogs{
|
||||
{
|
||||
Hash: ethcmn.BytesToHash([]byte("tx_hash_0")).String(),
|
||||
Logs: []*types.Log{
|
||||
{
|
||||
Address: suite.address.String(),
|
||||
Topics: []string{ethcmn.BytesToHash([]byte("topic")).String()},
|
||||
Data: []byte("data"),
|
||||
BlockNumber: 1,
|
||||
TxHash: ethcmn.BytesToHash([]byte("tx_hash_0")).String(),
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.BytesToHash([]byte("block_hash")).String(),
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Hash: ethcmn.BytesToHash([]byte("tx_hash_1")).String(),
|
||||
Logs: []*types.Log{
|
||||
{
|
||||
Address: suite.address.String(),
|
||||
Topics: []string{ethcmn.BytesToHash([]byte("topic")).String()},
|
||||
Data: []byte("data"),
|
||||
BlockNumber: 1,
|
||||
TxHash: ethcmn.BytesToHash([]byte("tx_hash_1")).String(),
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.BytesToHash([]byte("block_hash")).String(),
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
{
|
||||
Address: suite.address.String(),
|
||||
Topics: []string{ethcmn.BytesToHash([]byte("topic_1")).String()},
|
||||
Data: []byte("data_1"),
|
||||
BlockNumber: 1,
|
||||
TxHash: ethcmn.BytesToHash([]byte("tx_hash_1")).String(),
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.BytesToHash([]byte("block_hash")).String(),
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
suite.app.EvmKeeper.SetLogs(suite.ctx, ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs))
|
||||
suite.app.EvmKeeper.SetLogs(suite.ctx, ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs))
|
||||
|
||||
req = &types.QueryBlockLogsRequest{
|
||||
Hash: hash.String(),
|
||||
}
|
||||
},
|
||||
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.BlockLogs(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expLogs, res.TxLogs)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryBlockBloom() {
|
||||
var (
|
||||
req *types.QueryBlockBloomRequest
|
||||
expBloom []byte
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{"bloom bytes not found for height",
|
||||
func() {},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
func() {
|
||||
req = &types.QueryBlockBloomRequest{}
|
||||
bloom := ethtypes.BytesToBloom([]byte("bloom"))
|
||||
expBloom = bloom.Bytes()
|
||||
suite.ctx = suite.ctx.WithBlockHeight(1)
|
||||
suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 1, bloom)
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
suite.SetupTest() // reset
|
||||
|
||||
tc.malleate()
|
||||
ctx := sdk.WrapSDKContext(suite.ctx)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, fmt.Sprintf("%d", suite.ctx.BlockHeight()))
|
||||
res, err := suite.queryClient.BlockBloom(ctx, req)
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
|
||||
suite.Require().Equal(expBloom, res.Bloom)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestQueryParams() {
|
||||
ctx := sdk.WrapSDKContext(suite.ctx)
|
||||
expParams := types.DefaultParams()
|
||||
|
||||
res, err := suite.queryClient.Params(ctx, &types.QueryParamsRequest{})
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(expParams, res.Params)
|
||||
}
|
||||
@@ -4,10 +4,8 @@ import (
|
||||
"math/big"
|
||||
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
@@ -27,7 +25,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
||||
func() {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes())
|
||||
suite.Require().NotNil(acc)
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewPhotonCoinInt64(1))
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewInjectiveCoinInt64(1))
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
@@ -40,7 +38,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
||||
func() {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes())
|
||||
suite.Require().NotNil(acc)
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewPhotonCoinInt64(1))
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewInjectiveCoinInt64(1))
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
|
||||
+235
-28
@@ -1,20 +1,17 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"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
|
||||
@@ -27,11 +24,12 @@ type Keeper struct {
|
||||
// - storing Account's Code
|
||||
// - storing transaction Logs
|
||||
// - storing block height -> bloom filter map. Needed for the Web3 API.
|
||||
// - storing block hash -> block height map. Needed for the Web3 API.
|
||||
// - storing block hash -> block height map. Needed for the Web3 API. TODO: remove
|
||||
storeKey sdk.StoreKey
|
||||
// Account Keeper for fetching accounts
|
||||
|
||||
accountKeeper types.AccountKeeper
|
||||
bankKeeper types.BankKeeper
|
||||
|
||||
// Ethermint concrete implementation on the EVM StateDB interface
|
||||
CommitStateDB *types.CommitStateDB
|
||||
// Transaction counter in a block. Used on StateSB's Prepare function.
|
||||
@@ -39,6 +37,12 @@ type Keeper struct {
|
||||
// on the KVStore or adding it as a field on the EVM genesis state.
|
||||
TxCount int
|
||||
Bloom *big.Int
|
||||
|
||||
// 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
|
||||
@@ -53,6 +57,10 @@ func NewKeeper(
|
||||
|
||||
// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
|
||||
return &Keeper{
|
||||
svcTags: metrics.Tags{
|
||||
"svc": "evm_k",
|
||||
},
|
||||
|
||||
cdc: cdc,
|
||||
accountKeeper: ak,
|
||||
bankKeeper: bankKeeper,
|
||||
@@ -60,19 +68,116 @@ func NewKeeper(
|
||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak, bankKeeper),
|
||||
TxCount: 0,
|
||||
Bloom: big.NewInt(0),
|
||||
LogsCache: map[common.Address][]*ethtypes.Log{},
|
||||
}
|
||||
}
|
||||
|
||||
// Logger returns a module-specific logger.
|
||||
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
||||
return ctx.Logger().With("module", types.ModuleName)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Epoch Height -> hash mapping functions
|
||||
// Required by EVM context's GetHashFunc
|
||||
// Block bloom bits mapping functions
|
||||
// Required by Web3 API.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// 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)
|
||||
@@ -83,31 +188,121 @@ func (k Keeper) SetHeightHash(ctx sdk.Context, height uint64, hash common.Hash)
|
||||
k.CommitStateDB.WithContext(ctx).SetHeightHash(height, hash)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Block bloom bits mapping functions
|
||||
// Required by Web3 API.
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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()
|
||||
|
||||
// GetBlockBloom gets bloombits from block height
|
||||
func (k Keeper) GetBlockBloom(ctx sdk.Context, height int64) (ethtypes.Bloom, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBloom)
|
||||
has := store.Has(types.BloomKey(height))
|
||||
if !has {
|
||||
return ethtypes.Bloom{}, false
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
data := store.Get(types.KeyHashTxReceipt(hash))
|
||||
if data == nil || len(data) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bz := store.Get(types.BloomKey(height))
|
||||
return ethtypes.BytesToBloom(bz), true
|
||||
var receipt types.TxReceipt
|
||||
k.cdc.MustUnmarshalBinaryBare(data, &receipt)
|
||||
|
||||
return &receipt, true
|
||||
}
|
||||
|
||||
// SetBlockBloom sets the mapping from block height to bloom bits
|
||||
func (k Keeper) SetBlockBloom(ctx sdk.Context, height int64, bloom ethtypes.Bloom) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBloom)
|
||||
store.Set(types.BloomKey(height), bloom.Bytes())
|
||||
// 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 {
|
||||
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()
|
||||
@@ -125,6 +320,10 @@ func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
|
||||
|
||||
// GetAccountStorage return state storage associated with an account
|
||||
func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) {
|
||||
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 {
|
||||
@@ -140,6 +339,10 @@ func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (type
|
||||
|
||||
// GetChainConfig gets block height from block consensus hash
|
||||
func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
|
||||
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 {
|
||||
@@ -153,6 +356,10 @@ func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
|
||||
|
||||
// SetChainConfig sets the mapping from block consensus hash to block height
|
||||
func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinaryBare(&config)
|
||||
store.Set(types.KeyPrefixChainConfig, bz)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
@@ -32,24 +31,20 @@ var (
|
||||
type KeeperTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
app *app.EthermintApp
|
||||
queryClient types.QueryClient
|
||||
address ethcmn.Address
|
||||
ctx sdk.Context
|
||||
querier sdk.Querier
|
||||
app *app.InjectiveApp
|
||||
address ethcmn.Address
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-3", Time: time.Now().UTC()})
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()})
|
||||
suite.address = ethcmn.HexToAddress(addrHex)
|
||||
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
|
||||
types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper)
|
||||
suite.queryClient = types.NewQueryClient(queryHelper)
|
||||
|
||||
balance := ethermint.NewPhotonCoin(sdk.ZeroInt())
|
||||
balance := ethermint.NewInjectiveCoin(sdk.ZeroInt())
|
||||
acc := ðermint.EthAccount{
|
||||
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0),
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
@@ -69,13 +64,11 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
||||
Address: suite.address,
|
||||
Data: []byte("log"),
|
||||
BlockNumber: 10,
|
||||
Topics: []ethcmn.Hash{},
|
||||
}
|
||||
log2 := ðtypes.Log{
|
||||
Address: suite.address,
|
||||
Data: []byte("log2"),
|
||||
BlockNumber: 11,
|
||||
Topics: []ethcmn.Hash{},
|
||||
}
|
||||
expLogs := []*ethtypes.Log{log}
|
||||
|
||||
@@ -90,7 +83,6 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
||||
|
||||
// add another log under the zero hash
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, log2)
|
||||
log2.Index = 0
|
||||
logs = suite.app.EvmKeeper.AllLogs(suite.ctx)
|
||||
suite.Require().Equal(expLogs, logs)
|
||||
|
||||
@@ -99,19 +91,17 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
||||
Address: suite.address,
|
||||
Data: []byte("log3"),
|
||||
BlockNumber: 10,
|
||||
Topics: []ethcmn.Hash{},
|
||||
}
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, log3)
|
||||
log3.Index = 0
|
||||
|
||||
txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx)
|
||||
suite.Require().Equal(2, len(txLogs))
|
||||
|
||||
suite.Require().Equal(ethcmn.Hash{}.String(), txLogs[0].Hash)
|
||||
suite.Require().Equal([]*ethtypes.Log{log2, log3}, txLogs[0].EthLogs())
|
||||
suite.Require().Equal([]*ethtypes.Log{log2, log3}, txLogs[0].Logs)
|
||||
|
||||
suite.Require().Equal(ethHash.String(), txLogs[1].Hash)
|
||||
suite.Require().Equal([]*ethtypes.Log{log}, txLogs[1].EthLogs())
|
||||
suite.Require().Equal([]*ethtypes.Log{log}, txLogs[1].Logs)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestDBStorage() {
|
||||
|
||||
+288
-36
@@ -2,57 +2,68 @@ package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/xlab/suplog"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/telemetry"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/ethermint/metrics"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
var _ types.MsgServer = Keeper{}
|
||||
var _ types.MsgServer = &Keeper{}
|
||||
|
||||
func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
// EthereumTx implements the Msg/EthereumTx gRPC method.
|
||||
func (k Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error) {
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||
if err != nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Verify signature and retrieve sender address
|
||||
sender, err := msg.VerifySig(chainIDEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var homesteadErr error
|
||||
sender, eip155Err := msg.VerifySig(chainIDEpoch)
|
||||
if eip155Err != nil {
|
||||
sender, homesteadErr = msg.VerifySigHomestead()
|
||||
if homesteadErr != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"eip155_err": eip155Err.Error(),
|
||||
"homestead_err": homesteadErr.Error(),
|
||||
}).Warningln("failed to verify signatures with EIP155 and Homestead signers")
|
||||
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, errors.New("no valid signatures")
|
||||
}
|
||||
}
|
||||
|
||||
txHash := tmtypes.Tx(ctx.TxBytes()).Hash()
|
||||
ethHash := ethcmn.BytesToHash(txHash)
|
||||
|
||||
var recipient *ethcmn.Address
|
||||
|
||||
labels := []metrics.Label{telemetry.NewLabel("operation", "create")}
|
||||
|
||||
if msg.Data.Recipient != nil {
|
||||
addr := ethcmn.HexToAddress(msg.Data.Recipient.Address)
|
||||
if len(msg.Data.Recipient) > 0 {
|
||||
addr := ethcmn.BytesToAddress(msg.Data.Recipient)
|
||||
recipient = &addr
|
||||
labels = []metrics.Label{telemetry.NewLabel("operation", "call")}
|
||||
}
|
||||
|
||||
st := types.StateTransition{
|
||||
st := &types.StateTransition{
|
||||
AccountNonce: msg.Data.AccountNonce,
|
||||
Price: msg.Data.Price.BigInt(),
|
||||
Price: new(big.Int).SetBytes(msg.Data.Price),
|
||||
GasLimit: msg.Data.GasLimit,
|
||||
Recipient: recipient,
|
||||
Amount: msg.Data.Amount.BigInt(),
|
||||
Amount: new(big.Int).SetBytes(msg.Data.Amount),
|
||||
Payload: msg.Data.Payload,
|
||||
Csdb: k.CommitStateDB.WithContext(ctx),
|
||||
ChainID: chainIDEpoch,
|
||||
@@ -66,18 +77,47 @@ func (k Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*ty
|
||||
// other nodes, causing a consensus error
|
||||
if !st.Simulate {
|
||||
// Prepare db for logs
|
||||
blockHash := types.HashFromContext(ctx)
|
||||
k.Prepare(ctx, ethHash, blockHash, k.TxCount)
|
||||
hash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
|
||||
k.Prepare(ctx, ethHash, ethcmn.BytesToHash(hash), k.TxCount)
|
||||
k.TxCount++
|
||||
}
|
||||
|
||||
config, found := k.GetChainConfig(ctx)
|
||||
if !found {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, types.ErrChainConfigNotFound
|
||||
}
|
||||
|
||||
executionResult, err := st.TransitionDb(ctx, config)
|
||||
if err != nil {
|
||||
if err.Error() == "execution reverted" && executionResult != nil {
|
||||
// keep the execution result for revert reason
|
||||
executionResult.Response.Reverted = true
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
|
||||
if !st.Simulate {
|
||||
blockHash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
|
||||
k.SetTxReceiptToHash(ctx, ethHash, &types.TxReceipt{
|
||||
Hash: ethHash.Bytes(),
|
||||
From: sender.Bytes(),
|
||||
Data: msg.Data,
|
||||
BlockHeight: uint64(ctx.BlockHeight()),
|
||||
BlockHash: blockHash,
|
||||
Result: &types.TxResult{
|
||||
ContractAddress: executionResult.Response.ContractAddress,
|
||||
Bloom: executionResult.Response.Bloom,
|
||||
TxLogs: executionResult.Response.TxLogs,
|
||||
Ret: executionResult.Response.Ret,
|
||||
Reverted: executionResult.Response.Reverted,
|
||||
GasUsed: executionResult.GasInfo.GasConsumed,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return executionResult.Response, nil
|
||||
}
|
||||
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -86,28 +126,42 @@ func (k Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*ty
|
||||
k.Bloom.Or(k.Bloom, executionResult.Bloom)
|
||||
|
||||
// update transaction logs in KVStore
|
||||
err = k.SetLogs(ctx, ethcmn.BytesToHash(txHash), executionResult.Logs)
|
||||
err = k.SetLogs(ctx, ethHash, executionResult.Logs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// add metrics for the transaction
|
||||
defer func() {
|
||||
if st.Amount.IsInt64() {
|
||||
telemetry.SetGaugeWithLabels(
|
||||
[]string{"tx", "msg", "ethereum"},
|
||||
float32(st.Amount.Int64()),
|
||||
labels,
|
||||
)
|
||||
blockHash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
|
||||
k.SetTxReceiptToHash(ctx, ethHash, &types.TxReceipt{
|
||||
Hash: ethHash.Bytes(),
|
||||
From: sender.Bytes(),
|
||||
Data: msg.Data,
|
||||
Index: uint64(st.Csdb.TxIndex()),
|
||||
BlockHeight: uint64(ctx.BlockHeight()),
|
||||
BlockHash: blockHash,
|
||||
Result: &types.TxResult{
|
||||
ContractAddress: executionResult.Response.ContractAddress,
|
||||
Bloom: executionResult.Response.Bloom,
|
||||
TxLogs: executionResult.Response.TxLogs,
|
||||
Ret: executionResult.Response.Ret,
|
||||
Reverted: executionResult.Response.Reverted,
|
||||
GasUsed: executionResult.GasInfo.GasConsumed,
|
||||
},
|
||||
})
|
||||
|
||||
k.AddTxHashToBlock(ctx, ctx.BlockHeight(), ethHash)
|
||||
|
||||
for _, ethLog := range executionResult.Logs {
|
||||
k.LogsCache[ethLog.Address] = append(k.LogsCache[ethLog.Address], ethLog)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// emit events
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, st.Amount.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyTxHash, ethcmn.BytesToHash(txHash).Hex()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
@@ -116,11 +170,209 @@ func (k Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*ty
|
||||
),
|
||||
})
|
||||
|
||||
if msg.Data.Recipient != nil {
|
||||
if len(msg.Data.Recipient) > 0 {
|
||||
ethAddr := ethcmn.BytesToAddress(msg.Data.Recipient)
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.Recipient.Address),
|
||||
sdk.NewAttribute(types.AttributeKeyRecipient, ethAddr.Hex()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return executionResult.Response, nil
|
||||
}
|
||||
|
||||
func (k *Keeper) SendInternalEthereumTx(
|
||||
ctx sdk.Context,
|
||||
payload []byte,
|
||||
senderAddress common.Address,
|
||||
recipientAddress common.Address,
|
||||
) (*types.MsgEthereumTxResponse, error) {
|
||||
|
||||
accAddress := sdk.AccAddress(senderAddress.Bytes())
|
||||
|
||||
acc := k.accountKeeper.GetAccount(ctx, accAddress)
|
||||
if acc == nil {
|
||||
acc = k.accountKeeper.NewAccountWithAddress(ctx, accAddress)
|
||||
k.accountKeeper.SetAccount(ctx, acc)
|
||||
}
|
||||
|
||||
ethAccount, ok := acc.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
return nil, errors.New("could not cast account to EthAccount")
|
||||
}
|
||||
|
||||
if err := ethAccount.SetSequence(ethAccount.GetSequence() + 1); err != nil {
|
||||
return nil, errors.New("failed to set acc sequence")
|
||||
}
|
||||
|
||||
k.accountKeeper.SetAccount(ctx, ethAccount)
|
||||
|
||||
res, err := k.InternalEthereumTx(sdk.WrapSDKContext(ctx), senderAddress, &types.TxData{
|
||||
AccountNonce: ethAccount.GetSequence(),
|
||||
Recipient: recipientAddress.Bytes(),
|
||||
Amount: big.NewInt(0).Bytes(),
|
||||
Price: big.NewInt(0).Bytes(),
|
||||
GasLimit: 10000000, // TODO: don't hardcode, maybe set a limit?
|
||||
Payload: payload,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to execute InternalEthereumTx at contract %s", recipientAddress.Hex())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (k *Keeper) InternalEthereumTx(
|
||||
goCtx context.Context,
|
||||
sender common.Address,
|
||||
tx *types.TxData,
|
||||
) (*types.MsgEthereumTxResponse, error) {
|
||||
metrics.ReportFuncCall(k.svcTags)
|
||||
doneFn := metrics.ReportFuncTiming(k.svcTags)
|
||||
defer doneFn()
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||
if err != nil {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// true Ethereum tx hash based on its data
|
||||
ethHash := (&types.MsgEthereumTx{
|
||||
Data: tx,
|
||||
}).RLPSignBytes(chainIDEpoch)
|
||||
|
||||
var recipient *ethcmn.Address
|
||||
if len(tx.Recipient) > 0 {
|
||||
addr := ethcmn.BytesToAddress(tx.Recipient)
|
||||
recipient = &addr
|
||||
}
|
||||
|
||||
st := &types.StateTransition{
|
||||
AccountNonce: tx.AccountNonce,
|
||||
Price: new(big.Int).SetBytes(tx.Price),
|
||||
GasLimit: tx.GasLimit,
|
||||
Recipient: recipient,
|
||||
Amount: new(big.Int).SetBytes(tx.Amount),
|
||||
Payload: tx.Payload,
|
||||
Csdb: k.CommitStateDB.WithContext(ctx),
|
||||
ChainID: chainIDEpoch,
|
||||
TxHash: ðHash,
|
||||
Sender: sender,
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
}
|
||||
|
||||
// since the txCount is used by the stateDB, and a simulated tx is run only on the node it's submitted to,
|
||||
// then this will cause the txCount/stateDB of the node that ran the simulated tx to be different than the
|
||||
// other nodes, causing a consensus error
|
||||
if !st.Simulate {
|
||||
// Prepare db for logs
|
||||
hash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
|
||||
k.Prepare(ctx, ethHash, ethcmn.BytesToHash(hash), k.TxCount)
|
||||
k.TxCount++
|
||||
}
|
||||
|
||||
config, found := k.GetChainConfig(ctx)
|
||||
if !found {
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, types.ErrChainConfigNotFound
|
||||
}
|
||||
|
||||
executionResult, err := st.TransitionDb(ctx, config)
|
||||
if err != nil {
|
||||
if err.Error() == "execution reverted" && executionResult != nil {
|
||||
// keep the execution result for revert reason
|
||||
executionResult.Response.Reverted = true
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
|
||||
if !st.Simulate {
|
||||
blockHash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
|
||||
k.SetTxReceiptToHash(ctx, ethHash, &types.TxReceipt{
|
||||
Hash: ethHash.Bytes(),
|
||||
From: sender.Bytes(),
|
||||
Data: tx,
|
||||
BlockHeight: uint64(ctx.BlockHeight()),
|
||||
BlockHash: blockHash,
|
||||
Result: &types.TxResult{
|
||||
ContractAddress: executionResult.Response.ContractAddress,
|
||||
Bloom: executionResult.Response.Bloom,
|
||||
TxLogs: executionResult.Response.TxLogs,
|
||||
Ret: executionResult.Response.Ret,
|
||||
Reverted: executionResult.Response.Reverted,
|
||||
GasUsed: executionResult.GasInfo.GasConsumed,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return executionResult.Response, err
|
||||
}
|
||||
|
||||
metrics.ReportFuncError(k.svcTags)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockHash, _ := k.GetBlockHashFromHeight(ctx, ctx.BlockHeight())
|
||||
k.SetTxReceiptToHash(ctx, ethHash, &types.TxReceipt{
|
||||
Hash: ethHash.Bytes(),
|
||||
From: sender.Bytes(),
|
||||
Data: tx,
|
||||
Index: uint64(st.Csdb.TxIndex()),
|
||||
BlockHeight: uint64(ctx.BlockHeight()),
|
||||
BlockHash: blockHash,
|
||||
Result: &types.TxResult{
|
||||
ContractAddress: executionResult.Response.ContractAddress,
|
||||
Bloom: executionResult.Response.Bloom,
|
||||
TxLogs: executionResult.Response.TxLogs,
|
||||
Ret: executionResult.Response.Ret,
|
||||
Reverted: executionResult.Response.Reverted,
|
||||
GasUsed: executionResult.GasInfo.GasConsumed,
|
||||
},
|
||||
})
|
||||
|
||||
k.AddTxHashToBlock(ctx, ctx.BlockHeight(), ethHash)
|
||||
|
||||
if !st.Simulate {
|
||||
// update block bloom filter
|
||||
k.Bloom.Or(k.Bloom, executionResult.Bloom)
|
||||
|
||||
// update transaction logs in KVStore
|
||||
err = k.SetLogs(ctx, ethHash, executionResult.Logs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, ethLog := range executionResult.Logs {
|
||||
k.LogsCache[ethLog.Address] = append(k.LogsCache[ethLog.Address], ethLog)
|
||||
}
|
||||
}
|
||||
|
||||
// emit events
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, st.Amount.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyTxHash, ethHash.Hex()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, sender.String()),
|
||||
),
|
||||
})
|
||||
|
||||
if len(tx.Recipient) > 0 {
|
||||
ethAddr := ethcmn.BytesToAddress(tx.Recipient)
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeEthereumTx,
|
||||
sdk.NewAttribute(types.AttributeKeyRecipient, ethAddr.Hex()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
func (suite *KeeperTestSuite) TestParams() {
|
||||
params := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||
suite.Require().Equal(types.DefaultParams(), params)
|
||||
params.EvmDenom = "ara"
|
||||
params.EvmDenom = "inj"
|
||||
suite.app.EvmKeeper.SetParams(suite.ctx, params)
|
||||
newParams := suite.app.EvmKeeper.GetParams(suite.ctx)
|
||||
suite.Require().Equal(newParams, params)
|
||||
|
||||
@@ -49,6 +49,11 @@ func (k *Keeper) SetCode(ctx sdk.Context, addr ethcmn.Address, code []byte) {
|
||||
|
||||
// SetLogs calls CommitStateDB.SetLogs using the passed in context
|
||||
func (k *Keeper) SetLogs(ctx sdk.Context, hash ethcmn.Hash, logs []*ethtypes.Log) error {
|
||||
// TODO:@albert
|
||||
// since SetLogs is only called for non-simulation mode, GasEstimation is quite not correct
|
||||
// I suggest using ctx.ConsumeGas(XXX) where XXX is max of gas consume for set logs.
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
return k.CommitStateDB.WithContext(ctx).SetLogs(hash, logs)
|
||||
}
|
||||
|
||||
@@ -225,8 +230,8 @@ func (k *Keeper) Reset(ctx sdk.Context, root ethcmn.Hash) error {
|
||||
}
|
||||
|
||||
// Prepare calls CommitStateDB.Prepare using the passed in context
|
||||
func (k *Keeper) Prepare(ctx sdk.Context, thash ethcmn.Hash, txi int) {
|
||||
k.CommitStateDB.WithContext(ctx).Prepare(thash, txi)
|
||||
func (k *Keeper) Prepare(ctx sdk.Context, thash, bhash ethcmn.Hash, txi int) {
|
||||
k.CommitStateDB.WithContext(ctx).Prepare(thash, bhash, txi)
|
||||
}
|
||||
|
||||
// CreateAccount calls CommitStateDB.CreateAccount using the passed in context
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
func (suite *KeeperTestSuite) TestBloomFilter() {
|
||||
// Prepare db for logs
|
||||
tHash := ethcmn.BytesToHash([]byte{0x1})
|
||||
suite.app.EvmKeeper.Prepare(suite.ctx, tHash, 0)
|
||||
suite.app.EvmKeeper.Prepare(suite.ctx, tHash, ethcmn.Hash{}, 0)
|
||||
contractAddress := ethcmn.BigToAddress(big.NewInt(1))
|
||||
log := ethtypes.Log{Address: contractAddress, Topics: []ethcmn.Hash{}}
|
||||
|
||||
@@ -230,7 +230,6 @@ func (suite *KeeperTestSuite) TestStateDB_Logs() {
|
||||
suite.Require().Empty(dbLogs, tc.name)
|
||||
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, tc.log)
|
||||
tc.log.Index = 0 // reset index
|
||||
suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name)
|
||||
|
||||
//resets state but checking to see if storekey still persists.
|
||||
@@ -360,8 +359,7 @@ func (suite *KeeperTestSuite) TestSuiteDB_Prepare() {
|
||||
bhash := ethcmn.BytesToHash([]byte("bhash"))
|
||||
txi := 1
|
||||
|
||||
suite.app.EvmKeeper.Prepare(suite.ctx, thash, txi)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBlockHash(bhash)
|
||||
suite.app.EvmKeeper.Prepare(suite.ctx, thash, bhash, txi)
|
||||
|
||||
suite.Require().Equal(txi, suite.app.EvmKeeper.TxIndex(suite.ctx))
|
||||
suite.Require().Equal(bhash, suite.app.EvmKeeper.BlockHash(suite.ctx))
|
||||
|
||||
Reference in New Issue
Block a user