From c76f1a9cd4e8ff77713d3964e7180597904204d3 Mon Sep 17 00:00:00 2001 From: Tyler <48813565+technicallyty@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:29:42 -0700 Subject: [PATCH] docs (#575) --- docs/0-integrate-the-sdk.md | 208 ++++++++++++ docs/1-overview.md | 77 +++++ docs/2-how-it-works.md | 58 ++++ docs/3-searcher-docs.md | 206 ++++++++++++ docs/lanes/1-build-your-own-lane.md | 478 ++++++++++++++++++++++++++++ docs/lanes/existing-lanes/1-mev.md | 300 +++++++++++++++++ docs/lanes/existing-lanes/2-free.md | 161 ++++++++++ tests/e2e/go.mod | 46 +-- tests/e2e/go.sum | 24 ++ 9 files changed, 1535 insertions(+), 23 deletions(-) create mode 100644 docs/0-integrate-the-sdk.md create mode 100644 docs/1-overview.md create mode 100644 docs/2-how-it-works.md create mode 100644 docs/3-searcher-docs.md create mode 100644 docs/lanes/1-build-your-own-lane.md create mode 100644 docs/lanes/existing-lanes/1-mev.md create mode 100644 docs/lanes/existing-lanes/2-free.md diff --git a/docs/0-integrate-the-sdk.md b/docs/0-integrate-the-sdk.md new file mode 100644 index 00000000..c8ed8930 --- /dev/null +++ b/docs/0-integrate-the-sdk.md @@ -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`. + + + +# 🏗️ 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). diff --git a/docs/1-overview.md b/docs/1-overview.md new file mode 100644 index 00000000..4d40e686 --- /dev/null +++ b/docs/1-overview.md @@ -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` | diff --git a/docs/2-how-it-works.md b/docs/2-how-it-works.md new file mode 100644 index 00000000..46062f30 --- /dev/null +++ b/docs/2-how-it-works.md @@ -0,0 +1,58 @@ +# How it Works + + + +### 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. diff --git a/docs/3-searcher-docs.md b/docs/3-searcher-docs.md new file mode 100644 index 00000000..a6b6edfe --- /dev/null +++ b/docs/3-searcher-docs.md @@ -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 + +User’s can bootstrap their searching bots by utilizing Skip’s 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 didn’t 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` | diff --git a/docs/lanes/1-build-your-own-lane.md b/docs/lanes/1-build-your-own-lane.md new file mode 100644 index 00000000..2e23fa2f --- /dev/null +++ b/docs/lanes/1-build-your-own-lane.md @@ -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). diff --git a/docs/lanes/existing-lanes/1-mev.md b/docs/lanes/existing-lanes/1-mev.md new file mode 100644 index 00000000..53899b25 --- /dev/null +++ b/docs/lanes/existing-lanes/1-mev.md @@ -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` diff --git a/docs/lanes/existing-lanes/2-free.md b/docs/lanes/existing-lanes/2-free.md new file mode 100644 index 00000000..8fb5b632 --- /dev/null +++ b/docs/lanes/existing-lanes/2-free.md @@ -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()) + + ... +} +``` diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index 2e910db6..76308e24 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -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 diff --git a/tests/e2e/go.sum b/tests/e2e/go.sum index 5f78aef3..532a7f74 100644 --- a/tests/e2e/go.sum +++ b/tests/e2e/go.sum @@ -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=