# Contract-level Relative Timelocks (or, let's talk about ancestry proofs and singletons)

**URL:** <https://delvingbitcoin.org/t/contract-level-relative-timelocks-or-lets-talk-about-ancestry-proofs-and-singletons/1353>\
**Category:** Protocol Design\
**Created:** [January 2, 2025, 3:00pm UTC](https://delvingbitcoin.org/t/contract-level-relative-timelocks-or-lets-talk-about-ancestry-proofs-and-singletons/1353 "2025-01-02T15:00:14Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![JeremyRubin](https://delvingbitcoin.org/user_avatar/delvingbitcoin.org/jeremyrubin/32/264_2.png) [@JeremyRubin](https://delvingbitcoin.org/u/JeremyRubin)\
**Post date:** [January 3, 2025, 5:32pm UTC](https://delvingbitcoin.org/t/contract-level-relative-timelocks-or-lets-talk-about-ancestry-proofs-and-singletons/1353/3 "2025-01-03T17:32:57Z")

</div>

MUON has an interesting interplay here… [x.com](https://x.com/JeremyRubin/status/1782220444185116883)

_Note that you still end up needing some sort of recursive proof inside R here. But MUON does the job of ensuring that no Tx Update can be broadcast without the right R being created for it, and makes Tx Update non-malleable._

 ![20250103_115444](https://delvingbitcoin.org/uploads/default/original/1X/499025c5007133cbc1e131035da640fa52a5b9fc.jpeg)

Describing the above graph:

Tx Open:

- Inputs: …
- Outputs: V\_O ← N Sats

V\_O.program ← tr(musig(A, B), {CTVHASH(kickoff) CTV})

Tx Kickoff:

- Inputs: V\_O
- Outputs:
  - R[0] ← 0 sats / dust
  - V\_K ← N Sats

V\_K.program = tr(musig(A, B), {})

Updates are setup as follows:

Tx Update[i]:

- Sequence: 2 weeks
- Inputs V\_K
- Outputs
  - MUON X\_i ← 0 Sats
  - Alice ← k\*N
  - Bob ← (1-k) \*N

Tx Ratchet i:

- nLockTime i
- Input R[i]
- Output
  - R[i+1]

R[i]'s program:

tr(NUMS\_H, { ratchet, cospend })

ratchet: \<CTVHASH(Ratchet TX i+1)\> CTV \<musig(a,b)\> CSFS [i] CLTV

cospend: 1 GETINPUT \<COutpoint(MUON X\_i)\> EQUALVERIFY \<CTVHash(Tx Exit)\> CTV

Tx Exit:

- nSequence: 1 day (min time between last update?)
- Inputs: R[i] (via cospend path), MUON X\_i
- Outputs: OP\_RETURN Update[i].details\_to\_reconstruct

muon X\_i.program: tr(NUMS\_H, {\<CTVHash(Tx Exit)\> CTV 0 GETINPUT \<R[i]\> EQUALVERIFY })

* * *

How signing works:

First you open the protocol to create V\_O.

Then you create the updates off of V\_K (go ahead and sign – MUON X means a spend must exit).

You then create the ratchet update off of R, and exchange the sigs.

_Note that you still end up needing some sort of recursive proof inside R here. But MUON does the job of ensuring that no Tx Update can be broadcast without the right R being created for it, and makes Tx Update non-malleable._

---

_[View the full topic](https://delvingbitcoin.org/t/contract-level-relative-timelocks-or-lets-talk-about-ancestry-proofs-and-singletons/1353)._
