Skip to content

NUT-20: deterministic quote locking key derivation - #2

Merged
d4rp4t merged 1 commit into
feat/deterministic-p2pkfrom
feat/nut20-quote-key-derivation
Aug 2, 2026
Merged

NUT-20: deterministic quote locking key derivation#2
d4rp4t merged 1 commit into
feat/deterministic-p2pkfrom
feat/nut20-quote-key-derivation

Conversation

@d4rp4t

@d4rp4t d4rp4t commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Stacked on feat/deterministic-p2pk (upstream PR Kukks#33), so the diff here is the one commit on top of it. Retarget to master once Kukks#33 lands.

Adds the derivation from cashubtc/nuts#373:

m/129373'/20'/0'/0'/{counter}

Verified against the vectors in tests/20-test.md (same mnemonic as the NUT-13 ones), plus a test that quote keys and P2PK keys never collide, since they differ only in the account index.

The counter is its own DerivationPurpose.MintQuoteLock, which the spec calls out as independent from the NUT-13 keyset counters.

Why it matters

Today a NUT-20 lock uses a key the caller generates and has to store somewhere. If it is lost, so is any quote that was locked but not yet minted — the ecash cannot be claimed. Deriving from the seed makes those quotes recoverable on restore.

WithDeterministicPubkey() locks the quote to the derived key and the mint handler signs the request itself, so neither WithPubkey nor SignWithPrivkey is needed:

var quote = await wallet
    .CreateMintQuote()
    .WithDeterministicPubkey()
    .WithAmount(1337)
    .ProcessAsyncBolt12();

var proofs = await quote.Mint();   // signed with the derived key

Requires a mnemonic and a counter implementing IDerivationCounter, both from the base branch.


Unit tests: 116 passing. The integration test mints bolt12 end to end against cdk-mintd 0.17.3, checking that the pubkey the mint received is the derived one and that the counter advanced.

Adds the m/129373'/20'/0'/0'/{counter} derivation from cashubtc/nuts#373,
covered by the vectors in tests/20-test.md. The counter is its own
DerivationPurpose, which the spec requires to be independent of the NUT-13
keyset counters.

WithDeterministicPubkey locks a quote to the derived key instead of one the
caller has to keep, and the handler then signs the mint request itself. The
point is recovery: a quote that was locked but not yet minted is otherwise
lost with the key.
@d4rp4t
d4rp4t merged commit 5738a1a into feat/deterministic-p2pk Aug 2, 2026
2 checks passed
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