From a4d1ede3e98ed09b0824e5f63e3f193b2e0eeae5 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 14 Dec 2022 15:44:05 -0500 Subject: [PATCH] feat: no-op mempool (#14297) --- baseapp/baseapp.go | 54 +++++++++++++++++++++++++++++++++++-------- types/mempool/noop.go | 22 ++++++++++++++++++ 2 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 types/mempool/noop.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 0c49600ae3..de164579f3 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -169,7 +169,7 @@ func NewBaseApp( } if app.mempool == nil { - app.SetMempool(mempool.NewSenderNonceMempool()) + app.SetMempool(mempool.NoOpMempool{}) } if app.processProposal == nil { @@ -857,18 +857,35 @@ func createEvents(msg sdk.Msg) sdk.Events { return sdk.Events{msgEvent} } -// DefaultPrepareProposal returns the default implementation for processing an ABCI proposal. The application's -// mempool is enumerated and all valid transactions are added to the proposal. Transactions are valid if they: +// DefaultPrepareProposal returns the default implementation for processing an +// ABCI proposal. The application's mempool is enumerated and all valid +// transactions are added to the proposal. Transactions are valid if they: // // 1) Successfully encode to bytes. -// 2) Are valid (i.e. pass runTx, AnteHandler only) +// 2) Are valid (i.e. pass runTx, AnteHandler only). // -// Enumeration is halted once RequestPrepareProposal.MaxBytes of transactions is reached or the mempool is exhausted. -// Note that step (2) is identical to the validation step performed in DefaultProcessProposal. It is very important -// that the same validation logic is used in both steps, and applications must ensure that this is the case in +// Enumeration is halted once RequestPrepareProposal.MaxBytes of transactions is +// reached or the mempool is exhausted. +// +// Note: +// +// - Step (2) is identical to the validation step performed in +// DefaultProcessProposal. It is very important that the same validation logic +// is used in both steps, and applications must ensure that this is the case in // non-default handlers. +// +// - If no mempool is set or if the mempool is a no-op mempool, the transactions +// requested from Tendermint will simply be returned, which, by default, are in +// FIFO order. func (app *BaseApp) DefaultPrepareProposal() sdk.PrepareProposalHandler { return func(ctx sdk.Context, req abci.RequestPrepareProposal) abci.ResponsePrepareProposal { + // If the mempool is nil or a no-op mempool, we simply return the transactions + // requested from Tendermint, which, by default, should be in FIFO order. + _, isNoOp := app.mempool.(mempool.NoOpMempool) + if app.mempool == nil || isNoOp { + return abci.ResponsePrepareProposal{Txs: req.Txs} + } + var ( txsBytes [][]byte byteCount int64 @@ -885,14 +902,16 @@ func (app *BaseApp) DefaultPrepareProposal() sdk.PrepareProposalHandler { txSize := int64(len(bz)) - // NOTE: runTx was already run in CheckTx which calls mempool.Insert so ideally everything in the pool - // should be valid. But some mempool implementations may insert invalid txs, so we check again. + // NOTE: Since runTx was already executed in CheckTx, which calls + // mempool.Insert, ideally everything in the pool should be valid. But + // some mempool implementations may insert invalid txs, so we check again. _, _, _, _, err = app.runTx(runTxPrepareProposal, bz) if err != nil { err := app.mempool.Remove(memTx) if err != nil && !errors.Is(err, mempool.ErrTxNotFound) { panic(err) } + iterator = iterator.Next() continue } else if byteCount += txSize; byteCount <= req.MaxTxBytes { @@ -903,6 +922,7 @@ func (app *BaseApp) DefaultPrepareProposal() sdk.PrepareProposalHandler { iterator = iterator.Next() } + return abci.ResponsePrepareProposal{Txs: txsBytes} } } @@ -932,3 +952,19 @@ func (app *BaseApp) DefaultProcessProposal() sdk.ProcessProposalHandler { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} } } + +// NoOpPrepareProposal defines a no-op PrepareProposal handler. It will always +// return the transactions sent by the client's request. +func NoOpPrepareProposal() sdk.PrepareProposalHandler { + return func(_ sdk.Context, req abci.RequestPrepareProposal) abci.ResponsePrepareProposal { + return abci.ResponsePrepareProposal{Txs: req.Txs} + } +} + +// NoOpProcessProposal defines a no-op ProcessProposal Handler. It will always +// return ACCEPT. +func NoOpProcessProposal() sdk.ProcessProposalHandler { + return func(_ sdk.Context, _ abci.RequestProcessProposal) abci.ResponseProcessProposal { + return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} + } +} diff --git a/types/mempool/noop.go b/types/mempool/noop.go new file mode 100644 index 0000000000..802122c213 --- /dev/null +++ b/types/mempool/noop.go @@ -0,0 +1,22 @@ +package mempool + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ Mempool = (*NoOpMempool)(nil) + +// NoOpMempool defines a no-op mempool. Transactions are completely discarded and +// ignored when BaseApp interacts with the mempool. +// +// Note: When this mempool is used, it assumed that an application will rely +// on Tendermint's transaction ordering defined in `RequestPrepareProposal`, which +// is FIFO-ordered by default. +type NoOpMempool struct{} + +func (NoOpMempool) Insert(context.Context, sdk.Tx) error { return nil } +func (NoOpMempool) Select(context.Context, [][]byte) Iterator { return nil } +func (NoOpMempool) CountTx() int { return 0 } +func (NoOpMempool) Remove(sdk.Tx) error { return nil }