This commit is contained in:
Tyler 2024-08-09 13:29:42 -07:00 committed by GitHub
parent f1cde2acec
commit c76f1a9cd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1535 additions and 23 deletions

208
docs/0-integrate-the-sdk.md Normal file
View File

@ -0,0 +1,208 @@
# Integrate the Block SDK
The Block SDK is **open-source software** licensed under MIT. It is free to use, and has existing plug-and-play Lanes that work immediately!
Visit the GitHub repo [here](https://github.com/skip-mev/block-sdk).
We strive to be responsive to questions and issues within 1-2 weeks - please open a GitHub issue or join us on [**discord**](skip.build/discord). Note, we are not currently providing hands-on support for new integrations.
## ⚙️ Architecture [15 mins]
This is a high-level overview of the architecture, please reference [this page](2-how-it-works.md) or the [`Block-SDK` repo](https://github.com/skip-mev/block-sdk) for detailed and up to date info. For those eager to code, feel free to skip this and start down the page at **Set Up**!
### How Were Blocks Constructed pre-Block-SDK?
There are 3 relevant stages of consensus (these are all ABCI++ methods)
- **PrepareProposal**
- In this step, the consensus-engine (CometBFT, etc.) gives the application all of the transactions it has seen thus far.
- The app looks over these, performs some app-specific logic, and then gives them back to the consensus-engine. The consensus-engine then creates and broadcasts a proposal containing the transactions sent back from the app.
- **ProcessProposal**
- In this step, all validators check that the transactions in the proposal are valid, and that the proposal (as a whole) satisfies validity conditions determined by the application
- If the proposal fails, validators will not vote on the block, and the network will be forced to another round of consensus
- if the proposal passes, valdiators vote on the block, and the block will become canonical (barring unforeseen events)
### **Application Mempools**
In `v0.47.0` of the cosmos-sdk, **app-side mempools** were added to the SDK. With app-side mempools, validators no longer need to rely on the consensus-engine to keep track of and order all available transactions. Now applications can define their own mempool implementations, that
1. Store all pending (not finalized in a block) transactions
2. Order the set of pending transactions
#### **How does block-building change?**
Now in **PrepareProposal** instead of getting transactions from the consensus-engine, validators can pull transactions from their application-state aware mempools, and prioritize those transactions instead of the consensus-engine's transactions.
**Why is this better?**
- Mempools that are not app-state aware will not have the ability to make state-aware ordering rules. Like
1. All staker transactions are placed at the top of the block
2. All IBC `LightClientUpdate` messages are placed at the top of the block
3. Anything you can think of!!
- The consensus engine's mempool is generally in-efficient.
- The consensus-engine's mempool does not know when to remove transactions from its own mempool
- The consensus-engine spends most of its time re-broadcasting transactions between peers, hogging network bandwidth
## Block-SDK!!
The `Block-SDK` defines its own custom implementation of an **app-side mempool**, a `LaneMempool`. The `LaneMempool` is composed of `Lanes`, and handles transaction ingress, ordering, and cleaning.
**transaction ingress**
- The `LanedMempool` constructor defines an ordering of lanes. When a transaction is received by the app, it iterates through all lanes in order and inserts the transaction into the first `Lane` that it belongs in.
**ordering**
- Each `Lane` of the `LanedMempool` maintains its own ordering of transactions. When the `LanedMempool` routes a transaction to its corresponding `Lane` the `Lane` then inserts the transaction at its designated position with respect to all other transactions in the lane
### PrepareProposal
When the application is instructed to `PrepareProposal` it iterates through its `Lane`s in order, and calls each `Lane`'s `PrepareLane` method. The `Lane.PrepareLane` method collects transactions from a `Lane` and appends those transactions to the set of transactions from previous `Lane`'s `PrepareLane` calls. In other words, each block-proposal is now a collection of the transactions from the `LanedMempool`'s constituent lanes.
### ProcessProposal
When the application receives a proposal, and calls `ProcessProposal`, the app delegates the validation to the `LaneMempool.ProcessLanes` method. Remember, the proposal is composed of transactions from the sub-lanes of the `LaneMempool`, as such, the `LaneMempool` can route each `Lane`'s contribution to the Proposal to that `Lane` for validation. The proposal passes iff all `Lane`'s contributions are valid.
#### ⚠️ NOTE ⚠️
A block constructed from a `LaneMempool`'s `PrepareLanes` method must always pass that `LaneMempool`'s `ProcessLanes` method, otherwise, the chain will fail to produce blocks!! These functions are consensus critical, so practice caution when implementing them!!
## 📖 Set Up [20 mins]
To get set up, we're going to implement the `Default Lane`, which is the **most general and least restrictive** that accepts all transactions. This will cause **no changes** to your chain functionality, but will prepare you to add `lanes` with more functionality afterwards!
The default lane mirrors how CometBFT creates proposals today.
- It does a basic check to ensure that the transaction is valid.
- Orders the transactions based on tx fee amount (highest to lowest).
- The `PrepareLane` handler will reap transactions from the lane up to the `MaxBlockSpace` limit
- The `ProcessLane` handler will ensure that the transactions are ordered based on their fee amount and pass the same checks done in `PrepareLane`.
<!-- TODO: create script -->
# 🏗️ 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`.
### Release Compatibility Matrix
| Block SDK Version | Cosmos SDK |
| :---------------: | :--------: |
| `v1.x.x` | `v0.47.x` |
| `v2.x.x` | `v0.50.x` |
## 📥 Adding the Block SDK to Your Project
```bash
$ go get github.com/skip-mev/block-sdk
```
## 📚 Usage
1. First determine the set of lanes that you want to use in your application. This guide only sets up the `default lane`
```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"
)
// 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.
func NewApp() {
...
defaultConfig := base.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
}
defaultLane := defaultlane.NewDefaultLane(defaultConfig)
// TODO(you): Add more Lanes!!!
```
2. In your base application, you will need to create a `LanedMempool` composed
of the `lanes` you want to use.
```golang
// 2. Set up the relative priority of lanes
lanes := []block.Lane{
defaultLane,
}
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.App.SetMempool(mempool)
```
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.**
```golang
// 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. 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`.
```golang
// 4. Set the abci handlers on base app
// Create the LanedMempool's ProposalHandler
proposalHandler := abci.NewProposalHandler(
app.Logger(),
app.TxConfig().TxDecoder(),
mempool,
)
// set the Prepare / ProcessProposal Handlers on the app to be the `LanedMempool`'s
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
```
### 💅 Next step: implement other `lanes`
See the [Mev Lane](lanes/existing-lanes/1-mev.md) and select the `lanes` you want, or [Build Your Own](lanes/1-build-your-own-lane.md).

77
docs/1-overview.md Normal file
View File

@ -0,0 +1,77 @@
# Overview
### 🤔What is the Block SDK?
**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.
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.
🚦 **Blocks are like highways**
Let's say you're the designer of a 4 lane highway. You'd want a paid lane, for fast drivers who'd like to be separated from other lanes. You'd like a lane for large vehicles, you can configure this lane to be wider, require more space between vehicles, etc. The other two lanes are for the rest of traffic. The beauty here, is that as the owner of the highway, you get to decide what vehicles (transactions) you'll allow, and how they can behave (ordering)!!
#### If you've been here before
##### [Integrate Block-SDK](0-integrate-the-sdk.md)
##### [Building your own Lane](lanes/1-build-your-own-lane.md)
##### [Searcher docs for MEV Lane](3-searcher-docs.md)
### ❌ Problems: Blocks are not Customizable
Most Cosmos chains today utilize traditional block construction - which is too limited.
- Traditional block building is susceptible to MEV-related issues, such as front-running and sandwich attacks, since proposers have monopolistic rights on ordering and no verification of good behavior. MEV that is created cannot be redistributed to the protocol.
- Traditional block building uses a one-size-fits-all approach, which can result in inefficient transaction processing for specific applications or use cases and sub-optimal fee markets.
- Transactions tailored for specific applications may need custom prioritization, ordering or validation rules that the mempool is otherwise unaware of because transactions within a block are currently in-differentiable when a blockchain might want them to be.
### ✅ Solution: The Block SDK
You can think of the Block SDK 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.
In the Block SDK, 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.
### ✨ Block SDK Use Cases
A block 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](lanes/existing-lanes/1-mev.md) 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 facilitate _good_ 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
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.
### 🎆 Chains Currently Using the Block-SDK
#### Mainnets
| Chain Name | Chain-ID | Block-SDK Version |
| ----------- | --------------- | ----------------- |
| Juno | `juno-1` | `v1.0.2` |
| Persistence | `persistence-1` | `v1.0.2` |
| Initia | `NA` | `v1.0.2` |
| Prism | `NA` | `v1.0.2` |
| Terra | `phoenix-1` | `v1.0.2` |
#### Testnets
| Chain Name | Chain-ID | Block-SDK Version |
| ---------- | -------- | ----------------- |
| Juno | `uni-6` | `v1.0.2` |

58
docs/2-how-it-works.md Normal file
View File

@ -0,0 +1,58 @@
# How it Works
<!-- TODO: add images to this -->
### Summary
With the Block SDK, blocks are broken up into smaller partial blocks called `lanes`.
- Each `lane` has its own custom block building logic and stores distinct types of transactions.
- Each lane can only consume a portion of the block as defined on the `lane`'s configuration (`MaxBlockSpace`).
- When a block proposal is requested, a block will **fill** with transactions from each `lane`, iteratively, in the order in which the `lanes` are defined in the application.
- When a block proposal is processed, each `lane` will **verify** its portion of the block, iteratively, in the order in which the `lanes` are defined in the application.
- **Transactions in blocks MUST respect the ordering of lanes.**
### 🔁 Background: Transaction Lifecycle
Knowledge of the general transaction lifecycle is important to understand how `lanes` work.
- A transaction begins when it is signed and broadcasted to a node on a chain.
- It will be then be verified by the application on the node.
- If it is valid, it will be inserted into the node's `mempool`, which is a storage area for transactions before inclusion in a block.
- If the node happens to be a `validator`, and is proposing a block, the application will call `PrepareProposal` to create a new block proposal.
- The proposer will look at what transactions they have in their mempool, iteratively select transactions until the block is full, and share the proposal with other validators.
- When a different validator receives a proposal, the validator will verify its contents via `ProcessProposal` before signing it.
- If the proposal is valid, the validator will sign the proposal and broadcast their vote to the network.
- If the block is invalid, the validator will reject the proposal.
- Once a proposal is accepted by the network, it is committed as a block and the transactions that were included are removed from every validator's mempool.
### 🛣️ Lane Lifecycle
`Lanes` introduce new steps in the transaction lifecycle outlined above.
A `LanedMempool` is composed of several distinct `lanes` that store their own transactions. The `LanedMempool` will insert the transaction into all `lanes` that accept it
- After the base application accepts a transaction, the transaction will be checked to see if it can go into any `lanes`, as defined by the lane's `MatchHandler`.
- `Lane`'s can be configured to only accept transactions that match a certain criteria. For example, a `lane` could be configured to only accept transactions that are staking related (such as a free-transaction lane).
- When a new block is proposed, the `PrepareProposalHandler` of the application will iteratively call `PrepareLane` on each `lane` (in the order in which they are defined in the application). The `PrepareLane` method is similar to `PrepareProposal`.
- Calling `PrepareLane` on a `lane` will trigger the lane to reap transactions from its mempool and add them to the proposal (if they respect the verification rules of the `lane`).
- When proposals are verified in `ProcessProposal` by other validators, the `ProcessProposalHandler` defined in `abci/abci.go` will call `ProcessLane` on each `lane` in the same order as they were called in the `PrepareProposalHandler`.
- Each subsequent call to `ProcessLane` will filter out transactions that belong to previous lanes. **A given lane's ProcessLane will only verify transactions that belong to that lane.**
**Scenario**
Let's say we have a `LanedMempool` composed of two lanes: `LaneA` and `LaneB`.
`LaneA` is defined first in the `LanedMempool` and `LaneB` is defined second.
`LaneA` contains transactions Tx1 and Tx2 and `LaneB` contains transactions
Tx3 and Tx4.
When a new block needs to be proposed, the `PrepareProposalHandler` will call `PrepareLane` on `LaneA` first and `LaneB` second.
When `PrepareLane` is called on `LaneA`, `LaneA` will reap transactions from its mempool and add them to the proposal. The same applies for `LaneB`. Say `LaneA` reaps transactions Tx1 and Tx2 and `LaneB` reaps transactions Tx3 and Tx4. This gives us a proposal composed of the following:
- `Tx1`, `Tx2`, `Tx3`, `Tx4`
When the `ProcessProposalHandler` is called, it will call `ProcessLane` on `LaneA` with the proposal composed of Tx1, Tx2, Tx3, and Tx4. `LaneA` will then verify Tx1 and Tx2 and return the remaining transactions - Tx3 and Tx4. The `ProcessProposalHandler` will then call `ProcessLane` on `LaneB` with the remaining transactions - Tx3 and Tx4. `LaneB` will then verify Tx3 and Tx4 and return no remaining transactions.

206
docs/3-searcher-docs.md Normal file
View File

@ -0,0 +1,206 @@
# Searchers
Searcher Simluation
Bundles must only pass basic `CheckTx` validation (e.g. nonce, account balance, etc.) in order to be accepted by the auction. This means that bundles that are submitted to the auction may not be entirely valid on-chain since `runMsgs` is not executed. Searchers are encouraged to simulate their bundles before submitting them to the auction.
### ➡️ How do searchers submit bundles to chains that use the Block SDK?
Definitions
💡 `AuctionTx` (auction bid transaction) = `sdk.Tx` that includes a single `MsgAuctionBid`
Searchers submit bundles by broadcasting a `AuctionTx` in the same way they broadcast any other transaction. A few important things to note:
- When a `MsgAuctionBid` message is included in a transaction, no other `sdk.Msg` can be present.
- Interfacing with the auction _may be different across_ `Block SDK` chains. Bidding may involve interacting with a dedicated `AuctionHouse` smart contract instead of including this special message type. In the future, we will link a chain directory here to check on which interface you need, when there are different implementations.
#### Default Auction Bid Message
```go
// MsgAuctionBid defines a request type for sending bids to the x/auction
// module.
type MsgAuctionBid struct {
// bidder is the address of the account that is submitting a bid to the
// auction.
Bidder string `protobuf:"bytes,1,opt,name=bidder,proto3" json:"bidder,omitempty"`
// bid is the amount of coins that the bidder is bidding to participate in the
// auction.
Bid types.Coin `protobuf:"bytes,3,opt,name=bid,proto3" json:"bid"`
// transactions are the bytes of the transactions that the bidder wants to
// bundle together.
Transactions [][]byte `protobuf:"bytes,4,rep,name=transactions,proto3" json:"transactions,omitempty"`
}
```
There are three things searchers must specify to participate in an auction:
1. The **`Transactions`** they want executed at the top of block.
- Transactions will be executed in the order they are specified in the message.
- Each transactions included must be the raw bytes of the transaction.
2. The **`Bidder`** who is bidding for top of block execution.
- This must be the same account that signs the transaction.
3. The **`Bid`** they want to send alongside the bundle.
- This should be in the denom configured by the auction parameters (see below)
4. The **`Timeout`** height i.e. until what height the bid is valid for.
- This must be added to the transaction when it is being constructed i.e. `txBuilder.SetTimeoutHeight(timeoutHeight)`
#### Nonce Checking
In general, all bundles must respect nonce ordering of accounts. If a bundle is submitted with an invalid nonce, it will be rejected.
The execution of the bundle will always look like the following:
- Auction Transaction (which extracts the bid)
- All transactions in the bundle (in order)
For example, assume the following:
1. Searcher has account `A` with nonce `n`
2. Searcher wants to submit a bundle with 3 transactions from account `A`
The searcher must first sign the `AuctionTx` with nonce `n + 1`. Then, the searcher must sign the first transaction in the bundle with nonce `n + 2`, the second transaction with nonce `n + 3`, and the third transaction with nonce `n + 4`.
#### Skipper Bot
Users can bootstrap their searching bots by utilizing Skips own open source [Skipper Bot](https://github.com/skip-mev/skipper).
#### Creating an `AuctionTx`
```go
// createBidTx creates an auction bid tx with the given parameters
func createBidTx(
privateKey *secp256k1.PrivKey,
bidder string,
bid sdk.Coin,
bundle [][]byte,
height uint64,
) (sdk.Tx, error) {
// bid transaction can only include a single MsgAuctionBid
msgs := []sdk.Msg{
&buildertypes.MsgAuctionBid{
Bidder: bidder,
Bid: bid,
Transactions: bundle,
},
}
// Retrieve the expected account and sequence number
sequenceNum, accountNum := getAccountInfo(privateKey)
txConfig := authtx.NewTxConfig(
codec.NewProtoCodec(codectypes.NewInterfaceRegistry()),
authTx.DefaultSignModes,
)
txBuilder := txConfig.NewTxBuilder()
// Set the messages along with other fee information
txBuilder.SetMsgs(msgs...)
...
txBuilder.SetGasLimit(5000000)
// ******************************************************
// SET A TIMEOUT HEIGHT FOR HOW LONG THE BID IS VALID FOR
// ******************************************************
txBuilder.SetTimeoutHeight(height)
// Sign the auction transaction using nonce n + 1,
// and ensure that all subsequent transactions by
// the searcher are signed with incrementing values of auction transaction.
signerData := auth.SignerData{
ChainID: CHAIN_ID,
AccountNumber: accountNumber,
Sequence: sequenceNum,
}
sigV2, err = clientTx.SignWithPrivKey(
txConfig.SignModeHandler().DefaultMode(),
signerData,
txBuilder,
privateKey,
txConfig,
sequenceNum,
)
if err != nil {
return nil, err
}
if err = txBuilder.SetSignatures(sigV2); err != nil {
return nil, error
}
return txBuilder.GetTx(), nil
}
```
### ⚙️ Auction fees
Auction Configuration
All auction parameters are accessible though the `/block-sdk/x/auction/v1/params` HTTP path on a standard node or gRPC service defined by `x/auction`.
In order to participate in an auction, searchers must pay a fee. This fee is paid in the native token of the chain. The fee is determined by the auction parameters, which are set by the chain. The auction parameters are:
1. **`MaxBundleSize`**: specifies the maximum number of transactions that can be included in a bundle (bundle = an ordered list of transactions). Bundles must be ≤ this number.
2. **`ReserveFee`**: specifies the bid floor to participate in the auction. Bids that are lower than the reserve fee are ignored.
3. **`MinBidIncrement`**: specifies how much greater each subsequent bid must be (as seen by an individual node) in order to be considered. If the bid is lower than the `highest current bid + min bid increment`, the bid is ignored.
4. **`FrontRunningProtection`**: determines whether front-running and sandwich protection is enabled.
Front-running and sandwich protection
**If this is set to true, your bundle must follow these guidelines:**
- You must put your signed transactions **after** transactions you didnt sign
- You can only have **at most two** unique signers in the bundle
Bundle Examples:
1. **Valid**: [tx1, tx2, tx3] where tx1 is signed by the signer 1 and tx2 and tx3 are signed by the bidder.
2. **Valid**: [tx1, tx2, tx3, tx4] where tx1 - tx4 are signed by the bidder.
3. **Invalid**: [tx1, tx2, tx3] where tx1 and tx3 are signed by the bidder and tx2 is signed by some other signer. (possible sandwich attack)
4. **Invalid**: [tx1, tx2, tx3] where tx1 is signed by the bidder, and tx2, tx3 are signed by some other signer. (possible front-running attack)
#### Querying auction parameters
```go
func getAuctionParams() (*auctiontypes.Params, error) {
# Replace this URL with the gRPC url of a node
url := "localhost:9090"
# Establish a gRPC connection to query auction parameters
grpcConn, err := grpc.Dial(
url,
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
return nil, err
}
client := auctiontypes.NewQueryClient(grpcConn)
req := &auctiontypes.QueryParamsRequest{}
resp, err := client.Params(context.Background(), req)
return resp.Params
}
```
### 🚨 Chains currently using the MEV-Lane
#### Mainnets
| Chain Name | Chain-ID | Block-SDK Version |
| ----------- | --------------- | ----------------- |
| Osmosis | `osmosis-1` | `v1.4.2` |
| Neutron | `neutron-1` | `v1.4.0` |
| Juno | `juno-1` | `v1.0.4` |
| Persistence | `persistence-1` | `v1.0.5` |
| Pryzm | `NA` | `v1.0.2` |
#### Testnets
| Chain Name | Chain-ID | Block-SDK Version |
| ---------- | -------------- | ----------------- |
| Initia | `initiation-1` | `v2.1.2` |
| Juno | `uni-6` | `v1.0.4` |

View File

@ -0,0 +1,478 @@
# Build Your Own Lane
Before reading over this section, it is highly recommended that developers read over the [**How It Works**](../../2-how-it-works.md) page.
If you have not already, this assumes you have completed the [General Setup](../../0-integrate-the-sdk.md) guide first!
Please reach out to us on [**discord**](skip.build/discord) if you need help!
## 💡 Overview
The **Base Lane** 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.
With it, developers can build their own lane(s) in less than 10 minutes!
# 🏗️ 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() base.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 := base.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).

View File

@ -0,0 +1,300 @@
# Mev Lane
The `MEV Lane` allows top-of-block MEV auctions in-protocol, with revenue being redistributed to chains.
If you have not already, we recommend following the [General Setup](../../0-integrate-the-sdk.md) guide first!
Please reach out to us on [**discord**](skip.build/discord) if you need help!
### 💰 Overview
Blockspace is valuable, and MEV bots find arbitrage opportunities to capture value. The `MEV Lane` provides a fair auction for these by leveraging the `x/auction` module. The `MEVLane` ensures that proposals are constructed in accordance with the `x/auction`'s view of all bids, and the `x/auction` is responsible for ordering bids + keeping track of bids + selecting auction winners.
The Block SDK uses the app-side `LanedMempool`, `PrepareLane` / `ProcessLane`, and `CheckTx` to create an MEV marketplace inside the protocol. It introduces a new message type, called a `MsgAuctionBid`, that allows the submitter to execute multiple transactions at the **top of the block atomically** atomically.
NOTE:
**atomicity** here refers to **transaction inclusion** and not **transaction execution**. The `x/auction` module does not make any guarantees for atomic execution, some transactions in a bundle may fail!!
This means that searchers can find opportunities in the mempool, backrun them, and submit them at the top of the block. This covers most MEV recapture via arbitrage, liquidations, backrunning, oracle-updates, etc. It can be configured to **not allow** for sandwich attacks or harmful MEV.
### 📖 Set Up [10 mins]
**At a high level, to integrate the MEV Lane, chains must:**
1. Be using Cosmos SDK version or higher `v0.47.0`.
2. Import and configure the `MEV Lane` (alongside any other desired lanes) into their base app.
3. Import and configure the Block SDK mempool into their base app.
4. Import and configure the Block SDK `Prepare` / `Process` proposal handlers into their base app.
5. Import and instantiate the `x/auction` module into their base app.
# 🏗️ 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`.
The `MEVLane` requires that apps use the `LanedMempool` as their app-side mempool, see [here](../../0-integrate-the-sdk.md)
### Release Compatibility Matrix
| Block SDK Version | Cosmos SDK |
| :---------------: | :--------: |
| `v1.x.x` | `v0.47.x` |
| `v2.x.x` | `v0.50.x` |
## 📥 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 default lane.
2. You will need to instantiate the `x/auction` module into your application. This
module is responsible for processing auction transactions and distributing revenue
to the auction house. The `x/auction` module is also responsible for ensuring the
validity of auction transactions, and maintaining the set of bids / revenue distribution.
The `MEVLane` is responsible for constructing blocks with transactions from the winning bid.
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, auction types, and auction
module. This tutorial will go into more detail into each of the dependencies.
```go
import (
...
"github.com/skip-mev/block-sdk/abci"
"github.com/skip-mev/block-sdk/lanes/mev"
"github.com/skip-mev/block-sdk/lanes/base"
auctionmodule "github.com/skip-mev/block-sdk/x/auction"
auctionkeeper "github.com/skip-mev/block-sdk/x/auction/keeper"
auctiontypes "github.com/skip-mev/block-sdk/x/auction/types"
auctionante "github.com/skip-mev/block-sdk/x/auction/ante"
...
)
```
2. Add the `x/auction` 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(
...
auctionmodule.AppModuleBasic{},
)
...
)
```
3. The auction `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 {
...
// auctionKeeper is the keeper that handles processing auction transactions
AuctionKeeper auctionkeeper.Keeper
// Custom checkTx handler
checkTxHandler mev.CheckTx
}
```
b. Add the auction module to the list of module account permissions. This will
instantiate the auction module account on genesis.
```go
maccPerms = map[string][]string{
auction.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 relative priority of lanes
lanes := []block.Lane{
mevLane,
defaultLane,
}
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.App.SetMempool(mempool)
```
d. Add the `x/auction` 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(),
...
auctionante.NewauctionDecorator(
options.auctionKeeper,
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 auction 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(
auctiontypes.StoreKey,
...
)
...
app.auctionKeeper := auctionkeeper.NewKeeper(
appCodec,
keys[auctiontypes.StoreKey],
app.AccountKeeper,
app.BankKeeper,
app.DistrKeeper,
app.StakingKeeper,
authtypes.NewModuleAddress(govv1.ModuleName).String(),
)
app.ModuleManager = module.NewManager(
auction.NewAppModule(appCodec, app.auctionKeeper),
...
)
```
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(),
mempool,
)
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{
auctiontypes.ModuleName,
...,
}
```
## Params
- **MaxBundleSize** - What is the maximal number of transactions a bundle can have?
- **EscrowAccountAddress** - Where does the chain collect bid revenue? Notice, this is an `sdk.AccAdress` and is configured on genesis, or via `ParamChange` proposals.
- **ReserveFee** - The minimum possible bid. Notice, no bids less than the `ReserveFee` will be accepted.
- **MinBidIncrement** - This is the minimum difference from the max bid that `x/auction` module will accept. I.e if the current max bid is `12ujuno`, and `MinBidIncrement = 1`, then all new bids must be greater than `13ujuno` to be considered.
- **FrontRunningProtection** - This determines whether front-running bundles will be accepted by the `x/auction` module
- **ProposerFee** - This is a fractional value, i.e `0 <= ProposerFee <= 1`, this determines how much of the winning bid from the previous block goes to the proposer of that block, the rest will be sent to the `EscrowAccountAddress`

View File

@ -0,0 +1,161 @@
# Free Lane
The `Free Lane` allows certain transactions to be included in a block without paying fees. This lane can be used to encourage certain behaviors on the chain, such as staking, governance, or others.
If you have not already, this assumes you have completed the [General Setup](../../0-integrate-the-sdk.md) guide first!
Please reach out to us on [**discord**](skip.build/discord) if you need help!
### 📖 Overview
The free lane closely follows the block building logic of the default lane, with exception for the following:
- Transactions can only be included in the free lane if they are considered free (as defined by the lane's `MatchHandler`). The default implementation matches transactions to the free lane iff the transaction is staking related (e.g. stake, re-delegate, etc.).
- By default, the ordering of transactions in the free lane is based on the transaction's fee amount (highest to lowest). However, this can be overridden to support ordering mechanisms that are not based on fee amount (e.g. ordering based on the user's on-chain stake amount).
The free lane implements the same `ABCI++` interface as the other lanes, and does the same verification logic as the [default lane](../../0-integrate-the-sdk.md). The free lane's `PrepareLane` handler will reap transactions from the lane up to the `MaxBlockSpace` limit, and the `ProcessLane` handler will ensure that the transactions are ordered based on their fee amount (by default) and pass the same checks done in `PrepareLane`.
### 📖 Set Up [10 mins]
**At a high level, to integrate the MEV Lane, chains must:**
1. Be using Cosmos SDK version or higher `v0.47.0`.
2. Import and configure the `Free Lane` (alongside any other desired lanes) into their base app.
3. Import and configure the Block SDK mempool into their base app.
4. Import and configure the Block SDK `Prepare` / `Process` proposal handlers into their base app.
# 🏗️ 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`.
### Release Compatibility Matrix
| Block SDK Version | Cosmos SDK |
| :---------------: | :--------: |
| `v1.x.x` | `v0.47.x` |
| `v2.x.x` | `v0.50.x` |
## 📥 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.
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(),
mempool,
)
app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler())
app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler())
...
}
```

View File

@ -15,16 +15,16 @@ require (
github.com/cosmos/cosmos-sdk v0.50.4
github.com/skip-mev/block-sdk/v2 v2.1.0 // reference local
github.com/strangelove-ventures/interchaintest/v8 v8.0.0
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.26.0
golang.org/x/sync v0.6.0
google.golang.org/grpc v1.62.1
golang.org/x/sync v0.7.0
google.golang.org/grpc v1.65.0
)
require (
cloud.google.com/go v0.112.0 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/storage v1.36.0 // indirect
cosmossdk.io/api v0.7.3 // indirect
@ -39,7 +39,7 @@ require (
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect
github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect
@ -58,7 +58,7 @@ require (
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/cockroachdb/errors v1.11.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
@ -96,23 +96,23 @@ require (
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
github.com/emicklei/dot v1.6.1 // indirect
github.com/ethereum/go-ethereum v1.12.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.3 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/glog v1.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
@ -136,7 +136,7 @@ require (
github.com/hashicorp/go-metrics v0.5.2 // indirect
github.com/hashicorp/go-plugin v1.5.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
@ -184,7 +184,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect
github.com/pierrec/xxHash v0.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
@ -225,22 +225,22 @@ require (
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect
golang.org/x/tools v0.22.0 // indirect
google.golang.org/api v0.155.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/protobuf v1.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@ -72,6 +72,7 @@ cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiV
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
@ -224,6 +225,7 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
@ -312,6 +314,7 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
@ -469,6 +472,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@ -512,6 +516,7 @@ github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
@ -550,6 +555,7 @@ github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6x
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -586,6 +592,7 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -727,6 +734,7 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -961,6 +969,7 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA=
@ -1090,6 +1099,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -1103,6 +1113,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
@ -1205,6 +1216,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1246,6 +1258,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1307,6 +1320,7 @@ golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfS
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1334,6 +1348,7 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1350,6 +1365,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1449,11 +1465,13 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1467,6 +1485,7 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1536,6 +1555,7 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1717,8 +1737,10 @@ google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/b
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@ -1762,6 +1784,7 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -1780,6 +1803,7 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=