From 06812560fc0a61e438765be2c89ff5c5a99f2007 Mon Sep 17 00:00:00 2001 From: David Terpay <35130517+davidterpay@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:31:21 -0400 Subject: [PATCH] [ENG-702]: Caching context when iterating through auctions (#56) --- abci/abci.go | 66 ++++++++++++++++++++++++++++++++----------- abci/abci_test.go | 71 ++++++++++++++++------------------------------- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/abci/abci.go b/abci/abci.go index 84bef9a..0364236 100644 --- a/abci/abci.go +++ b/abci/abci.go @@ -9,33 +9,32 @@ import ( abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" "github.com/skip-mev/pob/mempool" ) type ProposalHandler struct { - mempool *mempool.AuctionMempool - logger log.Logger - txVerifier baseapp.ProposalTxVerifier - txEncoder sdk.TxEncoder - txDecoder sdk.TxDecoder + mempool *mempool.AuctionMempool + logger log.Logger + anteHandler sdk.AnteHandler + txEncoder sdk.TxEncoder + txDecoder sdk.TxDecoder } func NewProposalHandler( mp *mempool.AuctionMempool, logger log.Logger, - txVerifier baseapp.ProposalTxVerifier, + anteHandler sdk.AnteHandler, txEncoder sdk.TxEncoder, txDecoder sdk.TxDecoder, ) *ProposalHandler { return &ProposalHandler{ - mempool: mp, - logger: logger, - txVerifier: txVerifier, - txEncoder: txEncoder, - txDecoder: txDecoder, + mempool: mp, + logger: logger, + anteHandler: anteHandler, + txEncoder: txEncoder, + txDecoder: txDecoder, } } @@ -56,9 +55,10 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { // bundled transactions are valid. selectBidTxLoop: for ; bidTxIterator != nil; bidTxIterator = bidTxIterator.Next() { + cacheCtx, write := ctx.CacheContext() tmpBidTx := bidTxIterator.Tx() - bidTxBz, err := h.txVerifier.PrepareProposalVerifyTx(tmpBidTx) + bidTxBz, err := h.PrepareProposalVerifyTx(cacheCtx, tmpBidTx) if err != nil { txsToRemove[tmpBidTx] = struct{}{} continue selectBidTxLoop @@ -84,7 +84,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { continue selectBidTxLoop } - if _, err := h.txVerifier.PrepareProposalVerifyTx(refTx); err != nil { + if _, err := h.PrepareProposalVerifyTx(cacheCtx, refTx); err != nil { // Invalid bundled transaction, so we remove the bid transaction // and try the next top bid. txsToRemove[tmpBidTx] = struct{}{} @@ -106,6 +106,10 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { seenTxs[txHash] = struct{}{} } + // Write the cache context to the original context when we know we have a + // valid top of block bundle. + write() + break selectBidTxLoop } @@ -144,7 +148,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { continue selectTxLoop } - txBz, err = h.txVerifier.PrepareProposalVerifyTx(memTx) + txBz, err = h.PrepareProposalVerifyTx(ctx, memTx) if err != nil { txsToRemove[memTx] = struct{}{} continue selectTxLoop @@ -174,7 +178,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler { func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { return func(ctx sdk.Context, req abci.RequestProcessProposal) abci.ResponseProcessProposal { for index, txBz := range req.Txs { - tx, err := h.txVerifier.ProcessProposalVerifyTx(txBz) + tx, err := h.ProcessProposalVerifyTx(ctx, txBz) if err != nil { return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} } @@ -208,6 +212,36 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { } } +// PrepareProposalVerifyTx encodes a transaction and verifies it. +func (h *ProposalHandler) PrepareProposalVerifyTx(ctx sdk.Context, tx sdk.Tx) ([]byte, error) { + txBz, err := h.txEncoder(tx) + if err != nil { + return nil, err + } + + return txBz, h.verifyTx(ctx, tx) +} + +// ProcessProposalVerifyTx decodes a transaction and verifies it. +func (h *ProposalHandler) ProcessProposalVerifyTx(ctx sdk.Context, txBz []byte) (sdk.Tx, error) { + tx, err := h.txDecoder(txBz) + if err != nil { + return nil, err + } + + return tx, h.verifyTx(ctx, tx) +} + +// VerifyTx verifies a transaction against the application's state. +func (h *ProposalHandler) verifyTx(ctx sdk.Context, tx sdk.Tx) error { + if h.anteHandler != nil { + _, err := h.anteHandler(ctx, tx, false) + return err + } + + return nil +} + func (h *ProposalHandler) RemoveTx(tx sdk.Tx) { if err := h.mempool.RemoveWithoutRefTx(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) { panic(fmt.Errorf("failed to remove invalid transaction from the mempool: %w", err)) diff --git a/abci/abci_test.go b/abci/abci_test.go index e65228e..67d630e 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -107,60 +107,37 @@ func (suite *ABCITestSuite) SetupTest() { // Proposal handler set up suite.logger = log.NewNopLogger() - suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder()) + suite.proposalHandler = abci.NewProposalHandler(suite.mempool, suite.logger, suite.anteHandler, suite.encodingConfig.TxConfig.TxEncoder(), suite.encodingConfig.TxConfig.TxDecoder()) } -func (suite *ABCITestSuite) PrepareProposalVerifyTx(tx sdk.Tx) ([]byte, error) { - _, err := suite.executeAnteHandler(tx) - if err != nil { - return nil, err - } - - txBz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) - if err != nil { - return nil, err - } - - hash := sha256.Sum256(txBz) - txHash := hex.EncodeToString(hash[:]) - if _, ok := suite.txs[txHash]; ok { - return nil, fmt.Errorf("tx already in mempool") - } - suite.txs[txHash] = struct{}{} - - return txBz, nil -} - -func (suite *ABCITestSuite) ProcessProposalVerifyTx(txBz []byte) (sdk.Tx, error) { - tx, err := suite.encodingConfig.TxConfig.TxDecoder()(txBz) - if err != nil { - return nil, err - } - - _, err = suite.executeAnteHandler(tx) - if err != nil { - return tx, err - } - - hash := sha256.Sum256(txBz) - txHash := hex.EncodeToString(hash[:]) - if _, ok := suite.txs[txHash]; ok { - return nil, fmt.Errorf("tx already in mempool") - } - suite.txs[txHash] = struct{}{} - - return tx, nil -} - -func (suite *ABCITestSuite) executeAnteHandler(tx sdk.Tx) (sdk.Context, error) { +func (suite *ABCITestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { signer := tx.GetMsgs()[0].GetSigners()[0] - suite.bankKeeper.EXPECT().GetAllBalances(suite.ctx, signer).AnyTimes().Return(suite.balances) + suite.bankKeeper.EXPECT().GetAllBalances(ctx, signer).AnyTimes().Return(suite.balances) next := func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { return ctx, nil } - return suite.builderDecorator.AnteHandle(suite.ctx, tx, false, next) + ctx, err := suite.builderDecorator.AnteHandle(ctx, tx, false, next) + if err != nil { + return ctx, err + } + + bz, err := suite.encodingConfig.TxConfig.TxEncoder()(tx) + if err != nil { + return ctx, err + } + + if !simulate { + hash := sha256.Sum256(bz) + txHash := hex.EncodeToString(hash[:]) + if _, ok := suite.txs[txHash]; ok { + return ctx, fmt.Errorf("tx already in mempool") + } + suite.txs[txHash] = struct{}{} + } + + return ctx, nil } func (suite *ABCITestSuite) createFilledMempool(numNormalTxs, numAuctionTxs, numBundledTxs int, insertRefTxs bool) int { @@ -790,6 +767,6 @@ func (suite *ABCITestSuite) isTopBidValid() bool { } // check if the top bid is valid - _, err := suite.executeAnteHandler(iterator.Tx()) + _, err := suite.anteHandler(suite.ctx, iterator.Tx(), true) return err == nil }