diff --git a/CHANGELOG.md b/CHANGELOG.md index 67240c92ec9d..4ee3be853fa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ This patch update also includes minor dependency bumps. ### Improvements +* [#24207](https://github.com/cosmos/cosmos-sdk/pull/24207) Avoid decoding tx for in PrepareProposal if it's NoOpMempool. * (types) [#25342](https://github.com/cosmos/cosmos-sdk/pull/25342) Undeprecated `EmitEvent` and `EmitEvents` on the `EventManager`. These functions will continue to be maintained. * (types) [#24668](https://github.com/cosmos/cosmos-sdk/pull/24668) Scope the global config to a particular binary so that multiple SDK binaries can be properly run on the same machine. * (baseapp) [#24655](https://github.com/cosmos/cosmos-sdk/pull/24655) Add mutex locks for `state` and make `lastCommitInfo` atomic to prevent race conditions between `Commit` and `CreateQueryContext`. diff --git a/baseapp/abci_utils.go b/baseapp/abci_utils.go index 9c53f0f6550b..6e95f28e5ff4 100644 --- a/baseapp/abci_utils.go +++ b/baseapp/abci_utils.go @@ -213,6 +213,9 @@ type ( txVerifier ProposalTxVerifier txSelector TxSelector signerExtAdapter mempool.SignerExtractionAdapter + + // fastPrepareProposal together with NoOpMempool will bypass tx selector + fastPrepareProposal bool } ) @@ -225,6 +228,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 @@ -270,6 +279,11 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan // Note, we still need to ensure the transactions returned respect req.MaxTxBytes. _, isNoOp := h.mempool.(mempool.NoOpMempool) if h.mempool == nil || isNoOp { + if h.fastPrepareProposal { + txs := h.txSelector.SelectTxForProposalFast(ctx, req.Txs) + return &abci.ResponsePrepareProposal{Txs: txs}, nil + } + for _, txBz := range req.Txs { tx, err := h.txVerifier.TxDecode(txBz) if err != nil { @@ -478,6 +492,11 @@ type TxSelector interface { // return if the caller should halt the transaction selection loop // (typically over a mempool) or 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. + SelectTxForProposalFast(ctx context.Context, txs [][]byte) [][]byte } type defaultTxSelector struct { @@ -499,7 +518,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 { @@ -531,3 +550,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 +} diff --git a/baseapp/abci_utils_test.go b/baseapp/abci_utils_test.go index a1064705f5f4..8ef3f8ea4ba2 100644 --- a/baseapp/abci_utils_test.go +++ b/baseapp/abci_utils_test.go @@ -478,6 +478,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() builder := txConfig.NewTxBuilder() @@ -558,6 +561,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) } }) } diff --git a/baseapp/testutil/mock/mocks.go b/baseapp/testutil/mock/mocks.go index 51de61fa7156..1cf8db2c3e7b 100644 --- a/baseapp/testutil/mock/mocks.go +++ b/baseapp/testutil/mock/mocks.go @@ -229,6 +229,20 @@ func (mr *MockTxSelectorMockRecorder) SelectTxForProposal(ctx, maxTxBytes, maxBl return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTxForProposal", reflect.TypeOf((*MockTxSelector)(nil).SelectTxForProposal), ctx, maxTxBytes, maxBlockGas, memTx, txBz) } +// SelectTxForProposalFast mocks base method. +func (m *MockTxSelector) SelectTxForProposalFast(ctx context.Context, txs [][]byte) [][]byte { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SelectTxForProposalFast", ctx, txs) + ret0, _ := ret[0].([][]byte) + return ret0 +} + +// SelectTxForProposalFast indicates an expected call of SelectTxForProposalFast. +func (mr *MockTxSelectorMockRecorder) SelectTxForProposalFast(ctx, txs any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectTxForProposalFast", reflect.TypeOf((*MockTxSelector)(nil).SelectTxForProposalFast), ctx, txs) +} + // SelectedTxs mocks base method. func (m *MockTxSelector) SelectedTxs(ctx context.Context) [][]byte { m.ctrl.T.Helper()