From ffac0a11d9ff6aa533a1c7a650d07e16625b03de Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Thu, 30 Apr 2026 12:40:27 +0200 Subject: [PATCH 01/33] reduced runTx results --- sei-cosmos/baseapp/abci.go | 18 +++++---- sei-cosmos/baseapp/baseapp.go | 53 ++++++++++++--------------- sei-cosmos/baseapp/deliver_tx_test.go | 6 +-- sei-cosmos/baseapp/test_helpers.go | 8 ++-- 4 files changed, 41 insertions(+), 44 deletions(-) diff --git a/sei-cosmos/baseapp/abci.go b/sei-cosmos/baseapp/abci.go index fe822d86e8..33b9ae2883 100644 --- a/sei-cosmos/baseapp/abci.go +++ b/sei-cosmos/baseapp/abci.go @@ -178,14 +178,16 @@ func (app *BaseApp) DeliverTx(ctx sdk.Context, req abci.RequestDeliverTxV2, tx s telemetry.SetGauge(float32(gInfo.GasWanted), "tx", "gas", "wanted") }() - gInfo, result, anteEvents, _, _, _, _, resCtx, err := app.runTx(ctx.WithTxBytes(req.Tx).WithTxSum(checksum), runTxModeDeliver, tx, checksum) //nolint:dogsled // Because life is worth living instead of fixing this, considering sei solo is around the corner. + runTxRes, err := app.runTx(ctx.WithTxBytes(req.Tx).WithTxSum(checksum), runTxModeDeliver, tx, checksum) + gInfo = runTxRes.gasInfo + result := runTxRes.result if err != nil { resultStr = "failed" // if we have a result, use those events instead of just the anteEvents if result != nil { return sdkerrors.ResponseDeliverTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, sdk.MarkEventsToIndex(result.Events, app.IndexEvents), app.trace) } - return sdkerrors.ResponseDeliverTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, sdk.MarkEventsToIndex(anteEvents, app.IndexEvents), app.trace) + return sdkerrors.ResponseDeliverTxWithEvents(err, gInfo.GasWanted, gInfo.GasUsed, sdk.MarkEventsToIndex(runTxRes.anteEvents, app.IndexEvents), app.trace) } res = abci.ResponseDeliverTx{ @@ -195,11 +197,11 @@ func (app *BaseApp) DeliverTx(ctx sdk.Context, req abci.RequestDeliverTxV2, tx s Data: result.Data, Events: sdk.MarkEventsToIndex(result.Events, app.IndexEvents), } - if resCtx.IsEVM() { + if runTxRes.ctx.IsEVM() { res.EvmTxInfo = &abci.EvmTxInfo{ - SenderAddress: resCtx.EVMSenderAddress(), - Nonce: resCtx.EVMNonce(), - TxHash: resCtx.EVMTxHash(), + SenderAddress: runTxRes.ctx.EVMSenderAddress(), + Nonce: runTxRes.ctx.EVMNonce(), + TxHash: runTxRes.ctx.EVMTxHash(), VmError: result.EvmError, } // TODO: populate error data for EVM err @@ -762,8 +764,8 @@ func (app *BaseApp) Simulate(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) { if err != nil { return sdk.GasInfo{}, nil, err } - gasInfo, result, _, _, _, _, _, _, err := app.runTx(ctx, runTxModeSimulate, tx, sha256.Sum256(txBytes)) //nolint:dogsled // Because life is worth living instead of fixing this, considering sei solo is around the corner. - return gasInfo, result, err + runTxRes, err := app.runTx(ctx, runTxModeSimulate, tx, sha256.Sum256(txBytes)) + return runTxRes.gasInfo, runTxRes.result, err } func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { diff --git a/sei-cosmos/baseapp/baseapp.go b/sei-cosmos/baseapp/baseapp.go index c4f44d32ff..605c74e4ff 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -809,6 +809,13 @@ func (app *BaseApp) CacheTxContext(ctx sdk.Context, checksum [32]byte) (sdk.Cont return ctx.WithMultiStore(msCache), msCache } +type runTxResult struct { + gasInfo sdk.GasInfo + result *sdk.Result + anteEvents []abci.Event + ctx sdk.Context +} + // runTx processes a transaction within a given execution mode, encoded transaction // bytes, and the decoded transaction itself. All state transitions occur through // a cached Context depending on the mode provided. State only gets persisted @@ -816,17 +823,7 @@ func (app *BaseApp) CacheTxContext(ctx sdk.Context, checksum [32]byte) (sdk.Cont // Note, gas execution info is always returned. A reference to a Result is // returned if the tx does not run out of gas and if all the messages are valid // and execute successfully. An error is returned otherwise. -func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [32]byte) ( - gInfo sdk.GasInfo, - result *sdk.Result, - anteEvents []abci.Event, - priority int64, - pendingTxChecker abci.PendingTxChecker, - expireHandler abci.ExpireTxHandler, - checkTxCallback func(int64), - txCtx sdk.Context, - err error, -) { +func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [32]byte) (runTxRes runTxResult, err error) { defer telemetry.MeasureThroughputSinceWithLabels( telemetry.TxCount, []metrics.Label{ @@ -840,6 +837,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ defer span.End() ctx = ctx.WithTraceSpanContext(spanCtx) span.SetAttributes(attribute.String("txHash", fmt.Sprintf("%X", checksum))) + runTxRes.ctx = ctx // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas @@ -854,22 +852,22 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ if r := recover(); r != nil { recoveryMW := newOutOfGasRecoveryMiddleware(gasWanted, ctx, app.runTxRecoveryMiddleware) recoveryMW = newOCCAbortRecoveryMiddleware(recoveryMW) // TODO: do we have to wrap with occ enabled check? - err, result = processRecovery(r, recoveryMW), nil + err, runTxRes.result = processRecovery(r, recoveryMW), nil } if ctx.GasMeter() == blockGasMeter { return } - gInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed(), GasEstimate: gasEstimate} + runTxRes.gasInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed(), GasEstimate: gasEstimate} }() if tx == nil { - return sdk.GasInfo{}, nil, nil, 0, nil, nil, nil, ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx decode error") + return runTxRes, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx decode error") } msgs := tx.GetMsgs() if err := validateBasicTxMsgs(msgs); err != nil { - return sdk.GasInfo{}, nil, nil, 0, nil, nil, nil, ctx, err + return runTxRes, err } if app.anteHandler != nil { @@ -905,6 +903,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ // The GasMeter is a pointer and its passed to the RunMsg and tracks the consumed // gas there too. ctx = newCtx.WithMultiStore(ms) + runTxRes.ctx = ctx } defer func() { if newCtx.DeliverTxCallback() != nil { @@ -915,19 +914,15 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ events := ctx.EventManager().Events() if err != nil { - return gInfo, nil, nil, 0, nil, nil, nil, ctx, err + return runTxRes, err } // GasMeter expected to be set in AnteHandler gasWanted = ctx.GasMeter().Limit() gasEstimate = ctx.GasEstimate() - priority = ctx.Priority() - pendingTxChecker = ctx.PendingTxChecker() - expireHandler = ctx.ExpireTxHandler() msCache.Write() - anteEvents = events.ToABCIEvents() + runTxRes.anteEvents = events.ToABCIEvents() anteSpan.End() - checkTxCallback = ctx.CheckTxCallback() } // Create a new Context based off of the existing Context with a MultiStore branch @@ -938,30 +933,30 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ // Attempt to execute all messages and only update state if all messages pass // and we're in DeliverTx. Note, RunMsgs will never return a reference to a // Result if any single message fails or does not have a registered Handler. - result, err = app.RunMsgs(runMsgCtx, msgs) + runTxRes.result, err = app.RunMsgs(runMsgCtx, msgs) if err == nil { msCache.Write() } // we do this since we will only be looking at result in DeliverTx - if result != nil && len(anteEvents) > 0 { + if runTxRes.result != nil && len(runTxRes.anteEvents) > 0 { // append the events in the order of occurrence - result.Events = append(anteEvents, result.Events...) + runTxRes.result.Events = append(runTxRes.anteEvents, runTxRes.result.Events...) } // only apply hooks if no error - if err == nil && (!ctx.IsEVM() || result.EvmError == "") { + if err == nil && (!ctx.IsEVM() || runTxRes.result.EvmError == "") { var evmTxInfo *abci.EvmTxInfo if ctx.IsEVM() { evmTxInfo = &abci.EvmTxInfo{ SenderAddress: ctx.EVMSenderAddress(), Nonce: ctx.EVMNonce(), TxHash: ctx.EVMTxHash(), - VmError: result.EvmError, + VmError: runTxRes.result.EvmError, } } var events = []abci.Event{} - if result != nil { - events = sdk.MarkEventsToIndex(result.Events, app.IndexEvents) + if runTxRes.result != nil { + events = sdk.MarkEventsToIndex(runTxRes.result.Events, app.IndexEvents) } for _, hook := range app.deliverTxHooks { hook(ctx, tx, checksum, sdk.DeliverTxHookInput{ @@ -970,7 +965,7 @@ func (app *BaseApp) runTx(ctx sdk.Context, mode runTxMode, tx sdk.Tx, checksum [ }) } } - return gInfo, result, anteEvents, priority, pendingTxChecker, expireHandler, checkTxCallback, ctx, err + return runTxRes, err } // RunMsgs iterates through a list of messages and executes them with the provided diff --git a/sei-cosmos/baseapp/deliver_tx_test.go b/sei-cosmos/baseapp/deliver_tx_test.go index 21e23e1219..8ac7e4a034 100644 --- a/sei-cosmos/baseapp/deliver_tx_test.go +++ b/sei-cosmos/baseapp/deliver_tx_test.go @@ -619,10 +619,10 @@ func TestRunTxDecodeError(t *testing.T) { ctx.GasMeter().ConsumeGas(5000, "simulated prior gas") // A decode failure should not report block-level gas as its own - gInfo, _, _, _, _, _, _, _, err := app.runTx(ctx, runTxModeDeliver, nil, [32]byte{}) + runTxRes, err := app.runTx(ctx, runTxModeDeliver, nil, [32]byte{}) require.Error(t, err) - require.Equal(t, uint64(0), gInfo.GasUsed) - require.Equal(t, uint64(0), gInfo.GasWanted) + require.Equal(t, uint64(0), runTxRes.gasInfo.GasUsed) + require.Equal(t, uint64(0), runTxRes.gasInfo.GasWanted) } // Test that transactions exceeding gas limits fail diff --git a/sei-cosmos/baseapp/test_helpers.go b/sei-cosmos/baseapp/test_helpers.go index d183cdf9dd..97a0d70772 100644 --- a/sei-cosmos/baseapp/test_helpers.go +++ b/sei-cosmos/baseapp/test_helpers.go @@ -17,8 +17,8 @@ func (app *BaseApp) Check(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk return sdk.GasInfo{}, nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "%s", err) } ctx := app.checkState.ctx.WithTxBytes(bz).WithConsensusParams(app.GetConsensusParams(app.checkState.ctx)) - gasInfo, result, _, _, _, _, _, _, err := app.runTx(ctx, runTxModeCheck, tx, sha256.Sum256(bz)) //nolint:dogsled // Because life is worth living instead of fixing this, considering sei solo is around the corner. - return gasInfo, result, err + runTxRes, err := app.runTx(ctx, runTxModeCheck, tx, sha256.Sum256(bz)) + return runTxRes.gasInfo, runTxRes.result, err } func (app *BaseApp) Deliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) { @@ -32,8 +32,8 @@ func (app *BaseApp) Deliver(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk.GasInfo, *s if err != nil { return sdk.GasInfo{}, &sdk.Result{}, err } - gasInfo, result, _, _, _, _, _, _, err := app.runTx(ctx, runTxModeDeliver, decoded, sha256.Sum256(bz)) //nolint:dogsled // Because life is worth living instead of fixing this, considering sei solo is around the corner. - return gasInfo, result, err + runTxRes, err := app.runTx(ctx, runTxModeDeliver, decoded, sha256.Sum256(bz)) + return runTxRes.gasInfo, runTxRes.result, err } // Context with current {check, deliver}State of the app used by tests. From 4e7a82d76b6267a9b191cbceeff29c086241ce30 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Thu, 30 Apr 2026 13:21:13 +0200 Subject: [PATCH 02/33] removed sender --- sei-tendermint/abci/types/types.pb.go | 223 +++++++----------- sei-tendermint/internal/mempool/mempool.go | 14 -- .../internal/mempool/mempool_test.go | 35 +-- .../internal/mempool/priority_queue_test.go | 61 +++-- sei-tendermint/internal/mempool/tx.go | 25 +- sei-tendermint/internal/mempool/tx_test.go | 27 --- .../proto/tendermint/abci/types.proto | 4 +- .../spec/abci++/abci++_methods_002_draft.md | 1 - sei-tendermint/spec/abci/abci.md | 1 - 9 files changed, 120 insertions(+), 271 deletions(-) diff --git a/sei-tendermint/abci/types/types.pb.go b/sei-tendermint/abci/types/types.pb.go index 2599433f9b..39db3f6e7f 100644 --- a/sei-tendermint/abci/types/types.pb.go +++ b/sei-tendermint/abci/types/types.pb.go @@ -298,7 +298,6 @@ type ResponseCheckTx struct { Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"` - Sender string `protobuf:"bytes,9,opt,name=sender,proto3" json:"sender,omitempty"` Priority int64 `protobuf:"varint,10,opt,name=priority,proto3" json:"priority,omitempty"` GasEstimated int64 `protobuf:"varint,12,opt,name=gas_estimated,json=gasEstimated,proto3" json:"gas_estimated,omitempty"` } @@ -371,13 +370,6 @@ func (m *ResponseCheckTx) GetCodespace() string { return "" } -func (m *ResponseCheckTx) GetSender() string { - if m != nil { - return m.Sender - } - return "" -} - func (m *ResponseCheckTx) GetPriority() int64 { if m != nil { return m.Priority @@ -1266,92 +1258,92 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 1357 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x4b, 0x6f, 0x1b, 0xd5, - 0x17, 0xcf, 0xd8, 0x8e, 0xed, 0x39, 0x76, 0x12, 0xf7, 0xf6, 0xe5, 0xa6, 0xa9, 0xe3, 0xff, 0xfc, - 0x2b, 0x11, 0x55, 0xad, 0x23, 0x15, 0x21, 0x01, 0xad, 0x84, 0xe2, 0xd4, 0x34, 0x8f, 0x92, 0x86, - 0xc1, 0x69, 0x01, 0x09, 0x46, 0xd7, 0x33, 0xb7, 0xf6, 0x55, 0x3c, 0x0f, 0xcd, 0xbd, 0x76, 0xc7, - 0xdd, 0xb3, 0x62, 0x83, 0xf8, 0x38, 0xec, 0xd8, 0x75, 0xd9, 0x25, 0xab, 0x0a, 0xb5, 0x12, 0x8b, - 0x7e, 0x01, 0xc4, 0x0a, 0x74, 0x1f, 0x63, 0x3b, 0xb1, 0xa1, 0x48, 0xe9, 0x86, 0xdd, 0x39, 0xe7, - 0x9e, 0x73, 0xe6, 0x9e, 0xdf, 0x79, 0xdd, 0x81, 0xab, 0x9c, 0x04, 0x1e, 0x89, 0x7d, 0x1a, 0xf0, - 0x4d, 0xdc, 0x71, 0xe9, 0x26, 0x1f, 0x45, 0x84, 0x35, 0xa2, 0x38, 0xe4, 0x21, 0x5a, 0x99, 0x1c, - 0x36, 0xc4, 0xe1, 0xea, 0x85, 0x6e, 0xd8, 0x0d, 0xe5, 0xd9, 0xa6, 0xa0, 0x94, 0xda, 0xea, 0xda, - 0x94, 0x0f, 0x37, 0x1e, 0x45, 0x3c, 0xdc, 0x3c, 0x26, 0x23, 0xed, 0x64, 0xf5, 0xda, 0xec, 0x69, - 0x14, 0x87, 0xe1, 0x93, 0x39, 0xc7, 0xf2, 0xdb, 0x9b, 0x11, 0x8e, 0xb1, 0xaf, 0xad, 0xad, 0xdf, - 0x0c, 0x28, 0xdb, 0x84, 0x45, 0x61, 0xc0, 0xc8, 0x6e, 0xf0, 0x24, 0x44, 0x08, 0x72, 0x1e, 0xe6, - 0xb8, 0x6a, 0xd4, 0x8d, 0x0d, 0xd3, 0x96, 0x34, 0xaa, 0x42, 0x61, 0x48, 0x62, 0x46, 0xc3, 0xa0, - 0x9a, 0x91, 0xe2, 0x94, 0x45, 0xeb, 0x50, 0xc2, 0x51, 0xe4, 0xa4, 0xa7, 0xd9, 0xba, 0xb1, 0x91, - 0xb3, 0x01, 0x47, 0xd1, 0x23, 0xad, 0x70, 0x03, 0xce, 0xf5, 0x31, 0xe3, 0x4e, 0xa7, 0x1f, 0xba, - 0xc7, 0x4e, 0x8f, 0xd0, 0x6e, 0x8f, 0x57, 0x73, 0x75, 0x63, 0x23, 0x6b, 0xaf, 0x88, 0x83, 0xa6, - 0x90, 0xef, 0x48, 0x31, 0xba, 0x05, 0xe7, 0xa7, 0x74, 0x85, 0xdf, 0x1e, 0x66, 0xbd, 0xea, 0x62, - 0xdd, 0xd8, 0x28, 0xdb, 0x95, 0xb1, 0xf6, 0x56, 0x14, 0xed, 0x60, 0xd6, 0x43, 0x37, 0x01, 0xf9, - 0x34, 0xa0, 0xfe, 0xc0, 0x77, 0xba, 0x98, 0x39, 0x51, 0x4c, 0x5d, 0xc2, 0xaa, 0x79, 0x79, 0xc1, - 0x8a, 0x3e, 0xb9, 0x8f, 0xd9, 0xa1, 0x94, 0x5b, 0xbf, 0x1b, 0xb0, 0x94, 0x06, 0xfa, 0xf9, 0x80, - 0xc4, 0x23, 0x11, 0xa9, 0x1b, 0x7a, 0x44, 0x46, 0xba, 0x64, 0x4b, 0x1a, 0x55, 0x20, 0xdb, 0x0f, - 0xbb, 0x32, 0x0e, 0xd3, 0x16, 0xa4, 0xd0, 0xa2, 0xc1, 0x93, 0x50, 0xde, 0xd9, 0xb4, 0x25, 0x8d, - 0x2e, 0xc0, 0x22, 0x0d, 0x3c, 0x92, 0xc8, 0xab, 0x65, 0x6d, 0xc5, 0x08, 0xdb, 0x63, 0x32, 0x92, - 0x17, 0x28, 0xdb, 0x82, 0x14, 0x7a, 0x43, 0xdc, 0x1f, 0x90, 0x6a, 0x41, 0xca, 0x14, 0x83, 0x3e, - 0x04, 0x53, 0x26, 0xc8, 0x09, 0x23, 0x56, 0x2d, 0xd6, 0x8d, 0x8d, 0xd2, 0xed, 0xab, 0x8d, 0xa9, - 0x4a, 0x50, 0x49, 0x6c, 0x1c, 0x0a, 0x9d, 0x87, 0x11, 0xb3, 0x8b, 0x91, 0xa6, 0xd0, 0x25, 0xc8, - 0x6b, 0x04, 0x4d, 0xf9, 0x61, 0xcd, 0xa1, 0x35, 0x30, 0xc5, 0xed, 0x59, 0x84, 0x5d, 0x52, 0x05, - 0x79, 0xd1, 0x89, 0xc0, 0xfa, 0x16, 0x50, 0x1a, 0x78, 0x93, 0x74, 0x69, 0x20, 0x41, 0x44, 0x3b, - 0x90, 0x27, 0x43, 0x12, 0x70, 0x56, 0x35, 0xea, 0xd9, 0x8d, 0xd2, 0xed, 0x4b, 0x8d, 0x53, 0xc5, - 0xd8, 0x68, 0x89, 0xe3, 0x66, 0xf5, 0xf9, 0xcb, 0xf5, 0x85, 0x37, 0x2f, 0xd7, 0x2b, 0x4a, 0xfb, - 0x66, 0xe8, 0x53, 0x4e, 0xfc, 0x88, 0x8f, 0x6c, 0x6d, 0x6f, 0xfd, 0x61, 0xc0, 0x4a, 0xfa, 0x81, - 0xed, 0x1e, 0x71, 0x8f, 0xdb, 0xc9, 0x5c, 0x6c, 0xd3, 0xca, 0xca, 0x48, 0x30, 0x54, 0x65, 0xcd, - 0xe2, 0x7d, 0x0d, 0x40, 0x64, 0xf3, 0x29, 0x0e, 0x38, 0xf1, 0x34, 0xc0, 0x66, 0x17, 0xb3, 0xc7, - 0x52, 0x70, 0x32, 0xd4, 0xe2, 0xa9, 0x50, 0x05, 0x40, 0x4c, 0x46, 0x21, 0x01, 0x32, 0x6d, 0xcd, - 0xa1, 0x55, 0x28, 0x46, 0x31, 0x0d, 0x63, 0xca, 0x47, 0x12, 0x9f, 0xac, 0x3d, 0xe6, 0xd1, 0xff, - 0x61, 0x49, 0x7c, 0x90, 0x30, 0x4e, 0x7d, 0x2c, 0xbe, 0x59, 0x96, 0x0a, 0xe5, 0x2e, 0x66, 0xad, - 0x54, 0xb6, 0x97, 0x2b, 0xe6, 0x2a, 0x8b, 0x7b, 0xb9, 0x62, 0xbe, 0x52, 0xd8, 0xcb, 0x15, 0x0b, - 0x95, 0xe2, 0x5e, 0xae, 0x58, 0xaa, 0x94, 0xad, 0x9f, 0x33, 0x70, 0x2e, 0x8d, 0xfd, 0x1e, 0xe9, - 0xd3, 0x21, 0x89, 0xcf, 0x14, 0xfd, 0xbc, 0x6a, 0xab, 0xcd, 0x41, 0x64, 0x4a, 0x22, 0x82, 0x13, - 0xdc, 0x80, 0x11, 0x4f, 0x16, 0x5f, 0xd6, 0x1e, 0xf3, 0x53, 0x59, 0x2e, 0x9c, 0x2d, 0xcb, 0x6f, - 0x01, 0xfe, 0x63, 0x28, 0x91, 0xa1, 0xef, 0xf0, 0xc4, 0x91, 0xd7, 0x37, 0x65, 0x55, 0xaf, 0xce, - 0xf9, 0x98, 0xdf, 0x4e, 0xc4, 0x98, 0xb1, 0x4d, 0x92, 0x92, 0xd6, 0xf7, 0x19, 0xa8, 0xa4, 0x18, - 0xb6, 0x02, 0x4f, 0x95, 0xe7, 0x17, 0x70, 0x6e, 0x88, 0xfb, 0xd4, 0xc3, 0x3c, 0x8c, 0x9d, 0x41, - 0xe4, 0x61, 0x4e, 0xd2, 0x4a, 0xad, 0xcf, 0xb8, 0x7d, 0x94, 0x6a, 0x1e, 0x49, 0xc5, 0x66, 0x4e, - 0x44, 0x63, 0x57, 0x86, 0x27, 0xc5, 0x0c, 0x7d, 0x09, 0x97, 0x5d, 0xf1, 0x95, 0x80, 0x0d, 0x98, - 0x23, 0xc7, 0xe0, 0xd8, 0x75, 0x46, 0xde, 0x78, 0xd6, 0xf5, 0x76, 0xaa, 0x7f, 0x28, 0xa7, 0xa6, - 0x7d, 0xd1, 0x3d, 0x21, 0x48, 0x3d, 0x4f, 0x70, 0xce, 0x9e, 0xb1, 0x9b, 0x3e, 0x80, 0xe5, 0x71, - 0x33, 0x85, 0xbe, 0x4f, 0xb9, 0x28, 0xd0, 0x98, 0x70, 0x4c, 0x83, 0x74, 0x7c, 0x66, 0x55, 0x81, - 0x2a, 0xa1, 0x9a, 0x9d, 0xd6, 0x9b, 0x0c, 0x5c, 0x4c, 0xed, 0x3e, 0xa5, 0x01, 0xee, 0xd3, 0x67, - 0xe4, 0x1d, 0x37, 0x3a, 0xba, 0x0b, 0xc0, 0x13, 0x27, 0x26, 0x6c, 0xd0, 0xe7, 0x02, 0x31, 0xe1, - 0xed, 0xda, 0xac, 0xb7, 0x84, 0xb8, 0xed, 0xc4, 0x96, 0x5a, 0xb6, 0xc9, 0x35, 0xc5, 0xe6, 0x67, - 0x34, 0x7b, 0xc6, 0x8c, 0x7e, 0xf5, 0xf7, 0x19, 0xcd, 0xc9, 0x8c, 0xfe, 0x6f, 0xda, 0xb5, 0xda, - 0xbd, 0xff, 0x32, 0xa5, 0x57, 0xa0, 0x78, 0x6a, 0x05, 0x15, 0xb0, 0xda, 0x3c, 0xd6, 0x33, 0x58, - 0x94, 0x98, 0x89, 0x76, 0x15, 0x3e, 0xd3, 0x65, 0x29, 0x68, 0xf4, 0x0d, 0x00, 0xe6, 0x3c, 0xa6, - 0x9d, 0x81, 0xaa, 0x2b, 0x11, 0xe0, 0xfa, 0x7c, 0xcc, 0xb7, 0x52, 0xbd, 0xe6, 0x9a, 0x06, 0xff, - 0xc2, 0xc4, 0x74, 0x2a, 0x01, 0x53, 0x0e, 0xad, 0x03, 0x58, 0x3e, 0x69, 0x9b, 0xee, 0x1d, 0x63, - 0xce, 0xde, 0xc9, 0x4c, 0xef, 0x9d, 0xf1, 0xd6, 0x12, 0xf5, 0x53, 0xd4, 0x5b, 0xcb, 0xfa, 0x29, - 0x03, 0xe5, 0xe9, 0x94, 0xbd, 0xe3, 0xe1, 0xf5, 0x96, 0x71, 0x7e, 0x65, 0x66, 0x76, 0x15, 0xba, - 0x98, 0x1d, 0xfd, 0x57, 0x46, 0xd7, 0x77, 0x06, 0x14, 0xc7, 0xc0, 0x4d, 0xb6, 0xb3, 0x71, 0x62, - 0x3b, 0x8f, 0x71, 0xcf, 0x48, 0x44, 0xf5, 0x6b, 0x61, 0x19, 0x32, 0x5c, 0xa5, 0xa2, 0x6c, 0x67, - 0x78, 0x82, 0xee, 0x40, 0x5e, 0x75, 0x96, 0x2e, 0xdc, 0x7f, 0x6e, 0x2c, 0xdd, 0x10, 0xda, 0xc4, - 0xfa, 0xd1, 0x80, 0x92, 0xec, 0x76, 0x55, 0xd2, 0xe8, 0x2a, 0x98, 0x3e, 0x4e, 0x9c, 0xce, 0x48, - 0x4d, 0x4d, 0xb9, 0x13, 0x7c, 0x9c, 0x34, 0x05, 0x8f, 0x2e, 0x43, 0x41, 0x1c, 0x76, 0xb1, 0x9a, - 0x7a, 0x59, 0x3b, 0xef, 0xe3, 0xe4, 0x3e, 0x66, 0xe8, 0x3d, 0x10, 0xcf, 0x26, 0x87, 0x27, 0xcc, - 0xa1, 0x81, 0x7a, 0x85, 0xe9, 0x59, 0xb3, 0xe4, 0xd3, 0xa0, 0x9d, 0xb0, 0x5d, 0xfd, 0x76, 0xb8, - 0x0e, 0xcb, 0xda, 0x43, 0x9a, 0x58, 0xf5, 0xa2, 0x2b, 0x2b, 0x47, 0x2a, 0xb7, 0xd6, 0x9f, 0x06, - 0xac, 0x9c, 0xea, 0x35, 0x74, 0x1b, 0x16, 0x95, 0x5f, 0x43, 0x06, 0xb9, 0x36, 0x13, 0xe4, 0x54, - 0x14, 0xb6, 0x52, 0x45, 0x77, 0xa1, 0x48, 0x86, 0xd4, 0x23, 0x81, 0x4b, 0xe6, 0x8d, 0x69, 0xd5, - 0xd4, 0x2d, 0xad, 0xa1, 0x4d, 0xc7, 0x16, 0xe8, 0x13, 0x30, 0xc7, 0x53, 0x43, 0x46, 0x33, 0x77, - 0x26, 0x8c, 0xe7, 0x8d, 0xb6, 0x9f, 0xd8, 0xa0, 0x8f, 0x26, 0x8f, 0x5f, 0x95, 0x99, 0xf5, 0x39, - 0xe6, 0x4a, 0x41, 0x1b, 0xa7, 0xfa, 0x96, 0x07, 0x2b, 0xa7, 0x06, 0x19, 0xba, 0x03, 0x85, 0x68, - 0xd0, 0x71, 0xd2, 0x86, 0x3d, 0x05, 0x41, 0xfa, 0xf4, 0x1b, 0x74, 0xfa, 0xd4, 0xdd, 0x27, 0xa3, - 0x34, 0xcd, 0xd1, 0xa0, 0xb3, 0xaf, 0xfa, 0x3a, 0x0a, 0x9f, 0x92, 0x58, 0xe7, 0x4d, 0x31, 0xd6, - 0x08, 0xcc, 0x71, 0x71, 0xa2, 0xeb, 0xb0, 0xa4, 0xde, 0x3c, 0x5b, 0x9e, 0x17, 0x13, 0xc6, 0xf4, - 0x68, 0x3a, 0x29, 0x14, 0x8e, 0x82, 0x30, 0xc5, 0x33, 0x67, 0x2b, 0x46, 0x14, 0x30, 0x4f, 0xc4, - 0x80, 0xd3, 0x4d, 0xad, 0x39, 0xf9, 0xfc, 0xf7, 0x5b, 0x71, 0x1c, 0xc6, 0xba, 0xb5, 0x53, 0xf6, - 0xc6, 0x3e, 0xac, 0x7c, 0x46, 0x59, 0x87, 0xf4, 0xf0, 0x90, 0x86, 0x71, 0x5b, 0x8c, 0xbf, 0x12, - 0x14, 0x8e, 0x0e, 0xf6, 0x0f, 0x1e, 0x3e, 0x3e, 0xa8, 0x2c, 0x20, 0x04, 0xcb, 0xf7, 0x8e, 0x0e, - 0x1f, 0xec, 0x6e, 0x6f, 0xb5, 0x5b, 0xce, 0xa3, 0x87, 0xed, 0x56, 0xc5, 0x40, 0x97, 0xe1, 0xfc, - 0x83, 0xdd, 0xfb, 0x3b, 0x6d, 0x67, 0xfb, 0xc1, 0x6e, 0xeb, 0xa0, 0xed, 0x6c, 0xb5, 0xdb, 0x5b, - 0xdb, 0xfb, 0x95, 0x4c, 0xf3, 0xe8, 0xf9, 0xab, 0x9a, 0xf1, 0xe2, 0x55, 0xcd, 0xf8, 0xf5, 0x55, - 0xcd, 0xf8, 0xe1, 0x75, 0x6d, 0xe1, 0xc5, 0xeb, 0xda, 0xc2, 0x2f, 0xaf, 0x6b, 0x0b, 0x5f, 0xdf, - 0xe9, 0x52, 0xde, 0x1b, 0x74, 0x1a, 0x6e, 0xe8, 0x6f, 0x32, 0x42, 0x6f, 0xc9, 0x5f, 0x17, 0x37, - 0xec, 0x4b, 0xc6, 0xed, 0x61, 0x1a, 0x48, 0x6a, 0xee, 0xaf, 0x56, 0x27, 0x2f, 0xb5, 0xdf, 0xff, - 0x2b, 0x00, 0x00, 0xff, 0xff, 0x61, 0xbf, 0x37, 0x0b, 0x8a, 0x0d, 0x00, 0x00, + // 1356 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0xcb, 0x6f, 0x1b, 0x55, + 0x17, 0xcf, 0xd8, 0x8e, 0x3d, 0x73, 0xe2, 0x24, 0xd3, 0xdb, 0x97, 0x9b, 0xa6, 0x89, 0xbf, 0xf9, + 0x2a, 0x11, 0x55, 0xad, 0x23, 0x15, 0x21, 0x01, 0xad, 0x84, 0xe2, 0xd4, 0x34, 0x8f, 0x92, 0x86, + 0xc1, 0x69, 0x01, 0x09, 0x46, 0xd7, 0xe3, 0x5b, 0xfb, 0x2a, 0x9e, 0x87, 0xe6, 0x5e, 0xbb, 0xe3, + 0xee, 0x59, 0xb1, 0x41, 0xfc, 0x39, 0xec, 0xd8, 0x75, 0xd9, 0x25, 0xab, 0x0a, 0xb5, 0x12, 0x8b, + 0x6e, 0x58, 0xb2, 0x04, 0xdd, 0xc7, 0xd8, 0x4e, 0x6c, 0x28, 0x52, 0xba, 0x61, 0x77, 0xce, 0xb9, + 0xe7, 0x9c, 0xb9, 0xe7, 0x77, 0x5e, 0x77, 0xe0, 0x2a, 0x27, 0x61, 0x9b, 0x24, 0x01, 0x0d, 0xf9, + 0x26, 0x6e, 0xf9, 0x74, 0x93, 0x0f, 0x63, 0xc2, 0x6a, 0x71, 0x12, 0xf1, 0x08, 0x2d, 0x8f, 0x0f, + 0x6b, 0xe2, 0x70, 0xe5, 0x42, 0x27, 0xea, 0x44, 0xf2, 0x6c, 0x53, 0x50, 0x4a, 0x6d, 0x65, 0x75, + 0xc2, 0x87, 0x9f, 0x0c, 0x63, 0x1e, 0x6d, 0x1e, 0x93, 0xa1, 0x76, 0xb2, 0x72, 0x6d, 0xfa, 0x34, + 0x4e, 0xa2, 0xe8, 0xc9, 0x8c, 0x63, 0xf9, 0xed, 0xcd, 0x18, 0x27, 0x38, 0xd0, 0xd6, 0xce, 0x6f, + 0x06, 0x94, 0x5d, 0xc2, 0xe2, 0x28, 0x64, 0x64, 0x37, 0x7c, 0x12, 0x21, 0x04, 0x85, 0x36, 0xe6, + 0xb8, 0x62, 0x54, 0x8d, 0x0d, 0xcb, 0x95, 0x34, 0xaa, 0x40, 0x69, 0x40, 0x12, 0x46, 0xa3, 0xb0, + 0x92, 0x93, 0xe2, 0x8c, 0x45, 0xeb, 0xb0, 0x80, 0xe3, 0xd8, 0xcb, 0x4e, 0xf3, 0x55, 0x63, 0xa3, + 0xe0, 0x02, 0x8e, 0xe3, 0x47, 0x5a, 0xe1, 0x06, 0x9c, 0xeb, 0x61, 0xc6, 0xbd, 0x56, 0x2f, 0xf2, + 0x8f, 0xbd, 0x2e, 0xa1, 0x9d, 0x2e, 0xaf, 0x14, 0xaa, 0xc6, 0x46, 0xde, 0x5d, 0x16, 0x07, 0x75, + 0x21, 0xdf, 0x91, 0x62, 0x74, 0x0b, 0xce, 0x4f, 0xe8, 0x0a, 0xbf, 0x5d, 0xcc, 0xba, 0x95, 0xf9, + 0xaa, 0xb1, 0x51, 0x76, 0xed, 0x91, 0xf6, 0x56, 0x1c, 0xef, 0x60, 0xd6, 0x45, 0x37, 0x01, 0x05, + 0x34, 0xa4, 0x41, 0x3f, 0xf0, 0x3a, 0x98, 0x79, 0x71, 0x42, 0x7d, 0xc2, 0x2a, 0x45, 0x79, 0x41, + 0x5b, 0x9f, 0xdc, 0xc7, 0xec, 0x50, 0xca, 0x9d, 0x3f, 0x0c, 0x58, 0xcc, 0x02, 0xfd, 0xbc, 0x4f, + 0x92, 0xa1, 0x88, 0xd4, 0x8f, 0xda, 0x44, 0x46, 0xba, 0xe8, 0x4a, 0x1a, 0xd9, 0x90, 0xef, 0x45, + 0x1d, 0x19, 0x87, 0xe5, 0x0a, 0x52, 0x68, 0xd1, 0xf0, 0x49, 0x24, 0xef, 0x6c, 0xb9, 0x92, 0x46, + 0x17, 0x60, 0x9e, 0x86, 0x6d, 0x92, 0xca, 0xab, 0xe5, 0x5d, 0xc5, 0x08, 0xdb, 0x63, 0x32, 0x94, + 0x17, 0x28, 0xbb, 0x82, 0x14, 0x7a, 0x03, 0xdc, 0xeb, 0x93, 0x4a, 0x49, 0xca, 0x14, 0x83, 0x3e, + 0x04, 0x4b, 0x26, 0xc8, 0x8b, 0x62, 0x56, 0x31, 0xab, 0xc6, 0xc6, 0xc2, 0xed, 0xab, 0xb5, 0x89, + 0x4a, 0x50, 0x49, 0xac, 0x1d, 0x0a, 0x9d, 0x87, 0x31, 0x73, 0xcd, 0x58, 0x53, 0xe8, 0x12, 0x14, + 0x35, 0x82, 0x96, 0xfc, 0xb0, 0xe6, 0xd0, 0x2a, 0x58, 0xe2, 0xf6, 0x2c, 0xc6, 0x3e, 0xa9, 0x80, + 0xbc, 0xe8, 0x58, 0xe0, 0x7c, 0x0b, 0x28, 0x0b, 0xbc, 0x4e, 0x3a, 0x34, 0x94, 0x20, 0xa2, 0x1d, + 0x28, 0x92, 0x01, 0x09, 0x39, 0xab, 0x18, 0xd5, 0xfc, 0xc6, 0xc2, 0xed, 0x4b, 0xb5, 0x53, 0xc5, + 0x58, 0x6b, 0x88, 0xe3, 0x7a, 0xe5, 0xf9, 0xcb, 0xf5, 0xb9, 0x37, 0x2f, 0xd7, 0x6d, 0xa5, 0x7d, + 0x33, 0x0a, 0x28, 0x27, 0x41, 0xcc, 0x87, 0xae, 0xb6, 0x77, 0x7e, 0x37, 0x60, 0x39, 0xfb, 0xc0, + 0x76, 0x97, 0xf8, 0xc7, 0xcd, 0x74, 0x26, 0xb6, 0x59, 0x65, 0xe5, 0x24, 0x18, 0xaa, 0xb2, 0xa6, + 0xf1, 0xbe, 0x06, 0x20, 0xb2, 0xf9, 0x14, 0x87, 0x9c, 0xb4, 0x35, 0xc0, 0x56, 0x07, 0xb3, 0xc7, + 0x52, 0x70, 0x32, 0x54, 0xf3, 0x54, 0xa8, 0x68, 0x05, 0xcc, 0x38, 0xa1, 0x51, 0x42, 0xf9, 0x50, + 0xe2, 0x90, 0x77, 0x47, 0x3c, 0xfa, 0x3f, 0x2c, 0x0a, 0xc7, 0x84, 0x71, 0x1a, 0x60, 0xe1, 0xbb, + 0x2c, 0x15, 0xca, 0x1d, 0xcc, 0x1a, 0x99, 0x6c, 0xaf, 0x60, 0x16, 0xec, 0xf9, 0xbd, 0x82, 0x59, + 0xb4, 0x4b, 0x7b, 0x05, 0xb3, 0x64, 0x9b, 0x7b, 0x05, 0xd3, 0xb2, 0x61, 0xaf, 0x60, 0x2e, 0xd8, + 0x65, 0xb7, 0xc8, 0x24, 0x44, 0xce, 0xcf, 0x39, 0x38, 0x97, 0x45, 0x7c, 0x8f, 0xf4, 0xe8, 0x80, + 0x24, 0x67, 0x8a, 0x79, 0x56, 0x8d, 0xad, 0xcd, 0xc0, 0x61, 0x42, 0x22, 0x42, 0x15, 0x5c, 0x9f, + 0x91, 0xb6, 0x2c, 0xb9, 0xbc, 0x3b, 0xe2, 0x27, 0x72, 0x5b, 0x3a, 0x5b, 0x6e, 0xdf, 0x02, 0xf7, + 0xc7, 0xb0, 0x40, 0x06, 0x81, 0xc7, 0x53, 0x4f, 0x5e, 0xdf, 0x92, 0xb5, 0xbc, 0x32, 0xe3, 0x63, + 0x41, 0x33, 0x15, 0xc3, 0xc5, 0xb5, 0x48, 0x46, 0x3a, 0xdf, 0xe7, 0xc0, 0xce, 0x30, 0x6c, 0x84, + 0x6d, 0x55, 0x94, 0x5f, 0xc0, 0xb9, 0x01, 0xee, 0xd1, 0x36, 0xe6, 0x51, 0xe2, 0xf5, 0xe3, 0x36, + 0xe6, 0x24, 0xab, 0xcf, 0xea, 0x94, 0xdb, 0x47, 0x99, 0xe6, 0x91, 0x54, 0xac, 0x17, 0x44, 0x34, + 0xae, 0x3d, 0x38, 0x29, 0x66, 0xe8, 0x4b, 0xb8, 0xec, 0x8b, 0xaf, 0x84, 0xac, 0xcf, 0x3c, 0x39, + 0xfc, 0x46, 0xae, 0x73, 0xf2, 0xc6, 0xd3, 0xae, 0xb7, 0x33, 0xfd, 0x43, 0x39, 0x2b, 0xdd, 0x8b, + 0xfe, 0x09, 0x41, 0xe6, 0x79, 0x8c, 0x73, 0xfe, 0x8c, 0x3d, 0xf4, 0x01, 0x2c, 0x8d, 0x5a, 0x28, + 0x0a, 0x02, 0xca, 0x45, 0xb9, 0x26, 0x84, 0x63, 0x1a, 0x66, 0x43, 0x33, 0xaf, 0xca, 0x55, 0x09, + 0xd5, 0xc4, 0x74, 0xde, 0xe4, 0xe0, 0x62, 0x66, 0xf7, 0x29, 0x0d, 0x71, 0x8f, 0x3e, 0x23, 0xef, + 0xb8, 0xbd, 0xd1, 0x5d, 0x00, 0x9e, 0x7a, 0x09, 0x61, 0xfd, 0x1e, 0x17, 0x88, 0x09, 0x6f, 0xd7, + 0xa6, 0xbd, 0xa5, 0xc4, 0x6f, 0xa6, 0xae, 0xd4, 0x72, 0x2d, 0xae, 0x29, 0x36, 0x3b, 0xa3, 0xf9, + 0x33, 0x66, 0xf4, 0xab, 0xbf, 0xcf, 0x68, 0x41, 0x66, 0xf4, 0x7f, 0x93, 0xae, 0xd5, 0xc6, 0xfd, + 0x97, 0x29, 0xbd, 0x02, 0xe6, 0xa9, 0xc5, 0x53, 0xc2, 0x6a, 0xdf, 0x38, 0xcf, 0x60, 0x5e, 0x62, + 0x26, 0xda, 0x55, 0xf8, 0xcc, 0x56, 0xa4, 0xa0, 0xd1, 0x37, 0x00, 0x98, 0xf3, 0x84, 0xb6, 0xfa, + 0xaa, 0xae, 0x44, 0x80, 0xeb, 0xb3, 0x31, 0xdf, 0xca, 0xf4, 0xea, 0xab, 0x1a, 0xfc, 0x0b, 0x63, + 0xd3, 0x89, 0x04, 0x4c, 0x38, 0x74, 0x0e, 0x60, 0xe9, 0xa4, 0x6d, 0xb6, 0x6d, 0x8c, 0x19, 0xdb, + 0x26, 0x37, 0xb9, 0x6d, 0x46, 0xbb, 0x4a, 0xd4, 0x8f, 0xa9, 0x77, 0x95, 0xf3, 0x53, 0x0e, 0xca, + 0x93, 0x29, 0x7b, 0xc7, 0xc3, 0xeb, 0x2d, 0x43, 0xfc, 0xca, 0xd4, 0xec, 0x2a, 0x75, 0x30, 0x3b, + 0xfa, 0xaf, 0x8c, 0xae, 0xef, 0x0c, 0x30, 0x47, 0xc0, 0x8d, 0x77, 0xb2, 0x71, 0x62, 0x27, 0x8f, + 0x70, 0xcf, 0x49, 0x44, 0xf5, 0x1b, 0x61, 0x09, 0x72, 0x5c, 0xa5, 0xa2, 0xec, 0xe6, 0x78, 0x8a, + 0xee, 0x40, 0x51, 0x75, 0x96, 0x2e, 0xdc, 0x7f, 0x6e, 0x2c, 0xdd, 0x10, 0xda, 0xc4, 0xf9, 0xd1, + 0x80, 0x05, 0xd9, 0xed, 0xaa, 0xa4, 0xd1, 0x55, 0xb0, 0x02, 0x9c, 0x7a, 0xad, 0xa1, 0x9a, 0x9a, + 0x72, 0x27, 0x04, 0x38, 0xad, 0x0b, 0x1e, 0x5d, 0x86, 0x92, 0x38, 0xec, 0x60, 0x35, 0xf5, 0xf2, + 0x6e, 0x31, 0xc0, 0xe9, 0x7d, 0xcc, 0xd0, 0x7b, 0x20, 0x1e, 0x4b, 0x1e, 0x4f, 0x99, 0x47, 0x43, + 0xf5, 0xf6, 0xd2, 0xb3, 0x66, 0x31, 0xa0, 0x61, 0x33, 0x65, 0xbb, 0xfa, 0xc5, 0x70, 0x1d, 0x96, + 0xb4, 0x87, 0x2c, 0xb1, 0xea, 0x1d, 0x57, 0x56, 0x8e, 0x54, 0x6e, 0x9d, 0x3f, 0x0d, 0x58, 0x3e, + 0xd5, 0x6b, 0xe8, 0x36, 0xcc, 0x2b, 0xbf, 0x86, 0x0c, 0x72, 0x75, 0x2a, 0xc8, 0x89, 0x28, 0x5c, + 0xa5, 0x8a, 0xee, 0x82, 0x49, 0x06, 0xb4, 0x4d, 0x42, 0x9f, 0xcc, 0x1a, 0xd3, 0xaa, 0xa9, 0x1b, + 0x5a, 0x43, 0x9b, 0x8e, 0x2c, 0xd0, 0x27, 0x60, 0x8d, 0xa6, 0x86, 0x8c, 0x66, 0xe6, 0x4c, 0x18, + 0xcd, 0x1b, 0x6d, 0x3f, 0xb6, 0x41, 0x1f, 0x8d, 0x9f, 0xbc, 0x2a, 0x33, 0xeb, 0x33, 0xcc, 0x95, + 0x82, 0x36, 0xce, 0xf4, 0x9d, 0x36, 0x2c, 0x9f, 0x1a, 0x64, 0xe8, 0x0e, 0x94, 0xe2, 0x7e, 0xcb, + 0xcb, 0x1a, 0xf6, 0x14, 0x04, 0xd9, 0x83, 0xaf, 0xdf, 0xea, 0x51, 0x7f, 0x9f, 0x0c, 0xb3, 0x34, + 0xc7, 0xfd, 0xd6, 0xbe, 0xea, 0xeb, 0x38, 0x7a, 0x4a, 0x12, 0x9d, 0x37, 0xc5, 0x38, 0x43, 0xb0, + 0x46, 0xc5, 0x89, 0xae, 0xc3, 0xa2, 0x7a, 0x9a, 0x6c, 0xb5, 0xdb, 0x09, 0x61, 0x4c, 0x8f, 0xa6, + 0x93, 0x42, 0xe1, 0x28, 0x8c, 0x32, 0x3c, 0x0b, 0xae, 0x62, 0x44, 0x01, 0xf3, 0x54, 0x0c, 0x38, + 0xdd, 0xd4, 0x9a, 0x93, 0x8f, 0xfe, 0xa0, 0x91, 0x24, 0x51, 0xa2, 0x5b, 0x3b, 0x63, 0x6f, 0xec, + 0xc3, 0xf2, 0x67, 0x94, 0xb5, 0x48, 0x17, 0x0f, 0x68, 0x94, 0x34, 0xc5, 0xf8, 0x5b, 0x80, 0xd2, + 0xd1, 0xc1, 0xfe, 0xc1, 0xc3, 0xc7, 0x07, 0xf6, 0x1c, 0x42, 0xb0, 0x74, 0xef, 0xe8, 0xf0, 0xc1, + 0xee, 0xf6, 0x56, 0xb3, 0xe1, 0x3d, 0x7a, 0xd8, 0x6c, 0xd8, 0x06, 0xba, 0x0c, 0xe7, 0x1f, 0xec, + 0xde, 0xdf, 0x69, 0x7a, 0xdb, 0x0f, 0x76, 0x1b, 0x07, 0x4d, 0x6f, 0xab, 0xd9, 0xdc, 0xda, 0xde, + 0xb7, 0x73, 0xf5, 0xa3, 0xe7, 0xaf, 0xd6, 0x8c, 0x17, 0xaf, 0xd6, 0x8c, 0x5f, 0x5f, 0xad, 0x19, + 0x3f, 0xbc, 0x5e, 0x9b, 0x7b, 0xf1, 0x7a, 0x6d, 0xee, 0x97, 0xd7, 0x6b, 0x73, 0x5f, 0xdf, 0xe9, + 0x50, 0xde, 0xed, 0xb7, 0x6a, 0x7e, 0x14, 0x6c, 0x32, 0x42, 0x6f, 0xc9, 0x1f, 0x16, 0x3f, 0xea, + 0x49, 0xc6, 0xef, 0x62, 0x1a, 0x4a, 0x6a, 0xe6, 0x0f, 0x56, 0xab, 0x28, 0xb5, 0xdf, 0xff, 0x2b, + 0x00, 0x00, 0xff, 0xff, 0xd5, 0xe2, 0x1c, 0x51, 0x80, 0x0d, 0x00, 0x00, } func (m *ResponseInfo) Marshal() (dAtA []byte, err error) { @@ -1567,13 +1559,6 @@ func (m *ResponseCheckTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x50 } - if len(m.Sender) > 0 { - i -= len(m.Sender) - copy(dAtA[i:], m.Sender) - i = encodeVarintTypes(dAtA, i, uint64(len(m.Sender))) - i-- - dAtA[i] = 0x4a - } if len(m.Codespace) > 0 { i -= len(m.Codespace) copy(dAtA[i:], m.Codespace) @@ -2432,10 +2417,6 @@ func (m *ResponseCheckTx) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - l = len(m.Sender) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) - } if m.Priority != 0 { n += 1 + sovTypes(uint64(m.Priority)) } @@ -3529,38 +3510,6 @@ func (m *ResponseCheckTx) Unmarshal(dAtA []byte) error { } m.Codespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Sender = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 63ab63e210..2cc1f719e2 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -850,21 +850,8 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, res *abci.ResponseCheck return err } - sender := res.Sender priority := res.Priority - if len(sender) > 0 { - if wtx := txmp.txStore.GetTxBySender(sender); wtx != nil { - logger.Error( - "rejected incoming good transaction; tx already exists for sender", - "tx", wtx.Hash(), - "sender", sender, - ) - txmp.metrics.RejectedTxs.Add(1) - return nil - } - } - if err := txmp.canAddTx(wtx); err != nil { evictTxs := txmp.priorityIndex.GetEvictableTxs( priority, @@ -906,7 +893,6 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, res *abci.ResponseCheck wtx.gasWanted = res.GasWanted wtx.estimatedGas = res.GasEstimated wtx.priority = priority - wtx.sender = sender wtx.peers = map[uint16]struct{}{ txInfo.SenderID: {}, } diff --git a/sei-tendermint/internal/mempool/mempool_test.go b/sei-tendermint/internal/mempool/mempool_test.go index c1e8453dff..ce9f6fb5e1 100644 --- a/sei-tendermint/internal/mempool/mempool_test.go +++ b/sei-tendermint/internal/mempool/mempool_test.go @@ -41,10 +41,7 @@ var DefaultGasWanted = int64(1) func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) (*abci.ResponseCheckTxV2, error) { - var ( - priority int64 - sender string - ) + var priority int64 gasWanted := DefaultGasWanted if app.gasWanted != nil { @@ -143,7 +140,6 @@ func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) ( } priority = v - sender = string(parts[0]) } else { return &abci.ResponseCheckTxV2{ResponseCheckTx: &abci.ResponseCheckTx{ Priority: priority, @@ -154,7 +150,6 @@ func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTxV2) ( } return &abci.ResponseCheckTxV2{ResponseCheckTx: &abci.ResponseCheckTx{ Priority: priority, - Sender: sender, Code: code.CodeTypeOK, GasWanted: gasWanted, GasEstimated: gasEstimated, @@ -835,34 +830,6 @@ func TestTxMempool_CheckTxSamePeer(t *testing.T) { require.Error(t, err) } -func TestTxMempool_CheckTxSameSender(t *testing.T) { - ctx := t.Context() - - client := &application{Application: kvstore.NewApplication()} - - txmp := setup(t, client, 100, NopTxConstraintsFetcher) - peerID := uint16(1) - rng := rand.New(rand.NewSource(time.Now().UnixNano())) - - prefix1 := make([]byte, 20) - _, err := rng.Read(prefix1) - require.NoError(t, err) - - prefix2 := make([]byte, 20) - _, err = rng.Read(prefix2) - require.NoError(t, err) - - tx1 := []byte(fmt.Sprintf("sender-0=%X=%d", prefix1, 50)) - tx2 := []byte(fmt.Sprintf("sender-0=%X=%d", prefix2, 50)) - - _, err = txmp.CheckTx(ctx, tx1, TxInfo{SenderID: peerID}) - require.NoError(t, err) - require.Equal(t, 1, txmp.Size()) - _, err = txmp.CheckTx(ctx, tx2, TxInfo{SenderID: peerID}) - require.NoError(t, err) - require.Equal(t, 1, txmp.Size()) -} - func TestTxMempool_ConcurrentTxs(t *testing.T) { ctx := t.Context() diff --git a/sei-tendermint/internal/mempool/priority_queue_test.go b/sei-tendermint/internal/mempool/priority_queue_test.go index aea1f41080..3533c1aced 100644 --- a/sei-tendermint/internal/mempool/priority_queue_test.go +++ b/sei-tendermint/internal/mempool/priority_queue_test.go @@ -50,7 +50,7 @@ func TestTxPriorityQueue_ReapHalf(t *testing.T) { func TestAvoidPanicIfTransactionIsNil(t *testing.T) { pq := NewTxPriorityQueue() - pq.Push(&WrappedTx{sender: "1", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}) + pq.Push(&WrappedTx{isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}) pq.txs = append(pq.txs, nil) var count int @@ -67,70 +67,70 @@ func TestTxPriorityQueue_PriorityAndNonceOrdering(t *testing.T) { { name: "PriorityWithEVMAndNonEVMDuplicateNonce", inputTxs: []*WrappedTx{ - {sender: "1", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {sender: "3", isEVM: true, evmAddress: "0xabc", evmNonce: 3, priority: 9}, - {sender: "2", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 7}, + {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}, }, expectedOutput: []int64{1, 3}, }, { name: "PriorityWithEVMAndNonEVMDuplicateNonce", inputTxs: []*WrappedTx{ - {sender: "1", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {sender: "2", isEVM: false, priority: 9}, - {sender: "4", isEVM: true, evmAddress: "0xabc", evmNonce: 0, priority: 9}, // Same EVM address as first, lower nonce - {sender: "5", isEVM: true, evmAddress: "0xdef", evmNonce: 1, priority: 7}, - {sender: "3", isEVM: true, evmAddress: "0xdef", evmNonce: 0, priority: 8}, - {sender: "6", isEVM: false, priority: 6}, - {sender: "7", isEVM: true, evmAddress: "0xghi", evmNonce: 2, priority: 5}, + {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}, }, expectedOutput: []int64{2, 4, 1, 3, 5, 6, 7}, }, { name: "PriorityWithEVMAndNonEVM", inputTxs: []*WrappedTx{ - {sender: "1", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {sender: "2", isEVM: false, priority: 9}, - {sender: "4", isEVM: true, evmAddress: "0xabc", evmNonce: 0, priority: 9}, // Same EVM address as first, lower nonce - {sender: "5", isEVM: true, evmAddress: "0xdef", evmNonce: 1, priority: 7}, - {sender: "3", isEVM: true, evmAddress: "0xdef", evmNonce: 0, priority: 8}, - {sender: "6", isEVM: false, priority: 6}, - {sender: "7", isEVM: true, evmAddress: "0xghi", evmNonce: 2, priority: 5}, + {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}, }, expectedOutput: []int64{2, 4, 1, 3, 5, 6, 7}, }, { name: "IdenticalPrioritiesAndNoncesDifferentAddresses", inputTxs: []*WrappedTx{ - {sender: "1", isEVM: true, evmAddress: "0xabc", evmNonce: 2, priority: 5}, - {sender: "2", isEVM: true, evmAddress: "0xdef", evmNonce: 2, priority: 5}, - {sender: "3", isEVM: true, evmAddress: "0xghi", evmNonce: 2, priority: 5}, + {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}, }, expectedOutput: []int64{1, 2, 3}, }, { name: "InterleavedEVAndNonEVMTransactions", inputTxs: []*WrappedTx{ - {sender: "7", isEVM: false, priority: 15}, - {sender: "8", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 20}, - {sender: "9", isEVM: false, priority: 10}, - {sender: "10", isEVM: true, evmAddress: "0xdef", evmNonce: 2, priority: 20}, + {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}, }, expectedOutput: []int64{8, 10, 7, 9}, }, { name: "SameAddressPriorityDifferentNonces", inputTxs: []*WrappedTx{ - {sender: "11", isEVM: true, evmAddress: "0xabc", evmNonce: 3, priority: 10}, - {sender: "12", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, - {sender: "13", isEVM: true, evmAddress: "0xabc", evmNonce: 2, priority: 10}, + {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}, }, expectedOutput: []int64{12, 13, 11}, }, { name: "OneItem", inputTxs: []*WrappedTx{ - {sender: "14", isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, + {hashedTx: newHashedTx(types.Tx("14")), isEVM: true, evmAddress: "0xabc", evmNonce: 1, priority: 10}, }, expectedOutput: []int64{14}, }, @@ -144,7 +144,6 @@ func TestTxPriorityQueue_PriorityAndNonceOrdering(t *testing.T) { // Add input transactions to the queue and set timestamp to order inserted for i, tx := range tc.inputTxs { tx.timestamp = now.Add(time.Duration(i) * time.Second) - tx.hashedTx = newHashedTx(types.Tx(fmt.Sprintf("%d", time.Now().UnixNano()))) pq.PushTx(tx) } @@ -153,7 +152,7 @@ func TestTxPriorityQueue_PriorityAndNonceOrdering(t *testing.T) { require.Len(t, results, len(tc.expectedOutput)) for i, expectedTxID := range tc.expectedOutput { tx := results[i] - require.Equal(t, fmt.Sprintf("%d", expectedTxID), tx.sender) + require.Equal(t, fmt.Sprintf("%d", expectedTxID), string(tx.Tx())) } }) } diff --git a/sei-tendermint/internal/mempool/tx.go b/sei-tendermint/internal/mempool/tx.go index e2c2f8798e..6c18a2a172 100644 --- a/sei-tendermint/internal/mempool/tx.go +++ b/sei-tendermint/internal/mempool/tx.go @@ -56,10 +56,6 @@ type WrappedTx struct { // in the ResponseCheckTx response. priority int64 - // sender defines the transaction's sender as specified by the application in - // the ResponseCheckTx response. - sender string - // timestamp is the time at which the node first received the transaction from // a peer. It is used as a second dimension is prioritizing transactions when // two transactions have the same priority. @@ -97,7 +93,6 @@ func (wtx *WrappedTx) IsBefore(tx *WrappedTx) bool { type txStoreInner struct { byHash map[types.TxHash]*WrappedTx // primary index - bySender map[string]*WrappedTx // sender is defined by the ABCI application sizeBytes utils.AtomicSend[int64] } @@ -114,7 +109,6 @@ type TxStore struct { func NewTxStore() *TxStore { inner := &txStoreInner{ - bySender: make(map[string]*WrappedTx), byHash: make(map[types.TxHash]*WrappedTx), sizeBytes: utils.NewAtomicSend[int64](0), } @@ -157,15 +151,6 @@ func (txs *TxStore) GetAllTxs() []*WrappedTx { panic("unreachable") } -// GetTxBySender returns a *WrappedTx by the transaction's sender property -// defined by the ABCI application. -func (txs *TxStore) GetTxBySender(sender string) *WrappedTx { - for inner := range txs.inner.RLock() { - return inner.bySender[sender] - } - panic("unreachable") -} - // GetTxByHash returns a *WrappedTx by the transaction's hash. func (txs *TxStore) GetTxByHash(key types.TxHash) *WrappedTx { for inner := range txs.inner.RLock() { @@ -192,15 +177,10 @@ func (txs *TxStore) IsTxRemoved(wtx *WrappedTx) bool { return false } -// SetTx stores a *WrappedTx by it's hash. If the transaction also contains a -// non-empty sender, we additionally store the transaction by the sender as -// defined by the ABCI application. +// SetTx stores a *WrappedTx by its hash. func (txs *TxStore) SetTx(wtx *WrappedTx) { for inner := range txs.inner.Lock() { existing := inner.byHash[wtx.Hash()] - if len(wtx.sender) > 0 { - inner.bySender[wtx.sender] = wtx - } inner.byHash[wtx.Hash()] = wtx if existing == nil { inner.sizeBytes.Store(inner.sizeBytes.Load() + int64(wtx.Size())) @@ -212,9 +192,6 @@ func (txs *TxStore) SetTx(wtx *WrappedTx) { // indexes of the transaction. func (txs *TxStore) RemoveTx(wtx *WrappedTx) { for inner := range txs.inner.Lock() { - if len(wtx.sender) > 0 { - delete(inner.bySender, wtx.sender) - } if _, ok := inner.byHash[wtx.Hash()]; ok { delete(inner.byHash, wtx.Hash()) inner.sizeBytes.Store(inner.sizeBytes.Load() - int64(wtx.Size())) diff --git a/sei-tendermint/internal/mempool/tx_test.go b/sei-tendermint/internal/mempool/tx_test.go index b3fb181edd..5f0689ca5a 100644 --- a/sei-tendermint/internal/mempool/tx_test.go +++ b/sei-tendermint/internal/mempool/tx_test.go @@ -12,30 +12,10 @@ import ( "github.com/sei-protocol/sei-chain/sei-tendermint/types" ) -func TestTxStore_GetTxBySender(t *testing.T) { - txs := NewTxStore() - wtx := &WrappedTx{ - hashedTx: newHashedTx(types.Tx("test_tx")), - sender: "foo", - priority: 1, - timestamp: time.Now(), - } - - res := txs.GetTxBySender(wtx.sender) - require.Nil(t, res) - - txs.SetTx(wtx) - - res = txs.GetTxBySender(wtx.sender) - require.NotNil(t, res) - require.Equal(t, wtx, res) -} - func TestTxStore_GetTxByHash(t *testing.T) { txs := NewTxStore() wtx := &WrappedTx{ hashedTx: newHashedTx(types.Tx("test_tx")), - sender: "foo", priority: 1, timestamp: time.Now(), } @@ -65,13 +45,6 @@ func TestTxStore_SetTx(t *testing.T) { res := txs.GetTxByHash(key) require.NotNil(t, res) require.Equal(t, wtx, res) - - wtx.sender = "foo" - txs.SetTx(wtx) - - res = txs.GetTxByHash(key) - require.NotNil(t, res) - require.Equal(t, wtx, res) } func TestTxStore_IsTxRemoved(t *testing.T) { diff --git a/sei-tendermint/proto/tendermint/abci/types.proto b/sei-tendermint/proto/tendermint/abci/types.proto index b700f24aa2..40dd15dce1 100644 --- a/sei-tendermint/proto/tendermint/abci/types.proto +++ b/sei-tendermint/proto/tendermint/abci/types.proto @@ -50,11 +50,11 @@ message ResponseCheckTx { string log = 3; // nondeterministic int64 gas_wanted = 5; string codespace = 8; - string sender = 9; int64 priority = 10; int64 gas_estimated = 12; - reserved 4, 6, 7, 11; // see https://github.com/tendermint/tendermint/issues/8543 + reserved 4, 6, 7, 9, 11; // see https://github.com/tendermint/tendermint/issues/8543 + reserved "sender"; } message ResponseDeliverTx { diff --git a/sei-tendermint/spec/abci++/abci++_methods_002_draft.md b/sei-tendermint/spec/abci++/abci++_methods_002_draft.md index 4eb1bb295e..f40bca2333 100644 --- a/sei-tendermint/spec/abci++/abci++_methods_002_draft.md +++ b/sei-tendermint/spec/abci++/abci++_methods_002_draft.md @@ -138,7 +138,6 @@ title: Methods | data | bytes | Result bytes, if any. | 2 | | gas_wanted | int64 | Amount of gas requested for transaction. | 5 | | codespace | string | Namespace for the `code`. | 8 | - | sender | string | The transaction's sender (e.g. the signer) | 9 | | priority | int64 | The transaction's priority (for mempool ordering) | 10 | * **Usage**: diff --git a/sei-tendermint/spec/abci/abci.md b/sei-tendermint/spec/abci/abci.md index 5d9d59b711..49460148c2 100644 --- a/sei-tendermint/spec/abci/abci.md +++ b/sei-tendermint/spec/abci/abci.md @@ -405,7 +405,6 @@ the blockchain's `AppHash` which is verified via [light client verification](../ | gas_used | int64 | Amount of gas consumed by transaction. | 6 | | events | repeated [Event](#events) | Type & Key-Value events for indexing transactions (eg. by account). | 7 | | codespace | string | Namespace for the `code`. | 8 | - | sender | string | The transaction's sender (e.g. the signer) | 9 | | priority | int64 | The transaction's priority (for mempool ordering) | 10 | * **Usage**: From ed35c517a8c8a1f0681f8eead7263a0549c01bc9 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Thu, 30 Apr 2026 14:07:03 +0200 Subject: [PATCH 03/33] out-of-process configs deprecated --- docker/localnode/config/config.toml | 7 ------- docker/rpcnode/config/config.toml | 7 ------- docs/migration/seiv2_config_migration.md | 7 ------- sei-cosmos/server/README.md | 4 ++-- sei-cosmos/server/start.go | 17 +++++++++++------ .../cmd/tendermint/commands/run_node.go | 15 +++++++++------ sei-tendermint/config/config.go | 7 ++++--- sei-tendermint/config/toml.go | 7 ------- sei-tendermint/config/toml_test.go | 1 - sei-tendermint/test/e2e/README.md | 18 ------------------ 10 files changed, 26 insertions(+), 64 deletions(-) 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/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/start.go b/sei-cosmos/server/start.go index 417fa51085..bbee42f884 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -86,14 +86,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 +209,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 +246,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( 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/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. From 3086a7199a6e594af2f71737a4a51aa55027f7ec Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 1 May 2026 10:48:29 +0200 Subject: [PATCH 04/33] generic client context --- app/app.go | 12 ++- sei-cosmos/client/broadcast.go | 10 +-- sei-cosmos/client/broadcast_test.go | 6 +- sei-cosmos/client/cmd.go | 5 +- sei-cosmos/client/config/config.go | 6 +- sei-cosmos/client/context.go | 89 +++++++++++++---------- sei-cosmos/client/grpc_query.go | 6 +- sei-cosmos/client/query.go | 29 ++++---- sei-cosmos/server/start.go | 3 +- sei-cosmos/testutil/network/util.go | 4 +- sei-cosmos/x/auth/tx/service.go | 2 +- sei-cosmos/x/genutil/client/rest/query.go | 7 +- 12 files changed, 102 insertions(+), 77 deletions(-) diff --git a/app/app.go b/app/app.go index 247d008a4f..d03b0e7437 100644 --- a/app/app.go +++ b/app/app.go @@ -2388,7 +2388,11 @@ 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) + client, err := clientCtx.GetNode() + if err != nil { + panic(err) + } + evmHTTPServer, err := evmrpc.NewEVMHTTPServer(app.evmRPCConfig, client, &app.EvmKeeper, app.BeginBlockKeepers, app.BaseApp, app.TracerAnteHandler, app.RPCContextProvider, txConfigProvider, DefaultNodeHome, app.GetStateStore(), nil) if err != nil { panic(err) } @@ -2401,7 +2405,11 @@ 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()) + client, err := clientCtx.GetNode() + if err != nil { + panic(err) + } + evmWSServer, err := evmrpc.NewEVMWebSocketServer(app.evmRPCConfig, client, &app.EvmKeeper, app.BeginBlockKeepers, app.BaseApp, app.TracerAnteHandler, app.RPCContextProvider, txConfigProvider, DefaultNodeHome, app.GetStateStore()) if err != nil { panic(err) } diff --git a/sei-cosmos/client/broadcast.go b/sei-cosmos/client/broadcast.go index 4db88cf038..199e9b94e6 100644 --- a/sei-cosmos/client/broadcast.go +++ b/sei-cosmos/client/broadcast.go @@ -48,7 +48,7 @@ func (e ErrMempoolIsFull) Error() string { // based on the context parameters. 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) { +func (ctx contextG[C]) BroadcastTx(txBytes []byte) (res *sdk.TxResponse, err error) { switch ctx.BroadcastMode { case flags.BroadcastSync: res, err = ctx.BroadcastTxSync(txBytes) @@ -116,7 +116,7 @@ func CheckTendermintError(err error, tx tmtypes.Tx) *sdk.TxResponse { // 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) { +func (ctx contextG[C]) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { return nil, err @@ -135,7 +135,7 @@ func (ctx Context) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error) { // BroadcastTxSync broadcasts transaction bytes to a Tendermint node // synchronously (i.e. returns after CheckTx execution). -func (ctx Context) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { +func (ctx contextG[C]) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { return nil, err @@ -151,7 +151,7 @@ func (ctx Context) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { // BroadcastTxAsync broadcasts transaction bytes to a Tendermint node // asynchronously (i.e. returns immediately). -func (ctx Context) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { +func (ctx contextG[C]) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { return nil, err @@ -167,7 +167,7 @@ func (ctx Context) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { // 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 (clientCtx contextG[C]) TxServiceBroadcast(grpcCtx context.Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { if req == nil || req.TxBytes == nil { return nil, status.Error(codes.InvalidArgument, "invalid empty tx") } diff --git a/sei-cosmos/client/broadcast_test.go b/sei-cosmos/client/broadcast_test.go index ec04fcc0e1..bdcb55cc58 100644 --- a/sei-cosmos/client/broadcast_test.go +++ b/sei-cosmos/client/broadcast_test.go @@ -13,6 +13,8 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client/flags" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" + "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" + rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" ) type MockClient struct { @@ -34,8 +36,8 @@ func (c MockClient) BroadcastTxSync(ctx context.Context, tx tmtypes.Tx) (*ctypes func CreateContextWithErrorAndMode(err error, mode string) Context { return Context{ - Client: MockClient{err: err}, - BroadcastMode: mode, + Client: utils.Some[rpcclient.Client](MockClient{err: err}), + contextBase: contextBase{BroadcastMode: mode}, } } diff --git a/sei-cosmos/client/cmd.go b/sei-cosmos/client/cmd.go index 526cd26f2d..7478263ce7 100644 --- a/sei-cosmos/client/cmd.go +++ b/sei-cosmos/client/cmd.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/cli" + rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -133,7 +134,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) @@ -143,7 +144,7 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont return clientCtx, err } - clientCtx = clientCtx.WithClient(client) + clientCtx = WithClient[rpcclient.Client](clientCtx, client) } } diff --git a/sei-cosmos/client/config/config.go b/sei-cosmos/client/config/config.go index bae915baa7..cf25080b10 100644 --- a/sei-cosmos/client/config/config.go +++ b/sei-cosmos/client/config/config.go @@ -6,6 +6,7 @@ import ( "path/filepath" "github.com/sei-protocol/sei-chain/sei-cosmos/client" + rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" ) // Default constants @@ -84,13 +85,12 @@ func ReadFromClientConfig(ctx client.Context) (client.Context, error) { ctx = ctx.WithKeyring(keyring) // https://github.com/cosmos/cosmos-sdk/issues/8986 - client, err := client.NewClientFromNode(conf.Node) + c, err := client.NewClientFromNode(conf.Node) if err != nil { return ctx, fmt.Errorf("couldn't get client from nodeURI: %v", err) } - ctx = ctx.WithNodeURI(conf.Node). - WithClient(client). + ctx = client.WithClient[rpcclient.Client](ctx.WithNodeURI(conf.Node), c). WithBroadcastMode(conf.BroadcastMode) return ctx, nil diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index b6a70f605e..cd2a497d8f 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -12,6 +12,7 @@ import ( "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 +21,10 @@ 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 Context struct { +type Context = contextG[rpcclient.Client] + +type contextBase struct { FromAddress sdk.AccAddress - Client rpcclient.Client ChainID string // Deprecated: Codec codec will be changed to Codec: codec.Codec JSONCodec codec.JSONCodec @@ -58,20 +58,27 @@ type Context struct { LegacyAmino *codec.LegacyAmino } +// Context implements a typical context created in SDK modules for transaction +// handling and queries. +type contextG[C rpcclient.Client] struct { + contextBase + Client utils.Option[C] +} + // WithKeyring returns a copy of the context with an updated keyring. -func (ctx Context) WithKeyring(k keyring.Keyring) Context { +func (ctx contextG[C]) WithKeyring(k keyring.Keyring) contextG[C] { ctx.Keyring = k return ctx } // WithKeyringOptions returns a copy of the context with an updated keyring. -func (ctx Context) WithKeyringOptions(opts ...keyring.Option) Context { +func (ctx contextG[C]) WithKeyringOptions(opts ...keyring.Option) contextG[C] { ctx.KeyringOptions = opts return ctx } // WithInput returns a copy of the context with an updated input. -func (ctx Context) WithInput(r io.Reader) Context { +func (ctx contextG[C]) WithInput(r io.Reader) contextG[C] { // convert to a bufio.Reader to have a shared buffer between the keyring and the // the Commands, ensuring a read from one advance the read pointer for the other. // see https://github.com/cosmos/cosmos-sdk/issues/9566. @@ -80,7 +87,7 @@ func (ctx Context) WithInput(r io.Reader) Context { } // Deprecated: WithJSONCodec returns a copy of the Context with an updated JSONCodec. -func (ctx Context) WithJSONCodec(m codec.JSONCodec) Context { +func (ctx contextG[C]) WithJSONCodec(m codec.JSONCodec) contextG[C] { ctx.JSONCodec = m // since we are using ctx.Codec everywhere in the SDK, for backward compatibility // we need to try to set it here as well. @@ -91,7 +98,7 @@ func (ctx Context) WithJSONCodec(m codec.JSONCodec) Context { } // WithCodec returns a copy of the Context with an updated Codec. -func (ctx Context) WithCodec(m codec.Codec) Context { +func (ctx contextG[C]) WithCodec(m codec.Codec) contextG[C] { ctx.JSONCodec = m ctx.Codec = m return ctx @@ -99,62 +106,64 @@ func (ctx Context) WithCodec(m codec.Codec) Context { // WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec. // TODO: Deprecated (remove). -func (ctx Context) WithLegacyAmino(cdc *codec.LegacyAmino) Context { +func (ctx contextG[C]) WithLegacyAmino(cdc *codec.LegacyAmino) contextG[C] { ctx.LegacyAmino = cdc return ctx } // WithOutput returns a copy of the context with an updated output writer (e.g. stdout). -func (ctx Context) WithOutput(w io.Writer) Context { +func (ctx contextG[C]) WithOutput(w io.Writer) contextG[C] { ctx.Output = w return ctx } // WithFrom returns a copy of the context with an updated from address or name. -func (ctx Context) WithFrom(from string) Context { +func (ctx contextG[C]) WithFrom(from string) contextG[C] { ctx.From = from return ctx } // WithOutputFormat returns a copy of the context with an updated OutputFormat field. -func (ctx Context) WithOutputFormat(format string) Context { +func (ctx contextG[C]) WithOutputFormat(format string) contextG[C] { ctx.OutputFormat = format return ctx } // WithNodeURI returns a copy of the context with an updated node URI. -func (ctx Context) WithNodeURI(nodeURI string) Context { +func (ctx contextG[C]) WithNodeURI(nodeURI string) contextG[C] { ctx.NodeURI = nodeURI return ctx } // WithHeight returns a copy of the context with an updated height. -func (ctx Context) WithHeight(height int64) Context { +func (ctx contextG[C]) WithHeight(height int64) contextG[C] { ctx.Height = height return ctx } // WithClient returns a copy of the context with an updated RPC client // instance. -func (ctx Context) WithClient(client rpcclient.Client) Context { - ctx.Client = client - return ctx +func WithClient[C2, C1 rpcclient.Client](ctx contextG[C1], client C2) contextG[C2] { + return contextG[C2]{ + contextBase: ctx.contextBase, + Client: utils.Some(client), + } } // WithUseLedger returns a copy of the context with an updated UseLedger flag. -func (ctx Context) WithUseLedger(useLedger bool) Context { +func (ctx contextG[C]) WithUseLedger(useLedger bool) contextG[C] { ctx.UseLedger = useLedger return ctx } // WithChainID returns a copy of the context with an updated chain ID. -func (ctx Context) WithChainID(chainID string) Context { +func (ctx contextG[C]) WithChainID(chainID string) contextG[C] { ctx.ChainID = chainID return ctx } // WithHomeDir returns a copy of the Context with HomeDir set. -func (ctx Context) WithHomeDir(dir string) Context { +func (ctx contextG[C]) WithHomeDir(dir string) contextG[C] { if dir != "" { ctx.HomeDir = dir } @@ -162,91 +171,91 @@ func (ctx Context) WithHomeDir(dir string) Context { } // WithKeyringDir returns a copy of the Context with KeyringDir set. -func (ctx Context) WithKeyringDir(dir string) Context { +func (ctx contextG[C]) WithKeyringDir(dir string) contextG[C] { ctx.KeyringDir = dir return ctx } // WithGenerateOnly returns a copy of the context with updated GenerateOnly value -func (ctx Context) WithGenerateOnly(generateOnly bool) Context { +func (ctx contextG[C]) WithGenerateOnly(generateOnly bool) contextG[C] { ctx.GenerateOnly = generateOnly return ctx } // WithSimulation returns a copy of the context with updated Simulate value -func (ctx Context) WithSimulation(simulate bool) Context { +func (ctx contextG[C]) WithSimulation(simulate bool) contextG[C] { ctx.Simulate = simulate return ctx } // WithOffline returns a copy of the context with updated Offline value. -func (ctx Context) WithOffline(offline bool) Context { +func (ctx contextG[C]) WithOffline(offline bool) contextG[C] { ctx.Offline = offline return ctx } // WithFromName returns a copy of the context with an updated from account name. -func (ctx Context) WithFromName(name string) Context { +func (ctx contextG[C]) WithFromName(name string) contextG[C] { ctx.FromName = name return ctx } // WithFromAddress returns a copy of the context with an updated from account // address. -func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context { +func (ctx contextG[C]) WithFromAddress(addr sdk.AccAddress) contextG[C] { ctx.FromAddress = addr return ctx } // WithFeeGranterAddress returns a copy of the context with an updated fee granter account // address. -func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context { +func (ctx contextG[C]) WithFeeGranterAddress(addr sdk.AccAddress) contextG[C] { ctx.FeeGranter = addr return ctx } // WithBroadcastMode returns a copy of the context with an updated broadcast // mode. -func (ctx Context) WithBroadcastMode(mode string) Context { +func (ctx contextG[C]) WithBroadcastMode(mode string) contextG[C] { ctx.BroadcastMode = mode return ctx } // WithSignModeStr returns a copy of the context with an updated SignMode // value. -func (ctx Context) WithSignModeStr(signModeStr string) Context { +func (ctx contextG[C]) WithSignModeStr(signModeStr string) contextG[C] { ctx.SignModeStr = signModeStr return ctx } // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm // value. -func (ctx Context) WithSkipConfirmation(skip bool) Context { +func (ctx contextG[C]) WithSkipConfirmation(skip bool) contextG[C] { ctx.SkipConfirm = skip return ctx } // WithTxConfig returns the context with an updated TxConfig -func (ctx Context) WithTxConfig(generator TxConfig) Context { +func (ctx contextG[C]) WithTxConfig(generator TxConfig) contextG[C] { ctx.TxConfig = generator return ctx } // WithAccountRetriever returns the context with an updated AccountRetriever -func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context { +func (ctx contextG[C]) WithAccountRetriever(retriever AccountRetriever) contextG[C] { ctx.AccountRetriever = retriever return ctx } // WithInterfaceRegistry returns the context with an updated InterfaceRegistry -func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context { +func (ctx contextG[C]) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) contextG[C] { ctx.InterfaceRegistry = interfaceRegistry return ctx } // WithViper returns the context with Viper field. This Viper instance is used to read // client-side config from the config file. -func (ctx Context) WithViper(prefix string) Context { +func (ctx contextG[C]) WithViper(prefix string) contextG[C] { v := viper.New() v.SetEnvPrefix(prefix) v.AutomaticEnv() @@ -255,13 +264,13 @@ func (ctx Context) WithViper(prefix string) Context { } // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout -func (ctx Context) PrintString(str string) error { +func (ctx contextG[C]) PrintString(str string) error { return ctx.PrintBytes([]byte(str)) } // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout. // NOTE: for printing a complex state object, you should use ctx.PrintOutput -func (ctx Context) PrintBytes(o []byte) error { +func (ctx contextG[C]) PrintBytes(o []byte) error { writer := ctx.Output if writer == nil { writer = os.Stdout @@ -274,7 +283,7 @@ func (ctx Context) PrintBytes(o []byte) error { // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint // will be JSON encoded using ctx.Codec. An error is returned upon failure. -func (ctx Context) PrintProto(toPrint proto.Message) error { +func (ctx contextG[C]) PrintProto(toPrint proto.Message) error { // always serialize JSON initially because proto json can't be directly YAML encoded out, err := ctx.Codec.MarshalAsJSON(toPrint) if err != nil { @@ -286,7 +295,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 contextG[C]) PrintObjectLegacy(toPrint interface{}) error { out, err := ctx.LegacyAmino.MarshalAsJSON(toPrint) if err != nil { return err @@ -294,7 +303,7 @@ func (ctx Context) PrintObjectLegacy(toPrint interface{}) error { return ctx.printOutput(out) } -func (ctx Context) printOutput(out []byte) error { +func (ctx contextG[C]) printOutput(out []byte) error { if ctx.OutputFormat == "text" { // handle text format by decoding and re-encoding JSON as YAML var j interface{} diff --git a/sei-cosmos/client/grpc_query.go b/sei-cosmos/client/grpc_query.go index a44da6e197..8cbd7f38c1 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 contextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err 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,7 +41,7 @@ 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) + broadcastRes, err := ctx.TxServiceBroadcast(grpcCtx, reqProto) if err != nil { return err } @@ -111,6 +111,6 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, req, reply i } // NewStream implements the grpc ClientConn.NewStream method -func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { +func (contextG[C]) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { return nil, fmt.Errorf("streaming rpc not supported") } diff --git a/sei-cosmos/client/query.go b/sei-cosmos/client/query.go index 6f2c0fe689..504e43c730 100644 --- a/sei-cosmos/client/query.go +++ b/sei-cosmos/client/query.go @@ -20,32 +20,31 @@ 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 contextG[C]) GetNode() (rpcclient.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. // It returns the result and height of the query upon success or an error if // the query fails. -func (ctx Context) Query(path string) ([]byte, int64, error) { +func (ctx contextG[C]) Query(path string) ([]byte, int64, error) { return ctx.query(path, nil) } // QueryWithData performs a query to a Tendermint node with the provided path // and a data payload. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx Context) QueryWithData(path string, data []byte) ([]byte, int64, error) { +func (ctx contextG[C]) QueryWithData(path string, data []byte) ([]byte, int64, error) { return ctx.query(path, data) } // QueryStore performs a query to a Tendermint node with the provided key and // store name. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx Context) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { +func (ctx contextG[C]) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { return ctx.queryStore(key, storeName, "key") } @@ -53,26 +52,26 @@ func (ctx Context) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, i // It returns the ResultQuery obtained from the query. The height used to perform // the query is the RequestQuery Height if it is non-zero, otherwise the context // height is used. -func (ctx Context) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { +func (ctx contextG[C]) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { return ctx.queryABCI(req) } // GetFromAddress returns the from address from the context's name. -func (ctx Context) GetFromAddress() sdk.AccAddress { +func (ctx contextG[C]) GetFromAddress() sdk.AccAddress { return ctx.FromAddress } // GetFeeGranterAddress returns the fee granter address from the context -func (ctx Context) GetFeeGranterAddress() sdk.AccAddress { +func (ctx contextG[C]) GetFeeGranterAddress() sdk.AccAddress { return ctx.FeeGranter } // GetFromName returns the key name for the current context. -func (ctx Context) GetFromName() string { +func (ctx contextG[C]) GetFromName() string { return ctx.FromName } -func (ctx Context) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { +func (ctx contextG[C]) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { node, err := ctx.GetNode() if err != nil { return abci.ResponseQuery{}, err @@ -124,7 +123,7 @@ func sdkErrorToGRPCError(resp abci.ResponseQuery) error { // query performs a query to a Tendermint node with the provided store name // and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx Context) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { +func (ctx contextG[C]) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { resp, err := ctx.queryABCI(abci.RequestQuery{ Path: path, Data: key, @@ -140,7 +139,7 @@ func (ctx Context) query(path string, key tmbytes.HexBytes) ([]byte, int64, erro // queryStore performs a query to a Tendermint node with the provided a store // name and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx Context) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { +func (ctx contextG[C]) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { path := fmt.Sprintf("/store/%s/%s", storeName, endPath) return ctx.query(path, key) } diff --git a/sei-cosmos/server/start.go b/sei-cosmos/server/start.go index bbee42f884..2031ed2613 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -31,6 +31,7 @@ import ( 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" + rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "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" @@ -365,7 +366,7 @@ func startInProcess( if err != nil { return err } - clientCtx = clientCtx.WithClient(localClient) + clientCtx = client.WithClient[rpcclient.Client](clientCtx, localClient) app.RegisterTxService(clientCtx) app.RegisterTendermintService(clientCtx) diff --git a/sei-cosmos/testutil/network/util.go b/sei-cosmos/testutil/network/util.go index 5b86ffabcf..7804589eae 100644 --- a/sei-cosmos/testutil/network/util.go +++ b/sei-cosmos/testutil/network/util.go @@ -6,6 +6,7 @@ import ( "path/filepath" "time" + "github.com/sei-protocol/sei-chain/sei-cosmos/client" "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/codec" tmtime "github.com/sei-protocol/sei-chain/sei-cosmos/std" "github.com/sei-protocol/sei-chain/sei-cosmos/telemetry" @@ -77,8 +78,7 @@ func startInProcess(cfg Config, val *Validator) error { // 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) + val.ClientCtx = client.WithClient(val.ClientCtx, val.RPCClient) // Add the tx service in the gRPC router. app.RegisterTxService(val.ClientCtx) diff --git a/sei-cosmos/x/auth/tx/service.go b/sei-cosmos/x/auth/tx/service.go index 84fa7327a7..53bc85fd6d 100644 --- a/sei-cosmos/x/auth/tx/service.go +++ b/sei-cosmos/x/auth/tx/service.go @@ -240,7 +240,7 @@ 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 s.clientCtx.TxServiceBroadcast(ctx, req) } // RegisterTxService registers the tx service on the gRPC router. 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, From 1141f9f6024f9d911ea8f315596bafb5312a9f11 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 1 May 2026 10:54:34 +0200 Subject: [PATCH 05/33] registrations for local node --- sei-cosmos/server/start.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sei-cosmos/server/start.go b/sei-cosmos/server/start.go index 2031ed2613..9abde80718 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -356,20 +356,19 @@ 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 { + localClient, err := local.New(tmNode.(local.NodeService)) + if err != nil { + return err + } + clientCtx = client.WithClient[rpcclient.Client](clientCtx, localClient) - // 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 + app.RegisterTxService(clientCtx) + app.RegisterTendermintService(clientCtx) } - clientCtx = client.WithClient[rpcclient.Client](clientCtx, localClient) - - app.RegisterTxService(clientCtx) - app.RegisterTendermintService(clientCtx) } var apiSrv *api.Server From c7ce3a0b137993e2c75358b8925555ae69782000 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 1 May 2026 11:07:32 +0200 Subject: [PATCH 06/33] reordered defers in startInProcess --- sei-cosmos/server/api/server.go | 5 ++- sei-cosmos/server/start.go | 60 ++++++++++++--------------------- 2 files changed, 25 insertions(+), 40 deletions(-) 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/start.go b/sei-cosmos/server/start.go index 9abde80718..4fc9426c1e 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -36,7 +36,6 @@ import ( 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 ( @@ -270,7 +269,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 { @@ -281,11 +279,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) @@ -306,6 +304,12 @@ 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 @@ -356,6 +360,11 @@ func startInProcess( if err := tmNode.Start(goCtx); err != nil { return fmt.Errorf("error starting node: %w", 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. @@ -371,10 +380,11 @@ func startInProcess( } } - 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) @@ -392,23 +402,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() } } @@ -461,33 +468,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) } From 956b0e5f8fe2fca6a4026cbd7895a28d6280d006 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 1 May 2026 11:09:29 +0200 Subject: [PATCH 07/33] compilation fix --- loadtest/sign.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/loadtest/sign.go b/loadtest/sign.go index 3d5afd75d5..b4c1726e86 100644 --- a/loadtest/sign.go +++ b/loadtest/sign.go @@ -21,6 +21,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/types/tx/signing" xauthsigning "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/signing" authtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/types" + rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" ) type AccountInfo struct { @@ -226,7 +227,7 @@ func (sc *SignerClient) GetAccountNumberSequenceNumber(privKey cryptotypes.PrivK } context := client.Context{} context = context.WithNodeURI(sc.NodeURI) - context = context.WithClient(cl) + context = client.WithClient[rpcclient.Client](context, cl) context = context.WithInterfaceRegistry(TestConfig.InterfaceRegistry) userHomeDir, _ := os.UserHomeDir() kr, _ := keyring.New(sdk.KeyringServiceName(), "test", filepath.Join(userHomeDir, ".sei"), os.Stdin) From eb2c2c7272e62311133172fc5b7bb5dc3c68f537 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 1 May 2026 11:17:43 +0200 Subject: [PATCH 08/33] removed cast to NodeService --- sei-cosmos/server/start.go | 6 ++---- sei-tendermint/node/node.go | 3 ++- sei-tendermint/node/public.go | 4 ++-- sei-tendermint/node/seed.go | 3 ++- sei-tendermint/rpc/client/local/local.go | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sei-cosmos/server/start.go b/sei-cosmos/server/start.go index 4fc9426c1e..08a22d118d 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -29,7 +29,6 @@ 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" rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" @@ -312,7 +311,6 @@ func startInProcess( }() gRPCOnly := ctx.Viper.GetBool(flagGRPCOnly) - var tmNode service.Service var restartMtx sync.Mutex restartCh := make(chan struct{}) @@ -345,7 +343,7 @@ func startInProcess( gen = genDoc } } - tmNode, err = node.New( + tmNode, err := node.New( goCtx, ctx.Config, restartEvent, @@ -369,7 +367,7 @@ func startInProcess( // 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.(local.NodeService)) + localClient, err := local.New(tmNode) if err != nil { return err } diff --git a/sei-tendermint/node/node.go b/sei-tendermint/node/node.go index 8005962d10..3f933b61da 100644 --- a/sei-tendermint/node/node.go +++ b/sei-tendermint/node/node.go @@ -20,6 +20,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-tendermint/internal/consensus" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/eventbus" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/eventlog" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/evidence" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/mempool" mempoolreactor "github.com/sei-protocol/sei-chain/sei-tendermint/internal/mempool/reactor" @@ -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..d4debadad8 100644 --- a/sei-tendermint/node/public.go +++ b/sei-tendermint/node/public.go @@ -8,7 +8,7 @@ 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/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-tendermint/privval" tmtypes "github.com/sei-protocol/sei-chain/sei-tendermint/types" "github.com/sei-protocol/seilog" @@ -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..abc3c1985a 100644 --- a/sei-tendermint/node/seed.go +++ b/sei-tendermint/node/seed.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/config" atypes "github.com/sei-protocol/sei-chain/sei-tendermint/internal/autobahn/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..3dde998406 100644 --- a/sei-tendermint/rpc/client/local/local.go +++ b/sei-tendermint/rpc/client/local/local.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/sei-protocol/sei-chain/sei-tendermint/libs/service" "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" @@ -47,6 +48,7 @@ type Local struct { // 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 } From b04b46f9e9667945c4ffb6f462f9209a51b153ad Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 1 May 2026 11:20:34 +0200 Subject: [PATCH 09/33] LocalContext + test fix --- sei-cosmos/client/context.go | 2 ++ sei-cosmos/server/start.go | 3 ++- sei-cosmos/server/types/app.go | 2 +- x/tokenfactory/client/cli/tx_test.go | 8 ++------ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index cd2a497d8f..758034413f 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -13,6 +13,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-cosmos/codec" @@ -22,6 +23,7 @@ import ( ) type Context = contextG[rpcclient.Client] +type LocalContext = contextG[*local.Local] type contextBase struct { FromAddress sdk.AccAddress diff --git a/sei-cosmos/server/start.go b/sei-cosmos/server/start.go index 08a22d118d..604a0db86a 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -371,10 +371,11 @@ func startInProcess( if err != nil { return err } + localCtx := client.WithClient(clientCtx, localClient) clientCtx = client.WithClient[rpcclient.Client](clientCtx, localClient) app.RegisterTxService(clientCtx) - app.RegisterTendermintService(clientCtx) + app.RegisterTendermintService(localCtx) } } diff --git a/sei-cosmos/server/types/app.go b/sei-cosmos/server/types/app.go index 58934bfe2b..2171325784 100644 --- a/sei-cosmos/server/types/app.go +++ b/sei-cosmos/server/types/app.go @@ -54,7 +54,7 @@ type ( RegisterTxService(clientCtx client.Context) // RegisterTendermintService registers the gRPC Query service for tendermint queries. - RegisterTendermintService(clientCtx client.Context) + RegisterTendermintService(clientCtx client.LocalContext) // CommitMultiStore Returns the multistore instance CommitMultiStore() sdk.CommitMultiStore 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() From 7bba69847947f61971436de7f80acb923c78c7ba Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 13:42:14 +0200 Subject: [PATCH 10/33] depth 1 --- app/app.go | 2 +- sei-cosmos/client/context.go | 19 +++++++++++++----- sei-cosmos/client/grpc/tmservice/service.go | 6 +++--- sei-cosmos/testutil/network/util.go | 22 +++++++++++---------- sei-ibc-go/testing/simapp/app.go | 2 +- sei-wasmd/app/app.go | 2 +- 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/app/app.go b/app/app.go index d03b0e7437..3e2f040c5f 100644 --- a/app/app.go +++ b/app/app.go @@ -2374,7 +2374,7 @@ func (app *App) RPCContextProvider(i int64) sdk.Context { } // RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *App) RegisterTendermintService(clientCtx client.Context) { +func (app *App) RegisterTendermintService(clientCtx client.LocalContext) { tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) txConfigProvider := func(height int64) client.TxConfig { if app.ChainID != "pacific-1" { diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 758034413f..b048c21413 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -22,7 +22,8 @@ import ( sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" ) -type Context = contextG[rpcclient.Client] +type Client = rpcclient.Client +type Context = contextG[Client] type LocalContext = contextG[*local.Local] type contextBase struct { @@ -62,11 +63,19 @@ type contextBase struct { // Context implements a typical context created in SDK modules for transaction // handling and queries. -type contextG[C rpcclient.Client] struct { +type contextG[C Client] struct { contextBase Client utils.Option[C] } +func (ctx contextG[C]) Any() Context { + ctx2 := Context {contextBase: ctx.contextBase} + if c,ok := ctx.Client.Get(); ok { + ctx2.Client = utils.Some[Client](c) + } + return ctx2 +} + // WithKeyring returns a copy of the context with an updated keyring. func (ctx contextG[C]) WithKeyring(k keyring.Keyring) contextG[C] { ctx.Keyring = k @@ -145,7 +154,7 @@ func (ctx contextG[C]) WithHeight(height int64) contextG[C] { // WithClient returns a copy of the context with an updated RPC client // instance. -func WithClient[C2, C1 rpcclient.Client](ctx contextG[C1], client C2) contextG[C2] { +func WithClient[C2, C1 Client](ctx contextG[C1], client C2) contextG[C2] { return contextG[C2]{ contextBase: ctx.contextBase, Client: utils.Some(client), @@ -297,7 +306,7 @@ func (ctx contextG[C]) 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 contextG[C]) PrintObjectLegacy(toPrint interface{}) error { +func (ctx contextG[C]) PrintObjectLegacy(toPrint any) error { out, err := ctx.LegacyAmino.MarshalAsJSON(toPrint) if err != nil { return err @@ -308,7 +317,7 @@ func (ctx contextG[C]) PrintObjectLegacy(toPrint interface{}) error { func (ctx contextG[C]) 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/service.go b/sei-cosmos/client/grpc/tmservice/service.go index a1d9007a64..2c8b4c132c 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -26,9 +26,9 @@ 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(clientCtx client.LocalContext, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer { return queryServer{ - clientCtx: clientCtx, + clientCtx: clientCtx.Any(), interfaceRegistry: interfaceRegistry, } } @@ -196,7 +196,7 @@ 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, + clientCtx client.LocalContext, interfaceRegistry codectypes.InterfaceRegistry, ) { RegisterServiceServer( diff --git a/sei-cosmos/testutil/network/util.go b/sei-cosmos/testutil/network/util.go index 7804589eae..fc1605f8ec 100644 --- a/sei-cosmos/testutil/network/util.go +++ b/sei-cosmos/testutil/network/util.go @@ -72,21 +72,23 @@ 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 = client.WithClient(val.ClientCtx, val.RPCClient) + // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. + if val.APIAddress != "" || val.AppConfig.GRPC.Enable { + localCtx := client.WithClient(val.ClientCtx, localClient) + val.ClientCtx = client.WithClient(val.ClientCtx, val.RPCClient) - // Add the tx service in the gRPC router. - app.RegisterTxService(val.ClientCtx) + // 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) + // Add the tendermint queries service in the gRPC router. + app.RegisterTendermintService(localCtx) + } } + + if val.APIAddress != "" { apiSrv := api.New(val.ClientCtx) app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) diff --git a/sei-ibc-go/testing/simapp/app.go b/sei-ibc-go/testing/simapp/app.go index 059527e511..2bb7822d78 100644 --- a/sei-ibc-go/testing/simapp/app.go +++ b/sei-ibc-go/testing/simapp/app.go @@ -709,7 +709,7 @@ func (app *SimApp) RegisterTxService(clientCtx client.Context) { } // RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *SimApp) RegisterTendermintService(clientCtx client.Context) { +func (app *SimApp) RegisterTendermintService(clientCtx client.LocalContext) { tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) } diff --git a/sei-wasmd/app/app.go b/sei-wasmd/app/app.go index deabf4cfff..6c43cef748 100644 --- a/sei-wasmd/app/app.go +++ b/sei-wasmd/app/app.go @@ -836,7 +836,7 @@ func (app *WasmApp) RegisterTxService(clientCtx client.Context) { } // RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *WasmApp) RegisterTendermintService(clientCtx client.Context) { +func (app *WasmApp) RegisterTendermintService(clientCtx client.LocalContext) { tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) } From 9df91b7bcfe29818a7d43e79558c75731c27a108 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 13:48:30 +0200 Subject: [PATCH 11/33] fmt --- sei-cosmos/client/context.go | 6 +++--- sei-cosmos/testutil/network/util.go | 4 +--- sei-tendermint/node/node.go | 2 +- sei-tendermint/node/public.go | 2 +- sei-tendermint/node/seed.go | 2 +- sei-tendermint/rpc/client/local/local.go | 2 +- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index b048c21413..9ac84727f2 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -13,8 +13,8 @@ import ( "github.com/gogo/protobuf/proto" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" - "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-cosmos/codec" codectypes "github.com/sei-protocol/sei-chain/sei-cosmos/codec/types" @@ -69,8 +69,8 @@ type contextG[C Client] struct { } func (ctx contextG[C]) Any() Context { - ctx2 := Context {contextBase: ctx.contextBase} - if c,ok := ctx.Client.Get(); ok { + ctx2 := Context{contextBase: ctx.contextBase} + if c, ok := ctx.Client.Get(); ok { ctx2.Client = utils.Some[Client](c) } return ctx2 diff --git a/sei-cosmos/testutil/network/util.go b/sei-cosmos/testutil/network/util.go index fc1605f8ec..33798e5586 100644 --- a/sei-cosmos/testutil/network/util.go +++ b/sei-cosmos/testutil/network/util.go @@ -77,7 +77,7 @@ func startInProcess(cfg Config, val *Validator) error { // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. if val.APIAddress != "" || val.AppConfig.GRPC.Enable { localCtx := client.WithClient(val.ClientCtx, localClient) - val.ClientCtx = client.WithClient(val.ClientCtx, val.RPCClient) + val.ClientCtx = client.WithClient(val.ClientCtx, val.RPCClient) // Add the tx service in the gRPC router. app.RegisterTxService(val.ClientCtx) @@ -87,8 +87,6 @@ func startInProcess(cfg Config, val *Validator) error { } } - - if val.APIAddress != "" { apiSrv := api.New(val.ClientCtx) app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) diff --git a/sei-tendermint/node/node.go b/sei-tendermint/node/node.go index 3f933b61da..ca574118d5 100644 --- a/sei-tendermint/node/node.go +++ b/sei-tendermint/node/node.go @@ -20,7 +20,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-tendermint/internal/consensus" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/eventbus" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/eventlog" - "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/evidence" "github.com/sei-protocol/sei-chain/sei-tendermint/internal/mempool" mempoolreactor "github.com/sei-protocol/sei-chain/sei-tendermint/internal/mempool/reactor" @@ -37,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" diff --git a/sei-tendermint/node/public.go b/sei-tendermint/node/public.go index d4debadad8..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/rpc/client/local" "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" diff --git a/sei-tendermint/node/seed.go b/sei-tendermint/node/seed.go index abc3c1985a..7f7a077cec 100644 --- a/sei-tendermint/node/seed.go +++ b/sei-tendermint/node/seed.go @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/local" abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" "github.com/sei-protocol/sei-chain/sei-tendermint/config" atypes "github.com/sei-protocol/sei-chain/sei-tendermint/internal/autobahn/types" @@ -23,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" ) diff --git a/sei-tendermint/rpc/client/local/local.go b/sei-tendermint/rpc/client/local/local.go index 3dde998406..67bb8494ad 100644 --- a/sei-tendermint/rpc/client/local/local.go +++ b/sei-tendermint/rpc/client/local/local.go @@ -6,12 +6,12 @@ import ( "fmt" "time" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/service" "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" From 46faae0200c45841ed27de91d580bf6a07e9b02e Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 14:18:03 +0200 Subject: [PATCH 12/33] test fix --- sei-cosmos/client/context.go | 6 ++++++ sei-cosmos/server/rollback_test.go | 2 +- sei-cosmos/x/gov/client/utils/query_test.go | 5 ++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 9ac84727f2..941c9606a6 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -152,6 +152,12 @@ func (ctx contextG[C]) WithHeight(height int64) contextG[C] { return ctx } +// WithClient returns a copy of the context with an updated RPC client +// instance. +func (ctx contextG[C]) WithClient(client Client) Context { + return WithClient(ctx, client).Any() +} + // WithClient returns a copy of the context with an updated RPC client // instance. func WithClient[C2, C1 Client](ctx contextG[C1], client C2) contextG[C2] { diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index 4389119802..15dad33ba4 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -109,7 +109,7 @@ func (m *mockApplication) FinalizeBlock(ctx context.Context, req *abci.RequestFi 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) RegisterTendermintService(client.LocalContext) {} func (m *mockApplication) InplaceTestnetInitialize(cryptotypes.PubKey) {} // setupTestApp creates a test application with a CommitMultiStore at a specific height diff --git a/sei-cosmos/x/gov/client/utils/query_test.go b/sei-cosmos/x/gov/client/utils/query_test.go index 0550c93d28..5afc6c57c6 100644 --- a/sei-cosmos/x/gov/client/utils/query_test.go +++ b/sei-cosmos/x/gov/client/utils/query_test.go @@ -164,10 +164,9 @@ func TestGetPaginatedVotes(t *testing.T) { t.Run(tc.description, func(t *testing.T) { var marshalled = make([]tmtypes.Tx, len(tc.msgs)) cli := TxSearchMock{txs: marshalled, txConfig: encCfg.TxConfig} - clientCtx := client.Context{}. + clientCtx := client.WithClient(client.Context{}. WithLegacyAmino(encCfg.Amino). - WithClient(cli). - WithTxConfig(encCfg.TxConfig) + WithTxConfig(encCfg.TxConfig), cli).Any() for i := range tc.msgs { txBuilder := clientCtx.TxConfig.NewTxBuilder() From ad576ab4c31d5efd0e6ff3691b8433d98aea5e27 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 14:52:42 +0200 Subject: [PATCH 13/33] tx service is local --- app/app.go | 10 +-- sei-cosmos/client/broadcast.go | 10 +-- sei-cosmos/client/context.go | 82 ++++++++++----------- sei-cosmos/client/grpc/tmservice/block.go | 4 +- sei-cosmos/client/grpc/tmservice/service.go | 17 ++--- sei-cosmos/client/grpc/tmservice/status.go | 2 +- sei-cosmos/client/grpc_query.go | 4 +- sei-cosmos/client/query.go | 22 +++--- sei-cosmos/server/start.go | 8 +- sei-cosmos/server/types/app.go | 8 +- sei-cosmos/testutil/network/util.go | 10 +-- sei-cosmos/x/auth/tx/query.go | 6 +- sei-cosmos/x/auth/tx/service.go | 11 +-- sei-ibc-go/testing/simapp/app.go | 8 +- sei-wasmd/app/app.go | 8 +- 15 files changed, 89 insertions(+), 121 deletions(-) diff --git a/app/app.go b/app/app.go index 3e2f040c5f..3b860ce769 100644 --- a/app/app.go +++ b/app/app.go @@ -2352,11 +2352,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 { return app.GetCheckCtx().WithIsEVM(true).WithTraceMode(true).WithIsCheckTx(false).WithClosestUpgradeName(LatestUpgrade) @@ -2373,8 +2368,9 @@ 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.LocalContext) { +// RegisterTendermintService implements the Application.RegisterLocalServices method. +func (app *App) RegisterLocalServices(clientCtx client.LocalContext) { + authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) txConfigProvider := func(height int64) client.TxConfig { if app.ChainID != "pacific-1" { diff --git a/sei-cosmos/client/broadcast.go b/sei-cosmos/client/broadcast.go index 199e9b94e6..f5d3121066 100644 --- a/sei-cosmos/client/broadcast.go +++ b/sei-cosmos/client/broadcast.go @@ -48,7 +48,7 @@ func (e ErrMempoolIsFull) Error() string { // based on the context parameters. The result of the broadcast is parsed into // an intermediate structure which is logged if the context has a logger // defined. -func (ctx contextG[C]) BroadcastTx(txBytes []byte) (res *sdk.TxResponse, err error) { +func (ctx ContextG[C]) BroadcastTx(txBytes []byte) (res *sdk.TxResponse, err error) { switch ctx.BroadcastMode { case flags.BroadcastSync: res, err = ctx.BroadcastTxSync(txBytes) @@ -116,7 +116,7 @@ func CheckTendermintError(err error, tx tmtypes.Tx) *sdk.TxResponse { // 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 contextG[C]) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error) { +func (ctx ContextG[C]) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { return nil, err @@ -135,7 +135,7 @@ func (ctx contextG[C]) BroadcastTxCommit(txBytes []byte) (*sdk.TxResponse, error // BroadcastTxSync broadcasts transaction bytes to a Tendermint node // synchronously (i.e. returns after CheckTx execution). -func (ctx contextG[C]) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { +func (ctx ContextG[C]) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { return nil, err @@ -151,7 +151,7 @@ func (ctx contextG[C]) BroadcastTxSync(txBytes []byte) (*sdk.TxResponse, error) // BroadcastTxAsync broadcasts transaction bytes to a Tendermint node // asynchronously (i.e. returns immediately). -func (ctx contextG[C]) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { +func (ctx ContextG[C]) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { return nil, err @@ -167,7 +167,7 @@ func (ctx contextG[C]) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) // 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 (clientCtx contextG[C]) TxServiceBroadcast(grpcCtx context.Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { +func (clientCtx ContextG[C]) TxServiceBroadcast(grpcCtx context.Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { if req == nil || req.TxBytes == nil { return nil, status.Error(codes.InvalidArgument, "invalid empty tx") } diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 941c9606a6..b05a98feac 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -23,8 +23,8 @@ import ( ) type Client = rpcclient.Client -type Context = contextG[Client] -type LocalContext = contextG[*local.Local] +type Context = ContextG[Client] +type LocalContext = ContextG[*local.Local] type contextBase struct { FromAddress sdk.AccAddress @@ -63,12 +63,12 @@ type contextBase struct { // Context implements a typical context created in SDK modules for transaction // handling and queries. -type contextG[C Client] struct { +type ContextG[C Client] struct { contextBase Client utils.Option[C] } -func (ctx contextG[C]) Any() Context { +func (ctx ContextG[C]) Any() Context { ctx2 := Context{contextBase: ctx.contextBase} if c, ok := ctx.Client.Get(); ok { ctx2.Client = utils.Some[Client](c) @@ -77,19 +77,19 @@ func (ctx contextG[C]) Any() Context { } // WithKeyring returns a copy of the context with an updated keyring. -func (ctx contextG[C]) WithKeyring(k keyring.Keyring) contextG[C] { +func (ctx ContextG[C]) WithKeyring(k keyring.Keyring) ContextG[C] { ctx.Keyring = k return ctx } // WithKeyringOptions returns a copy of the context with an updated keyring. -func (ctx contextG[C]) WithKeyringOptions(opts ...keyring.Option) contextG[C] { +func (ctx ContextG[C]) WithKeyringOptions(opts ...keyring.Option) ContextG[C] { ctx.KeyringOptions = opts return ctx } // WithInput returns a copy of the context with an updated input. -func (ctx contextG[C]) WithInput(r io.Reader) contextG[C] { +func (ctx ContextG[C]) WithInput(r io.Reader) ContextG[C] { // convert to a bufio.Reader to have a shared buffer between the keyring and the // the Commands, ensuring a read from one advance the read pointer for the other. // see https://github.com/cosmos/cosmos-sdk/issues/9566. @@ -98,7 +98,7 @@ func (ctx contextG[C]) WithInput(r io.Reader) contextG[C] { } // Deprecated: WithJSONCodec returns a copy of the Context with an updated JSONCodec. -func (ctx contextG[C]) WithJSONCodec(m codec.JSONCodec) contextG[C] { +func (ctx ContextG[C]) WithJSONCodec(m codec.JSONCodec) ContextG[C] { ctx.JSONCodec = m // since we are using ctx.Codec everywhere in the SDK, for backward compatibility // we need to try to set it here as well. @@ -109,7 +109,7 @@ func (ctx contextG[C]) WithJSONCodec(m codec.JSONCodec) contextG[C] { } // WithCodec returns a copy of the Context with an updated Codec. -func (ctx contextG[C]) WithCodec(m codec.Codec) contextG[C] { +func (ctx ContextG[C]) WithCodec(m codec.Codec) ContextG[C] { ctx.JSONCodec = m ctx.Codec = m return ctx @@ -117,70 +117,70 @@ func (ctx contextG[C]) WithCodec(m codec.Codec) contextG[C] { // WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec. // TODO: Deprecated (remove). -func (ctx contextG[C]) WithLegacyAmino(cdc *codec.LegacyAmino) contextG[C] { +func (ctx ContextG[C]) WithLegacyAmino(cdc *codec.LegacyAmino) ContextG[C] { ctx.LegacyAmino = cdc return ctx } // WithOutput returns a copy of the context with an updated output writer (e.g. stdout). -func (ctx contextG[C]) WithOutput(w io.Writer) contextG[C] { +func (ctx ContextG[C]) WithOutput(w io.Writer) ContextG[C] { ctx.Output = w return ctx } // WithFrom returns a copy of the context with an updated from address or name. -func (ctx contextG[C]) WithFrom(from string) contextG[C] { +func (ctx ContextG[C]) WithFrom(from string) ContextG[C] { ctx.From = from return ctx } // WithOutputFormat returns a copy of the context with an updated OutputFormat field. -func (ctx contextG[C]) WithOutputFormat(format string) contextG[C] { +func (ctx ContextG[C]) WithOutputFormat(format string) ContextG[C] { ctx.OutputFormat = format return ctx } // WithNodeURI returns a copy of the context with an updated node URI. -func (ctx contextG[C]) WithNodeURI(nodeURI string) contextG[C] { +func (ctx ContextG[C]) WithNodeURI(nodeURI string) ContextG[C] { ctx.NodeURI = nodeURI return ctx } // WithHeight returns a copy of the context with an updated height. -func (ctx contextG[C]) WithHeight(height int64) contextG[C] { +func (ctx ContextG[C]) WithHeight(height int64) ContextG[C] { ctx.Height = height return ctx } // WithClient returns a copy of the context with an updated RPC client // instance. -func (ctx contextG[C]) WithClient(client Client) Context { - return WithClient(ctx, client).Any() +func (ctx ContextG[C]) WithClient(client C) ContextG[C] { + return WithClient(ctx, client) } // WithClient returns a copy of the context with an updated RPC client // instance. -func WithClient[C2, C1 Client](ctx contextG[C1], client C2) contextG[C2] { - return contextG[C2]{ +func WithClient[C2, C1 Client](ctx ContextG[C1], client C2) ContextG[C2] { + return ContextG[C2]{ contextBase: ctx.contextBase, Client: utils.Some(client), } } // WithUseLedger returns a copy of the context with an updated UseLedger flag. -func (ctx contextG[C]) WithUseLedger(useLedger bool) contextG[C] { +func (ctx ContextG[C]) WithUseLedger(useLedger bool) ContextG[C] { ctx.UseLedger = useLedger return ctx } // WithChainID returns a copy of the context with an updated chain ID. -func (ctx contextG[C]) WithChainID(chainID string) contextG[C] { +func (ctx ContextG[C]) WithChainID(chainID string) ContextG[C] { ctx.ChainID = chainID return ctx } // WithHomeDir returns a copy of the Context with HomeDir set. -func (ctx contextG[C]) WithHomeDir(dir string) contextG[C] { +func (ctx ContextG[C]) WithHomeDir(dir string) ContextG[C] { if dir != "" { ctx.HomeDir = dir } @@ -188,91 +188,91 @@ func (ctx contextG[C]) WithHomeDir(dir string) contextG[C] { } // WithKeyringDir returns a copy of the Context with KeyringDir set. -func (ctx contextG[C]) WithKeyringDir(dir string) contextG[C] { +func (ctx ContextG[C]) WithKeyringDir(dir string) ContextG[C] { ctx.KeyringDir = dir return ctx } // WithGenerateOnly returns a copy of the context with updated GenerateOnly value -func (ctx contextG[C]) WithGenerateOnly(generateOnly bool) contextG[C] { +func (ctx ContextG[C]) WithGenerateOnly(generateOnly bool) ContextG[C] { ctx.GenerateOnly = generateOnly return ctx } // WithSimulation returns a copy of the context with updated Simulate value -func (ctx contextG[C]) WithSimulation(simulate bool) contextG[C] { +func (ctx ContextG[C]) WithSimulation(simulate bool) ContextG[C] { ctx.Simulate = simulate return ctx } // WithOffline returns a copy of the context with updated Offline value. -func (ctx contextG[C]) WithOffline(offline bool) contextG[C] { +func (ctx ContextG[C]) WithOffline(offline bool) ContextG[C] { ctx.Offline = offline return ctx } // WithFromName returns a copy of the context with an updated from account name. -func (ctx contextG[C]) WithFromName(name string) contextG[C] { +func (ctx ContextG[C]) WithFromName(name string) ContextG[C] { ctx.FromName = name return ctx } // WithFromAddress returns a copy of the context with an updated from account // address. -func (ctx contextG[C]) WithFromAddress(addr sdk.AccAddress) contextG[C] { +func (ctx ContextG[C]) WithFromAddress(addr sdk.AccAddress) ContextG[C] { ctx.FromAddress = addr return ctx } // WithFeeGranterAddress returns a copy of the context with an updated fee granter account // address. -func (ctx contextG[C]) WithFeeGranterAddress(addr sdk.AccAddress) contextG[C] { +func (ctx ContextG[C]) WithFeeGranterAddress(addr sdk.AccAddress) ContextG[C] { ctx.FeeGranter = addr return ctx } // WithBroadcastMode returns a copy of the context with an updated broadcast // mode. -func (ctx contextG[C]) WithBroadcastMode(mode string) contextG[C] { +func (ctx ContextG[C]) WithBroadcastMode(mode string) ContextG[C] { ctx.BroadcastMode = mode return ctx } // WithSignModeStr returns a copy of the context with an updated SignMode // value. -func (ctx contextG[C]) WithSignModeStr(signModeStr string) contextG[C] { +func (ctx ContextG[C]) WithSignModeStr(signModeStr string) ContextG[C] { ctx.SignModeStr = signModeStr return ctx } // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm // value. -func (ctx contextG[C]) WithSkipConfirmation(skip bool) contextG[C] { +func (ctx ContextG[C]) WithSkipConfirmation(skip bool) ContextG[C] { ctx.SkipConfirm = skip return ctx } // WithTxConfig returns the context with an updated TxConfig -func (ctx contextG[C]) WithTxConfig(generator TxConfig) contextG[C] { +func (ctx ContextG[C]) WithTxConfig(generator TxConfig) ContextG[C] { ctx.TxConfig = generator return ctx } // WithAccountRetriever returns the context with an updated AccountRetriever -func (ctx contextG[C]) WithAccountRetriever(retriever AccountRetriever) contextG[C] { +func (ctx ContextG[C]) WithAccountRetriever(retriever AccountRetriever) ContextG[C] { ctx.AccountRetriever = retriever return ctx } // WithInterfaceRegistry returns the context with an updated InterfaceRegistry -func (ctx contextG[C]) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) contextG[C] { +func (ctx ContextG[C]) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) ContextG[C] { ctx.InterfaceRegistry = interfaceRegistry return ctx } // WithViper returns the context with Viper field. This Viper instance is used to read // client-side config from the config file. -func (ctx contextG[C]) WithViper(prefix string) contextG[C] { +func (ctx ContextG[C]) WithViper(prefix string) ContextG[C] { v := viper.New() v.SetEnvPrefix(prefix) v.AutomaticEnv() @@ -281,13 +281,13 @@ func (ctx contextG[C]) WithViper(prefix string) contextG[C] { } // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout -func (ctx contextG[C]) PrintString(str string) error { +func (ctx ContextG[C]) PrintString(str string) error { return ctx.PrintBytes([]byte(str)) } // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout. // NOTE: for printing a complex state object, you should use ctx.PrintOutput -func (ctx contextG[C]) PrintBytes(o []byte) error { +func (ctx ContextG[C]) PrintBytes(o []byte) error { writer := ctx.Output if writer == nil { writer = os.Stdout @@ -300,7 +300,7 @@ func (ctx contextG[C]) PrintBytes(o []byte) error { // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint // will be JSON encoded using ctx.Codec. An error is returned upon failure. -func (ctx contextG[C]) PrintProto(toPrint proto.Message) error { +func (ctx ContextG[C]) PrintProto(toPrint proto.Message) error { // always serialize JSON initially because proto json can't be directly YAML encoded out, err := ctx.Codec.MarshalAsJSON(toPrint) if err != nil { @@ -312,7 +312,7 @@ func (ctx contextG[C]) 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 contextG[C]) PrintObjectLegacy(toPrint any) error { +func (ctx ContextG[C]) PrintObjectLegacy(toPrint any) error { out, err := ctx.LegacyAmino.MarshalAsJSON(toPrint) if err != nil { return err @@ -320,7 +320,7 @@ func (ctx contextG[C]) PrintObjectLegacy(toPrint any) error { return ctx.printOutput(out) } -func (ctx contextG[C]) printOutput(out []byte) error { +func (ctx ContextG[C]) printOutput(out []byte) error { if ctx.OutputFormat == "text" { // handle text format by decoding and re-encoding JSON as YAML var j any diff --git a/sei-cosmos/client/grpc/tmservice/block.go b/sei-cosmos/client/grpc/tmservice/block.go index e24b98f390..a67bfb24e3 100644 --- a/sei-cosmos/client/grpc/tmservice/block.go +++ b/sei-cosmos/client/grpc/tmservice/block.go @@ -9,7 +9,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" ) -func getBlock(ctx context.Context, clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) { +func getBlock(ctx context.Context, clientCtx client.LocalContext, height *int64) (*ctypes.ResultBlock, error) { // get the node node, err := clientCtx.GetNode() if err != nil { @@ -19,7 +19,7 @@ func getBlock(ctx context.Context, clientCtx client.Context, height *int64) (*ct return node.Block(ctx, height) } -func GetProtoBlock(ctx context.Context, clientCtx client.Context, height *int64) (tmproto.BlockID, *tmproto.Block, error) { +func GetProtoBlock(ctx context.Context, clientCtx client.LocalContext, height *int64) (tmproto.BlockID, *tmproto.Block, error) { block, err := getBlock(ctx, clientCtx, height) if err != nil { return tmproto.BlockID{}, nil, err diff --git a/sei-cosmos/client/grpc/tmservice/service.go b/sei-cosmos/client/grpc/tmservice/service.go index 2c8b4c132c..004e416f5d 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -18,7 +18,7 @@ import ( // This is the struct that we will implement all the handlers on. type queryServer struct { - clientCtx client.Context + clientCtx client.LocalContext interfaceRegistry codectypes.InterfaceRegistry } @@ -28,7 +28,7 @@ var _ codectypes.UnpackInterfacesMessage = &GetLatestValidatorSetResponse{} // NewQueryServer creates a new tendermint query server. func NewQueryServer(clientCtx client.LocalContext, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer { return queryServer{ - clientCtx: clientCtx.Any(), + clientCtx: clientCtx, interfaceRegistry: interfaceRegistry, } } @@ -65,7 +65,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(s.clientCtx.Any()) if err != nil { return nil, err } @@ -90,7 +90,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.clientCtx.Any(), nil, page, limit) } func (m *GetLatestValidatorSetResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { @@ -111,14 +111,14 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida return nil, err } - chainHeight, err := rpc.GetChainHeight(s.clientCtx) + chainHeight, err := rpc.GetChainHeight(s.clientCtx.Any()) 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.clientCtx.Any(), &req.Height, page, limit) if err != nil { return nil, err } @@ -199,10 +199,7 @@ func RegisterTendermintService( clientCtx client.LocalContext, interfaceRegistry codectypes.InterfaceRegistry, ) { - RegisterServiceServer( - qrt, - NewQueryServer(clientCtx, interfaceRegistry), - ) + RegisterServiceServer(qrt, NewQueryServer(clientCtx, 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 index dd4fb0172e..2556379ace 100644 --- a/sei-cosmos/client/grpc/tmservice/status.go +++ b/sei-cosmos/client/grpc/tmservice/status.go @@ -8,7 +8,7 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client" ) -func getNodeStatus(ctx context.Context, clientCtx client.Context) (*ctypes.ResultStatus, error) { +func getNodeStatus(ctx context.Context, clientCtx client.LocalContext) (*ctypes.ResultStatus, error) { node, err := clientCtx.GetNode() if err != nil { return &ctypes.ResultStatus{}, err diff --git a/sei-cosmos/client/grpc_query.go b/sei-cosmos/client/grpc_query.go index 8cbd7f38c1..136711ce65 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 contextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) { +func (ctx ContextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err 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. @@ -111,6 +111,6 @@ func (ctx contextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, rep } // NewStream implements the grpc ClientConn.NewStream method -func (contextG[C]) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { +func (ContextG[C]) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { return nil, fmt.Errorf("streaming rpc not supported") } diff --git a/sei-cosmos/client/query.go b/sei-cosmos/client/query.go index 504e43c730..6f74b293eb 100644 --- a/sei-cosmos/client/query.go +++ b/sei-cosmos/client/query.go @@ -20,7 +20,7 @@ import ( // GetNode returns an RPC client. If the context's client is not defined, an // error is returned. -func (ctx contextG[C]) GetNode() (rpcclient.Client, error) { +func (ctx ContextG[C]) GetNode() (rpcclient.Client, error) { if c, ok := ctx.Client.Get(); ok { return c, nil } @@ -30,21 +30,21 @@ func (ctx contextG[C]) GetNode() (rpcclient.Client, error) { // Query performs a query to a Tendermint node with the provided path. // It returns the result and height of the query upon success or an error if // the query fails. -func (ctx contextG[C]) Query(path string) ([]byte, int64, error) { +func (ctx ContextG[C]) Query(path string) ([]byte, int64, error) { return ctx.query(path, nil) } // QueryWithData performs a query to a Tendermint node with the provided path // and a data payload. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx contextG[C]) QueryWithData(path string, data []byte) ([]byte, int64, error) { +func (ctx ContextG[C]) QueryWithData(path string, data []byte) ([]byte, int64, error) { return ctx.query(path, data) } // QueryStore performs a query to a Tendermint node with the provided key and // store name. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx contextG[C]) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { +func (ctx ContextG[C]) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { return ctx.queryStore(key, storeName, "key") } @@ -52,26 +52,26 @@ func (ctx contextG[C]) QueryStore(key tmbytes.HexBytes, storeName string) ([]byt // It returns the ResultQuery obtained from the query. The height used to perform // the query is the RequestQuery Height if it is non-zero, otherwise the context // height is used. -func (ctx contextG[C]) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { +func (ctx ContextG[C]) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { return ctx.queryABCI(req) } // GetFromAddress returns the from address from the context's name. -func (ctx contextG[C]) GetFromAddress() sdk.AccAddress { +func (ctx ContextG[C]) GetFromAddress() sdk.AccAddress { return ctx.FromAddress } // GetFeeGranterAddress returns the fee granter address from the context -func (ctx contextG[C]) GetFeeGranterAddress() sdk.AccAddress { +func (ctx ContextG[C]) GetFeeGranterAddress() sdk.AccAddress { return ctx.FeeGranter } // GetFromName returns the key name for the current context. -func (ctx contextG[C]) GetFromName() string { +func (ctx ContextG[C]) GetFromName() string { return ctx.FromName } -func (ctx contextG[C]) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { +func (ctx ContextG[C]) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { node, err := ctx.GetNode() if err != nil { return abci.ResponseQuery{}, err @@ -123,7 +123,7 @@ func sdkErrorToGRPCError(resp abci.ResponseQuery) error { // query performs a query to a Tendermint node with the provided store name // and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx contextG[C]) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { +func (ctx ContextG[C]) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { resp, err := ctx.queryABCI(abci.RequestQuery{ Path: path, Data: key, @@ -139,7 +139,7 @@ func (ctx contextG[C]) query(path string, key tmbytes.HexBytes) ([]byte, int64, // queryStore performs a query to a Tendermint node with the provided a store // name and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx contextG[C]) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { +func (ctx ContextG[C]) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { path := fmt.Sprintf("/store/%s/%s", storeName, endPath) return ctx.query(path, key) } diff --git a/sei-cosmos/server/start.go b/sei-cosmos/server/start.go index 604a0db86a..129a628fad 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -30,7 +30,6 @@ import ( "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/node" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "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" @@ -371,11 +370,8 @@ func startInProcess( if err != nil { return err } - localCtx := client.WithClient(clientCtx, localClient) - clientCtx = client.WithClient[rpcclient.Client](clientCtx, localClient) - - app.RegisterTxService(clientCtx) - app.RegisterTendermintService(localCtx) + clientCtx = clientCtx.WithClient(localClient) + app.RegisterLocalServices(client.WithClient(clientCtx, localClient)) } } diff --git a/sei-cosmos/server/types/app.go b/sei-cosmos/server/types/app.go index 2171325784..a95c1ffbe8 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.LocalContext) + // RegisterTxService registers RPCs of the local tendermint node. + RegisterLocalServices(clientCtx client.LocalContext) // 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 33798e5586..db9fe9855e 100644 --- a/sei-cosmos/testutil/network/util.go +++ b/sei-cosmos/testutil/network/util.go @@ -76,14 +76,8 @@ func startInProcess(cfg Config, val *Validator) error { 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 { - localCtx := client.WithClient(val.ClientCtx, localClient) - val.ClientCtx = client.WithClient(val.ClientCtx, 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(localCtx) + val.ClientCtx = val.ClientCtx.WithClient(val.RPCClient) + app.RegisterLocalServices(client.WithClient(val.ClientCtx, localClient)) } } diff --git a/sei-cosmos/x/auth/tx/query.go b/sei-cosmos/x/auth/tx/query.go index 9076df6bee..a9a4339f61 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[C client.Client](clientCtx client.ContextG[C], 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") } @@ -72,7 +72,7 @@ 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[C client.Client](clientCtx client.ContextG[C], hashHexStr string) (*sdk.TxResponse, error) { hash, err := hex.DecodeString(hashHexStr) if err != nil { return nil, err @@ -117,7 +117,7 @@ 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) { +func getBlocksForTxResults[C client.Client](clientCtx client.ContextG[C], resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { node, err := clientCtx.GetNode() 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 53bc85fd6d..50fac165ac 100644 --- a/sei-cosmos/x/auth/tx/service.go +++ b/sei-cosmos/x/auth/tx/service.go @@ -26,13 +26,13 @@ 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 + clientCtx client.LocalContext 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(clientCtx client.LocalContext, simulate baseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry) txtypes.ServiceServer { return txServer{ clientCtx: clientCtx, simulate: simulate, @@ -246,14 +246,11 @@ func (s txServer) BroadcastTx(ctx context.Context, req *txtypes.BroadcastTxReque // RegisterTxService registers the tx service on the gRPC router. func RegisterTxService( qrt gogogrpc.Server, - clientCtx client.Context, + clientCtx client.LocalContext, simulateFn baseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry, ) { - txtypes.RegisterServiceServer( - qrt, - NewTxServer(clientCtx, simulateFn, interfaceRegistry), - ) + txtypes.RegisterServiceServer(qrt, NewTxServer(clientCtx, simulateFn, interfaceRegistry)) } // RegisterGRPCGatewayRoutes mounts the tx service's GRPC-gateway routes on the diff --git a/sei-ibc-go/testing/simapp/app.go b/sei-ibc-go/testing/simapp/app.go index 2bb7822d78..1c70562a41 100644 --- a/sei-ibc-go/testing/simapp/app.go +++ b/sei-ibc-go/testing/simapp/app.go @@ -703,13 +703,9 @@ func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICon } } -// RegisterTxService implements the Application.RegisterTxService method. -func (app *SimApp) RegisterTxService(clientCtx client.Context) { +// RegisterTxService implements the Application.RegisterLocalServices method. +func (app *SimApp) RegisterLocalServices(clientCtx client.LocalContext) { authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) -} - -// RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *SimApp) RegisterTendermintService(clientCtx client.LocalContext) { tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) } diff --git a/sei-wasmd/app/app.go b/sei-wasmd/app/app.go index 6c43cef748..d08ab61e63 100644 --- a/sei-wasmd/app/app.go +++ b/sei-wasmd/app/app.go @@ -830,13 +830,9 @@ func (app *WasmApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICo } } -// RegisterTxService implements the Application.RegisterTxService method. -func (app *WasmApp) RegisterTxService(clientCtx client.Context) { +// RegisterTxService implements the Application.RegisterLocalServices method. +func (app *WasmApp) RegisterLocalServices(clientCtx client.LocalContext) { authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) -} - -// RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *WasmApp) RegisterTendermintService(clientCtx client.LocalContext) { tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) } From 149f9c8b75789163ca053df0ad0b5bb8650f6412 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 14:56:46 +0200 Subject: [PATCH 14/33] test fix --- sei-cosmos/server/rollback_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index 15dad33ba4..6c86d380e0 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -108,8 +108,7 @@ func (m *mockApplication) FinalizeBlock(ctx context.Context, req *abci.RequestFi 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.LocalContext) {} +func (m *mockApplication) RegisterLocalServices(client.LocalContext) {} func (m *mockApplication) InplaceTestnetInitialize(cryptotypes.PubKey) {} // setupTestApp creates a test application with a CommitMultiStore at a specific height From 3ec92c075cfd0ec2321780ef00d26601935eb2a9 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 15:14:36 +0200 Subject: [PATCH 15/33] simplified stuff --- sei-cosmos/client/grpc/tmservice/service.go | 25 ++++++++++++----- sei-cosmos/client/grpc/tmservice/status.go | 17 ------------ sei-cosmos/client/rpc/status.go | 30 ++++++++++----------- sei-cosmos/client/rpc/validators.go | 30 ++++++++++++--------- 4 files changed, 50 insertions(+), 52 deletions(-) delete mode 100644 sei-cosmos/client/grpc/tmservice/status.go diff --git a/sei-cosmos/client/grpc/tmservice/service.go b/sei-cosmos/client/grpc/tmservice/service.go index 004e416f5d..944088e922 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -35,7 +35,11 @@ func NewQueryServer(clientCtx client.LocalContext, interfaceRegistry codectypes. // GetSyncing implements ServiceServer.GetSyncing func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) { - status, err := getNodeStatus(ctx, s.clientCtx) + node, err := s.clientCtx.GetNode() + if err != nil { + return nil, err + } + status, err := node.Status(ctx) if err != nil { return nil, err } @@ -90,7 +94,7 @@ func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestVa if err != nil { return nil, err } - return validatorsOutput(ctx, s.clientCtx.Any(), nil, page, limit) + return validatorsOutput(ctx, s.clientCtx, nil, page, limit) } func (m *GetLatestValidatorSetResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { @@ -118,7 +122,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida 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.Any(), &req.Height, page, limit) + r, err := validatorsOutput(ctx, s.clientCtx, &req.Height, page, limit) if err != nil { return nil, err } @@ -129,8 +133,12 @@ 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, cctx client.LocalContext, height *int64, page, limit int) (*GetLatestValidatorSetResponse, error) { + node, err := cctx.GetNode() + if err != nil { + return nil, err + } + vs, err := rpc.GetValidators(ctx, node, height, &page, &limit) if err != nil { return nil, err } @@ -158,8 +166,11 @@ 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) - + node, err := s.clientCtx.GetNode() + if err != nil { + return nil, err + } + status, err := node.Status(ctx) if err != nil { return nil, err } diff --git a/sei-cosmos/client/grpc/tmservice/status.go b/sei-cosmos/client/grpc/tmservice/status.go deleted file mode 100644 index 2556379ace..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.LocalContext) (*ctypes.ResultStatus, error) { - node, err := clientCtx.GetNode() - if err != nil { - return &ctypes.ResultStatus{}, err - } - return node.Status(ctx) -} 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..b700006a3f 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 @@ -176,8 +174,11 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { rest.WriteErrorResponse(w, http.StatusNotFound, "requested block height is bigger then the chain length") return } - - output, err := GetValidators(r.Context(), clientCtx, &height, &page, &limit) + node, err := clientCtx.GetNode() + if rest.CheckInternalServerError(w, err) { + return + } + 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 } From a92bad22e6f703a898613d5bd2462be9f1ab5f1e Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 15:29:10 +0200 Subject: [PATCH 16/33] simplified block access --- sei-cosmos/baseapp/grpcrouter.go | 5 +-- sei-cosmos/client/grpc/tmservice/block.go | 34 --------------------- sei-cosmos/client/grpc/tmservice/service.go | 29 +++++++++++++++--- sei-cosmos/x/auth/tx/service.go | 7 ++++- 4 files changed, 32 insertions(+), 43 deletions(-) delete mode 100644 sei-cosmos/client/grpc/tmservice/block.go 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/grpc/tmservice/block.go b/sei-cosmos/client/grpc/tmservice/block.go deleted file mode 100644 index a67bfb24e3..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.LocalContext, 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.LocalContext, 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 944088e922..ba66b52253 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -14,8 +14,23 @@ 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.LocalContext @@ -50,13 +65,14 @@ 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) + node, err := s.clientCtx.GetNode() if err != nil { return nil, err } + block, err := node.Block(ctx, nil) - protoBlockID := status.BlockID.ToProto() - protoBlock, err := status.Block.ToProto() + protoBlockID := block.BlockID.ToProto() + protoBlock, err := block.Block.ToProto() if err != nil { return nil, err } @@ -78,7 +94,12 @@ 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) + node, err := s.clientCtx.GetNode() + if err != nil { + return nil, err + } + + protoBlockID, protoBlock, err := GetProtoBlock(ctx, node, &req.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 50fac165ac..2796ffb6d2 100644 --- a/sei-cosmos/x/auth/tx/service.go +++ b/sei-cosmos/x/auth/tx/service.go @@ -181,7 +181,12 @@ 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) + node, err := s.clientCtx.GetNode() + if err != nil { + return nil, err + } + + blockId, block, err := tmservice.GetProtoBlock(ctx, node, &req.Height) if err != nil { return nil, err } From 774423227ff53bfda6c42d1a46685e81d9939281 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 16:02:46 +0200 Subject: [PATCH 17/33] WIP --- app/app.go | 6 ++- sei-cosmos/client/context.go | 3 +- sei-cosmos/client/grpc/tmservice/service.go | 51 ++++++--------------- sei-cosmos/client/query.go | 5 +- sei-cosmos/client/rpc/block.go | 19 ++++---- sei-cosmos/client/rpc/validators.go | 12 ++--- sei-cosmos/x/auth/client/cli/query.go | 8 ++-- sei-cosmos/x/auth/tx/query.go | 22 ++++----- sei-cosmos/x/gov/client/cli/query.go | 2 +- sei-cosmos/x/gov/client/rest/query.go | 10 ++-- sei-cosmos/x/gov/client/utils/query.go | 32 +++++++------ 11 files changed, 74 insertions(+), 96 deletions(-) diff --git a/app/app.go b/app/app.go index 3b860ce769..185c4df362 100644 --- a/app/app.go +++ b/app/app.go @@ -2370,8 +2370,12 @@ func (app *App) RPCContextProvider(i int64) sdk.Context { // RegisterTendermintService implements the Application.RegisterLocalServices method. func (app *App) RegisterLocalServices(clientCtx client.LocalContext) { + node,err := clientCtx.GetNode() + if err!=nil { + panic(err) + } authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) - tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) + tmservice.RegisterTendermintService(app.GRPCQueryRouter(), node, app.interfaceRegistry) txConfigProvider := func(height int64) client.TxConfig { if app.ChainID != "pacific-1" { return app.encodingConfig.TxConfig diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index b05a98feac..7654a3e4e7 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -23,8 +23,9 @@ import ( ) type Client = rpcclient.Client +type LocalClient = *local.Local type Context = ContextG[Client] -type LocalContext = ContextG[*local.Local] +type LocalContext = ContextG[LocalClient] type contextBase struct { FromAddress sdk.AccAddress diff --git a/sei-cosmos/client/grpc/tmservice/service.go b/sei-cosmos/client/grpc/tmservice/service.go index ba66b52253..708f42b296 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -33,7 +33,7 @@ func GetProtoBlock(ctx context.Context, node client.Client, height *int64) (tmpr // This is the struct that we will implement all the handlers on. type queryServer struct { - clientCtx client.LocalContext + node client.Client interfaceRegistry codectypes.InterfaceRegistry } @@ -41,20 +41,16 @@ var _ ServiceServer = queryServer{} var _ codectypes.UnpackInterfacesMessage = &GetLatestValidatorSetResponse{} // NewQueryServer creates a new tendermint query server. -func NewQueryServer(clientCtx client.LocalContext, 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) { - node, err := s.clientCtx.GetNode() - if err != nil { - return nil, err - } - status, err := node.Status(ctx) + status, err := s.node.Status(ctx) if err != nil { return nil, err } @@ -65,11 +61,8 @@ func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*Get // GetLatestBlock implements ServiceServer.GetLatestBlock func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { - node, err := s.clientCtx.GetNode() - if err != nil { - return nil, err - } - block, err := node.Block(ctx, nil) + block, err := s.node.Block(ctx, nil) + if err!=nil { return nil, err } protoBlockID := block.BlockID.ToProto() protoBlock, err := block.Block.ToProto() @@ -85,7 +78,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.Any()) + chainHeight, err := rpc.GetChainHeight(ctx,s.node) if err != nil { return nil, err } @@ -94,12 +87,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") } - node, err := s.clientCtx.GetNode() - if err != nil { - return nil, err - } - - protoBlockID, protoBlock, err := GetProtoBlock(ctx, node, &req.Height) + protoBlockID, protoBlock, err := GetProtoBlock(ctx, s.node, &req.Height) if err != nil { return nil, err } @@ -115,7 +103,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 { @@ -135,15 +123,14 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida if err != nil { return nil, err } - - chainHeight, err := rpc.GetChainHeight(s.clientCtx.Any()) + 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 } @@ -154,11 +141,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida }, nil } -func validatorsOutput(ctx context.Context, cctx client.LocalContext, height *int64, page, limit int) (*GetLatestValidatorSetResponse, error) { - node, err := cctx.GetNode() - if err != nil { - return nil, err - } +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 @@ -187,11 +170,7 @@ func validatorsOutput(ctx context.Context, cctx client.LocalContext, height *int // GetNodeInfo implements ServiceServer.GetNodeInfo func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) { - node, err := s.clientCtx.GetNode() - if err != nil { - return nil, err - } - status, err := node.Status(ctx) + status, err := s.node.Status(ctx) if err != nil { return nil, err } @@ -228,10 +207,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.LocalContext, + 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/query.go b/sei-cosmos/client/query.go index 6f74b293eb..f513009332 100644 --- a/sei-cosmos/client/query.go +++ b/sei-cosmos/client/query.go @@ -12,6 +12,7 @@ import ( abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmbytes "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/libs/utils" "github.com/sei-protocol/sei-chain/sei-cosmos/store/rootmulti" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" @@ -20,11 +21,11 @@ import ( // GetNode returns an RPC client. If the context's client is not defined, an // error is returned. -func (ctx ContextG[C]) GetNode() (rpcclient.Client, error) { +func (ctx ContextG[C]) GetNode() (C, error) { if c, ok := ctx.Client.Get(); ok { return c, nil } - return nil, errors.New("no RPC client is defined in offline mode") + return utils.Zero[C](), 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..1983a54872 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/validators.go b/sei-cosmos/client/rpc/validators.go index b700006a3f..10722a4171 100644 --- a/sei-cosmos/client/rpc/validators.go +++ b/sei-cosmos/client/rpc/validators.go @@ -164,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 @@ -174,10 +177,7 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { rest.WriteErrorResponse(w, http.StatusNotFound, "requested block height is bigger then the chain length") return } - node, err := clientCtx.GetNode() - if rest.CheckInternalServerError(w, err) { - return - } + output, err := GetValidators(r.Context(), node, &height, &page, &limit) if rest.CheckInternalServerError(w, err) { return diff --git a/sei-cosmos/x/auth/client/cli/query.go b/sei-cosmos/x/auth/client/cli/query.go index 79a332655a..2092de0d8a 100644 --- a/sei-cosmos/x/auth/client/cli/query.go +++ b/sei-cosmos/x/auth/client/cli/query.go @@ -229,7 +229,7 @@ $ %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, "") + txs, err := authtx.QueryTxsByEvents(cmd.Context(), clientCtx, tmEvents, page, limit, "") if err != nil { return err } @@ -278,7 +278,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(), clientCtx, args[0]) if err != nil { return err } @@ -300,7 +300,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(), clientCtx, tmEvents, rest.DefaultPage, query.DefaultLimit, "") if err != nil { return err } @@ -323,7 +323,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(),clientCtx, tmEvents, rest.DefaultPage, query.DefaultLimit, "") if err != nil { return err } diff --git a/sei-cosmos/x/auth/tx/query.go b/sei-cosmos/x/auth/tx/query.go index a9a4339f61..7db7e1074f 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[C client.Client](clientCtx client.ContextG[C], events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) { +func QueryTxsByEvents[C client.Client](ctx context.Context, clientCtx client.ContextG[C], 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") } @@ -44,12 +44,11 @@ func QueryTxsByEvents[C client.Client](clientCtx client.ContextG[C], events []st // 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 } @@ -72,7 +71,7 @@ func QueryTxsByEvents[C client.Client](clientCtx client.ContextG[C], events []st // 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[C client.Client](clientCtx client.ContextG[C], hashHexStr string) (*sdk.TxResponse, error) { +func QueryTx[C client.Client](ctx context.Context, clientCtx client.ContextG[C], hashHexStr string) (*sdk.TxResponse, error) { hash, err := hex.DecodeString(hashHexStr) if err != nil { return nil, err @@ -85,12 +84,12 @@ func QueryTx[C client.Client](clientCtx client.ContextG[C], hashHexStr string) ( //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 } @@ -117,17 +116,12 @@ func formatTxResults(txConfig client.TxConfig, resTxs []*ctypes.ResultTx, resBlo return out, nil } -func getBlocksForTxResults[C client.Client](clientCtx client.ContextG[C], 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/gov/client/cli/query.go b/sei-cosmos/x/gov/client/cli/query.go index 81cf481090..9e6cf93d01 100644 --- a/sei-cosmos/x/gov/client/cli/query.go +++ b/sei-cosmos/x/gov/client/cli/query.go @@ -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 } diff --git a/sei-cosmos/x/gov/client/rest/query.go b/sei-cosmos/x/gov/client/rest/query.go index 0b161ae74a..ddbfc29cb7 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..735ba2a3c6 100644 --- a/sei-cosmos/x/gov/client/utils/query.go +++ b/sei-cosmos/x/gov/client/utils/query.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + "context" "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" @@ -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,11 @@ 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{} for _, events := range eventGroups { - res, err := authtx.QueryTxsByEvents(clientCtx, events, page, defaultLimit, "") + res, err := authtx.QueryTxsByEvents(ctx, clientCtx, events, page, defaultLimit, "") if err != nil { return nil, err } @@ -368,9 +370,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), From caf7a9af72cb46a828255dca171b97d8385325d7 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 17:26:16 +0200 Subject: [PATCH 18/33] constrained servers --- app/app.go | 20 +--- sei-cosmos/client/broadcast.go | 99 +++++-------------- sei-cosmos/client/grpc/tmservice/service.go | 10 +- sei-cosmos/client/grpc_query.go | 15 +-- sei-cosmos/client/query.go | 2 +- sei-cosmos/client/rpc/block.go | 2 +- sei-cosmos/client/tx/tx.go | 16 +-- sei-cosmos/server/rollback_test.go | 8 +- sei-cosmos/server/start.go | 2 +- sei-cosmos/server/types/app.go | 2 +- sei-cosmos/testutil/network/util.go | 3 +- sei-cosmos/x/auth/client/cli/broadcast.go | 6 +- sei-cosmos/x/auth/client/cli/query.go | 20 ++-- sei-cosmos/x/auth/client/rest/query.go | 14 ++- sei-cosmos/x/auth/tx/query.go | 18 +--- sei-cosmos/x/auth/tx/service.go | 28 +++--- sei-cosmos/x/auth/vesting/client/cli/tx.go | 2 +- sei-cosmos/x/authz/client/cli/tx.go | 6 +- sei-cosmos/x/bank/client/cli/tx.go | 2 +- sei-cosmos/x/crisis/client/cli/tx.go | 2 +- sei-cosmos/x/distribution/client/cli/tx.go | 18 ++-- sei-cosmos/x/feegrant/client/cli/tx.go | 4 +- sei-cosmos/x/gov/client/cli/query.go | 8 +- sei-cosmos/x/gov/client/cli/tx.go | 8 +- sei-cosmos/x/gov/client/rest/query.go | 2 +- sei-cosmos/x/gov/client/utils/query.go | 8 +- sei-cosmos/x/params/client/cli/tx.go | 2 +- sei-cosmos/x/slashing/client/cli/tx.go | 2 +- sei-cosmos/x/staking/client/cli/tx.go | 10 +- sei-cosmos/x/staking/client/rest/query.go | 2 +- sei-cosmos/x/staking/client/rest/utils.go | 9 +- sei-cosmos/x/upgrade/client/cli/tx.go | 4 +- .../host/client/cli/query.go | 6 +- .../modules/apps/transfer/client/cli/tx.go | 2 +- .../modules/core/02-client/client/cli/tx.go | 12 +-- sei-ibc-go/testing/simapp/app.go | 6 +- sei-wasmd/app/app.go | 6 +- sei-wasmd/x/wasm/client/cli/gov_tx.go | 20 ++-- sei-wasmd/x/wasm/client/cli/new_tx.go | 6 +- sei-wasmd/x/wasm/client/cli/tx.go | 6 +- x/evm/client/cli/gov_tx.go | 2 +- x/evm/client/cli/native_tx.go | 14 +-- x/mint/client/cli/tx.go | 2 +- x/oracle/client/cli/tx.go | 4 +- x/tokenfactory/client/cli/tx.go | 12 +-- 45 files changed, 207 insertions(+), 245 deletions(-) diff --git a/app/app.go b/app/app.go index 185c4df362..cef590405d 100644 --- a/app/app.go +++ b/app/app.go @@ -2369,12 +2369,8 @@ func (app *App) RPCContextProvider(i int64) sdk.Context { } // RegisterTendermintService implements the Application.RegisterLocalServices method. -func (app *App) RegisterLocalServices(clientCtx client.LocalContext) { - node,err := clientCtx.GetNode() - if err!=nil { - panic(err) - } - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) +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" { @@ -2388,11 +2384,7 @@ func (app *App) RegisterLocalServices(clientCtx client.LocalContext) { } if app.evmRPCConfig.HTTPEnabled { - client, err := clientCtx.GetNode() - if err != nil { - panic(err) - } - evmHTTPServer, err := evmrpc.NewEVMHTTPServer(app.evmRPCConfig, 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) } @@ -2405,11 +2397,7 @@ func (app *App) RegisterLocalServices(clientCtx client.LocalContext) { } if app.evmRPCConfig.WSEnabled { - client, err := clientCtx.GetNode() - if err != nil { - panic(err) - } - evmWSServer, err := evmrpc.NewEVMWebSocketServer(app.evmRPCConfig, 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/sei-cosmos/client/broadcast.go b/sei-cosmos/client/broadcast.go index f5d3121066..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 ContextG[C]) 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 ContextG[C]) 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 ContextG[C]) 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 ContextG[C]) 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 (clientCtx ContextG[C]) TxServiceBroadcast(grpcCtx context.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/grpc/tmservice/service.go b/sei-cosmos/client/grpc/tmservice/service.go index 708f42b296..fd66f4b484 100644 --- a/sei-cosmos/client/grpc/tmservice/service.go +++ b/sei-cosmos/client/grpc/tmservice/service.go @@ -33,7 +33,7 @@ func GetProtoBlock(ctx context.Context, node client.Client, height *int64) (tmpr // This is the struct that we will implement all the handlers on. type queryServer struct { - node client.Client + node client.Client interfaceRegistry codectypes.InterfaceRegistry } @@ -43,7 +43,7 @@ var _ codectypes.UnpackInterfacesMessage = &GetLatestValidatorSetResponse{} // NewQueryServer creates a new tendermint query server. func NewQueryServer(node client.Client, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer { return queryServer{ - node: node, + node: node, interfaceRegistry: interfaceRegistry, } } @@ -62,7 +62,9 @@ func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*Get // GetLatestBlock implements ServiceServer.GetLatestBlock func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockRequest) (*GetLatestBlockResponse, error) { block, err := s.node.Block(ctx, nil) - if err!=nil { return nil, err } + if err != nil { + return nil, err + } protoBlockID := block.BlockID.ToProto() protoBlock, err := block.Block.ToProto() @@ -78,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(ctx,s.node) + chainHeight, err := rpc.GetChainHeight(ctx, s.node) if err != nil { return nil, err } diff --git a/sei-cosmos/client/grpc_query.go b/sei-cosmos/client/grpc_query.go index 136711ce65..84a95268d6 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 ContextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) { +func (ctx ContextG[C]) 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 ContextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, rep return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req) } - broadcastRes, err := ctx.TxServiceBroadcast(grpcCtx, 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 ContextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, rep 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 f513009332..346041b057 100644 --- a/sei-cosmos/client/query.go +++ b/sei-cosmos/client/query.go @@ -11,8 +11,8 @@ import ( abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmbytes "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/libs/utils" + rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/sei-protocol/sei-chain/sei-cosmos/store/rootmulti" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" diff --git a/sei-cosmos/client/rpc/block.go b/sei-cosmos/client/rpc/block.go index 1983a54872..a229c985f1 100644 --- a/sei-cosmos/client/rpc/block.go +++ b/sei-cosmos/client/rpc/block.go @@ -94,7 +94,7 @@ func BlockRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - node,err := clientCtx.GetNode() + node, err := clientCtx.GetNode() 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/rollback_test.go b/sei-cosmos/server/rollback_test.go index 6c86d380e0..fde27eb965 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -106,10 +106,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) RegisterLocalServices(client.LocalContext) {} -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.LocalContext, client.LocalClient) {} +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 129a628fad..8536c5f6f5 100644 --- a/sei-cosmos/server/start.go +++ b/sei-cosmos/server/start.go @@ -371,7 +371,7 @@ func startInProcess( return err } clientCtx = clientCtx.WithClient(localClient) - app.RegisterLocalServices(client.WithClient(clientCtx, localClient)) + app.RegisterLocalServices(localClient, clientCtx.TxConfig) } } diff --git a/sei-cosmos/server/types/app.go b/sei-cosmos/server/types/app.go index a95c1ffbe8..fd617d4e9a 100644 --- a/sei-cosmos/server/types/app.go +++ b/sei-cosmos/server/types/app.go @@ -50,7 +50,7 @@ type ( RegisterGRPCServer(grpc.Server) // RegisterTxService registers RPCs of the local tendermint node. - RegisterLocalServices(clientCtx client.LocalContext) + 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 db9fe9855e..180b716039 100644 --- a/sei-cosmos/testutil/network/util.go +++ b/sei-cosmos/testutil/network/util.go @@ -6,7 +6,6 @@ import ( "path/filepath" "time" - "github.com/sei-protocol/sei-chain/sei-cosmos/client" "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/codec" tmtime "github.com/sei-protocol/sei-chain/sei-cosmos/std" "github.com/sei-protocol/sei-chain/sei-cosmos/telemetry" @@ -77,7 +76,7 @@ func startInProcess(cfg Config, val *Validator) error { // 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(client.WithClient(val.ClientCtx, localClient)) + app.RegisterLocalServices(localClient, val.ClientCtx.TxConfig) } } 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 2092de0d8a..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(cmd.Context(), 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(cmd.Context(), 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(cmd.Context(), 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(cmd.Context(),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 7db7e1074f..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[C client.Client](ctx context.Context, clientCtx client.ContextG[C], 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,11 +37,6 @@ func QueryTxsByEvents[C client.Client](ctx context.Context, clientCtx client.Con // 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(ctx, query, true, &page, &limit, orderBy) @@ -53,7 +48,7 @@ func QueryTxsByEvents[C client.Client](ctx context.Context, clientCtx client.Con return nil, err } - txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks) + txs, err := formatTxResults(txConfig, resTxs.Txs, resBlocks) if err != nil { return nil, err } @@ -71,17 +66,12 @@ func QueryTxsByEvents[C client.Client](ctx context.Context, clientCtx client.Con // 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[C client.Client](ctx context.Context, clientCtx client.ContextG[C], 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(ctx, hash, true) @@ -94,7 +84,7 @@ func QueryTx[C client.Client](ctx context.Context, clientCtx client.ContextG[C], 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 } diff --git a/sei-cosmos/x/auth/tx/service.go b/sei-cosmos/x/auth/tx/service.go index 2796ffb6d2..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.LocalContext + node client.LocalClient + txConfig client.TxConfig simulate baseAppSimulateFn interfaceRegistry codectypes.InterfaceRegistry } // NewTxServer creates a new Tx service server. -func NewTxServer(clientCtx client.LocalContext, 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,12 +183,7 @@ func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWith "or greater than the current height %d", req.Height, currentHeight) } - node, err := s.clientCtx.GetNode() - if err != nil { - return nil, err - } - - blockId, block, err := tmservice.GetProtoBlock(ctx, node, &req.Height) + blockId, block, err := tmservice.GetProtoBlock(ctx, s.node, &req.Height) if err != nil { return nil, err } @@ -208,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 } @@ -245,17 +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 s.clientCtx.TxServiceBroadcast(ctx, req) + return client.TxServiceBroadcast(ctx, s.node, req) } // RegisterTxService registers the tx service on the gRPC router. func RegisterTxService( qrt gogogrpc.Server, - clientCtx client.LocalContext, + 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/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/gov/client/cli/query.go b/sei-cosmos/x/gov/client/cli/query.go index 9e6cf93d01..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 } @@ -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 ddbfc29cb7..10de0bd195 100644 --- a/sei-cosmos/x/gov/client/rest/query.go +++ b/sei-cosmos/x/gov/client/rest/query.go @@ -300,7 +300,7 @@ func queryVoteHandlerFn(clientCtx client.Context) http.HandlerFunc { return } - res, err = gcutils.QueryVoteByTxQuery(r.Context(),clientCtx, params) + res, err = gcutils.QueryVoteByTxQuery(r.Context(), clientCtx, params) if rest.CheckInternalServerError(w, err) { return } diff --git a/sei-cosmos/x/gov/client/utils/query.go b/sei-cosmos/x/gov/client/utils/query.go index 735ba2a3c6..d1c105f7c9 100644 --- a/sei-cosmos/x/gov/client/utils/query.go +++ b/sei-cosmos/x/gov/client/utils/query.go @@ -1,8 +1,8 @@ package utils import ( - "fmt" "context" + "fmt" "github.com/sei-protocol/sei-chain/sei-cosmos/client" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" @@ -357,8 +357,12 @@ func QueryProposalByID(proposalID uint64, clientCtx client.Context, queryRoute s 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(ctx, clientCtx, events, page, defaultLimit, "") + res, err := authtx.QueryTxsByEvents(ctx, node, clientCtx.TxConfig, events, page, defaultLimit, "") if err != nil { return nil, err } 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 1c70562a41..c4619ea9c5 100644 --- a/sei-ibc-go/testing/simapp/app.go +++ b/sei-ibc-go/testing/simapp/app.go @@ -704,9 +704,9 @@ func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICon } // RegisterTxService implements the Application.RegisterLocalServices method. -func (app *SimApp) RegisterLocalServices(clientCtx client.LocalContext) { - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) - tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +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-wasmd/app/app.go b/sei-wasmd/app/app.go index d08ab61e63..3b6ce3d578 100644 --- a/sei-wasmd/app/app.go +++ b/sei-wasmd/app/app.go @@ -831,9 +831,9 @@ func (app *WasmApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICo } // RegisterTxService implements the Application.RegisterLocalServices method. -func (app *WasmApp) RegisterLocalServices(clientCtx client.LocalContext) { - authtx.RegisterTxService(app.GRPCQueryRouter(), clientCtx, app.Simulate, app.interfaceRegistry) - tmservice.RegisterTendermintService(app.GRPCQueryRouter(), clientCtx, app.interfaceRegistry) +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/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/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) }, } From a0d0a18a8bb1e72652c4655e017bee1c33639bcf Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 17:44:54 +0200 Subject: [PATCH 19/33] fully local evmrpc --- evmrpc/association.go | 5 ++- evmrpc/block.go | 9 +++--- evmrpc/block_txcount_parity_test.go | 2 ++ evmrpc/filter.go | 7 ++--- evmrpc/height_availability_test.go | 2 ++ evmrpc/info.go | 5 ++- evmrpc/net.go | 6 ++-- evmrpc/send.go | 5 ++- evmrpc/server.go | 5 ++- evmrpc/setup_test.go | 2 ++ evmrpc/simulate.go | 7 ++--- evmrpc/simulate_test.go | 39 ++++++++++++------------ evmrpc/state.go | 6 ++-- evmrpc/subscribe.go | 10 +++--- evmrpc/tests/mock_client.go | 2 ++ evmrpc/tracers.go | 7 ++--- evmrpc/tx.go | 7 ++--- evmrpc/txpool.go | 5 ++- evmrpc/utils.go | 13 ++++---- evmrpc/watermark_manager.go | 10 +++--- evmrpc/watermark_manager_test.go | 2 ++ sei-cosmos/client/context.go | 6 ++-- sei-tendermint/rpc/client/local/local.go | 4 +++ 23 files changed, 85 insertions(+), 81 deletions(-) diff --git a/evmrpc/association.go b/evmrpc/association.go index 1e1e06bb7b..d054fc5fe2 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 f03078ffce..b60958c8e9 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..ac86875a5b 100644 --- a/evmrpc/block_txcount_parity_test.go +++ b/evmrpc/block_txcount_parity_test.go @@ -33,6 +33,8 @@ type parityTxCountTMClient struct { block *coretypes.ResultBlock } +func (*parityTxCountTMClient) IsLocal() {} + 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 09b5a86c85..b20f180f0a 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/utils/metrics" @@ -236,7 +235,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 @@ -261,7 +260,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, @@ -696,7 +695,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..f340fc2735 100644 --- a/evmrpc/height_availability_test.go +++ b/evmrpc/height_availability_test.go @@ -29,6 +29,8 @@ type heightTestClient struct { latest int64 } +func (*heightTestClient) IsLocal() {} + func newHeightTestClient(highHeight, earliest, latest int64) *heightTestClient { return &heightTestClient{ Client: mock.Client{}, diff --git a/evmrpc/info.go b/evmrpc/info.go index c6d3c78a68..e205417ecb 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 8ce40da224..1d8044d95e 100644 --- a/evmrpc/net.go +++ b/evmrpc/net.go @@ -3,20 +3,20 @@ package evmrpc import ( "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 64545eb8d9..187dec568f 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..eec7896b84 100644 --- a/evmrpc/setup_test.go +++ b/evmrpc/setup_test.go @@ -119,6 +119,8 @@ type MockClient struct { latestOverride int64 } +func (*MockClient) IsLocal() {} + func NewMockClientWithLatest(latest int64) *MockClient { return &MockClient{latestOverride: latest} } diff --git a/evmrpc/simulate.go b/evmrpc/simulate.go index faa6f1d028..c6958c2774 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, @@ -222,7 +221,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 @@ -237,7 +236,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 6b3d2ab569..c0e9997269 100644 --- a/evmrpc/simulate_test.go +++ b/evmrpc/simulate_test.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" receipt "github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt" - "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client/mock" "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" "github.com/sei-protocol/sei-chain/x/evm/types" @@ -60,7 +59,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", @@ -70,13 +69,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 @@ -91,7 +90,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", @@ -99,7 +98,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 @@ -135,7 +134,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, @@ -179,7 +178,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", @@ -187,7 +186,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", @@ -195,7 +194,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 @@ -216,7 +215,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", @@ -228,11 +227,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) @@ -252,7 +251,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", @@ -260,7 +259,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) @@ -271,7 +270,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", @@ -279,11 +278,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") diff --git a/evmrpc/state.go b/evmrpc/state.go index 8765fecf4f..67899d9853 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 a88c096aa6..e0fb881b5b 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, @@ -232,10 +232,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..b1a7718fe6 100644 --- a/evmrpc/tests/mock_client.go +++ b/evmrpc/tests/mock_client.go @@ -35,6 +35,8 @@ type MockClient struct { mockedGenesis *coretypes.ResultGenesis } +func (c *MockClient) IsLocal() {} + 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 6c2176ec76..47a9567759 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 25e1c5df10..327a0bc7e8 100644 --- a/evmrpc/tx.go +++ b/evmrpc/tx.go @@ -21,7 +21,6 @@ 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/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" @@ -35,7 +34,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 @@ -53,7 +52,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, @@ -77,7 +76,7 @@ func NewTransactionAPI( } func NewSeiTransactionAPI( - tmClient rpcclient.Client, + tmClient client.LocalClient, k *keeper.Keeper, ctxProvider func(int64) sdk.Context, txConfigProvider func(int64) client.TxConfig, diff --git a/evmrpc/txpool.go b/evmrpc/txpool.go index b8646684bf..c01488daf8 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 f1fb7a8f11..d514ce3f78 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" "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..7ac5082110 100644 --- a/evmrpc/watermark_manager_test.go +++ b/evmrpc/watermark_manager_test.go @@ -162,6 +162,8 @@ type fakeTMClient struct { genesis *coretypes.ResultGenesis } +func (*fakeTMClient) IsLocal() {} + func (f *fakeTMClient) Status(context.Context) (*coretypes.ResultStatus, error) { if f.statusErr != nil { return nil, f.statusErr diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 7654a3e4e7..984c3a38aa 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -14,7 +14,6 @@ import ( "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-tendermint/rpc/client/local" "github.com/sei-protocol/sei-chain/sei-cosmos/codec" codectypes "github.com/sei-protocol/sei-chain/sei-cosmos/codec/types" @@ -23,7 +22,10 @@ import ( ) type Client = rpcclient.Client -type LocalClient = *local.Local +type LocalClient interface { + Client + IsLocal() +} type Context = ContextG[Client] type LocalContext = ContextG[LocalClient] diff --git a/sei-tendermint/rpc/client/local/local.go b/sei-tendermint/rpc/client/local/local.go index 67bb8494ad..c4981c926f 100644 --- a/sei-tendermint/rpc/client/local/local.go +++ b/sei-tendermint/rpc/client/local/local.go @@ -45,6 +45,10 @@ type Local struct { env *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 { From 2c18b7530b057e15fb5cfdbcb3b5417598384098 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 18:23:09 +0200 Subject: [PATCH 20/33] Re-run checks From d5a44f6116fdacf1f911295c72f40ee6aec317e5 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 18:54:34 +0200 Subject: [PATCH 21/33] test fix --- sei-cosmos/client/broadcast_test.go | 14 +++----------- sei-cosmos/server/rollback_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/sei-cosmos/client/broadcast_test.go b/sei-cosmos/client/broadcast_test.go index bdcb55cc58..917c5b882d 100644 --- a/sei-cosmos/client/broadcast_test.go +++ b/sei-cosmos/client/broadcast_test.go @@ -13,8 +13,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/client/flags" sdkerrors "github.com/sei-protocol/sei-chain/sei-cosmos/types/errors" - "github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" ) type MockClient struct { @@ -34,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: utils.Some[rpcclient.Client](MockClient{err: err}), - contextBase: contextBase{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(), @@ -60,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/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index fde27eb965..2308958526 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -106,10 +106,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) RegisterLocalServices(client.LocalContext, client.LocalClient) {} -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) { From 24bded44c6f76c6dd7c8c6897bc1e7cc4b0d7864 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Mon, 4 May 2026 19:06:59 +0200 Subject: [PATCH 22/33] test fix --- sei-cosmos/x/distribution/client/cli/tx_test.go | 6 ++++-- sei-cosmos/x/gov/client/utils/query_test.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) 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/gov/client/utils/query_test.go b/sei-cosmos/x/gov/client/utils/query_test.go index 5afc6c57c6..fe2a981e78 100644 --- a/sei-cosmos/x/gov/client/utils/query_test.go +++ b/sei-cosmos/x/gov/client/utils/query_test.go @@ -179,7 +179,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)) From 4f45db24ea6e3720b5a1b1cedb39997ea43d38e2 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 5 May 2026 14:31:24 +0200 Subject: [PATCH 23/33] reverted generic context --- loadtest/sign.go | 3 +- sei-cosmos/client/cmd.go | 3 +- sei-cosmos/client/config/config.go | 6 +- sei-cosmos/client/context.go | 100 +++++++++++------------------ sei-cosmos/client/grpc_query.go | 4 +- sei-cosmos/client/query.go | 25 ++++---- 6 files changed, 57 insertions(+), 84 deletions(-) diff --git a/loadtest/sign.go b/loadtest/sign.go index b4c1726e86..3d5afd75d5 100644 --- a/loadtest/sign.go +++ b/loadtest/sign.go @@ -21,7 +21,6 @@ import ( "github.com/sei-protocol/sei-chain/sei-cosmos/types/tx/signing" xauthsigning "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/signing" authtypes "github.com/sei-protocol/sei-chain/sei-cosmos/x/auth/types" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" ) type AccountInfo struct { @@ -227,7 +226,7 @@ func (sc *SignerClient) GetAccountNumberSequenceNumber(privKey cryptotypes.PrivK } context := client.Context{} context = context.WithNodeURI(sc.NodeURI) - context = client.WithClient[rpcclient.Client](context, cl) + context = context.WithClient(cl) context = context.WithInterfaceRegistry(TestConfig.InterfaceRegistry) userHomeDir, _ := os.UserHomeDir() kr, _ := keyring.New(sdk.KeyringServiceName(), "test", filepath.Join(userHomeDir, ".sei"), os.Stdin) diff --git a/sei-cosmos/client/cmd.go b/sei-cosmos/client/cmd.go index 7478263ce7..470ad9794d 100644 --- a/sei-cosmos/client/cmd.go +++ b/sei-cosmos/client/cmd.go @@ -6,7 +6,6 @@ import ( "github.com/pkg/errors" "github.com/sei-protocol/sei-chain/sei-tendermint/libs/cli" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -144,7 +143,7 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont return clientCtx, err } - clientCtx = WithClient[rpcclient.Client](clientCtx, client) + clientCtx = clientCtx.WithClient(client) } } diff --git a/sei-cosmos/client/config/config.go b/sei-cosmos/client/config/config.go index cf25080b10..bae915baa7 100644 --- a/sei-cosmos/client/config/config.go +++ b/sei-cosmos/client/config/config.go @@ -6,7 +6,6 @@ import ( "path/filepath" "github.com/sei-protocol/sei-chain/sei-cosmos/client" - rpcclient "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/client" ) // Default constants @@ -85,12 +84,13 @@ func ReadFromClientConfig(ctx client.Context) (client.Context, error) { ctx = ctx.WithKeyring(keyring) // https://github.com/cosmos/cosmos-sdk/issues/8986 - c, err := client.NewClientFromNode(conf.Node) + client, err := client.NewClientFromNode(conf.Node) if err != nil { return ctx, fmt.Errorf("couldn't get client from nodeURI: %v", err) } - ctx = client.WithClient[rpcclient.Client](ctx.WithNodeURI(conf.Node), c). + ctx = ctx.WithNodeURI(conf.Node). + WithClient(client). WithBroadcastMode(conf.BroadcastMode) return ctx, nil diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 984c3a38aa..a231152271 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -26,11 +26,10 @@ type LocalClient interface { Client IsLocal() } -type Context = ContextG[Client] -type LocalContext = ContextG[LocalClient] -type contextBase struct { +type Context struct { FromAddress sdk.AccAddress + Client utils.Option[rpcclient.Client] ChainID string // Deprecated: Codec codec will be changed to Codec: codec.Codec JSONCodec codec.JSONCodec @@ -64,35 +63,20 @@ type contextBase struct { LegacyAmino *codec.LegacyAmino } -// Context implements a typical context created in SDK modules for transaction -// handling and queries. -type ContextG[C Client] struct { - contextBase - Client utils.Option[C] -} - -func (ctx ContextG[C]) Any() Context { - ctx2 := Context{contextBase: ctx.contextBase} - if c, ok := ctx.Client.Get(); ok { - ctx2.Client = utils.Some[Client](c) - } - return ctx2 -} - // WithKeyring returns a copy of the context with an updated keyring. -func (ctx ContextG[C]) WithKeyring(k keyring.Keyring) ContextG[C] { +func (ctx Context) WithKeyring(k keyring.Keyring) Context { ctx.Keyring = k return ctx } // WithKeyringOptions returns a copy of the context with an updated keyring. -func (ctx ContextG[C]) WithKeyringOptions(opts ...keyring.Option) ContextG[C] { +func (ctx Context) WithKeyringOptions(opts ...keyring.Option) Context { ctx.KeyringOptions = opts return ctx } // WithInput returns a copy of the context with an updated input. -func (ctx ContextG[C]) WithInput(r io.Reader) ContextG[C] { +func (ctx Context) WithInput(r io.Reader) Context { // convert to a bufio.Reader to have a shared buffer between the keyring and the // the Commands, ensuring a read from one advance the read pointer for the other. // see https://github.com/cosmos/cosmos-sdk/issues/9566. @@ -101,7 +85,7 @@ func (ctx ContextG[C]) WithInput(r io.Reader) ContextG[C] { } // Deprecated: WithJSONCodec returns a copy of the Context with an updated JSONCodec. -func (ctx ContextG[C]) WithJSONCodec(m codec.JSONCodec) ContextG[C] { +func (ctx Context) WithJSONCodec(m codec.JSONCodec) Context { ctx.JSONCodec = m // since we are using ctx.Codec everywhere in the SDK, for backward compatibility // we need to try to set it here as well. @@ -112,7 +96,7 @@ func (ctx ContextG[C]) WithJSONCodec(m codec.JSONCodec) ContextG[C] { } // WithCodec returns a copy of the Context with an updated Codec. -func (ctx ContextG[C]) WithCodec(m codec.Codec) ContextG[C] { +func (ctx Context) WithCodec(m codec.Codec) Context { ctx.JSONCodec = m ctx.Codec = m return ctx @@ -120,70 +104,62 @@ func (ctx ContextG[C]) WithCodec(m codec.Codec) ContextG[C] { // WithLegacyAmino returns a copy of the context with an updated LegacyAmino codec. // TODO: Deprecated (remove). -func (ctx ContextG[C]) WithLegacyAmino(cdc *codec.LegacyAmino) ContextG[C] { +func (ctx Context) WithLegacyAmino(cdc *codec.LegacyAmino) Context { ctx.LegacyAmino = cdc return ctx } // WithOutput returns a copy of the context with an updated output writer (e.g. stdout). -func (ctx ContextG[C]) WithOutput(w io.Writer) ContextG[C] { +func (ctx Context) WithOutput(w io.Writer) Context { ctx.Output = w return ctx } // WithFrom returns a copy of the context with an updated from address or name. -func (ctx ContextG[C]) WithFrom(from string) ContextG[C] { +func (ctx Context) WithFrom(from string) Context { ctx.From = from return ctx } // WithOutputFormat returns a copy of the context with an updated OutputFormat field. -func (ctx ContextG[C]) WithOutputFormat(format string) ContextG[C] { +func (ctx Context) WithOutputFormat(format string) Context { ctx.OutputFormat = format return ctx } // WithNodeURI returns a copy of the context with an updated node URI. -func (ctx ContextG[C]) WithNodeURI(nodeURI string) ContextG[C] { +func (ctx Context) WithNodeURI(nodeURI string) Context { ctx.NodeURI = nodeURI return ctx } // WithHeight returns a copy of the context with an updated height. -func (ctx ContextG[C]) WithHeight(height int64) ContextG[C] { +func (ctx Context) WithHeight(height int64) Context { ctx.Height = height return ctx } // WithClient returns a copy of the context with an updated RPC client // instance. -func (ctx ContextG[C]) WithClient(client C) ContextG[C] { - return WithClient(ctx, client) -} - -// WithClient returns a copy of the context with an updated RPC client -// instance. -func WithClient[C2, C1 Client](ctx ContextG[C1], client C2) ContextG[C2] { - return ContextG[C2]{ - contextBase: ctx.contextBase, - Client: utils.Some(client), - } +func (ctx Context) WithClient(client Client) Context { + ctx.Client = utils.Some(client) + return ctx } // WithUseLedger returns a copy of the context with an updated UseLedger flag. -func (ctx ContextG[C]) WithUseLedger(useLedger bool) ContextG[C] { +func (ctx Context) WithUseLedger(useLedger bool) Context { ctx.UseLedger = useLedger return ctx } // WithChainID returns a copy of the context with an updated chain ID. -func (ctx ContextG[C]) WithChainID(chainID string) ContextG[C] { +func (ctx Context) WithChainID(chainID string) Context { ctx.ChainID = chainID return ctx } // WithHomeDir returns a copy of the Context with HomeDir set. -func (ctx ContextG[C]) WithHomeDir(dir string) ContextG[C] { +func (ctx Context) WithHomeDir(dir string) Context { if dir != "" { ctx.HomeDir = dir } @@ -191,91 +167,91 @@ func (ctx ContextG[C]) WithHomeDir(dir string) ContextG[C] { } // WithKeyringDir returns a copy of the Context with KeyringDir set. -func (ctx ContextG[C]) WithKeyringDir(dir string) ContextG[C] { +func (ctx Context) WithKeyringDir(dir string) Context { ctx.KeyringDir = dir return ctx } // WithGenerateOnly returns a copy of the context with updated GenerateOnly value -func (ctx ContextG[C]) WithGenerateOnly(generateOnly bool) ContextG[C] { +func (ctx Context) WithGenerateOnly(generateOnly bool) Context { ctx.GenerateOnly = generateOnly return ctx } // WithSimulation returns a copy of the context with updated Simulate value -func (ctx ContextG[C]) WithSimulation(simulate bool) ContextG[C] { +func (ctx Context) WithSimulation(simulate bool) Context { ctx.Simulate = simulate return ctx } // WithOffline returns a copy of the context with updated Offline value. -func (ctx ContextG[C]) WithOffline(offline bool) ContextG[C] { +func (ctx Context) WithOffline(offline bool) Context { ctx.Offline = offline return ctx } // WithFromName returns a copy of the context with an updated from account name. -func (ctx ContextG[C]) WithFromName(name string) ContextG[C] { +func (ctx Context) WithFromName(name string) Context { ctx.FromName = name return ctx } // WithFromAddress returns a copy of the context with an updated from account // address. -func (ctx ContextG[C]) WithFromAddress(addr sdk.AccAddress) ContextG[C] { +func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context { ctx.FromAddress = addr return ctx } // WithFeeGranterAddress returns a copy of the context with an updated fee granter account // address. -func (ctx ContextG[C]) WithFeeGranterAddress(addr sdk.AccAddress) ContextG[C] { +func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context { ctx.FeeGranter = addr return ctx } // WithBroadcastMode returns a copy of the context with an updated broadcast // mode. -func (ctx ContextG[C]) WithBroadcastMode(mode string) ContextG[C] { +func (ctx Context) WithBroadcastMode(mode string) Context { ctx.BroadcastMode = mode return ctx } // WithSignModeStr returns a copy of the context with an updated SignMode // value. -func (ctx ContextG[C]) WithSignModeStr(signModeStr string) ContextG[C] { +func (ctx Context) WithSignModeStr(signModeStr string) Context { ctx.SignModeStr = signModeStr return ctx } // WithSkipConfirmation returns a copy of the context with an updated SkipConfirm // value. -func (ctx ContextG[C]) WithSkipConfirmation(skip bool) ContextG[C] { +func (ctx Context) WithSkipConfirmation(skip bool) Context { ctx.SkipConfirm = skip return ctx } // WithTxConfig returns the context with an updated TxConfig -func (ctx ContextG[C]) WithTxConfig(generator TxConfig) ContextG[C] { +func (ctx Context) WithTxConfig(generator TxConfig) Context { ctx.TxConfig = generator return ctx } // WithAccountRetriever returns the context with an updated AccountRetriever -func (ctx ContextG[C]) WithAccountRetriever(retriever AccountRetriever) ContextG[C] { +func (ctx Context) WithAccountRetriever(retriever AccountRetriever) Context { ctx.AccountRetriever = retriever return ctx } // WithInterfaceRegistry returns the context with an updated InterfaceRegistry -func (ctx ContextG[C]) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) ContextG[C] { +func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) Context { ctx.InterfaceRegistry = interfaceRegistry return ctx } // WithViper returns the context with Viper field. This Viper instance is used to read // client-side config from the config file. -func (ctx ContextG[C]) WithViper(prefix string) ContextG[C] { +func (ctx Context) WithViper(prefix string) Context { v := viper.New() v.SetEnvPrefix(prefix) v.AutomaticEnv() @@ -284,13 +260,13 @@ func (ctx ContextG[C]) WithViper(prefix string) ContextG[C] { } // PrintString prints the raw string to ctx.Output if it's defined, otherwise to os.Stdout -func (ctx ContextG[C]) PrintString(str string) error { +func (ctx Context) PrintString(str string) error { return ctx.PrintBytes([]byte(str)) } // PrintBytes prints the raw bytes to ctx.Output if it's defined, otherwise to os.Stdout. // NOTE: for printing a complex state object, you should use ctx.PrintOutput -func (ctx ContextG[C]) PrintBytes(o []byte) error { +func (ctx Context) PrintBytes(o []byte) error { writer := ctx.Output if writer == nil { writer = os.Stdout @@ -303,7 +279,7 @@ func (ctx ContextG[C]) PrintBytes(o []byte) error { // PrintProto outputs toPrint to the ctx.Output based on ctx.OutputFormat which is // either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint // will be JSON encoded using ctx.Codec. An error is returned upon failure. -func (ctx ContextG[C]) PrintProto(toPrint proto.Message) error { +func (ctx Context) PrintProto(toPrint proto.Message) error { // always serialize JSON initially because proto json can't be directly YAML encoded out, err := ctx.Codec.MarshalAsJSON(toPrint) if err != nil { @@ -315,7 +291,7 @@ func (ctx ContextG[C]) 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 ContextG[C]) PrintObjectLegacy(toPrint any) error { +func (ctx Context) PrintObjectLegacy(toPrint any) error { out, err := ctx.LegacyAmino.MarshalAsJSON(toPrint) if err != nil { return err @@ -323,7 +299,7 @@ func (ctx ContextG[C]) PrintObjectLegacy(toPrint any) error { return ctx.printOutput(out) } -func (ctx ContextG[C]) printOutput(out []byte) 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 any diff --git a/sei-cosmos/client/grpc_query.go b/sei-cosmos/client/grpc_query.go index 84a95268d6..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 ContextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) 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. @@ -114,6 +114,6 @@ func (ctx ContextG[C]) Invoke(grpcCtx gocontext.Context, method string, req, rep } // NewStream implements the grpc ClientConn.NewStream method -func (ContextG[C]) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { +func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { return nil, fmt.Errorf("streaming rpc not supported") } diff --git a/sei-cosmos/client/query.go b/sei-cosmos/client/query.go index 346041b057..37b511d39d 100644 --- a/sei-cosmos/client/query.go +++ b/sei-cosmos/client/query.go @@ -11,7 +11,6 @@ import ( abci "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" tmbytes "github.com/sei-protocol/sei-chain/sei-tendermint/libs/bytes" - "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/store/rootmulti" @@ -21,31 +20,31 @@ import ( // GetNode returns an RPC client. If the context's client is not defined, an // error is returned. -func (ctx ContextG[C]) GetNode() (C, error) { +func (ctx Context) GetNode() (Client, error) { if c, ok := ctx.Client.Get(); ok { return c, nil } - return utils.Zero[C](), errors.New("no RPC client is defined in offline mode") + return nil, errors.New("no RPC client is defined in offline mode") } // Query performs a query to a Tendermint node with the provided path. // It returns the result and height of the query upon success or an error if // the query fails. -func (ctx ContextG[C]) Query(path string) ([]byte, int64, error) { +func (ctx Context) Query(path string) ([]byte, int64, error) { return ctx.query(path, nil) } // QueryWithData performs a query to a Tendermint node with the provided path // and a data payload. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx ContextG[C]) QueryWithData(path string, data []byte) ([]byte, int64, error) { +func (ctx Context) QueryWithData(path string, data []byte) ([]byte, int64, error) { return ctx.query(path, data) } // QueryStore performs a query to a Tendermint node with the provided key and // store name. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx ContextG[C]) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { +func (ctx Context) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte, int64, error) { return ctx.queryStore(key, storeName, "key") } @@ -53,26 +52,26 @@ func (ctx ContextG[C]) QueryStore(key tmbytes.HexBytes, storeName string) ([]byt // It returns the ResultQuery obtained from the query. The height used to perform // the query is the RequestQuery Height if it is non-zero, otherwise the context // height is used. -func (ctx ContextG[C]) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { +func (ctx Context) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { return ctx.queryABCI(req) } // GetFromAddress returns the from address from the context's name. -func (ctx ContextG[C]) GetFromAddress() sdk.AccAddress { +func (ctx Context) GetFromAddress() sdk.AccAddress { return ctx.FromAddress } // GetFeeGranterAddress returns the fee granter address from the context -func (ctx ContextG[C]) GetFeeGranterAddress() sdk.AccAddress { +func (ctx Context) GetFeeGranterAddress() sdk.AccAddress { return ctx.FeeGranter } // GetFromName returns the key name for the current context. -func (ctx ContextG[C]) GetFromName() string { +func (ctx Context) GetFromName() string { return ctx.FromName } -func (ctx ContextG[C]) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { +func (ctx Context) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { node, err := ctx.GetNode() if err != nil { return abci.ResponseQuery{}, err @@ -124,7 +123,7 @@ func sdkErrorToGRPCError(resp abci.ResponseQuery) error { // query performs a query to a Tendermint node with the provided store name // and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx ContextG[C]) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { +func (ctx Context) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { resp, err := ctx.queryABCI(abci.RequestQuery{ Path: path, Data: key, @@ -140,7 +139,7 @@ func (ctx ContextG[C]) query(path string, key tmbytes.HexBytes) ([]byte, int64, // queryStore performs a query to a Tendermint node with the provided a store // name and path. It returns the result and height of the query upon success // or an error if the query fails. -func (ctx ContextG[C]) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { +func (ctx Context) queryStore(key tmbytes.HexBytes, storeName, endPath string) ([]byte, int64, error) { path := fmt.Sprintf("/store/%s/%s", storeName, endPath) return ctx.query(path, key) } From ad1280df76ba9b6532a11bdc87b77e5b308b5d23 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 5 May 2026 14:49:27 +0200 Subject: [PATCH 24/33] compilation fix --- sei-cosmos/x/gov/client/utils/query_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sei-cosmos/x/gov/client/utils/query_test.go b/sei-cosmos/x/gov/client/utils/query_test.go index fe2a981e78..e72e23457b 100644 --- a/sei-cosmos/x/gov/client/utils/query_test.go +++ b/sei-cosmos/x/gov/client/utils/query_test.go @@ -164,9 +164,10 @@ func TestGetPaginatedVotes(t *testing.T) { t.Run(tc.description, func(t *testing.T) { var marshalled = make([]tmtypes.Tx, len(tc.msgs)) cli := TxSearchMock{txs: marshalled, txConfig: encCfg.TxConfig} - clientCtx := client.WithClient(client.Context{}. + clientCtx := client.Context{}. WithLegacyAmino(encCfg.Amino). - WithTxConfig(encCfg.TxConfig), cli).Any() + WithClient(cli). + WithTxConfig(encCfg.TxConfig) for i := range tc.msgs { txBuilder := clientCtx.TxConfig.NewTxBuilder() From 4d86336e0d3a085513758c280137e40c10609b2b Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 5 May 2026 15:00:48 +0200 Subject: [PATCH 25/33] reduced boiledplate --- sei-tendermint/rpc/client/local/local.go | 84 +++++++----------------- 1 file changed, 24 insertions(+), 60 deletions(-) diff --git a/sei-tendermint/rpc/client/local/local.go b/sei-tendermint/rpc/client/local/local.go index c4981c926f..4357e44575 100644 --- a/sei-tendermint/rpc/client/local/local.go +++ b/sei-tendermint/rpc/client/local/local.go @@ -42,7 +42,7 @@ backoff (10ms -> 20ms -> 40ms) until successful. */ type Local struct { *eventbus.EventBus - env *rpccore.Environment + *rpccore.Environment } // Marker of the local node @@ -65,130 +65,94 @@ func New(node NodeService) (*Local, error) { } return &Local{ EventBus: node.EventBus(), - env: env, + 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}) -} - -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) + return c.Environment.CheckTx(ctx, &coretypes.RequestCheckTx{Tx: tx}) } 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), @@ -196,11 +160,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), @@ -210,7 +174,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), @@ -219,7 +183,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) { From f8d4d592d33fe36ba38ac2c54319c4afefa95915 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 5 May 2026 21:12:48 +0200 Subject: [PATCH 26/33] EvmNextPendingNonce --- app/abci.go | 5 +++++ evmrpc/block_txcount_parity_test.go | 4 ++++ evmrpc/height_availability_test.go | 4 ++++ evmrpc/setup_test.go | 4 ++++ evmrpc/simulate_test.go | 4 ++++ evmrpc/tx.go | 6 +++--- evmrpc/watermark_manager_test.go | 4 ++++ sei-cosmos/baseapp/baseapp.go | 5 +++++ sei-cosmos/client/context.go | 2 ++ sei-cosmos/server/rollback_test.go | 5 +++++ sei-tendermint/abci/types/application.go | 7 +++++++ sei-tendermint/abci/types/mocks/application.go | 15 +++++++++++++++ sei-tendermint/internal/mempool/mempool.go | 5 +++++ sei-tendermint/internal/proxy/proxy.go | 6 ++++++ sei-tendermint/rpc/client/local/local.go | 7 ++++++- 15 files changed, 79 insertions(+), 4 deletions(-) diff --git a/app/abci.go b/app/abci.go index b54678d8bc..d1b49aee24 100644 --- a/app/abci.go +++ b/app/abci.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "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" @@ -110,6 +111,10 @@ func (app *App) CheckTx(ctx context.Context, req *abci.RequestCheckTxV2) (*abci. return res, nil } +func (app *App) EvmNextPendingNonce(addr common.Address) uint64 { + return app.EvmKeeper.CalculateNextNonce(app.GetCheckCtx(), addr, true) +} + 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/evmrpc/block_txcount_parity_test.go b/evmrpc/block_txcount_parity_test.go index ac86875a5b..73d340fca8 100644 --- a/evmrpc/block_txcount_parity_test.go +++ b/evmrpc/block_txcount_parity_test.go @@ -35,6 +35,10 @@ type parityTxCountTMClient struct { func (*parityTxCountTMClient) IsLocal() {} +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/height_availability_test.go b/evmrpc/height_availability_test.go index f340fc2735..c72c010ff7 100644 --- a/evmrpc/height_availability_test.go +++ b/evmrpc/height_availability_test.go @@ -31,6 +31,10 @@ type heightTestClient struct { func (*heightTestClient) IsLocal() {} +func (*heightTestClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func newHeightTestClient(highHeight, earliest, latest int64) *heightTestClient { return &heightTestClient{ Client: mock.Client{}, diff --git a/evmrpc/setup_test.go b/evmrpc/setup_test.go index eec7896b84..2be833928a 100644 --- a/evmrpc/setup_test.go +++ b/evmrpc/setup_test.go @@ -121,6 +121,10 @@ type MockClient struct { func (*MockClient) IsLocal() {} +func (*MockClient) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func NewMockClientWithLatest(latest int64) *MockClient { return &MockClient{latestOverride: latest} } diff --git a/evmrpc/simulate_test.go b/evmrpc/simulate_test.go index 32eb9502d0..ba4758e07f 100644 --- a/evmrpc/simulate_test.go +++ b/evmrpc/simulate_test.go @@ -869,6 +869,10 @@ type fixedBlockClient struct { func (c *fixedBlockClient) IsLocal() {} +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/tx.go b/evmrpc/tx.go index cf10a4740d..e5e09f257d 100644 --- a/evmrpc/tx.go +++ b/evmrpc/tx.go @@ -321,9 +321,9 @@ func (t *TransactionAPI) GetTransactionCount(ctx context.Context, address common recordMetricsWithError("eth_getTransactionCount", t.connectionType, startTime, _err) }() - 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) @@ -334,7 +334,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.CalculateNextNonce(sdkCtx, address, false) return (*hexutil.Uint64)(&nonce), nil } diff --git a/evmrpc/watermark_manager_test.go b/evmrpc/watermark_manager_test.go index 7ac5082110..5b44466163 100644 --- a/evmrpc/watermark_manager_test.go +++ b/evmrpc/watermark_manager_test.go @@ -164,6 +164,10 @@ type fakeTMClient struct { func (*fakeTMClient) IsLocal() {} +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..65ec6187f7 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -8,6 +8,7 @@ import ( "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 +80,10 @@ type ( DeliverTxHook func(sdk.Context, sdk.Tx, [32]byte, sdk.DeliverTxHookInput) ) +func (app *BaseApp) EvmNextPendingNonce(_ common.Address) uint64 { + return 0 +} + // BaseApp reflects the ABCI application implementation. type BaseApp struct { // initialized on creation diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index a231152271..11b9f7cc1a 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -7,6 +7,7 @@ import ( "io" "os" + "github.com/ethereum/go-ethereum/common" "github.com/spf13/viper" "gopkg.in/yaml.v2" @@ -25,6 +26,7 @@ type Client = rpcclient.Client type LocalClient interface { Client IsLocal() + EvmNextPendingNonce(addr common.Address) uint64 } type Context struct { diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index 2308958526..2320b5716d 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" "github.com/gogo/protobuf/grpc" "github.com/golang/protobuf/proto" "github.com/google/orderedcode" @@ -74,6 +75,10 @@ func (m *mockApplication) GetTxPriorityHint(ctx context.Context, req *abci.Reque return &abci.ResponseGetTxPriorityHint{}, nil } +func (m *mockApplication) EvmNextPendingNonce(common.Address) uint64 { + return 0 +} + func (m *mockApplication) BeginBlock(ctx context.Context, req *abci.RequestBeginBlock) (*abci.ResponseBeginBlock, error) { return &abci.ResponseBeginBlock{}, nil } diff --git a/sei-tendermint/abci/types/application.go b/sei-tendermint/abci/types/application.go index 607bbe2934..e9bc82c7a4 100644 --- a/sei-tendermint/abci/types/application.go +++ b/sei-tendermint/abci/types/application.go @@ -2,6 +2,8 @@ package types import ( "context" + + "github.com/ethereum/go-ethereum/common" ) // Application is an interface that enables any finite, deterministic state machine @@ -17,6 +19,7 @@ type Application interface { // Mempool Connection CheckTx(context.Context, *RequestCheckTxV2) (*ResponseCheckTxV2, error) // Validate a tx for the mempool GetTxPriorityHint(context.Context, *RequestGetTxPriorityHintV2) (*ResponseGetTxPriorityHint, error) // Get tx priority before checkTx + EvmNextPendingNonce(common.Address) uint64 // Consensus Connection InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) // Initialize blockchain w validators/other info from TendermintCore @@ -85,6 +88,10 @@ func (BaseApplication) GetTxPriorityHint(context.Context, *RequestGetTxPriorityH return &ResponseGetTxPriorityHint{}, nil } +func (BaseApplication) EvmNextPendingNonce(common.Address) uint64 { + return 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 315cd6f1f6..886b04f501 100644 --- a/sei-tendermint/abci/types/mocks/application.go +++ b/sei-tendermint/abci/types/mocks/application.go @@ -5,6 +5,7 @@ package mocks import ( context "context" + common "github.com/ethereum/go-ethereum/common" types "github.com/sei-protocol/sei-chain/sei-tendermint/abci/types" mock "github.com/stretchr/testify/mock" ) @@ -74,6 +75,20 @@ func (_m *Application) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTxV2) return r0, r1 } +// EvmNextPendingNonce provides a mock function with given fields: _a0 +func (_m *Application) EvmNextPendingNonce(_a0 common.Address) uint64 { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for EvmNextPendingNonce") + } + + if rf, ok := ret.Get(0).(func(common.Address) uint64); ok { + return rf(_a0) + } + return ret.Get(0).(uint64) +} + // Commit provides a mock function with given fields: _a0 func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error) { ret := _m.Called(_a0) diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 34dbed5015..8b48f7f0be 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -11,6 +11,7 @@ import ( "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" @@ -273,6 +274,10 @@ 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 { + return txmp.app.EvmNextPendingNonce(addr) +} + func (txmp *TxMempool) TxStore() *TxStore { return txmp.txStore } // Lock obtains a write-lock on the mempool. A caller must be sure to explicitly diff --git a/sei-tendermint/internal/proxy/proxy.go b/sei-tendermint/internal/proxy/proxy.go index 65dbdf8a14..2cdf479e1d 100644 --- a/sei-tendermint/internal/proxy/proxy.go +++ b/sei-tendermint/internal/proxy/proxy.go @@ -6,6 +6,7 @@ import ( "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 +43,11 @@ func (app *Proxy) GetTxPriorityHint(ctx context.Context, req *types.RequestGetTx return app.app.GetTxPriorityHint(ctx, req) } +func (app *Proxy) EvmNextPendingNonce(addr common.Address) uint64 { + defer addTimeSample(app.metrics.MethodTiming.With("method", "evm_next_pending_nonce", "type", "sync"))() + return app.app.EvmNextPendingNonce(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/rpc/client/local/local.go b/sei-tendermint/rpc/client/local/local.go index 4357e44575..79330832da 100644 --- a/sei-tendermint/rpc/client/local/local.go +++ b/sei-tendermint/rpc/client/local/local.go @@ -6,6 +6,7 @@ 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" @@ -64,7 +65,7 @@ func New(node NodeService) (*Local, error) { return nil, errors.New("rpc is nil") } return &Local{ - EventBus: node.EventBus(), + EventBus: node.EventBus(), Environment: env, }, nil } @@ -108,6 +109,10 @@ func (c *Local) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultChec return c.Environment.CheckTx(ctx, &coretypes.RequestCheckTx{Tx: tx}) } +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.Environment.GetConsensusState(ctx) } From f455ccf94af83ecc0271cc2c6ba543024c65e6fe Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Tue, 5 May 2026 22:27:53 +0200 Subject: [PATCH 27/33] duplicated evm nonce management --- app/abci.go | 4 + sei-cosmos/baseapp/baseapp.go | 4 + sei-cosmos/server/rollback_test.go | 4 + sei-tendermint/abci/types/application.go | 5 + .../abci/types/mocks/application.go | 14 ++ sei-tendermint/internal/mempool/mempool.go | 139 +++++++++++++++++- .../internal/mempool/recheck_drain_test.go | 49 ++++++ sei-tendermint/internal/proxy/proxy.go | 5 + 8 files changed, 223 insertions(+), 1 deletion(-) diff --git a/app/abci.go b/app/abci.go index d1b49aee24..8319c78056 100644 --- a/app/abci.go +++ b/app/abci.go @@ -115,6 +115,10 @@ func (app *App) EvmNextPendingNonce(addr common.Address) uint64 { return app.EvmKeeper.CalculateNextNonce(app.GetCheckCtx(), addr, true) } +func (app *App) EvmNonce(addr common.Address) uint64 { + return app.EvmKeeper.GetNonce(app.GetCheckCtx(), 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/sei-cosmos/baseapp/baseapp.go b/sei-cosmos/baseapp/baseapp.go index 65ec6187f7..ce7c8f823d 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -80,6 +80,10 @@ type ( DeliverTxHook func(sdk.Context, sdk.Tx, [32]byte, sdk.DeliverTxHookInput) ) +func (app *BaseApp) EvmNonce(_ common.Address) uint64 { + return 0 +} + func (app *BaseApp) EvmNextPendingNonce(_ common.Address) uint64 { return 0 } diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index 2320b5716d..cd737fa520 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -75,6 +75,10 @@ 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) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/sei-tendermint/abci/types/application.go b/sei-tendermint/abci/types/application.go index e9bc82c7a4..9395e9c529 100644 --- a/sei-tendermint/abci/types/application.go +++ b/sei-tendermint/abci/types/application.go @@ -19,6 +19,7 @@ type Application interface { // Mempool Connection CheckTx(context.Context, *RequestCheckTxV2) (*ResponseCheckTxV2, error) // Validate a tx for the mempool GetTxPriorityHint(context.Context, *RequestGetTxPriorityHintV2) (*ResponseGetTxPriorityHint, error) // Get tx priority before checkTx + EvmNonce(common.Address) uint64 EvmNextPendingNonce(common.Address) uint64 // Consensus Connection @@ -88,6 +89,10 @@ func (BaseApplication) GetTxPriorityHint(context.Context, *RequestGetTxPriorityH return &ResponseGetTxPriorityHint{}, nil } +func (BaseApplication) EvmNonce(common.Address) uint64 { + return 0 +} + func (BaseApplication) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/sei-tendermint/abci/types/mocks/application.go b/sei-tendermint/abci/types/mocks/application.go index 886b04f501..f4f1e803c4 100644 --- a/sei-tendermint/abci/types/mocks/application.go +++ b/sei-tendermint/abci/types/mocks/application.go @@ -89,6 +89,20 @@ func (_m *Application) EvmNextPendingNonce(_a0 common.Address) uint64 { return ret.Get(0).(uint64) } +// 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") + } + + if rf, ok := ret.Get(0).(func(common.Address) uint64); ok { + return rf(_a0) + } + return ret.Get(0).(uint64) +} + // Commit provides a mock function with given fields: _a0 func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error) { ret := _m.Called(_a0) diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 8b48f7f0be..eea97d0df1 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -6,6 +6,8 @@ import ( "crypto/sha256" "errors" "fmt" + "slices" + "sort" "strings" "sync" "sync/atomic" @@ -161,6 +163,17 @@ func DefaultConfig() *Config { } } +type evmAddressNoncePair struct { + Address string + Nonce uint64 +} + +type evmPendingTx struct { + Hash types.TxHash + Nonce uint64 + Priority int64 +} + // 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 @@ -224,6 +237,10 @@ type TxMempool struct { // if its checker returns Accepted pendingTxs *PendingTxs + nonceMx sync.Mutex + hashToNonce map[types.TxHash]*evmAddressNoncePair + pendingEVM map[string][]*evmPendingTx + // 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 @@ -254,6 +271,8 @@ func NewTxMempool( priorityIndex: NewTxPriorityQueue(), expirationIndex: NewWrappedTxList(), pendingTxs: NewPendingTxs(cfg), + hashToNonce: map[types.TxHash]*evmAddressNoncePair{}, + pendingEVM: map[string][]*evmPendingTx{}, txConstraintsFetcher: txConstraintsFetcher, priorityReservoir: reservoir.New[int64](cfg.DropPriorityReservoirSize, cfg.DropPriorityThreshold, nil), // Use non-deterministic RNG } @@ -275,7 +294,107 @@ 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 { - return txmp.app.EvmNextPendingNonce(addr) + txmp.nonceMx.Lock() + defer txmp.nonceMx.Unlock() + + nextNonce := txmp.app.EvmNonce(addr) + pending := txmp.pendingEVM[addr.Hex()] + for ; ; nextNonce++ { + if _, found := sort.Find(len(pending), func(i int) int { + switch { + case nextNonce < pending[i].Nonce: + return -1 + case nextNonce > pending[i].Nonce: + return 1 + default: + return 0 + } + }); !found { + return nextNonce + } + } +} + +func (txmp *TxMempool) addTrackedPendingNonce(hash types.TxHash, addr string, nonce uint64, priority int64) { + txmp.nonceMx.Lock() + defer txmp.nonceMx.Unlock() + + if existing, ok := txmp.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, existing.Address) + } + return + } + + for _, pendingTx := range txmp.pendingEVM[addr] { + if pendingTx.Nonce == nonce { + if priority > pendingTx.Priority { + delete(txmp.hashToNonce, pendingTx.Hash) + pendingTx.Priority = priority + pendingTx.Hash = hash + txmp.hashToNonce[hash] = &evmAddressNoncePair{ + Address: addr, + Nonce: nonce, + } + } + return + } + } + + txmp.hashToNonce[hash] = &evmAddressNoncePair{ + Address: addr, + Nonce: nonce, + } + txmp.pendingEVM[addr] = append(txmp.pendingEVM[addr], &evmPendingTx{ + Hash: hash, + Nonce: nonce, + Priority: priority, + }) + slices.SortStableFunc(txmp.pendingEVM[addr], func(a, b *evmPendingTx) int { + switch { + case a.Nonce < b.Nonce: + return -1 + case a.Nonce > b.Nonce: + return 1 + default: + return 0 + } + }) +} + +func (txmp *TxMempool) removeTrackedPendingNonce(hash types.TxHash) { + txmp.nonceMx.Lock() + defer txmp.nonceMx.Unlock() + + tx, ok := txmp.hashToNonce[hash] + if !ok { + return + } + delete(txmp.hashToNonce, hash) + + addr := tx.Address + pendings := txmp.pendingEVM[addr] + firstMatch, found := sort.Find(len(pendings), func(i int) int { + switch { + case tx.Nonce < pendings[i].Nonce: + return -1 + case tx.Nonce > pendings[i].Nonce: + return 1 + default: + return 0 + } + }) + if !found { + fmt.Printf("Removing tx %X without a corresponding pending nonce, which should not happen\n", hash) + return + } + txmp.pendingEVM[addr] = append(pendings[:firstMatch], pendings[firstMatch+1:]...) + if len(txmp.pendingEVM[addr]) == 0 { + delete(txmp.pendingEVM, addr) + } } func (txmp *TxMempool) TxStore() *TxStore { return txmp.txStore } @@ -473,6 +592,9 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) if err != nil { return nil, err } + if wtx.isEVM { + txmp.addTrackedPendingNonce(wtx.Hash(), wtx.evmAddress, wtx.evmNonce, res.Priority) + } } else { // otherwise add to pending txs store if err := txmp.canAddPendingTx(wtx); err != nil { @@ -484,6 +606,9 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) wtx.removeHandler(true) return nil, err } + if wtx.isEVM { + txmp.addTrackedPendingNonce(wtx.Hash(), wtx.evmAddress, wtx.evmNonce, res.Priority) + } } if res.CheckTxCallback != nil { @@ -1149,6 +1274,9 @@ func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool, shouldReen return } + if wtx.isEVM { + txmp.removeTrackedPendingNonce(wtx.Hash()) + } txmp.txStore.RemoveTx(wtx) toBeReenqueued := []*WrappedTx{} if updatePriorityIndex { @@ -1235,6 +1363,9 @@ func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) { // remove pending txs that have expired txmp.pendingTxs.PurgeExpired(blockHeight, now, func(wtx *WrappedTx) { + if wtx.isEVM { + txmp.removeTrackedPendingNonce(wtx.Hash()) + } txmp.expire(blockHeight, wtx) }) } @@ -1268,10 +1399,16 @@ func (txmp *TxMempool) handlePendingTransactions() { accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions() for _, tx := range accepted { if err := txmp.addNewTransaction(tx.tx, tx.checkTxResponse.ResponseCheckTx, tx.txInfo); err != nil { + if tx.tx.isEVM { + txmp.removeTrackedPendingNonce(tx.tx.Hash()) + } logger.Error("error adding pending transaction", "err", err) } } for _, tx := range rejected { + if tx.tx.isEVM { + txmp.removeTrackedPendingNonce(tx.tx.Hash()) + } if !txmp.config.KeepInvalidTxsInCache { tx.tx.removeHandler(true) } diff --git a/sei-tendermint/internal/mempool/recheck_drain_test.go b/sei-tendermint/internal/mempool/recheck_drain_test.go index f64caf6e83..f5d5e7ab18 100644 --- a/sei-tendermint/internal/mempool/recheck_drain_test.go +++ b/sei-tendermint/internal/mempool/recheck_drain_test.go @@ -8,11 +8,13 @@ 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: @@ -111,6 +113,12 @@ 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 @@ -175,3 +183,44 @@ 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") + require.Len(t, txmp.pendingEVM[sender.Hex()], 1, "nonce bookkeeping should track one occupied nonce") + require.Equal(t, types.Tx(highPriorityTx).Hash(), txmp.pendingEVM[sender.Hex()][0].Hash) + require.Equal(t, uint64(5), txmp.EvmNextPendingNonce(sender)) +} diff --git a/sei-tendermint/internal/proxy/proxy.go b/sei-tendermint/internal/proxy/proxy.go index 2cdf479e1d..ce947721fe 100644 --- a/sei-tendermint/internal/proxy/proxy.go +++ b/sei-tendermint/internal/proxy/proxy.go @@ -43,6 +43,11 @@ 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) EvmNextPendingNonce(addr common.Address) uint64 { defer addTimeSample(app.metrics.MethodTiming.With("method", "evm_next_pending_nonce", "type", "sync"))() return app.app.EvmNextPendingNonce(addr) From 7e861513a2aea1228d1b61113538ef902f0051d7 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 6 May 2026 16:46:03 +0200 Subject: [PATCH 28/33] rewritten nonce management --- sei-cosmos/client/context.go | 2 +- .../abci/types/mocks/application.go | 68 +++--- sei-tendermint/abci/types/types.go | 2 +- sei-tendermint/internal/mempool/mempool.go | 231 +++++------------- .../internal/mempool/priority_queue.go | 43 ++-- sei-tendermint/internal/mempool/tx.go | 2 +- 6 files changed, 122 insertions(+), 226 deletions(-) diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 11b9f7cc1a..1ef2d02ed2 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -25,7 +25,7 @@ import ( type Client = rpcclient.Client type LocalClient interface { Client - IsLocal() + EvmNonce(addr common.Address) uint64 EvmNextPendingNonce(addr common.Address) uint64 } diff --git a/sei-tendermint/abci/types/mocks/application.go b/sei-tendermint/abci/types/mocks/application.go index f4f1e803c4..20929ef72d 100644 --- a/sei-tendermint/abci/types/mocks/application.go +++ b/sei-tendermint/abci/types/mocks/application.go @@ -6,8 +6,10 @@ import ( context "context" 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 @@ -75,34 +77,6 @@ func (_m *Application) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTxV2) return r0, r1 } -// EvmNextPendingNonce provides a mock function with given fields: _a0 -func (_m *Application) EvmNextPendingNonce(_a0 common.Address) uint64 { - ret := _m.Called(_a0) - - if len(ret) == 0 { - panic("no return value specified for EvmNextPendingNonce") - } - - if rf, ok := ret.Get(0).(func(common.Address) uint64); ok { - return rf(_a0) - } - return ret.Get(0).(uint64) -} - -// 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") - } - - if rf, ok := ret.Get(0).(func(common.Address) uint64); ok { - return rf(_a0) - } - return ret.Get(0).(uint64) -} - // Commit provides a mock function with given fields: _a0 func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error) { ret := _m.Called(_a0) @@ -133,6 +107,42 @@ func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error return r0, r1 } +// EvmNextPendingNonce provides a mock function with given fields: _a0 +func (_m *Application) EvmNextPendingNonce(_a0 common.Address) uint64 { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for EvmNextPendingNonce") + } + + 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 +} + +// 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 +} + // 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 563ff6dd01..49b26e070d 100644 --- a/sei-tendermint/abci/types/types.go +++ b/sei-tendermint/abci/types/types.go @@ -230,7 +230,7 @@ type ResponseCheckTxV2 struct { // helper properties for prioritization in mempool EVMNonce uint64 - EVMSenderAddress string + EVMSenderAddress string IsEVM bool Priority int64 } diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index eea97d0df1..06ca22e3f9 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -6,8 +6,6 @@ import ( "crypto/sha256" "errors" "fmt" - "slices" - "sort" "strings" "sync" "sync/atomic" @@ -163,17 +161,11 @@ func DefaultConfig() *Config { } } -type evmAddressNoncePair struct { - Address string +type evmAddrNonce struct { + Address common.Address Nonce uint64 } -type evmPendingTx struct { - Hash types.TxHash - Nonce uint64 - Priority int64 -} - // 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 @@ -237,9 +229,7 @@ type TxMempool struct { // if its checker returns Accepted pendingTxs *PendingTxs - nonceMx sync.Mutex - hashToNonce map[types.TxHash]*evmAddressNoncePair - pendingEVM map[string][]*evmPendingTx + 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, @@ -271,8 +261,7 @@ func NewTxMempool( priorityIndex: NewTxPriorityQueue(), expirationIndex: NewWrappedTxList(), pendingTxs: NewPendingTxs(cfg), - hashToNonce: map[types.TxHash]*evmAddressNoncePair{}, - pendingEVM: map[string][]*evmPendingTx{}, + byAddrNonce: utils.NewMutex(map[evmAddrNonce]*WrappedTx{}), txConstraintsFetcher: txConstraintsFetcher, priorityReservoir: reservoir.New[int64](cfg.DropPriorityReservoirSize, cfg.DropPriorityThreshold, nil), // Use non-deterministic RNG } @@ -294,106 +283,36 @@ 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 { - txmp.nonceMx.Lock() - defer txmp.nonceMx.Unlock() - - nextNonce := txmp.app.EvmNonce(addr) - pending := txmp.pendingEVM[addr.Hex()] - for ; ; nextNonce++ { - if _, found := sort.Find(len(pending), func(i int) int { - switch { - case nextNonce < pending[i].Nonce: - return -1 - case nextNonce > pending[i].Nonce: - return 1 - default: - return 0 + an := evmAddrNonce{addr,txmp.app.EvmNonce(addr)} + for byAddrNonce := range txmp.byAddrNonce.Lock() { + for { + if _,ok := byAddrNonce[an]; !ok { + break } - }); !found { - return nextNonce + an.Nonce += 1 } } + return an.Nonce } -func (txmp *TxMempool) addTrackedPendingNonce(hash types.TxHash, addr string, nonce uint64, priority int64) { - txmp.nonceMx.Lock() - defer txmp.nonceMx.Unlock() - - if existing, ok := txmp.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, existing.Address) - } - return - } - - for _, pendingTx := range txmp.pendingEVM[addr] { - if pendingTx.Nonce == nonce { - if priority > pendingTx.Priority { - delete(txmp.hashToNonce, pendingTx.Hash) - pendingTx.Priority = priority - pendingTx.Hash = hash - txmp.hashToNonce[hash] = &evmAddressNoncePair{ - Address: addr, - Nonce: nonce, - } - } +func (txmp *TxMempool) addNonce(wtx *WrappedTx) { + if !wtx.isEVM { return } + an := evmAddrNonce{common.HexToAddress(wtx.evmAddress),wtx.evmNonce} + for byAddrNonce := range txmp.byAddrNonce.Lock() { + if old,ok := byAddrNonce[an]; ok && old.priority >= wtx.priority { return } + byAddrNonce[an] = wtx } - - txmp.hashToNonce[hash] = &evmAddressNoncePair{ - Address: addr, - Nonce: nonce, - } - txmp.pendingEVM[addr] = append(txmp.pendingEVM[addr], &evmPendingTx{ - Hash: hash, - Nonce: nonce, - Priority: priority, - }) - slices.SortStableFunc(txmp.pendingEVM[addr], func(a, b *evmPendingTx) int { - switch { - case a.Nonce < b.Nonce: - return -1 - case a.Nonce > b.Nonce: - return 1 - default: - return 0 - } - }) } -func (txmp *TxMempool) removeTrackedPendingNonce(hash types.TxHash) { - txmp.nonceMx.Lock() - defer txmp.nonceMx.Unlock() - - tx, ok := txmp.hashToNonce[hash] - if !ok { - return - } - delete(txmp.hashToNonce, hash) - - addr := tx.Address - pendings := txmp.pendingEVM[addr] - firstMatch, found := sort.Find(len(pendings), func(i int) int { - switch { - case tx.Nonce < pendings[i].Nonce: - return -1 - case tx.Nonce > pendings[i].Nonce: - return 1 - default: - return 0 +func (txmp *TxMempool) removeNonce(wtx *WrappedTx) { + if !wtx.isEVM { return } + an := evmAddrNonce{common.HexToAddress(wtx.evmAddress),wtx.evmNonce} + for byAddrNonce := range txmp.byAddrNonce.Lock() { + if byAddrNonce[an] == wtx { + delete(byAddrNonce,an) } - }) - if !found { - fmt.Printf("Removing tx %X without a corresponding pending nonce, which should not happen\n", hash) - return - } - txmp.pendingEVM[addr] = append(pendings[:firstMatch], pendings[firstMatch+1:]...) - if len(txmp.pendingEVM[addr]) == 0 { - delete(txmp.pendingEVM, addr) } } @@ -449,7 +368,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 @@ -458,11 +377,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 @@ -560,6 +479,7 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) evmNonce: res.EVMNonce, evmAddress: res.EVMSenderAddress, isEVM: res.IsEVM, + priority: res.Priority, removeHandler: func(removeFromCache bool) { if removeFromCache { txmp.cache.Remove(txHash) @@ -569,32 +489,15 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) } }, estimatedGas: res.GasEstimated, + gasWanted: res.GasWanted, + peers: map[uint16]struct{}{txInfo.SenderID: {}}, } // 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 !res.IsPending.IsPresent() { + if err := txmp.addNewTransaction(wtx); err != nil { return nil, err } - if wtx.isEVM { - txmp.addTrackedPendingNonce(wtx.Hash(), wtx.evmAddress, wtx.evmNonce, res.Priority) - } } else { // otherwise add to pending txs store if err := txmp.canAddPendingTx(wtx); err != nil { @@ -606,14 +509,8 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) wtx.removeHandler(true) return nil, err } - if wtx.isEVM { - txmp.addTrackedPendingNonce(wtx.Hash(), wtx.evmAddress, wtx.evmNonce, res.Priority) - } - } - - if res.CheckTxCallback != nil { - res.CheckTxCallback(res.Priority) } + txmp.addNonce(wtx) return res.ResponseCheckTx, nil } @@ -988,32 +885,37 @@ 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) + if err := txmp.checkResponseState(wtx); 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) - 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, @@ -1047,14 +949,7 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, res *abci.ResponseCheck ) txmp.metrics.EvictedTxs.Add(1) } - } - - 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 @@ -1121,7 +1016,7 @@ 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) // 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() { @@ -1274,9 +1169,7 @@ func (txmp *TxMempool) removeTx(wtx *WrappedTx, removeFromCache bool, shouldReen return } - if wtx.isEVM { - txmp.removeTrackedPendingNonce(wtx.Hash()) - } + txmp.removeNonce(wtx) txmp.txStore.RemoveTx(wtx) toBeReenqueued := []*WrappedTx{} if updatePriorityIndex { @@ -1363,9 +1256,7 @@ func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) { // remove pending txs that have expired txmp.pendingTxs.PurgeExpired(blockHeight, now, func(wtx *WrappedTx) { - if wtx.isEVM { - txmp.removeTrackedPendingNonce(wtx.Hash()) - } + txmp.removeNonce(wtx) txmp.expire(blockHeight, wtx) }) } @@ -1398,17 +1289,13 @@ func (txmp *TxMempool) AppendCheckTxErr(existingLogs string, log string) string func (txmp *TxMempool) handlePendingTransactions() { accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions() for _, tx := range accepted { - if err := txmp.addNewTransaction(tx.tx, tx.checkTxResponse.ResponseCheckTx, tx.txInfo); err != nil { - if tx.tx.isEVM { - txmp.removeTrackedPendingNonce(tx.tx.Hash()) - } + if err := txmp.addNewTransaction(tx.tx); err != nil { + txmp.removeNonce(tx.tx) logger.Error("error adding pending transaction", "err", err) } } for _, tx := range rejected { - if tx.tx.isEVM { - txmp.removeTrackedPendingNonce(tx.tx.Hash()) - } + txmp.removeNonce(tx.tx) if !txmp.config.KeepInvalidTxsInCache { tx.tx.removeHandler(true) } diff --git a/sei-tendermint/internal/mempool/priority_queue.go b/sei-tendermint/internal/mempool/priority_queue.go index 4ebb395d28..acc3c3ee84 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 { @@ -75,25 +75,26 @@ func (pq *TxPriorityQueue) tryReplacementUnsafe(tx *WrappedTx) (replaced *Wrappe if !tx.isEVM { 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[tx.evmAddress] + if len(queue) == 0 { + return nil,false } - return nil, false + existing, idx := pq.getTxWithSameNonceUnsafe(tx) + 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 + } + pq.evmQueue[tx.evmAddress][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 +106,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:]...) } diff --git a/sei-tendermint/internal/mempool/tx.go b/sei-tendermint/internal/mempool/tx.go index 08ae0d4b27..7b545389f0 100644 --- a/sei-tendermint/internal/mempool/tx.go +++ b/sei-tendermint/internal/mempool/tx.go @@ -80,7 +80,7 @@ type WrappedTx struct { removeHandler func(removeFromCache bool) // evm properties that aid in prioritization - evmAddress string + evmAddress string // hex encoded address evmNonce uint64 isEVM bool } From 661dada44c29848fb8eb29ba9b3c008d4eb4c26f Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 6 May 2026 16:52:37 +0200 Subject: [PATCH 29/33] codex fixes --- sei-tendermint/internal/mempool/mempool.go | 52 +++++++++++-------- .../internal/mempool/recheck_drain_test.go | 7 ++- sei-tendermint/internal/mempool/tx.go | 5 +- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 06ca22e3f9..74c4a5a81c 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -283,11 +283,11 @@ 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)} + an := evmAddrNonce{addr, txmp.app.EvmNonce(addr)} for byAddrNonce := range txmp.byAddrNonce.Lock() { for { - if _,ok := byAddrNonce[an]; !ok { - break + if _, ok := byAddrNonce[an]; !ok { + break } an.Nonce += 1 } @@ -296,10 +296,12 @@ func (txmp *TxMempool) EvmNextPendingNonce(addr common.Address) uint64 { } func (txmp *TxMempool) addNonce(wtx *WrappedTx) { - if !wtx.isEVM { return } - an := evmAddrNonce{common.HexToAddress(wtx.evmAddress),wtx.evmNonce} + if !wtx.isEVM { + return + } + an := evmAddrNonce{common.HexToAddress(wtx.evmAddress), wtx.evmNonce} for byAddrNonce := range txmp.byAddrNonce.Lock() { - if old,ok := byAddrNonce[an]; ok && old.priority >= wtx.priority { + if old, ok := byAddrNonce[an]; ok && old.priority >= wtx.priority { return } byAddrNonce[an] = wtx @@ -307,11 +309,13 @@ func (txmp *TxMempool) addNonce(wtx *WrappedTx) { } func (txmp *TxMempool) removeNonce(wtx *WrappedTx) { - if !wtx.isEVM { return } - an := evmAddrNonce{common.HexToAddress(wtx.evmAddress),wtx.evmNonce} + if !wtx.isEVM { + return + } + an := evmAddrNonce{common.HexToAddress(wtx.evmAddress), wtx.evmNonce} for byAddrNonce := range txmp.byAddrNonce.Lock() { if byAddrNonce[an] == wtx { - delete(byAddrNonce,an) + delete(byAddrNonce, an) } } } @@ -473,13 +477,14 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) txmp.metrics.observeCheckTxPriorityDistribution(res.Priority, false, txInfo.SenderNodeID, nil) wtx := &WrappedTx{ - hashedTx: newHashedTx(tx), - timestamp: time.Now().UTC(), - height: txmp.height, - evmNonce: res.EVMNonce, - evmAddress: res.EVMSenderAddress, - isEVM: res.IsEVM, - priority: res.Priority, + hashedTx: newHashedTx(tx), + timestamp: time.Now().UTC(), + height: txmp.height, + evmNonce: res.EVMNonce, + evmAddress: res.EVMSenderAddress, + isEVM: res.IsEVM, + priority: res.Priority, + checkTxCode: res.Code, removeHandler: func(removeFromCache bool) { if removeFromCache { txmp.cache.Remove(txHash) @@ -489,12 +494,12 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) } }, estimatedGas: res.GasEstimated, - gasWanted: res.GasWanted, - peers: map[uint16]struct{}{txInfo.SenderID: {}}, + gasWanted: res.GasWanted, + peers: map[uint16]struct{}{txInfo.SenderID: {}}, } // only add new transaction if checkTx passes and is not pending - if !res.IsPending.IsPresent() { + if !res.IsPending.IsPresent() { if err := txmp.addNewTransaction(wtx); err != nil { return nil, err } @@ -510,6 +515,9 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) return nil, err } } + if res.CheckTxCallback != nil { + res.CheckTxCallback(res.Priority) + } txmp.addNonce(wtx) return res.ResponseCheckTx, nil } @@ -901,12 +909,14 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx) error { // inaccurate. The true priority as determined by the application is the // most accurate. txmp.priorityReservoir.Add(wtx.priority) - if err := txmp.checkResponseState(wtx); err != nil { + err := txmp.checkResponseState(wtx) + if err != nil || wtx.checkTxCode != abci.CodeTypeOK { // ignore bad transactions logger.Info( "rejected bad transaction", "priority", wtx.priority, "tx", wtx.Hash(), + "code", wtx.checkTxCode, "post_check_err", err, ) txmp.metrics.FailedTxs.Add(1) @@ -949,7 +959,7 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx) error { ) txmp.metrics.EvictedTxs.Add(1) } - } + } if txmp.isInMempool(wtx.Hash()) { return nil diff --git a/sei-tendermint/internal/mempool/recheck_drain_test.go b/sei-tendermint/internal/mempool/recheck_drain_test.go index f5d5e7ab18..c4e9e7eba2 100644 --- a/sei-tendermint/internal/mempool/recheck_drain_test.go +++ b/sei-tendermint/internal/mempool/recheck_drain_test.go @@ -220,7 +220,10 @@ func TestTxMempool_EvmNextPendingNonceReplacesSameNonceByPriority(t *testing.T) require.NoError(t, err) require.Equal(t, 2, txmp.PendingSize(), "pending store keeps both txs") - require.Len(t, txmp.pendingEVM[sender.Hex()], 1, "nonce bookkeeping should track one occupied nonce") - require.Equal(t, types.Tx(highPriorityTx).Hash(), txmp.pendingEVM[sender.Hex()][0].Hash) + 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 7b545389f0..39adf525a2 100644 --- a/sei-tendermint/internal/mempool/tx.go +++ b/sei-tendermint/internal/mempool/tx.go @@ -56,6 +56,9 @@ type WrappedTx struct { // in the ResponseCheckTx response. priority int64 + // checkTxCode captures the ABCI CheckTx result code for initial insertion. + checkTxCode uint32 + // timestamp is the time at which the node first received the transaction from // a peer. It is used as a second dimension is prioritizing transactions when // two transactions have the same priority. @@ -80,7 +83,7 @@ type WrappedTx struct { removeHandler func(removeFromCache bool) // evm properties that aid in prioritization - evmAddress string // hex encoded address + evmAddress string // hex encoded address evmNonce uint64 isEVM bool } From c342b5c719249c89ef94747a7c9dbeb96f52eaa2 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 6 May 2026 16:58:43 +0200 Subject: [PATCH 30/33] codex --- sei-tendermint/internal/mempool/mempool.go | 24 ++++++++++------------ sei-tendermint/internal/mempool/tx.go | 3 --- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 0d36d90dbb..589349171d 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -481,14 +481,13 @@ 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, - priority: res.Priority, - checkTxCode: res.Code, + hashedTx: newHashedTx(tx), + timestamp: time.Now().UTC(), + height: txmp.height, + evmNonce: res.EVMNonce, + evmAddress: res.EVMSenderAddress, + isEVM: res.IsEVM, + priority: res.Priority, removeHandler: func(removeFromCache bool) { if removeFromCache { txmp.cache.Remove(txHash) @@ -885,9 +884,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. @@ -914,13 +913,12 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx) error { // most accurate. txmp.priorityReservoir.Add(wtx.priority) err := txmp.checkResponseState(wtx) - if err != nil || wtx.checkTxCode != abci.CodeTypeOK { + if err != nil { // ignore bad transactions logger.Info( "rejected bad transaction", "priority", wtx.priority, "tx", wtx.Hash(), - "code", wtx.checkTxCode, "post_check_err", err, ) txmp.metrics.FailedTxs.Add(1) diff --git a/sei-tendermint/internal/mempool/tx.go b/sei-tendermint/internal/mempool/tx.go index 39adf525a2..b70cb5d780 100644 --- a/sei-tendermint/internal/mempool/tx.go +++ b/sei-tendermint/internal/mempool/tx.go @@ -56,9 +56,6 @@ type WrappedTx struct { // in the ResponseCheckTx response. priority int64 - // checkTxCode captures the ABCI CheckTx result code for initial insertion. - checkTxCode uint32 - // timestamp is the time at which the node first received the transaction from // a peer. It is used as a second dimension is prioritizing transactions when // two transactions have the same priority. From 51981c000425a00ac95919fd5ea55cc548180959 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 6 May 2026 17:10:36 +0200 Subject: [PATCH 31/33] codex --- evmrpc/block_txcount_parity_test.go | 2 -- evmrpc/height_availability_test.go | 2 -- evmrpc/setup_test.go | 2 -- evmrpc/simulate_test.go | 2 -- evmrpc/tests/mock_client.go | 5 ++++- evmrpc/watermark_manager_test.go | 2 -- sei-cosmos/client/context.go | 1 - 7 files changed, 4 insertions(+), 12 deletions(-) diff --git a/evmrpc/block_txcount_parity_test.go b/evmrpc/block_txcount_parity_test.go index 73d340fca8..cfe3ae1d9d 100644 --- a/evmrpc/block_txcount_parity_test.go +++ b/evmrpc/block_txcount_parity_test.go @@ -33,8 +33,6 @@ type parityTxCountTMClient struct { block *coretypes.ResultBlock } -func (*parityTxCountTMClient) IsLocal() {} - func (*parityTxCountTMClient) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/evmrpc/height_availability_test.go b/evmrpc/height_availability_test.go index c72c010ff7..6b25334029 100644 --- a/evmrpc/height_availability_test.go +++ b/evmrpc/height_availability_test.go @@ -29,8 +29,6 @@ type heightTestClient struct { latest int64 } -func (*heightTestClient) IsLocal() {} - func (*heightTestClient) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/evmrpc/setup_test.go b/evmrpc/setup_test.go index 2be833928a..c611f8945a 100644 --- a/evmrpc/setup_test.go +++ b/evmrpc/setup_test.go @@ -119,8 +119,6 @@ type MockClient struct { latestOverride int64 } -func (*MockClient) IsLocal() {} - func (*MockClient) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/evmrpc/simulate_test.go b/evmrpc/simulate_test.go index ba4758e07f..d7674b103e 100644 --- a/evmrpc/simulate_test.go +++ b/evmrpc/simulate_test.go @@ -867,8 +867,6 @@ type fixedBlockClient struct { block *coretypes.ResultBlock } -func (c *fixedBlockClient) IsLocal() {} - func (c *fixedBlockClient) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/evmrpc/tests/mock_client.go b/evmrpc/tests/mock_client.go index b1a7718fe6..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,7 +36,9 @@ type MockClient struct { mockedGenesis *coretypes.ResultGenesis } -func (c *MockClient) IsLocal() {} +func (c *MockClient) EvmNextPendingNonce(_ common.Address) uint64 { + return 0 +} func (c *MockClient) Block(_ context.Context, h *int64) (*coretypes.ResultBlock, error) { if c.mockedBlockResults != nil { diff --git a/evmrpc/watermark_manager_test.go b/evmrpc/watermark_manager_test.go index 5b44466163..6a9fbec102 100644 --- a/evmrpc/watermark_manager_test.go +++ b/evmrpc/watermark_manager_test.go @@ -162,8 +162,6 @@ type fakeTMClient struct { genesis *coretypes.ResultGenesis } -func (*fakeTMClient) IsLocal() {} - func (*fakeTMClient) EvmNextPendingNonce(common.Address) uint64 { return 0 } diff --git a/sei-cosmos/client/context.go b/sei-cosmos/client/context.go index 1ef2d02ed2..d16fecf3ba 100644 --- a/sei-cosmos/client/context.go +++ b/sei-cosmos/client/context.go @@ -25,7 +25,6 @@ import ( type Client = rpcclient.Client type LocalClient interface { Client - EvmNonce(addr common.Address) uint64 EvmNextPendingNonce(addr common.Address) uint64 } From c136a19c12ee964412148221a4fe14379cbf0be9 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 6 May 2026 17:53:16 +0200 Subject: [PATCH 32/33] removed callbacks --- app/abci.go | 19 +-- app/ante/evm_checktx.go | 57 +------ evmrpc/tx.go | 2 +- sei-cosmos/baseapp/baseapp.go | 5 +- sei-cosmos/server/rollback_test.go | 5 +- sei-cosmos/types/context.go | 48 ++---- sei-tendermint/abci/types/application.go | 7 +- .../abci/types/mocks/application.go | 21 +-- sei-tendermint/abci/types/types.go | 11 +- sei-tendermint/internal/mempool/mempool.go | 112 +++++++----- .../internal/mempool/mempool_test.go | 41 +---- .../internal/mempool/recheck_drain_test.go | 26 +-- sei-tendermint/internal/mempool/tx.go | 60 +++---- sei-tendermint/internal/mempool/tx_test.go | 26 +-- sei-tendermint/internal/proxy/proxy.go | 7 +- x/evm/ante/sig.go | 58 +------ x/evm/ante/sig_test.go | 19 +-- x/evm/keeper/keeper.go | 134 --------------- x/evm/keeper/keeper_test.go | 160 ------------------ 19 files changed, 181 insertions(+), 637 deletions(-) diff --git a/app/abci.go b/app/abci.go index e5f953d343..0cff26be1f 100644 --- a/app/abci.go +++ b/app/abci.go @@ -3,6 +3,7 @@ package app import ( "context" "crypto/sha256" + "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -13,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" ) @@ -105,30 +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()) + RequiredBalance: txCtx.RequiredBalance(), } return res } -func (app *App) EvmNextPendingNonce(addr common.Address) uint64 { - return app.EvmKeeper.CalculateNextNonce(app.GetCheckCtx(), addr, true) -} - 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..71a3cb0c2e 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.WithRequiredBalance(fee) return ctx, nil } diff --git a/evmrpc/tx.go b/evmrpc/tx.go index abec3e0f9e..b18c2f1074 100644 --- a/evmrpc/tx.go +++ b/evmrpc/tx.go @@ -335,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, false) + nonce := t.keeper.GetNonce(sdkCtx, address) return (*hexutil.Uint64)(&nonce), nil } diff --git a/sei-cosmos/baseapp/baseapp.go b/sei-cosmos/baseapp/baseapp.go index ce7c8f823d..eb78c00a8d 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -2,6 +2,7 @@ package baseapp import ( "fmt" + "math/big" "reflect" "strings" "sync" @@ -84,8 +85,8 @@ func (app *BaseApp) EvmNonce(_ common.Address) uint64 { return 0 } -func (app *BaseApp) EvmNextPendingNonce(_ common.Address) uint64 { - return 0 +func (app *BaseApp) EvmBalance(_ common.Address) *big.Int { + return big.NewInt(0) } // BaseApp reflects the ABCI application implementation. diff --git a/sei-cosmos/server/rollback_test.go b/sei-cosmos/server/rollback_test.go index 76d915c656..aa48fa4cf0 100644 --- a/sei-cosmos/server/rollback_test.go +++ b/sei-cosmos/server/rollback_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "math/big" "os" "path/filepath" "testing" @@ -79,8 +80,8 @@ func (m *mockApplication) EvmNonce(common.Address) uint64 { return 0 } -func (m *mockApplication) EvmNextPendingNonce(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) { diff --git a/sei-cosmos/types/context.go b/sei-cosmos/types/context.go index 569725fb8f..b61e43282f 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" @@ -43,12 +44,10 @@ type Context struct { 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 + 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. + requiredBalance *big.Int // Required sender balance for this 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) RequiredBalance() *big.Int { + if c.requiredBalance == nil { + return nil + } + return new(big.Int).Set(c.requiredBalance) +} + 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) WithRequiredBalance(requiredBalance *big.Int) Context { + if requiredBalance == nil { + c.requiredBalance = nil + return c + } + c.requiredBalance = new(big.Int).Set(requiredBalance) return c } diff --git a/sei-tendermint/abci/types/application.go b/sei-tendermint/abci/types/application.go index e9d2e0c0ab..c781663007 100644 --- a/sei-tendermint/abci/types/application.go +++ b/sei-tendermint/abci/types/application.go @@ -2,6 +2,7 @@ package types import ( "context" + "math/big" "github.com/ethereum/go-ethereum/common" ) @@ -20,7 +21,7 @@ type Application interface { 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 - EvmNextPendingNonce(common.Address) uint64 + EvmBalance(common.Address) *big.Int // Consensus Connection InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) // Initialize blockchain w validators/other info from TendermintCore @@ -93,8 +94,8 @@ func (BaseApplication) EvmNonce(common.Address) uint64 { return 0 } -func (BaseApplication) EvmNextPendingNonce(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) { diff --git a/sei-tendermint/abci/types/mocks/application.go b/sei-tendermint/abci/types/mocks/application.go index 00c9b76c85..7ce022f62f 100644 --- a/sei-tendermint/abci/types/mocks/application.go +++ b/sei-tendermint/abci/types/mocks/application.go @@ -4,6 +4,7 @@ package mocks import ( context "context" + "math/big" common "github.com/ethereum/go-ethereum/common" @@ -97,12 +98,12 @@ func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error return r0, r1 } -// EvmNextPendingNonce provides a mock function with given fields: _a0 -func (_m *Application) EvmNextPendingNonce(_a0 common.Address) uint64 { +// 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 EvmNextPendingNonce") + panic("no return value specified for EvmNonce") } var r0 uint64 @@ -115,19 +116,19 @@ func (_m *Application) EvmNextPendingNonce(_a0 common.Address) uint64 { return r0 } -// EvmNonce provides a mock function with given fields: _a0 -func (_m *Application) EvmNonce(_a0 common.Address) uint64 { +// 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 EvmNonce") + panic("no return value specified for EvmBalance") } - var r0 uint64 - if rf, ok := ret.Get(0).(func(common.Address) uint64); ok { + var r0 *big.Int + if rf, ok := ret.Get(0).(func(common.Address) *big.Int); ok { r0 = rf(_a0) - } else { - r0 = ret.Get(0).(uint64) + } else if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) } return r0 diff --git a/sei-tendermint/abci/types/types.go b/sei-tendermint/abci/types/types.go index a697d86782..5c108bdbe7 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 + EVMSenderAddress string IsEVM bool Priority int64 + RequiredBalance *big.Int } type CheckTxTypeV2 int32 diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 589349171d..46ebe33d3d 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -6,6 +6,7 @@ import ( "crypto/sha256" "errors" "fmt" + "math/big" "sync" "sync/atomic" "time" @@ -225,7 +226,7 @@ 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] @@ -481,28 +482,23 @@ 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, - priority: res.Priority, - 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, + evmNonce: res.EVMNonce, + evmAddress: res.EVMSenderAddress, + isEVM: res.IsEVM, + priority: res.Priority, estimatedGas: res.GasEstimated, gasWanted: res.GasWanted, peers: map[uint16]struct{}{txInfo.SenderID: {}}, } + if res.RequiredBalance != nil { + wtx.requiredBalance = new(big.Int).Set(res.RequiredBalance) + } // only add new transaction if checkTx passes and is not pending - if !res.IsPending.IsPresent() { + if !txmp.shouldPending(wtx) { if err := txmp.addNewTransaction(wtx); err != nil { return nil, err } @@ -510,17 +506,14 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) // 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 } @@ -749,7 +742,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 @@ -764,9 +757,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. @@ -774,7 +767,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)]. @@ -791,8 +784,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). @@ -922,7 +915,7 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx) error { "post_check_err", err, ) txmp.metrics.FailedTxs.Add(1) - wtx.removeHandler(!txmp.config.KeepInvalidTxsInCache) + txmp.cleanupTx(wtx.Hash(), !txmp.config.KeepInvalidTxsInCache) return err } if err := txmp.canAddTx(wtx); err != nil { @@ -935,7 +928,7 @@ func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx) error { 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(), @@ -1029,9 +1022,14 @@ func (txmp *TxMempool) handleRecheckResult(tx types.Tx, res *abci.ResponseCheckT // callback is being executed for the same evicted transaction. if !txmp.txStore.IsTxRemoved(wtx) { err := txmp.checkResponseState(wtx) + if res.RequiredBalance != nil { + wtx.requiredBalance = new(big.Int).Set(res.RequiredBalance) + } else { + wtx.requiredBalance = nil + } // 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( @@ -1180,6 +1178,12 @@ 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 @@ -1199,7 +1203,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 { @@ -1219,7 +1223,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) { @@ -1288,18 +1292,48 @@ func (txmp *TxMempool) notifyTxsAvailable() { } } +func (txmp *TxMempool) shouldPending(wtx *WrappedTx) bool { + if !wtx.isEVM { + return false + } + addr := common.HexToAddress(wtx.evmAddress) + if wtx.evmNonce > txmp.EvmNextPendingNonce(addr) { + return true + } + if wtx.requiredBalance == nil { + return false + } + balance := txmp.app.EvmBalance(addr) + if balance == nil { + return true + } + return balance.Cmp(wtx.requiredBalance) < 0 +} + func (txmp *TxMempool) handlePendingTransactions() { - accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions() + accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions(func(wtx *WrappedTx) abci.PendingTxCheckerResponse { + if !wtx.isEVM { + return abci.Accepted + } + addr := common.HexToAddress(wtx.evmAddress) + if wtx.evmNonce < 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); err != nil { - txmp.removeNonce(tx.tx) + 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.tx) + 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/recheck_drain_test.go b/sei-tendermint/internal/mempool/recheck_drain_test.go index b1e0d95de9..78628c84ea 100644 --- a/sei-tendermint/internal/mempool/recheck_drain_test.go +++ b/sei-tendermint/internal/mempool/recheck_drain_test.go @@ -19,8 +19,7 @@ import ( // 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 { @@ -85,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 } @@ -125,7 +103,7 @@ func (a *evmNonceApp) EvmNonce(addr common.Address) uint64 { // 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 diff --git a/sei-tendermint/internal/mempool/tx.go b/sei-tendermint/internal/mempool/tx.go index b70cb5d780..859273964d 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" @@ -56,6 +57,9 @@ type WrappedTx struct { // in the ResponseCheckTx response. priority int64 + // requiredBalance is the sender balance threshold for this tx to become active. + requiredBalance *big.Int + // timestamp is the time at which the node first received the transaction from // a peer. It is used as a second dimension is prioritizing transactions when // two transactions have the same priority. @@ -76,9 +80,6 @@ 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 // hex encoded address evmNonce uint64 @@ -314,35 +315,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 +357,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 +366,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 +374,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 +388,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 +417,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 +435,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 f6a8ae7067..0193820e40 100644 --- a/sei-tendermint/internal/proxy/proxy.go +++ b/sei-tendermint/internal/proxy/proxy.go @@ -3,6 +3,7 @@ package proxy import ( "context" "fmt" + "math/big" "runtime/debug" "time" @@ -48,9 +49,9 @@ func (app *Proxy) EvmNonce(addr common.Address) uint64 { return app.app.EvmNonce(addr) } -func (app *Proxy) EvmNextPendingNonce(addr common.Address) uint64 { - defer addTimeSample(app.metrics.MethodTiming.With("method", "evm_next_pending_nonce", "type", "sync"))() - return app.app.EvmNextPendingNonce(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) { diff --git a/x/evm/ante/sig.go b/x/evm/ante/sig.go index 867ac0c04b..78821115dd 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.WithRequiredBalance(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..31cd4afe08 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.RequiredBalance()) + require.Zero(t, newCtx.RequiredBalance().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/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) From 655d39393231fc6e03c70aa94f5d25892114adab Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Wed, 6 May 2026 18:54:46 +0200 Subject: [PATCH 33/33] grouped evm data in WrappedTx, needs more work --- app/abci.go | 10 +- app/ante/evm_checktx.go | 2 +- sei-cosmos/types/context.go | 62 +++---- sei-tendermint/abci/types/types.go | 10 +- sei-tendermint/internal/mempool/mempool.go | 78 ++++---- .../internal/mempool/priority_queue.go | 168 ++++------------- .../internal/mempool/priority_queue_test.go | 173 +++++++++++------- sei-tendermint/internal/mempool/tx.go | 21 ++- x/evm/ante/sig.go | 2 +- x/evm/ante/sig_test.go | 4 +- 10 files changed, 245 insertions(+), 285 deletions(-) diff --git a/app/abci.go b/app/abci.go index 0cff26be1f..5f518d15ed 100644 --- a/app/abci.go +++ b/app/abci.go @@ -105,11 +105,11 @@ func (app *App) CheckTx(ctx context.Context, req *abci.RequestCheckTxV2) *abci.R Priority: txCtx.Priority(), GasEstimated: int64(gInfo.GasEstimate), //nolint:gosec }, - EVMNonce: txCtx.EVMNonce(), - EVMSenderAddress: txCtx.EVMSenderAddress(), - IsEVM: txCtx.IsEVM(), - Priority: txCtx.Priority(), - RequiredBalance: txCtx.RequiredBalance(), + EVMNonce: txCtx.EVMNonce(), + EVMSenderAddress: txCtx.EVMSenderAddress(), + IsEVM: txCtx.IsEVM(), + Priority: txCtx.Priority(), + EVMRequiredBalance: txCtx.EVMRequiredBalance(), } return res diff --git a/app/ante/evm_checktx.go b/app/ante/evm_checktx.go index 71a3cb0c2e..c2596d4719 100644 --- a/app/ante/evm_checktx.go +++ b/app/ante/evm_checktx.go @@ -291,7 +291,7 @@ func CheckNonce(ctx sdk.Context, ek *evmkeeper.Keeper, etx *ethtypes.Transaction if txNonce < nextNonce { return ctx, sdkerrors.ErrWrongSequence } - ctx = ctx.WithRequiredBalance(fee) + ctx = ctx.WithEVMRequiredBalance(fee) return ctx, nil } diff --git a/sei-cosmos/types/context.go b/sei-cosmos/types/context.go index b61e43282f..fce8f34431 100644 --- a/sei-cosmos/types/context.go +++ b/sei-cosmos/types/context.go @@ -24,30 +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 - deliverTxCallback func(Context) // callback to make at the end of DeliverTx. - requiredBalance *big.Int // Required sender balance for this tx, only relevant in CheckTx. + 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 @@ -181,11 +181,11 @@ func (c Context) DeliverTxCallback() func(Context) { return c.deliverTxCallback } -func (c Context) RequiredBalance() *big.Int { - if c.requiredBalance == nil { +func (c Context) EVMRequiredBalance() *big.Int { + if c.evmRequiredBalance == nil { return nil } - return new(big.Int).Set(c.requiredBalance) + return new(big.Int).Set(c.evmRequiredBalance) } func (c Context) MessageIndex() int { @@ -446,12 +446,12 @@ func (c Context) WithDeliverTxCallback(deliverTxCallback func(Context)) Context return c } -func (c Context) WithRequiredBalance(requiredBalance *big.Int) Context { - if requiredBalance == nil { - c.requiredBalance = nil +func (c Context) WithEVMRequiredBalance(evmRequiredBalance *big.Int) Context { + if evmRequiredBalance == nil { + c.evmRequiredBalance = nil return c } - c.requiredBalance = new(big.Int).Set(requiredBalance) + c.evmRequiredBalance = new(big.Int).Set(evmRequiredBalance) return c } diff --git a/sei-tendermint/abci/types/types.go b/sei-tendermint/abci/types/types.go index 5c108bdbe7..b8847a6c66 100644 --- a/sei-tendermint/abci/types/types.go +++ b/sei-tendermint/abci/types/types.go @@ -231,11 +231,11 @@ type ResponseCheckTxV2 struct { *ResponseCheckTx // helper properties for prioritization in mempool - EVMNonce uint64 - EVMSenderAddress string - IsEVM bool - Priority int64 - RequiredBalance *big.Int + EVMNonce uint64 + EVMSenderAddress string + IsEVM bool + Priority int64 + EVMRequiredBalance *big.Int } type CheckTxTypeV2 int32 diff --git a/sei-tendermint/internal/mempool/mempool.go b/sei-tendermint/internal/mempool/mempool.go index 46ebe33d3d..fcf8a6db06 100644 --- a/sei-tendermint/internal/mempool/mempool.go +++ b/sei-tendermint/internal/mempool/mempool.go @@ -296,10 +296,11 @@ func (txmp *TxMempool) EvmNextPendingNonce(addr common.Address) uint64 { } func (txmp *TxMempool) addNonce(wtx *WrappedTx) { - if !wtx.isEVM { + evm, ok := wtx.evm.Get() + if !ok { return } - an := evmAddrNonce{common.HexToAddress(wtx.evmAddress), wtx.evmNonce} + 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 @@ -309,10 +310,11 @@ func (txmp *TxMempool) addNonce(wtx *WrappedTx) { } func (txmp *TxMempool) removeNonce(wtx *WrappedTx) { - if !wtx.isEVM { + evm, ok := wtx.evm.Get() + if !ok { return } - an := evmAddrNonce{common.HexToAddress(wtx.evmAddress), wtx.evmNonce} + an := evmAddrNonce{common.HexToAddress(evm.address), evm.nonce} for byAddrNonce := range txmp.byAddrNonce.Lock() { if byAddrNonce[an] == wtx { delete(byAddrNonce, an) @@ -485,16 +487,17 @@ func (txmp *TxMempool) CheckTx(ctx context.Context, tx types.Tx, txInfo TxInfo) hashedTx: newHashedTx(tx), timestamp: time.Now().UTC(), height: txmp.height, - evmNonce: res.EVMNonce, - evmAddress: res.EVMSenderAddress, - isEVM: res.IsEVM, priority: res.Priority, estimatedGas: res.GasEstimated, gasWanted: res.GasWanted, peers: map[uint16]struct{}{txInfo.SenderID: {}}, } - if res.RequiredBalance != nil { - wtx.requiredBalance = new(big.Int).Set(res.RequiredBalance) + 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 @@ -696,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()) @@ -835,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) } } @@ -1022,10 +1025,9 @@ func (txmp *TxMempool) handleRecheckResult(tx types.Tx, res *abci.ResponseCheckT // callback is being executed for the same evicted transaction. if !txmp.txStore.IsTxRemoved(wtx) { err := txmp.checkResponseState(wtx) - if res.RequiredBalance != nil { - wtx.requiredBalance = new(big.Int).Set(res.RequiredBalance) - } else { - wtx.requiredBalance = nil + 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 @@ -1236,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, @@ -1293,30 +1307,26 @@ func (txmp *TxMempool) notifyTxsAvailable() { } func (txmp *TxMempool) shouldPending(wtx *WrappedTx) bool { - if !wtx.isEVM { + evm, ok := wtx.evm.Get() + if !ok { return false } - addr := common.HexToAddress(wtx.evmAddress) - if wtx.evmNonce > txmp.EvmNextPendingNonce(addr) { + addr := common.HexToAddress(evm.address) + if evm.nonce > txmp.EvmNextPendingNonce(addr) { return true } - if wtx.requiredBalance == nil { - return false - } balance := txmp.app.EvmBalance(addr) - if balance == nil { - return true - } - return balance.Cmp(wtx.requiredBalance) < 0 + return balance.Cmp(evm.requiredBalance) < 0 } func (txmp *TxMempool) handlePendingTransactions() { accepted, rejected := txmp.pendingTxs.EvaluatePendingTransactions(func(wtx *WrappedTx) abci.PendingTxCheckerResponse { - if !wtx.isEVM { + evm, ok := wtx.evm.Get() + if !ok { return abci.Accepted } - addr := common.HexToAddress(wtx.evmAddress) - if wtx.evmNonce < txmp.app.EvmNonce(addr) { + addr := common.HexToAddress(evm.address) + if evm.nonce < txmp.app.EvmNonce(addr) { return abci.Rejected } if txmp.shouldPending(wtx) { diff --git a/sei-tendermint/internal/mempool/priority_queue.go b/sei-tendermint/internal/mempool/priority_queue.go index acc3c3ee84..ece24e3bad 100644 --- a/sei-tendermint/internal/mempool/priority_queue.go +++ b/sei-tendermint/internal/mempool/priority_queue.go @@ -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,44 +44,42 @@ 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 := pq.evmQueue[tx.evmAddress] + queue := pq.evmQueue[evm.address] if len(queue) == 0 { - return nil,false + return nil, false } - existing, idx := pq.getTxWithSameNonceUnsafe(tx) + existing, idx := pq.txByAddrNonceUnsafe(evm.address,evm.nonce) if existing == nil { - return nil,false + return nil, false } if tx.priority <= existing.priority { // tx should be dropped since it's dominated by an existing tx @@ -93,7 +91,7 @@ func (pq *TxPriorityQueue) tryReplacementUnsafe(tx *WrappedTx) (replaced *Wrappe 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 + pq.evmQueue[evm.address][idx] = tx // replace queue item in-place return existing, false } @@ -160,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 } @@ -198,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 } @@ -233,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() @@ -374,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 } @@ -386,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/tx.go b/sei-tendermint/internal/mempool/tx.go index 859273964d..caba68da04 100644 --- a/sei-tendermint/internal/mempool/tx.go +++ b/sei-tendermint/internal/mempool/tx.go @@ -57,9 +57,6 @@ type WrappedTx struct { // in the ResponseCheckTx response. priority int64 - // requiredBalance is the sender balance threshold for this tx to become active. - requiredBalance *big.Int - // timestamp is the time at which the node first received the transaction from // a peer. It is used as a second dimension is prioritizing transactions when // two transactions have the same priority. @@ -81,15 +78,23 @@ type WrappedTx struct { removed bool // evm properties that aid in prioritization - evmAddress string // hex encoded address - 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 { diff --git a/x/evm/ante/sig.go b/x/evm/ante/sig.go index 78821115dd..acf683d8c8 100644 --- a/x/evm/ante/sig.go +++ b/x/evm/ante/sig.go @@ -67,7 +67,7 @@ func (svd *EVMSigVerifyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat if txNonce < nextNonce { return ctx, sdkerrors.ErrWrongSequence } - ctx = ctx.WithRequiredBalance(fee) + 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 31cd4afe08..8c2621a19c 100644 --- a/x/evm/ante/sig_test.go +++ b/x/evm/ante/sig_test.go @@ -144,8 +144,8 @@ func TestSigVerifyPendingTransaction(t *testing.T) { // 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.RequiredBalance()) - require.Zero(t, newCtx.RequiredBalance().Cmp(fee.BigInt())) + 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))