feat(wallet): add offline ecash receiving with DLEQ verification#1931
feat(wallet): add offline ecash receiving with DLEQ verification#1931GEET3001 wants to merge 2 commits into
Conversation
…-12) Implements offline receiving capability for the CDK wallet: - Adds `PendingReceive` state to the `State` enum in cashu/nut07 - Adds `OfflineReceiveOptions` struct (min_locktime, allowed_mints, require_locked) - Adds `receive_offline()` method to the Wallet trait and core implementation: - Verifies DLEQ proofs on all tokens without going online - Validates locking conditions (locktime, mint restrictions, P2PK) - Stores verified proofs in `PendingReceive` state in the database - Adds `finalize_pending_receives()` to swap pending proofs with the mint - Adds DB migrations for SQLite and Postgres to recognise the new state - Updates FFI layer (cdk-ffi) to expose the new methods and ProofState variant - Handles `PendingReceive` in all exhaustive match sites (test utils, FFI)
TheMhv
left a comment
There was a problem hiding this comment.
You need to fix formatting and some errors that you can find running just final-check command.
Can you implement some integration test for this receive_offline function too.
And please, return the wallet tests
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { |
There was a problem hiding this comment.
Why you removed those tests?
| } | ||
|
|
||
| let keysets_info = self.load_mint_keysets().await?; | ||
| use cdk_common::ProofsMethods; |
There was a problem hiding this comment.
Where it is used? I think that can be removed
| } else { | ||
| return Err(Error::LocktimeNotProvided); | ||
| } | ||
| } else { | ||
| return Err(Error::LocktimeNotProvided); | ||
| } | ||
| } else { | ||
| return Err(Error::LocktimeNotProvided); | ||
| } |
There was a problem hiding this comment.
I think that those else's statement can be unified
|
|
||
| let proofs: Proofs = proofs_info.into_iter().map(|p| p.proof).collect(); | ||
|
|
||
| self.receive_proofs(proofs, ReceiveOptions::default(), None, None) |
There was a problem hiding this comment.
You was using a default and null values as a argument. This is correct?
| if opts.require_dleq { | ||
| ensure_cdk!(proof.dleq.is_some(), Error::DleqProofNotProvided); | ||
| } | ||
|
|
||
| if proof.dleq.is_some() { | ||
| let keys = self.load_keyset_keys(proof.keyset_id).await?; | ||
| let key = keys.amount_key(proof.amount).ok_or(Error::AmountKey)?; | ||
| proof.verify_dleq(key)?; | ||
| } | ||
|
|
There was a problem hiding this comment.
Since the DLEQ proof is aways required for offline reception, this verification needs to aways run on this function, those if's statements can be removed.
Closes #1927
Implements offline receiving capability for the CDK wallet:
PendingReceivestate to theStateenum in cashu/nut07OfflineReceiveOptionsstruct (min_locktime, allowed_mints, require_locked)receive_offline()method to the Wallet trait and core implementation:PendingReceivestate in the databasefinalize_pending_receives()to swap pending proofs with the mintPendingReceivein all exhaustive match sites (test utils, FFI)Description
Currently, receiving in the CDK wallet requires the wallet to be online to perform a swap. However, Cashu enables offline receiving pending DLEQ verification and locking conditions. This PR adds that capability to CDK.
A new
receive_offline()function accepts a token and anOfflineReceiveOptionsstruct containing:min_locktime— the minimum locktime required on the tokenallowed_mints— a list of mints the token must originate fromrequire_locked— whether the token must be P2PK lockedThe wallet verifies the token's DLEQ proof and checks these conditions entirely offline. Upon successful verification, the proofs are added to the database in the new
PendingReceivestate and recorded in the wallet's transaction list. Wallets should display these transactions with a warning indicating that they must be swapped for final settlement.A second function,
finalize_pending_receives(), checks for all proofs inPendingReceivestate and executes the swaps with the mint, converting them to fully spendable proofs. This should be called whenever the wallet comes back online.Notes to the reviewers
PendingReceiveis kept intentionally separate fromPending(used for in-flight send/melt) to avoid ambiguity in state machine transitions and to make it easy to display pending offline receives distinctly in wallet UIs.CHECK (state IN (...))constraint on theprooftable to includePENDING_RECEIVE, preventing constraint violations at runtime.cdk-ffi) is fully updated:ProofState::PendingReceiveis exposed as a UniFFI enum variant, and bothreceive_offlineandfinalize_pending_receivesare implemented inwallet_trait.rsand callable from mobile/desktop consumers.Suggested CHANGELOG Updates
ADDED
State::PendingReceive— new proof state for offline-received ecash awaiting final swapWallet::receive_offline(token, options)— verify a token's DLEQ proof offline and store proofs inPendingReceivestateWallet::finalize_pending_receives()— sweep allPendingReceiveproofs and swap them with the mintOfflineReceiveOptionsstruct withmin_locktime,allowed_mints, andrequire_lockedfieldsPENDING_RECEIVEto the proof state constraintcdk-ffiChecklist
just quick-checkbefore committingcrates/cdk-ffi)