Skip to content

Commit bba6a03

Browse files
Freshenextclaude
andcommitted
docs(dos-removal): vendor PRD, EPICs, findings, architecture & handoff
Bring the DoS-removal planning + design docs into the repo under docs/dos-removal/ (PRD, EPICS breakdown + per-epic story/task tree, POC-FINDINGS, ARCHITECTURE.html, HANDOFF) so the contract branch carries its own design record. Fix the runbook's POC-FINDINGS link to the in-repo path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 59f1f28 commit bba6a03

160 files changed

Lines changed: 3184 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/dos-removal/ARCHITECTURE.html

Lines changed: 280 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# S0.1 Peg-out escrow state machine spec
2+
3+
**Epic:** E0 Design lock (Foundation track)
4+
**Type:** Story
5+
6+
## Statement
7+
8+
As an LBC engineer, I want a frozen escrow state machine, so that I can build E7 without re-deciding transitions mid-implementation.
9+
10+
## Output (the deliverable)
11+
12+
A markdown document `escrow-state-machine.md` containing a Mermaid state diagram, a transition table, a race-resolution table, and the no-release-path rule.
13+
14+
## Acceptance criteria
15+
16+
- The transition table covers every permitted transition and rejects all others by default.
17+
- Each race named in the proposal has a resolution: user cancel against LP claim in the same block, LP claim against the pre-claim deadline, and slash trigger against a late delivery.
18+
- The "no release path" rule is explicit: once an LP claims, it must fulfill or be slashed.
19+
- Reviewed and approved by one other engineer.
20+
21+
## Decisions (locked)
22+
23+
### Cancel vs claim race (2026-06-28)
24+
25+
**Decision:** first transaction mined wins, and the loser reverts. No gas-price cap for the PoC.
26+
27+
**Rationale:** pre-claim, both outcomes are safe because no BTC is committed yet, so this is the simplest correct rule. A user cannot cancel once a claim has landed, which is consistent with a claim being a hard commitment.
28+
29+
**Tradeoff:** a user cannot guarantee a cancel beats an LP's claim. If the claim lands first the user is served rather than refunded, which is safe because no BTC was committed. The alternative, a gas-price cap favoring cancel, guarantees the user's exit but adds mechanism and lets a user reliably beat LPs for no real pre-claim benefit.
30+
31+
### Claim vs pre-claim-deadline race (2026-06-28)
32+
33+
**Decision:** gate the claim on `block.timestamp <= preClaimDeadline`, first transaction mined wins. A claim at or before the deadline succeeds; after it, the claim reverts and the request becomes refund-and-global-slash eligible.
34+
35+
**Rationale:** consistent with the cancel-vs-claim rule, reuses the existing timestamp-based deadline checks in the peg-out contract, and is deterministic with no extra mechanism.
36+
37+
**Tradeoff:** an LP that claims a block or two late is global-slashed with everyone else, even on a near-miss. The alternative, a grace buffer past the deadline, is kinder to LPs but adds a tunable window and softens the deadline, which the PoC does not need.
38+
39+
### Slash-trigger vs late-delivery race (2026-06-28)
40+
41+
**Decision:** adjudicate late delivery by the BTC transaction timestamp via SPV, not by on-chain submission order. The contract compares the BTC tx timestamp plus the transfer window against the deadline. To treat honest LPs gently, the individual slash fires only when the BTC tx timestamp exceeds the deadline plus a delivery-grace tolerance, which absorbs BTC timestamp jitter and minor delays. The tolerance is a provisional parameter set in S0.3, reusing or extending the existing `btcBlockTime` buffer.
42+
43+
**Rationale:** the BTC tx timestamp is immutable and provable, so the outcome cannot be gamed by transaction ordering and it matches the current peg-out timestamp check. The grace tolerance keeps an honest LP from being slashed for a marginal, network-induced delay, which protects LP participation, especially at bootstrap with few LPs.
44+
45+
**Tradeoff:** a generous grace window lets a genuinely late LP escape the slash within the buffer and extends the user's wait before the request resolves. We accept that to avoid unfairly slashing honest LPs over BTC timestamp imprecision. Judging by proof-submission time instead would be simpler to compute but reintroduces a race and can slash an LP that delivered on time but submitted the proof slowly.
46+
47+
Note: the case where an LP delivers BTC but never submits the proof, letting the user collect a refund and keep the BTC, is the Watchtower honest-refund case, handled in E10b.
48+
49+
## Grounded in
50+
51+
Proposal `IPegOutEscrow` enum (`REQUESTED, CLAIMED, CANCELLED, FULFILLED, REFUNDED`), the proposal "Negative scenarios" peg-out list, and the "no release path for a claim" callout. Current peg-out behavior mapped in `PegOutContract` (`depositPegOut`, `refundPegOut`, `refundUserPegOut`).
52+
53+
## Children
54+
55+
Tasks: see `tasks/`. Tests: see `tests/`.
56+
57+
## Feeds
58+
59+
E7 (PegOutEscrow contract and claim flow).
60+
61+
## Depends on
62+
63+
None (within E0).
64+
65+
**Estimate:** 3. **Labels:** story, design, lbc.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Task: Enumerate the escrow states
2+
3+
**Parent story:** S0.1 Peg-out escrow state machine spec (E0)
4+
**Type:** Task
5+
6+
## Description
7+
8+
List each escrow state with its entry condition and its exit conditions: `REQUESTED`, `CLAIMED`, `FULFILLED`, `CANCELLED`, `REFUNDED`.
9+
10+
## Definition of done
11+
12+
- Every state has a stated entry condition and at least one exit, except terminal states.
13+
- Terminal states (`FULFILLED`, `CANCELLED`, `REFUNDED`) are marked terminal.
14+
15+
**Estimate:** 1. **Labels:** task, design, lbc.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Task: Write the transition table
2+
3+
**Parent story:** S0.1 Peg-out escrow state machine spec (E0)
4+
**Type:** Task
5+
6+
## Description
7+
8+
Write the transition table with columns: from-state, event or function, to-state, guard condition, who can call.
9+
10+
## Definition of done
11+
12+
- Every permitted transition has a row.
13+
- Each row names the function (`requestPegOut`, `claimPegOut`, `cancelPegOut`, `refundPegOut`) and the caller.
14+
- Transitions not in the table are rejected by default.
15+
16+
**Estimate:** 1. **Labels:** task, design, lbc.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Task: Resolve the races
2+
3+
**Parent story:** S0.1 Peg-out escrow state machine spec (E0)
4+
**Type:** Task
5+
6+
## Description
7+
8+
List each race and decide its resolution: user cancel against LP claim in the same block, LP claim against the pre-claim deadline, and slash trigger against a late delivery.
9+
10+
## Definition of done
11+
12+
- Each race has one chosen resolution and a one-line rationale.
13+
- The cancel-versus-claim race states the tie-breaker (for example, first transaction mined wins, or a gas-price cap).
14+
15+
**Estimate:** 1. **Labels:** task, design, lbc.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Task: Draw the state diagram
2+
3+
**Parent story:** S0.1 Peg-out escrow state machine spec (E0)
4+
**Type:** Task
5+
6+
## Description
7+
8+
Draw the Mermaid state diagram from the transition table.
9+
10+
## Definition of done
11+
12+
- The diagram shows every state and every transition from the table.
13+
- The diagram and the table agree, with no transition in one but not the other.
14+
15+
**Estimate:** 1. **Labels:** task, design, lbc.

0 commit comments

Comments
 (0)