Merge pull request #1 from skip-mev/terpay/rename-to-block-sdk
feat(Block SDK): We live
This commit is contained in:
commit
c1ce98a6e5
336
README.md
336
README.md
@ -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 @@
|
||||
[](https://github.com/skip-mev/pob/blob/main/LICENSE)
|
||||
[](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
311
SPEC.md
@ -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 ];
|
||||
}
|
||||
```
|
||||
@ -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
|
||||
}
|
||||
@ -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()),
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
79
block/base/config.go
Normal 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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
@ -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"
|
||||
@ -1,4 +1,4 @@
|
||||
package blockbuster
|
||||
package block
|
||||
|
||||
import (
|
||||
"cosmossdk.io/log"
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
@ -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.
|
||||
@ -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 {
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
@ -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 isn’t 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 lane’s
|
||||
`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 lane’s `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 lane’s mempool. Developers can define their own custom ordering
|
||||
by implementing a custom `TxPriority` struct that allows the lane’s 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. Developer’s 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 lane’s `VerifyTx` function. This logic can be
|
||||
completely arbitrary. For example, the default lane verifies transactions
|
||||
using base app’s `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 lane’s 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 user’s extend the functionality of the `Base` lane when first
|
||||
exploring the code base.
|
||||
|
||||
200
go.work.sum
200
go.work.sum
@ -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
111
lanes/base/README.md
Normal 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())
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
@ -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 {
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
463
lanes/build-your-own/README.md
Normal file
463
lanes/build-your-own/README.md
Normal 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
129
lanes/free/README.md
Normal 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())
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
@ -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
258
lanes/mev/README.md
Normal 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.
|
||||
@ -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")
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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,
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,4 +1,4 @@
|
||||
package auction
|
||||
package mev
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -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)
|
||||
}
|
||||
@ -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 {
|
||||
@ -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...)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user