chore(docs): Updating Build-Your-Own Lane docs (#136)
* moving bid extraction to antehandler * lint * fix * no zero timeout * reset integration tests * updating docs with latest changes * nit
This commit is contained in:
@@ -15,7 +15,13 @@ $ go install github.com/skip-mev/block-sdk
|
|||||||
|
|
||||||
## 🤔 How to use it [30 min]
|
## 🤔 How to use it [30 min]
|
||||||
|
|
||||||
There are **five** required components to building a custom lane using the base lane:
|
The Block SDK is designed to be modular and extensible. This means that
|
||||||
|
developers can build their own lanes and customize the block building/verification
|
||||||
|
logic to fit their needs. This guide will walk through the process of building
|
||||||
|
a custom lane and configuring it in the application. Developers should
|
||||||
|
extend the base lane (`block/base/lane.go`) to build their own lanes.
|
||||||
|
|
||||||
|
There are **five** components to building a custom lane using the base lane:
|
||||||
|
|
||||||
1. `Mempool` - The lane's mempool is responsible for storing transactions that
|
1. `Mempool` - The lane's mempool is responsible for storing transactions that
|
||||||
have been verified and are waiting to be included in proposals.
|
have been verified and are waiting to be included in proposals.
|
||||||
@@ -23,14 +29,10 @@ have been verified and are waiting to be included in proposals.
|
|||||||
should belong to this lane.
|
should belong to this lane.
|
||||||
3. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own
|
3. [**OPTIONAL**] `PrepareLaneHandler` - Allows developers to define their own
|
||||||
handler to customize the how transactions are verified and ordered before they
|
handler to customize the how transactions are verified and ordered before they
|
||||||
are included into a proposal.
|
are included in a proposal.
|
||||||
4. [**OPTIONAL**] `CheckOrderHandler` - Allows developers to define their own
|
4. [**OPTIONAL**] `ProcessLaneHandler` - 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.
|
handler for processing transactions that are included in block proposals.
|
||||||
6. `Configuration` - Configure high-level options for your lane.
|
5. `Configuration` - Configure high-level options for your lane.
|
||||||
|
|
||||||
### 1. 🗄️ Mempool
|
### 1. 🗄️ Mempool
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ does the following:
|
|||||||
* Implements a `MinValue` method that returns the minimum priority value
|
* Implements a `MinValue` method that returns the minimum priority value
|
||||||
that a transaction can have.
|
that a transaction can have.
|
||||||
|
|
||||||
The default implementation can be found in `block/base/mempool.go`.
|
The default implementation can be found in `block/base/mempool.go` - see `DefaultTxPriority`.
|
||||||
|
|
||||||
> Scenario
|
> Scenario
|
||||||
What if we wanted to prioritize transactions by the amount they have staked on
|
What if we wanted to prioritize transactions by the amount they have staked on
|
||||||
@@ -116,14 +118,15 @@ 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.
|
is pass in the `TxPriority[C]` to the `NewMempool` function.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
// Create the lane config
|
// Create the lane config (explained in more detail below)
|
||||||
laneCfg := NewLaneConfig(
|
laneCfg := NewLaneConfig(
|
||||||
...
|
...
|
||||||
MaxTxs: 100,
|
MaxTxs: 100,
|
||||||
...
|
...
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pseudocode for creating the custom tx priority
|
// Pseudocode for creating the custom tx priority. This is what
|
||||||
|
// returns your custom tx priority.
|
||||||
priorityCfg := NewPriorityConfig(
|
priorityCfg := NewPriorityConfig(
|
||||||
stakingKeeper,
|
stakingKeeper,
|
||||||
accountKeeper,
|
accountKeeper,
|
||||||
@@ -212,8 +215,8 @@ mempool := base.NewMempool[string](
|
|||||||
lane := base.NewBaseLane(
|
lane := base.NewBaseLane(
|
||||||
cfg,
|
cfg,
|
||||||
LaneName,
|
LaneName,
|
||||||
mempool,
|
mempool, // Custom mempool
|
||||||
handler.CustomMatchHandler(),
|
handler.CustomMatchHandler(), // Custom match handler
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -241,13 +244,33 @@ The handler should return the following for a given lane:
|
|||||||
2. The transactions to be removed from the lane's mempool.
|
2. The transactions to be removed from the lane's mempool.
|
||||||
3. An error if the lane is unable to prepare a block proposal.
|
3. An error if the lane is unable to prepare a block proposal.
|
||||||
|
|
||||||
|
When collecting transactions to include in the block proposal, the handler
|
||||||
|
must respect the lane limits - i.e. the maximum number of bytes and units
|
||||||
|
of gas that the lane can use in the block proposal. This is defined in the
|
||||||
|
`limit proposals.LaneLimits` struct.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
// PrepareLaneHandler is responsible for preparing transactions to be included
|
type (
|
||||||
// in the block from a given lane. Given a lane, this function should return
|
// PrepareLaneHandler is responsible for preparing transactions to be
|
||||||
// the transactions to include in the block, the transactions that must be
|
// included in the block from a given lane. Given a lane, this function
|
||||||
// removed from the lane, and an error if one occurred.
|
// should return the transactions to include in the block, the transactions
|
||||||
PrepareLaneHandler func(ctx sdk.Context,proposal BlockProposal,maxTxBytes int64)
|
// that must be removed from the lane, and an error if one occurred.
|
||||||
(txsToInclude [][]byte, txsToRemove []sdk.Tx, err error)
|
PrepareLaneHandler func(
|
||||||
|
ctx sdk.Context,
|
||||||
|
proposal proposals.Proposal,
|
||||||
|
limit proposals.LaneLimits,
|
||||||
|
) (txsToInclude, txsToRemove []sdk.Tx, err error)
|
||||||
|
|
||||||
|
// LaneLimits defines the constraints for a partial proposal. Each lane must
|
||||||
|
// only propose transactions that satisfy these constraints. Otherwise the
|
||||||
|
// partial proposal update will be rejected.
|
||||||
|
LaneLimits struct {
|
||||||
|
// MaxTxBytes is the maximum number of bytes allowed in the partial proposal.
|
||||||
|
MaxTxBytes int64
|
||||||
|
// MaxGasLimit is the maximum gas limit allowed in the partial proposal.
|
||||||
|
MaxGasLimit uint64
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
The default implementation is simple. It will continue to select transactions
|
The default implementation is simple. It will continue to select transactions
|
||||||
@@ -255,7 +278,7 @@ from its mempool under the following criteria:
|
|||||||
|
|
||||||
1. The transactions is not already included in the block proposal.
|
1. The transactions is not already included in the block proposal.
|
||||||
2. The transaction is valid and passes the AnteHandler check.
|
2. The transaction is valid and passes the AnteHandler check.
|
||||||
3. The transaction is not too large to be included in the block.
|
3. The transaction is not too large/gas intensive to be included in the block.
|
||||||
|
|
||||||
If a more involved selection process is required, you can implement your own
|
If a more involved selection process is required, you can implement your own
|
||||||
`PrepareLaneHandler` and and set it after creating the base lane.
|
`PrepareLaneHandler` and and set it after creating the base lane.
|
||||||
@@ -274,78 +297,36 @@ customLane := NewCustomLane(
|
|||||||
customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler())
|
customLane.SetPrepareLaneHandler(customlane.PrepareLaneHandler())
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. ✅ CheckOrderHandler
|
See `lanes/mev/abci.go` for an example of how to set up a custom `PrepareLaneHandler`.
|
||||||
|
|
||||||
The `CheckOrderHandler` is an optional field you can set on the base lane.
|
### 4. 🆗 ProcessLaneHandler
|
||||||
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.
|
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
|
This handler is responsible for verifying the transactions in the block proposal
|
||||||
that belong to the lane. This handler is executed after the `CheckOrderHandler`
|
that belong to the lane.
|
||||||
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
|
```golang
|
||||||
// ProcessLaneHandler is responsible for processing transactions that are
|
// ProcessLaneHandler is responsible for processing transactions that are
|
||||||
// included in a block and belong to a given lane. ProcessLaneHandler is
|
// included in a block and belong to a given lane. This handler must return an
|
||||||
// executed after CheckOrderHandler so the transactions passed into this
|
// error if the transactions are not correctly ordered, do not belong to this
|
||||||
// function SHOULD already be in order respecting the ordering rules of the
|
// lane, or any other relevant error.
|
||||||
// lane and respecting the ordering rules of mempool relative to the lanes it has.
|
ProcessLaneHandler func(ctx sdk.Context, partialProposal []sdk.Tx) error
|
||||||
ProcessLaneHandler func(ctx sdk.Context, txs []sdk.Tx) ([]sdk.Tx, error)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Given the invariants above, the default implementation is simple. It will
|
The `partialProposal` is a slice of transactions that belong to the lane and
|
||||||
|
are included in the block proposal. The handler should return an error if the
|
||||||
|
transactions are not correctly ordered, do not belong to this lane, or any
|
||||||
|
other relevant error.
|
||||||
|
|
||||||
|
Given the description above, the default implementation is simple. It will
|
||||||
continue to verify transactions in the block proposal under the following criteria:
|
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
|
1. All of the transactions included in `partialProposal` must belong to this
|
||||||
valid, return an error.
|
lane i.e. they must match the lane's `MatchHandler`.
|
||||||
2. If a transaction does not match to this lane, return the remaining
|
2. All of the transactions must be ordered respecting the ordering rules of the
|
||||||
transactions to the next lane to process.
|
mempool i.e. the transactions must be ordered by their priority which is defined
|
||||||
|
by the `TxPriority[C]` struct.
|
||||||
|
3. All of the transactions must be valid and pass the AnteHandler check.
|
||||||
|
|
||||||
Similar to the setup of handlers above, if a more involved verification 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
|
is required, you can implement your own `ProcessLaneHandler` and and set it
|
||||||
@@ -365,18 +346,9 @@ customLane := NewCustomLane(
|
|||||||
customLane.SetProcessLaneHandler(customlane.ProcessLaneHandler())
|
customLane.SetProcessLaneHandler(customlane.ProcessLaneHandler())
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. 📝 Lane Configuration
|
See `lanes/mev/abci.go` for an example of how to set up a custom `ProcessLaneHandler`.
|
||||||
|
|
||||||
Once you have created your custom lane, you can configure it in the application
|
### 5. 📝 Lane Configuration
|
||||||
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 lane config (`LaneConfig`) is a simple configuration object that defines
|
||||||
the desired amount of block space the lane should utilize when building a
|
the desired amount of block space the lane should utilize when building a
|
||||||
@@ -406,7 +378,7 @@ 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
|
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`
|
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
|
to be ignored if it qualifies for a given lane, they can do so by using the
|
||||||
`NewIgnoreDecorator` defined in `block/utils/ante.go`.
|
`NewIgnoreDecorator` defined in `block/ante.go`.
|
||||||
|
|
||||||
For example, a free lane might want to ignore the `DeductFeeDecorator` so that
|
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
|
its transactions are not charged any fees. Where ever the `AnteHandler` is
|
||||||
@@ -436,7 +408,7 @@ Anytime a transaction that qualifies for the free lane is being processed, the
|
|||||||
|
|
||||||
This sets the maximum number of transactions allowed in the mempool with the semantics:
|
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, 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,
|
* if `MaxTxs` > 0, the mempool will cap the number of transactions it stores,
|
||||||
and will prioritize transactions by their priority and sender-nonce
|
and will prioritize transactions by their priority and sender-nonce
|
||||||
(sequence number) when evicting transactions.
|
(sequence number) when evicting transactions.
|
||||||
@@ -445,7 +417,7 @@ and will prioritize transactions by their priority and sender-nonce
|
|||||||
#### **MaxBlockSpace**
|
#### **MaxBlockSpace**
|
||||||
|
|
||||||
MaxBlockSpace is the maximum amount of block space that the lane will attempt
|
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
|
to fill when building a proposal. This parameter may be useful for lanes that
|
||||||
should be limited (such as a free or onboarding lane) in space usage.
|
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
|
Setting this to 0 will allow the lane to fill the block with as many
|
||||||
transactions as possible.
|
transactions as possible.
|
||||||
@@ -461,3 +433,17 @@ 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 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
|
to the free lane will be processed by the default lane (which accepts all
|
||||||
transactions by default).
|
transactions by default).
|
||||||
|
|
||||||
|
|
||||||
|
### Set up
|
||||||
|
|
||||||
|
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!
|
||||||
|
|||||||
Reference in New Issue
Block a user