diff --git a/README.md b/README.md index 7865804..92c62bb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Protocol-Owned Builder

+

Block SDK 🧱

@@ -9,337 +9,21 @@ [![License: Apache-2.0](https://img.shields.io/github/license/skip-mev/pob.svg?style=flat-square)](https://github.com/skip-mev/pob/blob/main/LICENSE) [![Lines Of Code](https://img.shields.io/tokei/lines/github/skip-mev/pob?style=flat-square)](https://github.com/skip-mev/pob) -Skip Protocol's Protocol-Owned Builder (POB) is a set of Cosmos SDK and ABCI++ -primitives that provide application developers the ability to define how their -apps construct and validate blocks on-chain in a transparent, enforceable way, -such as giving complete control to the protocol to recapture, control, and -redistribute MEV. +### πŸ€”Β What is the Block SDK? -Skip's POB provides developers with a set of a few core primitives: - -* `BlockBuster`: BlockBuster is a generalized block-building and mempool SDK - that allows developers to define how their applications construct and validate blocks - on-chain in a transparent, enforceable way. At its core, BlockBuster is an app-side mempool + set - of proposal handlers (`PrepareProposal`/`ProcessProposal`) that allow developers to configure - modular lanes of transactions in their blocks with distinct validation/ordering logic. For more - information, see the [BlockBuster README](/blockbuster/README.md). -* `x/builder`: This Cosmos SDK module gives applications the ability to process - MEV bundled transactions in addition to having the ability to define how searchers - and block proposers are rewarded. In addition, the module defines a `AuctionDecorator`, - which is an AnteHandler decorator that enforces various chain configurable MEV - rules. - -## Releases - -### Release Compatibility Matrix - -| POB Version | Cosmos SDK | -| :---------: | :--------: | -| v1.x.x | v0.47.x | -| v1.x.x | v0.48.x | -| v1.x.x | v0.49.x | -| v1.x.x | v0.50.x | - -## Install - -```shell -$ go install github.com/skip-mev/pob -``` - -## Setup - ->This set up guide will walk you through the process of setting up a POB application. In particular, we will configure an application with the following features: -> ->* Top of block lane (auction lane). This will create an auction lane where users can bid to have their -> transactions executed at the top of the block. ->* Free lane. This will create a free lane where users can submit transactions that will be executed -> for free (no fees). ->* Default lane. This will create a default lane where users can submit transactions that will be executed -> with the default app logic. ->* Builder module that pairs with the auction lane to process auction transactions and distribute revenue -> to the auction house. -> -> To build your own custom BlockBuster Lane, please see the [BlockBuster README](/blockbuster/README.md). - -1. Import the necessary dependencies into your application. This includes the - blockbuster proposal handlers +mempool, keeper, builder types, and builder module. This - tutorial will go into more detail into each of the dependencies. - - ```go - import ( - ... - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/abci" - "github.com/skip-mev/pob/blockbuster/lanes/auction" - "github.com/skip-mev/pob/blockbuster/lanes/base" - "github.com/skip-mev/pob/blockbuster/lanes/free" - buildermodule "github.com/skip-mev/pob/x/builder" - builderkeeper "github.com/skip-mev/pob/x/builder/keeper" - ... - ) - ``` - -2. Add your module to the the `AppModuleBasic` manager. This manager is in - charge of setting up basic, non-dependent module elements such as codec - registration and genesis verification. This will register the special - `MsgAuctionBid` message. When users want to bid for top of block execution, - they will submit a transaction - which we call an auction transaction - that - includes a single `MsgAuctionBid`. We prevent any other messages from being - included in auction transaction to prevent malicious behavior - such as front - running or sandwiching. - - ```go - var ( - ModuleBasics = module.NewBasicManager( - ... - buildermodule.AppModuleBasic{}, - ) - ... - ) - ``` - -3. The builder `Keeper` is POB's gateway to processing special `MsgAuctionBid` - messages that allow users to participate in the top of block auction, distribute - revenue to the auction house, and ensure the validity of auction transactions. - - a. First add the keeper to the app's struct definition. We also want to add POB's custom - checkTx handler to the app's struct definition. This will allow us to override the - default checkTx handler to process bid transactions before they are inserted into the mempool. - NOTE: The custom handler is required as otherwise the auction can be held hostage by a malicious - users. - - ```go - type App struct { - ... - // BuilderKeeper is the keeper that handles processing auction transactions - BuilderKeeper builderkeeper.Keeper - - // Custom checkTx handler - checkTxHandler abci.CheckTx - } - ``` - - b. Add the builder module to the list of module account permissions. This will - instantiate the builder module account on genesis. - - ```go - maccPerms = map[string][]string{ - builder.ModuleName: nil, - ... - } - ``` - - c. Instantiate the blockbuster mempool with the application's desired lanes. - - ```go - // Set the blockbuster mempool into the app. - // Create the lanes. - // - // NOTE: The lanes are ordered by priority. The first lane is the highest priority - // lane and the last lane is the lowest priority lane. - // Top of block lane allows transactions to bid for inclusion at the top of the next block. - // - // blockbuster.BaseLaneConfig is utilized for basic encoding/decoding of transactions. - tobConfig := blockbuster.BaseLaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - // the desired portion of total block space to be reserved for the lane. a value of 0 - // indicates that the lane can use all available block space. - MaxBlockSpace: sdk.ZeroDec(), - } - tobLane := auction.NewTOBLane( - tobConfig, - // the maximum number of transactions that the mempool can store. a value of 0 indicates - // that the mempool can store an unlimited number of transactions. - 0, - // AuctionFactory is responsible for determining what is an auction bid transaction and - // how to extract the bid information from the transaction. There is a default implementation - // that can be used or application developers can implement their own. - auction.NewDefaultAuctionFactory(app.txConfig.TxDecoder()), - ) - - // Free lane allows transactions to be included in the next block for free. - freeConfig := blockbuster.BaseLaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: sdk.ZeroDec(), - // IgnoreList is a list of lanes that if a transaction should be included in, it will be - // ignored by the lane. For example, if a transaction should belong to the tob lane, it - // will be ignored by the free lane. - IgnoreList: []blockbuster.Lane{ - tobLane, - }, - } - freeLane := free.NewFreeLane( - freeConfig, - free.NewDefaultFreeFactory(app.txConfig.TxDecoder()), - ) - - // Default lane accepts all other transactions. - defaultConfig := blockbuster.BaseLaneConfig{ - Logger: app.Logger(), - TxEncoder: app.txConfig.TxEncoder(), - TxDecoder: app.txConfig.TxDecoder(), - MaxBlockSpace: sdk.ZeroDec(), - IgnoreList: []blockbuster.Lane{ - tobLane, - freeLane, - }, - } - defaultLane := base.NewDefaultLane(defaultConfig) - - // Set the lanes into the mempool. - lanes := []blockbuster.Lane{ - tobLane, - freeLane, - defaultLane, - } - mempool := blockbuster.NewMempool(lanes...) - app.App.SetMempool(mempool) - ``` - - d. Instantiate the antehandler chain for the application with awareness of the - blockbuster mempool. This will allow the application to verify the validity - of a transaction respecting the desired logic of a given lane. In this walkthrough, - we want the `FeeDecorator` to be ignored for all transactions that should belong to the - free lane. Additionally, we want to add the `x/builder` module's `AuctionDecorator` to the - ante-handler chain. The `AuctionDecorator` is an AnteHandler decorator that enforces various - chain configurable MEV rules. - - ```go - import ( - ... - "github.com/skip-mev/pob/blockbuster" - "github.com/skip-mev/pob/blockbuster/utils" - builderante "github.com/skip-mev/pob/x/builder/ante" - ... - ) - - anteDecorators := []sdk.AnteDecorator{ - ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - ... - // The IgnoreDecorator allows for certain decorators to be ignored for certain transactions. In - // this case, we want to ignore the FeeDecorator for all transactions that should belong to the - // free lane. - utils.NewIgnoreDecorator( - ante.NewDeductFeeDecorator( - options.BaseOptions.AccountKeeper, - options.BaseOptions.BankKeeper, - options.BaseOptions.FeegrantKeeper, - options.BaseOptions.TxFeeChecker, - ), - options.FreeLane, - ), - ... - builderante.NewBuilderDecorator(options.BuilderKeeper, options.TxEncoder, options.TOBLane, options.Mempool), - } - - anteHandler := sdk.ChainAnteDecorators(anteDecorators...) - app.SetAnteHandler(anteHandler) - - // Set the antehandlers on the lanes. - for _, lane := range lanes { - lane.SetAnteHandler(anteHandler) - } - app.App.SetAnteHandler(anteHandler) - ``` - - e. Instantiate the builder keeper, store keys, and module manager. Note, be - sure to do this after all the required keeper dependencies have been instantiated. - - ```go - keys := storetypes.NewKVStoreKeys( - buildertypes.StoreKey, - ... - ) - - ... - app.BuilderKeeper := builderkeeper.NewKeeper( - appCodec, - keys[buildertypes.StoreKey], - app.AccountKeeper, - app.BankKeeper, - app.DistrKeeper, - app.StakingKeeper, - authtypes.NewModuleAddress(govv1.ModuleName).String(), - ) - - - app.ModuleManager = module.NewManager( - builder.NewAppModule(appCodec, app.BuilderKeeper), - ... - ) - ``` - - e. With Cosmos SDK version 0.47.0, the process of building blocks has been - updated and moved from the consensus layer, CometBFT, to the application layer. - When a new block is requested, the proposer for that height will utilize the - `PrepareProposal` handler to build a block while the `ProcessProposal` handler - will verify the contents of the block proposal by all validators. The - combination of the `BlockBuster` mempool + `PrepareProposal`/`ProcessProposal` - handlers allows the application to verifiably build valid blocks with - top-of-block block space reserved for auctions and partial block for free transactions. - Additionally, we override the `BaseApp`'s `CheckTx` handler with our own custom - `CheckTx` handler that will be responsible for checking the validity of transactions. - We override the `CheckTx` handler so that we can verify auction transactions before they are - inserted into the mempool. With the POB `CheckTx`, we can verify the auction - transaction and all of the bundled transactions before inserting the auction - transaction into the mempool. This is important because we otherwise there may be - discrepencies between the auction transaction and the bundled transactions - are validated in `CheckTx` and `PrepareProposal` such that the auction can be - griefed. All other transactions will be executed with base app's `CheckTx`. - - ```go - - // Create the proposal handler that will be used to build and validate blocks. - proposalHandler := abci.NewProposalHandler( - app.Logger(), - app.txConfig.TxDecoder(), - mempool, - ) - app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) - app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) +**🌐 The Block SDK is a toolkit for building customized blocks**. The Block SDK is a set of Cosmos SDK and ABCI++ primitives that allow chains to fully customize blocks to specific use cases. It turns your chain's blocks into a **`highway`** consisting of individual **`lanes`** with their own special functionality. - // Set the custom CheckTx handler on BaseApp. - checkTxHandler := abci.NewCheckTxHandler( - app.App, - app.txConfig.TxDecoder(), - tobLane, - anteHandler, - app.ChainID(), - ) - app.SetCheckTx(checkTxHandler.CheckTx()) - ... +Skip has built out a number of plug-and-play `lanes` on the SDK that your protocol can use, including in-protocol MEV recapture and Oracles! Additionally, the Block SDK can be extended to add **your own custom `lanes`** to configure your blocks to exactly fit your application needs. - // CheckTx will check the transaction with the provided checkTxHandler. We override the default - // handler so that we can verify bid transactions before they are inserted into the mempool. - // With the POB CheckTx, we can verify the bid transaction and all of the bundled transactions - // before inserting the bid transaction into the mempool. - func (app *TestApp) CheckTx(req cometabci.RequestCheckTx) cometabci.ResponseCheckTx { - return app.checkTxHandler(req) - } +### πŸ“š Block SDK Documentation - // SetCheckTx sets the checkTxHandler for the app. - func (app *TestApp) SetCheckTx(handler abci.CheckTx) { - app.checkTxHandler = handler - } - ``` +To read more about how the Block SDK works, check out the [How it Works](https://docs.skip.money/chains/overview). - f. Finally, update the app's `InitGenesis` order and ante-handler chain. +#### Lane "App Store" - ```go - genesisModuleOrder := []string{ - buildertypes.ModuleName, - ..., - } - ``` +To read more about Skip's pre-built `lanes` and how to use them, check out the [Lane App Store](https://docs.skip.money/chains/lanes/existing-lanes/default). -## Params +#### Lane Development -Note, before building or upgrading the application, make sure to initialize the -escrow address for POB in the parameters of the module. The default parameters -initialize the escrow address to be the module account address. The escrow address -will be the address that is receiving a portion of auction house revenue alongside the proposer (or custom rewards providers). +To read more about how to build your own custom `lanes`, check out the [Build Your Own Lane](https://docs.skip.money/chains/lanes/build-your-own-lane). diff --git a/SPEC.md b/SPEC.md deleted file mode 100644 index d71156c..0000000 --- a/SPEC.md +++ /dev/null @@ -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 ]; -} -``` diff --git a/blockbuster/abci/abci.go b/abci/abci.go similarity index 92% rename from blockbuster/abci/abci.go rename to abci/abci.go index dd8feb6..1e203af 100644 --- a/blockbuster/abci/abci.go +++ b/abci/abci.go @@ -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 } diff --git a/blockbuster/abci/abci_test.go b/abci/abci_test.go similarity index 77% rename from blockbuster/abci/abci_test.go rename to abci/abci_test.go index 0a6fd51..dace5ed 100644 --- a/blockbuster/abci/abci_test.go +++ b/abci/abci_test.go @@ -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()), diff --git a/api/pob/abci/v1/auction.pulsar.go b/api/pob/abci/v1/auction.pulsar.go index fb2e665..eadfe00 100644 --- a/api/pob/abci/v1/auction.pulsar.go +++ b/api/pob/abci/v1/auction.pulsar.go @@ -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 -} \ No newline at end of file +} diff --git a/blockbuster/lane_abci.go b/block/base/abci.go similarity index 79% rename from blockbuster/lane_abci.go rename to block/base/abci.go index a70b102..4eb138a 100644 --- a/blockbuster/lane_abci.go +++ b/block/base/abci.go @@ -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) } diff --git a/block/base/config.go b/block/base/config.go new file mode 100644 index 0000000..e9a404f --- /dev/null +++ b/block/base/config.go @@ -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 +} diff --git a/blockbuster/lane_handlers.go b/block/base/handlers.go similarity index 90% rename from blockbuster/lane_handlers.go rename to block/base/handlers.go index 0a87a5d..3782d36 100644 --- a/blockbuster/lane_handlers.go +++ b/block/base/handlers.go @@ -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 } diff --git a/blockbuster/lane_constructor.go b/block/base/lane.go similarity index 78% rename from blockbuster/lane_constructor.go rename to block/base/lane.go index 7107814..dc58cf4 100644 --- a/blockbuster/lane_constructor.go +++ b/block/base/lane.go @@ -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 } diff --git a/blockbuster/lane_mempool.go b/block/base/mempool.go similarity index 81% rename from blockbuster/lane_mempool.go rename to block/base/mempool.go index e102ee8..22bcf05 100644 --- a/blockbuster/lane_mempool.go +++ b/block/base/mempool.go @@ -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) diff --git a/blockbuster/priority_nonce.go b/block/base/priority_nonce.go similarity index 94% rename from blockbuster/priority_nonce.go rename to block/base/priority_nonce.go index b561d50..1b32019 100644 --- a/blockbuster/priority_nonce.go +++ b/block/base/priority_nonce.go @@ -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" diff --git a/blockbuster/lane_interface.go b/block/lane.go similarity index 99% rename from blockbuster/lane_interface.go rename to block/lane.go index 59ee35e..0731948 100644 --- a/blockbuster/lane_interface.go +++ b/block/lane.go @@ -1,4 +1,4 @@ -package blockbuster +package block import ( "cosmossdk.io/log" diff --git a/blockbuster/mempool.go b/block/mempool.go similarity index 83% rename from blockbuster/mempool.go rename to block/mempool.go index 938d2b6..4029c09 100644 --- a/blockbuster/mempool.go +++ b/block/mempool.go @@ -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) -} diff --git a/blockbuster/mempool_test.go b/block/mempool_test.go similarity index 81% rename from blockbuster/mempool_test.go rename to block/mempool_test.go index 84e470c..242b27a 100644 --- a/blockbuster/mempool_test.go +++ b/block/mempool_test.go @@ -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) }) } diff --git a/blockbuster/proposals.go b/block/proposals.go similarity index 98% rename from blockbuster/proposals.go rename to block/proposals.go index 95da151..f2d77ea 100644 --- a/blockbuster/proposals.go +++ b/block/proposals.go @@ -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. diff --git a/blockbuster/types.go b/block/types.go similarity index 60% rename from blockbuster/types.go rename to block/types.go index d816863..8a1d2ed 100644 --- a/blockbuster/types.go +++ b/block/types.go @@ -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 { diff --git a/blockbuster/utils/ante.go b/block/utils/ante.go similarity index 100% rename from blockbuster/utils/ante.go rename to block/utils/ante.go diff --git a/blockbuster/utils/mocks/lane.go b/block/utils/mocks/lane.go similarity index 80% rename from blockbuster/utils/mocks/lane.go rename to block/utils/mocks/lane.go index 16faa82..3473e56 100644 --- a/blockbuster/utils/mocks/lane.go +++ b/block/utils/mocks/lane.go @@ -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) } diff --git a/blockbuster/utils/mocks/lane_mempool.go b/block/utils/mocks/lane_mempool.go similarity index 100% rename from blockbuster/utils/mocks/lane_mempool.go rename to block/utils/mocks/lane_mempool.go diff --git a/blockbuster/utils/utils.go b/block/utils/utils.go similarity index 100% rename from blockbuster/utils/utils.go rename to block/utils/utils.go diff --git a/blockbuster/utils/utils_test.go b/block/utils/utils_test.go similarity index 96% rename from blockbuster/utils/utils_test.go rename to block/utils/utils_test.go index e0b47e3..eb33706 100644 --- a/blockbuster/utils/utils_test.go +++ b/block/utils/utils_test.go @@ -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) { diff --git a/blockbuster/README.md b/blockbuster/README.md deleted file mode 100644 index ff80dcf..0000000 --- a/blockbuster/README.md +++ /dev/null @@ -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. - diff --git a/go.work.sum b/go.work.sum index 8109fb3..1b6fa7d 100644 --- a/go.work.sum +++ b/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= diff --git a/lanes/base/README.md b/lanes/base/README.md new file mode 100644 index 0000000..4f80073 --- /dev/null +++ b/lanes/base/README.md @@ -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()) + + ... +} +``` diff --git a/blockbuster/lanes/base/abci_test.go b/lanes/base/abci_test.go similarity index 89% rename from blockbuster/lanes/base/abci_test.go rename to lanes/base/abci_test.go index d5c054d..317df3b 100644 --- a/blockbuster/lanes/base/abci_test.go +++ b/lanes/base/abci_test.go @@ -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 { diff --git a/blockbuster/lanes/base/base_test.go b/lanes/base/base_test.go similarity index 100% rename from blockbuster/lanes/base/base_test.go rename to lanes/base/base_test.go diff --git a/blockbuster/lanes/base/lane.go b/lanes/base/lane.go similarity index 57% rename from blockbuster/lanes/base/lane.go rename to lanes/base/lane.go index cb997ae..9842592 100644 --- a/blockbuster/lanes/base/lane.go +++ b/lanes/base/lane.go @@ -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, } } diff --git a/blockbuster/lanes/base/mempool_test.go b/lanes/base/mempool_test.go similarity index 90% rename from blockbuster/lanes/base/mempool_test.go rename to lanes/base/mempool_test.go index 4f12e73..b8a5047 100644 --- a/blockbuster/lanes/base/mempool_test.go +++ b/lanes/base/mempool_test.go @@ -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, diff --git a/lanes/build-your-own/README.md b/lanes/build-your-own/README.md new file mode 100644 index 0000000..f025adf --- /dev/null +++ b/lanes/build-your-own/README.md @@ -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). diff --git a/lanes/free/README.md b/lanes/free/README.md new file mode 100644 index 0000000..4c51d6c --- /dev/null +++ b/lanes/free/README.md @@ -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()) + + ... +} +``` diff --git a/blockbuster/lanes/free/lane.go b/lanes/free/lane.go similarity index 74% rename from blockbuster/lanes/free/lane.go rename to lanes/free/lane.go index 2c4c424..10dcfaa 100644 --- a/blockbuster/lanes/free/lane.go +++ b/lanes/free/lane.go @@ -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) { diff --git a/lanes/mev/README.md b/lanes/mev/README.md new file mode 100644 index 0000000..17258b8 --- /dev/null +++ b/lanes/mev/README.md @@ -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. diff --git a/blockbuster/lanes/auction/abci.go b/lanes/mev/abci.go similarity index 93% rename from blockbuster/lanes/auction/abci.go rename to lanes/mev/abci.go index 0773c82..137da8c 100644 --- a/blockbuster/lanes/auction/abci.go +++ b/lanes/mev/abci.go @@ -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") } diff --git a/blockbuster/lanes/auction/check_tx.go b/lanes/mev/check_tx.go similarity index 94% rename from blockbuster/lanes/auction/check_tx.go rename to lanes/mev/check_tx.go index 089ee99..dbe890b 100644 --- a/blockbuster/lanes/auction/check_tx.go +++ b/lanes/mev/check_tx.go @@ -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") } diff --git a/blockbuster/lanes/auction/factory.go b/lanes/mev/factory.go similarity index 95% rename from blockbuster/lanes/auction/factory.go rename to lanes/mev/factory.go index 6032aa9..d0bca0c 100644 --- a/blockbuster/lanes/auction/factory.go +++ b/lanes/mev/factory.go @@ -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 diff --git a/blockbuster/lanes/auction/factory_test.go b/lanes/mev/factory_test.go similarity index 96% rename from blockbuster/lanes/auction/factory_test.go rename to lanes/mev/factory_test.go index 53c6ec1..b68587e 100644 --- a/blockbuster/lanes/auction/factory_test.go +++ b/lanes/mev/factory_test.go @@ -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 diff --git a/blockbuster/lanes/auction/lane.go b/lanes/mev/lane.go similarity index 51% rename from blockbuster/lanes/auction/lane.go rename to lanes/mev/lane.go index 14d7066..d3da91b 100644 --- a/blockbuster/lanes/auction/lane.go +++ b/lanes/mev/lane.go @@ -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, diff --git a/blockbuster/lanes/auction/mempool.go b/lanes/mev/mempool.go similarity index 73% rename from blockbuster/lanes/auction/mempool.go rename to lanes/mev/mempool.go index 68a3b81..68946df 100644 --- a/blockbuster/lanes/auction/mempool.go +++ b/lanes/mev/mempool.go @@ -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 diff --git a/blockbuster/lanes/auction/auction_test.go b/lanes/mev/mev_test.go similarity index 73% rename from blockbuster/lanes/auction/auction_test.go rename to lanes/mev/mev_test.go index ffe71ae..e1c9e65 100644 --- a/blockbuster/lanes/auction/auction_test.go +++ b/lanes/mev/mev_test.go @@ -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 diff --git a/blockbuster/lanes/auction/utils.go b/lanes/mev/utils.go similarity index 98% rename from blockbuster/lanes/auction/utils.go rename to lanes/mev/utils.go index 6d85330..4406c56 100644 --- a/blockbuster/lanes/auction/utils.go +++ b/lanes/mev/utils.go @@ -1,4 +1,4 @@ -package auction +package mev import ( "errors" diff --git a/blockbuster/lanes/auction/utils_test.go b/lanes/mev/utils_test.go similarity index 79% rename from blockbuster/lanes/auction/utils_test.go rename to lanes/mev/utils_test.go index 2db4b74..7ae1dae 100644 --- a/blockbuster/lanes/auction/utils_test.go +++ b/lanes/mev/utils_test.go @@ -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) } diff --git a/blockbuster/lanes/terminator/lane.go b/lanes/terminator/lane.go similarity index 86% rename from blockbuster/lanes/terminator/lane.go rename to lanes/terminator/lane.go index 35e1453..3fce765 100644 --- a/blockbuster/lanes/terminator/lane.go +++ b/lanes/terminator/lane.go @@ -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 { diff --git a/tests/app/ante.go b/tests/app/ante.go index 7fdbf91..53d0e5c 100644 --- a/tests/app/ante.go +++ b/tests/app/ante.go @@ -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...) diff --git a/tests/app/app.go b/tests/app/app.go index 8ebc49a..8185791 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -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 } diff --git a/tests/integration/pob_suite.go b/tests/integration/pob_suite.go index 4f406c0..dde0672 100644 --- a/tests/integration/pob_suite.go +++ b/tests/integration/pob_suite.go @@ -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 diff --git a/x/builder/ante/ante.go b/x/builder/ante/ante.go index 4c0a496..ae62909 100644 --- a/x/builder/ante/ante.go +++ b/x/builder/ante/ante.go @@ -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, diff --git a/x/builder/ante/ante_test.go b/x/builder/ante/ante_test.go index 5608d0b..6c25a43 100644 --- a/x/builder/ante/ante_test.go +++ b/x/builder/ante/ante_test.go @@ -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 {