Skip to content

Remove npubcash kv-marker workaround in addExternalMintQuote once cashubtc/cdk#2335 ships a fix #4402

Description

@kaloudis

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions