-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: David Terpay <[email protected]>
- Loading branch information
1 parent
f389452
commit 5416b88
Showing
6 changed files
with
348 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"default": true, | ||
"MD004": { "style": "asterisk" }, | ||
"MD007": { "indent": 4 }, | ||
"MD024": { "siblings_only": true }, | ||
"MD025": false, | ||
"MD033": false, | ||
"MD034": false, | ||
"MD014": false, | ||
"no-hard-tabs": false, | ||
"whitespace": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"default": true, | ||
"MD004": { | ||
"style": "asterisk" | ||
}, | ||
"MD007": { | ||
"indent": 4 | ||
}, | ||
"MD013": false, | ||
"MD024": { | ||
"siblings_only": true | ||
}, | ||
"MD025": false, | ||
"MD033": false, | ||
"MD034": false, | ||
"no-hard-tabs": false, | ||
"whitespace": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
|
||
# POB Specification | ||
|
||
## Abstract | ||
|
||
The `x/builder` module is a Cosmos SDK module that allows Cosmos chains to host | ||
top-of-block auctions directly in-protocol with auction revenue (MEV) being | ||
redistributed according to the preferences of the chain. The `x/builder` module | ||
introduces a new `MsgAuctionBid` message that allows users to submit a bid | ||
alongside an ordered list of transactions, i.e. a **bundle**, that they want | ||
executed at top-of-block before any other transactions are executed for that | ||
block. The `x/builder` module works alongside the `AuctionMempool` such that: | ||
|
||
* Auctions are held directly in the `AuctionMempool`, where a winner is determined | ||
when the proposer proposes a new block in `PrepareProposal`. | ||
* `x/builder` provides the necessary validation of auction bids and subsequent | ||
state transitions to extract bids. | ||
|
||
## Concepts | ||
|
||
### Miner Extractable Value (MEV) | ||
|
||
MEV refers to the potential profit that miners, or validators in a Proof-of-Stake | ||
system, can make by strategically ordering, selecting, or even censoring | ||
transactions in the blocks they produce. MEV can be classified into "good MEV" | ||
and "bad MEV" based on the effects it has on the blockchain ecosystem and its | ||
users. It's important to note that these classifications are subjective and may | ||
vary depending on one's perspective. | ||
|
||
**Good MEV** refers to the value that validators can extract while contributing | ||
positively to the blockchain ecosystem. This typically includes activities that | ||
enhance network efficiency, maintain fairness, and align incentives with the | ||
intended use of the system. Examples of good MEV include: | ||
|
||
* **Back-running**: Validators can place their own transactions immediately | ||
after a profitable transaction, capitalizing on the changes caused by the | ||
preceding transaction. | ||
* **Arbitrage**: By exploiting price differences across decentralized exchanges | ||
or other DeFi platforms, validators help maintain more consistent price levels | ||
across the ecosystem, ultimately contributing to its stability. | ||
* **Liquidations**: In DeFi platforms, when users' collateral falls below a | ||
specific threshold, validators can liquidate these positions, thereby maintaining | ||
the overall health of the platform and protecting its users from insolvency risks. | ||
|
||
**Bad MEV** refers to the value that validators can extract through activities | ||
that harm the blockchain ecosystem, lead to unfair advantages, or exploit users. | ||
Examples of bad MEV include: | ||
|
||
* **Front-running**: Validators can observe pending transactions in the mempool | ||
(the pool of unconfirmed transactions) and insert their own transactions ahead | ||
of them. This can be particularly profitable in decentralized finance (DeFi) | ||
applications, where a validator could front-run a large trade to take advantage | ||
of price movements. | ||
* **Sandwich attacks**: Validators can surround a user's transaction with their | ||
own transactions, effectively manipulating the market price for their benefit. | ||
* **Censorship**: Validators can selectively exclude certain transactions from | ||
blocks to benefit their own transactions or to extract higher fees from users. | ||
|
||
MEV is a topic of concern in the blockchain community because it can lead to | ||
unfair advantages for validators, reduced trust in the system, and a potential | ||
concentration of power. Various approaches have been proposed to mitigate MEV, | ||
such as proposer-builder separation (described below) and transparent and fair | ||
transaction ordering mechanisms at the protocol-level (`POB`) to make MEV | ||
extraction more incentive aligned with the users and blockchain ecosystem. | ||
|
||
### Proposer Builder Separation (PBS) | ||
|
||
Proposer-builder separation is a concept in the design of blockchain protocols, | ||
specifically in the context of transaction ordering within a block. In traditional | ||
blockchain systems, validators perform two main tasks: they create new blocks | ||
(acting as proposers) and determine the ordering of transactions within those | ||
blocks (acting as builders). | ||
|
||
|
||
**Proposers**: They are responsible for creating and broadcasting new blocks, | ||
just like in traditional blockchain systems. *However, they no longer determine | ||
the ordering of transactions within those blocks*. | ||
|
||
**Builders**: They have the exclusive role of determining the order of transactions | ||
within a block - can be full or partial block. Builders submit their proposed | ||
transaction orderings to an auction mechanism, which selects the winning template | ||
based on predefined criteria, e.g. highest bid. | ||
|
||
This dual role can lead to potential issues, such as front-running and other | ||
manipulations that benefit the miners/builders themselves. | ||
|
||
* *Increased complexity*: Introducing PBS adds an extra layer of complexity to | ||
the blockchain protocol. Designing, implementing, and maintaining an auction | ||
mechanism for transaction ordering requires additional resources and may | ||
introduce new vulnerabilities or points of failure in the system. | ||
* *Centralization risks*: With PBS, there's a risk that a few dominant builders | ||
may emerge, leading to centralization of transaction ordering. This centralization | ||
could result in a lack of diversity in transaction ordering algorithms and an | ||
increased potential for collusion or manipulation by the dominant builders. | ||
* *Incentive misalignments*: The bidding process may create perverse incentives | ||
for builders. For example, builders may be incentivized to include only high-fee | ||
transactions to maximize their profits, potentially leading to a neglect of | ||
lower-fee transactions. Additionally, builders may be incentivized to build | ||
blocks that include **bad-MEV** strategies because they are more profitable. | ||
|
||
## Specification | ||
|
||
### Mempool | ||
|
||
As the lifeblood of blockchains, mempools serve as the intermediary space for | ||
pending transactions, playing a vital role in transaction management, fee markets, | ||
and network health. With ABCI++, mempools can be defined at the application layer | ||
instead of the consensus layer (CometBFT). This means applications can define | ||
their own mempools that have their own custom verification, block building, and | ||
state transition logic. Adding on, these changes make it such that blocks are | ||
built (`PrepareProposal`) and verified (`ProcessProposal`) directly in the | ||
application layer. | ||
|
||
The `x/builder` module implements an application-side mempool, `AuctionMempool`, | ||
that implements the `sdk.Mempool` interface. The mempool is composed of two | ||
primary indexes, a global index that contains all non-auction transactions and | ||
an index that only contains auction transactions, i.e. transactions with a single | ||
`MsgAuctionBid` message. Both indexes order transactions based on priority respecting | ||
the sender's sequence number. The global index prioritizes transactions based on | ||
`ctx.Priority()` and the auction index prioritizes transactions based on the | ||
bid. | ||
|
||
### PrepareProposal | ||
|
||
After the proposer of the next block has been selected, the CometBFT client will | ||
call `PrepareProposal` to build the next block. The block will be built in two | ||
stages. First, it will host the auction and include the winning bidder's bundle | ||
as the first set of transactions for the block, i.e. it will select the bid | ||
transaction itself along with automatically including all the bundled transactions | ||
in the specified order they appear in the bid's `transactions` field. | ||
|
||
The auction currently supports only a single winner. Selecting the auction winner | ||
involves a greedy search for a valid auction transaction starting from highest | ||
paying bid, respecting user nonce, in the `AuctionMempool`. The `x/builder`'s | ||
ante handler is responsible for verifying the auction transaction based on the | ||
criteria described below (see **Ante Handler**). | ||
|
||
Then, it will build the rest of the block by reaping and validating the transactions | ||
in the global index. The second portion of block building iterates from highest | ||
to lowest priority transactions in the global index and adds them to the proposal | ||
if they are valid. If the proposer comes across a transaction that was already | ||
included in the top of block, it will be ignored. | ||
|
||
### ProcessProposal | ||
|
||
After the proposer proposes a block of transactions for the next block, the | ||
block will be verified by other nodes in the network in `ProcessProposal`. If | ||
there is an auction transaction in the proposal, it must be the first transaction | ||
in the proposal and all bundled transactions must follow the auction transaction | ||
in the exact order we would expect them to be seen. If this fails, the proposal | ||
is rejected. If this passes, the validator will then run `CheckTx` on all of the | ||
transactions in the block in the order in which they were provided in the proposal. | ||
|
||
### Ante Handler | ||
|
||
When users want to bid for the rights for top-of-block execution they will submit | ||
a normal `sdk.Tx` transaction with a single `MsgAuctionBid`. The ante handler is | ||
responsible for verification of this transaction. The ante handler will verify that: | ||
|
||
1. The auction transaction specifies a timeout height where the bid is no longer | ||
considered valid. Note, it is REQUIRED that all bid transactions include a | ||
height timeout. | ||
2. The auction transaction includes less than `MaxBundleSize` transactions in | ||
its bundle. | ||
3. The auction transaction includes only a SINGLE `MsgAuctionBid` message. We | ||
enforce that no other messages are included to prevent front-running. | ||
4. Enforce that the user has sufficient funds to pay the bid they entered while | ||
covering all relevant auction fees. | ||
5. Enforce that the transaction's min bid increment greater than the local highest | ||
bid in the mempool. | ||
6. Enforce that the bundle of transactions the bidder provided does not front-run | ||
or sandwich (if enabled). | ||
|
||
Note, the process of selecting auction winners occurs in a greedy manner. In | ||
`PrepareProposal`, the `AuctionMempool` will iterate from largest to smallest | ||
bidding transaction until it finds the first valid bid transaction. | ||
|
||
### State | ||
|
||
The `x/builder` module stores the following state objects: | ||
|
||
```protobuf | ||
message Params { | ||
option (amino.name) = "cosmos-sdk/x/builder/Params"; | ||
// max_bundle_size is the maximum number of transactions that can be bundled | ||
// in a single bundle. | ||
uint32 max_bundle_size = 1; | ||
// escrow_account_address is the address of the account that will receive a | ||
// portion of the bid proceeds. | ||
string escrow_account_address = 2; | ||
// reserve_fee specifies the bid floor for the auction. | ||
cosmos.base.v1beta1.Coin reserve_fee = 3 | ||
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; | ||
// min_buy_in_fee specifies the fee that the bidder must pay to enter the | ||
// auction. | ||
cosmos.base.v1beta1.Coin min_buy_in_fee = 4 | ||
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; | ||
// min_bid_increment specifies the minimum amount that the next bid must be | ||
// greater than the previous bid. | ||
cosmos.base.v1beta1.Coin min_bid_increment = 5 | ||
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; | ||
// front_running_protection specifies whether front running and sandwich | ||
// attack protection is enabled. | ||
bool front_running_protection = 6; | ||
// proposer_fee defines the portion of the winning bid that goes to the block | ||
// proposer that proposed the block. | ||
string proposer_fee = 7 [ | ||
(gogoproto.nullable) = false, | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec" | ||
]; | ||
} | ||
``` | ||
|
||
## Messages | ||
|
||
### MsgAuctionBid | ||
|
||
POB defines a new Cosmos SDK `Message`, `MsgAuctionBid`, that allows users to | ||
create an auction bid and participate in a top-of-block auction. The `MsgAuctionBid` | ||
message defines a bidder and a series of embedded transactions, i.e. the bundle. | ||
|
||
```protobuf | ||
message MsgAuctionBid { | ||
option (cosmos.msg.v1.signer) = "bidder"; | ||
option (amino.name) = "pob/x/builder/MsgAuctionBid"; | ||
option (gogoproto.equal) = false; | ||
// bidder is the address of the account that is submitting a bid to the | ||
// auction. | ||
string bidder = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; | ||
// bid is the amount of coins that the bidder is bidding to participate in the | ||
// auction. | ||
cosmos.base.v1beta1.Coin bid = 3 | ||
[ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; | ||
// transactions are the bytes of the transactions that the bidder wants to | ||
// bundle together. | ||
repeated bytes transactions = 4; | ||
} | ||
``` | ||
|
||
Note, the `transactions` may or may not exist in a node's application mempool. If | ||
a transaction containing a single `MsgAuctionBid` wins the auction, the block | ||
proposal will automatically include the `MsgAuctionBid` transaction along with | ||
injecting all the bundled transactions such that they are executed in the same | ||
order after the `MsgAuctionBid` transaction. | ||
|
||
When processing a `MsgAuctionBid`, the `x/builder` module will perform two primary | ||
actions: | ||
|
||
1. Ensure the bid is valid per the module's parameters and configuration. | ||
2. Extract fee payments from the bidder's account and escrow them to the module's | ||
escrow account and the proposer that included the winning bid in the block | ||
proposal. | ||
|
||
### MsgUpdateParams | ||
|
||
The `MsgUpdateParams` message allows for an authority, typically the `x/gov` | ||
module account, to update the `x/builder`'s parameters. | ||
|
||
```protobuf | ||
message MsgUpdateParams { | ||
option (cosmos.msg.v1.signer) = "authority"; | ||
option (amino.name) = "pob/x/builder/MsgUpdateParams"; | ||
option (gogoproto.equal) = false; | ||
// authority is the address of the account that is authorized to update the | ||
// x/builder module parameters. | ||
string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; | ||
// params is the new parameters for the x/builder module. | ||
Params params = 2 [ (gogoproto.nullable) = false ]; | ||
} | ||
``` |