fix(htlc): evaluate claim/reclaim deadlines against a deterministic reference time - #2160
Open
AkramBitar wants to merge 1 commit into
Open
fix(htlc): evaluate claim/reclaim deadlines against a deterministic reference time#2160AkramBitar wants to merge 1 commit into
AkramBitar wants to merge 1 commit into
Conversation
AkramBitar
force-pushed
the
fix-1750-htlc-deterministic-deadline
branch
5 times, most recently
from
August 7, 2026 14:35
72310b0 to
d53f695
Compare
…eference time Signed-off-by: AkramBitar <akram@il.ibm.com>
AkramBitar
force-pushed
the
fix-1750-htlc-deterministic-deadline
branch
from
August 7, 2026 14:47
d53f695 to
d4d7450
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1750
The setup
An HTLC is a locked token with a deadline. Alice locks tokens for Bob:
This is how atomic swaps work: two parties trade tokens without trusting each other. The deadline is what makes it safe.
Example
The problem
Several peers independently validate each transaction, and each one was checking the deadline against its own clock.
Clocks are never perfectly in sync. So for a spend submitted near the deadline:
Same transaction, opposite verdicts. That breaks endorsement, and worse, it makes the deadline fuzzy in an uncontrolled way: a claim can slip through past the intended cutoff, or a claim and a reclaim can race.
A third clock read lived inside
htlc.Verifier, so the signature check and the owner check could even disagree on the same peer.htlc.Verifieralready had aClockFunchook for exactly this purpose — it had never been wired up.In shot peers used different local clocks to evaluate the HTLC deadline, so the same transaction could be accepted by one peer and rejected by another.
Example:
The fix
Stop asking "what time is it?" and start asking "what time does the transaction say it is?"
Every Fabric transaction carries a timestamp in its channel header, and that header is covered by the proposal signature. Every peer reads the identical value, and so does any later re-validation. The claim/reclaim decision becomes pure arithmetic on two signed numbers — no clock, so no disagreement is possible.
All three clock reads now use that one reference time, carried on the
context:fabtoken/zkatdlogTransferHTLCValidatehtlc.TypedIdentityDeserializer.DeserializeVerifierClockFunc)Injected at both validation entry points: the token chaincode from
stub.GetTxTimestamp(), the FSC endorsement responder from the proposal's channel header.Why the timestamp is bounded against the local clock
The timestamp is written by the client and the ordering service does not vet it. Trusted outright, it would hand the deadline to the client: back-date a proposal and an expired HTLC stays claimable forever.
So peers still glance at their own clock — not to decide the deadline, but to sanity-check that the claimed time is plausible. Outside the tolerance the request is rejected.
The two jobs are deliberately separated:
Choosing the tolerance
The tolerance is exactly the window an adversary can shift a deadline by. Back-date by just under it and an expired HTLC stays claimable that long; post-date and a reclaim becomes available that early. So it is kept short:
driver.DefaultValidationTimeSkew). NTP-synced nodes agree to within milliseconds, so this is ample headroom while keeping the window tight.validation.maxTimeSkewfor the FSC responder,TOKEN_VALIDATION_MAX_TIME_SKEWfor the standalone chaincode. That split mirrors the existingvalidation.limits/TOKEN_VALIDATION_MAX_*precedent, and for the same reason — the chaincode process only ever has public parameters in scope, never a TMS identifier.Atomic swaps already set their two deadlines hours apart by design, so a bounded 30s is far beneath the protocol's safety margin. The key change is that the fuzziness is now known, bounded, and agreed on by every peer, instead of unbounded and non-deterministic.
Considered and rejected
Examples
Testing
ClockFuncwiring: the deserialized verifier demands the recipient's signature before the deadline and the sender's after, driven by the injected time.FuzzChannelHeaderTimestampNoPanicover the added wire-format parsing, wired intonightly-fuzz.yml.make checksandmake lintclean; race detector clean on all touched packages.Docs
docs/security/htlc_deadline_determinism.md, linked fromdocs/services/interop.mdanddocs/README.md.docs/configuration.md: the newvalidation.maxTimeSkewkey, alongsidevalidation.limits.