Context
PR #4397 (CDK 0.17.3 bump) contains a deliberate workaround in addExternalMintQuote on both platforms:
ios/CashuDevKit/CashuDevKitModule.swift
android/app/src/main/java/com/zeus/cashudevkit/CashuDevKitModule.kt
ZEUS Pay locks external mint quotes to the wallet's seed-prefix key (seed[0..32]). cdk's npubcash feature treats any stored quote key equal to seed[0..32] as a legacy NpubCash key and scrubs it from the quote record mid-mint-saga. The scrub bumps the quote row's optimistic-lock version, so the saga's post-mint write fails with Cdk(50000, "Concurrent update detected") after the mint has already issued the blind signatures. Locally no proofs or transaction are recorded and the funds are only recoverable via NUT-13 restore. This was deterministic on every lightning address redemption.
Upstream bug: cashubtc/cdk#2335
The workaround
Instead of storing the P2PK secret key on the quote, addExternalMintQuote stores the quote with no key and pre-writes cdk's own NpubCash quote-key marker:
kvWrite(primaryNamespace: "npubcash", secondaryNamespace: "quotes", key: quoteId, value: "legacy-seed-prefix")
At signing time cdk resolves the marker and re-derives the identical seed-prefix key (derive_legacy_npubcash_secret_key is SecretKey::from_slice(&seed[..32])), so the NUT-20 signature is unchanged and no mid-saga quote write occurs.
Why this needs tracking
The workaround couples ZEUS to cdk internals that are pub(crate): the npubcash/quotes kv namespaces and the legacy-seed-prefix marker bytes. If a future cdk release renames the namespace, changes the marker, or reworks the NpubCash quote-key resolution, ZEUS Pay redemptions will break again, likely silently and with the same stranded-funds failure mode.
Exit criteria
Related hardening (optional, can split out)
cdk 0.15+ provides Wallet::recoverIncompleteSagas() to complete or roll back interrupted operations (crash mid-mint/melt leaves proofs reserved or unpersisted). ZEUS never calls it. Calling it non-fatally at wallet initialization would self-heal states like the one this bug produced.
Context
PR #4397 (CDK 0.17.3 bump) contains a deliberate workaround in
addExternalMintQuoteon both platforms:ios/CashuDevKit/CashuDevKitModule.swiftandroid/app/src/main/java/com/zeus/cashudevkit/CashuDevKitModule.ktZEUS Pay locks external mint quotes to the wallet's seed-prefix key (
seed[0..32]). cdk'snpubcashfeature treats any stored quote key equal toseed[0..32]as a legacy NpubCash key and scrubs it from the quote record mid-mint-saga. The scrub bumps the quote row's optimistic-lock version, so the saga's post-mint write fails withCdk(50000, "Concurrent update detected")after the mint has already issued the blind signatures. Locally no proofs or transaction are recorded and the funds are only recoverable via NUT-13 restore. This was deterministic on every lightning address redemption.Upstream bug: cashubtc/cdk#2335
The workaround
Instead of storing the P2PK secret key on the quote,
addExternalMintQuotestores the quote with no key and pre-writes cdk's own NpubCash quote-key marker:At signing time cdk resolves the marker and re-derives the identical seed-prefix key (
derive_legacy_npubcash_secret_keyisSecretKey::from_slice(&seed[..32])), so the NUT-20 signature is unchanged and no mid-saga quote write occurs.Why this needs tracking
The workaround couples ZEUS to cdk internals that are
pub(crate): thenpubcash/quoteskv namespaces and thelegacy-seed-prefixmarker bytes. If a future cdk release renames the namespace, changes the marker, or reworks the NpubCash quote-key resolution, ZEUS Pay redemptions will break again, likely silently and with the same stranded-funds failure mode.Exit criteria
secretKeyon the quote again inaddExternalMintQuoteand drop thekvWritemarker call on both platformsRelated hardening (optional, can split out)
cdk 0.15+ provides
Wallet::recoverIncompleteSagas()to complete or roll back interrupted operations (crash mid-mint/melt leaves proofs reserved or unpersisted). ZEUS never calls it. Calling it non-fatally at wallet initialization would self-heal states like the one this bug produced.