|
| 1 | +# Integrate the Block SDK |
| 2 | + |
| 3 | +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! |
| 4 | + |
| 5 | +Visit the GitHub repo [here](https://github.com/skip-mev/block-sdk). |
| 6 | + |
| 7 | +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. |
| 8 | + |
| 9 | + |
| 10 | +## ⚙️ Architecture [15 mins] |
| 11 | + |
| 12 | +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**! |
| 13 | + |
| 14 | + |
| 15 | +### How Were Blocks Constructed pre-Block-SDK? |
| 16 | + |
| 17 | +There are 3 relevant stages of consensus (these are all ABCI++ methods) |
| 18 | + |
| 19 | +- **PrepareProposal** |
| 20 | + - In this step, the consensus-engine (CometBFT, etc.) gives the application all of the transactions it has seen thus far. |
| 21 | + - 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. |
| 22 | +- **ProcessProposal** |
| 23 | + - 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 |
| 24 | + - If the proposal fails, validators will not vote on the block, and the network will be forced to another round of consensus |
| 25 | + - if the proposal passes, valdiators vote on the block, and the block will become canonical (barring unforeseen events) |
| 26 | + |
| 27 | +### **Application Mempools** |
| 28 | + |
| 29 | +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 |
| 30 | + |
| 31 | +1. Store all pending (not finalized in a block) transactions |
| 32 | +2. Order the set of pending transactions |
| 33 | + |
| 34 | +#### **How does block-building change?** |
| 35 | + |
| 36 | +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. |
| 37 | + |
| 38 | +**Why is this better?** |
| 39 | + |
| 40 | +- Mempools that are not app-state aware will not have the ability to make state-aware ordering rules. Like |
| 41 | + |
| 42 | +1. All staker transactions are placed at the top of the block |
| 43 | +2. All IBC `LightClientUpdate` messages are placed at the top of the block |
| 44 | +3. Anything you can think of!! |
| 45 | + |
| 46 | +- The consensus engine's mempool is generally in-efficient. |
| 47 | + - The consensus-engine's mempool does not know when to remove transactions from its own mempool |
| 48 | + - The consensus-engine spends most of its time re-broadcasting transactions between peers, hogging network bandwidth |
| 49 | + |
| 50 | +## Block-SDK!! |
| 51 | + |
| 52 | +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. |
| 53 | + |
| 54 | +**transaction ingress** |
| 55 | + |
| 56 | +- 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. |
| 57 | + **ordering** |
| 58 | +- 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 |
| 59 | + |
| 60 | +### PrepareProposal |
| 61 | + |
| 62 | +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. |
| 63 | + |
| 64 | +### ProcessProposal |
| 65 | + |
| 66 | +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. |
| 67 | + |
| 68 | +#### ⚠️ NOTE ⚠️ |
| 69 | + |
| 70 | +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!! |
| 71 | + |
| 72 | + |
| 73 | +## 📖 Set Up [20 mins] |
| 74 | + |
| 75 | +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! |
| 76 | + |
| 77 | +The default lane mirrors how CometBFT creates proposals today. |
| 78 | + |
| 79 | +- It does a basic check to ensure that the transaction is valid. |
| 80 | +- Orders the transactions based on tx fee amount (highest to lowest). |
| 81 | +- The `PrepareLane` handler will reap transactions from the lane up to the `MaxBlockSpace` limit |
| 82 | +- The `ProcessLane` handler will ensure that the transactions are ordered based on their fee amount and pass the same checks done in `PrepareLane`. |
| 83 | + |
| 84 | +<!-- TODO: create script --> |
| 85 | + |
| 86 | +# 🏗️ Default Lane Setup |
| 87 | + |
| 88 | +## 📦 Dependencies |
| 89 | + |
| 90 | +The Block SDK is built on top of the Cosmos SDK. The Block SDK is currently |
| 91 | +compatible with Cosmos SDK versions greater than or equal to `v0.47.0`. |
| 92 | + |
| 93 | +### Release Compatibility Matrix |
| 94 | + |
| 95 | +| Block SDK Version | Cosmos SDK | |
| 96 | +| :---------------: | :--------: | |
| 97 | +| `v1.x.x` | `v0.47.x` | |
| 98 | +| `v2.x.x` | `v0.50.x` | |
| 99 | + |
| 100 | +## 📥 Adding the Block SDK to Your Project |
| 101 | + |
| 102 | +```bash |
| 103 | +$ go get github.com/skip-mev/block-sdk |
| 104 | +``` |
| 105 | + |
| 106 | +## 📚 Usage |
| 107 | + |
| 108 | +1. First determine the set of lanes that you want to use in your application. This guide only sets up the `default lane` |
| 109 | + |
| 110 | +```golang |
| 111 | +import ( |
| 112 | + "github.com/skip-mev/block-sdk/abci" |
| 113 | + "github.com/skip-mev/block-sdk/block/base" |
| 114 | + defaultlane "github.com/skip-mev/block-sdk/lanes/base" |
| 115 | +) |
| 116 | + |
| 117 | + // 1. Create the lanes. |
| 118 | + // |
| 119 | + // NOTE: The lanes are ordered by priority. The first lane is the highest priority |
| 120 | + // lane and the last lane is the lowest priority lane. Top of block lane allows |
| 121 | + // transactions to bid for inclusion at the top of the next block. |
| 122 | + // |
| 123 | + // For more information on how to utilize the LaneConfig please |
| 124 | + // visit the README in docs.skip.money/chains/lanes/build-your-own-lane#-lane-config. |
| 125 | + // |
| 126 | + // Default lane accepts all transactions. |
| 127 | + |
| 128 | +func NewApp() { |
| 129 | + ... |
| 130 | + defaultConfig := base.LaneConfig{ |
| 131 | + Logger: app.Logger(), |
| 132 | + TxEncoder: app.txConfig.TxEncoder(), |
| 133 | + TxDecoder: app.txConfig.TxDecoder(), |
| 134 | + MaxBlockSpace: math.LegacyZeroDec(), |
| 135 | + MaxTxs: 0, |
| 136 | + } |
| 137 | + defaultLane := defaultlane.NewDefaultLane(defaultConfig) |
| 138 | + // TODO(you): Add more Lanes!!! |
| 139 | +``` |
| 140 | +
|
| 141 | +2. In your base application, you will need to create a `LanedMempool` composed |
| 142 | + of the `lanes` you want to use. |
| 143 | +
|
| 144 | +```golang |
| 145 | + // 2. Set up the relative priority of lanes |
| 146 | + lanes := []block.Lane{ |
| 147 | + defaultLane, |
| 148 | + } |
| 149 | + mempool := block.NewLanedMempool(app.Logger(), true, lanes...) |
| 150 | + app.App.SetMempool(mempool) |
| 151 | +``` |
| 152 | +
|
| 153 | +3. Next, order the lanes by priority. The first lane is the highest priority lane |
| 154 | + and the last lane is the lowest priority lane. **It is recommended that the last |
| 155 | + lane is the default lane.** |
| 156 | +
|
| 157 | +```golang |
| 158 | + // 3. Set up the ante handler. |
| 159 | + anteDecorators := []sdk.AnteDecorator{ |
| 160 | + ante.NewSetUpContextDecorator(), |
| 161 | + ... |
| 162 | + utils.NewIgnoreDecorator( |
| 163 | + ante.NewDeductFeeDecorator( |
| 164 | + options.BaseOptions.AccountKeeper, |
| 165 | + options.BaseOptions.BankKeeper, |
| 166 | + options.BaseOptions.FeegrantKeeper, |
| 167 | + options.BaseOptions.TxFeeChecker, |
| 168 | + ), |
| 169 | + options.FreeLane, |
| 170 | + ), |
| 171 | + ... |
| 172 | + } |
| 173 | + |
| 174 | + anteHandler := sdk.ChainAnteDecorators(anteDecorators...) |
| 175 | + |
| 176 | + // Set the lane ante handlers on the lanes. |
| 177 | + // |
| 178 | + // NOTE: This step is very important. Without the antehandlers, lanes will not |
| 179 | + // be able to verify transactions. |
| 180 | + for _, lane := range lanes { |
| 181 | + lane.SetAnteHandler(anteHandler) |
| 182 | + } |
| 183 | + app.App.SetAnteHandler(anteHandler) |
| 184 | +``` |
| 185 | +
|
| 186 | +4. You will also need to create a `PrepareProposalHandler` and a |
| 187 | + `ProcessProposalHandler` that will be responsible for preparing and processing |
| 188 | + proposals respectively. Configure the order of the lanes in the |
| 189 | + `PrepareProposalHandler` and `ProcessProposalHandler` to match the order of the |
| 190 | + lanes in the `LanedMempool`. |
| 191 | +
|
| 192 | +```golang |
| 193 | + // 4. Set the abci handlers on base app |
| 194 | + // Create the LanedMempool's ProposalHandler |
| 195 | + proposalHandler := abci.NewProposalHandler( |
| 196 | + app.Logger(), |
| 197 | + app.TxConfig().TxDecoder(), |
| 198 | + mempool, |
| 199 | + ) |
| 200 | + |
| 201 | + // set the Prepare / ProcessProposal Handlers on the app to be the `LanedMempool`'s |
| 202 | + app.App.SetPrepareProposal(proposalHandler.PrepareProposalHandler()) |
| 203 | + app.App.SetProcessProposal(proposalHandler.ProcessProposalHandler()) |
| 204 | +``` |
| 205 | +
|
| 206 | +### 💅 Next step: implement other `lanes` |
| 207 | +
|
| 208 | +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). |
0 commit comments