From 7d48444cd2b4c43c81a499c5b02aaa9ccdf71b1a Mon Sep 17 00:00:00 2001 From: Stu Williams Date: Tue, 8 Sep 2026 19:07:12 +0100 Subject: [PATCH 1/4] =?UTF-8?q?docs(spec):=20name=20the=20yellow=20paper?= =?UTF-8?q?=20=C2=A79=20is=20deferring=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPEC.md already says scope is primitives only and that FLOP-network settlement "plugs in later as one rail among several". What it never does is name the document that binding satisfies — so someone reading FLOP's yellow paper, whose §10 is a normative HTLC specification with MUST-level requirements, finds no reference to it anywhere in this repository and cannot tell deliberate scope from an oversight. That someone was me, in #132. The answer was in SPEC.md's opening paragraph and I asked anyway; @bdunn77 pointed at it, verified both gaps on the exact tree, and proposed this sentence. Their framing. Assigns the two requirements rather than only mentioning them. R10.2's timelock margin is a relationship between a rail's own T_lock and these deadlines, so only a rail that has a T_lock can check it — which is why validateDeadlines supplies no universal default, and why the requirement belongs to the adapter. R10.5's "lock an estimated max, settle the actual" has no tclk/1 shape at all, since amount is fixed on the offer and contractId commits to the offer wholesale; that is a tclk/2 question (#57), not a patch. Docs only. check:protocol clean, 104 tests passing. Closes #132. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 7 +++++++ SPEC.md | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9d235e..901879a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ All notable changes to this project are documented here. Format follows ## [Unreleased] +### Changed + +- `SPEC.md` §9 now names FLOP's yellow paper §10 as the document the future FLOP-network rail + binding satisfies, and says which of its requirements land on the rail adapter rather than on + tclk/1. The scope was already stated; the external document it points at was not, so its + absence read as an oversight. + ### Fixed - `tclk_post_frame` now accepts exact decimal-string nonces in addition to safe integer diff --git a/SPEC.md b/SPEC.md index 99e3e67..37009d3 100644 --- a/SPEC.md +++ b/SPEC.md @@ -462,7 +462,15 @@ among several rather than the only ones available. ## 9. What is deliberately not here (yet) - No FLOP-network rail binding (comes later; the interface and encodings are already - compatible), no multi-hop route construction over rooms (single contract per room today; the + compatible). Concretely, that binding is what FLOP's yellow paper §10 specifies, and its + requirements land on the rail adapter rather than on tclk/1: **R10.2**'s timelock margin is a + relationship between a rail's own `T_lock` and these deadlines, which only a rail that has a + `T_lock` can check — `validateDeadlines` deliberately supplies no universal default — and + **R10.5**'s "lock an estimated max, settle the actual at redemption" has no tclk/1 shape at + all, since `amount` is fixed on the offer and `contractId` commits to the offer wholesale. Both + are noted here because the absence of any reference to that document reads as an oversight + rather than as scope, and because a variable-settlement shape is a tclk/2 question (#57) rather + than a patch. No multi-hop route construction over rooms (single contract per room today; the AMHL witness algebra plugs in above these primitives when routing lands), no k-of-n secret sharing (§8.4 says why, and §8.1–8.3 are what to use meanwhile), no reputation or spend accounting (receipts From 4735c6f611b016c7fcc7e4a79f374890c13a4070 Mon Sep 17 00:00:00 2001 From: Stu Williams Date: Thu, 10 Sep 2026 23:31:14 +0100 Subject: [PATCH 2/4] docs(spec): stop claiming a FLOP point leaf the yellow paper does not have MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §5 said the flop-htlc escrow is opened with an `And[Hash(h), Before(T)]` or `Point(Y)` policy leaf, and that our statement encodings are byte-identical to that escrow's for both halves. Naming the source document in §9 is what made that checkable, and @Qwealzy checked it on this PR: the hash half holds and the point half has nothing behind it. Verified independently against https://flop.finance/intro/yellowpaper/, fetched at 200. §10 R10.1 fixes the lock as H = SHA256(s), redeemable by s before T_lock — a hash-and-timeout leaf, and our 32-byte sha256 digest for Hash is byte-identical to it. §6.1's declarative spend-condition layer lists its predicates as sig, hash, timelock and threshold. PTLC, adaptor, Point( and Schnorr occur zero times in the whole document. So the 33-byte SEC1-compressed point we encode for Point matches nothing published on the FLOP side. That is now stated as the encoding this library would use if such a leaf lands, rather than as a compatibility claim. The rail registry row drops "hash/point" to "hash" for the same reason, and §9's "the interface and encodings are already compatible" is narrowed to the hash lock. This describes the published spec, not the pallet. If a point leaf exists in the implementation and not in the document, the document is where the gap is and I would rather be told than guess. Docs only. check:protocol clean, 104 tests passing. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 7 +++++++ SPEC.md | 24 +++++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 901879a..928e38b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ All notable changes to this project are documented here. Format follows binding satisfies, and says which of its requirements land on the rail adapter rather than on tclk/1. The scope was already stated; the external document it points at was not, so its absence read as an oversight. +- `SPEC.md` §5 no longer describes the `flop-htlc` escrow as carrying a `Point(Y)` policy leaf. + The yellow paper's §10 specifies a hash-and-timeout lock and nothing else, and its §6.1 + predicate list is sig, hash, timelock and threshold, so the hash encoding is byte-identical as + claimed and the point encoding matches nothing published. It is now stated as the encoding this + library would use if such a leaf lands rather than as a compatibility claim, and the rail + registry row and §9 say the same. Naming the source document in §9 is what made the claim + checkable; @Qwealzy checked it. ### Fixed diff --git a/SPEC.md b/SPEC.md index 37009d3..57209b9 100644 --- a/SPEC.md +++ b/SPEC.md @@ -270,7 +270,7 @@ Rail IDs are protocol identifiers, not display labels. New emissions use this cl |---|---|---|---|---|---| | `btc-htlc` | Bitcoin Script/Taproot | fund an output bound to the statement and timeout | spend with the preimage or completed adaptor signature | take the timeout spend | yes | | `evm-htlc` | EVM escrow contract | deposit under the statement and deadline | release with the preimage/witness | execute the expired refund path | yes | -| `flop-htlc` | FLOP typed escrow | create an escrow with the matching hash/point and time policy | satisfy the hash/point release leaf | satisfy its refund-time leaf | yes | +| `flop-htlc` | FLOP typed escrow | create an escrow with the matching hash and time policy | satisfy the hash release leaf | satisfy its refund-time leaf | yes | | `memory` | process-local reference implementation | record `LockTerms` in memory | verify the secret and mark claimed | check the supplied clock and mark refunded | no durable or external value | | `near-htlc` | NEAR escrow contract | deposit under the statement and deadline | release with the preimage/witness | execute the expired refund path | yes | | `paper` | technocore CAS note | write a rehearsal record containing the terms | verify the secret and CAS the record to claimed | CAS an expired record to refunded | **no** | @@ -314,11 +314,15 @@ the lock/claim/refund predicates in-process — it is the executable spec of wha enforce and what the tests drive end-to-end. Real rails to bind later, none required by this layer: -- **`flop-htlc`** — the FLOP network's on-chain typed escrow, opened with an - `And[Hash(h), Before(T)]` or `Point(Y)` policy leaf. The statement encodings here are chosen to - be byte-identical to that escrow's: a 32-byte sha256 digest for `Hash`, a 33-byte - SEC1-compressed point for `Point`. Block deadlines derive from the ms deadlines with a - timelock-symmetry margin. +- **`flop-htlc`** — the FLOP network's on-chain typed escrow, whose published specification is + the yellow paper's §10 (§9). Its lock is a hash-and-timeout leaf — R10.1 fixes `H = SHA256(s)`, + redeemable by `s` before `T_lock` — and the 32-byte sha256 digest encoded here for `Hash` is + byte-identical to it. The point half has no counterpart in that document: §10 states no point + or discrete-log condition, and §6.1's declarative spend-condition layer lists its predicates as + sig, hash, timelock and threshold. The 33-byte SEC1-compressed point encoded here for `Point` + is therefore the encoding this library would use if such a leaf lands, not a statement that one + exists today. Block deadlines derive from the ms deadlines with a timelock-symmetry margin, + which is R10.2's requirement on the adapter. - **`x402`** — the lock statement rides the existing `X-Payment-Hash-Lock` / `X-Payment-Timeout-Blocks` headers; `ref` is the payment id. - **`evm-htlc` / `near-htlc` / BTC** — counterparty escrow contracts on other chains: an EVM @@ -461,9 +465,11 @@ among several rather than the only ones available. ## 9. What is deliberately not here (yet) -- No FLOP-network rail binding (comes later; the interface and encodings are already - compatible). Concretely, that binding is what FLOP's yellow paper §10 specifies, and its - requirements land on the rail adapter rather than on tclk/1: **R10.2**'s timelock margin is a +- No FLOP-network rail binding (comes later; the hash-lock interface and encodings are already + compatible, and §5 says where that stops — the point half this library encodes has no + counterpart in the document below). Concretely, that binding is what FLOP's yellow paper §10 + specifies, and its requirements land on the rail adapter rather than on tclk/1: **R10.2**'s + timelock margin is a relationship between a rail's own `T_lock` and these deadlines, which only a rail that has a `T_lock` can check — `validateDeadlines` deliberately supplies no universal default — and **R10.5**'s "lock an estimated max, settle the actual at redemption" has no tclk/1 shape at From 27d39030469217780863db6c00d4161deaa3e0e0 Mon Sep 17 00:00:00 2001 From: Stu Williams Date: Fri, 11 Sep 2026 04:05:38 +0100 Subject: [PATCH 3/4] docs(spec): state R10.2 as the parametric requirement it is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §9 described R10.2's margin as "a relationship between a rail's own T_lock and these deadlines". That is not what the requirement says. R10.2 is T_FLOP >= T_other + max(ceil(T_other × p / 100), max_finality_stall + current_finality_lag), so evaluating it needs the counter-chain's own duration and three live FLOP chain parameters — not a rail's T_lock alone. The conclusion is unchanged and stronger: nothing that cannot read chain state at lock time can check this, which is precisely why validateDeadlines supplies no universal default. The error originates in #132, where I block-quoted R10.2 as "T_FLOP > T_other with margin (2 h FLOP, 1 h counter-chain)" and called those concrete constants. The constants were mine. That issue body is corrected in place with the change marked, and @yonelpyc — who was reasoning from it — has the detail on the thread. Also sharpens the R10.5 note. "amount is fixed on the offer" understates it: amount occurs once in the entire frame schema, on OfferFields, and no lock, reveal, refund or receipt frame carries one. A settled actual cannot be expressed, or recorded after the fact as commentary. That is a stronger statement of why R10.5 has no tclk/1 shape, and it answers the schema question @yonelpyc asked. Docs only. check:protocol clean, 104 tests passing. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 6 ++++++ SPEC.md | 12 +++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 928e38b..4e90a95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,12 @@ All notable changes to this project are documented here. Format follows library would use if such a leaf lands rather than as a compatibility claim, and the rail registry row and §9 say the same. Naming the source document in §9 is what made the claim checkable; @Qwealzy checked it. +- `SPEC.md` §9 now states **R10.2**'s margin as parametric rather than as a relationship a rail + could check from its own `T_lock` alone: the requirement needs the counter-chain's duration and + three live FLOP chain parameters, so lock-time chain state is what evaluates it. The **R10.5** + note is sharpened from "`amount` is fixed on the offer" to the stronger fact — `amount` occurs + once in the whole frame schema and no post-offer frame carries one, so a settled actual cannot + be recorded even after the fact. ### Fixed diff --git a/SPEC.md b/SPEC.md index 57209b9..be7a01d 100644 --- a/SPEC.md +++ b/SPEC.md @@ -469,11 +469,13 @@ among several rather than the only ones available. compatible, and §5 says where that stops — the point half this library encodes has no counterpart in the document below). Concretely, that binding is what FLOP's yellow paper §10 specifies, and its requirements land on the rail adapter rather than on tclk/1: **R10.2**'s - timelock margin is a - relationship between a rail's own `T_lock` and these deadlines, which only a rail that has a - `T_lock` can check — `validateDeadlines` deliberately supplies no universal default — and - **R10.5**'s "lock an estimated max, settle the actual at redemption" has no tclk/1 shape at - all, since `amount` is fixed on the offer and `contractId` commits to the offer wholesale. Both + timelock margin is parametric rather than constant — it needs the counter-chain's own duration + and FLOP's live `htlc_timelock_symmetry_safety_margin_percent`, `max_finality_stall` and + `current_finality_lag` — so only something reading chain state at lock time can evaluate it, + which is why `validateDeadlines` deliberately supplies no universal default. **R10.5**'s "lock + an estimated max, settle the actual at redemption" has no tclk/1 shape at all: `amount` occurs + once in the frame schema, on the offer, and no `lock`, `reveal`, `refund` or `receipt` frame + carries one, so a settled actual cannot be expressed or even recorded after the fact. Both are noted here because the absence of any reference to that document reads as an oversight rather than as scope, and because a variable-settlement shape is a tclk/2 question (#57) rather than a patch. No multi-hop route construction over rooms (single contract per room today; the From 059d3d33ef038579e4f64521e7e8009f21a18972 Mon Sep 17 00:00:00 2001 From: Stu Williams Date: Sat, 10 Oct 2026 15:39:54 +0100 Subject: [PATCH 4/4] =?UTF-8?q?docs(spec):=20=C2=A78.6=20shouldn't=20claim?= =?UTF-8?q?=20what=20=C2=A75=20just=20refused=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @Minh3132's bug-hunter caught it: §5 (this branch, two commits back) corrected the point-leaf claim to "not a statement that one exists today", but §8.6 still said FLOP's typed escrow "is exactly" the hash/point/signature/time policy shape this section describes. Same contradiction §5 existed to fix, now in the other section pointing back at it. §8.6 says what §5 says now: a hash-and-timeout lock is what's published, the richer policy is where a future rail would plug in, not a capability FLOP's escrow already has. pnpm -r --include-workspace-root build && test: 177 passed. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 5 +++++ SPEC.md | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e90a95..b2cda4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ All notable changes to this project are documented here. Format follows note is sharpened from "`amount` is fixed on the offer" to the stronger fact — `amount` occurs once in the whole frame schema and no post-offer frame carries one, so a settled actual cannot be recorded even after the fact. +- `SPEC.md` §8.6 no longer claims FLOP's typed escrow "is exactly" the policy shape (hash, point, + signature and time combined with and/or/k-of-n) this section describes. §5's point-leaf + correction above left that claim standing in §8.6, contradicting it — @Minh3132 (bug-hunter) + caught the two sections asserting incompatible things about the same rail. §8.6 now says what + §5 says: a hash-and-timeout lock today, with the richer policy still hypothetical. ### Fixed diff --git a/SPEC.md b/SPEC.md index be7a01d..0d4545a 100644 --- a/SPEC.md +++ b/SPEC.md @@ -456,8 +456,11 @@ them without a vote. A rail whose escrow evaluates a *policy* — hash, point, signature and time conditions combined with and / or / k-of-n — moves the count from custody to enforcement: k named signers, checked by -the settlement layer, with no secret for anyone to hold or leak. The FLOP network's typed escrow -is exactly that shape, which is why §5 names it. +the settlement layer, with no secret for anyone to hold or leak. §5 is why this section exists at +all, not a claim that FLOP's published escrow already has this shape: today it evaluates a +hash-and-timeout lock, and the paper it's specified in carries none of the point, signature or +k-of-n conditions a policy rail would need. This section describes where that richer rail would +plug in once one exists, on FLOP's network or elsewhere. Nothing in §3–§5 changes when it lands. The frames, the ids and the state machine are identical; the rail simply gains a richer release condition, and the conventions above become one option