Challenge: Covenants for Braidpool

Hi @mcelrath,

This suggests that Braidpool-style rolling aggregation can be implemented as a sequence of per-round covenant updates, rather than requiring a single persistent commitment across blocks.

The key insight on Req 4

Req 4 looks like a contradiction — CTV commits to a static hash, but UHPO payouts change every block. It dissolves when you treat each APO replacement as atomic with its CTV update:

  • APO handles the input side: the update signature doesn’t commit to the previous RCA’s txid, so you can pre-sign the next state before the current one hits the chain.

  • CTV handles the output side: each new RCA commits to the current period’s UHPO template. When the old RCA gets spent, its UHPO loses its input and can never be broadcast. Only the latest RCA survives, so only the latest UHPO can settle.

Dynamic payouts through a sequence of per-round static commits. APO and CTV fire together on every update — no single commitment needs to span multiple blocks.

The RCA script shape

Each RCA output is a three-leaf Taproot tree:

leaf 1 — ctv_uhpo:   OP_CHECKTEMPLATEVERIFY
                      locks spend to the committed UHPO template

leaf 2 — apo_update: <0x01||xonly_pubkey> OP_CHECKSIG  (BIP118)
                      next RCA can replace this one without knowing the txid

leaf 3 — csv_escape: <N> OP_CSV OP_DROP <pubkey> OP_CHECKSIG
                      timeout fallback for solo mining recovery

Demo 1 — APO rebinding

Two UTXOs, same script, same amount, different txids. One APO signature spends both — witness bytes are byte-for-byte identical across the two spend transactions, only the prevout differs. This is what makes pre-signing the next RCA state possible before the current one hits the chain.

TxID
Spend UTXO₁ (signature produced here) 03c0…f3a4
Spend UTXO₂ (same witness, different prevout) 4609…1a43

The two spend txs look identical on the surface (same script, same amount, same output address) — that’s by design. The proof is in the input: each spends a different prevout, but the witness bytes are identical. SIGHASH_ANYPREVOUT does not commit to the outpoint, so the same signature validates against either UTXO.

Witness: three stack items (BIP118 signature + 0x01‖xonly OP_CHECKSIG leaf + control block). Stacks are byte-for-byte identical on both spends; only vin outpoints differ.

Demo 2 — RCA Eltoo chain (three rounds + settlement)

Fund → RCA v1 (50/30/20) → RCA v2 (45/35/20) → RCA v3 (40/35/25) → UHPO settlement

Role TxID
Fund → RCA v1 386d…9c41
APO: v1 → v2 096e…5601
APO: v2 → v3 0913…d7f6
CTV settlement (UHPO v3) 1395…f9b7

Witness — APO updates (v1→v2, v2→v3): same three-part shape each time (BIP118 signature + leaf + control block), but each step uses a new signature (new state).

Witness — CTV settlement: two elements — 32-byte template hash push + control block. No Schnorr signature. nSequence is 0xffffffff. Explorers show the opcode as OP_NOP4.

Requirements mapping

Req Status Note
1 Three leaves are the only spending paths
2 partial csv_escape leaf exists; “incorrect block → solo claim” not fully demoed
3 partial CSV timeout present; demo uses normal UTXOs not real coinbase
4 APO + CTV atomic update dissolves the dynamic commitment problem
5 open See below
6 UHPO template committed into RCA at block template construction time
7 partial Real coinbase maturity needs extra timelock layering

Trade-offs

  • Each pool aggregation round that advances the UHPO snapshot uses its own on-chain RCA update (one APO spend) — one covenant step, one update tx.

  • On-chain footprint scales with how many rounds you run.

  • Complexity shifts from script expressiveness to transaction orchestration outside the script.

On Req 5

No solution here. “Detect an attack and immediately unlock” requires the script to observe external chain state — none of the current opcodes can do this. CSFS can verify an authorization signature, but whoever holds that key becomes a new trusted party, reintroducing the centralization you’re trying to remove. A minimal additional primitive enabling conditional unlock based on external chain state (something like OP_VAULT’s trigger mechanism) would close this gap.

Note on FROST

Demo uses a single key where production Braidpool would use a FROST aggregate — on-chain they look identical (64-byte Schnorr signature either way). The covenant layer limits FROST’s role: whatever key signs the APO update, the CTV commitment means they can only produce a valid UHPO. FROST goes from “decides payouts” to “triggers the update.”


Curious whether others see a path to Req 5 without introducing new trust assumptions.

Code: github.com/aaron-recompile/btcaaron (examples/braidpool/)