Skip to content

fix: unify validity-range semantics across Linear Vesting and Two-Party Escrow#175

Open
Unisay wants to merge 1 commit intomainfrom
yura/unify-validity-range-semantics
Open

fix: unify validity-range semantics across Linear Vesting and Two-Party Escrow#175
Unisay wants to merge 1 commit intomainfrom
yura/unify-validity-range-semantics

Conversation

@Unisay
Copy link
Copy Markdown
Collaborator

@Unisay Unisay commented Apr 27, 2026

Context

Approach

HTLC (#170) established a two-helper pattern: lowerBoundTime for "after deadline" checks and upperBoundTime for "before deadline" checks. Both helpers:

  1. Handle inclusive/exclusive bounds correctly (adjusting by ±1 millisecond).
  2. Call traceError on infinite or missing bounds, preventing malformed ranges from bypassing time checks.

Linear Vesting only has "after deadline" checks (PartialUnlock, FullUnlock), so lowerBoundTime was already the right choice. The fix is purely a type-level cleanup: the helper now returns POSIXTime instead of Integer, and upperBoundTime is added for API parity.

Two-Party Escrow needed a security fix: the deposit validator was extracting time from the lower bound without validating infinity or the inclusive/exclusive flag. The refund validator used from refundDeadline contains nowRange (≥ semantics) with a succ hack. Both are replaced with the canonical helpers and a direct lessThanEqualsInteger comparison.

Changes

lib/LinearVesting.hs

  • lowerBoundTime returns POSIXTime (was Integer); error message updated
  • Added upperBoundTime (unused but present for API consistency)
  • Both validators destructure the result as POSIXTime currentTime = lowerBoundTime ...

lib/TwoPartyEscrow.hs

  • Added lowerBoundTime / upperBoundTime (verbatim from HTLC)
  • invalidDepositDatum: records depositTime = upperBoundTime validRange (rejects +∞)
  • validateRefund: replaced succ/contains with lowerBoundTime + lessThanEqualsInteger
  • Removed import PlutusLedgerApi.V1.Data.Interval (no longer needed)

scenarios/*/cape-tests.json

  • two_party_escrow: successful_deposit changed to point interval [1000, 1000]; new tests deposit_infinite_upper_bound and refund_infinite_lower_bound
  • linear_vesting: new tests partial_unlock_infinite_lower_bound and full_unlock_infinite_lower_bound

scenarios/*/scenario.md

  • Updated time-check descriptions to reference the correct bound
  • Added "Note on time semantics" paragraph (matching HTLC style)
  • Fixed typo in two_party_escrow.md: "Refund Valid Time: 1801+" → "2801+" (1000 + 1800 = 2800)

test/LinearVestingSpec.hs / test/TwoPartyEscrowSpec.hs

  • New it blocks covering all four new failure paths

Submissions

  • Recompiled and remeasured Plinth 1.45.0.0 and 1.61.0.0 for both scenarios

Author's Checklist

  • Self-reviewed code
  • All 278 Hspec tests pass
  • cape submission verify passes for all four touched submissions
  • New cape-tests cover the attack vectors fixed in this PR
  • Scenario documentation updated to match implementation

…ty Escrow

Migrates both scenarios to the same production-safe convention introduced
for HTLC in #170: "before deadline" checks read the upper bound; "after
deadline" checks read the lower bound. Both helpers reject infinite/missing
bounds with a traceError.

Key changes:
- LinearVesting: lowerBoundTime now returns POSIXTime (was Integer); adds
  upperBoundTime for API parity; both validators use lowerBoundTime.
- TwoPartyEscrow: deposit now records depositTime via upperBoundTime so an
  attacker cannot pass validRange=[0,+∞) to force depositTime=0; refund
  uses lowerBoundTime with a strict > check (removes succ + contains).
- cape-tests.json: point-interval fixtures for deposit; new coverage tests
  for infinite-bound rejection in both scenarios.
- Hspec: matching tests for all new failure paths.
- Recompile and remeasure Plinth 1.45 and 1.61 submissions.

Closes #171
@github-actions
Copy link
Copy Markdown
Contributor

🚀 PR Preview Deployed

Preview URL: https://intersectmbo.github.io/UPLC-CAPE/pr-175/

The preview site is automatically updated on every push to this PR and will be removed when the PR is closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unify validity-range semantics across Linear Vesting and Two-Party Escrow scenarios (post-HTLC follow-up)

1 participant