Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i


### Improvements
* [#24207](https://github.com/cosmos/cosmos-sdk/pull/24207) Avoid decoding tx for in PrepareProposal if it's NoOpMempool.

### Bug Fixes

Expand Down
61 changes: 49 additions & 12 deletions baseapp/abci_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ type (
txVerifier ProposalTxVerifier
txSelector TxSelector
signerExtAdapter mempool.SignerExtractionAdapter

// fastPrepareProposal together with NoOpMempool will bypass tx selector
fastPrepareProposal bool
}
)

Expand All @@ -249,6 +252,12 @@ func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier
}
}

func NewDefaultProposalHandlerFast(mp mempool.Mempool, txVerifier ProposalTxVerifier) *DefaultProposalHandler {
h := NewDefaultProposalHandler(mp, txVerifier)
h.fastPrepareProposal = true
return h
}

// SetTxSelector sets the TxSelector function on the DefaultProposalHandler.
func (h *DefaultProposalHandler) SetTxSelector(ts TxSelector) {
h.txSelector = ts
Expand Down Expand Up @@ -283,23 +292,38 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan

defer h.txSelector.Clear()

// decode transactions
decodedTxs := make([]sdk.Tx, len(req.Txs))
for i, txBz := range req.Txs {
tx, err := h.txVerifier.TxDecode(txBz)
if err != nil {
return nil, err
}

decodedTxs[i] = tx
}

// If the mempool is nil or NoOp we simply return the transactions
// requested from CometBFT, which, by default, should be in FIFO order.
//
// Note, we still need to ensure the transactions returned respect req.MaxTxBytes.
_, isNoOp := h.mempool.(mempool.NoOpMempool)
// decode transactions
var decodedTxs []sdk.Tx
getDecodedTxs := func() error {
if decodedTxs != nil {
return nil
}

decodedTxs = make([]sdk.Tx, len(req.Txs))
for i, txBz := range req.Txs {
tx, err := h.txVerifier.TxDecode(txBz)
if err != nil {
return err
}

decodedTxs[i] = tx
}
return nil
}
if h.mempool == nil || isNoOp {
if h.fastPrepareProposal {
txs := h.txSelector.SelectTxForProposalFast(ctx, req.Txs)
return &abci.PrepareProposalResponse{Txs: txs}, nil
}
err := getDecodedTxs()
if err != nil {
return nil, err
}
for i, tx := range decodedTxs {
stop := h.txSelector.SelectTxForProposal(ctx, uint64(req.MaxTxBytes), maxBlockGas, tx, req.Txs[i])
if stop {
Expand All @@ -316,6 +340,10 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
selectedTxsNums int
invalidTxs []sdk.Tx // invalid txs to be removed out of the loop to avoid dead lock
)
err := getDecodedTxs()
if err != nil {
return nil, err
}
h.mempool.SelectBy(ctx, decodedTxs, func(memTx sdk.Tx) bool {
unorderedTx, ok := memTx.(sdk.TxWithUnordered)
isUnordered := ok && unorderedTx.GetUnordered()
Expand Down Expand Up @@ -504,6 +532,11 @@ type TxSelector interface {
// return <true> if the caller should halt the transaction selection loop
// (typically over a mempool) or <false> otherwise.
SelectTxForProposal(ctx context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte) bool
// SelectTxForProposalFast is called in the case of NoOpMempool,
// where cometbft already checked the block gas/size limit,
// so the tx selector should simply accept them all to the proposal.
// But extra validations on the tx are still possible though.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// But extra validations on the tx are still possible though.

is it still possible because i can implement my own TxSelector? i'm a bit confused on this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SelectTxForProposalFast(ctx context.Context, txs [][]byte) [][]byte
}

type defaultTxSelector struct {
Expand All @@ -525,7 +558,7 @@ func (ts *defaultTxSelector) SelectedTxs(_ context.Context) [][]byte {
func (ts *defaultTxSelector) Clear() {
ts.totalTxBytes = 0
ts.totalTxGas = 0
ts.selectedTxs = nil
ts.selectedTxs = ts.selectedTxs[:0] // keep the allocated memory
}

func (ts *defaultTxSelector) SelectTxForProposal(_ context.Context, maxTxBytes, maxBlockGas uint64, memTx sdk.Tx, txBz []byte) bool {
Expand Down Expand Up @@ -557,3 +590,7 @@ func (ts *defaultTxSelector) SelectTxForProposal(_ context.Context, maxTxBytes,
// check if we've reached capacity; if so, we cannot select any more transactions
return ts.totalTxBytes >= maxTxBytes || (maxBlockGas > 0 && (ts.totalTxGas >= maxBlockGas))
}

func (ts *defaultTxSelector) SelectTxForProposalFast(ctx context.Context, txs [][]byte) [][]byte {
return txs
}
7 changes: 7 additions & 0 deletions baseapp/abci_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,9 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_NoOpMempoolTxSelection()
ph := baseapp.NewDefaultProposalHandler(mempool.NoOpMempool{}, app)
handler := ph.PrepareProposalHandler()

phf := baseapp.NewDefaultProposalHandlerFast(mempool.NoOpMempool{}, app)
handlerFast := phf.PrepareProposalHandler()

// build a tx
_, _, addr := testdata.KeyTestPubAddr()
addrStr, err := signingCtx.AddressCodec().BytesToString(addr)
Expand Down Expand Up @@ -567,6 +570,10 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_NoOpMempoolTxSelection()
resp, err := handler(tc.ctx, tc.req)
s.Require().NoError(err)
s.Require().Len(resp.Txs, tc.expectedTxs)

resp, err = handlerFast(tc.ctx, tc.req)
s.Require().NoError(err)
s.Require().Equal(resp.Txs, tc.req.Txs)
}
})
}
Expand Down
Loading