Merge pull request #1 from skip-mev/terpay/rename-to-block-sdk

feat(Block SDK): We live
This commit is contained in:
David Terpay 2023-08-17 15:59:18 -04:00 committed by GitHub
commit c1ce98a6e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1694 additions and 1501 deletions

336
README.md
View File

@ -1,4 +1,4 @@
<h1 align="center">Protocol-Owned Builder</h1>
<h1 align="center">Block SDK 🧱</h1>
<!-- markdownlint-disable MD013 -->
<!-- markdownlint-disable MD041 -->
@ -9,337 +9,21 @@
[![License: Apache-2.0](https://img.shields.io/github/license/skip-mev/pob.svg?style=flat-square)](https://github.com/skip-mev/pob/blob/main/LICENSE)
[![Lines Of Code](https://img.shields.io/tokei/lines/github/skip-mev/pob?style=flat-square)](https://github.com/skip-mev/pob)
Skip Protocol's Protocol-Owned Builder (POB) is a set of Cosmos SDK and ABCI++
primitives that provide application developers the ability to define how their
apps construct and validate blocks on-chain in a transparent, enforceable way,
such as giving complete control to the protocol to recapture, control, and
redistribute MEV.
### 🤔 What is the Block SDK?
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.
## Releases
### Release Compatibility Matrix
| 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
```shell
$ 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
blockbuster proposal handlers +mempool, keeper, builder types, and builder module. This
tutorial will go into more detail into each of the dependencies.
```go
import (
...
"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"
...
)
```
2. Add your module to the the `AppModuleBasic` manager. This manager is in
charge of setting up basic, non-dependent module elements such as codec
registration and genesis verification. This will register the special
`MsgAuctionBid` message. When users want to bid for top of block execution,
they will submit a transaction - which we call an auction transaction - that
includes a single `MsgAuctionBid`. We prevent any other messages from being
included in auction transaction to prevent malicious behavior - such as front
running or sandwiching.
```go
var (
ModuleBasics = module.NewBasicManager(
...
buildermodule.AppModuleBasic{},
)
...
)
```
3. The builder `Keeper` is POB's gateway to processing special `MsgAuctionBid`
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. 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 {
...
// BuilderKeeper is the keeper that handles processing auction transactions
BuilderKeeper builderkeeper.Keeper
// Custom checkTx handler
checkTxHandler abci.CheckTx
}
```
b. Add the builder module to the list of module account permissions. This will
instantiate the builder module account on genesis.
```go
maccPerms = map[string][]string{
builder.ModuleName: nil,
...
}
```
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
keys := storetypes.NewKVStoreKeys(
buildertypes.StoreKey,
...
)
...
app.BuilderKeeper := builderkeeper.NewKeeper(
appCodec,
keys[buildertypes.StoreKey],
app.AccountKeeper,
app.BankKeeper,
app.DistrKeeper,
app.StakingKeeper,
authtypes.NewModuleAddress(govv1.ModuleName).String(),
)
app.ModuleManager = module.NewManager(
builder.NewAppModule(appCodec, app.BuilderKeeper),
...
)
```
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 `BlockBuster` mempool + `PrepareProposal`/`ProcessProposal`
handlers allows the application to verifiably build valid blocks with
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
discrepencies between the auction transaction and the bundled transactions
are validated in `CheckTx` and `PrepareProposal` such that the auction can be
griefed. All other transactions will be executed with base app's `CheckTx`.
```go
// Create the proposal handler that will be used to build and validate blocks.
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.txConfig.TxDecoder(),
mempool,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
**🌐 The Block SDK is a toolkit for building customized blocks**. The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality.
// Set the custom CheckTx handler on BaseApp.
checkTxHandler := abci.NewCheckTxHandler(
app.App,
app.txConfig.TxDecoder(),
tobLane,
anteHandler,
app.ChainID(),
)
app.SetCheckTx(checkTxHandler.CheckTx())
...
Skip has built out a number of plug-and-play `lanes` on the SDK that your protocol can use, including in-protocol MEV recapture and Oracles! Additionally, the Block SDK can be extended to add **your own custom `lanes`** to configure your blocks to exactly fit your application needs.
// CheckTx will check the transaction with the provided checkTxHandler. We override the default
// handler so that we can verify bid transactions before they are inserted into the mempool.
// With the POB CheckTx, we can verify the bid transaction and all of the bundled transactions
// before inserting the bid transaction into the mempool.
func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) cometabci.ResponseCheckTx {
return app.checkTxHandler(req)
}
### 📚 Block SDK Documentation
// SetCheckTx sets the checkTxHandler for the app.
func (app *TestApp) SetCheckTx(handler abci.CheckTx) {
app.checkTxHandler = handler
}
```
To read more about how the Block SDK works, check out the [How it Works](https://docs.skip.money/chains/overview).
f. Finally, update the app's `InitGenesis` order and ante-handler chain.
#### Lane "App Store"
```go
genesisModuleOrder := []string{
buildertypes.ModuleName,
...,
}
```
To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default).
## Params
#### Lane Development
Note, before building or upgrading the application, make sure to initialize the
escrow address for POB in the parameters of the module. The default parameters
initialize the escrow address to be the module account address. The escrow address
will be the address that is receiving a portion of auction house revenue alongside the proposer (or custom rewards providers).
To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](https://docs.skip.money/chains/lanes/build-your-own-lane).

311
SPEC.md
View File

@ -1,311 +0,0 @@
# POB Specification
## Abstract
The `x/builder` module is a Cosmos SDK module that allows Cosmos chains to host
top-of-block auctions directly in-protocol with auction revenue (MEV) being
redistributed according to the preferences of the chain. The `x/builder` module
introduces a new `MsgAuctionBid` message that allows users to submit a bid
alongside an ordered list of transactions, i.e. a **bundle**, that they want
executed at top-of-block before any other transactions are executed for that
block. The `x/builder` module works alongside the `AuctionMempool` such that:
* Auctions are held directly in the `AuctionMempool`, where a winner is determined
when the proposer proposes a new block in `PrepareProposal`.
* `x/builder` provides the necessary validation of auction bids and subsequent
state transitions to extract bids.
## Concepts
### Miner Extractable Value (MEV)
MEV refers to the potential profit that miners, or validators in a Proof-of-Stake
system, can make by strategically ordering, selecting, or even censoring
transactions in the blocks they produce. MEV can be classified into "good MEV"
and "bad MEV" based on the effects it has on the blockchain ecosystem and its
users. It's important to note that these classifications are subjective and may
vary depending on one's perspective.
**Good MEV** refers to the value that validators can extract while contributing
positively to the blockchain ecosystem. This typically includes activities that
enhance network efficiency, maintain fairness, and align incentives with the
intended use of the system. Examples of good MEV include:
* **Back-running**: Validators can place their own transactions immediately
after a profitable transaction, capitalizing on the changes caused by the
preceding transaction.
* **Arbitrage**: By exploiting price differences across decentralized exchanges
or other DeFi platforms, validators help maintain more consistent price levels
across the ecosystem, ultimately contributing to its stability.
* **Liquidations**: In DeFi platforms, when users' collateral falls below a
specific threshold, validators can liquidate these positions, thereby maintaining
the overall health of the platform and protecting its users from insolvency risks.
**Bad MEV** refers to the value that validators can extract through activities
that harm the blockchain ecosystem, lead to unfair advantages, or exploit users.
Examples of bad MEV include:
* **Front-running**: Validators can observe pending transactions in the mempool
(the pool of unconfirmed transactions) and insert their own transactions ahead
of them. This can be particularly profitable in decentralized finance (DeFi)
applications, where a validator could front-run a large trade to take advantage
of price movements.
* **Sandwich attacks**: Validators can surround a user's transaction with their
own transactions, effectively manipulating the market price for their benefit.
* **Censorship**: Validators can selectively exclude certain transactions from
blocks to benefit their own transactions or to extract higher fees from users.
MEV is a topic of concern in the blockchain community because it can lead to
unfair advantages for validators, reduced trust in the system, and a potential
concentration of power. Various approaches have been proposed to mitigate MEV,
such as proposer-builder separation (described below) and transparent and fair
transaction ordering mechanisms at the protocol-level (`POB`) to make MEV
extraction more incentive aligned with the users and blockchain ecosystem.
### Proposer Builder Separation (PBS)
Proposer-builder separation is a concept in the design of blockchain protocols,
specifically in the context of transaction ordering within a block. In traditional
blockchain systems, validators perform two main tasks: they create new blocks
(acting as proposers) and determine the ordering of transactions within those
blocks (acting as builders).
**Proposers**: They are responsible for creating and broadcasting new blocks,
just like in traditional blockchain systems. *However, they no longer determine
the ordering of transactions within those blocks*.
**Builders**: They have the exclusive role of determining the order of transactions
within a block - can be full or partial block. Builders submit their proposed
transaction orderings to an auction mechanism, which selects the winning template
based on predefined criteria, e.g. highest bid.
This dual role can lead to potential issues, such as front-running and other
manipulations that benefit the miners/builders themselves.
* *Increased complexity*: Introducing PBS adds an extra layer of complexity to
the blockchain protocol. Designing, implementing, and maintaining an auction
mechanism for transaction ordering requires additional resources and may
introduce new vulnerabilities or points of failure in the system.
* *Centralization risks*: With PBS, there's a risk that a few dominant builders
may emerge, leading to centralization of transaction ordering. This centralization
could result in a lack of diversity in transaction ordering algorithms and an
increased potential for collusion or manipulation by the dominant builders.
* *Incentive misalignments*: The bidding process may create perverse incentives
for builders. For example, builders may be incentivized to include only high-fee
transactions to maximize their profits, potentially leading to a neglect of
lower-fee transactions. Additionally, builders may be incentivized to build
blocks that include **bad-MEV** strategies because they are more profitable.
## Specification
### Mempool
As the lifeblood of blockchains, mempools serve as the intermediary space for
pending transactions, playing a vital role in transaction management, fee markets,
and network health. With ABCI++, mempools can be defined at the application layer
instead of the consensus layer (CometBFT). This means applications can define
their own mempools that have their own custom verification, block building, and
state transition logic. Adding on, these changes make it such that blocks are
built (`PrepareProposal`) and verified (`ProcessProposal`) directly in the
application layer.
The `x/builder` module implements an application-side mempool, `AuctionMempool`,
that implements the `sdk.Mempool` interface. The mempool is composed of two
primary indexes, a global index that contains all non-auction transactions and
an index that only contains auction transactions, i.e. transactions with a single
`MsgAuctionBid` message. Both indexes order transactions based on priority respecting
the sender's sequence number. The global index prioritizes transactions based on
`ctx.Priority()` and the auction index prioritizes transactions based on the
bid.
### Configuration
The `AuctionMempool` mempool implementation accepts a `AuctionFactory`
interface that allows the mempool to be generic across many Cosmos SDK
applications, such that it allows the ability for the application developer to
define their business logic in terms of how to perform things such as the following:
* Getting tx signers
* Getting bundled tx signers
* Retrieving bid information
```go
// AuctionFactory defines the interface for processing auction transactions.
// It is a wrapper around all of the functionality that each application chain
// must implement in order for auction processing to work.
type AuctionFactory interface {
// WrapBundleTransaction defines a function that wraps a bundle transaction
// into a sdk.Tx. Since this is a potentially expensive operation, we allow
// each application chain to define how they want to wrap the transaction
// such that it is only called when necessary (i.e. when the transaction is
// being considered in the proposal handlers).
WrapBundleTransaction(tx []byte) (sdk.Tx, error)
// GetAuctionBidInfo defines a function that returns the bid info from an
// auction transaction.
GetAuctionBidInfo(tx sdk.Tx) (*AuctionBidInfo, error)
}
```
### PrepareProposal
After the proposer of the next block has been selected, the CometBFT client will
call `PrepareProposal` to build the next block. The block will be built in two
stages. First, it will host the auction and include the winning bidder's bundle
as the first set of transactions for the block, i.e. it will select the bid
transaction itself along with automatically including all the bundled transactions
in the specified order they appear in the bid's `transactions` field.
The auction currently supports only a single winner. Selecting the auction winner
involves a greedy search for a valid auction transaction starting from highest
paying bid, respecting user nonce, in the `AuctionMempool`. The `x/builder`'s
ante handler is responsible for verifying the auction transaction based on the
criteria described below (see **Ante Handler**).
Then, it will build the rest of the block by reaping and validating the transactions
in the global index. The second portion of block building iterates from highest
to lowest priority transactions in the global index and adds them to the proposal
if they are valid. If the proposer comes across a transaction that was already
included in the top of block, it will be ignored.
### ProcessProposal
After the proposer proposes a block of transactions for the next block, the
block will be verified by other nodes in the network in `ProcessProposal`. If
there is an auction transaction in the proposal, it must be the first transaction
in the proposal and all bundled transactions must follow the auction transaction
in the exact order we would expect them to be seen. If this fails, the proposal
is rejected. If this passes, the validator will then run `CheckTx` on all of the
transactions in the block in the order in which they were provided in the proposal.
### Ante Handler
When users want to bid for the rights for top-of-block execution they will submit
a normal `sdk.Tx` transaction with a single `MsgAuctionBid`. The ante handler is
responsible for verification of this transaction. The ante handler will verify that:
1. The auction transaction specifies a timeout height where the bid is no longer
considered valid. Note, it is REQUIRED that all bid transactions include a
height timeout.
2. The auction transaction includes less than `MaxBundleSize` transactions in
its bundle.
3. The auction transaction includes only a SINGLE `MsgAuctionBid` message. We
enforce that no other messages are included to prevent front-running.
4. Enforce that the user has sufficient funds to pay the bid they entered while
covering all relevant auction fees.
5. Enforce that the transaction's min bid increment greater than the local highest
bid in the mempool.
6. Enforce that the bundle of transactions the bidder provided does not front-run
or sandwich (if enabled).
Note, the process of selecting auction winners occurs in a greedy manner. In
`PrepareProposal`, the `AuctionMempool` will iterate from largest to smallest
bidding transaction until it finds the first valid bid transaction.
### State
The `x/builder` module stores the following state objects:
```protobuf
message Params {
option (amino.name) = "cosmos-sdk/x/builder/Params";
// max_bundle_size is the maximum number of transactions that can be bundled
// in a single bundle.
uint32 max_bundle_size = 1;
// escrow_account_address is the address of the account that will receive a
// portion of the bid proceeds.
string escrow_account_address = 2;
// reserve_fee specifies the bid floor for the auction.
cosmos.base.v1beta1.Coin reserve_fee = 3
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
// min_buy_in_fee specifies the fee that the bidder must pay to enter the
// auction.
cosmos.base.v1beta1.Coin min_buy_in_fee = 4
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
// min_bid_increment specifies the minimum amount that the next bid must be
// greater than the previous bid.
cosmos.base.v1beta1.Coin min_bid_increment = 5
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
// front_running_protection specifies whether front running and sandwich
// attack protection is enabled.
bool front_running_protection = 6;
// proposer_fee defines the portion of the winning bid that goes to the block
// proposer that proposed the block.
string proposer_fee = 7 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"
];
}
```
## Messages
### MsgAuctionBid
POB defines a new Cosmos SDK `Message`, `MsgAuctionBid`, that allows users to
create an auction bid and participate in a top-of-block auction. The `MsgAuctionBid`
message defines a bidder and a series of embedded transactions, i.e. the bundle.
```protobuf
message MsgAuctionBid {
option (cosmos.msg.v1.signer) = "bidder";
option (amino.name) = "pob/x/builder/MsgAuctionBid";
option (gogoproto.equal) = false;
// bidder is the address of the account that is submitting a bid to the
// auction.
string bidder = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// bid is the amount of coins that the bidder is bidding to participate in the
// auction.
cosmos.base.v1beta1.Coin bid = 3
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ];
// transactions are the bytes of the transactions that the bidder wants to
// bundle together.
repeated bytes transactions = 4;
}
```
Note, the `transactions` may or may not exist in a node's application mempool. If
a transaction containing a single `MsgAuctionBid` wins the auction, the block
proposal will automatically include the `MsgAuctionBid` transaction along with
injecting all the bundled transactions such that they are executed in the same
order after the `MsgAuctionBid` transaction.
When processing a `MsgAuctionBid`, the `x/builder` module will perform two primary
actions:
1. Ensure the bid is valid per the module's parameters and configuration.
2. Extract fee payments from the bidder's account and escrow them to the module's
escrow account and the proposer that included the winning bid in the block
proposal.
### MsgUpdateParams
The `MsgUpdateParams` message allows for an authority, typically the `x/gov`
module account, to update the `x/builder`'s parameters.
```protobuf
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "pob/x/builder/MsgUpdateParams";
option (gogoproto.equal) = false;
// authority is the address of the account that is authorized to update the
// x/builder module parameters.
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// params is the new parameters for the x/builder module.
Params params = 2 [ (gogoproto.nullable) = false ];
}
```

View File

@ -6,9 +6,9 @@ import (
"cosmossdk.io/log"
abci "github.com/cometbft/cometbft/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/blockbuster/lanes/terminator"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/utils"
"github.com/skip-mev/pob/lanes/terminator"
)
type (
@ -17,14 +17,14 @@ type (
ProposalHandler struct {
logger log.Logger
txDecoder sdk.TxDecoder
prepareLanesHandler blockbuster.PrepareLanesHandler
processLanesHandler blockbuster.ProcessLanesHandler
prepareLanesHandler block.PrepareLanesHandler
processLanesHandler block.ProcessLanesHandler
}
)
// NewProposalHandler returns a new abci++ proposal handler. This proposal handler will
// iteratively call each of the lanes in the chain to prepare and process the proposal.
func NewProposalHandler(logger log.Logger, txDecoder sdk.TxDecoder, lanes []blockbuster.Lane) *ProposalHandler {
func NewProposalHandler(logger log.Logger, txDecoder sdk.TxDecoder, lanes []block.Lane) *ProposalHandler {
return &ProposalHandler{
logger: logger,
txDecoder: txDecoder,
@ -48,7 +48,7 @@ func (h *ProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
}
}()
proposal, err := h.prepareLanesHandler(ctx, blockbuster.NewProposal(req.MaxTxBytes))
proposal, err := h.prepareLanesHandler(ctx, block.NewProposal(req.MaxTxBytes))
if err != nil {
h.logger.Error("failed to prepare proposal", "err", err)
return &abci.ResponsePrepareProposal{Txs: make([][]byte, 0)}, err
@ -114,7 +114,7 @@ func (h *ProposalHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {
//
// In the case where any of the lanes fail to prepare the partial proposal, the lane that failed
// will be skipped and the next lane in the chain will be called to prepare the proposal.
func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandler {
func ChainPrepareLanes(chain ...block.Lane) block.PrepareLanesHandler {
if len(chain) == 0 {
return nil
}
@ -124,7 +124,7 @@ func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandle
chain = append(chain, terminator.Terminator{})
}
return func(ctx sdk.Context, partialProposal blockbuster.BlockProposal) (finalProposal blockbuster.BlockProposal, err error) {
return func(ctx sdk.Context, partialProposal block.BlockProposal) (finalProposal block.BlockProposal, err error) {
lane := chain[0]
lane.Logger().Info("preparing lane", "lane", lane.Name())
@ -182,7 +182,7 @@ func ChainPrepareLanes(chain ...blockbuster.Lane) blockbuster.PrepareLanesHandle
// ChainProcessLanes chains together the proposal verification logic from each lane
// into a single function. The first lane in the chain is the first lane to be verified and
// the last lane in the chain is the last lane to be verified.
func ChainProcessLanes(chain ...blockbuster.Lane) blockbuster.ProcessLanesHandler {
func ChainProcessLanes(chain ...block.Lane) block.ProcessLanesHandler {
if len(chain) == 0 {
return nil
}

View File

@ -13,11 +13,12 @@ import (
cometabci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/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"
"github.com/skip-mev/pob/abci"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
defaultlane "github.com/skip-mev/pob/lanes/base"
"github.com/skip-mev/pob/lanes/free"
"github.com/skip-mev/pob/lanes/mev"
testutils "github.com/skip-mev/pob/testutils"
"github.com/stretchr/testify/suite"
)
@ -54,9 +55,9 @@ func (s *ProposalsTestSuite) SetupTest() {
func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Run("can prepare a proposal with no transactions", func() {
// Set up the default lane with no transactions
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), nil)
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), nil)
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{})
s.Require().NoError(err)
@ -77,10 +78,10 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the default lane
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true})
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000})
s.Require().NotNil(resp)
s.Require().NoError(err)
@ -114,11 +115,11 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the default lane with both transactions passing
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: true})
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: true})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1))
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000})
s.Require().NotNil(resp)
s.Require().NoError(err)
@ -152,11 +153,11 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the default lane with both transactions passing
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: false})
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx1: true, tx2: false})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx1))
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx2))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000})
s.Require().NotNil(resp)
s.Require().NoError(err)
@ -167,10 +168,10 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
})
s.Run("can build a proposal an empty proposal with multiple lanes", func() {
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil)
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil)
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), nil)
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{})
s.Require().NoError(err)
@ -192,15 +193,15 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the TOB lane with the bid tx and the bundled tx
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
tx: true,
bundleTxs[0]: true,
})
s.Require().NoError(tobLane.Insert(sdk.Context{}, tx))
s.Require().NoError(mevLane.Insert(sdk.Context{}, tx))
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil)
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), nil)
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000})
s.Require().NoError(err)
@ -224,21 +225,21 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the TOB lane with the bid tx and the bundled tx
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
tx: true,
bundleTxs[0]: true,
})
s.Require().NoError(tobLane.Insert(sdk.Context{}, tx))
s.Require().NoError(mevLane.Insert(sdk.Context{}, tx))
// Set up the default lane with the bid tx and the bundled tx
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
tx: true,
bundleTxs[0]: true,
})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0]))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000})
s.Require().NoError(err)
@ -262,14 +263,14 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the TOB lane with the bid tx and the bundled tx
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
tx: false,
bundleTxs[0]: true,
})
s.Require().NoError(tobLane.Insert(sdk.Context{}, tx))
s.Require().NoError(mevLane.Insert(sdk.Context{}, tx))
// Set up the default lane with the bid tx and the bundled tx
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
// Even though this passes it should not include it in the proposal because it is in the ignore list
tx: true,
bundleTxs[0]: true,
@ -277,7 +278,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0]))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 10000000000})
s.Require().NoError(err)
@ -301,14 +302,14 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(err)
// Set up the TOB lane with the bid tx and the bundled tx
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), map[sdk.Tx]bool{
tx: true,
bundleTxs[0]: true,
})
s.Require().NoError(tobLane.Insert(sdk.Context{}, tx))
s.Require().NoError(mevLane.Insert(sdk.Context{}, tx))
// Set up the default lane with the bid tx and the bundled tx
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
// Even though this passes it should not include it in the proposal because it is in the ignore list
tx: true,
bundleTxs[0]: true,
@ -316,7 +317,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
s.Require().NoError(defaultLane.Insert(sdk.Context{}, bundleTxs[0]))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).PrepareProposalHandler()
proposal := s.getTxBytes(tx, bundleTxs[0])
size := int64(len(proposal[0]) - 1)
@ -329,7 +330,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
})
s.Run("can build a proposal with single tx from middle lane", func() {
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), nil)
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), nil)
freeTx, err := testutils.CreateFreeTx(
s.encodingConfig.TxConfig,
@ -342,7 +343,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
)
s.Require().NoError(err)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
freeTx: true,
})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, freeTx))
@ -352,7 +353,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
})
s.Require().NoError(freeLane.Insert(sdk.Context{}, freeTx))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler()
proposal := s.getTxBytes(freeTx)
@ -399,16 +400,16 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
)
s.Require().NoError(err)
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{
tx: true,
bundleTxs[0]: true,
bundleTxs[1]: true,
bundleTxs[2]: true,
bundleTxs[3]: true,
})
tobLane.Insert(sdk.Context{}, tx)
mevLane.Insert(sdk.Context{}, tx)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
normalTx: true,
})
defaultLane.Insert(sdk.Context{}, normalTx)
@ -418,7 +419,7 @@ func (s *ProposalsTestSuite) TestPrepareProposal() {
})
freeLane.Insert(sdk.Context{}, freeTx)
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).PrepareProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).PrepareProposalHandler()
proposal := s.getTxBytes(tx, bundleTxs[0], bundleTxs[1], bundleTxs[2], bundleTxs[3], freeTx, normalTx)
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000000})
@ -444,7 +445,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
tx: true,
})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
@ -452,7 +453,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
proposalHandler := abci.NewProposalHandler(
log.NewTestLogger(s.T()),
s.encodingConfig.TxConfig.TxDecoder(),
[]blockbuster.Lane{panicLane, defaultLane},
[]block.Lane{panicLane, defaultLane},
).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000})
@ -477,7 +478,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
tx: true,
})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
@ -485,7 +486,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
proposalHandler := abci.NewProposalHandler(
log.NewTestLogger(s.T()),
s.encodingConfig.TxConfig.TxDecoder(),
[]blockbuster.Lane{defaultLane, panicLane},
[]block.Lane{defaultLane, panicLane},
).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000})
@ -511,7 +512,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
tx: true,
})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
@ -519,7 +520,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
proposalHandler := abci.NewProposalHandler(
log.NewTestLogger(s.T()),
s.encodingConfig.TxConfig.TxDecoder(),
[]blockbuster.Lane{panicLane, panicLane2, defaultLane},
[]block.Lane{panicLane, panicLane2, defaultLane},
).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000})
@ -545,7 +546,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
)
s.Require().NoError(err)
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{
tx: true,
})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
@ -553,7 +554,7 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
proposalHandler := abci.NewProposalHandler(
log.NewTestLogger(s.T()),
s.encodingConfig.TxConfig.TxDecoder(),
[]blockbuster.Lane{defaultLane, panicLane, panicLane2},
[]block.Lane{defaultLane, panicLane, panicLane2},
).PrepareProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestPrepareProposal{MaxTxBytes: 1000000})
@ -568,11 +569,11 @@ func (s *ProposalsTestSuite) TestPrepareProposalEdgeCases() {
func (s *ProposalsTestSuite) TestProcessProposal() {
s.Run("can process a valid empty proposal", func() {
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{})
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{})
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).ProcessProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: nil})
s.Require().NoError(err)
@ -581,11 +582,11 @@ func (s *ProposalsTestSuite) TestProcessProposal() {
})
s.Run("rejects a proposal with bad txs", func() {
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
freeLane := s.setUpFreeLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{})
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.0"), map[sdk.Tx]bool{})
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, freeLane, defaultLane}).ProcessProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, freeLane, defaultLane}).ProcessProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{{0x01, 0x02, 0x03}}})
s.Require().Error(err)
@ -593,7 +594,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() {
})
s.Run("rejects a proposal when a lane panics", func() {
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.25"), map[sdk.Tx]bool{})
panicLane := s.setUpPanicLane(math.LegacyMustNewDecFromStr("0.0"))
txbz, err := testutils.CreateRandomTxBz(
@ -605,7 +606,7 @@ func (s *ProposalsTestSuite) TestProcessProposal() {
)
s.Require().NoError(err)
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, panicLane}).ProcessProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, panicLane}).ProcessProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: [][]byte{txbz}})
s.Require().Error(err)
s.Require().Equal(&cometabci.ResponseProcessProposal{Status: cometabci.ResponseProcessProposal_REJECT}, resp)
@ -635,10 +636,10 @@ func (s *ProposalsTestSuite) TestProcessProposal() {
s.Require().NoError(err)
// Set up the default lane
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true})
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("1"), map[sdk.Tx]bool{tx: true})
s.Require().NoError(defaultLane.Insert(sdk.Context{}, tx))
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{defaultLane}).ProcessProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{defaultLane}).ProcessProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(tx, tx2)})
s.Require().NotNil(resp)
s.Require().Error(err)
@ -676,14 +677,14 @@ func (s *ProposalsTestSuite) TestProcessProposal() {
s.Require().NoError(err)
// Set up the default lane
defaultLane := s.setUpDefaultLane(math.LegacyMustNewDecFromStr("0.5"), nil)
defaultLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler())
defaultLane := s.setUpStandardLane(math.LegacyMustNewDecFromStr("0.5"), nil)
defaultLane.SetProcessLaneHandler(block.NoOpProcessLaneHandler())
// Set up the TOB lane
tobLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil)
tobLane.SetProcessLaneHandler(blockbuster.NoOpProcessLaneHandler())
mevLane := s.setUpTOBLane(math.LegacyMustNewDecFromStr("0.5"), nil)
mevLane.SetProcessLaneHandler(block.NoOpProcessLaneHandler())
proposalHandler := s.setUpProposalHandlers([]blockbuster.Lane{tobLane, defaultLane}).ProcessProposalHandler()
proposalHandler := s.setUpProposalHandlers([]block.Lane{mevLane, defaultLane}).ProcessProposalHandler()
resp, err := proposalHandler(s.ctx, &cometabci.RequestProcessProposal{Txs: s.getTxBytes(bidTx, bundle[0], bundle[1], normalTx, normalTx2)})
s.Require().NotNil(resp)
s.Require().Error(err)
@ -723,8 +724,8 @@ func (s *ProposalsTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool)
return anteHandler
}
func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *base.DefaultLane {
cfg := blockbuster.LaneConfig{
func (s *ProposalsTestSuite) setUpStandardLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *defaultlane.DefaultLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
@ -732,11 +733,11 @@ func (s *ProposalsTestSuite) setUpDefaultLane(maxBlockSpace math.LegacyDec, expe
MaxBlockSpace: maxBlockSpace,
}
return base.NewDefaultLane(cfg)
return defaultlane.NewDefaultLane(cfg)
}
func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *auction.TOBLane {
cfg := blockbuster.LaneConfig{
func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *mev.MEVLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
@ -744,11 +745,11 @@ func (s *ProposalsTestSuite) setUpTOBLane(maxBlockSpace math.LegacyDec, expected
MaxBlockSpace: maxBlockSpace,
}
return auction.NewTOBLane(cfg, auction.NewDefaultAuctionFactory(cfg.TxDecoder))
return mev.NewMEVLane(cfg, mev.NewDefaultAuctionFactory(cfg.TxDecoder))
}
func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expectedExecution map[sdk.Tx]bool) *free.FreeLane {
cfg := blockbuster.LaneConfig{
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
@ -756,32 +757,32 @@ func (s *ProposalsTestSuite) setUpFreeLane(maxBlockSpace math.LegacyDec, expecte
MaxBlockSpace: maxBlockSpace,
}
return free.NewFreeLane(cfg, blockbuster.DefaultTxPriority(), free.DefaultMatchHandler())
return free.NewFreeLane(cfg, base.DefaultTxPriority(), free.DefaultMatchHandler())
}
func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *blockbuster.LaneConstructor {
cfg := blockbuster.LaneConfig{
func (s *ProposalsTestSuite) setUpPanicLane(maxBlockSpace math.LegacyDec) *base.BaseLane {
cfg := base.LaneConfig{
Logger: log.NewTestLogger(s.T()),
TxEncoder: s.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: s.encodingConfig.TxConfig.TxDecoder(),
MaxBlockSpace: maxBlockSpace,
}
lane := blockbuster.NewLaneConstructor(
lane := base.NewBaseLane(
cfg,
"panic",
blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), cfg.TxEncoder, 0),
blockbuster.DefaultMatchHandler(),
base.NewMempool[string](base.DefaultTxPriority(), cfg.TxEncoder, 0),
base.DefaultMatchHandler(),
)
lane.SetPrepareLaneHandler(blockbuster.PanicPrepareLaneHandler())
lane.SetProcessLaneHandler(blockbuster.PanicProcessLaneHandler())
lane.SetPrepareLaneHandler(block.PanicPrepareLaneHandler())
lane.SetProcessLaneHandler(block.PanicProcessLaneHandler())
return lane
}
func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []blockbuster.Lane) *abci.ProposalHandler {
mempool := blockbuster.NewMempool(log.NewTestLogger(s.T()), true, lanes...)
func (s *ProposalsTestSuite) setUpProposalHandlers(lanes []block.Lane) *abci.ProposalHandler {
mempool := block.NewLanedMempool(log.NewTestLogger(s.T()), true, lanes...)
return abci.NewProposalHandler(
log.NewTestLogger(s.T()),

View File

@ -1411,4 +1411,4 @@ func file_pob_abci_v1_auction_proto_init() {
file_pob_abci_v1_auction_proto_rawDesc = nil
file_pob_abci_v1_auction_proto_goTypes = nil
file_pob_abci_v1_auction_proto_depIdxs = nil
}
}

View File

@ -1,20 +1,21 @@
package blockbuster
package base
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/utils"
)
// PrepareLane will prepare a partial proposal for the lane. It will select transactions from the
// lane respecting the selection logic of the prepareLaneHandler. It will then update the partial
// proposal with the selected transactions. If the proposal is unable to be updated, we return an
// error. The proposal will only be modified if it passes all of the invarient checks.
func (l *LaneConstructor) PrepareLane(
func (l *BaseLane) PrepareLane(
ctx sdk.Context,
proposal BlockProposal,
proposal block.BlockProposal,
maxTxBytes int64,
next PrepareLanesHandler,
) (BlockProposal, error) {
next block.PrepareLanesHandler,
) (block.BlockProposal, error) {
txs, txsToRemove, err := l.prepareLaneHandler(ctx, proposal, maxTxBytes)
if err != nil {
return proposal, err
@ -39,7 +40,7 @@ func (l *LaneConstructor) PrepareLane(
// CheckOrder checks that the ordering logic of the lane is respected given the set of transactions
// in the block proposal. If the ordering logic is not respected, we return an error.
func (l *LaneConstructor) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error {
func (l *BaseLane) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error {
return l.checkOrderHandler(ctx, txs)
}
@ -47,7 +48,7 @@ func (l *LaneConstructor) CheckOrder(ctx sdk.Context, txs []sdk.Tx) error {
// the verification logic of the lane (processLaneHandler). If the transactions are valid, we
// return the transactions that do not belong to this lane to the next lane. If the transactions
// are invalid, we return an error.
func (l *LaneConstructor) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next ProcessLanesHandler) (sdk.Context, error) {
func (l *BaseLane) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next block.ProcessLanesHandler) (sdk.Context, error) {
remainingTxs, err := l.processLaneHandler(ctx, txs)
if err != nil {
return ctx, err
@ -58,7 +59,7 @@ func (l *LaneConstructor) ProcessLane(ctx sdk.Context, txs []sdk.Tx, next Proces
// AnteVerifyTx verifies that the transaction is valid respecting the ante verification logic of
// of the antehandler chain.
func (l *LaneConstructor) AnteVerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
func (l *BaseLane) AnteVerifyTx(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
if l.cfg.AnteHandler != nil {
return l.cfg.AnteHandler(ctx, tx, simulate)
}

79
block/base/config.go Normal file
View File

@ -0,0 +1,79 @@
package base
import (
"fmt"
"cosmossdk.io/log"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/block"
)
// LaneConfig defines the basic configurations needed for a lane.
type LaneConfig struct {
Logger log.Logger
TxEncoder sdk.TxEncoder
TxDecoder sdk.TxDecoder
AnteHandler sdk.AnteHandler
// MaxBlockSpace defines the relative percentage of block space that can be
// used by this lane. NOTE: If this is set to zero, then there is no limit
// on the number of transactions that can be included in the block for this
// lane (up to maxTxBytes as provided by the request). This is useful for the default lane.
MaxBlockSpace math.LegacyDec
// IgnoreList defines the list of lanes to ignore when processing transactions. This
// is useful for when you want lanes to exist after the default lane. For example,
// say there are two lanes: default and free. The free lane should be processed after
// the default lane. In this case, the free lane should be added to the ignore list
// of the default lane. Otherwise, the transactions that belong to the free lane
// will be processed by the default lane (which accepts all transactions by default).
IgnoreList []block.Lane
// MaxTxs sets the maximum number of transactions allowed in the mempool with
// the semantics:
// - if MaxTx == 0, there is no cap on the number of transactions in the mempool
// - if MaxTx > 0, the mempool will cap the number of transactions it stores,
// and will prioritize transactions by their priority and sender-nonce
// (sequence number) when evicting transactions.
// - if MaxTx < 0, `Insert` is a no-op.
MaxTxs int
}
// NewLaneConfig returns a new LaneConfig. This will be embedded in a lane.
func NewLaneConfig(
logger log.Logger,
txEncoder sdk.TxEncoder,
txDecoder sdk.TxDecoder,
anteHandler sdk.AnteHandler,
maxBlockSpace math.LegacyDec,
) LaneConfig {
return LaneConfig{
Logger: logger,
TxEncoder: txEncoder,
TxDecoder: txDecoder,
AnteHandler: anteHandler,
MaxBlockSpace: maxBlockSpace,
}
}
// ValidateBasic validates the lane configuration.
func (c *LaneConfig) ValidateBasic() error {
if c.Logger == nil {
return fmt.Errorf("logger cannot be nil")
}
if c.TxEncoder == nil {
return fmt.Errorf("tx encoder cannot be nil")
}
if c.TxDecoder == nil {
return fmt.Errorf("tx decoder cannot be nil")
}
if c.MaxBlockSpace.IsNil() || c.MaxBlockSpace.IsNegative() || c.MaxBlockSpace.GT(math.LegacyOneDec()) {
return fmt.Errorf("max block space must be set to a value between 0 and 1")
}
return nil
}

View File

@ -1,18 +1,19 @@
package blockbuster
package base
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/utils"
)
// DefaultPrepareLaneHandler returns a default implementation of the PrepareLaneHandler. It
// selects all transactions in the mempool that are valid and not already in the partial
// proposal. It will continue to reap transactions until the maximum block space for this
// lane has been reached. Additionally, any transactions that are invalid will be returned.
func (l *LaneConstructor) DefaultPrepareLaneHandler() PrepareLaneHandler {
return func(ctx sdk.Context, proposal BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) {
func (l *BaseLane) DefaultPrepareLaneHandler() block.PrepareLaneHandler {
return func(ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) {
var (
totalSize int64
txs [][]byte
@ -95,7 +96,7 @@ func (l *LaneConstructor) DefaultPrepareLaneHandler() PrepareLaneHandler {
// fails to verify, the entire proposal is rejected. If the handler comes across a transaction
// that does not match the lane's matcher, it will return the remaining transactions in the
// proposal.
func (l *LaneConstructor) DefaultProcessLaneHandler() ProcessLaneHandler {
func (l *BaseLane) DefaultProcessLaneHandler() block.ProcessLaneHandler {
return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) {
var err error
@ -122,7 +123,7 @@ func (l *LaneConstructor) DefaultProcessLaneHandler() ProcessLaneHandler {
// lane.
// 2. Transactions that belong to other lanes cannot be interleaved with transactions that
// belong to this lane.
func (l *LaneConstructor) DefaultCheckOrderHandler() CheckOrderHandler {
func (l *BaseLane) DefaultCheckOrderHandler() block.CheckOrderHandler {
return func(ctx sdk.Context, txs []sdk.Tx) error {
seenOtherLaneTx := false
@ -148,7 +149,7 @@ func (l *LaneConstructor) DefaultCheckOrderHandler() CheckOrderHandler {
// DefaultMatchHandler returns a default implementation of the MatchHandler. It matches all
// transactions.
func DefaultMatchHandler() MatchHandler {
func DefaultMatchHandler() block.MatchHandler {
return func(ctx sdk.Context, tx sdk.Tx) bool {
return true
}

View File

@ -1,4 +1,4 @@
package blockbuster
package base
import (
"fmt"
@ -6,16 +6,17 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/block"
)
var _ Lane = (*LaneConstructor)(nil)
var _ block.Lane = (*BaseLane)(nil)
// LaneConstructor is a generic implementation of a lane. It is meant to be used
// BaseLane is a generic implementation of a lane. It is meant to be used
// as a base for other lanes to be built on top of. It provides a default
// implementation of the MatchHandler, PrepareLaneHandler, ProcessLaneHandler,
// and CheckOrderHandler. To extend this lane, you must either utilize the default
// handlers or construct your own that you pass into the constructor/setters.
type LaneConstructor struct {
// handlers or construct your own that you pass into the base/setters.
type BaseLane struct { //nolint
// cfg stores functionality required to encode/decode transactions, maintains how
// many transactions are allowed in this lane's mempool, and the amount of block
// space this lane is allowed to consume.
@ -26,37 +27,37 @@ type LaneConstructor struct {
// LaneMempool is the mempool that is responsible for storing transactions
// that are waiting to be processed.
LaneMempool
block.LaneMempool
// matchHandler is the function that determines whether or not a transaction
// should be processed by this lane.
matchHandler MatchHandler
matchHandler block.MatchHandler
// prepareLaneHandler is the function that is called when a new proposal is being
// requested and the lane needs to submit transactions it wants included in the block.
prepareLaneHandler PrepareLaneHandler
prepareLaneHandler block.PrepareLaneHandler
// checkOrderHandler is the function that is called when a new proposal is being
// verified and the lane needs to verify that the transactions included in the proposal
// respect the ordering rules of the lane and does not interleave transactions from other lanes.
checkOrderHandler CheckOrderHandler
checkOrderHandler block.CheckOrderHandler
// processLaneHandler is the function that is called when a new proposal is being
// verified and the lane needs to verify that the transactions included in the proposal
// are valid respecting the verification logic of the lane.
processLaneHandler ProcessLaneHandler
processLaneHandler block.ProcessLaneHandler
}
// NewLaneConstructor returns a new lane constructor. When creating this lane, the type
// NewBaseLane returns a new lane base. When creating this lane, the type
// of the lane must be specified. The type of the lane is directly associated with the
// type of the mempool that is used to store transactions that are waiting to be processed.
func NewLaneConstructor(
func NewBaseLane(
cfg LaneConfig,
laneName string,
laneMempool LaneMempool,
matchHandlerFn MatchHandler,
) *LaneConstructor {
lane := &LaneConstructor{
laneMempool block.LaneMempool,
matchHandlerFn block.MatchHandler,
) *BaseLane {
lane := &BaseLane{
cfg: cfg,
laneName: laneName,
LaneMempool: laneMempool,
@ -72,7 +73,7 @@ func NewLaneConstructor(
// ValidateBasic ensures that the lane was constructed properly. In the case that
// the lane was not constructed with proper handlers, default handlers are set.
func (l *LaneConstructor) ValidateBasic() error {
func (l *BaseLane) ValidateBasic() error {
if err := l.cfg.ValidateBasic(); err != nil {
return err
}
@ -107,7 +108,7 @@ func (l *LaneConstructor) ValidateBasic() error {
// SetPrepareLaneHandler sets the prepare lane handler for the lane. This handler
// is called when a new proposal is being requested and the lane needs to submit
// transactions it wants included in the block.
func (l *LaneConstructor) SetPrepareLaneHandler(prepareLaneHandler PrepareLaneHandler) {
func (l *BaseLane) SetPrepareLaneHandler(prepareLaneHandler block.PrepareLaneHandler) {
if prepareLaneHandler == nil {
panic("prepare lane handler cannot be nil")
}
@ -119,7 +120,7 @@ func (l *LaneConstructor) SetPrepareLaneHandler(prepareLaneHandler PrepareLaneHa
// is called when a new proposal is being verified and the lane needs to verify
// that the transactions included in the proposal are valid respecting the verification
// logic of the lane.
func (l *LaneConstructor) SetProcessLaneHandler(processLaneHandler ProcessLaneHandler) {
func (l *BaseLane) SetProcessLaneHandler(processLaneHandler block.ProcessLaneHandler) {
if processLaneHandler == nil {
panic("process lane handler cannot be nil")
}
@ -131,7 +132,7 @@ func (l *LaneConstructor) SetProcessLaneHandler(processLaneHandler ProcessLaneHa
// is called when a new proposal is being verified and the lane needs to verify
// that the transactions included in the proposal respect the ordering rules of
// the lane and does not include transactions from other lanes.
func (l *LaneConstructor) SetCheckOrderHandler(checkOrderHandler CheckOrderHandler) {
func (l *BaseLane) SetCheckOrderHandler(checkOrderHandler block.CheckOrderHandler) {
if checkOrderHandler == nil {
panic("check order handler cannot be nil")
}
@ -143,14 +144,14 @@ func (l *LaneConstructor) SetCheckOrderHandler(checkOrderHandler CheckOrderHandl
// function first determines if the transaction matches the lane and then checks
// if the transaction is on the ignore list. If the transaction is on the ignore
// list, it returns false.
func (l *LaneConstructor) Match(ctx sdk.Context, tx sdk.Tx) bool {
func (l *BaseLane) Match(ctx sdk.Context, tx sdk.Tx) bool {
return l.matchHandler(ctx, tx) && !l.CheckIgnoreList(ctx, tx)
}
// CheckIgnoreList returns true if the transaction is on the ignore list. The ignore
// list is utilized to prevent transactions that should be considered in other lanes
// from being considered from this lane.
func (l *LaneConstructor) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool {
func (l *BaseLane) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool {
for _, lane := range l.cfg.IgnoreList {
if lane.Match(ctx, tx) {
return true
@ -161,38 +162,38 @@ func (l *LaneConstructor) CheckIgnoreList(ctx sdk.Context, tx sdk.Tx) bool {
}
// Name returns the name of the lane.
func (l *LaneConstructor) Name() string {
func (l *BaseLane) Name() string {
return l.laneName
}
// SetIgnoreList sets the ignore list for the lane. The ignore list is a list
// of lanes that the lane should ignore when processing transactions.
func (l *LaneConstructor) SetIgnoreList(lanes []Lane) {
func (l *BaseLane) SetIgnoreList(lanes []block.Lane) {
l.cfg.IgnoreList = lanes
}
// SetAnteHandler sets the ante handler for the lane.
func (l *LaneConstructor) SetAnteHandler(anteHandler sdk.AnteHandler) {
func (l *BaseLane) SetAnteHandler(anteHandler sdk.AnteHandler) {
l.cfg.AnteHandler = anteHandler
}
// Logger returns the logger for the lane.
func (l *LaneConstructor) Logger() log.Logger {
func (l *BaseLane) Logger() log.Logger {
return l.cfg.Logger
}
// TxDecoder returns the tx decoder for the lane.
func (l *LaneConstructor) TxDecoder() sdk.TxDecoder {
func (l *BaseLane) TxDecoder() sdk.TxDecoder {
return l.cfg.TxDecoder
}
// TxEncoder returns the tx encoder for the lane.
func (l *LaneConstructor) TxEncoder() sdk.TxEncoder {
func (l *BaseLane) TxEncoder() sdk.TxEncoder {
return l.cfg.TxEncoder
}
// GetMaxBlockSpace returns the maximum amount of block space that the lane is
// allowed to consume as a percentage of the total block space.
func (l *LaneConstructor) GetMaxBlockSpace() math.LegacyDec {
func (l *BaseLane) GetMaxBlockSpace() math.LegacyDec {
return l.cfg.MaxBlockSpace
}

View File

@ -1,4 +1,4 @@
package blockbuster
package base
import (
"context"
@ -7,16 +7,16 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block/utils"
)
type (
// ConstructorMempool defines a mempool that orders transactions based on the
// Mempool defines a mempool that orders transactions based on the
// txPriority. The mempool is a wrapper on top of the SDK's Priority Nonce mempool.
// It include's additional helper functions that allow users to determine if a
// transaction is already in the mempool and to compare the priority of two
// transactions.
ConstructorMempool[C comparable] struct {
Mempool[C comparable] struct {
// index defines an index of transactions.
index sdkmempool.Mempool
@ -79,9 +79,9 @@ func DefaultTxPriority() TxPriority[string] {
}
}
// NewConstructorMempool returns a new ConstructorMempool.
func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *ConstructorMempool[C] {
return &ConstructorMempool[C]{
// NewMempool returns a new Mempool.
func NewMempool[C comparable](txPriority TxPriority[C], txEncoder sdk.TxEncoder, maxTx int) *Mempool[C] {
return &Mempool[C]{
index: NewPriorityMempool(
PriorityNonceMempoolConfig[C]{
TxPriority: txPriority,
@ -95,7 +95,7 @@ func NewConstructorMempool[C comparable](txPriority TxPriority[C], txEncoder sdk
}
// Insert inserts a transaction into the mempool.
func (cm *ConstructorMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
func (cm *Mempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
if err := cm.index.Insert(ctx, tx); err != nil {
return fmt.Errorf("failed to insert tx into auction index: %w", err)
}
@ -112,7 +112,7 @@ func (cm *ConstructorMempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
}
// Remove removes a transaction from the mempool.
func (cm *ConstructorMempool[C]) Remove(tx sdk.Tx) error {
func (cm *Mempool[C]) Remove(tx sdk.Tx) error {
if err := cm.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) {
return fmt.Errorf("failed to remove transaction from the mempool: %w", err)
}
@ -131,17 +131,17 @@ func (cm *ConstructorMempool[C]) Remove(tx sdk.Tx) error {
// remove a transaction from the mempool while iterating over the transactions,
// the iterator will not be aware of the removal and will continue to iterate
// over the removed transaction. Be sure to reset the iterator if you remove a transaction.
func (cm *ConstructorMempool[C]) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator {
func (cm *Mempool[C]) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator {
return cm.index.Select(ctx, txs)
}
// CountTx returns the number of transactions in the mempool.
func (cm *ConstructorMempool[C]) CountTx() int {
func (cm *Mempool[C]) CountTx() int {
return cm.index.CountTx()
}
// Contains returns true if the transaction is contained in the mempool.
func (cm *ConstructorMempool[C]) Contains(tx sdk.Tx) bool {
func (cm *Mempool[C]) Contains(tx sdk.Tx) bool {
_, txHashStr, err := utils.GetTxHashStr(cm.txEncoder, tx)
if err != nil {
return false
@ -152,7 +152,7 @@ func (cm *ConstructorMempool[C]) Contains(tx sdk.Tx) bool {
}
// Compare determines the relative priority of two transactions belonging in the same lane.
func (cm *ConstructorMempool[C]) Compare(ctx sdk.Context, this sdk.Tx, other sdk.Tx) int {
func (cm *Mempool[C]) Compare(ctx sdk.Context, this sdk.Tx, other sdk.Tx) int {
firstPriority := cm.txPriority.GetTxPriority(ctx, this)
secondPriority := cm.txPriority.GetTxPriority(ctx, other)
return cm.txPriority.Compare(firstPriority, secondPriority)

View File

@ -1,4 +1,16 @@
package blockbuster
package base
// ------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------ //
// NOTE: THIS IS A COPY OF THE PRIORITY NONCE MEMPOOL FROM COSMOS-SDK. IT HAS BEEN
// MODIFIED FOR OUR USE CASE. THIS CODE WILL BE DEPRECATED ONCE THE COSMOS-SDK
// CUTS A FINAL v0.50.0 RELEASE.
// ------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------ //
import (
"context"

View File

@ -1,4 +1,4 @@
package blockbuster
package block
import (
"cosmossdk.io/log"

View File

@ -1,4 +1,4 @@
package blockbuster
package block
import (
"context"
@ -11,10 +11,10 @@ import (
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
)
var _ Mempool = (*BBMempool)(nil)
var _ Mempool = (*LanedMempool)(nil)
type (
// Mempool defines the Blockbuster mempool interface.
// Mempool defines the Block SDK mempool interface.
Mempool interface {
sdkmempool.Mempool
@ -26,14 +26,11 @@ type (
// GetTxDistribution returns the number of transactions in each lane.
GetTxDistribution() map[string]int
// GetLane returns the lane with the given name.
GetLane(name string) (Lane, error)
}
// BBMempool defines the Blockbuster mempool implementation. It contains a registry
// LanedMempool defines the Block SDK mempool implementation. It contains a registry
// of lanes, which allows for customizable block proposal construction.
BBMempool struct {
LanedMempool struct {
logger log.Logger
// registry contains the lanes in the mempool. The lanes are ordered
@ -43,7 +40,7 @@ type (
}
)
// NewMempool returns a new Blockbuster mempool. The blockbuster mempool is
// NewLanedMempool returns a new Block SDK LanedMempool. The laned mempool is
// comprised of a registry of lanes. Each lane is responsible for selecting
// transactions according to its own selection logic. The lanes are ordered
// according to their priority. The first lane in the registry has the highest
@ -54,8 +51,8 @@ type (
// attempt to insert, remove transactions from all lanes it belongs to. It is recommended,
// that mutex is set to true when creating the mempool. This will ensure that each
// transaction cannot be inserted into the lanes before it.
func NewMempool(logger log.Logger, mutex bool, lanes ...Lane) *BBMempool {
mempool := &BBMempool{
func NewLanedMempool(logger log.Logger, mutex bool, lanes ...Lane) *LanedMempool {
mempool := &LanedMempool{
logger: logger,
registry: lanes,
}
@ -79,7 +76,7 @@ func NewMempool(logger log.Logger, mutex bool, lanes ...Lane) *BBMempool {
// CountTx returns the total number of transactions in the mempool. This will
// be the sum of the number of transactions in each lane.
func (m *BBMempool) CountTx() int {
func (m *LanedMempool) CountTx() int {
var total int
for _, lane := range m.registry {
total += lane.CountTx()
@ -89,7 +86,7 @@ func (m *BBMempool) CountTx() int {
}
// GetTxDistribution returns the number of transactions in each lane.
func (m *BBMempool) GetTxDistribution() map[string]int {
func (m *LanedMempool) GetTxDistribution() map[string]int {
counts := make(map[string]int, len(m.registry))
for _, lane := range m.registry {
@ -101,7 +98,7 @@ func (m *BBMempool) GetTxDistribution() map[string]int {
// 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) (err error) {
func (m *LanedMempool) Insert(ctx context.Context, tx sdk.Tx) (err error) {
defer func() {
if r := recover(); r != nil {
m.logger.Error("panic in Insert", "err", r)
@ -136,12 +133,12 @@ func (m *BBMempool) Insert(ctx context.Context, tx sdk.Tx) (err error) {
// - Determine if it even makes sense to return an iterator. What does that even
// mean in the context where you have multiple lanes?
// - Perhaps consider implementing and returning a no-op iterator?
func (m *BBMempool) Select(_ context.Context, _ [][]byte) sdkmempool.Iterator {
func (m *LanedMempool) Select(_ context.Context, _ [][]byte) sdkmempool.Iterator {
return nil
}
// Remove removes a transaction from all of the lanes it is currently in.
func (m *BBMempool) Remove(tx sdk.Tx) (err error) {
func (m *LanedMempool) Remove(tx sdk.Tx) (err error) {
defer func() {
if r := recover(); r != nil {
m.logger.Error("panic in Remove", "err", r)
@ -179,7 +176,7 @@ func (m *BBMempool) Remove(tx sdk.Tx) (err error) {
}
// Contains returns true if the transaction is contained in any of the lanes.
func (m *BBMempool) Contains(tx sdk.Tx) (contains bool) {
func (m *LanedMempool) Contains(tx sdk.Tx) (contains bool) {
defer func() {
if r := recover(); r != nil {
m.logger.Error("panic in Contains", "err", r)
@ -197,7 +194,7 @@ func (m *BBMempool) Contains(tx sdk.Tx) (contains bool) {
}
// Registry returns the mempool's lane registry.
func (m *BBMempool) Registry() []Lane {
func (m *LanedMempool) Registry() []Lane {
return m.registry
}
@ -205,7 +202,7 @@ func (m *BBMempool) Registry() []Lane {
// the following:
// - The sum of the lane max block space percentages is less than or equal to 1.
// - There is no unused block space.
func (m *BBMempool) ValidateBasic() error {
func (m *LanedMempool) ValidateBasic() error {
sum := math.LegacyZeroDec()
seenZeroMaxBlockSpace := false
@ -230,14 +227,3 @@ func (m *BBMempool) ValidateBasic() error {
return nil
}
// GetLane returns the lane with the given name.
func (m *BBMempool) GetLane(name string) (Lane, error) {
for _, lane := range m.registry {
if lane.Name() == name {
return lane, nil
}
}
return nil, fmt.Errorf("lane %s not found", name)
}

View File

@ -1,4 +1,4 @@
package blockbuster_test
package block_test
import (
"math/rand"
@ -10,10 +10,11 @@ import (
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/blockbuster/lanes/auction"
"github.com/skip-mev/pob/blockbuster/lanes/base"
"github.com/skip-mev/pob/blockbuster/lanes/free"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
defaultlane "github.com/skip-mev/pob/lanes/base"
"github.com/skip-mev/pob/lanes/free"
"github.com/skip-mev/pob/lanes/mev"
testutils "github.com/skip-mev/pob/testutils"
buildertypes "github.com/skip-mev/pob/x/builder/types"
"github.com/stretchr/testify/suite"
@ -27,13 +28,13 @@ type BlockBusterTestSuite struct {
encodingConfig testutils.EncodingConfig
// Define all of the lanes utilized in the test suite
tobLane *auction.TOBLane
baseLane *base.DefaultLane
mevLane *mev.MEVLane
baseLane *defaultlane.DefaultLane
freeLane *free.FreeLane
gasTokenDenom string
lanes []blockbuster.Lane
mempool blockbuster.Mempool
lanes []block.Lane
mempool block.Mempool
// account set up
accounts []testutils.Account
@ -57,20 +58,20 @@ func (suite *BlockBusterTestSuite) SetupTest() {
//
// TOB lane set up
suite.gasTokenDenom = "stake"
tobConfig := blockbuster.LaneConfig{
mevConfig := base.LaneConfig{
Logger: log.NewNopLogger(),
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: nil,
MaxBlockSpace: math.LegacyZeroDec(),
}
suite.tobLane = auction.NewTOBLane(
tobConfig,
auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()),
suite.mevLane = mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()),
)
// Free lane set up
freeConfig := blockbuster.LaneConfig{
freeConfig := base.LaneConfig{
Logger: log.NewNopLogger(),
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
@ -79,25 +80,25 @@ func (suite *BlockBusterTestSuite) SetupTest() {
}
suite.freeLane = free.NewFreeLane(
freeConfig,
blockbuster.DefaultTxPriority(),
base.DefaultTxPriority(),
free.DefaultMatchHandler(),
)
// Base lane set up
baseConfig := blockbuster.LaneConfig{
baseConfig := base.LaneConfig{
Logger: log.NewNopLogger(),
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: nil,
MaxBlockSpace: math.LegacyZeroDec(),
}
suite.baseLane = base.NewDefaultLane(
suite.baseLane = defaultlane.NewDefaultLane(
baseConfig,
)
// Mempool set up
suite.lanes = []blockbuster.Lane{suite.tobLane, suite.freeLane, suite.baseLane}
suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
suite.lanes = []block.Lane{suite.mevLane, suite.freeLane, suite.baseLane}
suite.mempool = block.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
// Accounts set up
suite.accounts = testutils.RandomAccounts(suite.random, 10)
@ -113,15 +114,15 @@ func (suite *BlockBusterTestSuite) TestInsert() {
insertDistribution map[string]int
}{
{
"insert 1 tob tx",
"insert 1 mev tx",
map[string]int{
suite.tobLane.Name(): 1,
suite.mevLane.Name(): 1,
},
},
{
"insert 10 tob txs",
"insert 10 mev txs",
map[string]int{
suite.tobLane.Name(): 10,
suite.mevLane.Name(): 10,
},
},
{
@ -131,24 +132,24 @@ func (suite *BlockBusterTestSuite) TestInsert() {
},
},
{
"insert 10 base txs and 10 tob txs",
"insert 10 base txs and 10 mev txs",
map[string]int{
suite.baseLane.Name(): 10,
suite.tobLane.Name(): 10,
suite.mevLane.Name(): 10,
},
},
{
"insert 100 base txs and 100 tob txs",
"insert 100 base txs and 100 mev txs",
map[string]int{
suite.baseLane.Name(): 100,
suite.tobLane.Name(): 100,
suite.mevLane.Name(): 100,
},
},
{
"insert 100 base txs, 100 tob txs, and 100 free txs",
"insert 100 base txs, 100 mev txs, and 100 free txs",
map[string]int{
suite.baseLane.Name(): 100,
suite.tobLane.Name(): 100,
suite.mevLane.Name(): 100,
suite.freeLane.Name(): 100,
},
},
@ -166,9 +167,9 @@ func (suite *BlockBusterTestSuite) TestInsert() {
},
},
{
"insert 10 tob txs and 10 free txs",
"insert 10 mev txs and 10 free txs",
map[string]int{
suite.tobLane.Name(): 10,
suite.mevLane.Name(): 10,
suite.freeLane.Name(): 10,
},
},
@ -182,7 +183,7 @@ func (suite *BlockBusterTestSuite) TestInsert() {
suite.fillBaseLane(tc.insertDistribution[suite.baseLane.Name()])
// Fill the TOB lane with numTobTxs transactions
suite.fillTOBLane(tc.insertDistribution[suite.tobLane.Name()])
suite.fillTOBLane(tc.insertDistribution[suite.mevLane.Name()])
// Fill the Free lane with numFreeTxs transactions
suite.fillFreeLane(tc.insertDistribution[suite.freeLane.Name()])
@ -196,7 +197,7 @@ func (suite *BlockBusterTestSuite) TestInsert() {
suite.Require().Equal(sum, suite.mempool.CountTx())
// Validate the lanes
suite.Require().Equal(tc.insertDistribution[suite.tobLane.Name()], suite.tobLane.CountTx())
suite.Require().Equal(tc.insertDistribution[suite.mevLane.Name()], suite.mevLane.CountTx())
suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], suite.baseLane.CountTx())
suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], suite.freeLane.CountTx())
@ -204,7 +205,7 @@ func (suite *BlockBusterTestSuite) TestInsert() {
laneCounts := suite.mempool.GetTxDistribution()
// Ensure that the lane counts are correct
suite.Require().Equal(tc.insertDistribution[suite.tobLane.Name()], laneCounts[suite.tobLane.Name()])
suite.Require().Equal(tc.insertDistribution[suite.mevLane.Name()], laneCounts[suite.mevLane.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()])
})
@ -218,12 +219,12 @@ func (suite *BlockBusterTestSuite) TestRemove() {
numBaseTxs int
}{
{
"insert 1 tob tx",
"insert 1 mev tx",
1,
0,
},
{
"insert 10 tob txs",
"insert 10 mev txs",
10,
0,
},
@ -233,12 +234,12 @@ func (suite *BlockBusterTestSuite) TestRemove() {
1,
},
{
"insert 10 base txs and 10 tob txs",
"insert 10 base txs and 10 mev txs",
10,
10,
},
{
"insert 100 base txs and 100 tob txs",
"insert 100 base txs and 100 mev txs",
100,
100,
},
@ -255,7 +256,7 @@ func (suite *BlockBusterTestSuite) TestRemove() {
suite.fillTOBLane(tc.numTobTxs)
// Remove all transactions from the lanes
tobCount := tc.numTobTxs
mevCount := tc.numTobTxs
baseCount := tc.numBaseTxs
for iterator := suite.baseLane.Select(suite.ctx, nil); iterator != nil; {
tx := iterator.Tx()
@ -277,10 +278,10 @@ func (suite *BlockBusterTestSuite) TestRemove() {
}
suite.Require().Equal(0, suite.baseLane.CountTx())
suite.Require().Equal(tobCount, suite.tobLane.CountTx())
suite.Require().Equal(mevCount, suite.mevLane.CountTx())
// Remove all transactions from the lanes
for iterator := suite.tobLane.Select(suite.ctx, nil); iterator != nil; {
for iterator := suite.mevLane.Select(suite.ctx, nil); iterator != nil; {
tx := iterator.Tx()
// Remove the transaction from the mempool
@ -290,16 +291,16 @@ func (suite *BlockBusterTestSuite) TestRemove() {
suite.Require().Equal(false, suite.mempool.Contains(tx))
// Ensure the number of transactions in the lane is correct
tobCount--
suite.Require().Equal(suite.tobLane.CountTx(), tobCount)
mevCount--
suite.Require().Equal(suite.mevLane.CountTx(), mevCount)
distribution := suite.mempool.GetTxDistribution()
suite.Require().Equal(distribution[suite.tobLane.Name()], tobCount)
suite.Require().Equal(distribution[suite.mevLane.Name()], mevCount)
iterator = suite.tobLane.Select(suite.ctx, nil)
iterator = suite.mevLane.Select(suite.ctx, nil)
}
suite.Require().Equal(0, suite.tobLane.CountTx())
suite.Require().Equal(0, suite.mevLane.CountTx())
suite.Require().Equal(0, suite.baseLane.CountTx())
suite.Require().Equal(0, suite.mempool.CountTx())
@ -307,7 +308,7 @@ func (suite *BlockBusterTestSuite) TestRemove() {
distribution := suite.mempool.GetTxDistribution()
// Ensure that the lane counts are correct
suite.Require().Equal(distribution[suite.tobLane.Name()], 0)
suite.Require().Equal(distribution[suite.mevLane.Name()], 0)
suite.Require().Equal(distribution[suite.baseLane.Name()], 0)
})
}

View File

@ -1,4 +1,4 @@
package blockbuster
package block
import (
"crypto/sha256"
@ -7,7 +7,7 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/math"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block/utils"
)
var _ BlockProposal = (*Proposal)(nil)
@ -30,7 +30,7 @@ type (
// and updating proposals. BlockProposals are iteratively updated as each lane prepares its
// partial proposal. Each lane must call UpdateProposal with its partial proposal in PrepareLane. BlockProposals
// can also include vote extensions, which are included at the top of the proposal.
BlockProposal interface {
BlockProposal interface { //nolint
// UpdateProposal updates the proposal with the given transactions. There are a
// few invarients that are checked:
// 1. The total size of the proposal must be less than the maximum number of bytes allowed.

View File

@ -1,10 +1,6 @@
package blockbuster
package block
import (
"fmt"
"cosmossdk.io/log"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -44,77 +40,8 @@ type (
// function. You can think of it like an AnteHandler, but for processing proposals in the
// context of lanes instead of modules.
ProcessLanesHandler func(ctx sdk.Context, txs []sdk.Tx) (sdk.Context, error)
// LaneConfig defines the basic functionality needed for a lane.
LaneConfig struct {
Logger log.Logger
TxEncoder sdk.TxEncoder
TxDecoder sdk.TxDecoder
AnteHandler sdk.AnteHandler
// MaxBlockSpace defines the relative percentage of block space that can be
// used by this lane. NOTE: If this is set to zero, then there is no limit
// on the number of transactions that can be included in the block for this
// lane (up to maxTxBytes as provided by the request). This is useful for the default lane.
MaxBlockSpace math.LegacyDec
// IgnoreList defines the list of lanes to ignore when processing transactions. This
// is useful for when you want lanes to exist after the default lane. For example,
// say there are two lanes: default and free. The free lane should be processed after
// the default lane. In this case, the free lane should be added to the ignore list
// of the default lane. Otherwise, the transactions that belong to the free lane
// will be processed by the default lane (which accepts all transactions by default).
IgnoreList []Lane
// MaxTxs sets the maximum number of transactions allowed in the mempool with
// the semantics:
// - if MaxTx == 0, there is no cap on the number of transactions in the mempool
// - if MaxTx > 0, the mempool will cap the number of transactions it stores,
// and will prioritize transactions by their priority and sender-nonce
// (sequence number) when evicting transactions.
// - if MaxTx < 0, `Insert` is a no-op.
MaxTxs int
}
)
// NewLaneConfig returns a new LaneConfig. This will be embedded in a lane.
func NewBaseLaneConfig(
logger log.Logger,
txEncoder sdk.TxEncoder,
txDecoder sdk.TxDecoder,
anteHandler sdk.AnteHandler,
maxBlockSpace math.LegacyDec,
) LaneConfig {
return LaneConfig{
Logger: logger,
TxEncoder: txEncoder,
TxDecoder: txDecoder,
AnteHandler: anteHandler,
MaxBlockSpace: maxBlockSpace,
}
}
// ValidateBasic validates the lane configuration.
func (c *LaneConfig) ValidateBasic() error {
if c.Logger == nil {
return fmt.Errorf("logger cannot be nil")
}
if c.TxEncoder == nil {
return fmt.Errorf("tx encoder cannot be nil")
}
if c.TxDecoder == nil {
return fmt.Errorf("tx decoder cannot be nil")
}
if c.MaxBlockSpace.IsNil() || c.MaxBlockSpace.IsNegative() || c.MaxBlockSpace.GT(math.LegacyOneDec()) {
return fmt.Errorf("max block space must be set to a value between 0 and 1")
}
return nil
}
// NoOpPrepareLanesHandler returns a no-op prepare lanes handler.
// This should only be used for testing.
func NoOpPrepareLanesHandler() PrepareLanesHandler {

View File

@ -5,7 +5,7 @@ package mocks
import (
context "context"
blockbuster "github.com/skip-mev/pob/blockbuster"
block "github.com/skip-mev/pob/block"
log "cosmossdk.io/log"
@ -152,23 +152,23 @@ func (_m *Lane) Name() string {
}
// PrepareLane provides a mock function with given fields: ctx, proposal, maxTxBytes, next
func (_m *Lane) PrepareLane(ctx types.Context, proposal blockbuster.BlockProposal, maxTxBytes int64, next blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error) {
func (_m *Lane) PrepareLane(ctx types.Context, proposal block.BlockProposal, maxTxBytes int64, next block.PrepareLanesHandler) (block.BlockProposal, error) {
ret := _m.Called(ctx, proposal, maxTxBytes, next)
var r0 blockbuster.BlockProposal
var r0 block.BlockProposal
var r1 error
if rf, ok := ret.Get(0).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error)); ok {
if rf, ok := ret.Get(0).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) (block.BlockProposal, error)); ok {
return rf(ctx, proposal, maxTxBytes, next)
}
if rf, ok := ret.Get(0).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) blockbuster.BlockProposal); ok {
if rf, ok := ret.Get(0).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) block.BlockProposal); ok {
r0 = rf(ctx, proposal, maxTxBytes, next)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(blockbuster.BlockProposal)
r0 = ret.Get(0).(block.BlockProposal)
}
}
if rf, ok := ret.Get(1).(func(types.Context, blockbuster.BlockProposal, int64, blockbuster.PrepareLanesHandler) error); ok {
if rf, ok := ret.Get(1).(func(types.Context, block.BlockProposal, int64, block.PrepareLanesHandler) error); ok {
r1 = rf(ctx, proposal, maxTxBytes, next)
} else {
r1 = ret.Error(1)
@ -178,21 +178,21 @@ func (_m *Lane) PrepareLane(ctx types.Context, proposal blockbuster.BlockProposa
}
// ProcessLane provides a mock function with given fields: ctx, proposalTxs, next
func (_m *Lane) ProcessLane(ctx types.Context, proposalTxs []types.Tx, next blockbuster.ProcessLanesHandler) (types.Context, error) {
func (_m *Lane) ProcessLane(ctx types.Context, proposalTxs []types.Tx, next block.ProcessLanesHandler) (types.Context, error) {
ret := _m.Called(ctx, proposalTxs, next)
var r0 types.Context
var r1 error
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, blockbuster.ProcessLanesHandler) (types.Context, error)); ok {
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, block.ProcessLanesHandler) (types.Context, error)); ok {
return rf(ctx, proposalTxs, next)
}
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, blockbuster.ProcessLanesHandler) types.Context); ok {
if rf, ok := ret.Get(0).(func(types.Context, []types.Tx, block.ProcessLanesHandler) types.Context); ok {
r0 = rf(ctx, proposalTxs, next)
} else {
r0 = ret.Get(0).(types.Context)
}
if rf, ok := ret.Get(1).(func(types.Context, []types.Tx, blockbuster.ProcessLanesHandler) error); ok {
if rf, ok := ret.Get(1).(func(types.Context, []types.Tx, block.ProcessLanesHandler) error); ok {
r1 = rf(ctx, proposalTxs, next)
} else {
r1 = ret.Error(1)
@ -237,7 +237,7 @@ func (_m *Lane) SetAnteHandler(antehander types.AnteHandler) {
}
// SetIgnoreList provides a mock function with given fields: ignoreList
func (_m *Lane) SetIgnoreList(ignoreList []blockbuster.Lane) {
func (_m *Lane) SetIgnoreList(ignoreList []block.Lane) {
_m.Called(ignoreList)
}

View File

@ -4,7 +4,7 @@ import (
"testing"
"cosmossdk.io/math"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block/utils"
)
func TestGetMaxTxBytesForLane(t *testing.T) {

View File

@ -1,355 +0,0 @@
# BlockBuster
> 📕 BlockBuster is an app-side mempool + set of proposal handlers that allows
developers to configure modular lanes of transactions in their blocks with
distinct validation/ordering logic. **BlockBuster is the ultimate highway
system for transactions.**
## High Level Overview
**`BlockBuster`** is a framework for creating modular, application specific
mempools by **separating transactions into “lanes” with custom transaction handling.**
You can think of BlockBuster as a **transaction highway system**, where each
lane on the highway serves a specific purpose and has its own set of rules and
traffic flow.
Similarly, **BlockBuster** redefines block-space into **`lanes`** - where each
`lane` has its own set of rules and transaction flow management systems.
* A lane is what we might traditionally consider to be a standard mempool
where transaction ***validation***, ***ordering*** and ***prioritization*** for
contained transactions are shared.
* Lanes implement a **standard interface** that allows each individual lane to
propose and validate a portion of a block.
* Lanes are ordered with each other, configurable by developers. All lanes
together define the desired block structure of a chain.
## BlockBuster Use Cases
A mempool with separate `lanes` can be used for:
1. **MEV mitigation**: a top of block lane could be designed to create an
in-protocol top-of-block auction (as we are doing with POB) to recapture MEV
in a transparent and governable way.
2. **Free/reduced fee txs**: transactions with certain properties (e.g.
from trusted accounts or performing encouraged actions) could leverage a
free lane to reward behavior.
3. **Dedicated oracle space** Oracles could be included before other kinds
of transactions to ensure that price updates occur first, and are not able
to be sandwiched or manipulated.
4. **Orderflow auctions**: an OFA lane could be constructed such that order
flow providers can have their submitted transactions bundled with specific
backrunners, to guarantee MEV rewards are attributed back to users.
Imagine MEV-share but in protocol.
5. **Enhanced and customizable privacy**: privacy-enhancing features could
be introduced, such as threshold encrypted lanes, to protect user data and
maintain privacy for specific use cases.
6. **Fee market improvements**: one or many fee markets - such as EIP-1559 -
could be easily adopted for different lanes (potentially custom for certain
dApps). Each smart contract/exchange could have its own fee market or auction
for transaction ordering.
7. **Congestion management**: segmentation of transactions to lanes can help
mitigate network congestion by capping usage of certain applications and
tailoring fee markets.
## BlockBuster Design
BlockBuster is a mempool composed of sub-mempools called **lanes**. All
lanes together define the transaction highway system and BlockBuster mempool.
When instantiating the BlockBuster mempool, developers will define all of the
desired lanes and their configurations (including lane ordering).
Utilizing BlockBuster is a simple three step process:
* Determine the lanes desired. Currently, POB supports three different
implementations of lanes: top of block lane, free lane, and a default lane.
1. Top of block lane allows the top of every block to be auctioned off
and constructed using logic defined by the `x/builder` module.
2. Free lane allows base app to not charge certain types of transactions
any fees. For example, delegations and/or re-delegations might be charged no
fees. What qualifies as a free transaction is determined
[here](https://github.com/skip-mev/pob/blob/main/blockbuster/lanes/free/factory.go).
3. Default lane accepts all other transactions and is considered to be
analogous to how mempools and proposals are constructed today.
* Instantiate the mempool in base app.
```go
mempool := blockbuster.NewMempool(lanes...)
app.App.SetMempool(mempool)
```
* Instantiate the BlockBuster proposal handlers in base app.
```go
proposalHandlers := abci.NewProposalHandler(
app.Logger(),
app.txConfig.TxDecoder(),
mempool, // BlockBuster mempool
)
app.App.SetPrepareProposal(proposalHandlers.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandlers.ProcessProposalHandler())
```
***Note: BlockBuster should configure a `DefaultLane` that accepts transactions
that do not belong to any other lane.***
Transactions are inserted into the first lane that the transaction matches to.
This means that a given transaction should really only belong to one lane
(but this isnt enforced).
### Proposals
The ordering of lanes when initializing BlockBuster in base app will determine
the ordering of how proposals are built. For example, say that we instantiate
three lanes:
1. Top of block lane
2. Free lane
3. Default lane
#### Preparing Proposals
When the current proposer starts building a block, it will first populate the
proposal with transactions from the top of block lane, followed by free and
default lane. Each lane proposes its own set of transactions using the lanes
`PrepareLane` (analogous to `PrepareProposal`). Each lane has a limit on the
relative percentage of total block space that the lane can consume.
For example, the free lane might be configured to only make up 10% of any
block. This is defined on each lanes `Config` when it is instantiated.
In the case when any lane fails to propose its portion of the block, it will
be skipped and the next lane in the set of lanes will propose its portion of
the block. Failures of partial block proposals are independent of one another.
#### Processing Proposals
Block proposals are validated iteratively following the exact ordering of lanes
defined on base app. Transactions included in block proposals must respect the
ordering of lanes. Any proposal that includes transactions that are out of
order relative to the ordering of lanes will be rejected. Following the
example defined above, if a proposal contains the following transactions:
1. Default transaction (belonging to the default lane)
2. Top of block transaction (belonging to the top of block lane)
3. Free transaction (belonging to the free lane)
It will be rejected because it does not respect the lane ordering.
The BlockBuster `ProcessProposalHandler` processes the proposal by verifying
all transactions in the proposal according to each lane's verification logic
in a greedy fashion. If a lane's portion of the proposal is invalid, we
reject the proposal. After a lane's portion of the proposal is verified, we
pass the remaining transactions to the next lane in the chain.
#### Coming Soon
BlockBuster will have its own dedicated gRPC service for searchers, wallets,
and users that allows them to query what lane their transaction might belong
in, what fees they might have to pay for a given transaction, and the general
state of the BlockBuster mempool.
### Lanes
Each lane will define its own:
1. Unique prioritization/ordering mechanism i.e. how will transactions from a
given lane be ordered in a block / mempool.
2. Inclusion function to determine what types of transactions belong in the lane.
3. Unique block building/verification mechanism.
The general interface that each lane must implement can be found [here](https://github.com/skip-mev/pob/blob/main/blockbuster/lane.go):
```go
// Lane defines an interface used for block construction
Lane interface {
sdkmempool.Mempool
// Name returns the name of the lane.
Name() string
// Match determines if a transaction belongs to this lane.
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/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.
PrepareLane(
ctx sdk.Context,
proposal BlockProposal,
maxTxBytes int64,
next PrepareLanesHandler
) (BlockProposal, 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(
ctx sdk.Context,
proposalTxs []sdk.Tx,
next ProcessLanesHandler,
) (sdk.Context, error)
// SetAnteHandler sets the lane's antehandler.
SetAnteHandler(antehander sdk.AnteHandler)
// Logger returns the lane's logger.
Logger() log.Logger
// GetMaxBlockSpace returns the max block space for the lane as a relative percentage.
GetMaxBlockSpace() math.LegacyDec
}
```
### 1. Intra-lane Transaction Ordering
**Note: Lanes must implement the `sdk.Mempool` interface.**
Transactions within a lane are ordered in a proposal respecting the ordering
defined on the lanes mempool. Developers can define their own custom ordering
by implementing a custom `TxPriority` struct that allows the lanes mempool to
determine the priority of a transaction `GetTxPriority` and relatively order
two transactions given the priority `Compare`. The top of block lane includes
an custom `TxPriority` that orders transactions in the mempool based on their
bid.
```go
func TxPriority(config Factory) blockbuster.TxPriority[string] {
return blockbuster.TxPriority[string]{
GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string {
bidInfo, err := config.GetAuctionBidInfo(tx)
if err != nil {
panic(err)
}
return bidInfo.Bid.String()
},
Compare: func(a, b string) int {
aCoins, _ := sdk.ParseCoinsNormalized(a)
bCoins, _ := sdk.ParseCoinsNormalized(b)
switch {
case aCoins == nil && bCoins == nil:
return 0
case aCoins == nil:
return -1
case bCoins == nil:
return 1
default:
switch {
case aCoins.IsAllGT(bCoins):
return 1
case aCoins.IsAllLT(bCoins):
return -1
default:
return 0
}
}
},
MinValue: "",
}
}
// NewMempool returns a new auction mempool.
func NewMempool(txEncoder sdk.TxEncoder, maxTx int, config Factory) *TOBMempool {
return &TOBMempool{
index: blockbuster.NewPriorityMempool(
blockbuster.PriorityNonceMempoolConfig[string]{
TxPriority: TxPriority(config),
MaxTx: maxTx,
},
),
txEncoder: txEncoder,
txIndex: make(map[string]struct{}),
Factory: config,
}
}
```
### 2. [Optional] Transaction Information Retrieval
Each lane can define a factory that configures the necessary set of interfaces
required for transaction processing, ordering, and validation. Lanes are
designed such that any given chain can adopt upstream `POB` lanes as long as
developers implement the specified interface(s) associated with transaction
information retrieval for that lane.
***A standard cosmos chain or EVM chain can then implement their own versions
of these interfaces and automatically utilize the lane with no changes upstream!***
For example, the free lane defines an `Factory` that includes a single
`IsFreeTx` function that allows developers to configure what is a free
transaction. The default implementation categorizes free transactions as any
transaction that includes a delegate type message.
```go
// IsFreeTx defines a default function that checks if a transaction is free. In
// this case, any transaction that is a delegation/redelegation transaction is free.
func (config *DefaultFreeFactory) IsFreeTx(tx sdk.Tx) bool {
for _, msg := range tx.GetMsgs() {
switch msg.(type) {
case *types.MsgDelegate:
return true
case *types.MsgBeginRedelegate:
return true
case *types.MsgCancelUnbondingDelegation:
return true
}
}
return false
}
```
### 3. Lane Inclusion Functionality
Lanes must implement a `Match` interface which determines whether a transaction
should be considered for a given lane. Developers are encouraged to utilize the
same interfaces defined in the `Factory` to match transactions to lanes. For
example, developers might configure a top of block auction lane to accept
transactions if they contain a single `MsgAuctionBid` message in the transaction.
### 4.1. [Optional] Transaction Validation
Transactions will be verified the lanes `VerifyTx` function. This logic can be
completely arbitrary. For example, the default lane verifies transactions
using base apps `AnteHandler` while the top of block lane verifies transactions
by extracting all bundled transactions included in the bid transaction and then
verifying the transaction iteratively given the bundle.
### 4.2. Block Building/Verification Logic
Each lane will implement block building and verification logic - analogous to
`Prepare` and `Process` proposal - that is unique to itself.
* `PrepareLane` will be in charge of building a partial block given the
transactions in the lane.
* `ProcessLaneBasic` ensures that transactions that should be included in the
current lane are not interleaved with other lanes i.e. transactions in
proposals are ordered respecting the ordering of lanes.
* `ProcessLane` will be in charge of verifying the lanes partial block.
### Inheritance
Lanes can inherit the underlying implementation of other lanes and overwrite
any part of the implementation with their own custom functionality. We
recommend that users extend the functionality of the `Base` lane when first
exploring the code base.

View File

@ -1,14 +1,214 @@
4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU=
cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU=
github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc=
github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg=
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI=
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo=
github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s=
github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/cloudflare/circl v1.3.1/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8=
github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 h1:zlCp9n3uwQieELltZWHRmwPmPaZ8+XoL2Sj+A2YJlr8=
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI=
github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c=
github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY=
github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0=
github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/getsentry/sentry-go v0.17.0 h1:UustVWnOoDFHBS7IJUB2QK/nB5pap748ZEp0swnQJak=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU=
github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw=
github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ=
github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4=
github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA=
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak=
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes=
github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY=
github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY=
github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g=
github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw=
github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50=
github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ=
github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw=
github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY=
github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4=
github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg=
github.com/tomarrell/wrapcheck/v2 v2.8.1/go.mod h1:/n2Q3NZ4XFT50ho6Hbxg+RV1uyo2Uow/Vdm9NQcl5SE=
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA=
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA=
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is=

111
lanes/base/README.md Normal file
View File

@ -0,0 +1,111 @@
# 🏗️ Default Lane Setup
## 📦 Dependencies
The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently
compatible with Cosmos SDK versions greater than or equal to `v0.47.0`.
## 📥 Installation
To install the Block SDK, run the following command:
```bash
$ go install github.com/skip-mev/block-sdk
```
## 📚 Usage
1. First determine the set of lanes that you want to use in your application. The
available lanes can be found in our
[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default).
This guide only sets up the `default lane`
2. In your base application, you will need to create a `LanedMempool` composed
of the `lanes` you want to use.
3. Next, order the lanes by priority. The first lane is the highest priority lane
and the last lane is the lowest priority lane. **It is recommended that the last
lane is the default lane.**
4. You will also need to create a `PrepareProposalHandler` and a
`ProcessProposalHandler` that will be responsible for preparing and processing
proposals respectively. Configure the order of the lanes in the
`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the
lanes in the `LanedMempool`.
5. Configure your `app.go` to include the following:
```golang
import (
"github.com/skip-mev/block-sdk/abci"
"github.com/skip-mev/block-sdk/block/base"
defaultlane "github.com/skip-mev/block-sdk/lanes/base"
)
...
func NewApp() {
...
// 1. 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.
//
// For more information on how to utilize the LaneConfig please
// visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config.
//
// Default lane accepts all transactions.
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
defaultLane := defaultlane.NewDefaultLane(defaultConfig)
// 2. Set up the relative priority of lanes
lanes := []block.Lane{
defaultLane,
}
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.App.SetMempool(mempool)
...
// 3. Set up the ante handler.
anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(),
...
utils.NewIgnoreDecorator(
ante.NewDeductFeeDecorator(
options.BaseOptions.AccountKeeper,
options.BaseOptions.BankKeeper,
options.BaseOptions.FeegrantKeeper,
options.BaseOptions.TxFeeChecker,
),
options.FreeLane,
),
...
}
anteHandler := sdk.ChainAnteDecorators(anteDecorators...)
// Set the lane ante handlers on the lanes.
//
// NOTE: This step is very important. Without the antehandlers, lanes will not
// be able to verify transactions.
for _, lane := range lanes {
lane.SetAnteHandler(anteHandler)
}
app.App.SetAnteHandler(anteHandler)
// 4. Set the abci handlers on base app
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.TxConfig().TxDecoder(),
lanes,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
...
}
```

View File

@ -8,9 +8,10 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/blockbuster/lanes/base"
"github.com/skip-mev/pob/blockbuster/utils/mocks"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
"github.com/skip-mev/pob/block/utils/mocks"
defaultlane "github.com/skip-mev/pob/lanes/base"
testutils "github.com/skip-mev/pob/testutils"
)
@ -41,7 +42,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
// Create a proposal
maxTxBytes := int64(len(txBz) - 1)
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().NoError(err)
// Ensure the proposal is empty
@ -75,7 +76,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
// Create a proposal
maxTxBytes := int64(len(txBz))
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().Error(err)
// Ensure the proposal is empty
@ -109,7 +110,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
// Create a proposal
maxTxBytes := int64(len(txBz))
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().NoError(err)
// Ensure the proposal is not empty and contains the transaction
@ -144,7 +145,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
s.Require().NoError(err)
maxTxBytes := int64(len(txBz))
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().NoError(err)
// Ensure the proposal is empty
@ -196,7 +197,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
s.Require().NoError(err)
maxTxBytes := int64(len(txBz1)) + int64(len(txBz2))
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().NoError(err)
// Ensure the proposal is ordered correctly
@ -245,7 +246,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
s.Require().NoError(err)
maxTxBytes := int64(len(txBz1)) + int64(len(txBz2))
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().NoError(err)
// Ensure the proposal is ordered correctly
@ -294,7 +295,7 @@ func (s *BaseTestSuite) TestPrepareLane() {
s.Require().NoError(err)
maxTxBytes := int64(len(txBz1)) + int64(len(txBz2)) - 1
proposal, err := lane.PrepareLane(sdk.Context{}, blockbuster.NewProposal(maxTxBytes), maxTxBytes, blockbuster.NoOpPrepareLanesHandler())
proposal, err := lane.PrepareLane(sdk.Context{}, block.NewProposal(maxTxBytes), maxTxBytes, block.NoOpPrepareLanesHandler())
s.Require().NoError(err)
// Ensure the proposal is ordered correctly
@ -323,7 +324,7 @@ func (s *BaseTestSuite) TestProcessLane() {
tx1: true,
})
_, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler())
_, err = lane.ProcessLane(sdk.Context{}, proposal, block.NoOpProcessLanesHandler())
s.Require().NoError(err)
})
@ -345,7 +346,7 @@ func (s *BaseTestSuite) TestProcessLane() {
tx1: false,
})
_, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler())
_, err = lane.ProcessLane(sdk.Context{}, proposal, block.NoOpProcessLanesHandler())
s.Require().Error(err)
})
@ -389,7 +390,7 @@ func (s *BaseTestSuite) TestProcessLane() {
tx3: true,
})
_, err = lane.ProcessLane(sdk.Context{}, proposal, blockbuster.NoOpProcessLanesHandler())
_, err = lane.ProcessLane(sdk.Context{}, proposal, block.NoOpProcessLanesHandler())
s.Require().Error(err)
})
}
@ -487,7 +488,7 @@ func (s *BaseTestSuite) TestCheckOrder() {
mocklane.On("Match", sdk.Context{}, tx2).Return(false)
lane := s.initLane(math.LegacyMustNewDecFromStr("1"), nil)
lane.SetIgnoreList([]blockbuster.Lane{mocklane})
lane.SetIgnoreList([]block.Lane{mocklane})
proposal := []sdk.Tx{
tx1,
@ -501,8 +502,8 @@ func (s *BaseTestSuite) TestCheckOrder() {
func (s *BaseTestSuite) initLane(
maxBlockSpace math.LegacyDec,
expectedExecution map[sdk.Tx]bool,
) *base.DefaultLane {
config := blockbuster.NewBaseLaneConfig(
) *defaultlane.DefaultLane {
config := base.NewLaneConfig(
log.NewTestLogger(s.T()),
s.encodingConfig.TxConfig.TxEncoder(),
s.encodingConfig.TxConfig.TxDecoder(),
@ -510,7 +511,7 @@ func (s *BaseTestSuite) initLane(
maxBlockSpace,
)
return base.NewDefaultLane(config)
return defaultlane.NewDefaultLane(config)
}
func (s *BaseTestSuite) setUpAnteHandler(expectedExecution map[sdk.Tx]bool) sdk.AnteHandler {

View File

@ -1,7 +1,8 @@
package base
import (
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
)
const (
@ -9,32 +10,32 @@ const (
LaneName = "default"
)
var _ blockbuster.Lane = (*DefaultLane)(nil)
var _ block.Lane = (*DefaultLane)(nil)
// DefaultLane defines a default lane implementation. The default lane orders
// transactions by the transaction fees. The default lane accepts any transaction
// that is should not be ignored (as defined by the IgnoreList in the LaneConfig).
// that should not be ignored (as defined by the IgnoreList in the LaneConfig).
// The default lane builds and verifies blocks in a similar fashion to how the
// CometBFT/Tendermint consensus engine builds and verifies blocks pre SDK version
// 0.47.0.
type DefaultLane struct {
*blockbuster.LaneConstructor
*base.BaseLane
}
// NewDefaultLane returns a new default lane.
func NewDefaultLane(cfg blockbuster.LaneConfig) *DefaultLane {
lane := blockbuster.NewLaneConstructor(
func NewDefaultLane(cfg base.LaneConfig) *DefaultLane {
lane := base.NewBaseLane(
cfg,
LaneName,
blockbuster.NewConstructorMempool[string](
blockbuster.DefaultTxPriority(),
base.NewMempool[string](
base.DefaultTxPriority(),
cfg.TxEncoder,
cfg.MaxTxs,
),
blockbuster.DefaultMatchHandler(),
base.DefaultMatchHandler(),
)
return &DefaultLane{
LaneConstructor: lane,
BaseLane: lane,
}
}

View File

@ -3,12 +3,12 @@ package base_test
import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block/base"
testutils "github.com/skip-mev/pob/testutils"
)
func (s *BaseTestSuite) TestGetTxPriority() {
txPriority := blockbuster.DefaultTxPriority()
txPriority := base.DefaultTxPriority()
s.Run("should be able to get the priority off a normal transaction with fees", func() {
tx, err := testutils.CreateRandomTx(
@ -56,7 +56,7 @@ func (s *BaseTestSuite) TestGetTxPriority() {
}
func (s *BaseTestSuite) TestCompareTxPriority() {
txPriority := blockbuster.DefaultTxPriority()
txPriority := base.DefaultTxPriority()
s.Run("should return 0 when both priorities are nil", func() {
a := sdk.NewCoin(s.gasTokenDenom, math.NewInt(0)).String()
@ -84,7 +84,7 @@ func (s *BaseTestSuite) TestCompareTxPriority() {
}
func (s *BaseTestSuite) TestInsert() {
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
s.Run("should be able to insert a transaction", func() {
tx, err := testutils.CreateRandomTx(
@ -136,7 +136,7 @@ func (s *BaseTestSuite) TestInsert() {
}
func (s *BaseTestSuite) TestRemove() {
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
s.Run("should be able to remove a transaction", func() {
tx, err := testutils.CreateRandomTx(
@ -174,7 +174,7 @@ func (s *BaseTestSuite) TestRemove() {
func (s *BaseTestSuite) TestSelect() {
s.Run("should be able to select transactions in the correct order", func() {
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
tx1, err := testutils.CreateRandomTx(
s.encodingConfig.TxConfig,
@ -213,7 +213,7 @@ func (s *BaseTestSuite) TestSelect() {
})
s.Run("should be able to select a single transaction", func() {
mempool := blockbuster.NewConstructorMempool[string](blockbuster.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
mempool := base.NewMempool[string](base.DefaultTxPriority(), s.encodingConfig.TxConfig.TxEncoder(), 3)
tx1, err := testutils.CreateRandomTx(
s.encodingConfig.TxConfig,

View File

@ -0,0 +1,463 @@
# 🏗️ Build-Your-Own Lane Setup
## 📦 Dependencies
The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently
compatible with Cosmos SDK versions greater than or equal to `v0.47.0`.
## 📥 Installation
To install the Block SDK, run the following command:
```bash
$ go install github.com/skip-mev/block-sdk
```
## 🤔 How to use it [30 min]
There are **five** required components to building a custom lane using the base lane:
1. `Mempool` - The lane's mempool is responsible for storing transactions that
have been verified and are waiting to be included in proposals.
2. `MatchHandler` - This is responsible for determining whether a transaction
should belong to this lane.
3. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own
handler to customize the how transactions are verified and ordered before they
are included into a proposal.
4. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own
handler that will run any custom checks on whether transactions included in
block proposals are in the correct order (respecting the ordering rules of the
lane and the ordering rules of the other lanes).
5. [**OPTIONAL**] `ProcessLaneHandler` - Allows developers to define their own
handler for processing transactions that are included in block proposals.
6. `Configuration` - Configure high-level options for your lane.
### 1. 🗄️ Mempool
This is the data structure that is responsible for storing transactions as they
are being verified and are waiting to be included in proposals.
`block/base/mempool.go` provides an out-of-the-box implementation that should be
used as a starting point for building out the mempool and should cover most use
cases. To utilize the mempool, you must implement a `TxPriority[C]` struct that
does the following:
* Implements a `GetTxPriority` method that returns the priority (as defined
by the type `[C]`) of a given transaction.
* Implements a `Compare` method that returns the relative priority of two
transactions. If the first transaction has a higher priority, the method
should return -1, if the second transaction has a higher priority the method
should return 1, otherwise the method should return 0.
* Implements a `MinValue` method that returns the minimum priority value
that a transaction can have.
The default implementation can be found in `block/base/mempool.go`.
> Scenario
What if we wanted to prioritize transactions by the amount they have staked on
a chain?
We could do the following:
```golang
// CustomTxPriority returns a TxPriority that prioritizes transactions by the
// amount they have staked on chain. This means that transactions with a higher
// amount staked will be prioritized over transactions with a lower amount staked.
func (p *CustomTxPriority) CustomTxPriority() TxPriority[string] {
return TxPriority[string]{
GetTxPriority: func(ctx context.Context, tx sdk.Tx) string {
// Get the signer of the transaction.
signer := p.getTransactionSigner(tx)
// Get the total amount staked by the signer on chain.
// This is abstracted away in the example, but you can
// implement this using the staking keeper.
totalStake, err := p.getTotalStake(ctx, signer)
if err != nil {
return ""
}
return totalStake.String()
},
Compare: func(a, b string) int {
aCoins, _ := sdk.ParseCoinsNormalized(a)
bCoins, _ := sdk.ParseCoinsNormalized(b)
switch {
case aCoins == nil && bCoins == nil:
return 0
case aCoins == nil:
return -1
case bCoins == nil:
return 1
default:
switch {
case aCoins.IsAllGT(bCoins):
return 1
case aCoins.IsAllLT(bCoins):
return -1
default:
return 0
}
}
},
MinValue: "",
}
}
```
#### Using a Custom TxPriority
To utilize this new priority configuration in a lane, all you have to then do
is pass in the `TxPriority[C]` to the `NewMempool` function.
```golang
// Create the lane config
laneCfg := NewLaneConfig(
...
MaxTxs: 100,
...
)
// Pseudocode for creating the custom tx priority
priorityCfg := NewPriorityConfig(
stakingKeeper,
accountKeeper,
...
)
// define your mempool that orders transactions by on-chain stake
mempool := base.NewMempool[string](
priorityCfg.CustomTxPriority(), // pass in the custom tx priority
laneCfg.TxEncoder,
laneCfg.MaxTxs,
)
// Initialize your lane with the mempool
lane := base.NewBaseLane(
laneCfg,
LaneName,
mempool,
base.DefaultMatchHandler(),
)
```
### 2. 🤝 MatchHandler
`MatchHandler` is utilized to determine if a transaction should be included in
the lane. **This function can be a stateless or stateful check on the
transaction!** The default implementation can be found in `block/base/handlers.go`.
The match handler can be as custom as desired. Following the example above, if
we wanted to make a lane that only accepts transactions if they have a large
amount staked, we could do the following:
```golang
// CustomMatchHandler returns a custom implementation of the MatchHandler. It
// matches transactions that have a large amount staked. These transactions
// will then be charged no fees at execution time.
//
// NOTE: This is a stateful check on the transaction. The details of how to
// implement this are abstracted away in the example, but you can implement
// this using the staking keeper.
func (h *Handler) CustomMatchHandler() block.MatchHandler {
return func(ctx sdk.Context, tx sdk.Tx) bool {
if !h.IsStakingTx(tx) {
return false
}
signer, err := getTxSigner(tx)
if err != nil {
return false
}
stakedAmount, err := h.GetStakedAmount(signer)
if err != nil {
return false
}
// The transaction can only be considered for inclusion if the amount
// staked is greater than some predetermined threshold.
return stakeAmount.GT(h.Threshold)
}
}
```
#### Using a Custom MatchHandler
If we wanted to create the lane using the custom match handler along with the
custom mempool, we could do the following:
```golang
// Pseudocode for creating the custom match handler
handler := NewHandler(
stakingKeeper,
accountKeeper,
...
)
// define your mempool that orders transactions by on chain stake
mempool := base.NewMempool[string](
priorityCfg.CustomTxPriority(),
cfg.TxEncoder,
cfg.MaxTxs,
)
// Initialize your lane with the mempool
lane := base.NewBaseLane(
cfg,
LaneName,
mempool,
handler.CustomMatchHandler(),
)
```
### [OPTIONAL] Steps 3-5
The remaining steps walk through the process of creating custom block
building/verification logic. The default implementation found in
`block/base/handlers.go` should fit most use cases. Please reference that file
for more details on the default implementation and whether it fits your use case.
Implementing custom block building/verification logic is a bit more involved
than the previous steps and is a all or nothing approach. This means that if
you implement any of the handlers, you must implement all of them in most cases.
If you do not implement all of them, the lane may have unintended behavior.
### 3. 🛠️ PrepareLaneHandler
The `PrepareLaneHandler` is an optional field you can set on the base lane.
This handler is responsible for the transaction selection logic when a new proposal
is requested.
The handler should return the following for a given lane:
1. The transactions to be included in the block proposal.
2. The transactions to be removed from the lane's mempool.
3. An error if the lane is unable to prepare a block proposal.
```golang
// PrepareLaneHandler is responsible for preparing transactions to be included
// in the block from a given lane. Given a lane, this function should return
// the transactions to include in the block, the transactions that must be
// removed from the lane, and an error if one occurred.
PrepareLaneHandler func(ctx sdk.Context,proposal BlockProposal,maxTxBytes int64)
(txsToInclude [][]byte, txsToRemove []sdk.Tx, err error)
```
The default implementation is simple. It will continue to select transactions
from its mempool under the following criteria:
1. The transactions is not already included in the block proposal.
2. The transaction is valid and passes the AnteHandler check.
3. The transaction is not too large to be included in the block.
If a more involved selection process is required, you can implement your own
`PrepareLaneHandler` and and set it after creating the base lane.
```golang
// Pseudocode for creating the custom prepare lane handler
// This assumes that the CustomLane inherits from the base
// lane.
customLane := NewCustomLane(
cfg,
mempool,
handler.CustomMatchHandler(),
)
// Set the custom PrepareLaneHandler on the lane
customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler())
```
### 4. ✅ CheckOrderHandler
The `CheckOrderHandler` is an optional field you can set on the base lane.
This handler is responsible for verifying the ordering of the transactions in
the block proposal that belong to the lane.
```golang
// CheckOrderHandler is responsible for checking the order of transactions that
// belong to a given lane. This handler should be used to verify that the
// ordering of transactions passed into the function respect the ordering logic
// of the lane (if any transactions from the lane are included). This function
// should also ensure that transactions that belong to this lane are contiguous
// and do not have any transactions from other lanes in between them.
CheckOrderHandler func(ctx sdk.Context, txs []sdk.Tx) error
```
The default implementation is simple and utilizes the same `TxPriority` struct
that the mempool uses to determine if transactions are in order. The criteria
for determining if transactions are in order is as follows:
1. The transactions are in order according to the `TxPriority` struct. i.e.
any two transactions (that match to the lane) `tx1` and `tx2` where `tx1` has a
higher priority than `tx2` should be ordered before `tx2`.
2. The transactions are contiguous. i.e. there are no transactions from other
lanes in between the transactions that belong to this lane. i.e. if `tx1` and
`tx2` belong to the lane, there should be no transactions from other lanes in
between `tx1` and `tx2`.
If a more involved ordering process is required, you can implement your own
`CheckOrderHandler` and and set it after creating the base lane.
```golang
// Pseudocode for creating the custom check order handler
// This assumes that the CustomLane inherits from the base
// lane.
customLane := NewCustomLane(
cfg,
mempool,
handler.CustomMatchHandler(),
)
// Set the custom CheckOrderHandler on the lane
customLane.SetCheckOrderHandler(customlane.CheckOrderHandler())
```
### 5. 🆗 ProcessLaneHandler
The `ProcessLaneHandler` is an optional field you can set on the base lane.
This handler is responsible for verifying the transactions in the block proposal
that belong to the lane. This handler is executed after the `CheckOrderHandler`
so the transactions passed into this function SHOULD already be in order
respecting the ordering rules of the lane and respecting the ordering rules of
mempool relative to the lanes it has. This means that if the first transaction
does not belong to the lane, the remaining transactions should not belong to
the lane either.
```golang
// ProcessLaneHandler is responsible for processing transactions that are
// included in a block and belong to a given lane. ProcessLaneHandler is
// executed after CheckOrderHandler so the transactions passed into this
// function SHOULD already be in order respecting the ordering rules of the
// lane and respecting the ordering rules of mempool relative to the lanes it has.
ProcessLaneHandler func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error)
```
Given the invarients above, the default implementation is simple. It will
continue to verify transactions in the block proposal under the following criteria:
1. If a transaction matches to this lane, verify it and continue. If it is not
valid, return an error.
2. If a transaction does not match to this lane, return the remaining
transactions to the next lane to process.
Similar to the setup of handlers above, if a more involved verification process
is required, you can implement your own `ProcessLaneHandler` and and set it
after creating the base lane.
```golang
// Pseudocode for creating the custom check order handler
// This assumes that the CustomLane inherits from the base
// lane.
customLane := NewCustomLane(
cfg,
mempool,
handler.CustomMatchHandler(),
)
// Set the custom ProcessLaneHandler on the lane
customLane.SetProcessLaneHandler(customlane.ProcessLaneHandler())
```
### 6. 📝 Lane Configuration
Once you have created your custom lane, you can configure it in the application
by doing the following:
1. Create a custom `LaneConfig` struct that defines the configuration of the lane.
2. Instantiate the lane with the custom `LaneConfig` struct alongside any other
dependencies (mempool, match handler, etc.).
3. Instantiate a new `LanedMempool` with the custom lane.
4. Set the `LanedMempool` on the `BaseApp` instance.
5. Set up the proposal handlers of the Block SDK to use your lane.
6. That's it! You're done!
The lane config (`LaneConfig`) is a simple configuration object that defines
the desired amount of block space the lane should utilize when building a
proposal, an antehandler that is used to verify transactions as they are
added/verified to/in a proposal, and more. By default, we recommend that user's
pass in all of the base apps configurations (txDecoder, logger, etc.). A sample
`LaneConfig` might look like the following:
```golang
config := block.LaneConfig{
Logger: app.Logger(),
TxDecoder: app.TxDecoder(),
TxEncoder: app.TxEncoder(),
AnteHandler: app.AnteHandler(),
MaxTxs: 0,
MaxBlockSpace: math.LegacyZeroDec(),
IgnoreList: []block.Lane{},
}
```
The three most important parameters to set are the `AnteHandler`, `MaxTxs`, and `MaxBlockSpace`.
#### **AnteHandler**
With the default implementation, the `AnteHandler` is responsible for verifying
transactions as they are being considered for a new proposal or are being
processed in a proposed block. We recommend user's utilize the same antehandler
chain that is used in the base app. If developers want a certain `AnteDecorator`
to be ignored if it qualifies for a given lane, they can do so by using the
`NewIgnoreDecorator` defined in `block/utils/ante.go`.
For example, a free lane might want to ignore the `DeductFeeDecorator` so that
its transactions are not charged any fees. Where ever the `AnteHandler` is
defined, we could add the following to ignore the `DeductFeeDecorator`:
```golang
anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(),
...,
utils.NewIgnoreDecorator(
ante.NewDeductFeeDecorator(
options.BaseOptions.AccountKeeper,
options.BaseOptions.BankKeeper,
options.BaseOptions.FeegrantKeeper,
options.BaseOptions.TxFeeChecker,
),
options.FreeLane,
),
...,
}
```
Anytime a transaction that qualifies for the free lane is being processed, the
`DeductFeeDecorator` will be ignored and no fees will be deducted!
#### **MaxTxs**
This sets the maximum number of transactions allowed in the mempool with the semantics:
* if `MaxTxs` == 0, there is no cap on the number of transactions in the mempool
* if `MaxTxs` > 0, the mempool will cap the number of transactions it stores,
and will prioritize transactions by their priority and sender-nonce
(sequence number) when evicting transactions.
* if `MaxTxs` < 0, `Insert` is a no-op.
#### **MaxBlockSpace**
MaxBlockSpace is the maximum amount of block space that the lane will attempt
to fill when building a proposal. This parameter may be useful lanes that
should be limited (such as a free or onboarding lane) in space usage.
Setting this to 0 will allow the lane to fill the block with as many
transactions as possible.
If a block proposal request has a `MaxTxBytes` of 1000 and the lane has a
`MaxBlockSpace` of 0.5, the lane will attempt to fill the block with 500 bytes.
#### **[OPTIONAL] IgnoreList**
`IgnoreList` defines the list of lanes to ignore when processing transactions.
For example, say there are two lanes: default and free. The free lane is
processed after the default lane. In this case, the free lane should be added
to the ignore list of the default lane. Otherwise, the transactions that belong
to the free lane will be processed by the default lane (which accepts all
transactions by default).

129
lanes/free/README.md Normal file
View File

@ -0,0 +1,129 @@
# 🏗️ Free Lane Setup
## 📦 Dependencies
The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently
compatible with Cosmos SDK versions greater than or equal to `v0.47.0`.
## 📥 Installation
To install the Block SDK, run the following command:
```bash
$ go install github.com/skip-mev/block-sdk
```
## 📚 Usage
1. First determine the set of lanes that you want to use in your application. The
available lanes can be found in our
[Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default).
In your base application, you will need to create a `LanedMempool` composed of the
lanes you want to use. *The free lane should not exist on its own. At minimum, it
is recommended that the free lane is paired with the default lane.*
2. Next, order the lanes by priority. The first lane is the highest priority lane
and the last lane is the lowest priority lane.
3. Set up your `FeeDeductorDecorator` to ignore the free lane where ever you
initialize your `AnteHandler`. This will ensure that the free lane is not
subject to deducting transaction fees.
4. You will also need to create a `PrepareProposalHandler` and a
`ProcessProposalHandler` that will be responsible for preparing and processing
proposals respectively. Configure the order of the lanes in the
`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the
lanes in the `LanedMempool`.
NOTE: This example walks through setting up the Free and Default lanes.
```golang
import (
"github.com/skip-mev/block-sdk/abci"
"github.com/skip-mev/block-sdk/block/base"
defaultlane "github.com/skip-mev/block-sdk/lanes/base"
freelane "github.com/skip-mev/block-sdk/lanes/free"
)
...
func NewApp() {
...
// 1. 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.
//
// For more information on how to utilize the LaneConfig please
// visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config.
//
// Set up the configuration of the free lane and instantiate it.
freeConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
freeLane := freelane.NewFreeLane(freeConfig, base.DefaultTxPriority(), freelane.DefaultMatchHandler())
// Default lane accepts all transactions.
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
defaultLane := defaultlane.NewDefaultLane(defaultConfig)
// 2. Set up the relative priority of lanes
lanes := []block.Lane{
freeLane,
defaultLane,
}
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.App.SetMempool(mempool)
...
// 3. Set up the ante handler.
//
// This will allow any transaction that matches the to the free lane to
// be processed without paying any fees.
anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(),
...
utils.NewIgnoreDecorator(
ante.NewDeductFeeDecorator(
options.BaseOptions.AccountKeeper,
options.BaseOptions.BankKeeper,
options.BaseOptions.FeegrantKeeper,
options.BaseOptions.TxFeeChecker,
),
options.FreeLane,
),
...
}
anteHandler := sdk.ChainAnteDecorators(anteDecorators...)
// Set the lane ante handlers on the lanes.
//
// NOTE: This step is very important. Without the antehandlers, lanes will not
// be able to verify transactions.
for _, lane := range lanes {
lane.SetAnteHandler(anteHandler)
}
app.App.SetAnteHandler(anteHandler)
// 4. Set the abci handlers on base app
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.TxConfig().TxDecoder(),
lanes,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
...
}
```

View File

@ -3,7 +3,8 @@ package free
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
)
const (
@ -11,24 +12,24 @@ const (
LaneName = "free"
)
var _ blockbuster.Lane = (*FreeLane)(nil)
var _ block.Lane = (*FreeLane)(nil)
// FreeLane defines the lane that is responsible for processing free transactions.
// By default, transactions that are staking related are considered free.
type FreeLane struct { //nolint
*blockbuster.LaneConstructor
*base.BaseLane
}
// NewFreeLane returns a new free lane.
func NewFreeLane(
cfg blockbuster.LaneConfig,
txPriority blockbuster.TxPriority[string],
matchFn blockbuster.MatchHandler,
cfg base.LaneConfig,
txPriority base.TxPriority[string],
matchFn block.MatchHandler,
) *FreeLane {
lane := blockbuster.NewLaneConstructor(
lane := base.NewBaseLane(
cfg,
LaneName,
blockbuster.NewConstructorMempool[string](
base.NewMempool[string](
txPriority,
cfg.TxEncoder,
cfg.MaxTxs,
@ -37,14 +38,14 @@ func NewFreeLane(
)
return &FreeLane{
LaneConstructor: lane,
BaseLane: lane,
}
}
// DefaultMatchHandler returns the default match handler for the free lane. The
// default implementation matches transactions that are staking related. In particular,
// any transaction that is a MsgDelegate, MsgBeginRedelegate, or MsgCancelUnbondingDelegation.
func DefaultMatchHandler() blockbuster.MatchHandler {
func DefaultMatchHandler() block.MatchHandler {
return func(ctx sdk.Context, tx sdk.Tx) bool {
for _, msg := range tx.GetMsgs() {
switch msg.(type) {

258
lanes/mev/README.md Normal file
View File

@ -0,0 +1,258 @@
# 🏗️ MEV Lane Setup
## 📦 Dependencies
The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently
compatible with Cosmos SDK versions greater than or equal to `v0.47.0`.
## 📥 Installation
To install the Block SDK, run the following command:
```bash
$ go install github.com/skip-mev/block-sdk
```
## 📚 Usage
1. This guide assumes you have already set up the [Block SDK (and the default lane)](https://docs.skip.money/chains/overview)
2. You will need to instantiate the `x/builder` module into your application. This
module is responsible for processing auction transactions and distributing revenue
to the auction house. The `x/builder` module is also responsible for ensuring the
validity of auction transactions. *The `x/builder` module should not exist on its
own. **This is the most intensive part of the set up process.**
3. Next, add the MEV lane into the `lane` object on your `app.go`. The first
lane is the highest priority lane and the last lane is the lowest priority lane.
Since the MEV lane is meant to auction off the top of the block, **it should be
the highest priority lane**. The default lane should follow.
4. You will also need to create a `PrepareProposalHandler` and a
`ProcessProposalHandler` that will be responsible for preparing and processing
proposals respectively. Configure the order of the lanes in the
`PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the
lanes in the `LanedMempool`.
NOTE: This example walks through setting up the MEV and Default lanes.
1. Import the necessary dependencies into your application. This includes the
Block SDK proposal handlers + mempool, keeper, builder types, and builder
module. This tutorial will go into more detail into each of the dependencies.
```go
import (
...
"github.com/skip-mev/pob/block-sdk"
"github.com/skip-mev/pob/block-sdk/abci"
"github.com/skip-mev/pob/block-sdk/lanes/mev"
"github.com/skip-mev/pob/block-sdk/lanes/base"
buildermodule "github.com/skip-mev/block-sdk/x/builder"
builderkeeper "github.com/skip-mev/block-sdk/x/builder/keeper"
buildertypes "github.com/skip-mev/block-sdk/x/builder/types"
builderante "github.com/skip-mev/block-sdk/x/builder/ante"
...
)
```
2. Add your module to the the `AppModuleBasic` manager. This manager is in
charge of setting up basic, non-dependent module elements such as codec
registration and genesis verification. This will register the special
`MsgAuctionBid` message. When users want to bid for top of block execution,
they will submit a transaction - which we call an auction transaction - that
includes a single `MsgAuctionBid`. We prevent any other messages from being
included in auction transaction to prevent malicious behavior - such as front
running or sandwiching.
```go
var (
ModuleBasics = module.NewBasicManager(
...
buildermodule.AppModuleBasic{},
)
...
)
```
3. The builder `Keeper` is MEV lane's gateway to processing special `MsgAuctionBid`
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. We also want to add
MEV lane'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 `LanedMempool`. NOTE: The custom handler
is required as otherwise the auction can be held hostage by a malicious
users.
```go
type App struct {
...
// BuilderKeeper is the keeper that handles processing auction transactions
BuilderKeeper builderkeeper.Keeper
// Custom checkTx handler
checkTxHandler mev.CheckTx
}
```
b. Add the builder module to the list of module account permissions. This will
instantiate the builder module account on genesis.
```go
maccPerms = map[string][]string{
builder.ModuleName: nil,
...
}
```
c. Instantiate the Block SDK's `LanedMempool` with the application's
desired lanes.
```go
// 1. 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.
//
// For more information on how to utilize the LaneConfig please
// visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config.
//
// MEV lane hosts an auction at the top of the block.
mevConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
mevLane := mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()),
)
// default lane accepts all other transactions.
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
defaultLane := base.NewStandardLane(defaultConfig)
// 2. Set up the relateive priority of lanes
lanes := []block.Lane{
mevLane,
defaultLane,
}
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.App.SetMempool(mempool)
```
d. 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
anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(),
...
builderante.NewBuilderDecorator(
options.BuilderKeeper,
options.TxEncoder,
options.TOBLane,
options.Mempool,
),
}
anteHandler := sdk.ChainAnteDecorators(anteDecorators...)
app.SetAnteHandler(anteHandler)
// Set the antehandlers on the lanes.
//
// NOTE: This step is required as otherwise the lanes will not be able to
// process auction transactions.
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
keys := storetypes.NewKVStoreKeys(
buildertypes.StoreKey,
...
)
...
app.BuilderKeeper := builderkeeper.NewKeeper(
appCodec,
keys[buildertypes.StoreKey],
app.AccountKeeper,
app.BankKeeper,
app.DistrKeeper,
app.StakingKeeper,
authtypes.NewModuleAddress(govv1.ModuleName).String(),
)
app.ModuleManager = module.NewManager(
builder.NewAppModule(appCodec, app.BuilderKeeper),
...
)
```
f. Configure the proposal/checkTx handlers on base app.
```go
// Create the proposal handler that will be used to build and validate blocks.
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.txConfig.TxDecoder(),
lanes,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
// Set the custom CheckTx handler on BaseApp.
checkTxHandler := mev.NewCheckTxHandler(
app.App,
app.txConfig.TxDecoder(),
mevLane,
anteHandler,
)
app.SetCheckTx(checkTxHandler.CheckTx())
// CheckTx will check the transaction with the provided checkTxHandler.
// We override the default handler so that we can verify transactions
// before they are inserted into the mempool. With the CheckTx, we can
// verify the bid transaction and all of the bundled transactions
// before inserting the bid transaction into the mempool.
func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx)
(*cometabci.ResponseCheckTx, error) {
return app.checkTxHandler(req)
}
// SetCheckTx sets the checkTxHandler for the app.
func (app *TestApp) SetCheckTx(handler mev.CheckTx) {
app.checkTxHandler = handler
}
```
g. Finally, update the app's `InitGenesis` order.
```go
genesisModuleOrder := []string{
buildertypes.ModuleName,
...,
}
```
## Params
Note, before building or upgrading the application, make sure to initialize the
escrow address in the parameters of the module. The default parameters
initialize the escrow address to be the module account address.

View File

@ -1,12 +1,12 @@
package auction
package mev
import (
"bytes"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/utils"
"github.com/skip-mev/pob/x/builder/types"
)
@ -14,8 +14,8 @@ import (
// and whose bundled transactions are valid and include them in the proposal. It
// will return no transactions if no valid bids are found. If any of the bids are invalid,
// it will return them and will only remove the bids and not the bundled transactions.
func (l *TOBLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler {
return func(ctx sdk.Context, proposal blockbuster.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) {
func (l *MEVLane) PrepareLaneHandler() block.PrepareLaneHandler {
return func(ctx sdk.Context, proposal block.BlockProposal, maxTxBytes int64) ([][]byte, []sdk.Tx, error) {
// Define all of the info we need to select transactions for the partial proposal.
var (
txs [][]byte
@ -128,7 +128,7 @@ func (l *TOBLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler {
txs = append(txs, bundledTxBz...)
// Write the cache context to the original context when we know we have a
// valid top of block bundle.
// valid bundle.
write()
break selectBidTxLoop
@ -146,8 +146,8 @@ func (l *TOBLane) PrepareLaneHandler() blockbuster.PrepareLaneHandler {
}
// ProcessLaneHandler will ensure that block proposals that include transactions from
// the top-of-block auction lane are valid.
func (l *TOBLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler {
// the mev lane are valid.
func (l *MEVLane) ProcessLaneHandler() block.ProcessLaneHandler {
return func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error) {
if len(txs) == 0 {
return txs, nil
@ -178,7 +178,7 @@ func (l *TOBLane) ProcessLaneHandler() blockbuster.ProcessLaneHandler {
// - there are no other bid transactions in the proposal
// - transactions from other lanes are not interleaved with transactions from the bid
// transaction.
func (l *TOBLane) CheckOrderHandler() blockbuster.CheckOrderHandler {
func (l *MEVLane) CheckOrderHandler() block.CheckOrderHandler {
return func(ctx sdk.Context, txs []sdk.Tx) error {
if len(txs) == 0 {
return nil
@ -241,7 +241,7 @@ func (l *TOBLane) CheckOrderHandler() blockbuster.CheckOrderHandler {
// VerifyTx will verify that the bid transaction and all of its bundled
// transactions are valid. It will return an error if any of the transactions
// are invalid.
func (l *TOBLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *types.BidInfo) (err error) {
func (l *MEVLane) VerifyTx(ctx sdk.Context, bidTx sdk.Tx, bidInfo *types.BidInfo) (err error) {
if bidInfo == nil {
return fmt.Errorf("bid info is nil")
}

View File

@ -1,4 +1,4 @@
package auction
package mev
import (
"fmt"
@ -26,9 +26,9 @@ type (
// bid transactions.
txDecoder sdk.TxDecoder
// TOBLane is utilized to retrieve the bid info of a transaction and to
// MEVLane is utilized to retrieve the bid info of a transaction and to
// insert a bid transaction into the application-side mempool.
tobLane TOBLaneI
mevLane MEVLaneI
// anteHandler is utilized to verify the bid transaction against the latest
// committed state.
@ -63,17 +63,17 @@ type (
}
)
// NewCheckTxHandler is a constructor for CheckTxHandler.
// NewCheckTxHandler constructs a new CheckTxHandler instance.
func NewCheckTxHandler(
baseApp BaseApp,
txDecoder sdk.TxDecoder,
tobLane TOBLaneI,
mevLane MEVLaneI,
anteHandler sdk.AnteHandler,
) *CheckTxHandler {
return &CheckTxHandler{
baseApp: baseApp,
txDecoder: txDecoder,
tobLane: tobLane,
mevLane: mevLane,
anteHandler: anteHandler,
}
}
@ -121,7 +121,7 @@ func (handler *CheckTxHandler) CheckTx() CheckTx {
}
// Attempt to get the bid info of the transaction.
bidInfo, err := handler.tobLane.GetAuctionBidInfo(tx)
bidInfo, err := handler.mevLane.GetAuctionBidInfo(tx)
if err != nil {
handler.baseApp.Logger().Info(
"failed to get auction bid info",
@ -173,7 +173,7 @@ func (handler *CheckTxHandler) CheckTx() CheckTx {
}
// If the bid transaction is valid, we know we can insert it into the mempool for consideration in the next block.
if err := handler.tobLane.Insert(ctx, tx); err != nil {
if err := handler.mevLane.Insert(ctx, tx); err != nil {
handler.baseApp.Logger().Info(
"invalid bid tx; failed to insert bid transaction into mempool",
"err", err,
@ -212,13 +212,13 @@ func (handler *CheckTxHandler) ValidateBidTx(ctx sdk.Context, bidTx sdk.Tx, bidI
// Verify all of the bundled transactions.
for _, tx := range bidInfo.Transactions {
bundledTx, err := handler.tobLane.WrapBundleTransaction(tx)
bundledTx, err := handler.mevLane.WrapBundleTransaction(tx)
if err != nil {
return gasInfo, fmt.Errorf("invalid bid tx; failed to decode bundled tx: %w", err)
}
// bid txs cannot be included in bundled txs
bidInfo, _ := handler.tobLane.GetAuctionBidInfo(bundledTx)
bidInfo, _ := handler.mevLane.GetAuctionBidInfo(bundledTx)
if bidInfo != nil {
return gasInfo, fmt.Errorf("invalid bid tx; bundled tx cannot be a bid tx")
}

View File

@ -1,11 +1,11 @@
package auction
package mev
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/x/builder/types"
)
@ -24,7 +24,7 @@ type (
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
// MatchHandler defines a function that checks if a transaction matches the auction lane.
MatchHandler() blockbuster.MatchHandler
MatchHandler() block.MatchHandler
}
// DefaultAuctionFactory defines a default implmentation for the auction factory interface for processing auction transactions.
@ -95,7 +95,7 @@ func (config *DefaultAuctionFactory) GetAuctionBidInfo(tx sdk.Tx) (*types.BidInf
}, nil
}
func (config *DefaultAuctionFactory) MatchHandler() blockbuster.MatchHandler {
func (config *DefaultAuctionFactory) MatchHandler() block.MatchHandler {
return func(ctx sdk.Context, tx sdk.Tx) bool {
bidInfo, err := config.GetAuctionBidInfo(tx)
return bidInfo != nil && err == nil

View File

@ -1,4 +1,4 @@
package auction_test
package mev_test
import (
"crypto/rand"
@ -8,7 +8,7 @@ import (
testutils "github.com/skip-mev/pob/testutils"
)
func (suite *IntegrationTestSuite) TestIsAuctionTx() {
func (suite *MEVTestSuite) TestIsAuctionTx() {
testCases := []struct {
name string
createTx func() sdk.Tx
@ -92,7 +92,7 @@ func (suite *IntegrationTestSuite) TestIsAuctionTx() {
}
}
func (suite *IntegrationTestSuite) TestGetTransactionSigners() {
func (suite *MEVTestSuite) TestGetTransactionSigners() {
testCases := []struct {
name string
createTx func() sdk.Tx
@ -176,7 +176,7 @@ func (suite *IntegrationTestSuite) TestGetTransactionSigners() {
}
}
func (suite *IntegrationTestSuite) TestWrapBundleTransaction() {
func (suite *MEVTestSuite) TestWrapBundleTransaction() {
testCases := []struct {
name string
createBundleTx func() (sdk.Tx, []byte)
@ -229,7 +229,7 @@ func (suite *IntegrationTestSuite) TestWrapBundleTransaction() {
}
}
func (suite *IntegrationTestSuite) TestGetBidder() {
func (suite *MEVTestSuite) TestGetBidder() {
testCases := []struct {
name string
createTx func() sdk.Tx
@ -304,7 +304,7 @@ func (suite *IntegrationTestSuite) TestGetBidder() {
}
}
func (suite *IntegrationTestSuite) TestGetBid() {
func (suite *MEVTestSuite) TestGetBid() {
testCases := []struct {
name string
createTx func() sdk.Tx
@ -379,7 +379,7 @@ func (suite *IntegrationTestSuite) TestGetBid() {
}
}
func (suite *IntegrationTestSuite) TestGetBundledTransactions() {
func (suite *MEVTestSuite) TestGetBundledTransactions() {
testCases := []struct {
name string
createTx func() (sdk.Tx, [][]byte)
@ -450,7 +450,7 @@ func (suite *IntegrationTestSuite) TestGetBundledTransactions() {
}
}
func (suite *IntegrationTestSuite) TestGetTimeout() {
func (suite *MEVTestSuite) TestGetTimeout() {
testCases := []struct {
name string
createTx func() sdk.Tx

View File

@ -1,37 +1,37 @@
package auction
package mev
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
)
const (
// LaneName defines the name of the top-of-block auction lane.
LaneName = "top-of-block"
// LaneName defines the name of the mev lane.
LaneName = "mev"
)
var _ TOBLaneI = (*TOBLane)(nil)
var _ MEVLaneI = (*MEVLane)(nil)
// TOBLane defines a top-of-block auction lane. The top of block auction lane
// MEVLane defines a MEV (Maximal Extracted Value) auction lane. The MEV auction lane
// hosts transactions that want to bid for inclusion at the top of the next block.
// The top of block auction lane stores bid transactions that are sorted by
// their bid price. The highest valid bid transaction is selected for inclusion in the
// next block. The bundled transactions of the selected bid transaction are also
// included in the next block.
// The MEV auction lane stores bid transactions that are sorted by their bid price.
// The highest valid bid transaction is selected for inclusion in the next block.
// The bundled transactions of the selected bid transaction are also included in the
// next block.
type (
// TOBLaneI defines the interface for the top-of-block auction lane. This interface
// MEVLaneI defines the interface for the mev auction lane. This interface
// is utilized by both the x/builder module and the checkTx handler.
TOBLaneI interface {
blockbuster.Lane
MEVLaneI interface { //nolint
block.Lane
Factory
GetTopAuctionTx(ctx context.Context) sdk.Tx
}
TOBLane struct {
// LaneConfig defines the base lane configuration.
*blockbuster.LaneConstructor
MEVLane struct { //nolint
*base.BaseLane
// Factory defines the API/functionality which is responsible for determining
// if a transaction is a bid transaction and how to extract relevant
@ -40,16 +40,16 @@ type (
}
)
// NewTOBLane returns a new TOB lane.
func NewTOBLane(
cfg blockbuster.LaneConfig,
// NewMEVLane returns a new TOB lane.
func NewMEVLane(
cfg base.LaneConfig,
factory Factory,
) *TOBLane {
lane := &TOBLane{
LaneConstructor: blockbuster.NewLaneConstructor(
) *MEVLane {
lane := &MEVLane{
BaseLane: base.NewBaseLane(
cfg,
LaneName,
blockbuster.NewConstructorMempool[string](
base.NewMempool[string](
TxPriority(factory),
cfg.TxEncoder,
cfg.MaxTxs,

View File

@ -1,16 +1,16 @@
package auction
package mev
import (
"context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block/base"
)
// TxPriority returns a TxPriority over auction bid transactions only. It
// is to be used in the auction index only.
func TxPriority(config Factory) blockbuster.TxPriority[string] {
return blockbuster.TxPriority[string]{
// TxPriority returns a TxPriority over mev lane transactions only. It
// is to be used in the mev index only.
func TxPriority(config Factory) base.TxPriority[string] {
return base.TxPriority[string]{
GetTxPriority: func(goCtx context.Context, tx sdk.Tx) string {
bidInfo, err := config.GetAuctionBidInfo(tx)
if err != nil {
@ -52,7 +52,7 @@ func TxPriority(config Factory) blockbuster.TxPriority[string] {
// GetTopAuctionTx returns the highest bidding transaction in the auction mempool.
// This is primarily a helper function for the x/builder module.
func (l *TOBLane) GetTopAuctionTx(ctx context.Context) sdk.Tx {
func (l *MEVLane) GetTopAuctionTx(ctx context.Context) sdk.Tx {
iterator := l.Select(ctx, nil)
if iterator == nil {
return nil

View File

@ -1,4 +1,4 @@
package auction_test
package mev_test
import (
"math/rand"
@ -8,16 +8,16 @@ import (
"cosmossdk.io/log"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/skip-mev/pob/blockbuster/lanes/auction"
"github.com/skip-mev/pob/lanes/mev"
testutils "github.com/skip-mev/pob/testutils"
"github.com/stretchr/testify/suite"
)
type IntegrationTestSuite struct {
type MEVTestSuite struct {
suite.Suite
encCfg testutils.EncodingConfig
config auction.Factory
config mev.Factory
ctx sdk.Context
random *rand.Rand
accounts []testutils.Account
@ -25,13 +25,13 @@ type IntegrationTestSuite struct {
}
func TestMempoolTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
suite.Run(t, new(MEVTestSuite))
}
func (suite *IntegrationTestSuite) SetupTest() {
func (suite *MEVTestSuite) SetupTest() {
// Mempool setup
suite.encCfg = testutils.CreateTestEncodingConfig()
suite.config = auction.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder())
suite.config = mev.NewDefaultAuctionFactory(suite.encCfg.TxConfig.TxDecoder())
suite.ctx = sdk.NewContext(nil, cmtproto.Header{}, false, log.NewTestLogger(suite.T()))
// Init accounts

View File

@ -1,4 +1,4 @@
package auction
package mev
import (
"errors"

View File

@ -1,10 +1,10 @@
package auction_test
package mev_test
import (
"testing"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/skip-mev/pob/blockbuster/lanes/auction"
"github.com/skip-mev/pob/lanes/mev"
testutils "github.com/skip-mev/pob/testutils"
buildertypes "github.com/skip-mev/pob/x/builder/types"
"github.com/stretchr/testify/require"
@ -16,7 +16,7 @@ func TestGetMsgAuctionBidFromTx_Valid(t *testing.T) {
txBuilder := encCfg.TxConfig.NewTxBuilder()
txBuilder.SetMsgs(&buildertypes.MsgAuctionBid{})
msg, err := auction.GetMsgAuctionBidFromTx(txBuilder.GetTx())
msg, err := mev.GetMsgAuctionBidFromTx(txBuilder.GetTx())
require.NoError(t, err)
require.NotNil(t, msg)
}
@ -31,7 +31,7 @@ func TestGetMsgAuctionBidFromTx_MultiMsgBid(t *testing.T) {
&banktypes.MsgSend{},
)
msg, err := auction.GetMsgAuctionBidFromTx(txBuilder.GetTx())
msg, err := mev.GetMsgAuctionBidFromTx(txBuilder.GetTx())
require.Error(t, err)
require.Nil(t, msg)
}
@ -42,7 +42,7 @@ func TestGetMsgAuctionBidFromTx_NoBid(t *testing.T) {
txBuilder := encCfg.TxConfig.NewTxBuilder()
txBuilder.SetMsgs(&banktypes.MsgSend{})
msg, err := auction.GetMsgAuctionBidFromTx(txBuilder.GetTx())
msg, err := mev.GetMsgAuctionBidFromTx(txBuilder.GetTx())
require.NoError(t, err)
require.Nil(t, msg)
}

View File

@ -7,7 +7,7 @@ import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/block"
)
const (
@ -36,10 +36,10 @@ const (
// snd \ \ \ /
type Terminator struct{}
var _ blockbuster.Lane = (*Terminator)(nil)
var _ block.Lane = (*Terminator)(nil)
// PrepareLane is a no-op
func (t Terminator) PrepareLane(_ sdk.Context, proposal blockbuster.BlockProposal, _ int64, _ blockbuster.PrepareLanesHandler) (blockbuster.BlockProposal, error) {
func (t Terminator) PrepareLane(_ sdk.Context, proposal block.BlockProposal, _ int64, _ block.PrepareLanesHandler) (block.BlockProposal, error) {
return proposal, nil
}
@ -49,7 +49,7 @@ func (t Terminator) CheckOrder(sdk.Context, []sdk.Tx) error {
}
// ProcessLane is a no-op
func (t Terminator) ProcessLane(ctx sdk.Context, _ []sdk.Tx, _ blockbuster.ProcessLanesHandler) (sdk.Context, error) {
func (t Terminator) ProcessLane(ctx sdk.Context, _ []sdk.Tx, _ block.ProcessLanesHandler) (sdk.Context, error) {
return ctx, nil
}
@ -72,7 +72,7 @@ func (t Terminator) Name() string {
func (t Terminator) SetAnteHandler(sdk.AnteHandler) {}
// SetIgnoreList is a no-op
func (t Terminator) SetIgnoreList([]blockbuster.Lane) {}
func (t Terminator) SetIgnoreList([]block.Lane) {}
// Match is a no-op
func (t Terminator) Match(sdk.Context, sdk.Tx) bool {

View File

@ -3,20 +3,20 @@ package app
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/blockbuster/utils"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/utils"
builderante "github.com/skip-mev/pob/x/builder/ante"
builderkeeper "github.com/skip-mev/pob/x/builder/keeper"
)
type POBHandlerOptions struct {
BaseOptions ante.HandlerOptions
Mempool blockbuster.Mempool
TOBLane builderante.TOBLane
Mempool block.Mempool
MEVLane builderante.MEVLane
TxDecoder sdk.TxDecoder
TxEncoder sdk.TxEncoder
BuilderKeeper builderkeeper.Keeper
FreeLane blockbuster.Lane
FreeLane block.Lane
}
// NewPOBAnteHandler wraps all of the default Cosmos SDK AnteDecorators with the POB AnteHandler.
@ -54,7 +54,7 @@ func NewPOBAnteHandler(options POBHandlerOptions) sdk.AnteHandler {
ante.NewSigGasConsumeDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SigGasConsumer),
ante.NewSigVerificationDecorator(options.BaseOptions.AccountKeeper, options.BaseOptions.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.BaseOptions.AccountKeeper),
builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.TOBLane, options.Mempool),
builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.MEVLane, options.Mempool),
}
return sdk.ChainAnteDecorators(anteDecorators...)

View File

@ -61,11 +61,12 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"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"
"github.com/skip-mev/pob/abci"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
defaultlane "github.com/skip-mev/pob/lanes/base"
"github.com/skip-mev/pob/lanes/free"
"github.com/skip-mev/pob/lanes/mev"
buildermodule "github.com/skip-mev/pob/x/builder"
builderkeeper "github.com/skip-mev/pob/x/builder/keeper"
)
@ -139,7 +140,7 @@ type TestApp struct {
FeeGrantKeeper feegrantkeeper.Keeper
// custom checkTx handler
checkTxHandler auction.CheckTx
checkTxHandler mev.CheckTx
}
func init() {
@ -261,21 +262,21 @@ func New(
//
// 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.
tobConfig := blockbuster.LaneConfig{
// MEV lane allows transactions to bid for inclusion at the top of the next block.
mevConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(), // This means the lane has no limit on block space.
MaxTxs: 0, // This means the lane has no limit on the number of transactions it can store.
}
tobLane := auction.NewTOBLane(
tobConfig,
auction.NewDefaultAuctionFactory(app.txConfig.TxDecoder()),
mevLane := mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(app.txConfig.TxDecoder()),
)
// Free lane allows transactions to be included in the next block for free.
freeConfig := blockbuster.LaneConfig{
freeConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
@ -284,27 +285,27 @@ func New(
}
freeLane := free.NewFreeLane(
freeConfig,
blockbuster.DefaultTxPriority(),
base.DefaultTxPriority(),
free.DefaultMatchHandler(),
)
// Default lane accepts all other transactions.
defaultConfig := blockbuster.LaneConfig{
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
defaultLane := base.NewDefaultLane(defaultConfig)
defaultLane := defaultlane.NewDefaultLane(defaultConfig)
// Set the lanes into the mempool.
lanes := []blockbuster.Lane{
tobLane,
lanes := []block.Lane{
mevLane,
freeLane,
defaultLane,
}
mempool := blockbuster.NewMempool(app.Logger(), true, lanes...)
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.App.SetMempool(mempool)
// Create a global ante handler that will be called on each transaction when
@ -322,7 +323,7 @@ func New(
TxDecoder: app.txConfig.TxDecoder(),
TxEncoder: app.txConfig.TxEncoder(),
FreeLane: freeLane,
TOBLane: tobLane,
MEVLane: mevLane,
Mempool: mempool,
}
anteHandler := NewPOBAnteHandler(options)
@ -343,10 +344,10 @@ func New(
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
// Set the custom CheckTx handler on BaseApp.
checkTxHandler := auction.NewCheckTxHandler(
checkTxHandler := mev.NewCheckTxHandler(
app.App,
app.txConfig.TxDecoder(),
tobLane,
mevLane,
anteHandler,
)
app.SetCheckTx(checkTxHandler.CheckTx())
@ -392,7 +393,7 @@ func (app *TestApp) CheckTx(req *cometabci.RequestCheckTx) (*cometabci.ResponseC
}
// SetCheckTx sets the checkTxHandler for the app.
func (app *TestApp) SetCheckTx(handler auction.CheckTx) {
func (app *TestApp) SetCheckTx(handler mev.CheckTx) {
app.checkTxHandler = handler
}

View File

@ -1003,7 +1003,7 @@ func (s *POBIntegrationTestSuite) TestLanes() {
params := QueryBuilderParams(s.T(), s.chain)
s.Run("block with tob, free, and normal tx", func() {
s.Run("block with mev, free, and normal tx", func() {
user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom)
// create free-tx, bid-tx, and normal-tx\
@ -1071,7 +1071,7 @@ func (s *POBIntegrationTestSuite) TestLanes() {
require.Equal(s.T(), user2BalanceBefore, user2BalanceAfter+delegation.Amount.Int64())
})
s.Run("failing top of block transaction, free, and normal tx", func() {
s.Run("failing MEV transaction, free, and normal tx", func() {
user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom)
user1Balance := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user1.FormattedAddress(), s.denom)
// create free-tx, bid-tx, and normal-tx\
@ -1151,7 +1151,7 @@ func (s *POBIntegrationTestSuite) TestLanes() {
require.Equal(s.T(), user2BalanceBefore, user2BalanceAfter+delegation.Amount.Int64())
})
s.Run("top of block transaction that includes transactions from the free lane", func() {
s.Run("MEV transaction that includes transactions from the free lane", func() {
user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom)
delegateTx := Tx{
@ -1212,7 +1212,7 @@ func (s *POBIntegrationTestSuite) TestLanes() {
VerifyBlock(s.T(), block, 0, TxHash(txs[0]), bundledTx)
})
s.Run("top of block transaction that includes transaction from free lane + other free lane txs + normal txs", func() {
s.Run("MEV transaction that includes transaction from free lane + other free lane txs + normal txs", func() {
user2BalanceBefore := QueryAccountBalance(s.T(), s.chain.(*cosmos.CosmosChain), s.user2.FormattedAddress(), s.denom)
// create free-txs signed by user2 / 3

View File

@ -14,9 +14,9 @@ import (
var _ sdk.AnteDecorator = BuilderDecorator{}
type (
// TOBLane is an interface that defines the methods required to interact with the top of block
// MEVLane is an interface that defines the methods required to interact with the MEV
// lane.
TOBLane interface {
MEVLane interface {
GetAuctionBidInfo(tx sdk.Tx) (*types.BidInfo, error)
GetTopAuctionTx(ctx context.Context) sdk.Tx
}
@ -30,12 +30,12 @@ type (
BuilderDecorator struct {
builderKeeper keeper.Keeper
txEncoder sdk.TxEncoder
lane TOBLane
lane MEVLane
mempool Mempool
}
)
func NewBuilderDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane TOBLane, mempool Mempool) BuilderDecorator {
func NewBuilderDecorator(ak keeper.Keeper, txEncoder sdk.TxEncoder, lane MEVLane, mempool Mempool) BuilderDecorator {
return BuilderDecorator{
builderKeeper: ak,
txEncoder: txEncoder,

View File

@ -11,9 +11,10 @@ import (
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/golang/mock/gomock"
"github.com/skip-mev/pob/blockbuster"
"github.com/skip-mev/pob/blockbuster/lanes/auction"
"github.com/skip-mev/pob/blockbuster/lanes/base"
"github.com/skip-mev/pob/block"
"github.com/skip-mev/pob/block/base"
defaultlane "github.com/skip-mev/pob/lanes/base"
"github.com/skip-mev/pob/lanes/mev"
testutils "github.com/skip-mev/pob/testutils"
"github.com/skip-mev/pob/x/builder/ante"
"github.com/skip-mev/pob/x/builder/keeper"
@ -39,10 +40,10 @@ type AnteTestSuite struct {
authorityAccount sdk.AccAddress
// mempool and lane set up
mempool blockbuster.Mempool
tobLane *auction.TOBLane
baseLane *base.DefaultLane
lanes []blockbuster.Lane
mempool block.Mempool
mevLane *mev.MEVLane
baseLane *defaultlane.DefaultLane
lanes []block.Lane
// Account set up
balance sdk.Coin
@ -83,32 +84,32 @@ func (suite *AnteTestSuite) SetupTest() {
// Lanes configuration
//
// TOB lane set up
tobConfig := blockbuster.LaneConfig{
mevConfig := base.LaneConfig{
Logger: suite.ctx.Logger(),
TxEncoder: suite.encodingConfig.TxConfig.TxEncoder(),
TxDecoder: suite.encodingConfig.TxConfig.TxDecoder(),
AnteHandler: suite.anteHandler,
MaxBlockSpace: math.LegacyZeroDec(),
}
suite.tobLane = auction.NewTOBLane(
tobConfig,
auction.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()),
suite.mevLane = mev.NewMEVLane(
mevConfig,
mev.NewDefaultAuctionFactory(suite.encodingConfig.TxConfig.TxDecoder()),
)
// Base lane set up
baseConfig := blockbuster.LaneConfig{
baseConfig := base.LaneConfig{
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},
IgnoreList: []block.Lane{suite.mevLane},
}
suite.baseLane = base.NewDefaultLane(baseConfig)
suite.baseLane = defaultlane.NewDefaultLane(baseConfig)
// Mempool set up
suite.lanes = []blockbuster.Lane{suite.tobLane, suite.baseLane}
suite.mempool = blockbuster.NewMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
suite.lanes = []block.Lane{suite.mevLane, suite.baseLane}
suite.mempool = block.NewLanedMempool(log.NewTestLogger(suite.T()), true, suite.lanes...)
}
func (suite *AnteTestSuite) anteHandler(ctx sdk.Context, tx sdk.Tx, _ bool) (sdk.Context, error) {
@ -180,7 +181,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
false,
},
{
"valid auction bid tx",
"valid mev bid tx",
func() {
balance = sdk.NewCoin("stake", math.NewInt(10000))
bid = sdk.NewCoin("stake", math.NewInt(1000))
@ -189,14 +190,14 @@ func (suite *AnteTestSuite) TestAnteHandler() {
true,
},
{
"invalid auction bid tx with no timeout",
"invalid mev bid tx with no timeout",
func() {
timeout = 0
},
false,
},
{
"auction tx is the top bidding tx",
"mev tx is the top bidding tx",
func() {
timeout = 1000
balance = sdk.NewCoin("stake", math.NewInt(10000))
@ -211,7 +212,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
true,
},
{
"invalid frontrunning auction bid tx",
"invalid frontrunning mev bid tx",
func() {
randomAccount := testutils.RandomAccounts(suite.random, 2)
bidder := randomAccount[0]
@ -223,7 +224,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
false,
},
{
"valid frontrunning auction bid tx",
"valid frontrunning mev bid tx",
func() {
randomAccount := testutils.RandomAccounts(suite.random, 2)
bidder := randomAccount[0]
@ -235,7 +236,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
true,
},
{
"invalid sandwiching auction bid tx",
"invalid sandwiching mev bid tx",
func() {
randomAccount := testutils.RandomAccounts(suite.random, 2)
bidder := randomAccount[0]
@ -247,7 +248,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
false,
},
{
"invalid auction bid tx with many signers",
"invalid mev bid tx with many signers",
func() {
signers = testutils.RandomAccounts(suite.random, 10)
frontRunningProtection = true
@ -263,7 +264,7 @@ func (suite *AnteTestSuite) TestAnteHandler() {
suite.ctx = suite.ctx.WithBlockHeight(1)
// Set the auction params
// Set the mev params
err := suite.builderKeeper.SetParams(suite.ctx, buildertypes.Params{
MaxBundleSize: maxBundleSize,
ReserveFee: reserveFee,
@ -278,24 +279,24 @@ func (suite *AnteTestSuite) TestAnteHandler() {
suite.Require().NoError(err)
distribution := suite.mempool.GetTxDistribution()
suite.Require().Equal(0, distribution[auction.LaneName])
suite.Require().Equal(0, distribution[base.LaneName])
suite.Require().Equal(0, distribution[mev.LaneName])
suite.Require().Equal(0, distribution[defaultlane.LaneName])
suite.Require().NoError(suite.mempool.Insert(suite.ctx, topAuctionTx))
distribution = suite.mempool.GetTxDistribution()
suite.Require().Equal(1, distribution[auction.LaneName])
suite.Require().Equal(0, distribution[base.LaneName])
suite.Require().Equal(1, distribution[mev.LaneName])
suite.Require().Equal(0, distribution[defaultlane.LaneName])
}
// Create the actual auction tx and insert into the mempool
auctionTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, timeout, signers)
// Create the actual mev tx and insert into the mempool
mevTx, err := testutils.CreateAuctionTxWithSigners(suite.encodingConfig.TxConfig, bidder, bid, 0, timeout, signers)
suite.Require().NoError(err)
// Execute the ante handler
suite.balance = balance
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.tobLane, suite.mempool)
_, err = suite.anteHandler(suite.ctx, auctionTx, false)
suite.builderDecorator = ante.NewBuilderDecorator(suite.builderKeeper, suite.encodingConfig.TxConfig.TxEncoder(), suite.mevLane, suite.mempool)
_, err = suite.anteHandler(suite.ctx, mevTx, false)
if tc.pass {
suite.Require().NoError(err)
} else {