diff --git a/README.md b/README.md index 9606c4c..7865804 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,17 @@ redistribute MEV. Skip's POB provides developers with a set of a few core primitives: +* `BlockBuster`: BlockBuster is a generalized block-building and mempool SDK + that allows developers to define how their applications construct and validate blocks + on-chain in a transparent, enforceable way. At its core, BlockBuster is an app-side mempool + set + of proposal handlers (`PrepareProposal`/`ProcessProposal`) that allow developers to configure + modular lanes of transactions in their blocks with distinct validation/ordering logic. For more + information, see the [BlockBuster README](/blockbuster/README.md). * `x/builder`: This Cosmos SDK module gives applications the ability to process MEV bundled transactions in addition to having the ability to define how searchers and block proposers are rewarded. In addition, the module defines a `AuctionDecorator`, which is an AnteHandler decorator that enforces various chain configurable MEV rules. -* `ProposalHandler`: This ABCI++ handler defines `PrepareProposal` and `ProcessProposal` - methods that give applications the ability to perform top-of-block auctions, - which enables recapturing, redistributing and control over MEV. These methods - are responsible for block proposal construction and validation. -* `AuctionMempool`: An MEV-aware mempool that enables searchers to submit bundled - transactions to the mempool and have them bundled into blocks via a top-of-block - auction. Searchers include a bid in their bundled transactions and the highest - bid wins the auction. Application devs have control over levers that control - aspects such as the bid floor and minimum bid increment. ## Releases @@ -39,6 +36,9 @@ Skip's POB provides developers with a set of a few core primitives: | POB Version | Cosmos SDK | | :---------: | :--------: | | v1.x.x | v0.47.x | +| v1.x.x | v0.48.x | +| v1.x.x | v0.49.x | +| v1.x.x | v0.50.x | ## Install @@ -48,17 +48,33 @@ $ go install github.com/skip-mev/pob ## Setup +>This set up guide will walk you through the process of setting up a POB application. In particular, we will configure an application with the following features: +> +>* Top of block lane (auction lane). This will create an auction lane where users can bid to have their +> transactions executed at the top of the block. +>* Free lane. This will create a free lane where users can submit transactions that will be executed +> for free (no fees). +>* Default lane. This will create a default lane where users can submit transactions that will be executed +> with the default app logic. +>* Builder module that pairs with the auction lane to process auction transactions and distribute revenue +> to the auction house. +> +> To build your own custom BlockBuster Lane, please see the [BlockBuster README](/blockbuster/README.md). + 1. Import the necessary dependencies into your application. This includes the - proposal handlers, mempool, keeper, builder types, and builder module. This + blockbuster proposal handlers +mempool, keeper, builder types, and builder module. This tutorial will go into more detail into each of the dependencies. ```go import ( - proposalhandler "github.com/skip-mev/pob/abci" - "github.com/skip-mev/pob/mempool" - "github.com/skip-mev/pob/x/builder" - builderkeeper "github.com/skip-mev/pob/x/builder/keeper" - buildertypes "github.com/skip-mev/pob/x/builder/types" + ... + "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/abci" + "github.com/skip-mev/pob/blockbuster/lanes/auction" + "github.com/skip-mev/pob/blockbuster/lanes/base" + "github.com/skip-mev/pob/blockbuster/lanes/free" + buildermodule "github.com/skip-mev/pob/x/builder" + builderkeeper "github.com/skip-mev/pob/x/builder/keeper" ... ) ``` @@ -76,7 +92,7 @@ $ go install github.com/skip-mev/pob var ( ModuleBasics = module.NewBasicManager( ... - builder.AppModuleBasic{}, + buildermodule.AppModuleBasic{}, ) ... ) @@ -86,7 +102,11 @@ $ go install github.com/skip-mev/pob messages that allow users to participate in the top of block auction, distribute revenue to the auction house, and ensure the validity of auction transactions. - a. First add the keeper to the app's struct definition. + a. First add the keeper to the app's struct definition. We also want to add POB's custom + checkTx handler to the app's struct definition. This will allow us to override the + default checkTx handler to process bid transactions before they are inserted into the mempool. + NOTE: The custom handler is required as otherwise the auction can be held hostage by a malicious + users. ```go type App struct { @@ -109,7 +129,124 @@ $ go install github.com/skip-mev/pob } ``` - c. Instantiate the builder keeper, store keys, and module manager. Note, be + c. Instantiate the blockbuster mempool with the application's desired lanes. + + ```go + // Set the blockbuster mempool into the app. + // Create the lanes. + // + // NOTE: The lanes are ordered by priority. The first lane is the highest priority + // lane and the last lane is the lowest priority lane. + // Top of block lane allows transactions to bid for inclusion at the top of the next block. + // + // blockbuster.BaseLaneConfig is utilized for basic encoding/decoding of transactions. + tobConfig := blockbuster.BaseLaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + // the desired portion of total block space to be reserved for the lane. a value of 0 + // indicates that the lane can use all available block space. + MaxBlockSpace: sdk.ZeroDec(), + } + tobLane := auction.NewTOBLane( + tobConfig, + // the maximum number of transactions that the mempool can store. a value of 0 indicates + // that the mempool can store an unlimited number of transactions. + 0, + // AuctionFactory is responsible for determining what is an auction bid transaction and + // how to extract the bid information from the transaction. There is a default implementation + // that can be used or application developers can implement their own. + auction.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), + ) + + // Free lane allows transactions to be included in the next block for free. + freeConfig := blockbuster.BaseLaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: sdk.ZeroDec(), + // IgnoreList is a list of lanes that if a transaction should be included in, it will be + // ignored by the lane. For example, if a transaction should belong to the tob lane, it + // will be ignored by the free lane. + IgnoreList: []blockbuster.Lane{ + tobLane, + }, + } + freeLane := free.NewFreeLane( + freeConfig, + free.NewDefaultFreeFactory(app.txConfig.TxDecoder()), + ) + + // Default lane accepts all other transactions. + defaultConfig := blockbuster.BaseLaneConfig{ + Logger: app.Logger(), + TxEncoder: app.txConfig.TxEncoder(), + TxDecoder: app.txConfig.TxDecoder(), + MaxBlockSpace: sdk.ZeroDec(), + IgnoreList: []blockbuster.Lane{ + tobLane, + freeLane, + }, + } + defaultLane := base.NewDefaultLane(defaultConfig) + + // Set the lanes into the mempool. + lanes := []blockbuster.Lane{ + tobLane, + freeLane, + defaultLane, + } + mempool := blockbuster.NewMempool(lanes...) + app.App.SetMempool(mempool) + ``` + + d. Instantiate the antehandler chain for the application with awareness of the + blockbuster mempool. This will allow the application to verify the validity + of a transaction respecting the desired logic of a given lane. In this walkthrough, + we want the `FeeDecorator` to be ignored for all transactions that should belong to the + free lane. Additionally, we want to add the `x/builder` module's `AuctionDecorator` to the + ante-handler chain. The `AuctionDecorator` is an AnteHandler decorator that enforces various + chain configurable MEV rules. + + ```go + import ( + ... + "github.com/skip-mev/pob/blockbuster" + "github.com/skip-mev/pob/blockbuster/utils" + builderante "github.com/skip-mev/pob/x/builder/ante" + ... + ) + + anteDecorators := []sdk.AnteDecorator{ + ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + ... + // The IgnoreDecorator allows for certain decorators to be ignored for certain transactions. In + // this case, we want to ignore the FeeDecorator for all transactions that should belong to the + // free lane. + utils.NewIgnoreDecorator( + ante.NewDeductFeeDecorator( + options.BaseOptions.AccountKeeper, + options.BaseOptions.BankKeeper, + options.BaseOptions.FeegrantKeeper, + options.BaseOptions.TxFeeChecker, + ), + options.FreeLane, + ), + ... + builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.TOBLane, options.Mempool), + } + + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) + app.SetAnteHandler(anteHandler) + + // Set the antehandlers on the lanes. + for _, lane := range lanes { + lane.SetAnteHandler(anteHandler) + } + app.App.SetAnteHandler(anteHandler) + ``` + + e. Instantiate the builder keeper, store keys, and module manager. Note, be sure to do this after all the required keeper dependencies have been instantiated. ```go @@ -136,40 +273,17 @@ $ go install github.com/skip-mev/pob ) ``` - d. Searchers bid to have their bundles executed at the top of the block - using `MsgAuctionBid` messages (by default). While the builder `Keeper` is capable of - tracking valid bids, it is unable to correctly sequence the auction - transactions alongside the normal transactions without having access to the - application’s mempool. As such, we have to instantiate POB’s custom - `AuctionMempool` - a modified version of the SDK’s priority sender-nonce - mempool - into the application. Note, this should be done after `BaseApp` is - instantiated. - - d.1. Application developers can choose to implement their own `AuctionFactory` implementation - or use the default implementation provided by POB. The `AuctionFactory` is responsible - for determining what is an auction bid transaction and how to extract the bid information - from the transaction. The default implementation provided by POB is `DefaultAuctionFactory` - which uses the `MsgAuctionBid` message to determine if a transaction is an auction bid - transaction and extracts the bid information from the message. - - ```go - config := mempool.NewDefaultAuctionFactory(txDecoder) - - mempool := mempool.NewAuctionMempool(txDecoder, txEncoder, maxTx, config) - bApp.SetMempool(mempool) - ``` - e. With Cosmos SDK version 0.47.0, the process of building blocks has been updated and moved from the consensus layer, CometBFT, to the application layer. When a new block is requested, the proposer for that height will utilize the `PrepareProposal` handler to build a block while the `ProcessProposal` handler will verify the contents of the block proposal by all validators. The - combination of the `AuctionMempool`, `PrepareProposal` and `ProcessProposal` + combination of the `BlockBuster` mempool + `PrepareProposal`/`ProcessProposal` handlers allows the application to verifiably build valid blocks with - top-of-block block space reserved for auctions. Additionally, we override the - `BaseApp`'s `CheckTx` handler with our own custom `CheckTx` handler that will - be responsible for checking the validity of transactions. We override the - `CheckTx` handler so that we can verify auction transactions before they are + top-of-block block space reserved for auctions and partial block for free transactions. + Additionally, we override the `BaseApp`'s `CheckTx` handler with our own custom + `CheckTx` handler that will be responsible for checking the validity of transactions. + We override the `CheckTx` handler so that we can verify auction transactions before they are inserted into the mempool. With the POB `CheckTx`, we can verify the auction transaction and all of the bundled transactions before inserting the auction transaction into the mempool. This is important because we otherwise there may be @@ -178,42 +292,26 @@ $ go install github.com/skip-mev/pob griefed. All other transactions will be executed with base app's `CheckTx`. ```go - // Create the entire chain of AnteDecorators for the application. - anteDecorators := []sdk.AnteDecorator{ - auction.NewAuctionDecorator( - app.BuilderKeeper, - txConfig.TxEncoder(), - mempool, - ), - ..., - } - - // Create the antehandler that will be used to check transactions throughout the lifecycle - // of the application. - anteHandler := sdk.ChainAnteDecorators(anteDecorators...) - app.SetAnteHandler(anteHandler) // Create the proposal handler that will be used to build and validate blocks. - handler := proposalhandler.NewProposalHandler( - mempool, - bApp.Logger(), - anteHandler, - txConfig.TxEncoder(), - txConfig.TxDecoder(), + proposalHandler := abci.NewProposalHandler( + app.Logger(), + app.txConfig.TxDecoder(), + mempool, ) - app.SetPrepareProposal(handler.PrepareProposalHandler()) - app.SetProcessProposal(handler.ProcessProposalHandler()) + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) + // Set the custom CheckTx handler on BaseApp. - checkTxHandler := pobabci.CheckTxHandler( + checkTxHandler := abci.NewCheckTxHandler( app.App, - app.TxDecoder, - mempool, + app.txConfig.TxDecoder(), + tobLane, anteHandler, - chainID, + app.ChainID(), ) - app.SetCheckTx(checkTxHandler) - + app.SetCheckTx(checkTxHandler.CheckTx()) ... // CheckTx will check the transaction with the provided checkTxHandler. We override the default diff --git a/abci/abci_test.go b/abci/abci_test.go index 395d512..9048e45 100644 --- a/abci/abci_test.go +++ b/abci/abci_test.go @@ -77,7 +77,7 @@ func (suite *ABCITestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - config := blockbuster.BaseLaneConfig{ + tobConfig := blockbuster.BaseLaneConfig{ Logger: suite.logger, TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -85,18 +85,27 @@ func (suite *ABCITestSuite) SetupTest() { MaxBlockSpace: math.LegacyZeroDec(), } suite.tobLane = auction.NewTOBLane( - config, + tobConfig, 0, // No bound on the number of transactions in the lane auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Base lane set up + baseConfig := blockbuster.BaseLaneConfig{ + Logger: suite.logger, + TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: suite.anteHandler, + MaxBlockSpace: math.LegacyZeroDec(), + IgnoreList: []blockbuster.Lane{suite.tobLane}, + } suite.baseLane = base.NewDefaultLane( - config, + baseConfig, ) // Mempool set up suite.mempool = blockbuster.NewMempool( + suite.logger, suite.tobLane, suite.baseLane, ) diff --git a/blockbuster/README.md b/blockbuster/README.md index fb2236f..ff80dcf 100644 --- a/blockbuster/README.md +++ b/blockbuster/README.md @@ -169,33 +169,33 @@ Lane interface { Name() string // Match determines if a transaction belongs to this lane. - Match(tx sdk.Tx) bool + Match(ctx sdk.Context, tx sdk.Tx) bool // VerifyTx verifies the transaction belonging to this lane. VerifyTx(ctx sdk.Context, tx sdk.Tx) error - // Contains returns true if the mempool contains the given transaction. - Contains(tx sdk.Tx) (bool, error) + // Contains returns true if the mempool/lane contains the given transaction. + Contains(tx sdk.Tx) bool // PrepareLane builds a portion of the block. It inputs the maxTxBytes that - // can be included in the proposal for the given lane, the partial - // proposal, and a function to call the next lane in the chain. The - // next lane in the chain will be called with the updated proposal and context. + // can be included in the proposal for the given lane, the partial proposal, + // and a function to call the next lane in the chain. The next lane in the + // chain will be called with the updated proposal and context. PrepareLane( ctx sdk.Context, proposal BlockProposal, maxTxBytes int64, - next PrepareLanesHandler, + next PrepareLanesHandler ) (BlockProposal, error) - // ProcessLaneBasic validates that transactions belonging to this lane are - // not misplaced in the block proposal. - ProcessLaneBasic(txs []sdk.Tx) error + // ProcessLaneBasic validates that transactions belonging to this lane + // are not misplaced in the block proposal. + ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error - // ProcessLane verifies this lane's portion of a proposed block. It inputs - // the transactions that may belong to this lane and a function to call - // the next lane in the chain. The next lane in the chain will be - // called with the updated context and filtered down transactions. + // ProcessLane verifies this lane's portion of a proposed block. It inputs + // the transactions that may belong to this lane and a function to call the + // next lane in the chain. The next lane in the chain will be called with + // the updated context and filtered down transactions. ProcessLane( ctx sdk.Context, proposalTxs []sdk.Tx, @@ -209,7 +209,7 @@ Lane interface { Logger() log.Logger // GetMaxBlockSpace returns the max block space for the lane as a relative percentage. - GetMaxBlockSpace() sdk.Dec + GetMaxBlockSpace() math.LegacyDec } ``` diff --git a/blockbuster/abci/abci.go b/blockbuster/abci/abci.go index 3ddcc7c..50f418e 100644 --- a/blockbuster/abci/abci.go +++ b/blockbuster/abci/abci.go @@ -198,7 +198,7 @@ func ChainProcessLanes(chain ...blockbuster.Lane) blockbuster.ProcessLanesHandle chain[0].Logger().Info("processing lane", "lane", chain[0].Name()) - if err := chain[0].ProcessLaneBasic(proposalTxs); err != nil { + if err := chain[0].ProcessLaneBasic(ctx, proposalTxs); err != nil { chain[0].Logger().Error("failed to process lane", "lane", chain[0].Name(), "err", err) return ctx, err } diff --git a/blockbuster/abci/abci_test.go b/blockbuster/abci/abci_test.go index 183cc4f..4444d4a 100644 --- a/blockbuster/abci/abci_test.go +++ b/blockbuster/abci/abci_test.go @@ -75,16 +75,14 @@ func (suite *ABCITestSuite) SetupTest() { suite.ctx = testCtx.Ctx.WithBlockHeight(1) // Lanes configuration - config := blockbuster.BaseLaneConfig{ + // Top of block lane set up + suite.tobConfig = blockbuster.BaseLaneConfig{ Logger: log.NewTestLogger(suite.T()), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: suite.anteHandler, MaxBlockSpace: math.LegacyZeroDec(), // It can be as big as it wants (up to maxTxBytes) } - - // Top of block lane set up - suite.tobConfig = config suite.tobLane = auction.NewTOBLane( suite.tobConfig, 0, // No bound on the number of transactions in the lane @@ -92,21 +90,35 @@ func (suite *ABCITestSuite) SetupTest() { ) // Free lane set up - suite.freeConfig = config + suite.freeConfig = blockbuster.BaseLaneConfig{ + Logger: log.NewTestLogger(suite.T()), + TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: suite.anteHandler, + MaxBlockSpace: math.LegacyZeroDec(), // It can be as big as it wants (up to maxTxBytes) + IgnoreList: []blockbuster.Lane{suite.tobLane}, + } suite.freeLane = free.NewFreeLane( suite.freeConfig, free.NewDefaultFreeFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Base lane set up - suite.baseConfig = config + suite.baseConfig = blockbuster.BaseLaneConfig{ + Logger: log.NewTestLogger(suite.T()), + TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: suite.anteHandler, + MaxBlockSpace: math.LegacyZeroDec(), // It can be as big as it wants (up to maxTxBytes) + IgnoreList: []blockbuster.Lane{suite.tobLane, suite.freeLane}, + } suite.baseLane = base.NewDefaultLane( suite.baseConfig, ) // Mempool set up suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(suite.lanes...) + suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...) // Accounts set up suite.accounts = testutils.RandomAccounts(suite.random, 10) @@ -179,7 +191,7 @@ func (suite *ABCITestSuite) resetLanesWithNewConfig() { suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(suite.lanes...) + suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...) } func (suite *ABCITestSuite) TestPrepareProposal() { @@ -711,13 +723,12 @@ func (suite *ABCITestSuite) TestPrepareProposal() { tx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[txIndex]) suite.Require().NoError(err) - lane, err := suite.mempool.Match(tx) - suite.Require().NoError(err) + suite.Require().Equal(true, suite.mempool.Contains(tx)) // In the case where we have a tob tx, we skip the other transactions in the bundle // in order to not double count switch { - case lane.Name() == suite.tobLane.Name(): + case suite.tobLane.Match(suite.ctx, tx): bidInfo, err := suite.tobLane.GetAuctionBidInfo(tx) suite.Require().NoError(err) @@ -767,7 +778,7 @@ func (suite *ABCITestSuite) TestPrepareProposal() { sdkTx, err := suite.encodingConfig.TxConfig.TxDecoder()(res.Txs[txIndex]) suite.Require().NoError(err) - if suite.lanes[laneIndex].Match(sdkTx) { + if suite.lanes[laneIndex].Match(suite.ctx, sdkTx) { switch suite.lanes[laneIndex].Name() { case suite.tobLane.Name(): bidInfo, err := suite.tobLane.GetAuctionBidInfo(sdkTx) diff --git a/blockbuster/lane.go b/blockbuster/lane.go index e1c2478..c0d9bcc 100644 --- a/blockbuster/lane.go +++ b/blockbuster/lane.go @@ -50,13 +50,13 @@ type ( Name() string // Match determines if a transaction belongs to this lane. - Match(tx sdk.Tx) bool + Match(ctx sdk.Context, tx sdk.Tx) bool // VerifyTx verifies the transaction belonging to this lane. VerifyTx(ctx sdk.Context, tx sdk.Tx) error - // Contains returns true if the mempool contains the given transaction. - Contains(tx sdk.Tx) (bool, error) + // Contains returns true if the mempool/lane contains the given transaction. + Contains(tx sdk.Tx) bool // PrepareLane builds a portion of the block. It inputs the maxTxBytes that can be // included in the proposal for the given lane, the partial proposal, and a function @@ -66,7 +66,7 @@ type ( // ProcessLaneBasic validates that transactions belonging to this lane are not misplaced // in the block proposal. - ProcessLaneBasic(txs []sdk.Tx) error + ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error // ProcessLane verifies this lane's portion of a proposed block. It inputs the transactions // that may belong to this lane and a function to call the next lane in the chain. The next diff --git a/blockbuster/lanes/auction/abci.go b/blockbuster/lanes/auction/abci.go index 83359f3..9a1f62b 100644 --- a/blockbuster/lanes/auction/abci.go +++ b/blockbuster/lanes/auction/abci.go @@ -172,7 +172,7 @@ func (l *TOBLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.Pr } bidTx := txs[0] - if !l.Match(bidTx) { + if !l.Match(ctx, bidTx) { return next(ctx, txs) } @@ -193,7 +193,7 @@ func (l *TOBLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.Pr // - all of the bundled transactions are included after the bid transaction in the order // they were included in the bid transaction. // - there are no other bid transactions in the proposal -func (l *TOBLane) ProcessLaneBasic(txs []sdk.Tx) error { +func (l *TOBLane) ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error { if len(txs) == 0 { return nil } @@ -201,9 +201,9 @@ func (l *TOBLane) ProcessLaneBasic(txs []sdk.Tx) error { bidTx := txs[0] // If there is a bid transaction, it must be the first transaction in the block proposal. - if !l.Match(bidTx) { + if !l.Match(ctx, bidTx) { for _, tx := range txs[1:] { - if l.Match(tx) { + if l.Match(ctx, tx) { return fmt.Errorf("misplaced bid transactions in lane %s", l.Name()) } } @@ -222,7 +222,7 @@ func (l *TOBLane) ProcessLaneBasic(txs []sdk.Tx) error { // Ensure that the order of transactions in the bundle is preserved. for i, bundleTx := range txs[1 : len(bidInfo.Transactions)+1] { - if l.Match(bundleTx) { + if l.Match(ctx, bundleTx) { return fmt.Errorf("multiple bid transactions in lane %s", l.Name()) } @@ -238,7 +238,7 @@ func (l *TOBLane) ProcessLaneBasic(txs []sdk.Tx) error { // Ensure that there are no more bid transactions in the block proposal. for _, tx := range txs[len(bidInfo.Transactions)+1:] { - if l.Match(tx) { + if l.Match(ctx, tx) { return fmt.Errorf("multiple bid transactions in lane %s", l.Name()) } } diff --git a/blockbuster/lanes/auction/lane.go b/blockbuster/lanes/auction/lane.go index 40e8c5b..15be52d 100644 --- a/blockbuster/lanes/auction/lane.go +++ b/blockbuster/lanes/auction/lane.go @@ -47,19 +47,18 @@ func NewTOBLane( return &TOBLane{ Mempool: NewMempool(cfg.TxEncoder, maxTx, af), - DefaultLane: base.NewDefaultLane(cfg), + DefaultLane: base.NewDefaultLane(cfg).WithName(LaneName), Factory: af, } } // Match returns true if the transaction is a bid transaction. This is determined // by the AuctionFactory. -func (l *TOBLane) Match(tx sdk.Tx) bool { +func (l *TOBLane) Match(ctx sdk.Context, tx sdk.Tx) bool { + if l.MatchIgnoreList(ctx, tx) { + return false + } + bidInfo, err := l.GetAuctionBidInfo(tx) return bidInfo != nil && err == nil } - -// Name returns the name of the lane. -func (l *TOBLane) Name() string { - return LaneName -} diff --git a/blockbuster/lanes/auction/mempool.go b/blockbuster/lanes/auction/mempool.go index 2a9529f..8c71798 100644 --- a/blockbuster/lanes/auction/mempool.go +++ b/blockbuster/lanes/auction/mempool.go @@ -22,7 +22,7 @@ type ( GetTopAuctionTx(ctx context.Context) sdk.Tx // Contains returns true if the transaction is contained in the mempool. - Contains(tx sdk.Tx) (bool, error) + Contains(tx sdk.Tx) bool } // TOBMempool defines an auction mempool. It can be seen as an extension of @@ -106,16 +106,6 @@ func NewMempool(txEncoder sdk.TxEncoder, maxTx int, config Factory) *TOBMempool // Insert inserts a transaction into the auction mempool. func (am *TOBMempool) Insert(ctx context.Context, tx sdk.Tx) error { - bidInfo, err := am.GetAuctionBidInfo(tx) - if err != nil { - return err - } - - // This mempool only supports auction bid transactions. - if bidInfo == nil { - return fmt.Errorf("invalid transaction type") - } - if err := am.index.Insert(ctx, tx); err != nil { return fmt.Errorf("failed to insert tx into auction index: %w", err) } @@ -132,17 +122,16 @@ func (am *TOBMempool) Insert(ctx context.Context, tx sdk.Tx) error { // Remove removes a transaction from the mempool based. func (am *TOBMempool) Remove(tx sdk.Tx) error { - bidInfo, err := am.GetAuctionBidInfo(tx) + if err := am.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) { + return fmt.Errorf("failed to remove invalid transaction from the mempool: %w", err) + } + + _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) if err != nil { - return err + return fmt.Errorf("failed to get tx hash string: %w", err) } - // This mempool only supports auction bid transactions. - if bidInfo == nil { - return fmt.Errorf("invalid transaction type") - } - - am.removeTx(am.index, tx) + delete(am.txIndex, txHashStr) return nil } @@ -166,25 +155,12 @@ func (am *TOBMempool) CountTx() int { } // Contains returns true if the transaction is contained in the mempool. -func (am *TOBMempool) Contains(tx sdk.Tx) (bool, error) { +func (am *TOBMempool) Contains(tx sdk.Tx) bool { _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) if err != nil { - return false, fmt.Errorf("failed to get tx hash string: %w", err) + return false } _, ok := am.txIndex[txHashStr] - return ok, nil -} - -func (am *TOBMempool) removeTx(mp sdkmempool.Mempool, tx sdk.Tx) { - if err := mp.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) { - panic(fmt.Errorf("failed to remove invalid transaction from the mempool: %w", err)) - } - - _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) - if err != nil { - panic(fmt.Errorf("failed to get tx hash string: %w", err)) - } - - delete(am.txIndex, txHashStr) + return ok } diff --git a/blockbuster/lanes/base/abci.go b/blockbuster/lanes/base/abci.go index b38f6e1..b8ada51 100644 --- a/blockbuster/lanes/base/abci.go +++ b/blockbuster/lanes/base/abci.go @@ -95,7 +95,7 @@ func (l *DefaultLane) PrepareLane( // we only need to verify the contiguous set of transactions that match to the default lane. func (l *DefaultLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuster.ProcessLanesHandler) (sdk.Context, error) { for index, tx := range txs { - if l.Match(tx) { + if l.Match(ctx, tx) { if err := l.VerifyTx(ctx, tx); err != nil { return ctx, fmt.Errorf("failed to verify tx: %w", err) } @@ -111,12 +111,12 @@ func (l *DefaultLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next blockbuste // transactions that belong to this lane are not misplaced in the block proposal i.e. // the proposal only contains contiguous transactions that belong to this lane - there // can be no interleaving of transactions from other lanes. -func (l *DefaultLane) ProcessLaneBasic(txs []sdk.Tx) error { +func (l *DefaultLane) ProcessLaneBasic(ctx sdk.Context, txs []sdk.Tx) error { seenOtherLaneTx := false lastSeenIndex := 0 for _, tx := range txs { - if l.Match(tx) { + if l.Match(ctx, tx) { if seenOtherLaneTx { return fmt.Errorf("the %s lane contains a transaction that belongs to another lane", l.Name()) } diff --git a/blockbuster/lanes/base/lane.go b/blockbuster/lanes/base/lane.go index 6624253..a1c4b9b 100644 --- a/blockbuster/lanes/base/lane.go +++ b/blockbuster/lanes/base/lane.go @@ -55,14 +55,20 @@ func (l *DefaultLane) WithName(name string) *DefaultLane { // Match returns true if the transaction belongs to this lane. Since // this is the default lane, it always returns true except for transactions // that belong to lanes in the ignore list. -func (l *DefaultLane) Match(tx sdk.Tx) bool { +func (l *DefaultLane) Match(ctx sdk.Context, tx sdk.Tx) bool { + return !l.MatchIgnoreList(ctx, tx) +} + +// MatchIgnoreList returns true if any of the lanes that are in the ignore list +// match the current transaction. +func (l *DefaultLane) MatchIgnoreList(ctx sdk.Context, tx sdk.Tx) bool { for _, lane := range l.Cfg.IgnoreList { - if lane.Match(tx) { - return false + if lane.Match(ctx, tx) { + return true } } - return true + return false } // Name returns the name of the lane. diff --git a/blockbuster/lanes/base/mempool.go b/blockbuster/lanes/base/mempool.go index e2ca98f..54b2388 100644 --- a/blockbuster/lanes/base/mempool.go +++ b/blockbuster/lanes/base/mempool.go @@ -19,7 +19,7 @@ type ( sdkmempool.Mempool // Contains returns true if the transaction is contained in the mempool. - Contains(tx sdk.Tx) (bool, error) + Contains(tx sdk.Tx) bool } // DefaultMempool defines the most basic mempool. It can be seen as an extension of @@ -60,6 +60,7 @@ func (am *DefaultMempool) Insert(ctx context.Context, tx sdk.Tx) error { _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) if err != nil { + am.Remove(tx) return err } @@ -70,7 +71,17 @@ func (am *DefaultMempool) Insert(ctx context.Context, tx sdk.Tx) error { // Remove removes a transaction from the mempool based on the transaction type (normal or auction). func (am *DefaultMempool) Remove(tx sdk.Tx) error { - am.removeTx(am.index, tx) + if err := am.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) { + return fmt.Errorf("failed to remove transaction from the mempool: %w", err) + } + + _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) + if err != nil { + return fmt.Errorf("failed to get tx hash string: %w", err) + } + + delete(am.txIndex, txHashStr) + return nil } @@ -83,26 +94,12 @@ func (am *DefaultMempool) CountTx() int { } // Contains returns true if the transaction is contained in the mempool. -func (am *DefaultMempool) Contains(tx sdk.Tx) (bool, error) { +func (am *DefaultMempool) Contains(tx sdk.Tx) bool { _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) if err != nil { - return false, fmt.Errorf("failed to get tx hash string: %w", err) + return false } _, ok := am.txIndex[txHashStr] - return ok, nil -} - -func (am *DefaultMempool) removeTx(mp sdkmempool.Mempool, tx sdk.Tx) { - err := mp.Remove(tx) - if err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) { - panic(fmt.Errorf("failed to remove invalid transaction from the mempool: %w", err)) - } - - _, txHashStr, err := utils.GetTxHashStr(am.txEncoder, tx) - if err != nil { - panic(fmt.Errorf("failed to get tx hash string: %w", err)) - } - - delete(am.txIndex, txHashStr) + return ok } diff --git a/blockbuster/lanes/free/lane.go b/blockbuster/lanes/free/lane.go index 58db1ce..6413d1e 100644 --- a/blockbuster/lanes/free/lane.go +++ b/blockbuster/lanes/free/lane.go @@ -32,11 +32,10 @@ func NewFreeLane(cfg blockbuster.BaseLaneConfig, factory Factory) *Lane { } // Match returns true if the transaction is a free transaction. -func (l *Lane) Match(tx sdk.Tx) bool { +func (l *Lane) Match(ctx sdk.Context, tx sdk.Tx) bool { + if l.MatchIgnoreList(ctx, tx) { + return false + } + return l.IsFreeTx(tx) } - -// Name returns the name of the free lane. -func (l *Lane) Name() string { - return LaneName -} diff --git a/blockbuster/lanes/terminator/lane.go b/blockbuster/lanes/terminator/lane.go index 4174917..159d69a 100644 --- a/blockbuster/lanes/terminator/lane.go +++ b/blockbuster/lanes/terminator/lane.go @@ -51,7 +51,7 @@ func (t Terminator) Name() string { } // Match is a no-op -func (t Terminator) Match(sdk.Tx) bool { +func (t Terminator) Match(sdk.Context, sdk.Tx) bool { return false } @@ -61,8 +61,8 @@ func (t Terminator) VerifyTx(sdk.Context, sdk.Tx) error { } // Contains is a no-op -func (t Terminator) Contains(sdk.Tx) (bool, error) { - return false, nil +func (t Terminator) Contains(sdk.Tx) bool { + return false } // CountTx is a no-op @@ -86,7 +86,7 @@ func (t Terminator) Select(context.Context, [][]byte) sdkmempool.Iterator { } // ValidateLaneBasic is a no-op -func (t Terminator) ProcessLaneBasic([]sdk.Tx) error { +func (t Terminator) ProcessLaneBasic(sdk.Context, []sdk.Tx) error { return nil } diff --git a/blockbuster/mempool.go b/blockbuster/mempool.go index 6c8b412..82f997b 100644 --- a/blockbuster/mempool.go +++ b/blockbuster/mempool.go @@ -3,7 +3,9 @@ package blockbuster import ( "context" "fmt" + "strings" + "cosmossdk.io/log" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool" @@ -19,15 +21,12 @@ type ( // Registry returns the mempool's lane registry. Registry() []Lane - // Contains returns true if the transaction is contained in the mempool. - Contains(tx sdk.Tx) (bool, error) + // Contains returns the any of the lanes currently contain the transaction. + Contains(tx sdk.Tx) bool // GetTxDistribution returns the number of transactions in each lane. GetTxDistribution() map[string]int - // Match will return the lane that the transaction belongs to. - Match(tx sdk.Tx) (Lane, error) - // GetLane returns the lane with the given name. GetLane(name string) (Lane, error) } @@ -36,6 +35,7 @@ type ( // of lanes, which allows for customizable block proposal construction. BBMempool struct { registry []Lane + logger log.Logger } ) @@ -44,11 +44,13 @@ type ( // transactions according to its own selection logic. The lanes are ordered // according to their priority. The first lane in the registry has the highest // priority. Proposals are verified according to the order of the lanes in the -// registry. Basic mempool API, such as insertion, removal, and contains, are -// delegated to the first lane that matches the transaction. Each transaction -// should only belong in one lane. -func NewMempool(lanes ...Lane) *BBMempool { +// registry. Each transaction should only belong in one lane but this is NOT enforced. +// To enforce that each transaction belong to a single lane, you must configure the +// ignore list of each lane to include all preceding lanes. Basic mempool API will +// attempt to insert, remove transactions from all lanes it belongs to. +func NewMempool(logger log.Logger, lanes ...Lane) *BBMempool { mempool := &BBMempool{ + logger: logger, registry: lanes, } @@ -81,27 +83,28 @@ func (m *BBMempool) GetTxDistribution() map[string]int { return counts } -// Match will return the lane that the transaction belongs to. It matches to -// the first lane where lane.Match(tx) is true. -func (m *BBMempool) Match(tx sdk.Tx) (Lane, error) { - for _, lane := range m.registry { - if lane.Match(tx) { - return lane, nil - } - } - - return nil, fmt.Errorf("no lane matched transaction") -} - // Insert will insert a transaction into the mempool. It inserts the transaction // into the first lane that it matches. func (m *BBMempool) Insert(ctx context.Context, tx sdk.Tx) error { - lane, err := m.Match(tx) - if err != nil { - return err + var errors []string + + unwrappedCtx := sdk.UnwrapSDKContext(ctx) + for _, lane := range m.registry { + if !lane.Match(unwrappedCtx, tx) { + continue + } + + if err := lane.Insert(ctx, tx); err != nil { + m.logger.Debug("failed to insert tx into lane", "lane", lane.Name(), "err", err) + errors = append(errors, fmt.Sprintf("failed to insert tx into lane %s: %s", lane.Name(), err.Error())) + } } - return lane.Insert(ctx, tx) + if len(errors) == 0 { + return nil + } + + return fmt.Errorf(strings.Join(errors, ";")) } // Insert returns a nil iterator. @@ -114,26 +117,46 @@ func (m *BBMempool) Select(_ context.Context, _ [][]byte) sdkmempool.Iterator { return nil } -// Remove removes a transaction from the mempool based on the first lane that -// it matches. +// Remove removes a transaction from all of the lanes it is currently in. func (m *BBMempool) Remove(tx sdk.Tx) error { - lane, err := m.Match(tx) - if err != nil { - return err + var errors []string + + for _, lane := range m.registry { + if !lane.Contains(tx) { + continue + } + + if err := lane.Remove(tx); err != nil { + m.logger.Debug("failed to remove tx from lane", "lane", lane.Name(), "err", err) + + // We only care about errors that are not "tx not found" errors. + // + // TODO: Figure out whether we should be erroring in the mempool if + // the tx is not found in the lane. Downstream, if the removal fails runTx will + // error out and will NOT execute runMsgs (which is where the tx is actually + // executed). + if err != sdkmempool.ErrTxNotFound { + errors = append(errors, fmt.Sprintf("failed to remove tx from lane %s: %s;", lane.Name(), err.Error())) + } + } } - return lane.Remove(tx) + if len(errors) == 0 { + return nil + } + + return fmt.Errorf(strings.Join(errors, ";")) } -// Contains returns true if the transaction is contained in the mempool. It -// checks the first lane that it matches to. -func (m *BBMempool) Contains(tx sdk.Tx) (bool, error) { - lane, err := m.Match(tx) - if err != nil { - return false, err +// Contains returns true if the transaction is contained in any of the lanes. +func (m *BBMempool) Contains(tx sdk.Tx) bool { + for _, lane := range m.registry { + if lane.Contains(tx) { + return true + } } - return lane.Contains(tx) + return false } // Registry returns the mempool's lane registry. diff --git a/blockbuster/mempool_test.go b/blockbuster/mempool_test.go index d794664..2384a1b 100644 --- a/blockbuster/mempool_test.go +++ b/blockbuster/mempool_test.go @@ -55,35 +55,54 @@ func (suite *BlockBusterTestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - config := blockbuster.BaseLaneConfig{ + tobConfig := blockbuster.BaseLaneConfig{ Logger: log.NewNopLogger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), AnteHandler: nil, MaxBlockSpace: math.LegacyZeroDec(), } - - // Top of block lane set up suite.tobLane = auction.NewTOBLane( - config, + tobConfig, 0, // No bound on the number of transactions in the lane auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Free lane set up + freeConfig := blockbuster.BaseLaneConfig{ + Logger: log.NewNopLogger(), + TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: nil, + MaxBlockSpace: math.LegacyZeroDec(), + IgnoreList: []blockbuster.Lane{ + suite.tobLane, + }, + } suite.freeLane = free.NewFreeLane( - config, + freeConfig, free.NewDefaultFreeFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Base lane set up + baseConfig := blockbuster.BaseLaneConfig{ + Logger: log.NewNopLogger(), + TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: nil, + MaxBlockSpace: math.LegacyZeroDec(), + IgnoreList: []blockbuster.Lane{ + suite.tobLane, + suite.freeLane, + }, + } suite.baseLane = base.NewDefaultLane( - config, + baseConfig, ) // Mempool set up suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(suite.lanes...) + suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...) // Accounts set up suite.accounts = testutils.RandomAccounts(suite.random, 10) @@ -179,20 +198,20 @@ func (suite *BlockBusterTestSuite) TestInsert() { } // Validate the mempool - suite.Require().Equal(suite.mempool.CountTx(), sum) + suite.Require().Equal(sum, suite.mempool.CountTx()) // Validate the lanes - suite.Require().Equal(suite.baseLane.CountTx(), tc.insertDistribution[suite.baseLane.Name()]) - suite.Require().Equal(suite.tobLane.CountTx(), tc.insertDistribution[suite.tobLane.Name()]) - suite.Require().Equal(suite.freeLane.CountTx(), tc.insertDistribution[suite.freeLane.Name()]) + suite.Require().Equal(tc.insertDistribution[suite.tobLane.Name()], suite.tobLane.CountTx()) + suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], suite.baseLane.CountTx()) + suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], suite.freeLane.CountTx()) // Validate the lane counts laneCounts := suite.mempool.GetTxDistribution() // Ensure that the lane counts are correct - suite.Require().Equal(laneCounts[suite.tobLane.Name()], tc.insertDistribution[suite.tobLane.Name()]) - suite.Require().Equal(laneCounts[suite.baseLane.Name()], tc.insertDistribution[suite.baseLane.Name()]) - suite.Require().Equal(laneCounts[suite.freeLane.Name()], tc.insertDistribution[suite.freeLane.Name()]) + suite.Require().Equal(tc.insertDistribution[suite.tobLane.Name()], laneCounts[suite.tobLane.Name()]) + suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], laneCounts[suite.baseLane.Name()]) + suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], laneCounts[suite.freeLane.Name()]) }) } } @@ -250,9 +269,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { suite.Require().NoError(suite.mempool.Remove(tx)) // Ensure that the transaction is no longer in the mempool - contains, err := suite.mempool.Contains(tx) - suite.Require().NoError(err) - suite.Require().False(contains) + suite.Require().Equal(false, suite.mempool.Contains(tx)) // Ensure the number of transactions in the lane is correct baseCount-- @@ -275,9 +292,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { suite.Require().NoError(suite.mempool.Remove(tx)) // Ensure that the transaction is no longer in the mempool - contains, err := suite.mempool.Contains(tx) - suite.Require().NoError(err) - suite.Require().False(contains) + suite.Require().Equal(false, suite.mempool.Contains(tx)) // Ensure the number of transactions in the lane is correct tobCount-- diff --git a/blockbuster/utils/ante.go b/blockbuster/utils/ante.go index 7d4708c..418662e 100644 --- a/blockbuster/utils/ante.go +++ b/blockbuster/utils/ante.go @@ -8,7 +8,7 @@ type ( // Lane defines the required functionality for a lane. The ignore decorator // will check if a transaction belongs to a lane by calling the Match function. Lane interface { - Match(tx sdk.Tx) bool + Match(ctx sdk.Context, tx sdk.Tx) bool } // IgnoreDecorator is an AnteDecorator that wraps an existing AnteDecorator. It allows @@ -34,7 +34,7 @@ func (sd IgnoreDecorator) AnteHandle( ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler, ) (sdk.Context, error) { for _, lane := range sd.lanes { - if lane.Match(tx) { + if lane.Match(ctx, tx) { return next(ctx, tx, simulate) } } diff --git a/tests/app/app.go b/tests/app/app.go index ab94df1..3742d8a 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -310,7 +310,7 @@ func New( freeLane, defaultLane, } - mempool := blockbuster.NewMempool(lanes...) + mempool := blockbuster.NewMempool(app.Logger(), lanes...) app.App.SetMempool(mempool) // Create a global ante handler that will be called on each transaction when diff --git a/x/builder/ante/ante.go b/x/builder/ante/ante.go index 3d587a0..4c0a496 100644 --- a/x/builder/ante/ante.go +++ b/x/builder/ante/ante.go @@ -23,7 +23,7 @@ type ( // Mempool is an interface that defines the methods required to interact with the application-side mempool. Mempool interface { - Contains(tx sdk.Tx) (bool, error) + Contains(tx sdk.Tx) bool } // BuilderDecorator is an AnteDecorator that validates the auction bid and bundled transactions. @@ -49,12 +49,7 @@ func NewBuilderDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane TOBLane func (bd BuilderDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { // If comet is re-checking a transaction, we only need to check if the transaction is in the application-side mempool. if ctx.IsReCheckTx() { - contains, err := bd.mempool.Contains(tx) - if err != nil { - return ctx, err - } - - if !contains { + if !bd.mempool.Contains(tx) { return ctx, fmt.Errorf("transaction not found in application-side mempool") } } diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index 01ed63c..1c93700 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "cosmossdk.io/log" "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/testutil" @@ -82,7 +83,7 @@ func (suite *AnteTestSuite) SetupTest() { // Lanes configuration // // TOB lane set up - config := blockbuster.BaseLaneConfig{ + tobConfig := blockbuster.BaseLaneConfig{ Logger: suite.ctx.Logger(), TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), @@ -90,17 +91,25 @@ func (suite *AnteTestSuite) SetupTest() { MaxBlockSpace: math.LegacyZeroDec(), } suite.tobLane = auction.NewTOBLane( - config, + tobConfig, 0, // No bound on the number of transactions in the lane auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()), ) // Base lane set up - suite.baseLane = base.NewDefaultLane(config) + baseConfig := blockbuster.BaseLaneConfig{ + Logger: suite.ctx.Logger(), + TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(), + TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(), + AnteHandler: suite.anteHandler, + MaxBlockSpace: math.LegacyZeroDec(), + IgnoreList: []blockbuster.Lane{suite.tobLane}, + } + suite.baseLane = base.NewDefaultLane(baseConfig) // Mempool set up suite.lanes = []blockbuster.Lane{suite.tobLane, suite.baseLane} - suite.mempool = blockbuster.NewMempool(suite.lanes...) + suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), suite.lanes...) } func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {