From 4072ad8cb8268761910045bd6209dbb69270d43a Mon Sep 17 00:00:00 2001 From: David Terpay Date: Tue, 15 Aug 2023 15:03:50 -0400 Subject: [PATCH] init constructor docs --- block/README.md | 399 ++++-------------------------------- block/constructor/README.md | 292 ++++++++++++++++++++------ 2 files changed, 274 insertions(+), 417 deletions(-) diff --git a/block/README.md b/block/README.md index adee3e7..f80eb41 100644 --- a/block/README.md +++ b/block/README.md @@ -1,355 +1,44 @@ -# 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 MEV lane could be designed to create an -in-protocol mev 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: MEV 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/block/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 := block.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 MEV 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 MEV 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/block/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 MEV lane includes -an custom `TxPriority` that orders transactions in the mempool based on their -bid. - -```go -func TxPriority(config Factory) block.TxPriority[string] { - return block.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: block.NewPriorityMempool( - block.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 MEV 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 MEV 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. - +## 🤔 How does it work + +### Transaction Lifecycle + +The best way to understand how lanes work is to first understand the lifecycle +of a transaction. When a transaction is submitted to the chain, it will be checked +in `CheckTx` by the base application. If the transaction is valid, it will be +inserted into the applications mempool. The transaction then waits in the mempool +until a new block needs to be proposed. When a new block needs to be proposed, +the application will call `PrepareProposal` (which is a new ABCI++ addition) to +request a new block from the current proposer. The proposer will look at what the +transactions currently waiting to be included in a block in their mempool and +will iterative select transactions until the block is full. The proposer will then +send the block to other validators in the network. When a validator receives a +proposed block, the validator will first want to verify the contents of the block +before signing off on it. The validator will call `ProcessProposal` to verify the +contents of the block. If the block is valid, the validator will sign off on the +block and broadcast their vote to the network. If the block is invalid, the validator +will reject the block. Once a block is accepted by the network, it is committed +and the transactions that were included in the block are removed from the mempool. + +### Lane Lifecycle + +The Lane Constructor implements the `Lane` interface. After transactions are +check in `CheckTx`, they will be added to this lane's mempool (data structure +responsible for storing transactions). When a new block is proposed, `PrepareLane` +will be called by the `PrepareProposalHandler` defined in `abci/abci.go`. This +will trigger the lane to reap transactions from its mempool and add them to the +proposal. By default, transactions are added to proposals in the order that they +are reaped from the mempool. Transactions will only be added to a proposal +if they are valid according to the lane's verification logic. The default implementation +determines whether a transaction is valid by running the transaction through the +lane's `AnteHandler`. If any transactions are invalid, they will be removed from +lane's mempool from further consideration. + +When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` +defined in `abci/abci.go` will call `ProcessLane` on each lane. This will trigger +the lane to process all transactions that are included in the proposal. Lane's +should only verify transactions that belong to their lane. The default implementation +of `ProcessLane` will first check that transactions that should belong to the +current lane are ordered correctly in the proposal. If they are not, the proposal +will be rejected. If they are, the lane will run the transactions through its `ProcessLaneHandler` +which is responsible for verifying the transactions against the lane's verification +logic. If any transactions are invalid, the proposal will be rejected. \ No newline at end of file diff --git a/block/constructor/README.md b/block/constructor/README.md index 4653bba..e4238d7 100644 --- a/block/constructor/README.md +++ b/block/constructor/README.md @@ -1,6 +1,6 @@ # 🎨 Lane Constructor -> 🏗️ Build your own lane in less than 30 minutes using the Lane Constructor +> 🏗️ Build your own lane in less than 10 minutes using the Lane Constructor ## 💡 Overview @@ -8,52 +8,7 @@ The Lane Constructor is a generic implementation of a lane. It comes out of the box with default implementations for all the required interfaces. It is meant to be used as a starting point for building your own lane. -## 🤔 How does it work - -### Transaction Lifecycle - -The best way to understand how lanes work is to first understand the lifecycle -of a transaction. When a transaction is submitted to the chain, it will be checked -in `CheckTx` by the base application. If the transaction is valid, it will be -inserted into the applications mempool. The transaction then waits in the mempool -until a new block needs to be proposed. When a new block needs to be proposed, -the application will call `PrepareProposal` (which is a new ABCI++ addition) to -request a new block from the current proposer. The proposer will look at what the -transactions currently waiting to be included in a block in their mempool and -will iterative select transactions until the block is full. The proposer will then -send the block to other validators in the network. When a validator receives a -proposed block, the validator will first want to verify the contents of the block -before signing off on it. The validator will call `ProcessProposal` to verify the -contents of the block. If the block is valid, the validator will sign off on the -block and broadcast their vote to the network. If the block is invalid, the validator -will reject the block. Once a block is accepted by the network, it is committed -and the transactions that were included in the block are removed from the mempool. - -### Lane Lifecycle - -The Lane Constructor implements the `Lane` interface. After transactions are -check in `CheckTx`, they will be added to this lane's mempool (data structure -responsible for storing transactions). When a new block is proposed, `PrepareLane` -will be called by the `PrepareProposalHandler` defined in `abci/abci.go`. This -will trigger the lane to reap transactions from its mempool and add them to the -proposal. By default, transactions are added to proposals in the order that they -are reaped from the mempool. Transactions will only be added to a proposal -if they are valid according to the lane's verification logic. The default implementation -determines whether a transaction is valid by running the transaction through the -lane's `AnteHandler`. If any transactions are invalid, they will be removed from -lane's mempool from further consideration. - -When proposals need to be verified in `ProcessProposal`, the `ProcessProposalHandler` -defined in `abci/abci.go` will call `ProcessLane` on each lane. This will trigger -the lane to process all transactions that are included in the proposal. Lane's -should only verify transactions that belong to their lane. The default implementation -of `ProcessLane` will first check that transactions that should belong to the -current lane are ordered correctly in the proposal. If they are not, the proposal -will be rejected. If they are, the lane will run the transactions through its `ProcessLaneHandler` -which is responsible for verifying the transactions against the lane's verification -logic. If any transactions are invalid, the proposal will be rejected. - -## How to use it +## 🤔 How to use it There are **three** critical components to the Lane Constructor: @@ -68,15 +23,15 @@ be accepted to this lane. is responsible for reaping transactions from its mempool and adding them to a proposal. This allows users to customize the order/how transactions are added to a proposal if any custom block building logic is required. -5. [**OPTIONAL**] Users can optionally define their own `ProcessLaneHandler`, which +5. [**OPTIONAL**] Users can optionally define their own `CheckOrderHandler`, which +is responsible for determining whether transactions that are included in a proposal +and belong to a given lane are ordered correctly in a block proposal. This is useful +for lanes that require a specific ordering of transactions in a proposal. +6. [**OPTIONAL**] Users can optionally define their own `ProcessLaneHandler`, which is responsible for processing transactions that are included in block proposals. In the case where a custom `PrepareLaneHandler` is defined, a custom `ProcessLaneHandler` will likely follow. This will allow a proposal to be verified against the custom block building logic. -6. [**OPTIONAL**] Users can optionally define their own `CheckOrderHandler`, which -is responsible for determining whether transactions that are included in a proposal -and belong to a given lane are ordered correctly in a block proposal. This is useful -for lanes that require a specific ordering of transactions in a proposal. ### 1. Lane Config @@ -143,7 +98,7 @@ 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/constructor/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 define a `TxPriority[C]` struct that does the +utilize the mempool, you must implement a `TxPriority[C]` struct that does the following: - Implements a `GetTxPriority` method that returns the priority (as defined @@ -155,20 +110,29 @@ 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/constructor/mempool.go`. +The default implementation can be found in `block/constructor/mempool.go`. What if +we wanted to prioritize transactions by the amount they have staked on chain? Well +we could do something like the following: ```golang -// DefaultTxPriority returns a default implementation of the TxPriority. It prioritizes -// transactions by their fee. -func DefaultTxPriority() TxPriority[string] { +// 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(goCtx context.Context, tx sdk.Tx) string { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { + 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 feeTx.GetFee().String() + return totalStake.String() }, Compare: func(a, b string) int { aCoins, _ := sdk.ParseCoinsNormalized(a) @@ -202,4 +166,208 @@ func DefaultTxPriority() TxPriority[string] { } ``` -### LaneHandlers +To utilize this mempool in a lane, all you have to then do is pass in the +`TxPriority[C]` to the `NewLaneMempool` function. + +```golang +// Pseudocode for creating the custom tx priority +priorityCfg := NewPriorityConfig( + stakingKeeper, + accountKeeper, + ... +) + + +// define your mempool that orders transactions by on chain stake +mempool := constructor.NewMempool[string]( + priorityCfg.CustomTxPriority(), + cfg.TxEncoder, + cfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := constructor.NewLaneConstructor( + cfg, + LaneName, + mempool, + constructor.DefaultMatchHandler(), +) +``` + +### 3. MatchHandler + +> 🔒 `MatchHandler` Invarients +> +> The handler assumes that the transactions passed into the function are already +> ordered respecting the lane's ordering rules and respecting the ordering rules +> of the mempool relative to the lanes it has. This means that the transactions +> should already be in contiguous order. + +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/constructor/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 + } + + return stakeAmount.GT(h.Threshold) + } +} +``` + +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 := constructor.NewMempool[string]( + priorityCfg.CustomTxPriority(), + cfg.TxEncoder, + cfg.MaxTxs, +) + +// Initialize your lane with the mempool +lane := constructor.NewLaneConstructor( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) +``` + +### Notes on Steps 4-6 + +Although not required, if you implement any single custom handler, whether it's +the `PrepareLaneHandler`, `ProcessLaneHandler`, or `CheckOrderHandler`, you must +implement all of them. This is because the default implementation of the lane +constructor will call all of these handlers. If you do not implement all of them, +the lane may have unintended behavior. + +### 4. [OPTIONAL] PrepareLaneHandler + +> 🔒 `PrepareLaneHandler` Invarients +> +> Transactions should be reaped respecting the priority mechanism of the lane. +> By default this is the TxPriority object used to initialize the lane's mempool. + +The `PrepareLaneHandler` is an optional field you can set on the lane constructor. +This handler is responsible for the transaction selection logic when a new proposal +is requested. The default implementation should fit most use cases and can be found +in `block/constructor/handlers.go`. + +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 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 lane constructor. + +```golang +customLane := constructor.NewLaneConstructor( + cfg, + LaneName, + mempool, + handler.CustomMatchHandler(), +) + +customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler()) +``` + +### 5. [OPTIONAL] CheckOrderHandler + +> 🔒 `CheckOrderHandler` Invarients +> +> The CheckOrderHandler must ensure that transactions included in block proposals +> only include transactions that are in contiguous order respecting the lane's +> ordering rules and respecting the ordering rules of the mempool relative to the +> lanes it has. This means that all transactions that belong to the same lane, must +> be right next to each other in the block proposal. Additionally, the relative priority +> of each transaction belonging to the lane must be respected. + +The `CheckOrderHandler` is an optional field you can set on the lane constructor. + + +### 6. [OPTIONAL] ProcessLaneHandler + +> 🔒 `ProcessLaneHandler` Invarients +> +> The handler assumes that the transactions passed into the function are already +> ordered respecting the lane's ordering rules and respecting the ordering rules +> of the mempool relative to the lanes it has. This means that the transactions +> should already be in contiguous order. + +The `ProcessLaneHandler` is an optional field you can set on the lane constructor. +This handler is responsible for verifying the transactions in the block proposal +that belong to the lane. The default implementation should fit most use cases and +can be found in `block/constructor/handlers.go`. + + +```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. \ No newline at end of file