Skip to content

refactor(abstract-utxo): offline vault applies the descriptor policy spend rules - #10000

Draft
OttoAllmendinger wants to merge 2 commits into
otto/SI-1971-pox5-hot-descriptor-spend-policyfrom
otto/SI-1971-offlinevault-policy-validate-spend
Draft

OttoAllmendinger wants to merge 2 commits into
otto/SI-1971-pox5-hot-descriptor-spend-policyfrom
otto/SI-1971-offlinevault-policy-validate-spend

Conversation

@OttoAllmendinger

@OttoAllmendinger OttoAllmendinger commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Description

Stacked on #9991 (base: its branch; retarget to master once #9987 and #9991 have merged). Two commits:

1. refactor(abstract-utxo): offline vault applies the descriptor policy spend rules (no behavior change)

#9991 made hot descriptor wallets check PoX-5 spends through DescriptorValidationPolicy.validateSpend. The offline vault still called assertPox5SpendPolicy directly in its half-sign step. Both paths now use the same entry point: the offline vault's descriptor policy is built in one function (getDescriptorPolicy) and getHalfSignedPsbt calls policy.validateSpend?.(psbt, descriptorMap). The offline vault sign step no longer names PoX-5.

2. refactor(abstract-utxo): tighten the PoX-5 early-exit policy (follow-ups from the review of #9987, #9987 (review))

  • "exactly one PSBT_IN_SHA256" vs "exactly one matching record": an early exit now requires exactly one PSBT_IN_SHA256 record on the lockup input, so an extra unrelated record is refused (before, getPox5PrincipalPreimage ignored non-matching records, so two records with one matching passed). The commitment check still applies to that one record.
  • classify and validate disagree: kept on purpose, now documented. classifyPox5Spend treats any PSBT_IN_SHA256 record as an early exit, so a stray record fails closed in the early-exit checks (for a late exit it hits the nLockTime 0 requirement). Classifying on "matches the descriptor commitment" would let a late exit carry an unrelated record through to signing.
  • assertPox5EarlyExitSpend only checks the descriptor match: documented on the shared function, pointing at the policy that adds the nLockTime and preimage checks.
  • redundant getMatchedTransactionInput and unreachable default:: removed the repeated descriptor-match check in the early-exit branch (classification has already done it), and the branch switch is exhaustive (never) instead of having an unreachable default.

The "live-tested" gate in the review stays as is: it needs the OVC beta bump, fixtures and sign-export coverage (offline-vault-console#1315), and real early-exit exports depend on SI-2002.

Issue Number

Refs: SI-1971

Type of change

  • Refactor / hardening, no change for correctly built exits

How Has This Been Tested?

npx mocha --recursive test/unit/transaction test/unit/signTransaction.ts test/unit/offlineVault (450 passing). New tests: an early exit with a second, unrelated PSBT_IN_SHA256 record is rejected, and a late exit carrying a stray PSBT_IN_SHA256 record is rejected. With the validateSpend call removed from getHalfSignedPsbt, 14 of the offline vault PoX-5 tests fail. modules/utxo-descriptors unit tests still pass (53); the only change there is a doc comment.

@linear-code

linear-code Bot commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

SI-1971

@OttoAllmendinger
OttoAllmendinger force-pushed the otto/SI-1971-offlinevault-policy-validate-spend branch from 91008c4 to fd1b93d Compare October 19, 2026 08:25
…spend rules

The offline vault half-sign step called assertPox5SpendPolicy directly while
the hot descriptor wallet applies the spend rules through the descriptor
policy (validateSpend). Build the offline vault descriptor policy in one place
and have getHalfSignedPsbt call policy.validateSpend, so both paths share one
entry point and the offline vault no longer names PoX-5 in its sign step.
No behavior change.

Refs: SI-1971
Follow-ups from the review of the early-exit policy:

- require exactly one PSBT_IN_SHA256 record on the early-exit input, not
  just exactly one that matches the descriptor commitment, so an unrelated
  extra record is refused too
- drop the redundant descriptor-match check in the early-exit branch
  (classifyPox5Spend has already done it) and make the branch switch
  exhaustive instead of having an unreachable default
- document that classifyPox5Spend treats any PSBT_IN_SHA256 record as an
  early exit on purpose, so a stray record in a late exit fails closed, and
  that the shared assertPox5EarlyExitSpend only checks the descriptor match
  and not nLockTime or the preimage

Refs: SI-1971
@OttoAllmendinger
OttoAllmendinger force-pushed the otto/SI-1971-offlinevault-policy-validate-spend branch from fd1b93d to f67a332 Compare October 19, 2026 08:26
@OttoAllmendinger
OttoAllmendinger added this pull request to stack #9993 October 19, 2026 08:34
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Unit tests are failing on Node 26.x (Current release line, non-blocking). This is not an LTS version yet, so it does not block merge, but it signals an incompatibility to fix before Node 26.x becomes LTS.

View run

This branch has not been deployed

No deployments
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.

1 participant