Repository navigation
feat: V2 unpoisonable swaps and recovery (internal review) - #2
Closed
SatsAndSports wants to merge 6 commits into
Closed
SatsAndSports wants to merge 6 commits into
SatsAndSports wants to merge 6 commits into
Conversation
Owner
Author
|
Superseded by #3, which targets main directly and keeps ordinary SIG_ALL behavior unchanged. Review and follow-up discussion should continue there. |
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.
Purpose and review base
Internal review of the first working V2 unpoisonable swap implementation.
This PR targets
SatsAndSports/cdk:feat/sigall-v1, our copy of Rob Woodgate's length-framed SIG_ALL branch at8064df40. It builds on cashubtc/cdk#2232 and cashubtc/nuts#404, so the diff contains our additions rather than Rob's framing work.Proposal: Unpoisonable Blinded Outputs.
Implemented
Authorization and tweak derivation
unpoisonable: trueon the swap request; absent/false retains ordinary processing.SIG_ALLspending conditions are required. Ordinary swaps retain both accepted signature formats from Rob's PR; unpoisonable swaps accept only the length-framed digest taggedCashu_SigAllUnpoisonable_v1.R, then derive one shared nonzero scalartusing BIP-340-style tagged SHA-256 overR || uint32_be(counter), with tagCashu_UnpoisonableTweak_secp256k1_v1and rejection sampling starting at counter zero.B' = B + t*G. Reject duplicate original points using CDK's existing validation.Existing saga integration
R, up to 16 attempts. Input failures and other errors remain terminal.Rin one batched proof UPDATE, and record completion in the same transaction.Ris persisted during setup or exposed for pending inputs.Storage and recovery
proof.unpoisonable_rwith a 32-byte/spent-state constraint. Ordinary records retain NULL; the output/signature table is unchanged.Ris carried in memory until finalization. Incomplete swaps use existing compensation on recovery.unpoisonable_ras 64 hex characters. Absent fields are omitted.R; the FFI proof-state representation preserves it.Tests and validation
Implemented coverage includes:
Rfrom NUT-07, restores through NUT-09, verifies DLEQ against the tweaked pair, removes the additive tweak, preserves the unchanged DLEQ usingr + tas the effective blinding scalar, and spends the resulting ordinary proof.Rwhile pending or before finalization; signing failure; rollback after writingR; incomplete-saga cleanup; subscription snapshots.Checks passed during implementation:
Latest full database test runs: 143 SQLite and 135 PostgreSQL tests passed. After the additive-tweak update, 627 CDK library tests, 585 cashu unit tests, and 13 cashu doctests passed, with 2 pre-existing ignored cashu tests. PostgreSQL tests ran against a local disposable PostgreSQL 17 container. The storage-only commit was also checked independently in an isolated checkout.
Remaining work
Rand SPENT-with-Rfollows commit; current added subscription test exercises snapshots.Scope is V2 swaps. General-wallet opt-in/recovery UX, melts, and V3 support are future work. Tweaked DLEQ proofs are verified before recovery and can remain transferable NUT-12 proofs by recording
r + tas the effective V2 blinding scalar.The additive migration tolerates representative older named-column queries, but full downgrade testing with an older mint binary remains to be done. Older code cannot serve
Rfor recovery even when it tolerates the new column.Commit guide
0f18819a: authorization and initial rejection guard.ac7c284c: tweak derivation and preparation.1524e880: migrations and storage support.66b04aa0: executable saga path and recovery responses/tests (replaces the temporary guard).cb3caf78: batch the spent-state andRwrite together.14e25f36: switch V2 to one shared tagged-hash scalar and additive point tweaks.