|
| 1 | +# EPICs: Flyover liquidity-DoS removal (PoC) |
| 2 | + |
| 3 | +**Status:** Draft for review |
| 4 | +**Owner:** Francis Rodriguez |
| 5 | +**Date:** 2026-06-28 |
| 6 | +**Source:** [PRD-dos-removal.md](./PRD-dos-removal.md) |
| 7 | +**Tracker:** JIRA FLY (epics not yet created) |
| 8 | + |
| 9 | +Twelve epics across three tracks. A shared foundation is built once, then the peg-in and peg-out tracks proceed independently, each on its own branches and worktrees. The epic folders live under `EPICS/FOUNDATION`, `EPICS/PEGIN`, and `EPICS/PEGOUT`. Each epic lists what it delivers, why, the acceptance criteria, dependencies, an estimate, and the PRD story it serves. Estimates use t-shirt sizes. Priority P0 marks the critical path that closes the peg-in DoS. |
| 10 | + |
| 11 | +- **Foundation:** E0, E1, E3, EB. |
| 12 | +- **Peg-in:** E2, E4, E5, E6, E10a. |
| 13 | +- **Peg-out:** E7, E8, E9, E10b. |
| 14 | + |
| 15 | +> **EB is a P0 foundation blocker discovered by the PoC** (see `POC-FINDINGS.md`, finding B): the registry's deterministic address derivation is incompatible with the native fast-bridge settlement derivation, so `resolvePegIn` cannot release funds. EB gates `resolvePegIn` completion, the whole peg-out track (E7–E9 settle through the same bridge), and prod. Resolve EB before continuing E7+. |
| 16 | +
|
| 17 | +--- |
| 18 | + |
| 19 | +## E0. Design lock |
| 20 | + |
| 21 | +**Delivers.** A short design note that freezes the peg-out escrow state machine, the three contract interfaces, the provisional parameter set, and the registration-auth decision. No code. |
| 22 | + |
| 23 | +**Why.** Two open items gate every build epic, and the fee floor is a security parameter, not just an economic one. |
| 24 | + |
| 25 | +**Acceptance criteria.** |
| 26 | + |
| 27 | +- The escrow state machine lists every state, every permitted transition, and a resolution for each race named in the proposal. |
| 28 | +- A provisional parameter table fixes fee floor, slash amounts, deadlines, and minimum collateral, each marked provisional. |
| 29 | +- A decision record states whether registration requires an ECDSA signature or accepts any caller, which selects the institutional path in E10. |
| 30 | + |
| 31 | +**Repos.** None (doc). **Serves.** All. **Depends on.** Nothing. **Blocks.** E1 to E10. **Estimate.** M. **Labels.** epic, P0, design. |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## E1. FlyoverConfigurations contract |
| 36 | + |
| 37 | +**Delivers.** A new `FlyoverConfigurations` contract in LBC holding both peg-in and peg-out configuration: fees (fixed floor plus percentage), confirmation tiers, deadlines, and limits, with time-locked setters. Both flows read it, and neither edits it after this epic. |
| 38 | + |
| 39 | +**Why.** Removing the quote removes where these values lived, so they need an on-chain home both flows read. As a foundation contract, it holds both configurations once so the flow tracks only read it. |
| 40 | + |
| 41 | +**Acceptance criteria.** |
| 42 | + |
| 43 | +- `getPegInConfiguration` / `calculatePegInFee` and `getPegOutConfiguration` / `calculatePegOutFee` return the values their flows use. |
| 44 | +- Setters apply only after a time lock, so a change cannot land mid-operation. |
| 45 | +- Foundry tests cover fee calculation for both flows, confirmation tiers, and the time lock. |
| 46 | + |
| 47 | +**Repos.** lbc. **Serves.** S1, S2. **Depends on.** E0. **Blocks.** E4, E5, E7. **Estimate.** M. **Labels.** epic, P0, lbc. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## E2. PegInAddressRegistry and deterministic address |
| 52 | + |
| 53 | +**Delivers.** A `PegInAddressRegistry` contract deriving the BTC address from the registered RSK address, with the `registrationRoot` running hash, deposit-gated registration, and a federation-change policy. |
| 54 | + |
| 55 | +**Why.** The per-quote address feeds the DoS and blocks institutions who cannot whitelist an address that changes every peg-in. |
| 56 | + |
| 57 | +**Acceptance criteria.** |
| 58 | + |
| 59 | +- `getPegInAddress` returns the same address across calls for one RSK address, and changes only on a federation change. |
| 60 | +- An LP can rebuild its watch set from `AddressRegistered` events and match the on-chain `registrationRoot`. |
| 61 | +- Registration succeeds only against a confirmed deposit. |
| 62 | +- Foundry tests cover repeat derivation, a federation change, and the running-hash match. |
| 63 | + |
| 64 | +**Repos.** lbc. **Serves.** S3, S4. **Depends on.** E0. **Blocks.** E4, E5, E6, E10. **Estimate.** L. **Labels.** epic, P0, lbc. |
| 65 | + |
| 66 | +--- |
| 67 | + |
| 68 | +## E3. Global slash and grace window |
| 69 | + |
| 70 | +**Delivers.** A proportional global slash across all registered LPs in `CollateralManagement`, plus a no-penalty window after registration. The existing individual slash stays for the post-claim case. |
| 71 | + |
| 72 | +**Why.** The new model must penalize "no one served a valid operation," and the global slash creates bootstrap fragility that the grace window offsets. |
| 73 | + |
| 74 | +**Acceptance criteria.** |
| 75 | + |
| 76 | +- An unserved valid operation slashes all registered LPs proportionally. |
| 77 | +- An LP registered within the window is not slashed. |
| 78 | +- Foundry tests cover the few-LP bootstrap case and the window boundary. |
| 79 | + |
| 80 | +**Repos.** lbc. **Serves.** S2, S5. **Depends on.** E0. **Blocks.** E4, E7. **Estimate.** M. **Labels.** epic, P0, lbc. |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## E4. Peg-in claim flow |
| 85 | + |
| 86 | +**Delivers.** `requestPegIn` and `resolvePegIn` in `PegInContract`, where the LP fronts RBTC from its own wallet, the claimer earns the full fee, an unclaimed valid peg-in triggers the global slash, `OP_RETURN(destContract, maxGas, callData)` carries contract-call data, and a second claim reverts on the first line. |
| 87 | + |
| 88 | +**Why.** This is the peg-in half of user-commits-first on-chain. |
| 89 | + |
| 90 | +**Acceptance criteria.** |
| 91 | + |
| 92 | +- An LP claims a confirmed peg-in and the user receives RBTC minus fees, with no quote involved. |
| 93 | +- A second claim on the same peg-in reverts cheaply. |
| 94 | +- A smart-contract peg-in driven by `OP_RETURN` calls the destination with the carried data. |
| 95 | +- An unclaimed valid peg-in triggers the global slash from E3. |
| 96 | + |
| 97 | +**Known constraint.** The `OP_RETURN` standard ~80-byte limit caps SC-call calldata to roughly 28 bytes after the destination and max-gas fields. See `EPICS/PEGIN/E4-pegin-claim-flow/constraints.md`. |
| 98 | + |
| 99 | +**Repos.** lbc. **Serves.** S1, S5. **Depends on.** E1, E2, E3. **Blocks.** E5, E6. **Estimate.** L. **Labels.** epic, P0, lbc. |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +## E5. LPS peg-in: event discovery and competitive claim |
| 104 | + |
| 105 | +**Delivers.** Watchers in LPS for `AddressRegistered` with a `registrationRoot` check, Bitcoin monitoring of registered addresses, a competitive claim through `requestPegIn`, and removal of the off-chain reservation in `accept_pegin_quote.go`. |
| 106 | + |
| 107 | +**Why.** The DoS dies here. Once LPS commits liquidity only against an on-chain commitment, the attack has nothing to spam. |
| 108 | + |
| 109 | +**Acceptance criteria.** |
| 110 | + |
| 111 | +- LPS claims a confirmed peg-in it discovered from events and the chain, with no user accept call. |
| 112 | +- The accept-quote reservation path is gone, and liquidity is checked only at claim time against the LP wallet. |
| 113 | +- The accept-spam attack leaves available liquidity unchanged (PoC success criterion 1). |
| 114 | + |
| 115 | +**Repos.** lps. **Serves.** S5. **Depends on.** E4 (uses E1, E2). **Blocks.** Milestone A. **Estimate.** L. **Labels.** epic, P0, lps. |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +## E6. SDK peg-in: derive address and build the BTC transaction |
| 120 | + |
| 121 | +**Delivers.** `flyover-sdk` fetches the deposit address from the registry and re-derives on a federation change, reads fees from `FlyoverConfigurations`, and builds the BTC transaction with an optional `OP_RETURN` output. `bridges-core-sdk` gains any missing BTC tx-build helpers. |
| 122 | + |
| 123 | +**Why.** The client must commit on-chain first instead of negotiating. |
| 124 | + |
| 125 | +**Acceptance criteria.** |
| 126 | + |
| 127 | +- An integration test completes a peg-in commit-first against regtest with E4 and E5. |
| 128 | +- The SDK re-derives a stale address after a simulated federation change. |
| 129 | +- A contract-call peg-in produces a correct `OP_RETURN` output. |
| 130 | + |
| 131 | +**Known constraint.** The `OP_RETURN` standard ~80-byte limit caps the calldata the SDK can build into a SC-call peg-in. Validate and fail early above the cap. See `EPICS/PEGIN/E6-sdk-pegin/constraints.md`. |
| 132 | + |
| 133 | +**Repos.** flyover-sdk, bridges-core-sdk. **Serves.** S1. **Depends on.** E4 (uses E2). **Completes.** Milestone A: peg-in DoS closed on regtest. **Estimate.** M. **Labels.** epic, P0, sdk. |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## E7. PegOutEscrow contract and claim flow |
| 138 | + |
| 139 | +**Delivers.** A `PegOutEscrow` contract with the E0 state machine: `requestPegOut` (payable, user-first), `claimPegOut` with a signature that moves RBTC to `PegOutContract`, `cancelPegOut`, a global slash before claim, and an individual slash after. The existing SPV refund path is reused post-claim. |
| 140 | + |
| 141 | +**Why.** This is the peg-out half of user-commits-first. |
| 142 | + |
| 143 | +**Acceptance criteria.** |
| 144 | + |
| 145 | +- A user deposits RBTC with `requestPegOut`, an LP claims and the responsibility is recorded on-chain. |
| 146 | +- An unclaimed request past its deadline refunds the user and global-slashes registered LPs. |
| 147 | +- The documented races resolve as specified in E0. |
| 148 | + |
| 149 | +**Repos.** lbc. **Serves.** S2. **Depends on.** E1, E3 (uses E0). **Blocks.** E8, E9. **Estimate.** L. **Labels.** epic, P1, lbc. |
| 150 | + |
| 151 | +--- |
| 152 | + |
| 153 | +## E8. LPS peg-out: escrow watcher and claim |
| 154 | + |
| 155 | +**Delivers.** A `PegOutRequested` watcher in LPS, a claim through `claimPegOut`, BTC delivery, SPV refund, and removal of the off-chain reservation in `accept_pegout_quote.go`. |
| 156 | + |
| 157 | +**Why.** Closes the peg-out DoS on the server side. |
| 158 | + |
| 159 | +**Acceptance criteria.** |
| 160 | + |
| 161 | +- LPS claims an escrow request it discovered from events, delivers BTC, and recovers RBTC by SPV proof. |
| 162 | +- The peg-out accept-quote reservation path is gone. |
| 163 | + |
| 164 | +**Repos.** lps. **Serves.** S5. **Depends on.** E7. **Blocks.** Milestone B. **Estimate.** M. **Labels.** epic, P1, lps. |
| 165 | + |
| 166 | +--- |
| 167 | + |
| 168 | +## E9. SDK peg-out: deposit into the escrow |
| 169 | + |
| 170 | +**Delivers.** `flyover-sdk` calls `requestPegOut` on the escrow instead of negotiating a quote, and reads peg-out fees from `FlyoverConfigurations`. |
| 171 | + |
| 172 | +**Why.** The client must deposit to escrow first. |
| 173 | + |
| 174 | +**Acceptance criteria.** |
| 175 | + |
| 176 | +- An integration test completes a peg-out commit-first against regtest with E7 and E8. |
| 177 | + |
| 178 | +**Repos.** flyover-sdk, bridges-core-sdk. **Serves.** S2. **Depends on.** E7. **Completes.** Milestone B: peg-out DoS closed on regtest. **Estimate.** S. **Labels.** epic, P1, sdk. |
| 179 | + |
| 180 | +--- |
| 181 | + |
| 182 | +## E10a. Institutional registration path (peg-in track) |
| 183 | + |
| 184 | +**Delivers.** One registration path chosen in E0, either a Watchtower service or the RIF Relay smart wallet, working end-to-end on regtest, with the contract and SDK support it needs. |
| 185 | + |
| 186 | +**Why.** Some custody accounts cannot sign the way registration expects, so they need a self-serve or sponsored path. |
| 187 | + |
| 188 | +**Acceptance criteria.** |
| 189 | + |
| 190 | +- A user without RBTC registers an address end-to-end on regtest through the chosen path. |
| 191 | +- Registration stays gated on a confirmed BTC deposit. |
| 192 | + |
| 193 | +**Repos.** new service or lbc plus sdk. **Serves.** S6. **Depends on.** E0, E2. **Completes.** Milestone C: whitelist-once demonstrated. **Estimate.** L. **Labels.** epic, P2, institutional. |
| 194 | + |
| 195 | +--- |
| 196 | + |
| 197 | +## E10b. Watchtower honest refund (peg-out track) |
| 198 | + |
| 199 | +**Delivers.** The Watchtower executes `refundPegOut` on the LP's behalf when the LP stalls, earning a reward. Built only if the Watchtower path is chosen in E0. |
| 200 | + |
| 201 | +**Why.** A user can attack an LP's availability after delivery to force a refund and duplicate funds. A third party incentivized to submit the proof closes that window. |
| 202 | + |
| 203 | +**Acceptance criteria.** |
| 204 | + |
| 205 | +- When an LP delivers BTC but does not submit the proof, the Watchtower submits `refundPegOut` and receives a reward. |
| 206 | +- The contract pays the caller a reward in this scenario, not only in the penalizable one. |
| 207 | + |
| 208 | +**Repos.** new service plus lbc. **Serves.** S2. **Depends on.** E0, E7. **Estimate.** M. **Labels.** epic, P2, institutional. |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +## EB. Settlement-compatible address derivation (Foundation, P0 blocker) |
| 213 | + |
| 214 | +**Delivers.** A resolution to the conflict between the registry's deterministic deposit-address derivation and the native fast-bridge settlement derivation, so `resolvePegIn` can actually release funds and the LP is reimbursed. |
| 215 | + |
| 216 | +**Why.** Discovered by the PoC (`POC-FINDINGS.md`, finding B). The bridge's `registerFastBridgeBtcTransaction` folds `(userRefundBtcAddress, lbcAddress, lpBtcAddress)` into the flyover derivation hash; `getPegInAddress` uses only `keccak256("FLYOVER_PEGIN_V1", rskAddr)`. The addresses differ, so the bridge finds no matching UTXO (returns -900). The user is served (LP fronts RBTC) but the LP cannot recover from the bridge. The LP-agnostic "address from RSK address" premise is in tension with the bridge requiring `lpBtcAddress` in its derivation. |
| 217 | + |
| 218 | +**Acceptance criteria.** |
| 219 | + |
| 220 | +- A design decision is recorded choosing the approach (see the spike), with rationale and tradeoff. |
| 221 | +- After implementing it, a live regtest `resolvePegIn` settles successfully (bridge releases funds, LP reimbursed) for a peg-in to a registry-derived address. |
| 222 | +- The deposit address stays deterministic from the RSK address and servable by any authorized LP (or the decision explicitly accepts a documented constraint). |
| 223 | + |
| 224 | +**Work items.** EB.1 design spike (decide), then implementation stories TBD from the decision. |
| 225 | + |
| 226 | +**Repos.** lbc (likely); possibly powpeg/bridge if option 3 is chosen. **Serves.** S1, S2 (settlement). **Depends on.** E0. **Blocks.** `resolvePegIn` completion, E7, E8, E9, prod. **Estimate.** spike S, implementation TBD. **Labels.** epic, P0, lbc, design, blocker. |
| 227 | + |
| 228 | +--- |
| 229 | + |
| 230 | +## Dependency summary |
| 231 | + |
| 232 | +- **Foundation (built once):** E0, then E1 and E3 in parallel. Both tracks depend on this. |
| 233 | +- **Peg-in track (after foundation, runs independently):** E2, then E4, then E5 and E6, reaching Milestone A. E10a follows (needs E2), reaching Milestone C. |
| 234 | +- **Peg-out track (after foundation, runs independently):** E7, then E8 and E9, reaching Milestone B. E10b follows (needs E7) if the Watchtower path is chosen. |
| 235 | +- **EB (P0 blocker, foundation):** must be resolved before the peg-out track and before `resolvePegIn` is production-usable. The PoC peg-in succeeds without it (the LP fronts RBTC), but LP reimbursement via the bridge does not. Resolve EB next. |
| 236 | + |
| 237 | +Every epic carries acceptance criteria that trace to a PRD success criterion or story, so the epic layer stays anchored to the PRD. |
0 commit comments