diff --git a/app/abci.go b/app/abci.go index bcc8c3cbb8..5f518d15ed 100644 --- a/app/abci.go +++ b/app/abci.go @@ -3,8 +3,10 @@ package app import ( "context" "crypto/sha256" + "math/big" "time" + "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/app/legacyabci" "github.com/sei-protocol/sei-chain/sei-cosmos/tasks" "github.com/sei-protocol/sei-chain/sei-cosmos/telemetry" @@ -12,7 +14,6 @@ import ( sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" "github.com/sei-protocol/sei-chain/sei-cosmos/types/legacytm" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" "github.com/sei-protocol/sei-chain/utils/metrics" ) @@ -104,22 +105,25 @@ func (app *App) CheckTx(ctx context.Context, req *abci.RequestCheckTxV2) *abci.R Priority: txCtx.Priority(), GasEstimated: int64(gInfo.GasEstimate), //nolint:gosec }, - CheckTxCallback: txCtx.CheckTxCallback(), - EVMNonce: txCtx.EVMNonce(), - EVMSenderAddress: txCtx.EVMSenderAddress(), - IsEVM: txCtx.IsEVM(), - Priority: txCtx.Priority(), - } - if txCtx.PendingTxChecker() != nil { - res.IsPending = utils.Some(txCtx.PendingTxChecker()) - } - if txCtx.ExpireTxHandler() != nil { - res.ExpireTxHandler = utils.Some(txCtx.ExpireTxHandler()) + EVMNonce: txCtx.EVMNonce(), + EVMSenderAddress: txCtx.EVMSenderAddress(), + IsEVM: txCtx.IsEVM(), + Priority: txCtx.Priority(), + EVMRequiredBalance: txCtx.EVMRequiredBalance(), } return res } +func (app *App) EvmNonce(addr common.Address) uint64 { + return app.EvmKeeper.GetNonce(app.GetCheckCtx(), addr) +} + +func (app *App) EvmBalance(addr common.Address) *big.Int { + ctx := app.GetCheckCtx() + return app.EvmKeeper.GetBalance(ctx, app.EvmKeeper.GetSeiAddressOrDefault(ctx, addr)) +} + func (app *App) DeliverTx(ctx sdk.Context, req abci.RequestDeliverTxV2, tx sdk.Tx, checksum [32]byte) abci.ResponseDeliverTx { defer metrics.MeasureDeliverTxDuration(time.Now()) // ensure we carry the initial context from tracer here diff --git a/app/ante/evm_checktx.go b/app/ante/evm_checktx.go index 02a0e53dc8..c2596d4719 100644 --- a/app/ante/evm_checktx.go +++ b/app/ante/evm_checktx.go @@ -19,11 +19,8 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" upgradekeeper "github.com/sei-protocol/sei-chain/sei-cosmos/x/upgrade/keeper" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" - tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/sei-chain/utils" "github.com/sei-protocol/sei-chain/utils/helpers" - "github.com/sei-protocol/sei-chain/utils/metrics" evmante "github.com/sei-protocol/sei-chain/x/evm/ante" "github.com/sei-protocol/sei-chain/x/evm/derived" evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" @@ -41,7 +38,7 @@ func EvmCheckTxAnte( tx sdk.Tx, upgradeKeeper *upgradekeeper.Keeper, ek *evmkeeper.Keeper, - latestCtxGetter func() sdk.Context, + _ func() sdk.Context, ) (returnCtx sdk.Context, returnErr error) { chainID := ek.ChainID(ctx) if err := EvmStatelessChecks(ctx, tx, chainID); err != nil { @@ -66,7 +63,7 @@ func EvmCheckTxAnte( return ctx, err } - ctx, err = CheckNonce(ctx, latestCtxGetter, ek, etx, evmAddr, seiAddr) + ctx, err = CheckNonce(ctx, ek, etx, evmAddr) if err != nil { return ctx, err } @@ -283,7 +280,7 @@ func EvmCheckAndChargeFees(ctx sdk.Context, sender common.Address, ek *evmkeeper return stateDB, nil } -func CheckNonce(ctx sdk.Context, latestCtxGetter func() sdk.Context, ek *evmkeeper.Keeper, etx *ethtypes.Transaction, evmAddr common.Address, seiAddr sdk.AccAddress) (sdk.Context, error) { +func CheckNonce(ctx sdk.Context, ek *evmkeeper.Keeper, etx *ethtypes.Transaction, evmAddr common.Address) (sdk.Context, error) { fee := new(big.Int).Mul(etx.GasPrice(), new(big.Int).SetUint64(etx.Gas())) if etx.Value() != nil { fee = new(big.Int).Add(fee, etx.Value()) @@ -294,53 +291,7 @@ func CheckNonce(ctx sdk.Context, latestCtxGetter func() sdk.Context, ek *evmkeep if txNonce < nextNonce { return ctx, sdkerrors.ErrWrongSequence } - ctx = ctx.WithCheckTxCallback(func(priority int64) { - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - ek.AddPendingNonce(txHash, evmAddr, etx.Nonce(), priority) - metrics.IncrementPendingNonce("added") - }) - - // if the mempool expires a transaction, this handler is invoked - ctx = ctx.WithExpireTxHandler(func() { - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - ek.RemovePendingNonce(txHash) - metrics.IncrementPendingNonce("expired") - }) - - if txNonce > nextNonce { - // transaction shall be added to mempool as a pending transaction - ctx = ctx.WithPendingTxChecker(func() abci.PendingTxCheckerResponse { - latestCtx := latestCtxGetter() - - // nextNonceToBeMined is the next nonce that will be mined - // geth calls SetNonce(n+1) after a transaction is mined - nextNonceToBeMined := ek.GetNonce(latestCtx, evmAddr) - - // nextPendingNonce is the minimum nonce a user may send without stomping on an already-sent - // nonce, including non-mined or pending transactions - // If a user skips a nonce [1,2,4], then this will be the value of that hole (e.g., 3) - nextPendingNonce := ek.CalculateNextNonce(latestCtx, evmAddr, true) - - if txNonce < nextNonceToBeMined { - // this nonce has already been mined, we cannot accept it again - metrics.IncrementPendingNonce("rejected") - return abci.Rejected - } else if txNonce < nextPendingNonce { - // check if the sender still has enough funds to pay for gas - balance := ek.GetBalance(latestCtx, seiAddr) - if balance.Cmp(fee) < 0 { - // not enough funds. Go back to pending as it may obtain sufficient funds later. - return abci.Pending - } - // this nonce is allowed to process as it is part of the - // consecutive nonces from nextNonceToBeMined to nextPendingNonce - // This logic allows multiple nonces from an account to be processed in a block. - metrics.IncrementPendingNonce("accepted") - return abci.Accepted - } - return abci.Pending - }) - } + ctx = ctx.WithEVMRequiredBalance(fee) return ctx, nil } diff --git a/app/app.go b/app/app.go index 6bcef5afab..6873de58d1 100644 --- a/app/app.go +++ b/app/app.go @@ -2356,11 +2356,6 @@ func (app *App) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig } -// RegisterTxService implements the Application.RegisterTxService method. -func (app *App) RegisterTxService(clientCtx client.Context) { - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) -} - func (app *App) RPCContextProvider(i int64) sdk.Context { if i == evmrpc.LatestCtxHeight { ctx := app.GetCheckCtx() @@ -2385,9 +2380,10 @@ func (app *App) RPCContextProvider(i int64) sdk.Context { return ctx.WithIsEVM(true).WithTraceMode(true).WithIsCheckTx(false) } -// RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *App) RegisterTendermintService(clientCtx client.Context) { - tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +// RegisterTendermintService implements the Application.RegisterLocalServices method. +func (app *App) RegisterLocalServices(node client.LocalClient, txConfig client.TxConfig) { + authtx.RegisterTxService(app.GRPCQueryRouter(), node, txConfig, app.Simulate, app.interfaceRegistry) + tmservice.RegisterTendermintService(app.GRPCQueryRouter(), node, app.interfaceRegistry) txConfigProvider := func(height int64) client.TxConfig { if app.ChainID != "pacific-1" { return app.encodingConfig.TxConfig @@ -2400,7 +2396,7 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) { } if app.evmRPCConfig.HTTPEnabled { - evmHTTPServer, err := evmrpc.NewEVMHTTPServer(app.evmRPCConfig, clientCtx.Client, &app.EvmKeeper, app.BeginBlockKeepers, app.BaseApp, app.TracerAnteHandler, app.RPCContextProvider, txConfigProvider, DefaultNodeHome, app.GetStateStore(), nil) + evmHTTPServer, err := evmrpc.NewEVMHTTPServer(app.evmRPCConfig, node, &app.EvmKeeper, app.BeginBlockKeepers, app.BaseApp, app.TracerAnteHandler, app.RPCContextProvider, txConfigProvider, DefaultNodeHome, app.GetStateStore(), nil) if err != nil { panic(err) } @@ -2413,7 +2409,7 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) { } if app.evmRPCConfig.WSEnabled { - evmWSServer, err := evmrpc.NewEVMWebSocketServer(app.evmRPCConfig, clientCtx.Client, &app.EvmKeeper, app.BeginBlockKeepers, app.BaseApp, app.TracerAnteHandler, app.RPCContextProvider, txConfigProvider, DefaultNodeHome, app.GetStateStore()) + evmWSServer, err := evmrpc.NewEVMWebSocketServer(app.evmRPCConfig, node, &app.EvmKeeper, app.BeginBlockKeepers, app.BaseApp, app.TracerAnteHandler, app.RPCContextProvider, txConfigProvider, DefaultNodeHome, app.GetStateStore()) if err != nil { panic(err) } diff --git a/docker/localnode/config/config.toml b/docker/localnode/config/config.toml index 43fd4af3b0..2caf572247 100644 --- a/docker/localnode/config/config.toml +++ b/docker/localnode/config/config.toml @@ -2,10 +2,6 @@ ### Main Base Config Options ### ####################################################################### -# TCP or UNIX socket address of the ABCI application, -# or the name of an ABCI application compiled in with the Tendermint binary -proxy-app = "tcp://127.0.0.1:26658" - # A custom human readable name for this node moniker = "sei-node-0" @@ -59,9 +55,6 @@ genesis-file = "config/genesis.json" # Path to the JSON file containing the private key to use for node authentication in the p2p protocol node-key-file = "config/node_key.json" -# Mechanism to connect to the ABCI application: socket | grpc -abci = "socket" - ####################################################### ### Priv Validator Configuration ### ####################################################### diff --git a/docker/rpcnode/config/config.toml b/docker/rpcnode/config/config.toml index 6f2acc7f24..a280b91989 100644 --- a/docker/rpcnode/config/config.toml +++ b/docker/rpcnode/config/config.toml @@ -2,10 +2,6 @@ ### Main Base Config Options ### ####################################################################### -# TCP or UNIX socket address of the ABCI application, -# or the name of an ABCI application compiled in with the Tendermint binary -proxy-app = "tcp://127.0.0.1:26658" - # A custom human readable name for this node moniker = "sei-rpc-node" @@ -59,9 +55,6 @@ genesis-file = "config/genesis.json" # Path to the JSON file containing the private key to use for node authentication in the p2p protocol node-key-file = "config/node_key.json" -# Mechanism to connect to the ABCI application: socket | grpc -abci = "socket" - ####################################################### ### Priv Validator Configuration ### ####################################################### diff --git a/docs/migration/seiv2_config_migration.md b/docs/migration/seiv2_config_migration.md index da8e3a936d..5dde0f69db 100644 --- a/docs/migration/seiv2_config_migration.md +++ b/docs/migration/seiv2_config_migration.md @@ -42,10 +42,6 @@ pending-ttl-num-blocks = 0 ### Main Base Config Options ### ####################################################################### -# TCP or UNIX socket address of the ABCI application, -# or the name of an ABCI application compiled in with the Tendermint binary -proxy-app = "tcp://127.0.0.1:26658" - # A custom human readable name for this node moniker = "demo" @@ -99,9 +95,6 @@ genesis-file = "config/genesis.json" # Path to the JSON file containing the private key to use for node authentication in the p2p protocol node-key-file = "config/node_key.json" -# Mechanism to connect to the ABCI application: socket | grpc -abci = "socket" - ####################################################### ### Priv Validator Configuration ### ####################################################### diff --git a/evmrpc/association.go b/evmrpc/association.go index 60a03cd694..dd9fe909ea 100644 --- a/evmrpc/association.go +++ b/evmrpc/association.go @@ -14,14 +14,13 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/types" "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" ) type AssociationAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig @@ -31,7 +30,7 @@ type AssociationAPI struct { } func NewAssociationAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, diff --git a/evmrpc/block.go b/evmrpc/block.go index 2aab45f035..9948c37a55 100644 --- a/evmrpc/block.go +++ b/evmrpc/block.go @@ -20,7 +20,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" banktypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" wasmtypes "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/types" "github.com/sei-protocol/sei-chain/x/evm/keeper" @@ -69,7 +68,7 @@ func encodeGenesisBlock() map[string]any { } type BlockAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig @@ -87,7 +86,7 @@ type SeiBlockAPI struct { isPanicTx func(ctx context.Context, hash common.Hash) (bool, error) } -func NewBlockAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, connectionType ConnectionType, watermarks *WatermarkManager, globalBlockCache BlockCache, cacheCreationMutex *sync.Mutex) *BlockAPI { +func NewBlockAPI(tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, connectionType ConnectionType, watermarks *WatermarkManager, globalBlockCache BlockCache, cacheCreationMutex *sync.Mutex) *BlockAPI { return &BlockAPI{ tmClient: tmClient, keeper: k, @@ -104,7 +103,7 @@ func NewBlockAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(i } func NewSeiBlockAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, @@ -134,7 +133,7 @@ func NewSeiBlockAPI( } func NewSei2BlockAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, diff --git a/evmrpc/block_txcount_parity_test.go b/evmrpc/block_txcount_parity_test.go index 2220806289..cfe3ae1d9d 100644 --- a/evmrpc/block_txcount_parity_test.go +++ b/evmrpc/block_txcount_parity_test.go @@ -33,6 +33,10 @@ type parityTxCountTMClient struct { block *coretypes.ResultBlock } +func (*parityTxCountTMClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func (c *parityTxCountTMClient) Block(_ context.Context, h *int64) (*coretypes.ResultBlock, error) { if h != nil && *h == parityTestHeight { return c.block, nil diff --git a/evmrpc/filter.go b/evmrpc/filter.go index 6711a5ab59..d2e5c198d6 100644 --- a/evmrpc/filter.go +++ b/evmrpc/filter.go @@ -20,7 +20,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/sei-chain/x/evm/keeper" @@ -243,7 +242,7 @@ func (h *logMergeHeap) Pop() interface{} { } type FilterAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient filtersMu sync.RWMutex filters map[ethrpc.ID]filter toDelete chan ethrpc.ID @@ -268,7 +267,7 @@ type EventItemDataWrapper struct { } func NewFilterAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, @@ -716,7 +715,7 @@ func (a *FilterAPI) Cleanup() { } type LogFetcher struct { - tmClient rpcclient.Client + tmClient client.LocalClient k *keeper.Keeper txConfigProvider func(int64) client.TxConfig ctxProvider func(int64) sdk.Context diff --git a/evmrpc/height_availability_test.go b/evmrpc/height_availability_test.go index 68a6dda94d..6b25334029 100644 --- a/evmrpc/height_availability_test.go +++ b/evmrpc/height_availability_test.go @@ -29,6 +29,10 @@ type heightTestClient struct { latest int64 } +func (*heightTestClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func newHeightTestClient(highHeight, earliest, latest int64) *heightTestClient { return &heightTestClient{ Client: mock.Client{}, diff --git a/evmrpc/info.go b/evmrpc/info.go index b3e1cfd16b..40f11ad3af 100644 --- a/evmrpc/info.go +++ b/evmrpc/info.go @@ -14,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" "github.com/sei-protocol/sei-chain/x/evm/keeper" evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" @@ -25,7 +24,7 @@ const defaultPriorityFeePerGas = 1000000000 // 1gwei const defaultThresholdPercentage = 80 // 80% type InfoAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig @@ -36,7 +35,7 @@ type InfoAPI struct { watermarks *WatermarkManager } -func NewInfoAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, homeDir string, maxBlocks int64, connectionType ConnectionType, txDecoder sdk.TxDecoder, watermarks *WatermarkManager) *InfoAPI { +func NewInfoAPI(tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, homeDir string, maxBlocks int64, connectionType ConnectionType, txDecoder sdk.TxDecoder, watermarks *WatermarkManager) *InfoAPI { return &InfoAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, txConfigProvider: txConfigProvider, homeDir: homeDir, connectionType: connectionType, maxBlocks: maxBlocks, txDecoder: txDecoder, watermarks: watermarks} } diff --git a/evmrpc/net.go b/evmrpc/net.go index e9f2e27a72..58e68f371f 100644 --- a/evmrpc/net.go +++ b/evmrpc/net.go @@ -4,20 +4,20 @@ import ( "context" "fmt" + "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/time" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/x/evm/keeper" ) type NetAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context connectionType ConnectionType } -func NewNetAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, connectionType ConnectionType) *NetAPI { +func NewNetAPI(tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, connectionType ConnectionType) *NetAPI { return &NetAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, connectionType: connectionType} } diff --git a/evmrpc/send.go b/evmrpc/send.go index 431fc5596d..9f09118f0f 100644 --- a/evmrpc/send.go +++ b/evmrpc/send.go @@ -19,14 +19,13 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/types" "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" ) type SendAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient txConfigProvider func(int64) client.TxConfig sendConfig *SendConfig keeper *keeper.Keeper @@ -41,7 +40,7 @@ type SendConfig struct { } func NewSendAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, txConfigProvider func(int64) client.TxConfig, sendConfig *SendConfig, k *keeper.Keeper, diff --git a/evmrpc/server.go b/evmrpc/server.go index 91ad926ace..3acf8b3c1d 100644 --- a/evmrpc/server.go +++ b/evmrpc/server.go @@ -15,7 +15,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/sei-protocol/sei-chain/sei-db/db_engine/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" evmCfg "github.com/sei-protocol/sei-chain/x/evm/config" "github.com/sei-protocol/sei-chain/x/evm/keeper" ) @@ -35,7 +34,7 @@ type EVMServer interface { func NewEVMHTTPServer( config evmrpcconfig.Config, - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, beginBlockKeepers legacyabci.BeginBlockKeepers, app *baseapp.BaseApp, @@ -229,7 +228,7 @@ func NewEVMHTTPServer( func NewEVMWebSocketServer( config evmrpcconfig.Config, - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, beginBlockKeepers legacyabci.BeginBlockKeepers, app *baseapp.BaseApp, diff --git a/evmrpc/setup_test.go b/evmrpc/setup_test.go index 4d03f2e4e0..c611f8945a 100644 --- a/evmrpc/setup_test.go +++ b/evmrpc/setup_test.go @@ -119,6 +119,10 @@ type MockClient struct { latestOverride int64 } +func (*MockClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func NewMockClientWithLatest(latest int64) *MockClient { return &MockClient{latestOverride: latest} } diff --git a/evmrpc/simulate.go b/evmrpc/simulate.go index 42b60efdd6..14a04daaf7 100644 --- a/evmrpc/simulate.go +++ b/evmrpc/simulate.go @@ -34,7 +34,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/sei-chain/utils" @@ -59,7 +58,7 @@ func NewSimulationAPI( keeper *keeper.Keeper, beginBlockKeepers legacyabci.BeginBlockKeepers, txConfigProvider func(int64) client.TxConfig, - tmClient rpcclient.Client, + tmClient client.LocalClient, config *SimulateConfig, app *baseapp.BaseApp, antehandler sdk.AnteHandler, @@ -230,7 +229,7 @@ type Backend struct { ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig keeper *keeper.Keeper - tmClient rpcclient.Client + tmClient client.LocalClient config *SimulateConfig app *baseapp.BaseApp beginBlockKeepers legacyabci.BeginBlockKeepers @@ -245,7 +244,7 @@ func NewBackend( keeper *keeper.Keeper, beginBlockKeepers legacyabci.BeginBlockKeepers, txConfigProvider func(int64) client.TxConfig, - tmClient rpcclient.Client, + tmClient client.LocalClient, config *SimulateConfig, app *baseapp.BaseApp, antehandler sdk.AnteHandler, diff --git a/evmrpc/simulate_test.go b/evmrpc/simulate_test.go index 5b06ef3e43..d7674b103e 100644 --- a/evmrpc/simulate_test.go +++ b/evmrpc/simulate_test.go @@ -63,7 +63,7 @@ func TestEstimateGas(t *testing.T) { // transfer _, from := testkeeper.MockAddressPair() _, to := testkeeper.MockAddressPair() - txArgs := map[string]interface{}{ + txArgs := map[string]any{ "from": from.Hex(), "to": to.Hex(), "value": "0x10", @@ -73,13 +73,13 @@ func TestEstimateGas(t *testing.T) { amts := sdk.NewCoins(sdk.NewCoin(EVMKeeper.GetBaseDenom(Ctx), sdk.NewInt(20))) EVMKeeper.BankKeeper().MintCoins(Ctx, types.ModuleName, amts) EVMKeeper.BankKeeper().SendCoinsFromModuleToAccount(Ctx, types.ModuleName, sdk.AccAddress(from[:]), amts) - resObj := sendRequestGood(t, "estimateGas", txArgs, nil, map[string]interface{}{}) + resObj := sendRequestGood(t, "estimateGas", txArgs, nil, map[string]any{}) result := resObj["result"].(string) require.Equal(t, "0x5208", result) // 21000 - resObj = sendRequestGood(t, "estimateGas", txArgs, "latest", map[string]interface{}{}) + resObj = sendRequestGood(t, "estimateGas", txArgs, "latest", map[string]any{}) result = resObj["result"].(string) require.Equal(t, "0x5208", result) // 21000 - resObj = sendRequestGood(t, "estimateGas", txArgs, "0x1", map[string]interface{}{}) + resObj = sendRequestGood(t, "estimateGas", txArgs, "0x1", map[string]any{}) result = resObj["result"].(string) require.Equal(t, "0x5208", result) // 21000 @@ -94,7 +94,7 @@ func TestEstimateGas(t *testing.T) { input, err := abi.Pack("set", big.NewInt(20)) require.Nil(t, err) EVMKeeper.SetCode(Ctx, contractAddr, bz) - txArgs = map[string]interface{}{ + txArgs = map[string]any{ "from": from.Hex(), "to": contractAddr.Hex(), "value": "0x0", @@ -102,7 +102,7 @@ func TestEstimateGas(t *testing.T) { "chainId": fmt.Sprintf("%#x", EVMKeeper.ChainID(Ctx)), "input": fmt.Sprintf("%#x", input), } - resObj = sendRequestGood(t, "estimateGas", txArgs, nil, map[string]interface{}{}) + resObj = sendRequestGood(t, "estimateGas", txArgs, nil, map[string]any{}) result = resObj["result"].(string) require.Equal(t, "0x54ac", result) // 21497 @@ -138,7 +138,7 @@ func TestChainConfigReflectsSstoreParam(t *testing.T) { } encodingCfg := app.MakeEncodingConfig() - tmClient := &mock.Client{} + tmClient := &MockClient{} watermarks := evmrpc.NewWatermarkManager(tmClient, ctxProvider, nil, testApp.EvmKeeper.ReceiptStore()) backend := evmrpc.NewBackend( ctxProvider, @@ -182,7 +182,7 @@ func TestEstimateGasAfterCalls(t *testing.T) { input, err := abi.Pack("get") require.Nil(t, err) EVMKeeper.SetCode(Ctx, contractAddr, bz) - txArgs := map[string]interface{}{ + txArgs := map[string]any{ "from": from.Hex(), "to": contractAddr.Hex(), "value": "0x0", @@ -190,7 +190,7 @@ func TestEstimateGasAfterCalls(t *testing.T) { "chainId": fmt.Sprintf("%#x", EVMKeeper.ChainID(Ctx)), "input": fmt.Sprintf("%#x", input), } - callArgs := map[string]interface{}{ + callArgs := map[string]any{ "from": from.Hex(), "to": contractAddr.Hex(), "value": "0x0", @@ -198,7 +198,7 @@ func TestEstimateGasAfterCalls(t *testing.T) { "chainId": fmt.Sprintf("%#x", EVMKeeper.ChainID(Ctx)), "input": fmt.Sprintf("%#x", call), } - resObj := sendRequestGood(t, "estimateGasAfterCalls", txArgs, []interface{}{callArgs}, nil, map[string]interface{}{}) + resObj := sendRequestGood(t, "estimateGasAfterCalls", txArgs, []any{callArgs}, nil, map[string]any{}) result := resObj["result"].(string) require.Equal(t, "0x536d", result) // 21357 for get @@ -219,7 +219,7 @@ func TestCreateAccessList(t *testing.T) { input, err := abi.Pack("set", big.NewInt(20)) require.Nil(t, err) EVMKeeper.SetCode(Ctx, contractAddr, bz) - txArgs := map[string]interface{}{ + txArgs := map[string]any{ "from": from.Hex(), "to": contractAddr.Hex(), "value": "0x0", @@ -231,11 +231,11 @@ func TestCreateAccessList(t *testing.T) { EVMKeeper.BankKeeper().MintCoins(Ctx, types.ModuleName, amts) EVMKeeper.BankKeeper().SendCoinsFromModuleToAccount(Ctx, types.ModuleName, sdk.AccAddress(from[:]), amts) resObj := sendRequestGood(t, "createAccessList", txArgs, "latest") - result := resObj["result"].(map[string]interface{}) - require.Equal(t, []interface{}{}, result["accessList"]) // the code uses MSTORE which does not trace access list + result := resObj["result"].(map[string]any) + require.Equal(t, []any{}, result["accessList"]) // the code uses MSTORE which does not trace access list resObj = sendRequestBad(t, "createAccessList", txArgs, "latest") - result = resObj["error"].(map[string]interface{}) + result = resObj["error"].(map[string]any) require.Equal(t, "error block", result["message"]) Ctx = Ctx.WithBlockHeight(8) @@ -255,7 +255,7 @@ func TestCall(t *testing.T) { input, err := abi.Pack("set", big.NewInt(20)) require.Nil(t, err) EVMKeeper.SetCode(Ctx, contractAddr, bz) - txArgs := map[string]interface{}{ + txArgs := map[string]any{ "from": from.Hex(), "to": contractAddr.Hex(), "value": "0x0", @@ -263,7 +263,7 @@ func TestCall(t *testing.T) { "chainId": fmt.Sprintf("%#x", EVMKeeper.ChainID(Ctx)), "input": fmt.Sprintf("%#x", input), } - resObj := sendRequestGood(t, "call", txArgs, nil, map[string]interface{}{}, map[string]interface{}{}) + resObj := sendRequestGood(t, "call", txArgs, nil, map[string]any{}, map[string]any{}) result := resObj["result"].(string) require.Equal(t, "0x608060405234801561000f575f80fd5b506004361061003f575f3560e01c806360fe47b1146100435780636d4ce63c1461005f5780639c3674fc1461007d575b5f80fd5b61005d6004803603810190610058919061010a565b610087565b005b6100676100c7565b6040516100749190610144565b60405180910390f35b6100856100cf565b005b805f819055507f0de2d86113046b9e8bb6b785e96a6228f6803952bf53a40b68a36dce316218c1816040516100bc9190610144565b60405180910390a150565b5f8054905090565b5f80fd5b5f80fd5b5f819050919050565b6100e9816100d7565b81146100f3575f80fd5b50565b5f81359050610104816100e0565b92915050565b5f6020828403121561011f5761011e6100d3565b5b5f61012c848285016100f6565b91505092915050565b61013e816100d7565b82525050565b5f6020820190506101575f830184610135565b9291505056fea2646970667358221220bb55137839ea2afda11ab2d30ad07fee30bb9438caaa46e30ccd1053ed72439064736f6c63430008150033", result) @@ -274,7 +274,7 @@ func TestEthCallHighAmount(t *testing.T) { Ctx = Ctx.WithBlockHeight(1) _, from := testkeeper.MockAddressPair() _, to := testkeeper.MockAddressPair() - txArgs := map[string]interface{}{ + txArgs := map[string]any{ "from": from.Hex(), "to": to.Hex(), "value": "0x0", @@ -282,11 +282,11 @@ func TestEthCallHighAmount(t *testing.T) { "chainId": fmt.Sprintf("%#x", EVMKeeper.ChainID(Ctx)), } - overrides := map[string]map[string]interface{}{ + overrides := map[string]map[string]any{ from.Hex(): {"balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, } resObj := sendRequestGood(t, "call", txArgs, "latest", overrides) - errMap := resObj["error"].(map[string]interface{}) + errMap := resObj["error"].(map[string]any) result := errMap["message"] require.Equal(t, result, "error: balance override overflow") @@ -867,6 +867,10 @@ type fixedBlockClient struct { block *coretypes.ResultBlock } +func (c *fixedBlockClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func (c *fixedBlockClient) Block(_ context.Context, _ *int64) (*coretypes.ResultBlock, error) { return c.block, nil } diff --git a/evmrpc/state.go b/evmrpc/state.go index b9b3de5f83..d90544cde1 100644 --- a/evmrpc/state.go +++ b/evmrpc/state.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" gigacachekv "github.com/sei-protocol/sei-chain/giga/deps/store" + "github.com/sei-protocol/sei-chain/sei-cosmos/client" "github.com/sei-protocol/sei-chain/sei-cosmos/store/cachekv" "github.com/sei-protocol/sei-chain/sei-cosmos/store/prefix" "github.com/sei-protocol/sei-chain/sei-cosmos/store/tracekv" @@ -19,7 +20,6 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/crypto" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/state" @@ -29,14 +29,14 @@ import ( var errNoProofCapableQueryableKVStore = errors.New("cannot find a proof-capable queryable KV store") type StateAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context connectionType ConnectionType watermarks *WatermarkManager } -func NewStateAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, connectionType ConnectionType, watermarks *WatermarkManager) *StateAPI { +func NewStateAPI(tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, connectionType ConnectionType, watermarks *WatermarkManager) *StateAPI { return &StateAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, connectionType: connectionType, watermarks: watermarks} } diff --git a/evmrpc/subscribe.go b/evmrpc/subscribe.go index 1d31d4283c..50ca4d96ae 100644 --- a/evmrpc/subscribe.go +++ b/evmrpc/subscribe.go @@ -13,8 +13,8 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/rpc" + "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/sei-chain/utils" @@ -25,7 +25,7 @@ const SleepInterval = 5 * time.Second const NewHeadsListenerBuffer = 10 type SubscriptionAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient subscriptionManager *SubscriptionManager subscriptonConfig *SubscriptionConfig @@ -40,7 +40,7 @@ type SubscriptionConfig struct { newHeadLimit uint64 } -func NewSubscriptionAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, logFetcher *LogFetcher, subscriptionConfig *SubscriptionConfig, filterConfig *FilterConfig, connectionType ConnectionType) *SubscriptionAPI { +func NewSubscriptionAPI(tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, logFetcher *LogFetcher, subscriptionConfig *SubscriptionConfig, filterConfig *FilterConfig, connectionType ConnectionType) *SubscriptionAPI { logFetcher.filterConfig = filterConfig api := &SubscriptionAPI{ tmClient: tmClient, @@ -240,10 +240,10 @@ type SubscriptionManager struct { subMu sync.Mutex NextID SubscriberID SubscriptionInfo map[SubscriberID]SubInfo - tmClient rpcclient.Client + tmClient client.LocalClient } -func NewSubscriptionManager(tmClient rpcclient.Client) *SubscriptionManager { +func NewSubscriptionManager(tmClient client.LocalClient) *SubscriptionManager { return &SubscriptionManager{ subMu: sync.Mutex{}, NextID: 1, diff --git a/evmrpc/tests/mock_client.go b/evmrpc/tests/mock_client.go index c9c99d48aa..75257b47d3 100644 --- a/evmrpc/tests/mock_client.go +++ b/evmrpc/tests/mock_client.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/evmrpc" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmbytes "github.com/sei-protocol/sei-chain/sei-tendermint/libs/bytes" @@ -35,6 +36,10 @@ type MockClient struct { mockedGenesis *coretypes.ResultGenesis } +func (c *MockClient) EvmNextPendingNonce(_ common.Address) uint64 { + return 0 +} + func (c *MockClient) Block(_ context.Context, h *int64) (*coretypes.ResultBlock, error) { if c.mockedBlockResults != nil { blockNum := int64(-1) diff --git a/evmrpc/tracers.go b/evmrpc/tracers.go index e2609d5282..be9ac9da49 100644 --- a/evmrpc/tracers.go +++ b/evmrpc/tracers.go @@ -22,7 +22,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/baseapp" "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/state" ) @@ -36,7 +35,7 @@ var errTraceConcurrencyLimit = errors.New("trace request rejected due to concurr type DebugAPI struct { tracersAPI *tracers.API - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig @@ -93,7 +92,7 @@ type SeiDebugAPI struct { } func NewDebugAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, beginBlockKeepers legacyabci.BeginBlockKeepers, ctxProvider func(int64) sdk.Context, @@ -134,7 +133,7 @@ func NewDebugAPI( } func NewSeiDebugAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, beginBlockKeepers legacyabci.BeginBlockKeepers, ctxProvider func(int64) sdk.Context, diff --git a/evmrpc/tx.go b/evmrpc/tx.go index 9e798d0083..b18c2f1074 100644 --- a/evmrpc/tx.go +++ b/evmrpc/tx.go @@ -22,7 +22,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" receiptpkg "github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" wasmtypes "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/types" @@ -36,7 +35,7 @@ const UnconfirmedTxQueryMaxPage = 20 const UnconfirmedTxQueryPerPage = 30 type TransactionAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig @@ -54,7 +53,7 @@ type SeiTransactionAPI struct { } func NewTransactionAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, @@ -78,7 +77,7 @@ func NewTransactionAPI( } func NewSeiTransactionAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, @@ -323,9 +322,9 @@ func (t *TransactionAPI) GetTransactionCount(ctx context.Context, address common recordMetricsWithError(ctx, "eth_getTransactionCount", t.connectionType, startTime, returnErr, recover()) }() - var pending bool if blockNrOrHash.BlockHash == nil && *blockNrOrHash.BlockNumber == rpc.PendingBlockNumber { - pending = true + nonce := t.tmClient.EvmNextPendingNonce(address) + return (*hexutil.Uint64)(&nonce), nil } height, err := t.watermarks.ResolveHeight(ctx, blockNrOrHash) @@ -336,7 +335,7 @@ func (t *TransactionAPI) GetTransactionCount(ctx context.Context, address common if err := CheckVersion(sdkCtx, t.keeper); err != nil { return nil, err } - nonce := t.keeper.CalculateNextNonce(sdkCtx, address, pending) + nonce := t.keeper.GetNonce(sdkCtx, address) return (*hexutil.Uint64)(&nonce), nil } diff --git a/evmrpc/txpool.go b/evmrpc/txpool.go index 0ca557d561..dd6a6f7871 100644 --- a/evmrpc/txpool.go +++ b/evmrpc/txpool.go @@ -9,13 +9,12 @@ import ( "github.com/sei-protocol/sei-chain/evmrpc/rpcutils" "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/types" ) type TxPoolAPI struct { - tmClient rpcclient.Client + tmClient client.LocalClient keeper *keeper.Keeper ctxProvider func(int64) sdk.Context txConfigProvider func(int64) client.TxConfig @@ -32,7 +31,7 @@ func NewTxPoolConfig(maxNumTxs int) *TxPoolConfig { return &TxPoolConfig{maxNumTxs: maxNumTxs} } -func NewTxPoolAPI(tmClient rpcclient.Client, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, txPoolConfig *TxPoolConfig, connectionType ConnectionType) *TxPoolAPI { +func NewTxPoolAPI(tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, txPoolConfig *TxPoolConfig, connectionType ConnectionType) *TxPoolAPI { return &TxPoolAPI{tmClient: tmClient, keeper: k, ctxProvider: ctxProvider, txConfigProvider: txConfigProvider, txPoolConfig: txPoolConfig, connectionType: connectionType} } diff --git a/evmrpc/utils.go b/evmrpc/utils.go index af6c83ba5a..2510697f0c 100644 --- a/evmrpc/utils.go +++ b/evmrpc/utils.go @@ -28,7 +28,6 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" banktypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/bank/types" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/bytes" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" wasmtypes "github.com/sei-protocol/sei-chain/sei-wasmd/x/wasm/types" utilmetrics "github.com/sei-protocol/sei-chain/utils/metrics" @@ -47,7 +46,7 @@ const Pacific1EVMLaunchHeight int64 = 79123881 var ErrBlockNotFoundByHash = errors.New("block not found by hash") // GetBlockNumberByNrOrHash returns the height of the block with the given number or hash. -func GetBlockNumberByNrOrHash(ctx context.Context, tmClient rpcclient.Client, wm *WatermarkManager, blockNrOrHash rpc.BlockNumberOrHash) (*int64, error) { +func GetBlockNumberByNrOrHash(ctx context.Context, tmClient client.LocalClient, wm *WatermarkManager, blockNrOrHash rpc.BlockNumberOrHash) (*int64, error) { if blockNrOrHash.BlockHash != nil { // Synthetic genesis from eth_getBlockByNumber("0x0") is not stored under this hash in Tendermint. if *blockNrOrHash.BlockHash == genesisBlockHash { @@ -64,7 +63,7 @@ func GetBlockNumberByNrOrHash(ctx context.Context, tmClient rpcclient.Client, wm return getBlockNumber(ctx, tmClient, *blockNrOrHash.BlockNumber) } -func getBlockNumber(ctx context.Context, tmClient rpcclient.Client, number rpc.BlockNumber) (*int64, error) { +func getBlockNumber(ctx context.Context, tmClient client.LocalClient, number rpc.BlockNumber) (*int64, error) { var numberPtr *int64 switch number { case rpc.SafeBlockNumber, rpc.FinalizedBlockNumber, rpc.LatestBlockNumber, rpc.PendingBlockNumber: @@ -140,7 +139,7 @@ func getAddressPrivKeyMap(kb keyring.Keyring) map[string]*ecdsa.PrivateKey { return res } -func blockResultsWithRetry(ctx context.Context, client rpcclient.Client, height *int64) (*coretypes.ResultBlockResults, error) { +func blockResultsWithRetry(ctx context.Context, client client.LocalClient, height *int64) (*coretypes.ResultBlockResults, error) { blockRes, err := client.BlockResults(ctx, height) if err != nil { // retry once, since application DB and block DB are not committed atomically so it's possible for @@ -154,7 +153,7 @@ func blockResultsWithRetry(ctx context.Context, client rpcclient.Client, height return blockRes, err } -func blockByNumberWithRetry(ctx context.Context, client rpcclient.Client, height *int64, maxRetries int) (*coretypes.ResultBlock, error) { +func blockByNumberWithRetry(ctx context.Context, client client.LocalClient, height *int64, maxRetries int) (*coretypes.ResultBlock, error) { blockRes, err := client.Block(ctx, height) var retryCount = 0 for err != nil && retryCount < maxRetries { @@ -174,11 +173,11 @@ func blockByNumberWithRetry(ctx context.Context, client rpcclient.Client, height return blockRes, err } -func blockByHash(ctx context.Context, client rpcclient.Client, hash bytes.HexBytes) (*coretypes.ResultBlock, error) { +func blockByHash(ctx context.Context, client client.LocalClient, hash bytes.HexBytes) (*coretypes.ResultBlock, error) { return blockByHashWithRetry(ctx, client, hash, 0) } -func blockByHashWithRetry(ctx context.Context, client rpcclient.Client, hash bytes.HexBytes, maxRetries int) (*coretypes.ResultBlock, error) { +func blockByHashWithRetry(ctx context.Context, client client.LocalClient, hash bytes.HexBytes, maxRetries int) (*coretypes.ResultBlock, error) { blockRes, err := client.BlockByHash(ctx, hash) var retryCount = 0 for err != nil && retryCount < maxRetries { diff --git a/evmrpc/watermark_manager.go b/evmrpc/watermark_manager.go index d751d3421d..03d0cf89d3 100644 --- a/evmrpc/watermark_manager.go +++ b/evmrpc/watermark_manager.go @@ -6,10 +6,10 @@ import ( "fmt" "github.com/ethereum/go-ethereum/rpc" + "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/sei-protocol/sei-chain/sei-db/db_engine/types" "github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" ) @@ -24,14 +24,14 @@ var ErrBlockHeightNotYetAvailable = errors.New("block height not yet available") // requests only target heights where all backing data sources are fully // synchronized. type WatermarkManager struct { - tmClient rpcclient.Client + tmClient client.LocalClient ctxProvider func(int64) sdk.Context stateStore types.StateStore receiptStore receipt.ReceiptStore } func NewWatermarkManager( - tmClient rpcclient.Client, + tmClient client.LocalClient, ctxProvider func(int64) sdk.Context, stateStore types.StateStore, receiptStore receipt.ReceiptStore, @@ -231,7 +231,7 @@ func (m *WatermarkManager) ensureWithinWatermarks(height, earliest, latest int64 func blockByNumberRespectingWatermarks( ctx context.Context, - client rpcclient.Client, + client client.LocalClient, wm *WatermarkManager, heightPtr *int64, maxRetries int, @@ -252,7 +252,7 @@ func blockByNumberRespectingWatermarks( func blockByHashRespectingWatermarks( ctx context.Context, - client rpcclient.Client, + client client.LocalClient, wm *WatermarkManager, hash []byte, maxRetries int, diff --git a/evmrpc/watermark_manager_test.go b/evmrpc/watermark_manager_test.go index 025ef8ba05..6a9fbec102 100644 --- a/evmrpc/watermark_manager_test.go +++ b/evmrpc/watermark_manager_test.go @@ -162,6 +162,10 @@ type fakeTMClient struct { genesis *coretypes.ResultGenesis } +func (*fakeTMClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func (f *fakeTMClient) Status(context.Context) (*coretypes.ResultStatus, error) { if f.statusErr != nil { return nil, f.statusErr diff --git a/sei-cosmos/baseapp/baseapp.go b/sei-cosmos/baseapp/baseapp.go index c72a37cb8e..eb78c00a8d 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -2,12 +2,14 @@ package baseapp import ( "fmt" + "math/big" "reflect" "strings" "sync" "time" "github.com/armon/go-metrics" + "github.com/ethereum/go-ethereum/common" "github.com/gogo/protobuf/proto" "github.com/sei-protocol/sei-chain/sei-cosmos/codec/types" cryptotypes "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/types" @@ -79,6 +81,14 @@ type ( DeliverTxHook func(sdk.Context, sdk.Tx, [32]byte, sdk.DeliverTxHookInput) ) +func (app *BaseApp) EvmNonce(_ common.Address) uint64 { + return 0 +} + +func (app *BaseApp) EvmBalance(_ common.Address) *big.Int { + return big.NewInt(0) +} + // BaseApp reflects the ABCI application implementation. type BaseApp struct { // initialized on creation diff --git a/sei-cosmos/baseapp/grpcrouter.go b/sei-cosmos/baseapp/grpcrouter.go index 8028274b4f..795cf376b4 100644 --- a/sei-cosmos/baseapp/grpcrouter.go +++ b/sei-cosmos/baseapp/grpcrouter.go @@ -122,8 +122,5 @@ func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.In qrt.interfaceRegistry = interfaceRegistry // Once we have an interface registry, we can register the interface // registry reflection gRPC service. - reflection.RegisterReflectionServiceServer( - qrt, - reflection.NewReflectionServiceServer(interfaceRegistry), - ) + reflection.RegisterReflectionServiceServer(qrt, reflection.NewReflectionServiceServer(interfaceRegistry)) } diff --git a/sei-cosmos/client/broadcast.go b/sei-cosmos/client/broadcast.go index 4db88cf038..1aaeb1a663 100644 --- a/sei-cosmos/client/broadcast.go +++ b/sei-cosmos/client/broadcast.go @@ -44,26 +44,32 @@ func (e ErrMempoolIsFull) Error() string { ) } -// BroadcastTx broadcasts a transactions either synchronously or asynchronously -// based on the context parameters. The result of the broadcast is parsed into +// BroadcastTx broadcasts a transactions either synchronously or asynchronously. The result of the broadcast is parsed into // an intermediate structure which is logged if the context has a logger // defined. -func (ctx Context) BroadcastTx(txBytes []byte) (res *sdk.TxResponse, err error) { - switch ctx.BroadcastMode { +func BroadcastTx(ctx context.Context, node Client, broadcastMode string, txBytes []byte) (*sdk.TxResponse, error) { + switch broadcastMode { case flags.BroadcastSync: - res, err = ctx.BroadcastTxSync(txBytes) - + res, err := node.BroadcastTxSync(ctx, txBytes) + if errRes := CheckTendermintError(err, txBytes); errRes != nil { + return errRes, nil + } + return sdk.NewResponseFormatBroadcastTx(res), err case flags.BroadcastAsync: - res, err = ctx.BroadcastTxAsync(txBytes) - + res, err := node.BroadcastTxAsync(ctx, txBytes) + if errRes := CheckTendermintError(err, txBytes); errRes != nil { + return errRes, nil + } + return sdk.NewResponseFormatBroadcastTx(res), err case flags.BroadcastBlock: - res, err = ctx.BroadcastTxCommit(txBytes) - + res, err := node.BroadcastTxCommit(ctx, txBytes) + if errRes := CheckTendermintError(err, txBytes); errRes != nil { + return errRes, nil + } + return sdk.NewResponseFormatBroadcastTxCommit(res), err default: - return nil, fmt.Errorf("unsupported return type %s; supported types: sync, async, block", ctx.BroadcastMode) + return nil, fmt.Errorf("unsupported return type %s; supported types: sync, async, block", broadcastMode) } - - return res, err } // CheckTendermintError checks if the error returned from BroadcastTx is a @@ -109,78 +115,17 @@ func CheckTendermintError(err error, tx tmtypes.Tx) *sdk.TxResponse { } } -// BroadcastTxCommit broadcasts transaction bytes to a Tendermint node and -// waits for a commit. An error is only returned if there is no RPC node -// connection or if broadcasting fails. -// -// NOTE: This should ideally not be used as the request may timeout but the tx -// may still be included in a block. Use BroadcastTxAsync or BroadcastTxSync -// instead. -func (ctx Context) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error) { - node, err := ctx.GetNode() - if err != nil { - return nil, err - } - - res, err := node.BroadcastTxCommit(context.Background(), txBytes) - if err == nil { - return sdk.NewResponseFormatBroadcastTxCommit(res), nil - } - - if errRes := CheckTendermintError(err, txBytes); errRes != nil { - return errRes, nil - } - return sdk.NewResponseFormatBroadcastTxCommit(res), err -} - -// BroadcastTxSync broadcasts transaction bytes to a Tendermint node -// synchronously (i.e. returns after CheckTx execution). -func (ctx Context) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { - node, err := ctx.GetNode() - if err != nil { - return nil, err - } - - res, err := node.BroadcastTxSync(context.Background(), txBytes) - if errRes := CheckTendermintError(err, txBytes); errRes != nil { - return errRes, nil - } - - return sdk.NewResponseFormatBroadcastTx(res), err -} - -// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node -// asynchronously (i.e. returns immediately). -func (ctx Context) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { - node, err := ctx.GetNode() - if err != nil { - return nil, err - } - - res, err := node.BroadcastTxAsync(context.Background(), txBytes) - if errRes := CheckTendermintError(err, txBytes); errRes != nil { - return errRes, nil - } - - return sdk.NewResponseFormatBroadcastTx(res), err -} - // TxServiceBroadcast is a helper function to broadcast a Tx with the correct gRPC types // from the tx service. Calls `clientCtx.BroadcastTx` under the hood. -func TxServiceBroadcast(grpcCtx context.Context, clientCtx Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { +func TxServiceBroadcast(grpcCtx context.Context, node Client, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { if req == nil || req.TxBytes == nil { return nil, status.Error(codes.InvalidArgument, "invalid empty tx") } - - clientCtx = clientCtx.WithBroadcastMode(normalizeBroadcastMode(req.Mode)) - resp, err := clientCtx.BroadcastTx(req.TxBytes) + resp, err := BroadcastTx(grpcCtx, node, normalizeBroadcastMode(req.Mode), req.TxBytes) if err != nil { return nil, err } - - return &tx.BroadcastTxResponse{ - TxResponse: resp, - }, nil + return &tx.BroadcastTxResponse{TxResponse: resp}, nil } // normalizeBroadcastMode converts a broadcast mode into a normalized string diff --git a/sei-cosmos/client/broadcast_test.go b/sei-cosmos/client/broadcast_test.go index ec04fcc0e1..917c5b882d 100644 --- a/sei-cosmos/client/broadcast_test.go +++ b/sei-cosmos/client/broadcast_test.go @@ -32,15 +32,9 @@ func (c MockClient) BroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*ctypes return nil, c.err } -func CreateContextWithErrorAndMode(err error, mode string) Context { - return Context{ - Client: MockClient{err: err}, - BroadcastMode: mode, - } -} - // Test the correct code is returned when func TestBroadcastError(t *testing.T) { + ctx := t.Context() errors := map[error]uint32{ ErrTxInCache: sdkerrors.ErrTxInMempoolCache.ABCICode(), ErrTxTooLarge{}: sdkerrors.ErrTxTooLarge.ABCICode(), @@ -58,8 +52,8 @@ func TestBroadcastError(t *testing.T) { for _, mode := range modes { for err, code := range errors { - ctx := CreateContextWithErrorAndMode(err, mode) - resp, returnedErr := ctx.BroadcastTx(txBytes) + node := MockClient{err: err} + resp, returnedErr := BroadcastTx(ctx, node, mode, txBytes) require.NoError(t, returnedErr) require.Equal(t, code, resp.Code) require.NotEmpty(t, resp.Codespace) diff --git a/sei-cosmos/client/cmd.go b/sei-cosmos/client/cmd.go index 526cd26f2d..470ad9794d 100644 --- a/sei-cosmos/client/cmd.go +++ b/sei-cosmos/client/cmd.go @@ -133,7 +133,7 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont } } - if clientCtx.Client == nil || flagSet.Changed(flags.FlagNode) { + if !clientCtx.Client.IsPresent() || flagSet.Changed(flags.FlagNode) { rpcURI, _ := flagSet.GetString(flags.FlagNode) if rpcURI != "" { clientCtx = clientCtx.WithNodeURI(rpcURI) diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index b6a70f605e..d16fecf3ba 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -7,11 +7,13 @@ import ( "io" "os" + "github.com/ethereum/go-ethereum/common" "github.com/spf13/viper" "gopkg.in/yaml.v2" "github.com/gogo/protobuf/proto" + "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-cosmos/codec" @@ -20,11 +22,15 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" ) -// Context implements a typical context created in SDK modules for transaction -// handling and queries. +type Client = rpcclient.Client +type LocalClient interface { + Client + EvmNextPendingNonce(addr common.Address) uint64 +} + type Context struct { FromAddress sdk.AccAddress - Client rpcclient.Client + Client utils.Option[rpcclient.Client] ChainID string // Deprecated: Codec codec will be changed to Codec: codec.Codec JSONCodec codec.JSONCodec @@ -136,8 +142,8 @@ func (ctx Context) WithHeight(height int64) Context { // WithClient returns a copy of the context with an updated RPC client // instance. -func (ctx Context) WithClient(client rpcclient.Client) Context { - ctx.Client = client +func (ctx Context) WithClient(client Client) Context { + ctx.Client = utils.Some(client) return ctx } @@ -286,7 +292,7 @@ func (ctx Context) PrintProto(toPrint proto.Message) error { // PrintObjectLegacy is a variant of PrintProto that doesn't require a proto.Message type // and uses amino JSON encoding. // Deprecated: It will be removed in the near future! -func (ctx Context) PrintObjectLegacy(toPrint interface{}) error { +func (ctx Context) PrintObjectLegacy(toPrint any) error { out, err := ctx.LegacyAmino.MarshalAsJSON(toPrint) if err != nil { return err @@ -297,7 +303,7 @@ func (ctx Context) PrintObjectLegacy(toPrint interface{}) error { func (ctx Context) printOutput(out []byte) error { if ctx.OutputFormat == "text" { // handle text format by decoding and re-encoding JSON as YAML - var j interface{} + var j any err := json.Unmarshal(out, &j) if err != nil { diff --git a/sei-cosmos/client/grpc/tmservice/block.go b/sei-cosmos/client/grpc/tmservice/block.go deleted file mode 100644 index e24b98f390..0000000000 --- a/sei-cosmos/client/grpc/tmservice/block.go +++ /dev/null @@ -1,34 +0,0 @@ -package tmservice - -import ( - "context" - - tmproto "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" - ctypes "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" - - "github.com/sei-protocol/sei-chain/sei-cosmos/client" -) - -func getBlock(ctx context.Context, clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) { - // get the node - node, err := clientCtx.GetNode() - if err != nil { - return nil, err - } - - return node.Block(ctx, height) -} - -func GetProtoBlock(ctx context.Context, clientCtx client.Context, height *int64) (tmproto.BlockID, *tmproto.Block, error) { - block, err := getBlock(ctx, clientCtx, height) - if err != nil { - return tmproto.BlockID{}, nil, err - } - protoBlock, err := block.Block.ToProto() - if err != nil { - return tmproto.BlockID{}, nil, err - } - protoBlockId := block.BlockID.ToProto() - - return protoBlockId, protoBlock, nil -} diff --git a/sei-cosmos/client/grpc/tmservice/service.go b/sei-cosmos/client/grpc/tmservice/service.go index a1d9007a64..fd66f4b484 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -14,11 +14,26 @@ import ( cryptotypes "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/types" qtypes "github.com/sei-protocol/sei-chain/sei-cosmos/types/query" "github.com/sei-protocol/sei-chain/sei-cosmos/version" + tmproto "github.com/sei-protocol/sei-chain/sei-tendermint/proto/tendermint/types" ) +func GetProtoBlock(ctx context.Context, node client.Client, height *int64) (tmproto.BlockID, *tmproto.Block, error) { + block, err := node.Block(ctx, height) + if err != nil { + return tmproto.BlockID{}, nil, err + } + protoBlock, err := block.Block.ToProto() + if err != nil { + return tmproto.BlockID{}, nil, err + } + protoBlockId := block.BlockID.ToProto() + + return protoBlockId, protoBlock, nil +} + // This is the struct that we will implement all the handlers on. type queryServer struct { - clientCtx client.Context + node client.Client interfaceRegistry codectypes.InterfaceRegistry } @@ -26,16 +41,16 @@ var _ ServiceServer = queryServer{} var _ codectypes.UnpackInterfacesMessage = &GetLatestValidatorSetResponse{} // NewQueryServer creates a new tendermint query server. -func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer { +func NewQueryServer(node client.Client, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer { return queryServer{ - clientCtx: clientCtx, + node: node, interfaceRegistry: interfaceRegistry, } } // GetSyncing implements ServiceServer.GetSyncing func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) { - status, err := getNodeStatus(ctx, s.clientCtx) + status, err := s.node.Status(ctx) if err != nil { return nil, err } @@ -46,13 +61,13 @@ func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*Get // GetLatestBlock implements ServiceServer.GetLatestBlock func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { - status, err := getBlock(ctx, s.clientCtx, nil) + block, err := s.node.Block(ctx, nil) if err != nil { return nil, err } - protoBlockID := status.BlockID.ToProto() - protoBlock, err := status.Block.ToProto() + protoBlockID := block.BlockID.ToProto() + protoBlock, err := block.Block.ToProto() if err != nil { return nil, err } @@ -65,7 +80,7 @@ func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockReques // GetBlockByHeight implements ServiceServer.GetBlockByHeight func (s queryServer) GetBlockByHeight(ctx context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) { - chainHeight, err := rpc.GetChainHeight(s.clientCtx) + chainHeight, err := rpc.GetChainHeight(ctx, s.node) if err != nil { return nil, err } @@ -74,7 +89,7 @@ func (s queryServer) GetBlockByHeight(ctx context.Context, req *GetBlockByHeight return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length") } - protoBlockID, protoBlock, err := GetProtoBlock(ctx, s.clientCtx, &req.Height) + protoBlockID, protoBlock, err := GetProtoBlock(ctx, s.node, &req.Height) if err != nil { return nil, err } @@ -90,7 +105,7 @@ func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestVa if err != nil { return nil, err } - return validatorsOutput(ctx, s.clientCtx, nil, page, limit) + return validatorsOutput(ctx, s.node, nil, page, limit) } func (m *GetLatestValidatorSetResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { @@ -110,15 +125,14 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida if err != nil { return nil, err } - - chainHeight, err := rpc.GetChainHeight(s.clientCtx) + chainHeight, err := rpc.GetChainHeight(ctx, s.node) if err != nil { return nil, status.Error(codes.Internal, "failed to parse chain height") } if req.Height > chainHeight { return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length") } - r, err := validatorsOutput(ctx, s.clientCtx, &req.Height, page, limit) + r, err := validatorsOutput(ctx, s.node, &req.Height, page, limit) if err != nil { return nil, err } @@ -129,8 +143,8 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida }, nil } -func validatorsOutput(ctx context.Context, cctx client.Context, height *int64, page, limit int) (*GetLatestValidatorSetResponse, error) { - vs, err := rpc.GetValidators(ctx, cctx, height, &page, &limit) +func validatorsOutput(ctx context.Context, node client.Client, height *int64, page, limit int) (*GetLatestValidatorSetResponse, error) { + vs, err := rpc.GetValidators(ctx, node, height, &page, &limit) if err != nil { return nil, err } @@ -158,8 +172,7 @@ func validatorsOutput(ctx context.Context, cctx client.Context, height *int64, p // GetNodeInfo implements ServiceServer.GetNodeInfo func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) { - status, err := getNodeStatus(ctx, s.clientCtx) - + status, err := s.node.Status(ctx) if err != nil { return nil, err } @@ -196,13 +209,10 @@ func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) ( // RegisterTendermintService registers the tendermint queries on the gRPC router. func RegisterTendermintService( qrt gogogrpc.Server, - clientCtx client.Context, + node client.LocalClient, interfaceRegistry codectypes.InterfaceRegistry, ) { - RegisterServiceServer( - qrt, - NewQueryServer(clientCtx, interfaceRegistry), - ) + RegisterServiceServer(qrt, NewQueryServer(node, interfaceRegistry)) } // RegisterGRPCGatewayRoutes mounts the tendermint service's GRPC-gateway routes on the diff --git a/sei-cosmos/client/grpc/tmservice/status.go b/sei-cosmos/client/grpc/tmservice/status.go deleted file mode 100644 index dd4fb0172e..0000000000 --- a/sei-cosmos/client/grpc/tmservice/status.go +++ /dev/null @@ -1,17 +0,0 @@ -package tmservice - -import ( - "context" - - ctypes "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" - - "github.com/sei-protocol/sei-chain/sei-cosmos/client" -) - -func getNodeStatus(ctx context.Context, clientCtx client.Context) (*ctypes.ResultStatus, error) { - node, err := clientCtx.GetNode() - if err != nil { - return &ctypes.ResultStatus{}, err - } - return node.Status(ctx) -} diff --git a/sei-cosmos/client/grpc_query.go b/sei-cosmos/client/grpc_query.go index a44da6e197..3f5c9691cf 100644 --- a/sei-cosmos/client/grpc_query.go +++ b/sei-cosmos/client/grpc_query.go @@ -24,7 +24,7 @@ var _ gogogrpc.ClientConn = Context{} var protoCodec = encoding.GetCodec(proto.Name) // Invoke implements the grpc ClientConn.Invoke method -func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) { +func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) error { // Two things can happen here: // 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly, // 2. or we are querying for state, in which case we call ABCI's Query. @@ -41,13 +41,17 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req) } - broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, reqProto) + node, err := ctx.GetNode() if err != nil { return err } - *res = *broadcastRes - return err + broadcastRes, err := TxServiceBroadcast(grpcCtx, node, reqProto) + if err != nil { + return err + } + *res = *broadcastRes + return nil } // Case 2. Querying state. @@ -83,8 +87,7 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i return err } - err = protoCodec.Unmarshal(res.Value, reply) - if err != nil { + if err := protoCodec.Unmarshal(res.Value, reply); err != nil { return err } diff --git a/sei-cosmos/client/query.go b/sei-cosmos/client/query.go index 6f2c0fe689..37b511d39d 100644 --- a/sei-cosmos/client/query.go +++ b/sei-cosmos/client/query.go @@ -20,12 +20,11 @@ import ( // GetNode returns an RPC client. If the context's client is not defined, an // error is returned. -func (ctx Context) GetNode() (rpcclient.Client, error) { - if ctx.Client == nil { - return nil, errors.New("no RPC client is defined in offline mode") +func (ctx Context) GetNode() (Client, error) { + if c, ok := ctx.Client.Get(); ok { + return c, nil } - - return ctx.Client, nil + return nil, errors.New("no RPC client is defined in offline mode") } // Query performs a query to a Tendermint node with the provided path. diff --git a/sei-cosmos/client/rpc/block.go b/sei-cosmos/client/rpc/block.go index f21449a25b..a229c985f1 100644 --- a/sei-cosmos/client/rpc/block.go +++ b/sei-cosmos/client/rpc/block.go @@ -74,19 +74,12 @@ func getBlock(clientCtx client.Context, height *int64) ([]byte, error) { } // get the current blockchain height -func GetChainHeight(clientCtx client.Context) (int64, error) { - node, err := clientCtx.GetNode() - if err != nil { - return -1, err - } - - status, err := node.Status(context.Background()) +func GetChainHeight(ctx context.Context, node client.Client) (int64, error) { + status, err := node.Status(ctx) if err != nil { return -1, err } - - height := status.SyncInfo.LatestBlockHeight - return height, nil + return status.SyncInfo.LatestBlockHeight, nil } // REST handler to get a block @@ -101,7 +94,11 @@ func BlockRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - chainHeight, err := GetChainHeight(clientCtx) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + chainHeight, err := GetChainHeight(r.Context(), node) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, "failed to parse chain height") return diff --git a/sei-cosmos/client/rpc/status.go b/sei-cosmos/client/rpc/status.go index d631e2ef8f..e0a3508b92 100644 --- a/sei-cosmos/client/rpc/status.go +++ b/sei-cosmos/client/rpc/status.go @@ -1,7 +1,6 @@ package rpc import ( - "context" "net/http" "github.com/spf13/cobra" @@ -44,8 +43,11 @@ func StatusCommand() *cobra.Command { if err != nil { return err } - - status, err := getNodeStatus(clientCtx) + node, err := clientCtx.GetNode() + if err != nil { + return err + } + status, err := node.Status(cmd.Context()) if err != nil { return err } @@ -86,15 +88,6 @@ func StatusCommand() *cobra.Command { return cmd } -func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) { - node, err := clientCtx.GetNode() - if err != nil { - return &ctypes.ResultStatus{}, err - } - - return node.Status(context.Background()) -} - // NodeInfoResponse defines a response type that contains node status and version // information. type NodeInfoResponse struct { @@ -106,7 +99,11 @@ type NodeInfoResponse struct { // REST handler for node info func NodeInfoRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - status, err := getNodeStatus(clientCtx) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + status, err := node.Status(r.Context()) if rest.CheckInternalServerError(w, err) { return } @@ -128,11 +125,14 @@ type SyncingResponse struct { // REST handler for node syncing func NodeSyncingRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - status, err := getNodeStatus(clientCtx) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + status, err := node.Status(r.Context()) if rest.CheckInternalServerError(w, err) { return } - rest.PostProcessResponseBare(w, clientCtx, SyncingResponse{Syncing: status.SyncInfo.CatchingUp}) } } diff --git a/sei-cosmos/client/rpc/validators.go b/sei-cosmos/client/rpc/validators.go index 5612efe2bc..10722a4171 100644 --- a/sei-cosmos/client/rpc/validators.go +++ b/sei-cosmos/client/rpc/validators.go @@ -48,8 +48,12 @@ func ValidatorCommand() *cobra.Command { page, _ := cmd.Flags().GetInt(flags.FlagPage) limit, _ := cmd.Flags().GetInt(flags.FlagLimit) - - result, err := GetValidators(cmd.Context(), clientCtx, height, &page, &limit) + // get the node + node, err := clientCtx.GetNode() + if err != nil { + return err + } + result, err := GetValidators(cmd.Context(), node, height, &page, &limit) if err != nil { return err } @@ -118,13 +122,7 @@ func validatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) { } // GetValidators from client -func GetValidators(ctx context.Context, clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) { - // get the node - node, err := clientCtx.GetNode() - if err != nil { - return ResultValidatorsOutput{}, err - } - +func GetValidators(ctx context.Context, node client.Client, height *int64, page, limit *int) (ResultValidatorsOutput, error) { validatorsRes, err := node.Validators(ctx, height, page, limit) if err != nil { return ResultValidatorsOutput{}, err @@ -166,8 +164,11 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse block height") return } - - chainHeight, err := GetChainHeight(clientCtx) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + chainHeight, err := GetChainHeight(r.Context(), node) if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, "failed to parse chain height") return @@ -177,7 +178,7 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - output, err := GetValidators(r.Context(), clientCtx, &height, &page, &limit) + output, err := GetValidators(r.Context(), node, &height, &page, &limit) if rest.CheckInternalServerError(w, err) { return } @@ -193,8 +194,11 @@ func LatestValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFu rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse pagination parameters") return } - - output, err := GetValidators(r.Context(), clientCtx, nil, &page, &limit) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + output, err := GetValidators(r.Context(), node, nil, &page, &limit) if rest.CheckInternalServerError(w, err) { return } diff --git a/sei-cosmos/client/tx/tx.go b/sei-cosmos/client/tx/tx.go index dbc708905d..457137113e 100644 --- a/sei-cosmos/client/tx/tx.go +++ b/sei-cosmos/client/tx/tx.go @@ -26,14 +26,14 @@ import ( // GenerateOrBroadcastTxCLI will either generate and print and unsigned transaction // or sign it and broadcast it returning an error upon failure. -func GenerateOrBroadcastTxCLI(clientCtx client.Context, flagSet *pflag.FlagSet, msgs ...sdk.Msg) error { +func GenerateOrBroadcastTxCLI(ctx context.Context, clientCtx client.Context, flagSet *pflag.FlagSet, msgs ...sdk.Msg) error { txf := NewFactoryCLI(clientCtx, flagSet) - return GenerateOrBroadcastTxWithFactory(clientCtx, txf, msgs...) + return GenerateOrBroadcastTxWithFactory(ctx, clientCtx, txf, msgs...) } // GenerateOrBroadcastTxWithFactory will either generate and print and unsigned transaction // or sign it and broadcast it returning an error upon failure. -func GenerateOrBroadcastTxWithFactory(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { +func GenerateOrBroadcastTxWithFactory(ctx context.Context, clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { // Validate all msgs before generating or broadcasting the tx. // We were calling ValidateBasic separately in each CLI handler before. // Right now, we're factorizing that call inside this function. @@ -48,7 +48,7 @@ func GenerateOrBroadcastTxWithFactory(clientCtx client.Context, txf Factory, msg return GenerateTx(clientCtx, txf, msgs...) } - return BroadcastTx(clientCtx, txf, msgs...) + return broadcastTx(ctx, clientCtx, txf, msgs...) } // GenerateTx will generate an unsigned transaction and print it to the writer @@ -86,7 +86,7 @@ func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { // BroadcastTx attempts to generate, sign and broadcast a transaction with the // given set of messages. It will also simulate gas requirements if necessary. // It will return an error upon failure. -func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { +func broadcastTx(ctx context.Context, clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { txf, err := prepareFactory(clientCtx, txf) if err != nil { return err @@ -145,7 +145,11 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error { } // broadcast to a Tendermint node - res, err := clientCtx.BroadcastTx(txBytes) + node, err := clientCtx.GetNode() + if err != nil { + return err + } + res, err := client.BroadcastTx(ctx, node, clientCtx.BroadcastMode, txBytes) if err != nil { return err } diff --git a/sei-cosmos/server/README.md b/sei-cosmos/server/README.md index 31926f69c1..dcb8632271 100644 --- a/sei-cosmos/server/README.md +++ b/sei-cosmos/server/README.md @@ -63,8 +63,8 @@ literal that exists in the `server.Context`. All the possible options an applica may use and provide to the construction process are defined by the `StartCmd` and by the application's config file, `app.toml`. -The application can either be started in-process or as an external process. The -former creates a Tendermint service and the latter creates a Tendermint Node. +The application is started in-process with Tendermint. External ABCI process +support via socket or gRPC has been removed. Under the hood, `StartCmd` will call `GetServerContextFromCmd`, which provides the command access to a `server.Context`. This context provides access to the diff --git a/sei-cosmos/server/api/server.go b/sei-cosmos/server/api/server.go index 79495d1c6f..c1ebf09c70 100644 --- a/sei-cosmos/server/api/server.go +++ b/sei-cosmos/server/api/server.go @@ -128,7 +128,10 @@ func (s *Server) Start(cfg config.Config, apiMetrics *telemetry.Metrics) error { func (s *Server) Close() error { s.mtx.Lock() defer s.mtx.Unlock() - return s.listener.Close() + if s.listener != nil { + return s.listener.Close() + } + return nil } func (s *Server) registerGRPCGatewayRoutes() { diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index df3c2ca9aa..aa48fa4cf0 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -4,11 +4,13 @@ import ( "context" "fmt" "io" + "math/big" "os" "path/filepath" "testing" "time" + "github.com/ethereum/go-ethereum/common" "github.com/gogo/protobuf/grpc" "github.com/golang/protobuf/proto" "github.com/google/orderedcode" @@ -74,6 +76,14 @@ func (m *mockApplication) GetTxPriorityHint(ctx context.Context, req *abci.Reque return &abci.ResponseGetTxPriorityHint{}, nil } +func (m *mockApplication) EvmNonce(common.Address) uint64 { + return 0 +} + +func (m *mockApplication) EvmBalance(common.Address) *big.Int { + return big.NewInt(0) +} + func (m *mockApplication) BeginBlock(ctx context.Context, req *abci.RequestBeginBlock) (*abci.ResponseBeginBlock, error) { return &abci.ResponseBeginBlock{}, nil } @@ -106,11 +116,10 @@ func (m *mockApplication) FinalizeBlock(ctx context.Context, req *abci.RequestFi return &abci.ResponseFinalizeBlock{}, nil } -func (m *mockApplication) RegisterAPIRoutes(*api.Server, serverconfig.APIConfig) {} -func (m *mockApplication) RegisterGRPCServer(grpc.Server) {} -func (m *mockApplication) RegisterTxService(client.Context) {} -func (m *mockApplication) RegisterTendermintService(client.Context) {} -func (m *mockApplication) InplaceTestnetInitialize(cryptotypes.PubKey) {} +func (m *mockApplication) RegisterAPIRoutes(*api.Server, serverconfig.APIConfig) {} +func (m *mockApplication) RegisterGRPCServer(grpc.Server) {} +func (m *mockApplication) RegisterLocalServices(client.LocalClient, client.TxConfig) {} +func (m *mockApplication) InplaceTestnetInitialize(cryptotypes.PubKey) {} // setupTestApp creates a test application with a CommitMultiStore at a specific height func setupTestApp(t *testing.T, height int64) (*mockApplication, string) { diff --git a/sei-cosmos/server/start.go b/sei-cosmos/server/start.go index 417fa51085..8536c5f6f5 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -29,13 +29,11 @@ import ( genesistypes "github.com/sei-protocol/sei-chain/sei-cosmos/types/genesis" "github.com/sei-protocol/sei-chain/sei-cosmos/utils/tracing" tcmd "github.com/sei-protocol/sei-chain/sei-tendermint/cmd/tendermint/commands" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/service" "github.com/sei-protocol/sei-chain/sei-tendermint/node" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/spf13/cobra" "go.opentelemetry.io/otel/sdk/trace" - "google.golang.org/grpc" ) const ( @@ -86,14 +84,12 @@ const ( FlagChainID = "chain-id" ) -// StartCmd runs the service passed in, either stand-alone or in-process with -// Tendermint. +// StartCmd runs the service passed in with Tendermint in-process. func StartCmd(appCreator types.AppCreator, defaultNodeHome string, tracerProviderOptions []trace.TracerProviderOption) *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Run the full node", - Long: `Run the full node application with Tendermint in or out of process. By -default, the application will run with Tendermint in process. + Long: `Run the full node application with Tendermint in process. Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' together. For '--pruning' the options are as follows: @@ -211,8 +207,6 @@ is performed. Note, when enabled, gRPC will also be automatically enabled. func addStartNodeFlags(cmd *cobra.Command, defaultNodeHome string) { cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") - cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") - cmd.Flags().String(flagTransport, "socket", "Transport protocol: socket, grpc") cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") cmd.Flags().String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)") cmd.Flags().IntSlice(FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary") @@ -250,6 +244,15 @@ func addStartNodeFlags(cmd *cobra.Command, defaultNodeHome string) { // add support for all Tendermint-specific command line options tcmd.AddNodeFlags(cmd, NewDefaultContext().Config) + mustMarkDeprecated(cmd, flagAddress, "out-of-process ABCI has been removed; this flag is ignored") + mustMarkDeprecated(cmd, flagTransport, "out-of-process ABCI has been removed; this flag is ignored") +} + +func mustMarkDeprecated(cmd *cobra.Command, name, message string) { + cmd.Flags().String(name, "", "") + if err := cmd.Flags().MarkDeprecated(name, message); err != nil { + panic(err) + } } func startInProcess( @@ -264,7 +267,6 @@ func startInProcess( home := cfg.RootDir goCtx, cancel := context.WithCancel(context.Background()) defer cancel() - var cpuProfileCleanup func() if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { f, err := os.Create(filepath.Clean(cpuProfile)) if err != nil { @@ -275,11 +277,11 @@ func startInProcess( return fmt.Errorf("failed to start CPU Profiler %w", err) } - cpuProfileCleanup = func() { + defer func() { logger.Info("stopping CPU profiler", "profile", cpuProfile) pprof.StopCPUProfile() _ = f.Close() - } + }() } traceWriterFile := ctx.Viper.GetString(flagTraceStore) @@ -300,9 +302,14 @@ func startInProcess( "(SDK v0.45). Please explicitly put the desired minimum-gas-prices in your app.toml.") } app := appCreator(nil, traceWriter, ctx.Config, ctx.Viper) + defer func() { + logger.Info("close any other open resource...") + if err := app.Close(); err != nil { + logger.Error("error closing database", "err", err) + } + }() gRPCOnly := ctx.Viper.GetBool(flagGRPCOnly) - var tmNode service.Service var restartMtx sync.Mutex restartCh := make(chan struct{}) @@ -335,7 +342,7 @@ func startInProcess( gen = genDoc } } - tmNode, err = node.New( + tmNode, err := node.New( goCtx, ctx.Config, restartEvent, @@ -350,26 +357,29 @@ func startInProcess( if err := tmNode.Start(goCtx); err != nil { return fmt.Errorf("error starting node: %w", err) } - } - - // Add the tx service to the gRPC router. We only need to register this - // service if API or gRPC is enabled, and avoid doing so in the general - // case, because it spawns a new local tendermint RPC client. - if (config.API.Enable || config.GRPC.Enable) && tmNode != nil { - localClient, err := local.New(tmNode.(local.NodeService)) - if err != nil { - return err + defer func() { + if tmNode.IsRunning() { + tmNode.Wait() + } + }() + // Add the tx service to the gRPC router. We only need to register this + // service if API or gRPC is enabled, and avoid doing so in the general + // case, because it spawns a new local tendermint RPC client. + if config.API.Enable || config.GRPC.Enable { + localClient, err := local.New(tmNode) + if err != nil { + return err + } + clientCtx = clientCtx.WithClient(localClient) + app.RegisterLocalServices(localClient, clientCtx.TxConfig) } - clientCtx = clientCtx.WithClient(localClient) - - app.RegisterTxService(clientCtx) - app.RegisterTendermintService(clientCtx) } - var apiSrv *api.Server if config.API.Enable { + var apiSrv *api.Server clientCtx := clientCtx.WithHomeDir(home).WithChainID(clientCtx.ChainID) apiSrv = api.New(clientCtx) + defer apiSrv.Close() app.RegisterAPIRoutes(apiSrv, config.API) errCh := make(chan error) @@ -387,23 +397,20 @@ func startInProcess( } } - var ( - grpcSrv *grpc.Server - grpcWebSrv *http.Server - ) - if config.GRPC.Enable { - grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, config.GRPC.Address) + grpcSrv, err := servergrpc.StartGRPCServer(clientCtx, app, config.GRPC.Address) if err != nil { return err } + defer grpcSrv.Stop() if config.GRPCWeb.Enable { - grpcWebSrv, err = servergrpc.StartGRPCWeb(grpcSrv, config) + grpcWebSrv, err := servergrpc.StartGRPCWeb(grpcSrv, config) if err != nil { logger.Error("failed to start grpc-web http server", "err", err) return err } + defer grpcWebSrv.Close() } } @@ -456,33 +463,8 @@ func startInProcess( } } - defer func() { - cancel() - if tmNode.IsRunning() { - tmNode.Wait() - } - - if cpuProfileCleanup != nil { - cpuProfileCleanup() - } - - if apiSrv != nil { - _ = apiSrv.Close() - } - - if grpcSrv != nil { - grpcSrv.Stop() - if grpcWebSrv != nil { - _ = grpcWebSrv.Close() - } - } - - logger.Info("close any other open resource...") - if err := app.Close(); err != nil { - logger.Error("error closing database", "err", err) - } - }() - + // Defer cancelling as the last so that it is called first during unwinding. + defer cancel() // wait for signal capture and gracefully return return WaitForQuitSignals(goCtx, restartCh) } diff --git a/sei-cosmos/server/types/app.go b/sei-cosmos/server/types/app.go index 58934bfe2b..fd617d4e9a 100644 --- a/sei-cosmos/server/types/app.go +++ b/sei-cosmos/server/types/app.go @@ -49,12 +49,8 @@ type ( // server. RegisterGRPCServer(grpc.Server) - // RegisterTxService registers the gRPC Query service for tx (such as tx - // simulation, fetching txs by hash...). - RegisterTxService(clientCtx client.Context) - - // RegisterTendermintService registers the gRPC Query service for tendermint queries. - RegisterTendermintService(clientCtx client.Context) + // RegisterTxService registers RPCs of the local tendermint node. + RegisterLocalServices(node client.LocalClient, txConfig client.TxConfig) // CommitMultiStore Returns the multistore instance CommitMultiStore() sdk.CommitMultiStore diff --git a/sei-cosmos/testutil/network/util.go b/sei-cosmos/testutil/network/util.go index 5b86ffabcf..180b716039 100644 --- a/sei-cosmos/testutil/network/util.go +++ b/sei-cosmos/testutil/network/util.go @@ -71,20 +71,13 @@ func startInProcess(cfg Config, val *Validator) error { val.tmNode = tmNode if val.RPCAddress != "" { - localClient, _ := local.New(tmNode.(local.NodeService)) + localClient, _ := local.New(tmNode) val.RPCClient = localClient - } - - // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. - if val.APIAddress != "" || val.AppConfig.GRPC.Enable { - val.ClientCtx = val.ClientCtx. - WithClient(val.RPCClient) - - // Add the tx service in the gRPC router. - app.RegisterTxService(val.ClientCtx) - - // Add the tendermint queries service in the gRPC router. - app.RegisterTendermintService(val.ClientCtx) + // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. + if val.APIAddress != "" || val.AppConfig.GRPC.Enable { + val.ClientCtx = val.ClientCtx.WithClient(val.RPCClient) + app.RegisterLocalServices(localClient, val.ClientCtx.TxConfig) + } } if val.APIAddress != "" { diff --git a/sei-cosmos/types/context.go b/sei-cosmos/types/context.go index 569725fb8f..fce8f34431 100644 --- a/sei-cosmos/types/context.go +++ b/sei-cosmos/types/context.go @@ -3,6 +3,7 @@ package types import ( "context" fmt "fmt" + "math/big" "time" "github.com/gogo/protobuf/proto" @@ -23,32 +24,30 @@ but please do not over-use it. We try to keep all data structured and standard additions here would be better just to add to the Context struct */ type Context struct { - ctx context.Context - ms MultiStore - nextMs MultiStore // ms of the next height; only used in tracing - nextStoreKeys map[string]struct{} // store key names that should use nextMs - header tmproto.Header - headerHash tmbytes.HexBytes - chainID string - txBytes []byte - txSum [32]byte - voteInfo []abci.VoteInfo - gasMeter GasMeter - gasEstimate uint64 - occEnabled bool - blockGasMeter GasMeter - checkTx bool - recheckTx bool // if recheckTx == true, then checkTx must also be true - minGasPrice DecCoins - consParams *tmproto.ConsensusParams - eventManager *EventManager - evmEventManager *EVMEventManager - priority int64 // The tx priority, only relevant in CheckTx - hasPriority bool // Whether the tx has a priority set - pendingTxChecker abci.PendingTxChecker // Checker for pending transaction, only relevant in CheckTx - checkTxCallback func(int64) // callback to make at the end of CheckTx - deliverTxCallback func(Context) // callback to make at the end of DeliverTx. - expireTxHandler func() // callback that the mempool invokes when a tx is expired + ctx context.Context + ms MultiStore + nextMs MultiStore // ms of the next height; only used in tracing + nextStoreKeys map[string]struct{} // store key names that should use nextMs + header tmproto.Header + headerHash tmbytes.HexBytes + chainID string + txBytes []byte + txSum [32]byte + voteInfo []abci.VoteInfo + gasMeter GasMeter + gasEstimate uint64 + occEnabled bool + blockGasMeter GasMeter + checkTx bool + recheckTx bool // if recheckTx == true, then checkTx must also be true + minGasPrice DecCoins + consParams *tmproto.ConsensusParams + eventManager *EventManager + evmEventManager *EVMEventManager + priority int64 // The tx priority, only relevant in CheckTx + hasPriority bool // Whether the tx has a priority set + deliverTxCallback func(Context) // callback to make at the end of DeliverTx. + evmRequiredBalance *big.Int // Required sender balance for this EVM tx, only relevant in CheckTx. // EVM properties evm bool // EVM transaction flag @@ -150,10 +149,6 @@ func (c Context) Priority() int64 { return c.priority } -func (c Context) ExpireTxHandler() abci.ExpireTxHandler { - return c.expireTxHandler -} - func (c Context) EVMSenderAddress() string { return c.evmSenderAddress } @@ -182,18 +177,17 @@ func (c Context) EVMPrecompileCalledFromDelegateCall() bool { return c.evmPrecompileCalledFromDelegateCall } -func (c Context) PendingTxChecker() abci.PendingTxChecker { - return c.pendingTxChecker -} - -func (c Context) CheckTxCallback() func(int64) { - return c.checkTxCallback -} - func (c Context) DeliverTxCallback() func(Context) { return c.deliverTxCallback } +func (c Context) EVMRequiredBalance() *big.Int { + if c.evmRequiredBalance == nil { + return nil + } + return new(big.Int).Set(c.evmRequiredBalance) +} + func (c Context) MessageIndex() int { return c.messageIndex } @@ -447,23 +441,17 @@ func (c Context) WithEVMPrecompileCalledFromDelegateCall(e bool) Context { return c } -func (c Context) WithPendingTxChecker(checker abci.PendingTxChecker) Context { - c.pendingTxChecker = checker - return c -} - -func (c Context) WithCheckTxCallback(checkTxCallback func(int64)) Context { - c.checkTxCallback = checkTxCallback - return c -} - func (c Context) WithDeliverTxCallback(deliverTxCallback func(Context)) Context { c.deliverTxCallback = deliverTxCallback return c } -func (c Context) WithExpireTxHandler(expireTxHandler func()) Context { - c.expireTxHandler = expireTxHandler +func (c Context) WithEVMRequiredBalance(evmRequiredBalance *big.Int) Context { + if evmRequiredBalance == nil { + c.evmRequiredBalance = nil + return c + } + c.evmRequiredBalance = new(big.Int).Set(evmRequiredBalance) return c } diff --git a/sei-cosmos/x/auth/client/cli/broadcast.go b/sei-cosmos/x/auth/client/cli/broadcast.go index 894b4581b0..92f2be00c9 100644 --- a/sei-cosmos/x/auth/client/cli/broadcast.go +++ b/sei-cosmos/x/auth/client/cli/broadcast.go @@ -44,7 +44,11 @@ $ tx broadcast ./mytxn.json return err } - res, err := clientCtx.BroadcastTx(txBytes) + node, err := clientCtx.GetNode() + if err != nil { + return err + } + res, err := client.BroadcastTx(cmd.Context(), node, clientCtx.BroadcastMode, txBytes) if err != nil { return err } diff --git a/sei-cosmos/x/auth/client/cli/query.go b/sei-cosmos/x/auth/client/cli/query.go index 79a332655a..ea8bc3f7bb 100644 --- a/sei-cosmos/x/auth/client/cli/query.go +++ b/sei-cosmos/x/auth/client/cli/query.go @@ -229,7 +229,11 @@ $ %s query txs --%s 'message.sender=cosmos1...&message.action=withdraw_delegator page, _ := cmd.Flags().GetInt(flags.FlagPage) limit, _ := cmd.Flags().GetInt(flags.FlagLimit) - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, page, limit, "") + node, err := clientCtx.GetNode() + if err != nil { + return err + } + txs, err := authtx.QueryTxsByEvents(cmd.Context(), node, clientCtx.TxConfig, tmEvents, page, limit, "") if err != nil { return err } @@ -268,9 +272,11 @@ $ %s query tx --%s=%s , return err } - typ, _ := cmd.Flags().GetString(flagType) - - switch typ { + node, err := clientCtx.GetNode() + if err != nil { + return err + } + switch typ, _ := cmd.Flags().GetString(flagType); typ { case typeHash: { if args[0] == "" { @@ -278,7 +284,7 @@ $ %s query tx --%s=%s , } // If hash is given, then query the tx by hash. - output, err := authtx.QueryTx(clientCtx, args[0]) + output, err := authtx.QueryTx(cmd.Context(), node, clientCtx.TxConfig, args[0]) if err != nil { return err } @@ -300,7 +306,7 @@ $ %s query tx --%s=%s , tmEvents[i] = fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) } - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, rest.DefaultPage, query.DefaultLimit, "") + txs, err := authtx.QueryTxsByEvents(cmd.Context(), node, clientCtx.TxConfig, tmEvents, rest.DefaultPage, query.DefaultLimit, "") if err != nil { return err } @@ -323,7 +329,7 @@ $ %s query tx --%s=%s , tmEvents := []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeyAccountSequence, args[0]), } - txs, err := authtx.QueryTxsByEvents(clientCtx, tmEvents, rest.DefaultPage, query.DefaultLimit, "") + txs, err := authtx.QueryTxsByEvents(cmd.Context(), node, clientCtx.TxConfig, tmEvents, rest.DefaultPage, query.DefaultLimit, "") if err != nil { return err } diff --git a/sei-cosmos/x/auth/client/rest/query.go b/sei-cosmos/x/auth/client/rest/query.go index a058b67007..66dade03fd 100644 --- a/sei-cosmos/x/auth/client/rest/query.go +++ b/sei-cosmos/x/auth/client/rest/query.go @@ -99,7 +99,11 @@ func QueryTxsRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - searchResult, err := authtx.QueryTxsByEvents(clientCtx, events, page, limit, "") + node, err := clientCtx.GetNode() + if rest.CheckBadRequestError(w, err) { + return + } + searchResult, err := authtx.QueryTxsByEvents(r.Context(), node, clientCtx.TxConfig, events, page, limit, "") if rest.CheckInternalServerError(w, err) { return } @@ -138,7 +142,11 @@ func QueryTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - output, err := authtx.QueryTx(clientCtx, hashHexStr) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + output, err := authtx.QueryTx(r.Context(), node, clientCtx.TxConfig, hashHexStr) if err != nil { if strings.Contains(err.Error(), hashHexStr) { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) @@ -207,7 +215,7 @@ func packStdTxResponse(w http.ResponseWriter, clientCtx client.Context, txRes *s // checkAminoMarshalError checks if there are errors with marshalling non-amino // txs with amino. -func checkAminoMarshalError(ctx client.Context, resp interface{}, grpcEndPoint string) error { +func checkAminoMarshalError(ctx client.Context, resp any, grpcEndPoint string) error { // LegacyAmino used intentionally here to handle the SignMode errors marshaler := ctx.LegacyAmino diff --git a/sei-cosmos/x/auth/tx/query.go b/sei-cosmos/x/auth/tx/query.go index 9076df6bee..8eb1c45204 100644 --- a/sei-cosmos/x/auth/tx/query.go +++ b/sei-cosmos/x/auth/tx/query.go @@ -21,7 +21,7 @@ import ( // concatenated with an 'AND' operand. It returns a slice of Info object // containing txs and metadata. An error is returned if the query fails. // If an empty string is provided it will order txs by asc -func QueryTxsByEvents(clientCtx client.Context, events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) { +func QueryTxsByEvents(ctx context.Context, node client.Client, txConfig client.TxConfig, events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) { if len(events) == 0 { return nil, errors.New("must declare at least one event to search") } @@ -37,24 +37,18 @@ func QueryTxsByEvents(clientCtx client.Context, events []string, page, limit int // XXX: implement ANY query := strings.Join(events, " AND ") - node, err := clientCtx.GetNode() - if err != nil { - return nil, err - } - // TODO: this may not always need to be proven // https://github.com/cosmos/cosmos-sdk/issues/6807 - resTxs, err := node.TxSearch(context.Background(), query, true, &page, &limit, orderBy) + resTxs, err := node.TxSearch(ctx, query, true, &page, &limit, orderBy) if err != nil { return nil, err } - - resBlocks, err := getBlocksForTxResults(clientCtx, resTxs.Txs) + resBlocks, err := getBlocksForTxResults(ctx, node, resTxs.Txs) if err != nil { return nil, err } - txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks) + txs, err := formatTxResults(txConfig, resTxs.Txs, resBlocks) if err != nil { return nil, err } @@ -72,30 +66,25 @@ func QueryTxsByEvents(clientCtx client.Context, events []string, page, limit int // QueryTx queries for a single transaction by a hash string in hex format. An // error is returned if the transaction does not exist or cannot be queried. -func QueryTx(clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, error) { +func QueryTx(ctx context.Context, node client.Client, txConfig client.TxConfig, hashHexStr string) (*sdk.TxResponse, error) { hash, err := hex.DecodeString(hashHexStr) if err != nil { return nil, err } - node, err := clientCtx.GetNode() - if err != nil { - return nil, err - } - //TODO: this may not always need to be proven // https://github.com/cosmos/cosmos-sdk/issues/6807 - resTx, err := node.Tx(context.Background(), hash, true) + resTx, err := node.Tx(ctx, hash, true) if err != nil { return nil, err } - resBlocks, err := getBlocksForTxResults(clientCtx, []*ctypes.ResultTx{resTx}) + resBlocks, err := getBlocksForTxResults(ctx, node, []*ctypes.ResultTx{resTx}) if err != nil { return nil, err } - out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height]) + out, err := mkTxResult(txConfig, resTx, resBlocks[resTx.Height]) if err != nil { return out, err } @@ -117,17 +106,12 @@ func formatTxResults(txConfig client.TxConfig, resTxs []*ctypes.ResultTx, resBlo return out, nil } -func getBlocksForTxResults(clientCtx client.Context, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { - node, err := clientCtx.GetNode() - if err != nil { - return nil, err - } - +func getBlocksForTxResults(ctx context.Context, node client.Client, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { resBlocks := make(map[int64]*ctypes.ResultBlock) for _, resTx := range resTxs { if _, ok := resBlocks[resTx.Height]; !ok { - resBlock, err := node.Block(context.Background(), &resTx.Height) + resBlock, err := node.Block(ctx, &resTx.Height) if err != nil { return nil, err } diff --git a/sei-cosmos/x/auth/tx/service.go b/sei-cosmos/x/auth/tx/service.go index 84fa7327a7..e138089018 100644 --- a/sei-cosmos/x/auth/tx/service.go +++ b/sei-cosmos/x/auth/tx/service.go @@ -26,15 +26,17 @@ type baseAppSimulateFn func(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) // txServer is the server for the protobuf Tx service. type txServer struct { - clientCtx client.Context + node client.LocalClient + txConfig client.TxConfig simulate baseAppSimulateFn interfaceRegistry codectypes.InterfaceRegistry } // NewTxServer creates a new Tx service server. -func NewTxServer(clientCtx client.Context, simulate baseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry) txtypes.ServiceServer { +func NewTxServer(node client.LocalClient, txConfig client.TxConfig, simulate baseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry) txtypes.ServiceServer { return txServer{ - clientCtx: clientCtx, + node: node, + txConfig: txConfig, simulate: simulate, interfaceRegistry: interfaceRegistry, } @@ -68,7 +70,7 @@ func (s txServer) GetTxsEvent(ctx context.Context, req *txtypes.GetTxsEventReque } } - result, err := QueryTxsByEvents(s.clientCtx, req.Events, page, limit, orderBy) + result, err := QueryTxsByEvents(ctx, s.node, s.txConfig, req.Events, page, limit, orderBy) if err != nil { return nil, err } @@ -140,7 +142,7 @@ func (s txServer) GetTx(ctx context.Context, req *txtypes.GetTxRequest) (*txtype // TODO We should also check the proof flag in gRPC header. // https://github.com/cosmos/cosmos-sdk/issues/7036. - result, err := QueryTx(s.clientCtx, req.Hash) + result, err := QueryTx(ctx, s.node, s.txConfig, req.Hash) if err != nil { if strings.Contains(err.Error(), "not found") { return nil, status.Errorf(codes.NotFound, "tx not found: %s", req.Hash) @@ -181,7 +183,7 @@ func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWith "or greater than the current height %d", req.Height, currentHeight) } - blockId, block, err := tmservice.GetProtoBlock(ctx, s.clientCtx, &req.Height) + blockId, block, err := tmservice.GetProtoBlock(ctx, s.node, &req.Height) if err != nil { return nil, err } @@ -203,7 +205,7 @@ func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWith } decodeTxAt := func(i uint64) error { tx := blockTxs[i] - txb, err := s.clientCtx.TxConfig.TxDecoder()(tx) + txb, err := s.txConfig.TxDecoder()(tx) if err != nil { return err } @@ -240,20 +242,18 @@ func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWith } func (s txServer) BroadcastTx(ctx context.Context, req *txtypes.BroadcastTxRequest) (*txtypes.BroadcastTxResponse, error) { - return client.TxServiceBroadcast(ctx, s.clientCtx, req) + return client.TxServiceBroadcast(ctx, s.node, req) } // RegisterTxService registers the tx service on the gRPC router. func RegisterTxService( qrt gogogrpc.Server, - clientCtx client.Context, + node client.LocalClient, + txConfig client.TxConfig, simulateFn baseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry, ) { - txtypes.RegisterServiceServer( - qrt, - NewTxServer(clientCtx, simulateFn, interfaceRegistry), - ) + txtypes.RegisterServiceServer(qrt, NewTxServer(node, txConfig, simulateFn, interfaceRegistry)) } // RegisterGRPCGatewayRoutes mounts the tx service's GRPC-gateway routes on the diff --git a/sei-cosmos/x/auth/vesting/client/cli/tx.go b/sei-cosmos/x/auth/vesting/client/cli/tx.go index e0aefe028d..c55422715c 100644 --- a/sei-cosmos/x/auth/vesting/client/cli/tx.go +++ b/sei-cosmos/x/auth/vesting/client/cli/tx.go @@ -80,7 +80,7 @@ timestamp. You can also optionally configure the 'admin' field using the flag '- msg := types.NewMsgCreateVestingAccount(clientCtx.GetFromAddress(), toAddr, amount, endTime, delayed, adminAddr) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/authz/client/cli/tx.go b/sei-cosmos/x/authz/client/cli/tx.go index 807558460a..82b460f964 100644 --- a/sei-cosmos/x/authz/client/cli/tx.go +++ b/sei-cosmos/x/authz/client/cli/tx.go @@ -165,7 +165,7 @@ Examples: return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) @@ -203,7 +203,7 @@ Example: msgAuthorized := args[1] msg := authz.NewMsgRevoke(granter, grantee, msgAuthorized) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } flags.AddTxFlagsToCmd(cmd) @@ -240,7 +240,7 @@ Example: } msg := authz.NewMsgExec(grantee, theTx.GetMsgs()) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } diff --git a/sei-cosmos/x/bank/client/cli/tx.go b/sei-cosmos/x/bank/client/cli/tx.go index 1ef804f999..24a32e4d7e 100644 --- a/sei-cosmos/x/bank/client/cli/tx.go +++ b/sei-cosmos/x/bank/client/cli/tx.go @@ -56,7 +56,7 @@ func NewSendTxCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/crisis/client/cli/tx.go b/sei-cosmos/x/crisis/client/cli/tx.go index 5466a2526e..fa4b2f0b21 100644 --- a/sei-cosmos/x/crisis/client/cli/tx.go +++ b/sei-cosmos/x/crisis/client/cli/tx.go @@ -50,7 +50,7 @@ func NewMsgVerifyInvariantTxCmd() *cobra.Command { msg := types.NewMsgVerifyInvariant(senderAddr, moduleName, route) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/distribution/client/cli/tx.go b/sei-cosmos/x/distribution/client/cli/tx.go index 9c00f54172..b666599da6 100644 --- a/sei-cosmos/x/distribution/client/cli/tx.go +++ b/sei-cosmos/x/distribution/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "strings" @@ -46,15 +47,16 @@ func NewTxCmd() *cobra.Command { return distTxCmd } -type newGenerateOrBroadcastFunc func(client.Context, *pflag.FlagSet, ...sdk.Msg) error +type newGenerateOrBroadcastFunc func(context.Context, client.Context, *pflag.FlagSet, ...sdk.Msg) error func NewSplitAndApply( + ctx context.Context, genOrBroadcastFn newGenerateOrBroadcastFunc, clientCtx client.Context, fs *pflag.FlagSet, msgs []sdk.Msg, chunkSize int, ) error { if chunkSize == 0 { - return genOrBroadcastFn(clientCtx, fs, msgs...) + return genOrBroadcastFn(ctx, clientCtx, fs, msgs...) } // split messages into slices of length chunkSize @@ -67,7 +69,7 @@ func NewSplitAndApply( } msgChunk := msgs[i:sliceEnd] - if err := genOrBroadcastFn(clientCtx, fs, msgChunk...); err != nil { + if err := genOrBroadcastFn(ctx, clientCtx, fs, msgChunk...); err != nil { return err } } @@ -116,7 +118,7 @@ $ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj } } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msgs...) }, } @@ -182,7 +184,7 @@ $ %[1]s tx distribution withdraw-all-rewards --from mykey clientCtx.BroadcastMode, FlagMaxMessagesPerTx) } - return NewSplitAndApply(tx.GenerateOrBroadcastTxCLI, clientCtx, cmd.Flags(), msgs, chunkSize) + return NewSplitAndApply(cmd.Context(), tx.GenerateOrBroadcastTxCLI, clientCtx, cmd.Flags(), msgs, chunkSize) }, } @@ -221,7 +223,7 @@ $ %s tx distribution set-withdraw-addr %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -257,7 +259,7 @@ $ %s tx distribution fund-community-pool 100uatom --from mykey msg := types.NewMsgFundCommunityPool(amount, depositorAddr) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -326,7 +328,7 @@ Where proposal.json contains: return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/distribution/client/cli/tx_test.go b/sei-cosmos/x/distribution/client/cli/tx_test.go index e58ec12ca5..6947cfeedf 100644 --- a/sei-cosmos/x/distribution/client/cli/tx_test.go +++ b/sei-cosmos/x/distribution/client/cli/tx_test.go @@ -1,6 +1,7 @@ package cli_test import ( + "context" "testing" "github.com/sei-protocol/sei-chain/app" @@ -22,7 +23,7 @@ import ( func Test_splitAndCall_NoMessages(t *testing.T) { clientCtx := client.Context{} - err := cli.NewSplitAndApply(nil, clientCtx, nil, nil, 10) + err := cli.NewSplitAndApply(t.Context(), nil, clientCtx, nil, nil, 10) assert.NoError(t, err, "") } @@ -45,7 +46,8 @@ func Test_splitAndCall_Splitting(t *testing.T) { callCount := 0 err := cli.NewSplitAndApply( - func(clientCtx client.Context, fs *pflag.FlagSet, msgs ...sdk.Msg) error { + t.Context(), + func(_ context.Context, clientCtx client.Context, fs *pflag.FlagSet, msgs ...sdk.Msg) error { callCount++ assert.NotNil(t, clientCtx) diff --git a/sei-cosmos/x/feegrant/client/cli/tx.go b/sei-cosmos/x/feegrant/client/cli/tx.go index dd4d11acfb..737f57edd4 100644 --- a/sei-cosmos/x/feegrant/client/cli/tx.go +++ b/sei-cosmos/x/feegrant/client/cli/tx.go @@ -168,7 +168,7 @@ Examples: return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -213,7 +213,7 @@ Example: msg := feegrant.NewMsgRevokeAllowance(clientCtx.GetFromAddress(), grantee) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } diff --git a/sei-cosmos/x/genutil/client/rest/query.go b/sei-cosmos/x/genutil/client/rest/query.go index 981633b0b4..555023ff42 100644 --- a/sei-cosmos/x/genutil/client/rest/query.go +++ b/sei-cosmos/x/genutil/client/rest/query.go @@ -14,7 +14,12 @@ import ( // QueryGenesisTxs writes the genesis transactions to the response if no error // occurs. func QueryGenesisTxs(clientCtx client.Context, w http.ResponseWriter) { - resultGenesis, err := clientCtx.Client.Genesis(context.Background()) + client, err := clientCtx.GetNode() + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + resultGenesis, err := client.Genesis(context.Background()) if err != nil { rest.WriteErrorResponse( w, http.StatusInternalServerError, diff --git a/sei-cosmos/x/gov/client/cli/query.go b/sei-cosmos/x/gov/client/cli/query.go index 81cf481090..75b82d1564 100644 --- a/sei-cosmos/x/gov/client/cli/query.go +++ b/sei-cosmos/x/gov/client/cli/query.go @@ -232,7 +232,7 @@ $ %s query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk vote := res.GetVote() if vote.Empty() { params := types.NewQueryVoteParams(proposalID, voterAddr) - resByTxQuery, err := gcutils.QueryVoteByTxQuery(clientCtx, params) + resByTxQuery, err := gcutils.QueryVoteByTxQuery(ctx, clientCtx, params) if err != nil { return err @@ -297,7 +297,7 @@ $ %[1]s query gov votes 1 --page=2 --limit=100 limit, _ := cmd.Flags().GetInt(flags.FlagLimit) params := types.NewQueryProposalVotesParams(proposalID, page, limit) - resByTxQuery, err := gcutils.QueryVotesByTxQuery(clientCtx, params) + resByTxQuery, err := gcutils.QueryVotesByTxQuery(ctx, clientCtx, params) if err != nil { return err } @@ -383,7 +383,7 @@ $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk propStatus := proposalRes.Proposal.Status if propStatus != types.StatusVotingPeriod && propStatus != types.StatusDepositPeriod { params := types.NewQueryDepositParams(proposalID, depositorAddr) - resByTxQuery, err := gcutils.QueryDepositByTxQuery(clientCtx, params) + resByTxQuery, err := gcutils.QueryDepositByTxQuery(ctx, clientCtx, params) if err != nil { return err } @@ -450,7 +450,7 @@ $ %s query gov deposits 1 propStatus := proposalRes.GetProposal().Status if propStatus != types.StatusVotingPeriod && propStatus != types.StatusDepositPeriod { params := types.NewQueryProposalParams(proposalID) - resByTxQuery, err := gcutils.QueryDepositsByTxQuery(clientCtx, params) + resByTxQuery, err := gcutils.QueryDepositsByTxQuery(ctx, clientCtx, params) if err != nil { return err } @@ -688,7 +688,7 @@ $ %s query gov proposer 1 return fmt.Errorf("proposal-id %s is not a valid uint", args[0]) } - prop, err := gcutils.QueryProposerByTxQuery(clientCtx, proposalID) + prop, err := gcutils.QueryProposerByTxQuery(cmd.Context(), clientCtx, proposalID) if err != nil { return err } diff --git a/sei-cosmos/x/gov/client/cli/tx.go b/sei-cosmos/x/gov/client/cli/tx.go index 250ca9bdd9..b8edbe2470 100644 --- a/sei-cosmos/x/gov/client/cli/tx.go +++ b/sei-cosmos/x/gov/client/cli/tx.go @@ -130,7 +130,7 @@ $ %s tx gov submit-proposal --title="Test Proposal" --description="My awesome pr return fmt.Errorf("invalid message: %w", err) } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -183,7 +183,7 @@ $ %s tx gov deposit 1 10stake --from mykey msg := types.NewMsgDeposit(from, proposalID, amount) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -231,7 +231,7 @@ $ %s tx gov vote 1 yes --from mykey // Build vote message and run basic validation msg := types.NewMsgVote(from, proposalID, byteVoteOption) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -284,7 +284,7 @@ $ %s tx gov weighted-vote 1 yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05 --from return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/gov/client/rest/query.go b/sei-cosmos/x/gov/client/rest/query.go index 0b161ae74a..10de0bd195 100644 --- a/sei-cosmos/x/gov/client/rest/query.go +++ b/sei-cosmos/x/gov/client/rest/query.go @@ -120,7 +120,7 @@ func queryDepositsHandlerFn(clientCtx client.Context) http.HandlerFunc { // as they're no longer in state. propStatus := proposal.Status if propStatus != types.StatusVotingPeriod && propStatus != types.StatusDepositPeriod { - res, err = gcutils.QueryDepositsByTxQuery(clientCtx, params) + res, err = gcutils.QueryDepositsByTxQuery(r.Context(), clientCtx, params) } else { res, _, err = clientCtx.QueryWithData("custom/gov/deposits", bz) } @@ -148,7 +148,7 @@ func queryProposerHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, err := gcutils.QueryProposerByTxQuery(clientCtx, proposalID) + res, err := gcutils.QueryProposerByTxQuery(r.Context(), clientCtx, proposalID) if rest.CheckInternalServerError(w, err) { return } @@ -223,7 +223,7 @@ func queryDepositHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, err = gcutils.QueryDepositByTxQuery(clientCtx, params) + res, err = gcutils.QueryDepositByTxQuery(r.Context(), clientCtx, params) if rest.CheckInternalServerError(w, err) { return } @@ -300,7 +300,7 @@ func queryVoteHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, err = gcutils.QueryVoteByTxQuery(clientCtx, params) + res, err = gcutils.QueryVoteByTxQuery(r.Context(), clientCtx, params) if rest.CheckInternalServerError(w, err) { return } @@ -358,7 +358,7 @@ func queryVotesOnProposalHandlerFn(clientCtx client.Context) http.HandlerFunc { propStatus := proposal.Status if propStatus != types.StatusVotingPeriod && propStatus != types.StatusDepositPeriod { - res, err = gcutils.QueryVotesByTxQuery(clientCtx, params) + res, err = gcutils.QueryVotesByTxQuery(r.Context(), clientCtx, params) } else { bz, err = clientCtx.LegacyAmino.MarshalAsJSON(params) if rest.CheckBadRequestError(w, err) { diff --git a/sei-cosmos/x/gov/client/utils/query.go b/sei-cosmos/x/gov/client/utils/query.go index 38935ea307..d1c105f7c9 100644 --- a/sei-cosmos/x/gov/client/utils/query.go +++ b/sei-cosmos/x/gov/client/utils/query.go @@ -1,6 +1,7 @@ package utils import ( + "context" "fmt" "github.com/sei-protocol/sei-chain/sei-cosmos/client" @@ -37,11 +38,11 @@ func (p Proposer) String() string { // // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. -func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposalParams) ([]byte, error) { +func QueryDepositsByTxQuery(ctx context.Context, clientCtx client.Context, params types.QueryProposalParams) ([]byte, error) { var deposits []types.Deposit // initial deposit was submitted with proposal, so must be queried separately - initialDeposit, err := queryInitialDepositByTxQuery(clientCtx, params.ProposalID) + initialDeposit, err := queryInitialDepositByTxQuery(ctx, clientCtx, params.ProposalID) if err != nil { return nil, err } @@ -51,7 +52,7 @@ func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposal } searchResult, err := combineEvents( - clientCtx, defaultPage, + ctx, clientCtx, defaultPage, // Query legacy Msgs event action []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), @@ -90,7 +91,7 @@ func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposal // QueryVotesByTxQuery will query for votes via a direct txs tags query. It // will fetch and build votes directly from the returned txs and return a JSON // marshalled result or any error that occurred. -func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVotesParams) ([]byte, error) { +func QueryVotesByTxQuery(ctx context.Context, clientCtx client.Context, params types.QueryProposalVotesParams) ([]byte, error) { var ( votes []types.Vote nextTxPage = defaultPage @@ -101,7 +102,7 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot for len(votes) < totalLimit { // Search for both (legacy) votes and weighted votes. searchResult, err := combineEvents( - clientCtx, nextTxPage, + ctx, clientCtx, nextTxPage, // Query legacy Vote Msgs []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), @@ -168,9 +169,9 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot } // QueryVoteByTxQuery will query for a single vote via a direct txs tags query. -func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) ([]byte, error) { +func QueryVoteByTxQuery(ctx context.Context, clientCtx client.Context, params types.QueryVoteParams) ([]byte, error) { searchResult, err := combineEvents( - clientCtx, defaultPage, + ctx, clientCtx, defaultPage, // Query legacy Vote Msgs []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), @@ -236,10 +237,10 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) // QueryDepositByTxQuery will query for a single deposit via a direct txs tags // query. -func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositParams) ([]byte, error) { +func QueryDepositByTxQuery(ctx context.Context, clientCtx client.Context, params types.QueryDepositParams) ([]byte, error) { // initial deposit was submitted with proposal, so must be queried separately - initialDeposit, err := queryInitialDepositByTxQuery(clientCtx, params.ProposalID) + initialDeposit, err := queryInitialDepositByTxQuery(ctx, clientCtx, params.ProposalID) if err != nil { return nil, err } @@ -254,7 +255,7 @@ func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositPa } searchResult, err := combineEvents( - clientCtx, defaultPage, + ctx, clientCtx, defaultPage, // Query legacy Msgs event action []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), @@ -297,8 +298,9 @@ func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositPa // QueryProposerByTxQuery will query for a proposer of a governance proposal by // ID. -func QueryProposerByTxQuery(clientCtx client.Context, proposalID uint64) (Proposer, error) { +func QueryProposerByTxQuery(ctx context.Context, clientCtx client.Context, proposalID uint64) (Proposer, error) { searchResult, err := combineEvents( + ctx, clientCtx, defaultPage, // Query legacy Msgs event action @@ -352,11 +354,15 @@ func QueryProposalByID(proposalID uint64, clientCtx client.Context, queryRoute s // - via ADR-031 proto msgs, their `Type()` is the protobuf FQ method name. // In searching for events, we search for both `Type()`s, and we use the // `combineEvents` function here to merge events. -func combineEvents(clientCtx client.Context, page int, eventGroups ...[]string) (*sdk.SearchTxsResult, error) { +func combineEvents(ctx context.Context, clientCtx client.Context, page int, eventGroups ...[]string) (*sdk.SearchTxsResult, error) { // Only the Txs field will be populated in the final SearchTxsResult. allTxs := []*sdk.TxResponse{} + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } for _, events := range eventGroups { - res, err := authtx.QueryTxsByEvents(clientCtx, events, page, defaultLimit, "") + res, err := authtx.QueryTxsByEvents(ctx, node, clientCtx.TxConfig, events, page, defaultLimit, "") if err != nil { return nil, err } @@ -368,9 +374,9 @@ func combineEvents(clientCtx client.Context, page int, eventGroups ...[]string) // queryInitialDepositByTxQuery will query for a initial deposit of a governance proposal by // ID. -func queryInitialDepositByTxQuery(clientCtx client.Context, proposalID uint64) (types.Deposit, error) { +func queryInitialDepositByTxQuery(ctx context.Context, clientCtx client.Context, proposalID uint64) (types.Deposit, error) { searchResult, err := combineEvents( - clientCtx, defaultPage, + ctx, clientCtx, defaultPage, // Query legacy Msgs event action []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal), diff --git a/sei-cosmos/x/gov/client/utils/query_test.go b/sei-cosmos/x/gov/client/utils/query_test.go index 0550c93d28..e72e23457b 100644 --- a/sei-cosmos/x/gov/client/utils/query_test.go +++ b/sei-cosmos/x/gov/client/utils/query_test.go @@ -180,7 +180,7 @@ func TestGetPaginatedVotes(t *testing.T) { } params := types.NewQueryProposalVotesParams(0, tc.page, tc.limit) - votesData, err := utils.QueryVotesByTxQuery(clientCtx, params) + votesData, err := utils.QueryVotesByTxQuery(t.Context(), clientCtx, params) require.NoError(t, err) votes := []types.Vote{} require.NoError(t, clientCtx.LegacyAmino.UnmarshalAsJSON(votesData, &votes)) diff --git a/sei-cosmos/x/params/client/cli/tx.go b/sei-cosmos/x/params/client/cli/tx.go index 7d067e4260..586f54afb6 100644 --- a/sei-cosmos/x/params/client/cli/tx.go +++ b/sei-cosmos/x/params/client/cli/tx.go @@ -82,7 +82,7 @@ Where proposal.json contains: return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } } diff --git a/sei-cosmos/x/slashing/client/cli/tx.go b/sei-cosmos/x/slashing/client/cli/tx.go index 1fd6f080d9..aab106b72e 100644 --- a/sei-cosmos/x/slashing/client/cli/tx.go +++ b/sei-cosmos/x/slashing/client/cli/tx.go @@ -42,7 +42,7 @@ $ tx slashing unjail --from mykey msg := types.NewMsgUnjail(sdk.ValAddress(valAddr)) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/staking/client/cli/tx.go b/sei-cosmos/x/staking/client/cli/tx.go index d590c3ceed..84fd54949b 100644 --- a/sei-cosmos/x/staking/client/cli/tx.go +++ b/sei-cosmos/x/staking/client/cli/tx.go @@ -66,7 +66,7 @@ func NewCreateValidatorCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } @@ -132,7 +132,7 @@ func NewEditValidatorCmd() *cobra.Command { msg := types.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -178,7 +178,7 @@ $ %s tx staking delegate %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --f msg := types.NewMsgDelegate(delAddr, valAddr, amount) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -226,7 +226,7 @@ $ %s tx staking redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp3 msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -269,7 +269,7 @@ $ %s tx staking unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from msg := types.NewMsgUndelegate(delAddr, valAddr, amount) - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-cosmos/x/staking/client/rest/query.go b/sei-cosmos/x/staking/client/rest/query.go index ed06534ebb..cc29ced034 100644 --- a/sei-cosmos/x/staking/client/rest/query.go +++ b/sei-cosmos/x/staking/client/rest/query.go @@ -176,7 +176,7 @@ func delegatorTxsHandlerFn(clientCtx client.Context) http.HandlerFunc { } for _, action := range actions { - foundTxs, errQuery := queryTxs(clientCtx, action, delegatorAddr) + foundTxs, errQuery := queryTxs(r.Context(), clientCtx, action, delegatorAddr) if rest.CheckInternalServerError(w, errQuery) { return } diff --git a/sei-cosmos/x/staking/client/rest/utils.go b/sei-cosmos/x/staking/client/rest/utils.go index cb95ac2b08..8f617fa5a9 100644 --- a/sei-cosmos/x/staking/client/rest/utils.go +++ b/sei-cosmos/x/staking/client/rest/utils.go @@ -1,6 +1,7 @@ package rest import ( + "context" "fmt" "net/http" @@ -25,7 +26,7 @@ func contains(stringSlice []string, txType string) bool { } // queries staking txs -func queryTxs(clientCtx client.Context, action string, delegatorAddr string) (*sdk.SearchTxsResult, error) { +func queryTxs(ctx context.Context, clientCtx client.Context, action string, delegatorAddr string) (*sdk.SearchTxsResult, error) { page := 1 limit := 100 events := []string{ @@ -33,7 +34,11 @@ func queryTxs(clientCtx client.Context, action string, delegatorAddr string) (*s fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, delegatorAddr), } - return authtx.QueryTxsByEvents(clientCtx, events, page, limit, "") + node, err := clientCtx.GetNode() + if err != nil { + return nil, err + } + return authtx.QueryTxsByEvents(ctx, node, clientCtx.TxConfig, events, page, limit, "") } func queryBonds(clientCtx client.Context, endpoint string) http.HandlerFunc { diff --git a/sei-cosmos/x/upgrade/client/cli/tx.go b/sei-cosmos/x/upgrade/client/cli/tx.go index d00910bacf..cf6edf6070 100644 --- a/sei-cosmos/x/upgrade/client/cli/tx.go +++ b/sei-cosmos/x/upgrade/client/cli/tx.go @@ -67,7 +67,7 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -127,7 +127,7 @@ func NewCmdSubmitCancelUpgradeProposal() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-ibc-go/modules/apps/27-interchain-accounts/host/client/cli/query.go b/sei-ibc-go/modules/apps/27-interchain-accounts/host/client/cli/query.go index d8b8160fbf..a14a9145d7 100644 --- a/sei-ibc-go/modules/apps/27-interchain-accounts/host/client/cli/query.go +++ b/sei-ibc-go/modules/apps/27-interchain-accounts/host/client/cli/query.go @@ -77,7 +77,11 @@ func GetCmdPacketEvents() *cobra.Command { fmt.Sprintf("%s.%s='%d'", channeltypes.EventTypeRecvPacket, channeltypes.AttributeKeySequence, seq), } - result, err := tx.QueryTxsByEvents(clientCtx, searchEvents, 1, 1, "") + node, err := clientCtx.GetNode() + if err != nil { + return err + } + result, err := tx.QueryTxsByEvents(cmd.Context(), node, clientCtx.TxConfig, searchEvents, 1, 1, "") if err != nil { return err } diff --git a/sei-ibc-go/modules/apps/transfer/client/cli/tx.go b/sei-ibc-go/modules/apps/transfer/client/cli/tx.go index 47ce23cd85..e47e33d0b0 100644 --- a/sei-ibc-go/modules/apps/transfer/client/cli/tx.go +++ b/sei-ibc-go/modules/apps/transfer/client/cli/tx.go @@ -121,7 +121,7 @@ corresponding to the counterparty channel. Any timeout set to 0 is disabled.`), ) msg.Memo = memo - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-ibc-go/modules/core/02-client/client/cli/tx.go b/sei-ibc-go/modules/core/02-client/client/cli/tx.go index ec09ee4aa8..482178b971 100644 --- a/sei-ibc-go/modules/core/02-client/client/cli/tx.go +++ b/sei-ibc-go/modules/core/02-client/client/cli/tx.go @@ -75,7 +75,7 @@ func NewCreateClientCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -121,7 +121,7 @@ func NewUpdateClientCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } } @@ -162,7 +162,7 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } } @@ -225,7 +225,7 @@ func NewUpgradeClientCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -284,7 +284,7 @@ func NewCmdSubmitUpdateClientProposal() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -383,7 +383,7 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/sei-ibc-go/testing/simapp/app.go b/sei-ibc-go/testing/simapp/app.go index 059527e511..c4619ea9c5 100644 --- a/sei-ibc-go/testing/simapp/app.go +++ b/sei-ibc-go/testing/simapp/app.go @@ -703,14 +703,10 @@ func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICon } } -// RegisterTxService implements the Application.RegisterTxService method. -func (app *SimApp) RegisterTxService(clientCtx client.Context) { - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) -} - -// RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *SimApp) RegisterTendermintService(clientCtx client.Context) { - tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +// RegisterTxService implements the Application.RegisterLocalServices method. +func (app *SimApp) RegisterLocalServices(node client.LocalClient, txConfig client.TxConfig) { + authtx.RegisterTxService(app.GRPCQueryRouter(), node, txConfig, app.Simulate, app.interfaceRegistry) + tmservice.RegisterTendermintService(app.GRPCQueryRouter(), node, app.interfaceRegistry) } // RegisterSwaggerAPI registers swagger route with API Server diff --git a/sei-tendermint/abci/types/application.go b/sei-tendermint/abci/types/application.go index 8582298dd0..c781663007 100644 --- a/sei-tendermint/abci/types/application.go +++ b/sei-tendermint/abci/types/application.go @@ -2,6 +2,9 @@ package types import ( "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" ) // Application is an interface that enables any finite, deterministic state machine @@ -17,6 +20,8 @@ type Application interface { // Mempool Connection CheckTx(context.Context, *RequestCheckTxV2) *ResponseCheckTxV2 // Validate a tx for the mempool GetTxPriorityHint(context.Context, *RequestGetTxPriorityHintV2) (*ResponseGetTxPriorityHint, error) // Get tx priority before checkTx + EvmNonce(common.Address) uint64 + EvmBalance(common.Address) *big.Int // Consensus Connection InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) // Initialize blockchain w validators/other info from TendermintCore @@ -85,6 +90,14 @@ func (BaseApplication) GetTxPriorityHint(context.Context, *RequestGetTxPriorityH return &ResponseGetTxPriorityHint{}, nil } +func (BaseApplication) EvmNonce(common.Address) uint64 { + return 0 +} + +func (BaseApplication) EvmBalance(common.Address) *big.Int { + return big.NewInt(0) +} + func (BaseApplication) FinalizeBlock(_ context.Context, req *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) { txs := make([]*ExecTxResult, len(req.Txs)) for i := range req.Txs { diff --git a/sei-tendermint/abci/types/mocks/application.go b/sei-tendermint/abci/types/mocks/application.go index 4b577bdc99..7ce022f62f 100644 --- a/sei-tendermint/abci/types/mocks/application.go +++ b/sei-tendermint/abci/types/mocks/application.go @@ -4,9 +4,13 @@ package mocks import ( context "context" + "math/big" + + common "github.com/ethereum/go-ethereum/common" - types "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" mock "github.com/stretchr/testify/mock" + + types "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" ) // Application is an autogenerated mock type for the Application type @@ -94,6 +98,42 @@ func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error return r0, r1 } +// EvmNonce provides a mock function with given fields: _a0 +func (_m *Application) EvmNonce(_a0 common.Address) uint64 { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for EvmNonce") + } + + var r0 uint64 + if rf, ok := ret.Get(0).(func(common.Address) uint64); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(uint64) + } + + return r0 +} + +// EvmBalance provides a mock function with given fields: _a0 +func (_m *Application) EvmBalance(_a0 common.Address) *big.Int { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for EvmBalance") + } + + var r0 *big.Int + if rf, ok := ret.Get(0).(func(common.Address) *big.Int); ok { + r0 = rf(_a0) + } else if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + + return r0 +} + // FinalizeBlock provides a mock function with given fields: _a0, _a1 func (_m *Application) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { ret := _m.Called(_a0, _a1) diff --git a/sei-tendermint/abci/types/types.go b/sei-tendermint/abci/types/types.go index d6f48821d2..b8847a6c66 100644 --- a/sei-tendermint/abci/types/types.go +++ b/sei-tendermint/abci/types/types.go @@ -4,11 +4,11 @@ import ( "bytes" "encoding/json" "errors" + "math/big" "github.com/gogo/protobuf/jsonpb" "github.com/sei-protocol/sei-chain/sei-tendermint/crypto" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/jsontypes" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" ) const ( @@ -225,22 +225,17 @@ const ( Pending ) -type PendingTxChecker func() PendingTxCheckerResponse -type ExpireTxHandler func() - // ResponseCheckTxV2 response type contains non-protobuf fields, so non-local ABCI clients will not be able // to utilize the new fields in V2 type (but still be backwards-compatible) type ResponseCheckTxV2 struct { *ResponseCheckTx - IsPending utils.Option[PendingTxChecker] - ExpireTxHandler utils.Option[ExpireTxHandler] - CheckTxCallback func(int64) // helper properties for prioritization in mempool - EVMNonce uint64 - EVMSenderAddress string - IsEVM bool - Priority int64 + EVMNonce uint64 + EVMSenderAddress string + IsEVM bool + Priority int64 + EVMRequiredBalance *big.Int } type CheckTxTypeV2 int32 diff --git a/sei-tendermint/cmd/tendermint/commands/run_node.go b/sei-tendermint/cmd/tendermint/commands/run_node.go index 99562f79cd..d8eeeb14ec 100644 --- a/sei-tendermint/cmd/tendermint/commands/run_node.go +++ b/sei-tendermint/cmd/tendermint/commands/run_node.go @@ -37,12 +37,8 @@ func AddNodeFlags(cmd *cobra.Command, conf *cfg.Config) { "consensus votes before joining consensus") // abci flags - cmd.Flags().String( - "proxy-app", - conf.ProxyApp, - "proxy app address, or one of: 'kvstore',"+ - " 'persistent_kvstore', 'e2e' or 'noop' for local testing.") - cmd.Flags().String("abci", conf.ABCI, "specify abci transport (socket | grpc)") + mustMarkDeprecated(cmd, "proxy-app", "out-of-process ABCI has been removed; this flag is ignored") + mustMarkDeprecated(cmd, "abci", "out-of-process ABCI has been removed; this flag is ignored") // rpc flags cmd.Flags().String("rpc.laddr", conf.RPC.ListenAddress, "RPC listen address. Port required") @@ -78,6 +74,13 @@ func AddNodeFlags(cmd *cobra.Command, conf *cfg.Config) { addDBFlags(cmd, conf) } +func mustMarkDeprecated(cmd *cobra.Command, name, message string) { + cmd.Flags().String(name, "", "") + if err := cmd.Flags().MarkDeprecated(name, message); err != nil { + panic(err) + } +} + func addDBFlags(cmd *cobra.Command, conf *cfg.Config) { cmd.Flags().String( "db-backend", diff --git a/sei-tendermint/config/config.go b/sei-tendermint/config/config.go index c946e7abed..99b3b72667 100644 --- a/sei-tendermint/config/config.go +++ b/sei-tendermint/config/config.go @@ -173,8 +173,8 @@ type BaseConfig struct { // This should be set in viper so it can unmarshal into this struct RootDir string `mapstructure:"home"` - // TCP or UNIX socket address of the ABCI application, - // or the name of an ABCI application compiled in with the Tendermint binary + // Deprecated: out-of-process ABCI has been removed and this option no longer + // has any effect. ProxyApp string `mapstructure:"proxy-app"` // A custom human readable name for this node @@ -228,7 +228,8 @@ type BaseConfig struct { // A JSON file containing the private key to use for p2p authenticated encryption NodeKey string `mapstructure:"node-key-file"` - // Mechanism to connect to the ABCI application: socket | grpc + // Deprecated: out-of-process ABCI has been removed and this option no longer + // has any effect. ABCI string `mapstructure:"abci"` // Deprecated: peer filtering via ABCI has been removed and this option no longer has any effect. diff --git a/sei-tendermint/config/toml.go b/sei-tendermint/config/toml.go index 63998e32bb..9ed79f8882 100644 --- a/sei-tendermint/config/toml.go +++ b/sei-tendermint/config/toml.go @@ -88,10 +88,6 @@ const manualConfigTemplate = `# This is a TOML config file. ### Main Base Config Options ### ####################################################################### -# TCP or UNIX socket address of the ABCI application, -# or the name of an ABCI application compiled in with the Tendermint binary -proxy-app = "{{ .BaseConfig.ProxyApp }}" - # A custom human readable name for this node moniker = "{{ .BaseConfig.Moniker }}" @@ -145,9 +141,6 @@ genesis-file = "{{ js .BaseConfig.Genesis }}" # Path to the JSON file containing the private key to use for node authentication in the p2p protocol node-key-file = "{{ js .BaseConfig.NodeKey }}" -# Mechanism to connect to the ABCI application: socket | grpc -abci = "{{ .BaseConfig.ABCI }}" - ####################################################################### ### Advanced Configuration Options ### ####################################################################### diff --git a/sei-tendermint/config/toml_test.go b/sei-tendermint/config/toml_test.go index cf27c4484a..33f34b754c 100644 --- a/sei-tendermint/config/toml_test.go +++ b/sei-tendermint/config/toml_test.go @@ -63,7 +63,6 @@ func checkConfig(t *testing.T, configFile string) { var elems = []string{ "moniker", "seeds", - "proxy-app", "create-empty-blocks", "peer", "timeout", diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index b40e2ff8f7..fcf8a6db06 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -6,10 +6,12 @@ import ( "crypto/sha256" "errors" "fmt" + "math/big" "sync" "sync/atomic" "time" + "github.com/ethereum/go-ethereum/common" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/libs/clist" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/libs/reservoir" @@ -159,6 +161,11 @@ func DefaultConfig() *Config { } } +type evmAddrNonce struct { + Address common.Address + Nonce uint64 +} + // TxMempool defines a prioritized mempool data structure used by the v1 mempool // reactor. It keeps a thread-safe priority queue of transactions that is used // when a block proposer constructs a block and a thread-safe linked-list that @@ -219,9 +226,11 @@ type TxMempool struct { expirationIndex *WrappedTxList // pendingTxs stores transactions that are not valid yet but might become valid - // if its checker returns Accepted + // once nonce ordering or sender balance catches up. pendingTxs *PendingTxs + byAddrNonce utils.Mutex[map[evmAddrNonce]*WrappedTx] + // A read/write lock is used to safe guard updates, insertions and deletions // from the mempool. A read-lock is implicitly acquired when executing CheckTx, // however, a caller must explicitly grab a write-lock via Lock when updating @@ -252,6 +261,7 @@ func NewTxMempool( priorityIndex: NewTxPriorityQueue(), expirationIndex: NewWrappedTxList(), pendingTxs: NewPendingTxs(cfg), + byAddrNonce: utils.NewMutex(map[evmAddrNonce]*WrappedTx{}), txConstraintsFetcher: txConstraintsFetcher, priorityReservoir: reservoir.New[int64](cfg.DropPriorityReservoirSize, cfg.DropPriorityThreshold, nil), // Use non-deterministic RNG } @@ -272,6 +282,46 @@ func (txmp *TxMempool) Config() *Config { return txmp.config } func (txmp *TxMempool) App() *proxy.Proxy { return txmp.app } +func (txmp *TxMempool) EvmNextPendingNonce(addr common.Address) uint64 { + an := evmAddrNonce{addr, txmp.app.EvmNonce(addr)} + for byAddrNonce := range txmp.byAddrNonce.Lock() { + for { + if _, ok := byAddrNonce[an]; !ok { + break + } + an.Nonce += 1 + } + } + return an.Nonce +} + +func (txmp *TxMempool) addNonce(wtx *WrappedTx) { + evm, ok := wtx.evm.Get() + if !ok { + return + } + an := evmAddrNonce{common.HexToAddress(evm.address), evm.nonce} + for byAddrNonce := range txmp.byAddrNonce.Lock() { + if old, ok := byAddrNonce[an]; ok && old.priority >= wtx.priority { + return + } + byAddrNonce[an] = wtx + } +} + +func (txmp *TxMempool) removeNonce(wtx *WrappedTx) { + evm, ok := wtx.evm.Get() + if !ok { + return + } + an := evmAddrNonce{common.HexToAddress(evm.address), evm.nonce} + for byAddrNonce := range txmp.byAddrNonce.Lock() { + if byAddrNonce[an] == wtx { + delete(byAddrNonce, an) + } + } +} + func (txmp *TxMempool) TxStore() *TxStore { return txmp.txStore } // Lock obtains a write-lock on the mempool. A caller must be sure to explicitly @@ -324,7 +374,7 @@ func (txmp *TxMempool) TxsAvailable() <-chan struct{} { return txmp.txsAvailable } -func (txmp *TxMempool) checkResponseState(res *abci.ResponseCheckTx) error { +func (txmp *TxMempool) checkResponseState(wtx *WrappedTx) error { constraints, err := txmp.txConstraintsFetcher() if err != nil { return err @@ -333,11 +383,11 @@ func (txmp *TxMempool) checkResponseState(res *abci.ResponseCheckTx) error { if constraints.MaxGas == -1 { return nil } - if res.GasWanted < 0 { - return fmt.Errorf("negative gas wanted: %d", res.GasWanted) + if wtx.gasWanted < 0 { + return fmt.Errorf("negative gas wanted: %d", wtx.gasWanted) } - if res.GasWanted > constraints.MaxGas { - return fmt.Errorf("gas wanted exceeds max gas: gas wanted %d is greater than max gas %d", res.GasWanted, constraints.MaxGas) + if wtx.gasWanted > constraints.MaxGas { + return fmt.Errorf("gas wanted exceeds max gas: gas wanted %d is greater than max gas %d", wtx.gasWanted, constraints.MaxGas) } return nil @@ -434,60 +484,40 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) txmp.metrics.observeCheckTxPriorityDistribution(res.Priority, false, txInfo.SenderNodeID, false) wtx := &WrappedTx{ - hashedTx: newHashedTx(tx), - timestamp: time.Now().UTC(), - height: txmp.height, - evmNonce: res.EVMNonce, - evmAddress: res.EVMSenderAddress, - isEVM: res.IsEVM, - removeHandler: func(removeFromCache bool) { - if removeFromCache { - txmp.cache.Remove(txHash) - } - if expireTxHandler, ok := res.ExpireTxHandler.Get(); ok { - expireTxHandler() - } - }, + hashedTx: newHashedTx(tx), + timestamp: time.Now().UTC(), + height: txmp.height, + priority: res.Priority, estimatedGas: res.GasEstimated, + gasWanted: res.GasWanted, + peers: map[uint16]struct{}{txInfo.SenderID: {}}, + } + if res.IsEVM { + wtx.evm = utils.Some(evmTx{ + address: res.EVMSenderAddress, + nonce: res.EVMNonce, + requiredBalance: res.EVMRequiredBalance, + }) } // only add new transaction if checkTx passes and is not pending - if !res.IsPending.IsPresent() { - // Update transaction priority reservoir with the true Tx priority - // as determined by the application. - // - // NOTE: This is done before potentially rejecting the transaction due to - // mempool being full. This is to ensure that the reservoir contains a - // representative sample of all transactions that have been processed by - // CheckTx. - // - // However, this is NOT done if the tx is pending, since a spammer could - // throw off the correct priority percentiles otherwise. - // - // We do not use the priority hint here as it may be misleading and - // inaccurate. The true priority as determined by the application is the - // most accurate. - txmp.priorityReservoir.Add(res.Priority) - err = txmp.addNewTransaction(wtx, res.ResponseCheckTx, txInfo) - if err != nil { + if !txmp.shouldPending(wtx) { + if err := txmp.addNewTransaction(wtx); err != nil { return nil, err } } else { // otherwise add to pending txs store if err := txmp.canAddPendingTx(wtx); err != nil { // TODO: eviction strategy for pending transactions - wtx.removeHandler(true) + txmp.cleanupTx(txHash, true) return nil, err } - if err := txmp.pendingTxs.Insert(wtx, res, txInfo); err != nil { - wtx.removeHandler(true) + if err := txmp.pendingTxs.Insert(wtx); err != nil { + txmp.cleanupTx(txHash, true) return nil, err } } - - if res.CheckTxCallback != nil { - res.CheckTxCallback(res.Priority) - } + txmp.addNonce(wtx) return res.ResponseCheckTx, nil } @@ -669,7 +699,7 @@ func (txmp *TxMempool) reapTxs(l ReapLimits) (types.Txs, int64) { totalGasWanted = prospectiveGasWanted totalGasEstimated = prospectiveGasEstimated - if wtx.isEVM { + if wtx.evm.IsPresent() { evmTxs = append(evmTxs, wtx.Tx()) } else { nonEvmTxs = append(nonEvmTxs, wtx.Tx()) @@ -715,7 +745,7 @@ func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs { // retrieve more from pending txs pending := txmp.pendingTxs.Peek(max - len(txs)) for _, ptx := range pending { - txs = append(txs, ptx.tx.Tx()) + txs = append(txs, ptx.Tx()) } } return txs @@ -730,9 +760,9 @@ func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs { // // WARNING: callers should almost always pass recheck=false. recheck=true // re-runs CheckTx on every tx still in the mempool after each block, and -// handleRecheckResult treats a "now IsPending" response as terminal: it +// handleRecheckResult treats a "now pending" response as terminal: it // evicts the tx and async-re-CheckTx-es it, which lands it back in -// pendingTxs. For chains whose antehandler returns IsPending for any +// pendingTxs. For chains whose antehandler returns pending for any // ahead-of-nonce EVM tx (Sei), this evicts perfectly-valid queued txs. // // Example. txA (nonce 3), txB (nonce 2), txC (nonce 1) on the same sender. @@ -740,7 +770,7 @@ func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs { // 1. txA, txB, txC are submitted in this order. // 2. txA and txB enter pendingTxs (their nonce is ahead of the sender's // expected nonce at CheckTx time so the EVM antehandler marks them -// IsPending). txC enters the priority index (its nonce matches expected). +// pending). txC enters the priority index (its nonce matches expected). // 3. Block 1 reaps and mines txC. The sender's expected nonce becomes 2. // 4. handlePendingTransactions promotes txA and txB into the priority // index. The per-sender evmQueue is now [txB (head), txA (tail)]. @@ -757,8 +787,8 @@ func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs { // recheck=true (broken): // // 5. updateReCheckTxs re-runs CheckTx on each tx in the priority index: -// - txB: nonce 2 == expected 2 → not IsPending → stays. -// - txA: nonce 3 > expected 2 → IsPending again. handleRecheckResult +// - txB: nonce 2 == expected 2 → not pending → stays. +// - txA: nonce 3 > expected 2 → pending again. handleRecheckResult // evicts it and async-re-CheckTx-es it, which lands it back in // pendingTxs. // 6. Block 2 reaps txB only (txA is no longer in the priority index). @@ -808,10 +838,10 @@ func (txmp *TxMempool) Update( if execTxResult[i].EvmTxInfo != nil { // remove any tx that has the same nonce (because the committed tx // may be from block proposal and is never in the local mempool) - if wtx, _ := txmp.priorityIndex.GetTxWithSameNonce(&WrappedTx{ - evmAddress: execTxResult[i].EvmTxInfo.SenderAddress, - evmNonce: execTxResult[i].EvmTxInfo.Nonce, - }); wtx != nil { + if wtx, _ := txmp.priorityIndex.TxByAddrNonce( + execTxResult[i].EvmTxInfo.SenderAddress, + execTxResult[i].EvmTxInfo.Nonce, + ); wtx != nil { txmp.removeTx(wtx, false, false, true) } } @@ -850,9 +880,9 @@ func (txmp *TxMempool) Update( // // addNewTransaction runs after the ABCI application executes CheckTx. // It runs the consensus-derived post-check for the current state snapshot. -// If the CheckTx response code is not OK, or if the post-check -// reports an error, the transaction is rejected. Otherwise, we attempt to insert -// the transaction into the mempool. +// If the post-check reports an error, the transaction is rejected. Otherwise, +// we attempt to insert the transaction into the mempool. CheckTx response codes +// are filtered earlier in CheckTx. // // When inserting a transaction, we first check if there is sufficient capacity. // If there is, the transaction is added to the txStore and all indexes. @@ -862,32 +892,38 @@ func (txmp *TxMempool) Update( // // NOTE: // - An explicit lock is NOT required. -func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, res *abci.ResponseCheckTx, txInfo TxInfo) error { - err := txmp.checkResponseState(res) - - if err != nil || res.Code != abci.CodeTypeOK { +func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx) error { + // Update transaction priority reservoir with the true Tx priority + // as determined by the application. + // + // NOTE: This is done before potentially rejecting the transaction due to + // mempool being full. This is to ensure that the reservoir contains a + // representative sample of all transactions that have been processed by + // CheckTx. + // + // However, this is NOT done if the tx is pending, since a spammer could + // throw off the correct priority percentiles otherwise. + // + // We do not use the priority hint here as it may be misleading and + // inaccurate. The true priority as determined by the application is the + // most accurate. + txmp.priorityReservoir.Add(wtx.priority) + err := txmp.checkResponseState(wtx) + if err != nil { // ignore bad transactions logger.Info( "rejected bad transaction", "priority", wtx.priority, "tx", wtx.Hash(), - "peer_id", txInfo.SenderNodeID, - "code", res.Code, "post_check_err", err, ) - txmp.metrics.FailedTxs.Add(1) - - wtx.removeHandler(!txmp.config.KeepInvalidTxsInCache) - + txmp.cleanupTx(wtx.Hash(), !txmp.config.KeepInvalidTxsInCache) return err } - - priority := res.Priority - if err := txmp.canAddTx(wtx); err != nil { evictTxs := txmp.priorityIndex.GetEvictableTxs( - priority, + wtx.priority, int64(wtx.Size()), txmp.SizeBytes(), txmp.config.MaxTxsBytes, @@ -895,7 +931,7 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, res *abci.ResponseCheck if len(evictTxs) == 0 { // No room for the new incoming transaction so we just remove it from // the cache. - wtx.removeHandler(true) + txmp.cleanupTx(wtx.Hash(), true) logger.Error( "rejected incoming good transaction; mempool full", "tx", wtx.Hash(), @@ -923,13 +959,6 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, res *abci.ResponseCheck } } - wtx.gasWanted = res.GasWanted - wtx.estimatedGas = res.GasEstimated - wtx.priority = priority - wtx.peers = map[uint16]struct{}{ - txInfo.SenderID: {}, - } - if txmp.isInMempool(wtx.Hash()) { return nil } @@ -995,10 +1024,14 @@ func (txmp *TxMempool) handleRecheckResult(tx types.Tx, res *abci.ResponseCheckT // if an existing transaction is evicted during CheckTx and while this // callback is being executed for the same evicted transaction. if !txmp.txStore.IsTxRemoved(wtx) { - err := txmp.checkResponseState(res.ResponseCheckTx) + err := txmp.checkResponseState(wtx) + if evm, ok := wtx.evm.Get(); ok { + evm.requiredBalance = new(big.Int).Set(res.EVMRequiredBalance) + wtx.evm = utils.Some(evm) + } // we will treat a transaction that turns pending in a recheck as invalid and evict it - if res.Code == abci.CodeTypeOK && err == nil && !res.IsPending.IsPresent() { + if res.Code == abci.CodeTypeOK && err == nil && !txmp.shouldPending(wtx) { wtx.priority = res.Priority } else { logger.Debug( @@ -1147,11 +1180,18 @@ func (txmp *TxMempool) insertTx(wtx *WrappedTx) bool { return true } +func (txmp *TxMempool) cleanupTx(txHash types.TxHash, removeFromCache bool) { + if removeFromCache { + txmp.cache.Remove(txHash) + } +} + func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool, shouldReenqueue bool, updatePriorityIndex bool) { if txmp.txStore.IsTxRemoved(wtx) { return } + txmp.removeNonce(wtx) txmp.txStore.RemoveTx(wtx) toBeReenqueued := []*WrappedTx{} if updatePriorityIndex { @@ -1165,7 +1205,7 @@ func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool, shouldReen wtx.gossipEl.DetachPrev() txmp.metrics.RemovedTxs.Add(1) - wtx.removeHandler(removeFromCache) + txmp.cleanupTx(wtx.Hash(), removeFromCache) if shouldReenqueue { for _, reenqueue := range toBeReenqueued { @@ -1185,7 +1225,7 @@ func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool, shouldReen func (txmp *TxMempool) expire(blockHeight int64, wtx *WrappedTx) { txmp.metrics.ExpiredTxs.Add(1) txmp.logExpiredTx(blockHeight, wtx) - wtx.removeHandler(!txmp.config.KeepInvalidTxsInCache) + txmp.cleanupTx(wtx.Hash(), !txmp.config.KeepInvalidTxsInCache) } func (txmp *TxMempool) logExpiredTx(blockHeight int64, wtx *WrappedTx) { @@ -1198,9 +1238,21 @@ func (txmp *TxMempool) logExpiredTx(blockHeight int64, wtx *WrappedTx) { "transaction expired", "priority", wtx.priority, "tx", wtx.Hash(), - "address", wtx.evmAddress, - "evm", wtx.isEVM, - "nonce", wtx.evmNonce, + "address", func() string { + evm, ok := wtx.evm.Get() + if !ok { + return "" + } + return evm.address + }(), + "evm", wtx.evm.IsPresent(), + "nonce", func() uint64 { + evm, ok := wtx.evm.Get() + if !ok { + return 0 + } + return evm.nonce + }(), "height", blockHeight, "tx_height", wtx.height, "tx_timestamp", wtx.timestamp, @@ -1238,6 +1290,7 @@ func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) { // remove pending txs that have expired txmp.pendingTxs.PurgeExpired(blockHeight, now, func(wtx *WrappedTx) { + txmp.removeNonce(wtx) txmp.expire(blockHeight, wtx) }) } @@ -1253,16 +1306,44 @@ func (txmp *TxMempool) notifyTxsAvailable() { } } +func (txmp *TxMempool) shouldPending(wtx *WrappedTx) bool { + evm, ok := wtx.evm.Get() + if !ok { + return false + } + addr := common.HexToAddress(evm.address) + if evm.nonce > txmp.EvmNextPendingNonce(addr) { + return true + } + balance := txmp.app.EvmBalance(addr) + return balance.Cmp(evm.requiredBalance) < 0 +} + func (txmp *TxMempool) handlePendingTransactions() { - accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions() + accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions(func(wtx *WrappedTx) abci.PendingTxCheckerResponse { + evm, ok := wtx.evm.Get() + if !ok { + return abci.Accepted + } + addr := common.HexToAddress(evm.address) + if evm.nonce < txmp.app.EvmNonce(addr) { + return abci.Rejected + } + if txmp.shouldPending(wtx) { + return abci.Pending + } + return abci.Accepted + }) for _, tx := range accepted { - if err := txmp.addNewTransaction(tx.tx, tx.checkTxResponse.ResponseCheckTx, tx.txInfo); err != nil { + if err := txmp.addNewTransaction(tx); err != nil { + txmp.removeNonce(tx) logger.Error("error adding pending transaction", "err", err) } } for _, tx := range rejected { + txmp.removeNonce(tx) if !txmp.config.KeepInvalidTxsInCache { - tx.tx.removeHandler(true) + txmp.cleanupTx(tx.Hash(), true) } } } diff --git a/sei-tendermint/internal/mempool/mempool_test.go b/sei-tendermint/internal/mempool/mempool_test.go index bae2619853..8e06173064 100644 --- a/sei-tendermint/internal/mempool/mempool_test.go +++ b/sei-tendermint/internal/mempool/mempool_test.go @@ -27,9 +27,8 @@ import ( type application struct { *kvstore.Application - gasWanted *int64 - gasEstimated *int64 - occupiedNonces map[string][]uint64 + gasWanted *int64 + gasEstimated *int64 } type testTx struct { @@ -79,27 +78,6 @@ func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) * GasEstimated: gasEstimated, }} } - if app.occupiedNonces == nil { - app.occupiedNonces = make(map[string][]uint64) - } - if _, exists := app.occupiedNonces[account]; !exists { - app.occupiedNonces[account] = []uint64{} - } - active := true - for i := uint64(0); i < nonce; i++ { - found := false - for _, occ := range app.occupiedNonces[account] { - if occ == i { - found = true - break - } - } - if !found { - active = false - break - } - } - app.occupiedNonces[account] = append(app.occupiedNonces[account], nonce) res := &abci.ResponseCheckTxV2{ ResponseCheckTx: &abci.ResponseCheckTx{ Priority: v, @@ -110,21 +88,6 @@ func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) * EVMNonce: nonce, EVMSenderAddress: account, IsEVM: true, - ExpireTxHandler: utils.Some(abci.ExpireTxHandler(func() { - idx := -1 - for i, n := range app.occupiedNonces[account] { - if n == nonce { - idx = i - break - } - } - if idx >= 0 { - app.occupiedNonces[account] = append(app.occupiedNonces[account][:idx], app.occupiedNonces[account][idx+1:]...) - } - })), - } - if !active { - res.IsPending = utils.Some(abci.PendingTxChecker(func() abci.PendingTxCheckerResponse { return abci.Pending })) } return res } diff --git a/sei-tendermint/internal/mempool/priority_queue.go b/sei-tendermint/internal/mempool/priority_queue.go index 4ebb395d28..ece24e3bad 100644 --- a/sei-tendermint/internal/mempool/priority_queue.go +++ b/sei-tendermint/internal/mempool/priority_queue.go @@ -17,7 +17,7 @@ type TxPriorityQueue struct { // invariant 1: no duplicate nonce in the same queue // invariant 2: no nonce gap in the same queue // invariant 3: head of the queue must be in heap - evmQueue map[string][]*WrappedTx // sorted by nonce + evmQueue map[string][]*WrappedTx // indexed by hex encoded address, sorted by nonce } func insertToEVMQueue(queue []*WrappedTx, tx *WrappedTx, i int) []*WrappedTx { @@ -29,11 +29,11 @@ func insertToEVMQueue(queue []*WrappedTx, tx *WrappedTx, i int) []*WrappedTx { } // binarySearch finds the index at which tx should be inserted in queue -func binarySearch(queue []*WrappedTx, tx *WrappedTx) int { +func binarySearch(queue []*WrappedTx, nonce uint64) int { low, high := 0, len(queue) for low < high { mid := low + (high-low)/2 - if queue[mid].IsBefore(tx) { + if queue[mid].EVMNonce() < nonce { low = mid + 1 } else { high = mid @@ -44,56 +44,55 @@ func binarySearch(queue []*WrappedTx, tx *WrappedTx) int { func NewTxPriorityQueue() *TxPriorityQueue { pq := &TxPriorityQueue{ - txs: make([]*WrappedTx, 0), - evmQueue: make(map[string][]*WrappedTx), + txs: nil, + evmQueue: map[string][]*WrappedTx{}, } - heap.Init(pq) - return pq } -func (pq *TxPriorityQueue) GetTxWithSameNonce(tx *WrappedTx) (*WrappedTx, int) { +func (pq *TxPriorityQueue) TxByAddrNonce(addr string, nonce uint64) (*WrappedTx, int) { pq.mtx.RLock() defer pq.mtx.RUnlock() - return pq.getTxWithSameNonceUnsafe(tx) + return pq.txByAddrNonceUnsafe(addr, nonce) } -func (pq *TxPriorityQueue) getTxWithSameNonceUnsafe(tx *WrappedTx) (*WrappedTx, int) { - queue, ok := pq.evmQueue[tx.evmAddress] +func (pq *TxPriorityQueue) txByAddrNonceUnsafe(addr string, nonce uint64) (*WrappedTx, int) { + queue, ok := pq.evmQueue[addr] if !ok { return nil, -1 } - idx := binarySearch(queue, tx) - if idx < len(queue) && queue[idx].evmNonce == tx.evmNonce { + if idx := binarySearch(queue, nonce); idx < len(queue) { return queue[idx], idx } return nil, -1 } func (pq *TxPriorityQueue) tryReplacementUnsafe(tx *WrappedTx) (replaced *WrappedTx, shouldDrop bool) { - if !tx.isEVM { + evm, ok := tx.evm.Get() + if !ok { return nil, false } - queue, ok := pq.evmQueue[tx.evmAddress] - if ok && len(queue) > 0 { - existing, idx := pq.getTxWithSameNonceUnsafe(tx) - if existing != nil { - if tx.priority > existing.priority { - // should replace - // replace heap if applicable - if hi, ok := pq.findTxIndexUnsafe(existing); ok { - heap.Remove(pq, hi) - heap.Push(pq, tx) // need to be in the heap since it has the same nonce - } - pq.evmQueue[tx.evmAddress][idx] = tx // replace queue item in-place - return existing, false - } - // tx should be dropped since it's dominated by an existing tx - return nil, true - } + queue := pq.evmQueue[evm.address] + if len(queue) == 0 { + return nil, false + } + existing, idx := pq.txByAddrNonceUnsafe(evm.address,evm.nonce) + if existing == nil { + return nil, false + } + if tx.priority <= existing.priority { + // tx should be dropped since it's dominated by an existing tx + return nil, true + } + // should replace + // replace heap if applicable + if hi, ok := pq.findTxIndexUnsafe(existing); ok { + heap.Remove(pq, hi) + heap.Push(pq, tx) // need to be in the heap since it has the same nonce } - return nil, false + pq.evmQueue[evm.address][idx] = tx // replace queue item in-place + return existing, false } // GetEvictableTxs attempts to find and return a list of *WrappedTx than can be @@ -105,9 +104,7 @@ func (pq *TxPriorityQueue) tryReplacementUnsafe(tx *WrappedTx) (replaced *Wrappe func (pq *TxPriorityQueue) GetEvictableTxs(priority, txSize, totalSize, cap int64) []*WrappedTx { pq.mtx.RLock() defer pq.mtx.RUnlock() - - txs := make([]*WrappedTx, 0, len(pq.txs)) - txs = append(txs, pq.txs...) + txs := append([]*WrappedTx{}, pq.txs...) for _, queue := range pq.evmQueue { txs = append(txs, queue[1:]...) } @@ -161,12 +158,16 @@ func (pq *TxPriorityQueue) NumTxs() int { } func (pq *TxPriorityQueue) removeQueuedEvmTxUnsafe(tx *WrappedTx) (removedIdx int) { - if queue, ok := pq.evmQueue[tx.evmAddress]; ok { + evm, ok := tx.evm.Get() + if !ok { + return -1 + } + if queue, ok := pq.evmQueue[evm.address]; ok { for i, t := range queue { if t.Hash() == tx.Hash() { - pq.evmQueue[tx.evmAddress] = append(queue[:i], queue[i+1:]...) - if len(pq.evmQueue[tx.evmAddress]) == 0 { - delete(pq.evmQueue, tx.evmAddress) + pq.evmQueue[evm.address] = append(queue[:i], queue[i+1:]...) + if len(pq.evmQueue[evm.address]) == 0 { + delete(pq.evmQueue, evm.address) } return i } @@ -199,31 +200,32 @@ func (pq *TxPriorityQueue) RemoveTx(tx *WrappedTx, shouldReenqueue bool) (toBeRe if idx, ok := pq.findTxIndexUnsafe(tx); ok { heap.Remove(pq, idx) - if tx.isEVM { + if evm, ok := tx.evm.Get(); ok { removedIdx = pq.removeQueuedEvmTxUnsafe(tx) - if !shouldReenqueue && len(pq.evmQueue[tx.evmAddress]) > 0 { - heap.Push(pq, pq.evmQueue[tx.evmAddress][0]) + if !shouldReenqueue && len(pq.evmQueue[evm.address]) > 0 { + heap.Push(pq, pq.evmQueue[evm.address][0]) } } - } else if tx.isEVM { + } else if tx.evm.IsPresent() { removedIdx = pq.removeQueuedEvmTxUnsafe(tx) } - if tx.isEVM && shouldReenqueue && len(pq.evmQueue[tx.evmAddress]) > 0 && removedIdx >= 0 { - toBeReenqueued = pq.evmQueue[tx.evmAddress][removedIdx:] + if evm, ok := tx.evm.Get(); ok && shouldReenqueue && len(pq.evmQueue[evm.address]) > 0 && removedIdx >= 0 { + toBeReenqueued = pq.evmQueue[evm.address][removedIdx:] } return } func (pq *TxPriorityQueue) pushTxUnsafe(tx *WrappedTx) { - if !tx.isEVM { + evm, ok := tx.evm.Get() + if !ok { heap.Push(pq, tx) return } // if there aren't other waiting txs, init and return - queue, exists := pq.evmQueue[tx.evmAddress] + queue, exists := pq.evmQueue[evm.address] if !exists { - pq.evmQueue[tx.evmAddress] = []*WrappedTx{tx} + pq.evmQueue[evm.address] = []*WrappedTx{tx} heap.Push(pq, tx) return } @@ -234,107 +236,15 @@ func (pq *TxPriorityQueue) pushTxUnsafe(tx *WrappedTx) { // the queue's first item (and ONLY the first item) must be on the heap // if this tx is before the first item, then we need to remove the first // item from the heap - if tx.IsBefore(first) { + if evm.nonce < first.EVMNonce() { if idx, ok := pq.findTxIndexUnsafe(first); ok { heap.Remove(pq, idx) } heap.Push(pq, tx) } - pq.evmQueue[tx.evmAddress] = insertToEVMQueue(queue, tx, binarySearch(queue, tx)) + pq.evmQueue[evm.address] = insertToEVMQueue(queue, tx, binarySearch(queue, evm.nonce)) } -// These are available if we need to test the invariant checks -// these can be used to troubleshoot invariant violations -//func (pq *TxPriorityQueue) checkInvariants(msg string) { -// uniqHashes := make(map[string]bool) -// for idx, tx := range pq.txs { -// if tx == nil { -// pq.print() -// panic(fmt.Sprintf("DEBUG PRINT: found nil item on heap: idx=%d\n", idx)) -// } -// if tx.tx == nil { -// pq.print() -// panic(fmt.Sprintf("DEBUG PRINT: found nil tx.tx on heap: idx=%d\n", idx)) -// } -// if _, ok := uniqHashes[fmt.Sprintf("%x", tx.tx.Hash())]; ok { -// pq.print() -// panic(fmt.Sprintf("INVARIANT (%s): duplicate hash=%x in heap", msg, tx.tx.Hash())) -// } -// uniqHashes[fmt.Sprintf("%x", tx.tx.Hash())] = true -// -// //if _, ok := pq.keys[tx.tx.Hash()]; !ok { -// // pq.print() -// // panic(fmt.Sprintf("INVARIANT (%s): tx in heap but not in keys hash=%x", msg, tx.tx.Hash())) -// //} -// -// if tx.isEVM { -// if queue, ok := pq.evmQueue[tx.evmAddress]; ok { -// if queue[0].tx.Hash() != tx.tx.Hash() { -// pq.print() -// panic(fmt.Sprintf("INVARIANT (%s): tx in heap but not at front of evmQueue hash=%x", msg, tx.tx.Hash())) -// } -// } else { -// pq.print() -// panic(fmt.Sprintf("INVARIANT (%s): tx in heap but not in evmQueue hash=%x", msg, tx.tx.Hash())) -// } -// } -// } -// -// // each item in all queues should be unique nonce -// for _, queue := range pq.evmQueue { -// hashes := make(map[string]bool) -// for idx, tx := range queue { -// if idx == 0 { -// _, ok := pq.findTxIndexUnsafe(tx) -// if !ok { -// pq.print() -// panic(fmt.Sprintf("INVARIANT (%s): did not find tx[0] hash=%x nonce=%d in heap", msg, tx.tx.Hash(), tx.evmNonce)) -// } -// } -// //if _, ok := pq.keys[tx.tx.Hash()]; !ok { -// // pq.print() -// // panic(fmt.Sprintf("INVARIANT (%s): tx in heap but not in keys hash=%x", msg, tx.tx.Hash())) -// //} -// if _, ok := hashes[fmt.Sprintf("%x", tx.tx.Hash())]; ok { -// pq.print() -// panic(fmt.Sprintf("INVARIANT (%s): duplicate hash=%x in queue nonce=%d", msg, tx.tx.Hash(), tx.evmNonce)) -// } -// hashes[fmt.Sprintf("%x", tx.tx.Hash())] = true -// } -// } -//} - -// for debugging situations where invariant violations occur -//func (pq *TxPriorityQueue) print() { -// fmt.Println("PRINT PRIORITY QUEUE ****************** ") -// for _, tx := range pq.txs { -// if tx == nil { -// fmt.Printf("DEBUG PRINT: heap (nil): nonce=?, hash=?\n") -// continue -// } -// if tx.tx == nil { -// fmt.Printf("DEBUG PRINT: heap (%s): nonce=%d, tx.tx is nil \n", tx.evmAddress, tx.evmNonce) -// continue -// } -// fmt.Printf("DEBUG PRINT: heap (%s): nonce=%d, hash=%x, time=%d\n", tx.evmAddress, tx.evmNonce, tx.tx.Hash(), tx.timestamp.UnixNano()) -// } -// -// for addr, queue := range pq.evmQueue { -// for idx, tx := range queue { -// if tx == nil { -// fmt.Printf("DEBUG PRINT: found nil item on evmQueue(%s): idx=%d\n", addr, idx) -// continue -// } -// if tx.tx == nil { -// fmt.Printf("DEBUG PRINT: found nil tx.tx on evmQueue(%s): idx=%d\n", addr, idx) -// continue -// } -// -// fmt.Printf("DEBUG PRINT: evmQueue(%s)[%d]: nonce=%d, hash=%x, time=%d\n", tx.evmAddress, idx, tx.evmNonce, tx.tx.Hash(), tx.timestamp.UnixNano()) -// } -// } -//} - // PushTx adds a valid transaction to the priority queue. It is thread safe. func (pq *TxPriorityQueue) PushTx(tx *WrappedTx) (*WrappedTx, bool) { pq.mtx.Lock() @@ -375,7 +285,7 @@ func (pq *TxPriorityQueue) popTxUnsafe() *WrappedTx { } // non-evm transactions do not have txs waiting on a nonce - if !tx.isEVM { + if !tx.evm.IsPresent() { return tx } @@ -387,8 +297,9 @@ func (pq *TxPriorityQueue) popTxUnsafe() *WrappedTx { pq.removeQueuedEvmTxUnsafe(tx) // if there is a next item, now it can be added to the heap - if len(pq.evmQueue[tx.evmAddress]) > 0 { - heap.Push(pq, pq.evmQueue[tx.evmAddress][0]) + evm, _ := tx.evm.Get() + if len(pq.evmQueue[evm.address]) > 0 { + heap.Push(pq, pq.evmQueue[evm.address][0]) } return tx diff --git a/sei-tendermint/internal/mempool/priority_queue_test.go b/sei-tendermint/internal/mempool/priority_queue_test.go index 3533c1aced..c2f63de6a2 100644 --- a/sei-tendermint/internal/mempool/priority_queue_test.go +++ b/sei-tendermint/internal/mempool/priority_queue_test.go @@ -8,10 +8,22 @@ import ( "testing" "time" + "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/stretchr/testify/require" ) +func wrappedEVMTx(tx types.Tx, address string, nonce uint64, priority int64) *WrappedTx { + return &WrappedTx{ + hashedTx: newHashedTx(tx), + priority: priority, + evm: utils.Some(evmTx{ + address: address, + nonce: nonce, + }), + } +} + // TxTestCase represents a single test case for the TxPriorityQueue type TxTestCase struct { name string @@ -50,7 +62,7 @@ func TestTxPriorityQueue_ReapHalf(t *testing.T) { func TestAvoidPanicIfTransactionIsNil(t *testing.T) { pq := NewTxPriorityQueue() - pq.Push(&WrappedTx{isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}) + pq.Push(wrappedEVMTx(nil, "0xabc", 1, 10)) pq.txs = append(pq.txs, nil) var count int @@ -67,70 +79,70 @@ func TestTxPriorityQueue_PriorityAndNonceOrdering(t *testing.T) { { name: "PriorityWithEVMAndNonEVMDuplicateNonce", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("1")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {hashedTx: newHashedTx(types.Tx("3")), isEVM: true, evmAddress: "0xabc", evmNonce: 3, priority: 9}, - {hashedTx: newHashedTx(types.Tx("2")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 7}, + wrappedEVMTx(types.Tx("1"), "0xabc", 1, 10), + wrappedEVMTx(types.Tx("3"), "0xabc", 3, 9), + wrappedEVMTx(types.Tx("2"), "0xabc", 1, 7), }, expectedOutput: []int64{1, 3}, }, { name: "PriorityWithEVMAndNonEVMDuplicateNonce", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("1")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {hashedTx: newHashedTx(types.Tx("2")), isEVM: false, priority: 9}, - {hashedTx: newHashedTx(types.Tx("4")), isEVM: true, evmAddress: "0xabc", evmNonce: 0, priority: 9}, // Same EVM address as first, lower nonce - {hashedTx: newHashedTx(types.Tx("5")), isEVM: true, evmAddress: "0xdef", evmNonce: 1, priority: 7}, - {hashedTx: newHashedTx(types.Tx("3")), isEVM: true, evmAddress: "0xdef", evmNonce: 0, priority: 8}, - {hashedTx: newHashedTx(types.Tx("6")), isEVM: false, priority: 6}, - {hashedTx: newHashedTx(types.Tx("7")), isEVM: true, evmAddress: "0xghi", evmNonce: 2, priority: 5}, + wrappedEVMTx(types.Tx("1"), "0xabc", 1, 10), + {hashedTx: newHashedTx(types.Tx("2")), priority: 9}, + wrappedEVMTx(types.Tx("4"), "0xabc", 0, 9), // Same EVM address as first, lower nonce + wrappedEVMTx(types.Tx("5"), "0xdef", 1, 7), + wrappedEVMTx(types.Tx("3"), "0xdef", 0, 8), + {hashedTx: newHashedTx(types.Tx("6")), priority: 6}, + wrappedEVMTx(types.Tx("7"), "0xghi", 2, 5), }, expectedOutput: []int64{2, 4, 1, 3, 5, 6, 7}, }, { name: "PriorityWithEVMAndNonEVM", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("1")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {hashedTx: newHashedTx(types.Tx("2")), isEVM: false, priority: 9}, - {hashedTx: newHashedTx(types.Tx("4")), isEVM: true, evmAddress: "0xabc", evmNonce: 0, priority: 9}, // Same EVM address as first, lower nonce - {hashedTx: newHashedTx(types.Tx("5")), isEVM: true, evmAddress: "0xdef", evmNonce: 1, priority: 7}, - {hashedTx: newHashedTx(types.Tx("3")), isEVM: true, evmAddress: "0xdef", evmNonce: 0, priority: 8}, - {hashedTx: newHashedTx(types.Tx("6")), isEVM: false, priority: 6}, - {hashedTx: newHashedTx(types.Tx("7")), isEVM: true, evmAddress: "0xghi", evmNonce: 2, priority: 5}, + wrappedEVMTx(types.Tx("1"), "0xabc", 1, 10), + {hashedTx: newHashedTx(types.Tx("2")), priority: 9}, + wrappedEVMTx(types.Tx("4"), "0xabc", 0, 9), // Same EVM address as first, lower nonce + wrappedEVMTx(types.Tx("5"), "0xdef", 1, 7), + wrappedEVMTx(types.Tx("3"), "0xdef", 0, 8), + {hashedTx: newHashedTx(types.Tx("6")), priority: 6}, + wrappedEVMTx(types.Tx("7"), "0xghi", 2, 5), }, expectedOutput: []int64{2, 4, 1, 3, 5, 6, 7}, }, { name: "IdenticalPrioritiesAndNoncesDifferentAddresses", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("1")), isEVM: true, evmAddress: "0xabc", evmNonce: 2, priority: 5}, - {hashedTx: newHashedTx(types.Tx("2")), isEVM: true, evmAddress: "0xdef", evmNonce: 2, priority: 5}, - {hashedTx: newHashedTx(types.Tx("3")), isEVM: true, evmAddress: "0xghi", evmNonce: 2, priority: 5}, + wrappedEVMTx(types.Tx("1"), "0xabc", 2, 5), + wrappedEVMTx(types.Tx("2"), "0xdef", 2, 5), + wrappedEVMTx(types.Tx("3"), "0xghi", 2, 5), }, expectedOutput: []int64{1, 2, 3}, }, { name: "InterleavedEVAndNonEVMTransactions", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("7")), isEVM: false, priority: 15}, - {hashedTx: newHashedTx(types.Tx("8")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 20}, - {hashedTx: newHashedTx(types.Tx("9")), isEVM: false, priority: 10}, - {hashedTx: newHashedTx(types.Tx("10")), isEVM: true, evmAddress: "0xdef", evmNonce: 2, priority: 20}, + {hashedTx: newHashedTx(types.Tx("7")), priority: 15}, + wrappedEVMTx(types.Tx("8"), "0xabc", 1, 20), + {hashedTx: newHashedTx(types.Tx("9")), priority: 10}, + wrappedEVMTx(types.Tx("10"), "0xdef", 2, 20), }, expectedOutput: []int64{8, 10, 7, 9}, }, { name: "SameAddressPriorityDifferentNonces", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("11")), isEVM: true, evmAddress: "0xabc", evmNonce: 3, priority: 10}, - {hashedTx: newHashedTx(types.Tx("12")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {hashedTx: newHashedTx(types.Tx("13")), isEVM: true, evmAddress: "0xabc", evmNonce: 2, priority: 10}, + wrappedEVMTx(types.Tx("11"), "0xabc", 3, 10), + wrappedEVMTx(types.Tx("12"), "0xabc", 1, 10), + wrappedEVMTx(types.Tx("13"), "0xabc", 2, 10), }, expectedOutput: []int64{12, 13, 11}, }, { name: "OneItem", inputTxs: []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("14")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, + wrappedEVMTx(types.Tx("14"), "0xabc", 1, 10), }, expectedOutput: []int64{14}, }, @@ -163,17 +175,23 @@ func TestTxPriorityQueue_SameAddressDifferentNonces(t *testing.T) { address := "0x123" // Insert transactions with the same address but different nonces and priorities - pq.PushTx(&WrappedTx{hashedTx: newHashedTx(types.Tx("tx1")), isEVM: true, evmAddress: address, evmNonce: 2, priority: 10}) - pq.PushTx(&WrappedTx{hashedTx: newHashedTx(types.Tx("tx2")), isEVM: true, evmAddress: address, evmNonce: 1, priority: 5}) - pq.PushTx(&WrappedTx{hashedTx: newHashedTx(types.Tx("tx3")), isEVM: true, evmAddress: address, evmNonce: 3, priority: 15}) + pq.PushTx(wrappedEVMTx(types.Tx("tx1"), address, 2, 10)) + pq.PushTx(wrappedEVMTx(types.Tx("tx2"), address, 1, 5)) + pq.PushTx(wrappedEVMTx(types.Tx("tx3"), address, 3, 15)) // Pop transactions and verify they are in the correct order of nonce tx1 := pq.PopTx() - require.Equal(t, uint64(1), tx1.evmNonce) + evm, ok := tx1.evm.Get() + require.True(t, ok) + require.Equal(t, uint64(1), evm.nonce) tx2 := pq.PopTx() - require.Equal(t, uint64(2), tx2.evmNonce) + evm, ok = tx2.evm.Get() + require.True(t, ok) + require.Equal(t, uint64(2), evm.nonce) tx3 := pq.PopTx() - require.Equal(t, uint64(3), tx3.evmNonce) + evm, ok = tx3.evm.Get() + require.True(t, ok) + require.Equal(t, uint64(3), evm.nonce) } func TestTxPriorityQueue(t *testing.T) { @@ -312,18 +330,20 @@ func TestTxPriorityQueue_RemoveTxEvm(t *testing.T) { pq := NewTxPriorityQueue() tx1 := &WrappedTx{ - hashedTx: newHashedTx(types.Tx("tx1")), - priority: 1, - isEVM: true, - evmAddress: "0xabc", - evmNonce: 1, + hashedTx: newHashedTx(types.Tx("tx1")), + priority: 1, + evm: utils.Some(evmTx{ + address: "0xabc", + nonce: 1, + }), } tx2 := &WrappedTx{ - hashedTx: newHashedTx(types.Tx("tx2")), - priority: 1, - isEVM: true, - evmAddress: "0xabc", - evmNonce: 2, + hashedTx: newHashedTx(types.Tx("tx2")), + priority: 1, + evm: utils.Some(evmTx{ + address: "0xabc", + nonce: 2, + }), } pq.PushTx(tx1) @@ -380,50 +400,50 @@ func TestTxPriorityQueue_TryReplacement(t *testing.T) { expectedHeap []*WrappedTx }{ // non-evm transaction is inserted into empty queue - {&WrappedTx{isEVM: false}, []*WrappedTx{}, false, false, []*WrappedTx{{isEVM: false}}, []*WrappedTx{{isEVM: false}}}, + {&WrappedTx{}, []*WrappedTx{}, false, false, []*WrappedTx{{}}, []*WrappedTx{{}}}, // evm transaction is inserted into empty queue - {&WrappedTx{isEVM: true, evmAddress: "addr1"}, []*WrappedTx{}, false, false, []*WrappedTx{{isEVM: true, evmAddress: "addr1"}}, []*WrappedTx{{isEVM: true, evmAddress: "addr1"}}}, + {wrappedEVMTx(nil, "addr1", 0, 0), []*WrappedTx{}, false, false, []*WrappedTx{wrappedEVMTx(nil, "addr1", 0, 0)}, []*WrappedTx{wrappedEVMTx(nil, "addr1", 0, 0)}}, // evm transaction (new nonce) is inserted into queue with existing tx (lower nonce) { - &WrappedTx{hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 1, priority: 100}, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, + wrappedEVMTx(types.Tx("abc"), "addr1", 1, 100), []*WrappedTx{ + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), }, false, false, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, - {hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 1, priority: 100}, + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), + wrappedEVMTx(types.Tx("abc"), "addr1", 1, 100), }, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, - {hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 1, priority: 100}, + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), + wrappedEVMTx(types.Tx("abc"), "addr1", 1, 100), }, }, // evm transaction (new nonce) is not inserted because it's a duplicate nonce and same priority { - &WrappedTx{hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, + wrappedEVMTx(types.Tx("abc"), "addr1", 0, 100), []*WrappedTx{ + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), }, false, true, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), }, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), }, }, // evm transaction (new nonce) replaces the existing nonce transaction because its priority is higher { - &WrappedTx{hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 101}, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, + wrappedEVMTx(types.Tx("abc"), "addr1", 0, 101), []*WrappedTx{ + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), }, true, false, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 101}, + wrappedEVMTx(types.Tx("abc"), "addr1", 0, 101), }, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 101}, + wrappedEVMTx(types.Tx("abc"), "addr1", 0, 101), }, }, { - &WrappedTx{hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 1, priority: 100}, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, - {hashedTx: newHashedTx(types.Tx("ghi")), isEVM: true, evmAddress: "addr1", evmNonce: 1, priority: 99}, + wrappedEVMTx(types.Tx("abc"), "addr1", 1, 100), []*WrappedTx{ + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), + wrappedEVMTx(types.Tx("ghi"), "addr1", 1, 99), }, true, false, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, - {hashedTx: newHashedTx(types.Tx("abc")), isEVM: true, evmAddress: "addr1", evmNonce: 1, priority: 100}, + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), + wrappedEVMTx(types.Tx("abc"), "addr1", 1, 100), }, []*WrappedTx{ - {hashedTx: newHashedTx(types.Tx("def")), isEVM: true, evmAddress: "addr1", evmNonce: 0, priority: 100}, + wrappedEVMTx(types.Tx("def"), "addr1", 0, 100), }, }, } { @@ -438,15 +458,28 @@ func TestTxPriorityQueue_TryReplacement(t *testing.T) { require.Nil(t, replaced) } require.Equal(t, test.expectedDropped, !inserted) - for i, q := range pq.evmQueue[test.tx.evmAddress] { + txEVM, ok := test.tx.evm.Get() + require.True(t, ok) + for i, q := range pq.evmQueue[txEVM.address] { require.Equal(t, test.expectedQueue[i].Hash(), q.Hash()) require.Equal(t, test.expectedQueue[i].priority, q.priority) - require.Equal(t, test.expectedQueue[i].evmNonce, q.evmNonce) + expectedEVM, ok := test.expectedQueue[i].evm.Get() + require.True(t, ok) + queueEVM, ok := q.evm.Get() + require.True(t, ok) + require.Equal(t, expectedEVM.nonce, queueEVM.nonce) } for i, q := range pq.txs { require.Equal(t, test.expectedHeap[i].Hash(), q.Hash()) require.Equal(t, test.expectedHeap[i].priority, q.priority) - require.Equal(t, test.expectedHeap[i].evmNonce, q.evmNonce) + expectedEVM, ok := test.expectedHeap[i].evm.Get() + if ok { + queueEVM, ok := q.evm.Get() + require.True(t, ok) + require.Equal(t, expectedEVM.nonce, queueEVM.nonce) + } else { + require.False(t, q.evm.IsPresent()) + } } } } diff --git a/sei-tendermint/internal/mempool/recheck_drain_test.go b/sei-tendermint/internal/mempool/recheck_drain_test.go index 7b0c858c5a..78628c84ea 100644 --- a/sei-tendermint/internal/mempool/recheck_drain_test.go +++ b/sei-tendermint/internal/mempool/recheck_drain_test.go @@ -8,17 +8,18 @@ import ( "sync" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/sei-tendermint/abci/example/code" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/proxy" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils/require" + "github.com/sei-protocol/sei-chain/sei-tendermint/types" ) // evmNonceApp models a Sei-like EVM antehandler for mempool tests: // - tracks the next-expected nonce per sender (the "mined" nonce frontier) -// - on CheckTx for txNonce > nextNonce, returns IsPending whose checker -// resolves to Accepted as soon as nextNonce catches up. +// - returns nonce metadata used by mempool-side pending evaluation // // Test format: "evm===". type evmNonceApp struct { @@ -83,27 +84,6 @@ func (a *evmNonceApp) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) *ab IsEVM: true, Priority: priority, } - if nonce > expected { - // Ahead of expected — mark pending. The checker re-evaluates against - // the live nextNonce map at handlePendingTransactions time. - // Mirror Sei's EVM antehandler: once the sender has *any* expected-nonce - // progress (i.e. nextNonce has advanced past where this tx was submitted), - // all sequentially-queued nonces from that sender are eligible. This is - // what `CalculateNextNonce(includePending=true)` does in x/evm/keeper. - res.IsPending = utils.Some(abci.PendingTxChecker(func() abci.PendingTxCheckerResponse { - a.mu.Lock() - cur := a.nextNonce[sender] - a.mu.Unlock() - switch { - case nonce < cur: - return abci.Rejected - case nonce >= cur: - return abci.Accepted - default: - return abci.Pending - } - })) - } return res } @@ -111,13 +91,19 @@ func (a *evmNonceApp) GetTxPriorityHint(context.Context, *abci.RequestGetTxPrior return &abci.ResponseGetTxPriorityHint{Priority: 1}, nil } +func (a *evmNonceApp) EvmNonce(addr common.Address) uint64 { + a.mu.Lock() + defer a.mu.Unlock() + return a.nextNonce[addr.Hex()] +} + // TestTxMempool_DescendingNonceDrain exercises the producer-style flow: // submit N EVM nonces from a single sender in descending order (worst case // for the gap-pending pool — every tx except the last is ahead of expected // at CheckTx time), then drain by repeatedly PopTxs-ing and Update-ing. // // Regression for the recheck=true eviction loop: with recheck=true, the -// EVM antehandler's IsPending response for the rest of the per-sender +// mempool-side pending classification for the rest of the per-sender // evmQueue causes handleRecheckResult to evict + async re-CheckTx every // non-head tx, dumping them back into pendingTxs. The mempool's priority // pool collapses to 1 each Update cycle and the chain only mines 1 per @@ -175,3 +161,47 @@ func TestTxMempool_DescendingNonceDrain(t *testing.T) { require.Zero(t, txmp.Size(), "mempool should fully drain within %d blocks", maxBlocks) require.Equal(t, uint64(N), app.nextNonce[sender], "all N nonces should have been mined") } + +func TestTxMempool_EvmNextPendingNonceIncludesPendingTransactions(t *testing.T) { + ctx := t.Context() + sender := common.HexToAddress("0x00000000000000000000000000000000000000aa") + + app := newEVMNonceApp() + app.nextNonce[sender.Hex()] = 5 + txmp := setup(t, proxy.New(app, proxy.NopMetrics()), 5000, NopTxConstraintsFetcher) + + for _, nonce := range []uint64{7, 5, 6} { + tx := []byte(fmt.Sprintf("evm=%s=%d=1", sender.Hex(), nonce)) + _, err := txmp.CheckTx(ctx, tx, TxInfo{}) + require.NoError(t, err) + } + + require.Equal(t, 1, txmp.NumTxsNotPending()) + require.Equal(t, 2, txmp.PendingSize()) + require.Equal(t, uint64(8), txmp.EvmNextPendingNonce(sender)) +} + +func TestTxMempool_EvmNextPendingNonceReplacesSameNonceByPriority(t *testing.T) { + ctx := t.Context() + sender := common.HexToAddress("0x00000000000000000000000000000000000000bb") + + app := newEVMNonceApp() + app.nextNonce[sender.Hex()] = 5 + txmp := setup(t, proxy.New(app, proxy.NopMetrics()), 5000, NopTxConstraintsFetcher) + + lowPriorityTx := []byte(fmt.Sprintf("evm=%s=%d=%d", sender.Hex(), 6, 1)) + highPriorityTx := []byte(fmt.Sprintf("evm=%s=%d=%d", sender.Hex(), 6, 2)) + + _, err := txmp.CheckTx(ctx, lowPriorityTx, TxInfo{}) + require.NoError(t, err) + _, err = txmp.CheckTx(ctx, highPriorityTx, TxInfo{}) + require.NoError(t, err) + + require.Equal(t, 2, txmp.PendingSize(), "pending store keeps both txs") + for byAddrNonce := range txmp.byAddrNonce.Lock() { + wtx, ok := byAddrNonce[evmAddrNonce{Address: sender, Nonce: 6}] + require.True(t, ok, "nonce bookkeeping should track one occupied nonce") + require.Equal(t, types.Tx(highPriorityTx).Hash(), wtx.Hash()) + } + require.Equal(t, uint64(5), txmp.EvmNextPendingNonce(sender)) +} diff --git a/sei-tendermint/internal/mempool/tx.go b/sei-tendermint/internal/mempool/tx.go index 08ae0d4b27..caba68da04 100644 --- a/sei-tendermint/internal/mempool/tx.go +++ b/sei-tendermint/internal/mempool/tx.go @@ -3,6 +3,7 @@ package mempool import ( "context" "errors" + "math/big" "sync/atomic" "time" @@ -76,19 +77,24 @@ type WrappedTx struct { // a reCheckTx callback executed. removed bool - // this is the callback that can be called when a transaction is removed - removeHandler func(removeFromCache bool) - // evm properties that aid in prioritization - evmAddress string - evmNonce uint64 - isEVM bool + evm utils.Option[evmTx] +} + +type evmTx struct { + address string // hex encoded address + nonce uint64 + // evmRequiredBalance is the sender balance threshold for this EVM tx to become active. + requiredBalance *big.Int } // IsBefore returns true if the WrappedTx is before the given WrappedTx // this applies to EVM transactions only -func (wtx *WrappedTx) IsBefore(tx *WrappedTx) bool { - return wtx.evmNonce < tx.evmNonce +func (wtx *WrappedTx) EVMNonce() uint64 { + if evm, ok := wtx.evm.Get(); ok { + return evm.nonce + } + return 0 } type txStoreInner struct { @@ -314,35 +320,29 @@ type PendingTxs struct { } type pendingTxsInner struct { - txs []TxWithResponse -} - -type TxWithResponse struct { - tx *WrappedTx - checkTxResponse *abci.ResponseCheckTxV2 - txInfo TxInfo + txs []*WrappedTx } func NewPendingTxs(conf *Config) *PendingTxs { return &PendingTxs{ inner: utils.NewRWMutex(&pendingTxsInner{ - txs: []TxWithResponse{}, + txs: []*WrappedTx{}, }), config: conf, } } -func (p *PendingTxs) EvaluatePendingTransactions() ( - acceptedTxs []TxWithResponse, - rejectedTxs []TxWithResponse, + +func (p *PendingTxs) EvaluatePendingTransactions( + evaluate func(*WrappedTx) abci.PendingTxCheckerResponse, +) ( + acceptedTxs []*WrappedTx, + rejectedTxs []*WrappedTx, ) { poppedIndices := []int{} for inner := range p.inner.Lock() { for i := 0; i < len(inner.txs); i++ { - checker, ok := inner.txs[i].checkTxResponse.IsPending.Get() - if !ok { - continue - } - switch checker() { + result := evaluate(inner.txs[i]) + switch result { case abci.Accepted: acceptedTxs = append(acceptedTxs, inner.txs[i]) poppedIndices = append(poppedIndices, i) @@ -362,7 +362,7 @@ func (p *PendingTxs) popTxsAtIndices(inner *pendingTxsInner, indices []int) { if len(indices) == 0 { return } - newTxs := make([]TxWithResponse, 0, max(0, len(inner.txs)-len(indices))) + newTxs := make([]*WrappedTx, 0, max(0, len(inner.txs)-len(indices))) start := 0 for _, idx := range indices { if idx <= start-1 { @@ -371,7 +371,7 @@ func (p *PendingTxs) popTxsAtIndices(inner *pendingTxsInner, indices []int) { if idx >= len(inner.txs) { panic("indices popped from pending tx store out of range") } - p.sizeBytes.Add(int64(-inner.txs[idx].tx.Size())) + p.sizeBytes.Add(int64(-inner.txs[idx].Size())) newTxs = append(newTxs, inner.txs[start:idx]...) start = idx + 1 } @@ -379,17 +379,12 @@ func (p *PendingTxs) popTxsAtIndices(inner *pendingTxsInner, indices []int) { inner.txs = newTxs } -func (p *PendingTxs) Insert(tx *WrappedTx, resCheckTx *abci.ResponseCheckTxV2, txInfo TxInfo) error { +func (p *PendingTxs) Insert(tx *WrappedTx) error { for inner := range p.inner.Lock() { if len(inner.txs) >= p.config.PendingSize || int64(tx.Size())+p.sizeBytes.Load() > p.config.MaxPendingTxsBytes { return errors.New("pending store is full") } - - inner.txs = append(inner.txs, TxWithResponse{ - tx: tx, - checkTxResponse: resCheckTx, - txInfo: txInfo, - }) + inner.txs = append(inner.txs, tx) p.sizeBytes.Add(int64(tx.Size())) return nil } @@ -398,7 +393,7 @@ func (p *PendingTxs) Insert(tx *WrappedTx, resCheckTx *abci.ResponseCheckTxV2, t func (p *PendingTxs) SizeBytes() int64 { return p.sizeBytes.Load() } -func (p *PendingTxs) Peek(max int) []TxWithResponse { +func (p *PendingTxs) Peek(max int) []*WrappedTx { for inner := range p.inner.RLock() { // priority is fifo if max > len(inner.txs) { @@ -427,12 +422,12 @@ func (p *PendingTxs) PurgeExpired(blockHeight int64, now time.Time, cb func(wtx idxFirstNotExpiredTx := len(inner.txs) for i, ptx := range inner.txs { // once found, we can break because these are ordered - if (blockHeight - ptx.tx.height) <= p.config.TTLNumBlocks { + if (blockHeight - ptx.height) <= p.config.TTLNumBlocks { idxFirstNotExpiredTx = i break } - cb(ptx.tx) - p.sizeBytes.Add(int64(-ptx.tx.Size())) + cb(ptx) + p.sizeBytes.Add(int64(-ptx.Size())) } inner.txs = inner.txs[idxFirstNotExpiredTx:] } @@ -445,12 +440,12 @@ func (p *PendingTxs) PurgeExpired(blockHeight int64, now time.Time, cb func(wtx idxFirstNotExpiredTx := len(inner.txs) for i, ptx := range inner.txs { // once found, we can break because these are ordered - if now.Sub(ptx.tx.timestamp) <= p.config.TTLDuration { + if now.Sub(ptx.timestamp) <= p.config.TTLDuration { idxFirstNotExpiredTx = i break } - cb(ptx.tx) - p.sizeBytes.Add(int64(-ptx.tx.Size())) + cb(ptx) + p.sizeBytes.Add(int64(-ptx.Size())) } inner.txs = inner.txs[idxFirstNotExpiredTx:] } diff --git a/sei-tendermint/internal/mempool/tx_test.go b/sei-tendermint/internal/mempool/tx_test.go index 5f0689ca5a..0afc7e57cb 100644 --- a/sei-tendermint/internal/mempool/tx_test.go +++ b/sei-tendermint/internal/mempool/tx_test.go @@ -6,7 +6,6 @@ import ( "testing" "time" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils/require" "github.com/sei-protocol/sei-chain/sei-tendermint/types" @@ -291,18 +290,19 @@ func TestPendingTxsPopTxsGood(t *testing.T) { }, } { for inner := range pendingTxs.inner.Lock() { - inner.txs = []TxWithResponse{} + inner.txs = []*WrappedTx{} pendingTxs.sizeBytes.Store(0) for i := 0; i < test.origLen; i++ { - inner.txs = append(inner.txs, TxWithResponse{ - tx: &WrappedTx{hashedTx: newHashedTx(types.Tx{})}, - txInfo: TxInfo{SenderID: uint16(i)}, + inner.txs = append(inner.txs, &WrappedTx{ + hashedTx: newHashedTx(types.Tx{byte(i)}), + peers: map[uint16]struct{}{uint16(i): {}}, }) } pendingTxs.popTxsAtIndices(inner, test.popIndices) require.Equal(t, len(test.expected), len(inner.txs)) for i, e := range test.expected { - require.Equal(t, e, int(inner.txs[i].txInfo.SenderID)) + _, ok := inner.txs[i].peers[uint16(e)] + require.True(t, ok) } } } @@ -318,7 +318,7 @@ func TestPendingTxsPopTxsBad(t *testing.T) { }) // out of order for inner := range pendingTxs.inner.Lock() { - inner.txs = []TxWithResponse{{}, {}, {}} + inner.txs = []*WrappedTx{{}, {}, {}} } require.Panics(t, func() { for inner := range pendingTxs.inner.Lock() { @@ -354,10 +354,10 @@ func TestPendingTxs_InsertCondition(t *testing.T) { } tx2Size := tx2.Size() - err := pendingTxs.Insert(tx1, &abci.ResponseCheckTxV2{}, TxInfo{}) + err := pendingTxs.Insert(tx1) require.Nil(t, err) - err = pendingTxs.Insert(tx2, &abci.ResponseCheckTxV2{}, TxInfo{}) + err = pendingTxs.Insert(tx2) require.Nil(t, err) // Should fail due to pending store size limit @@ -366,7 +366,7 @@ func TestPendingTxs_InsertCondition(t *testing.T) { priority: 3, } - err = pendingTxs.Insert(tx3, &abci.ResponseCheckTxV2{}, TxInfo{}) + err = pendingTxs.Insert(tx3) require.NotNil(t, err) // Second test exceeding byte size condition @@ -374,10 +374,10 @@ func TestPendingTxs_InsertCondition(t *testing.T) { pendingTxs = NewPendingTxs(mempoolCfg) mempoolCfg.MaxPendingTxsBytes = int64(tx1Size + tx2Size) - err = pendingTxs.Insert(tx1, &abci.ResponseCheckTxV2{}, TxInfo{}) + err = pendingTxs.Insert(tx1) require.Nil(t, err) - err = pendingTxs.Insert(tx2, &abci.ResponseCheckTxV2{}, TxInfo{}) + err = pendingTxs.Insert(tx2) require.Nil(t, err) // Should fail due to exceeding max pending transaction bytes @@ -386,6 +386,6 @@ func TestPendingTxs_InsertCondition(t *testing.T) { priority: 3, } - err = pendingTxs.Insert(tx3, &abci.ResponseCheckTxV2{}, TxInfo{}) + err = pendingTxs.Insert(tx3) require.NotNil(t, err) } diff --git a/sei-tendermint/internal/proxy/proxy.go b/sei-tendermint/internal/proxy/proxy.go index 72b6a41b39..0193820e40 100644 --- a/sei-tendermint/internal/proxy/proxy.go +++ b/sei-tendermint/internal/proxy/proxy.go @@ -3,9 +3,11 @@ package proxy import ( "context" "fmt" + "math/big" "runtime/debug" "time" + "github.com/ethereum/go-ethereum/common" "github.com/go-kit/kit/metrics" "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" @@ -42,6 +44,16 @@ func (app *Proxy) GetTxPriorityHint(ctx context.Context, req *types.RequestGetTx return app.app.GetTxPriorityHint(ctx, req) } +func (app *Proxy) EvmNonce(addr common.Address) uint64 { + defer addTimeSample(app.metrics.MethodTiming.With("method", "evm_nonce", "type", "sync"))() + return app.app.EvmNonce(addr) +} + +func (app *Proxy) EvmBalance(addr common.Address) *big.Int { + defer addTimeSample(app.metrics.MethodTiming.With("method", "evm_balance", "type", "sync"))() + return app.app.EvmBalance(addr) +} + func (app *Proxy) Commit(ctx context.Context) (*types.ResponseCommit, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "commit", "type", "sync"))() return app.app.Commit(ctx) diff --git a/sei-tendermint/node/node.go b/sei-tendermint/node/node.go index 9c8ebb3298..484ce1ba51 100644 --- a/sei-tendermint/node/node.go +++ b/sei-tendermint/node/node.go @@ -36,6 +36,7 @@ import ( tmtime "github.com/sei-protocol/sei-chain/sei-tendermint/libs/time" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" "github.com/sei-protocol/sei-chain/sei-tendermint/privval" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-tendermint/types" _ "github.com/grafana/pyroscope-go/godeltaprof/http/pprof" @@ -87,7 +88,7 @@ func makeNode( dbProvider config.DBProvider, tracerProviderOptions []trace.TracerProviderOption, nodeMetrics *NodeMetrics, -) (_ service.Service, err error) { +) (_ local.NodeService, err error) { var cancel context.CancelFunc ctx, cancel = context.WithCancel(ctx) closers := []closer{convertCancelCloser(cancel)} diff --git a/sei-tendermint/node/public.go b/sei-tendermint/node/public.go index 8ca39d9493..72f6c86998 100644 --- a/sei-tendermint/node/public.go +++ b/sei-tendermint/node/public.go @@ -8,8 +8,8 @@ import ( abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/config" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/proxy" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/service" "github.com/sei-protocol/sei-chain/sei-tendermint/privval" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/seilog" "go.opentelemetry.io/otel/sdk/trace" @@ -30,7 +30,7 @@ func New( gen *tmtypes.GenesisDoc, tracerProviderOptions []trace.TracerProviderOption, nodeMetrics *NodeMetrics, -) (service.Service, error) { +) (local.NodeService, error) { proxyApp := proxy.New(app, nodeMetrics.proxy) nodeKey, err := tmtypes.LoadOrGenNodeKey(conf.NodeKeyFile()) if err != nil { diff --git a/sei-tendermint/node/seed.go b/sei-tendermint/node/seed.go index 1e19d9daa3..7f7a077cec 100644 --- a/sei-tendermint/node/seed.go +++ b/sei-tendermint/node/seed.go @@ -22,6 +22,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-tendermint/libs/service" tmtime "github.com/sei-protocol/sei-chain/sei-tendermint/libs/time" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-tendermint/types" ) @@ -52,7 +53,7 @@ func makeSeedNode( nodeKey types.NodeKey, genesisDocProvider genesisDocProvider, nodeMetrics *NodeMetrics, -) (_ service.Service, err error) { +) (_ local.NodeService, err error) { closers := []closer{} defer func() { if err != nil { diff --git a/sei-tendermint/rpc/client/local/local.go b/sei-tendermint/rpc/client/local/local.go index dff8fd0016..79330832da 100644 --- a/sei-tendermint/rpc/client/local/local.go +++ b/sei-tendermint/rpc/client/local/local.go @@ -6,11 +6,13 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/common" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/eventbus" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/pubsub" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/pubsub/query" rpccore "github.com/sei-protocol/sei-chain/sei-tendermint/internal/rpc/core" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/bytes" + "github.com/sei-protocol/sei-chain/sei-tendermint/libs/service" rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" "github.com/sei-protocol/sei-chain/sei-tendermint/types" @@ -41,12 +43,17 @@ backoff (10ms -> 20ms -> 40ms) until successful. */ type Local struct { *eventbus.EventBus - env *rpccore.Environment + *rpccore.Environment } +// Marker of the local node +// TODO(gprusak): replace with actual extra functionality +func (c *Local) IsLocal() {} + // NodeService describes the portion of the node interface that the // local RPC client constructor needs to build a local client. type NodeService interface { + service.Service RPCEnvironment() *rpccore.Environment EventBus() *eventbus.EventBus } @@ -58,131 +65,99 @@ func New(node NodeService) (*Local, error) { return nil, errors.New("rpc is nil") } return &Local{ - EventBus: node.EventBus(), - env: env, + EventBus: node.EventBus(), + Environment: env, }, nil } var _ rpcclient.Client = (*Local)(nil) -func (c *Local) Status(ctx context.Context) (*coretypes.ResultStatus, error) { - return c.env.Status(ctx) -} - -func (c *Local) LagStatus(ctx context.Context) (*coretypes.ResultLagStatus, error) { - return c.env.LagStatus(ctx) -} - -func (c *Local) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error) { - return c.env.ABCIInfo(ctx) -} - func (c *Local) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions) } func (c *Local) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { - return c.env.ABCIQuery(ctx, &coretypes.RequestABCIQuery{ + return c.Environment.ABCIQuery(ctx, &coretypes.RequestABCIQuery{ Path: path, Data: data, Height: coretypes.Int64(opts.Height), Prove: opts.Prove, }) } func (c *Local) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) { - return c.env.BroadcastTxCommit(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) + return c.Environment.BroadcastTxCommit(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) } func (c *Local) BroadcastTx(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { - return c.env.BroadcastTx(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) + return c.Environment.BroadcastTx(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) } func (c *Local) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { - return c.env.BroadcastTxAsync(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) + return c.Environment.BroadcastTxAsync(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) } func (c *Local) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { - return c.env.BroadcastTxSync(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) + return c.Environment.BroadcastTxSync(ctx, &coretypes.RequestBroadcastTx{Tx: tx}) } func (c *Local) UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { - return c.env.UnconfirmedTxs(ctx, &coretypes.RequestUnconfirmedTxs{ + return c.Environment.UnconfirmedTxs(ctx, &coretypes.RequestUnconfirmedTxs{ Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), }) } -func (c *Local) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) { - return c.env.NumUnconfirmedTxs(ctx) -} - func (c *Local) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) { - return c.env.CheckTx(ctx, &coretypes.RequestCheckTx{Tx: tx}) + return c.Environment.CheckTx(ctx, &coretypes.RequestCheckTx{Tx: tx}) } -func (c *Local) NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, error) { - return c.env.NetInfo(ctx) -} - -func (c *Local) DumpConsensusState(ctx context.Context) (*coretypes.ResultDumpConsensusState, error) { - return c.env.DumpConsensusState(ctx) +func (c *Local) EvmNextPendingNonce(addr common.Address) uint64 { + return c.Environment.Mempool.EvmNextPendingNonce(addr) } func (c *Local) ConsensusState(ctx context.Context) (*coretypes.ResultConsensusState, error) { - return c.env.GetConsensusState(ctx) + return c.Environment.GetConsensusState(ctx) } func (c *Local) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) { - return c.env.ConsensusParams(ctx, &coretypes.RequestConsensusParams{Height: (*coretypes.Int64)(height)}) -} - -func (c *Local) Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error) { - return c.env.Events(ctx, req) -} - -func (c *Local) Health(ctx context.Context) (*coretypes.ResultHealth, error) { - return c.env.Health(ctx) + return c.Environment.ConsensusParams(ctx, &coretypes.RequestConsensusParams{Height: (*coretypes.Int64)(height)}) } func (c *Local) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) { - return c.env.BlockchainInfo(ctx, &coretypes.RequestBlockchainInfo{ + return c.Environment.BlockchainInfo(ctx, &coretypes.RequestBlockchainInfo{ MinHeight: coretypes.Int64(minHeight), MaxHeight: coretypes.Int64(maxHeight), }) } -func (c *Local) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) { - return c.env.Genesis(ctx) -} - func (c *Local) GenesisChunked(ctx context.Context, id uint) (*coretypes.ResultGenesisChunk, error) { - return c.env.GenesisChunked(ctx, &coretypes.RequestGenesisChunked{Chunk: coretypes.Int64(id)}) //nolint:gosec // id is a small genesis chunk index + return c.Environment.GenesisChunked(ctx, &coretypes.RequestGenesisChunked{Chunk: coretypes.Int64(id)}) //nolint:gosec // id is a small genesis chunk index } func (c *Local) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) { - return c.env.Block(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) + return c.Environment.Block(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) } func (c *Local) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) { - return c.env.BlockByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash}) + return c.Environment.BlockByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash}) } func (c *Local) BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) { - return c.env.BlockResults(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) + return c.Environment.BlockResults(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) } func (c *Local) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) { - return c.env.Header(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) + return c.Environment.Header(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) } func (c *Local) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) { - return c.env.HeaderByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash}) + return c.Environment.HeaderByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash}) } func (c *Local) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) { - return c.env.Commit(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) + return c.Environment.Commit(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)}) } func (c *Local) Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error) { - return c.env.Validators(ctx, &coretypes.RequestValidators{ + return c.Environment.Validators(ctx, &coretypes.RequestValidators{ Height: (*coretypes.Int64)(height), Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), @@ -190,11 +165,11 @@ func (c *Local) Validators(ctx context.Context, height *int64, page, perPage *in } func (c *Local) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) { - return c.env.Tx(ctx, &coretypes.RequestTx{Hash: hash, Prove: prove}) + return c.Environment.Tx(ctx, &coretypes.RequestTx{Hash: hash, Prove: prove}) } func (c *Local) TxSearch(ctx context.Context, queryString string, prove bool, page, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) { - return c.env.TxSearch(ctx, &coretypes.RequestTxSearch{ + return c.Environment.TxSearch(ctx, &coretypes.RequestTxSearch{ Query: queryString, Prove: prove, Page: coretypes.Int64Ptr(page), @@ -204,7 +179,7 @@ func (c *Local) TxSearch(ctx context.Context, queryString string, prove bool, pa } func (c *Local) BlockSearch(ctx context.Context, queryString string, page, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) { - return c.env.BlockSearch(ctx, &coretypes.RequestBlockSearch{ + return c.Environment.BlockSearch(ctx, &coretypes.RequestBlockSearch{ Query: queryString, Page: coretypes.Int64Ptr(page), PerPage: coretypes.Int64Ptr(perPage), @@ -213,7 +188,7 @@ func (c *Local) BlockSearch(ctx context.Context, queryString string, page, perPa } func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) { - return c.env.BroadcastEvidence(ctx, &coretypes.RequestBroadcastEvidence{Evidence: ev}) + return c.Environment.BroadcastEvidence(ctx, &coretypes.RequestBroadcastEvidence{Evidence: ev}) } func (c *Local) Subscribe(ctx context.Context, subscriber, queryString string, capacity ...int) (<-chan coretypes.ResultEvent, error) { diff --git a/sei-tendermint/test/e2e/README.md b/sei-tendermint/test/e2e/README.md index 70510b6faa..1797ad5c33 100644 --- a/sei-tendermint/test/e2e/README.md +++ b/sei-tendermint/test/e2e/README.md @@ -162,22 +162,4 @@ tendermint init validator TMHOME=$HOME/.tendermint ./build/node ./node/built-in.toml ``` -To make things simpler the e2e application can also be run in the tendermint binary -by running - -```bash -tendermint start --proxy-app e2e -``` - -However this won't offer the same level of configurability of the application. - -**Socket** - -```bash -make node -tendermint init validator -tendermint start -./build/node ./node.socket.toml -``` - Check `node/config.go` to see how the settings of the test application can be tweaked. diff --git a/sei-wasmd/app/app.go b/sei-wasmd/app/app.go index deabf4cfff..3b6ce3d578 100644 --- a/sei-wasmd/app/app.go +++ b/sei-wasmd/app/app.go @@ -830,14 +830,10 @@ func (app *WasmApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICo } } -// RegisterTxService implements the Application.RegisterTxService method. -func (app *WasmApp) RegisterTxService(clientCtx client.Context) { - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) -} - -// RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *WasmApp) RegisterTendermintService(clientCtx client.Context) { - tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +// RegisterTxService implements the Application.RegisterLocalServices method. +func (app *WasmApp) RegisterLocalServices(node client.LocalClient, txConfig client.TxConfig) { + authtx.RegisterTxService(app.GRPCQueryRouter(), node, txConfig, app.Simulate, app.interfaceRegistry) + tmservice.RegisterTendermintService(app.GRPCQueryRouter(), node, app.interfaceRegistry) } func (app *WasmApp) AppCodec() codec.Codec { diff --git a/sei-wasmd/x/wasm/client/cli/gov_tx.go b/sei-wasmd/x/wasm/client/cli/gov_tx.go index 1c7106b8e7..994494a8a7 100644 --- a/sei-wasmd/x/wasm/client/cli/gov_tx.go +++ b/sei-wasmd/x/wasm/client/cli/gov_tx.go @@ -72,7 +72,7 @@ func ProposalStoreCodeCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -150,7 +150,7 @@ func ProposalInstantiateContractCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } cmd.Flags().String(flagAmount, "", "Coins to send to the contract during instantiation") @@ -218,7 +218,7 @@ func ProposalMigrateContractCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -295,7 +295,7 @@ func ProposalExecuteContractCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } cmd.Flags().String(flagRunAs, "", "The address that is passed as sender to the contract on proposal execution") @@ -357,7 +357,7 @@ func ProposalSudoContractCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -419,7 +419,7 @@ func ProposalUpdateContractAdminCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } // proposal flags @@ -474,7 +474,7 @@ func ProposalClearContractAdminCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } // proposal flags @@ -533,7 +533,7 @@ func ProposalPinCodesCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } // proposal flags @@ -604,7 +604,7 @@ func ProposalUnpinCodesCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } // proposal flags @@ -711,7 +711,7 @@ $ %s tx gov submit-proposal update-instantiate-config 1,nobody 2,everybody 3,%s1 return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } // proposal flags diff --git a/sei-wasmd/x/wasm/client/cli/new_tx.go b/sei-wasmd/x/wasm/client/cli/new_tx.go index 502ec12cfc..c21df0c396 100644 --- a/sei-wasmd/x/wasm/client/cli/new_tx.go +++ b/sei-wasmd/x/wasm/client/cli/new_tx.go @@ -32,7 +32,7 @@ func MigrateContractCmd() *cobra.Command { if err := msg.ValidateBasic(); err != nil { return nil } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } flags.AddTxFlagsToCmd(cmd) @@ -77,7 +77,7 @@ func UpdateContractAdminCmd() *cobra.Command { if err := msg.ValidateBasic(); err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } flags.AddTxFlagsToCmd(cmd) @@ -113,7 +113,7 @@ func ClearContractAdminCmd() *cobra.Command { if err := msg.ValidateBasic(); err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } flags.AddTxFlagsToCmd(cmd) diff --git a/sei-wasmd/x/wasm/client/cli/tx.go b/sei-wasmd/x/wasm/client/cli/tx.go index c41da01d87..abcf35c02c 100644 --- a/sei-wasmd/x/wasm/client/cli/tx.go +++ b/sei-wasmd/x/wasm/client/cli/tx.go @@ -70,7 +70,7 @@ func StoreCodeCmd() *cobra.Command { if err = msg.ValidateBasic(); err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } @@ -169,7 +169,7 @@ func InstantiateContractCmd() *cobra.Command { if err := msg.ValidateBasic(); err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } @@ -252,7 +252,7 @@ func ExecuteContractCmd() *cobra.Command { if err := msg.ValidateBasic(); err != nil { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), &msg) }, } diff --git a/x/evm/ante/sig.go b/x/evm/ante/sig.go index 867ac0c04b..acf683d8c8 100644 --- a/x/evm/ante/sig.go +++ b/x/evm/ante/sig.go @@ -6,8 +6,6 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" - tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/seilog" "github.com/sei-protocol/sei-chain/utils/metrics" @@ -18,14 +16,12 @@ import ( var logger = seilog.NewLogger("x", "evm", "ante") type EVMSigVerifyDecorator struct { - evmKeeper *evmkeeper.Keeper - latestCtxGetter func() sdk.Context // should be read-only + evmKeeper *evmkeeper.Keeper } -func NewEVMSigVerifyDecorator(evmKeeper *evmkeeper.Keeper, latestCtxGetter func() sdk.Context) *EVMSigVerifyDecorator { +func NewEVMSigVerifyDecorator(evmKeeper *evmkeeper.Keeper, _ func() sdk.Context) *EVMSigVerifyDecorator { return &EVMSigVerifyDecorator{ - evmKeeper: evmKeeper, - latestCtxGetter: latestCtxGetter, + evmKeeper: evmKeeper, } } @@ -71,53 +67,7 @@ func (svd *EVMSigVerifyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat if txNonce < nextNonce { return ctx, sdkerrors.ErrWrongSequence } - ctx = ctx.WithCheckTxCallback(func(priority int64) { - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - svd.evmKeeper.AddPendingNonce(txHash, evmAddr, txNonce, priority) - metrics.IncrementPendingNonce("added") - }) - - // if the mempool expires a transaction, this handler is invoked - ctx = ctx.WithExpireTxHandler(func() { - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - svd.evmKeeper.RemovePendingNonce(txHash) - metrics.IncrementPendingNonce("expired") - }) - - if txNonce > nextNonce { - // transaction shall be added to mempool as a pending transaction - ctx = ctx.WithPendingTxChecker(func() abci.PendingTxCheckerResponse { - latestCtx := svd.latestCtxGetter() - - // nextNonceToBeMined is the next nonce that will be mined - // geth calls SetNonce(n+1) after a transaction is mined - nextNonceToBeMined := svd.evmKeeper.GetNonce(latestCtx, evmAddr) - - // nextPendingNonce is the minimum nonce a user may send without stomping on an already-sent - // nonce, including non-mined or pending transactions - // If a user skips a nonce [1,2,4], then this will be the value of that hole (e.g., 3) - nextPendingNonce := svd.evmKeeper.CalculateNextNonce(latestCtx, evmAddr, true) - - if txNonce < nextNonceToBeMined { - // this nonce has already been mined, we cannot accept it again - metrics.IncrementPendingNonce("rejected") - return abci.Rejected - } else if txNonce < nextPendingNonce { - // check if the sender still has enough funds to pay for gas - balance := svd.evmKeeper.GetBalance(latestCtx, types.MustGetEVMTransactionMessage(tx).Derived.SenderSeiAddr) - if balance.Cmp(fee) < 0 { - // not enough funds. Go back to pending as it may obtain sufficient funds later. - return abci.Pending - } - // this nonce is allowed to process as it is part of the - // consecutive nonces from nextNonceToBeMined to nextPendingNonce - // This logic allows multiple nonces from an account to be processed in a block. - metrics.IncrementPendingNonce("accepted") - return abci.Accepted - } - return abci.Pending - }) - } + ctx = ctx.WithEVMRequiredBalance(fee) } else if txNonce != nextNonce { metrics.IncrementNonceMismatch(txNonce > nextNonce) return ctx, sdkerrors.ErrWrongSequence diff --git a/x/evm/ante/sig_test.go b/x/evm/ante/sig_test.go index 783821b1ea..8c2621a19c 100644 --- a/x/evm/ante/sig_test.go +++ b/x/evm/ante/sig_test.go @@ -10,7 +10,6 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" - abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" "github.com/sei-protocol/sei-chain/x/evm/ante" "github.com/sei-protocol/sei-chain/x/evm/types" @@ -137,33 +136,23 @@ func TestSigVerifyPendingTransaction(t *testing.T) { }) require.Nil(t, err) - // should not return error but include pending tx checker + // should not return error but include the required balance threshold newCtx, err := handler.AnteHandle(ctx, mockTx{msgs: []sdk.Msg{msg}}, false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil }) require.Nil(t, err) - require.NotNil(t, newCtx.PendingTxChecker()) - newCtx.CheckTxCallback()(newCtx.Priority()) - - // test checker - require.Equal(t, abci.Pending, newCtx.PendingTxChecker()()) - k.SetNonce(ctx, evmAddr, 1) - // not enough fund - require.Equal(t, abci.Pending, newCtx.PendingTxChecker()()) // Calculate the exact fee needed: gas * gasPrice + value = 1000 * 10 + 1000 = 11000 wei. fee := sdk.NewIntFromUint64(tx.Gas()).Mul(sdk.NewIntFromBigInt(tx.GasPrice())).Add(sdk.NewIntFromBigInt(tx.Value())) + require.NotNil(t, newCtx.EVMRequiredBalance()) + require.Zero(t, newCtx.EVMRequiredBalance().Cmp(fee.BigInt())) // Add an amount that exposes the unit mismatch bug (if wei and non-wei are compared). amountInCosmosUnits := fee.Sub(sdk.NewInt(1)) _ = k.BankKeeper().AddCoins(ctx, msg.Derived.SenderSeiAddr, sdk.NewCoins(sdk.NewCoin("usei", amountInCosmosUnits)), false) - require.Equal(t, abci.Accepted, newCtx.PendingTxChecker()()) - // incorrect nonce - k.SetNonce(ctx, evmAddr, 2) - require.Equal(t, abci.Rejected, newCtx.PendingTxChecker()()) - // should return error because current nonce is larger than tx nonce + k.SetNonce(ctx, evmAddr, 2) _, err = handler.AnteHandle(ctx, mockTx{msgs: []sdk.Msg{msg}}, false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil }) diff --git a/x/evm/client/cli/gov_tx.go b/x/evm/client/cli/gov_tx.go index 2472e8ad01..d6cb3c483e 100644 --- a/x/evm/client/cli/gov_tx.go +++ b/x/evm/client/cli/gov_tx.go @@ -56,7 +56,7 @@ func NewAddERCNativePointerProposalTxCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/x/evm/client/cli/native_tx.go b/x/evm/client/cli/native_tx.go index 1b2501ba3b..14bd115d55 100644 --- a/x/evm/client/cli/native_tx.go +++ b/x/evm/client/cli/native_tx.go @@ -48,7 +48,7 @@ func NativeSendTxCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -77,7 +77,7 @@ func RegisterCwPointerCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -172,7 +172,7 @@ func AssociateContractAddressCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -197,7 +197,7 @@ func NativeAssociateCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -223,7 +223,7 @@ func PrintClaimTxPayloadCmd() *cobra.Command { } clientCtx.PrintSignedOnly = true - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -249,7 +249,7 @@ func PrintClaimTxBySenderPayloadCmd() *cobra.Command { } clientCtx.PrintSignedOnly = true - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } @@ -294,7 +294,7 @@ func PrintClaimSpecificTxPayloadCmd() *cobra.Command { } clientCtx.PrintSignedOnly = true - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index f9cc564187..0b8a7cc722 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -7,8 +7,6 @@ import ( "fmt" "math" "math/big" - "slices" - "sort" "sync" "github.com/ethereum/go-ethereum/common" @@ -67,9 +65,6 @@ type Keeper struct { cachedFeeCollectorAddressMtx *sync.RWMutex cachedFeeCollectorAddress *common.Address - nonceMx *sync.RWMutex - pendingTxs map[string][]*PendingTx - hashToNonce map[tmtypes.TxHash]*AddressNoncePair QueryConfig *querier.Config @@ -95,17 +90,6 @@ type Keeper struct { latestUpgrade string } -type AddressNoncePair struct { - Address common.Address - Nonce uint64 -} - -type PendingTx struct { - Hash tmtypes.TxHash - Nonce uint64 - Priority int64 -} - // only used during ETH replay type ReplayChainContext struct { ethClient *ethclient.Client @@ -148,10 +132,7 @@ func NewKeeper( wasmKeeper: wasmKeeper, wasmViewKeeper: wasmViewKeeper, upgradeKeeper: upgradeKeeper, - pendingTxs: make(map[string][]*PendingTx), - nonceMx: &sync.RWMutex{}, cachedFeeCollectorAddressMtx: &sync.RWMutex{}, - hashToNonce: make(map[tmtypes.TxHash]*AddressNoncePair), receiptStore: receiptStore, } return k @@ -336,111 +317,6 @@ func (k *Keeper) getHistoricalHash(ctx sdk.Context, h int64) common.Hash { return common.BytesToHash(header.Hash()) } -// CalculateNextNonce calculates the next nonce for an address -// If includePending is true, it will consider pending nonces -// If includePending is false, it will only return the next nonce from GetNonce -func (k *Keeper) CalculateNextNonce(ctx sdk.Context, addr common.Address, includePending bool) uint64 { - k.nonceMx.Lock() - defer k.nonceMx.Unlock() - - nextNonce := k.GetNonce(ctx, addr) - - // we only want the latest nonce if we're not including pending - if !includePending { - return nextNonce - } - - // get the pending nonces (nil is fine) - pending := k.pendingTxs[addr.Hex()] - - // Check each nonce starting from latest until we find a gap - // That gap is the next nonce we should use. - for ; ; nextNonce++ { - // if it's not in pending, then it's the next nonce - if _, found := sort.Find(len(pending), func(i int) int { return uint64Cmp(nextNonce, pending[i].Nonce) }); !found { - return nextNonce - } - } -} - -// AddPendingNonce adds a pending nonce to the keeper -func (k *Keeper) AddPendingNonce(hash tmtypes.TxHash, addr common.Address, nonce uint64, priority int64) { - k.nonceMx.Lock() - defer k.nonceMx.Unlock() - - addrStr := addr.Hex() - if existing, ok := k.hashToNonce[hash]; ok { - if existing.Nonce != nonce { - fmt.Printf("Seeing transactions with the same hash %X but different nonces (%d vs. %d), which should be impossible\n", hash, nonce, existing.Nonce) - } - if existing.Address != addr { - fmt.Printf("Seeing transactions with the same hash %X but different addresses (%s vs. %s), which should be impossible\n", hash, addr.Hex(), existing.Address.Hex()) - } - // we want to no-op whether it's a genuine duplicate or not - return - } - for _, pendingTx := range k.pendingTxs[addrStr] { - if pendingTx.Nonce == nonce { - if priority > pendingTx.Priority { - // replace existing tx - delete(k.hashToNonce, pendingTx.Hash) - pendingTx.Priority = priority - pendingTx.Hash = hash - k.hashToNonce[hash] = &AddressNoncePair{ - Address: addr, - Nonce: nonce, - } - } - // we don't need to return error here if priority is lower. - // Tendermint will take care of rejecting the tx from mempool - return - } - } - k.hashToNonce[hash] = &AddressNoncePair{ - Address: addr, - Nonce: nonce, - } - k.pendingTxs[addrStr] = append(k.pendingTxs[addrStr], &PendingTx{ - Hash: hash, - Nonce: nonce, - Priority: priority, - }) - slices.SortStableFunc(k.pendingTxs[addrStr], func(a, b *PendingTx) int { - if a.Nonce < b.Nonce { - return -1 - } else if a.Nonce > b.Nonce { - return 1 - } - return 0 - }) -} - -// RemovePendingNonce removes a pending nonce from the keeper but leaves a hole -// so that a future transaction must use this nonce. -func (k *Keeper) RemovePendingNonce(hash tmtypes.TxHash) { - k.nonceMx.Lock() - defer k.nonceMx.Unlock() - tx, ok := k.hashToNonce[hash] - - if !ok { - return - } - - delete(k.hashToNonce, hash) - - addr := tx.Address.Hex() - pendings := k.pendingTxs[addr] - firstMatch, found := sort.Find(len(pendings), func(i int) int { return uint64Cmp(tx.Nonce, pendings[i].Nonce) }) - if !found { - fmt.Printf("Removing tx %X without a corresponding pending nonce, which should not happen\n", hash) - return - } - k.pendingTxs[addr] = append(k.pendingTxs[addr][:firstMatch], k.pendingTxs[addr][firstMatch+1:]...) - if len(k.pendingTxs[addr]) == 0 { - delete(k.pendingTxs, addr) - } -} - func (k *Keeper) SetTxResults(txResults []*abci.ExecTxResult) { k.txResults = txResults } @@ -449,16 +325,6 @@ func (k *Keeper) SetMsgs(msgs []*types.MsgEVMTransaction) { k.msgs = msgs } -// Test use only -func (k *Keeper) GetPendingTxs() map[string][]*PendingTx { - return k.pendingTxs -} - -// Test use only -func (k *Keeper) GetHashesToNonces() map[tmtypes.TxHash]*AddressNoncePair { - return k.hashToNonce -} - // Only used in ETH replay func (k *Keeper) PrepareReplayedAddr(ctx sdk.Context, addr common.Address) { if !k.EthReplayConfig.Enabled { diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index 8b654f4f96..02c999fd75 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -9,7 +9,6 @@ import ( "os" "sort" "strings" - "sync" "testing" "github.com/ethereum/go-ethereum/common" @@ -19,13 +18,10 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" authtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/types" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/rand" - tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/sei-chain/testutil/keeper" testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" "github.com/sei-protocol/sei-chain/utils" "github.com/sei-protocol/sei-chain/x/evm/config" - evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" "github.com/sei-protocol/sei-chain/x/evm/types" "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" "github.com/stretchr/testify/require" @@ -74,141 +70,6 @@ func TestGetHashFn(t *testing.T) { require.Equal(t, common.Hash{}, f(uint64(ctx.BlockHeight())-1)) } -func TestKeeper_CalculateNextNonce(t *testing.T) { - address1 := common.BytesToAddress([]byte("addr1")) - key1 := tmtypes.TxHash(rand.NewRand().Bytes(32)) - key2 := tmtypes.TxHash(rand.NewRand().Bytes(32)) - tests := []struct { - name string - address common.Address - pending bool - setup func(ctx sdk.Context, k *evmkeeper.Keeper) - expectedNonce uint64 - }{ - { - name: "latest block, no latest stored", - address: address1, - pending: false, - expectedNonce: 0, - }, - { - name: "latest block, latest stored", - address: address1, - pending: false, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - k.SetNonce(ctx, address1, 50) - }, - expectedNonce: 50, - }, - { - name: "latest block, latest stored with pending nonces", - address: address1, - pending: false, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - k.SetNonce(ctx, address1, 50) - // because pending:false, these won't matter - k.AddPendingNonce(key1, address1, 50, 0) - k.AddPendingNonce(key2, address1, 51, 0) - }, - expectedNonce: 50, - }, - { - name: "pending block, nonce should follow the last pending", - address: address1, - pending: true, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - k.SetNonce(ctx, address1, 50) - k.AddPendingNonce(key1, address1, 50, 0) - k.AddPendingNonce(key2, address1, 51, 0) - }, - expectedNonce: 52, - }, - { - name: "pending block, nonce should be the value of hole", - address: address1, - pending: true, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - k.SetNonce(ctx, address1, 50) - k.AddPendingNonce(key1, address1, 50, 0) - // missing 51, so nonce = 51 - k.AddPendingNonce(key2, address1, 52, 0) - }, - expectedNonce: 51, - }, - { - name: "pending block, completed nonces should also be skipped", - address: address1, - pending: true, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - k.SetNonce(ctx, address1, 50) - k.AddPendingNonce(key1, address1, 50, 0) - k.AddPendingNonce(key2, address1, 51, 0) - k.SetNonce(ctx, address1, 52) - k.RemovePendingNonce(key1) - k.RemovePendingNonce(key2) - }, - expectedNonce: 52, - }, - { - name: "pending block, hole created by expiration", - address: address1, - pending: true, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - k.SetNonce(ctx, address1, 50) - k.AddPendingNonce(key1, address1, 50, 0) - k.AddPendingNonce(key2, address1, 51, 0) - k.RemovePendingNonce(key1) - }, - expectedNonce: 50, - }, - { - name: "pending block, skipped nonces all in pending", - address: address1, - pending: true, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - // next expected for latest is 50, but 51,52 were sent - k.SetNonce(ctx, address1, 50) - k.AddPendingNonce(key1, address1, 51, 0) - k.AddPendingNonce(key2, address1, 52, 0) - }, - expectedNonce: 50, - }, - { - name: "try 1000 nonces concurrently", - address: address1, - pending: true, - setup: func(ctx sdk.Context, k *evmkeeper.Keeper) { - // next expected for latest is 50, but 51,52 were sent - k.SetNonce(ctx, address1, 50) - wg := sync.WaitGroup{} - for i := 50; i < 1000; i++ { - wg.Add(1) - go func(nonce int) { - defer wg.Done() - key := tmtypes.TxHash(rand.NewRand().Bytes(32)) - // call this just to exercise locks - k.CalculateNextNonce(ctx, address1, true) - k.AddPendingNonce(key, address1, uint64(nonce), 0) - }(i) - } - wg.Wait() - }, - expectedNonce: 1000, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - k, ctx := keeper.MockEVMKeeper(t) - if test.setup != nil { - test.setup(ctx, k) - } - next := k.CalculateNextNonce(ctx, test.address, test.pending) - require.Equal(t, test.expectedNonce, next) - }) - } -} - func TestDeferredInfo(t *testing.T) { a := app.Setup(t, false, false, false) k := a.EvmKeeper @@ -244,27 +105,6 @@ func TestDeferredInfo(t *testing.T) { require.Empty(t, len(infoList)) } -func TestAddPendingNonce(t *testing.T) { - k, _ := keeper.MockEVMKeeper(t) - k.AddPendingNonce(tmtypes.TxHash{1}, common.HexToAddress("123"), 1, 1) - k.AddPendingNonce(tmtypes.TxHash{2}, common.HexToAddress("123"), 2, 1) - k.AddPendingNonce(tmtypes.TxHash{3}, common.HexToAddress("123"), 2, 2) // should replace the one above - pendingTxs := k.GetPendingTxs()[common.HexToAddress("123").Hex()] - require.Equal(t, 2, len(pendingTxs)) - require.Equal(t, tmtypes.TxHash{1}, pendingTxs[0].Hash) - require.Equal(t, uint64(1), pendingTxs[0].Nonce) - require.Equal(t, int64(1), pendingTxs[0].Priority) - require.Equal(t, tmtypes.TxHash{3}, pendingTxs[1].Hash) - require.Equal(t, uint64(2), pendingTxs[1].Nonce) - require.Equal(t, int64(2), pendingTxs[1].Priority) - hashToNonce := k.GetHashesToNonces() - require.Equal(t, common.HexToAddress("123"), hashToNonce[tmtypes.TxHash{1}].Address) - require.Equal(t, uint64(1), hashToNonce[tmtypes.TxHash{1}].Nonce) - require.Equal(t, common.HexToAddress("123"), hashToNonce[tmtypes.TxHash{3}].Address) - require.Equal(t, uint64(2), hashToNonce[tmtypes.TxHash{3}].Nonce) - require.NotContains(t, hashToNonce, tmtypes.TxHash{2}) -} - func TestGetCustomPrecompiles(t *testing.T) { // Read all version files from precompiles subfolders tagSet := make(map[string]bool) diff --git a/x/mint/client/cli/tx.go b/x/mint/client/cli/tx.go index 54053c23f2..c8e6dbcb87 100644 --- a/x/mint/client/cli/tx.go +++ b/x/mint/client/cli/tx.go @@ -87,7 +87,7 @@ func MsgUpdateMinterProposalCmd() *cobra.Command { return err } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msg) }, } diff --git a/x/oracle/client/cli/tx.go b/x/oracle/client/cli/tx.go index 27888fbdce..3f7662622b 100755 --- a/x/oracle/client/cli/tx.go +++ b/x/oracle/client/cli/tx.go @@ -74,7 +74,7 @@ where "terra1..." is the address you want to delegate your voting rights to. } } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msgs...) }, } @@ -133,7 +133,7 @@ $ seid tx oracle aggregate-vote 1234 8888.0ukrw,1.243uusd,0.99usdr seivaloper1.. } } - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...) + return tx.GenerateOrBroadcastTxCLI(cmd.Context(), clientCtx, cmd.Flags(), msgs...) }, } diff --git a/x/tokenfactory/client/cli/tx.go b/x/tokenfactory/client/cli/tx.go index 1075821db7..9c01e5fec0 100644 --- a/x/tokenfactory/client/cli/tx.go +++ b/x/tokenfactory/client/cli/tx.go @@ -91,7 +91,7 @@ func NewCreateDenomCmd() *cobra.Command { msg.AllowList = &allowList } - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } @@ -140,7 +140,7 @@ func NewUpdateDenomCmd() *cobra.Command { &allowList, ) - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } @@ -173,7 +173,7 @@ func NewMintCmd() *cobra.Command { amount, ) - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } @@ -205,7 +205,7 @@ func NewBurnCmd() *cobra.Command { amount, ) - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } @@ -233,7 +233,7 @@ func NewChangeAdminCmd() *cobra.Command { args[1], ) - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } @@ -290,7 +290,7 @@ Where metadata.json contains: metadata, ) - return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) + return tx.GenerateOrBroadcastTxWithFactory(cmd.Context(), clientCtx, txf, msg) }, } diff --git a/x/tokenfactory/client/cli/tx_test.go b/x/tokenfactory/client/cli/tx_test.go index 23026e0693..e160f70d0f 100644 --- a/x/tokenfactory/client/cli/tx_test.go +++ b/x/tokenfactory/client/cli/tx_test.go @@ -177,9 +177,7 @@ func Test_ParseAllowListJSON(t *testing.T) { func TestNewCreateDenomCmd_AllowList(t *testing.T) { // Setup codec and client context cdc := codec.NewLegacyAmino() - clientCtx := client.Context{ - LegacyAmino: cdc, - } + clientCtx := client.Context{}.WithLegacyAmino(cdc) // Create a temporary command to test cmd := NewCreateDenomCmd() @@ -238,9 +236,7 @@ func TestNewCreateDenomCmd_AllowList(t *testing.T) { func TestNewUpdateDenomCmd_AllowList(t *testing.T) { // Setup codec and client context cdc := codec.NewLegacyAmino() - clientCtx := client.Context{ - LegacyAmino: cdc, - } + clientCtx := client.Context{}.WithLegacyAmino(cdc) // Create a temporary command to test cmd := NewUpdateDenomCmd()