feat(BB): Update match function to include sdk.Context (#234)
This commit is contained in:
parent
128cc26f1a
commit
14f83b5e25
246
README.md
246
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
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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--
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user