feat: add p2pk_signing_keys to SendOptions#1750
Conversation
Add `p2pk_signing_keys: Vec<SecretKey>` to `SendOptions` so wallets holding P2PK-locked proofs can spend them through the standard `prepare_send` / `confirm` flow without manually calling `swap()`. - Extract `sign_proofs()` into `wallet/util.rs` with four unit tests - Refactor `receive/saga/mod.rs` to call `sign_proofs()` (no behaviour change) - Sign `proofs_to_swap` in `send/saga/mod.rs::confirm()` before swap - Add `p2pk_signing_keys` to cdk-ffi `SendOptions` struct and both `From` conversions (was previously hardcoded to `Vec::new()`) - Fix CHANGELOG `## Added` heading level to `### Added` - Add integration test `test_p2pk_send_options_signing_keys` covering the full prepare_send / confirm flow with P2PK-locked input proofs
… conditions When a wallet holds P2PK-locked proofs whose total exactly equals the requested send amount, prepare_send short-circuits: all proofs go to proofs_to_send and proofs_to_swap is empty. Since confirm only signs proofs_to_swap, signing is skipped and the locked proofs flow out in the token unchanged, causing the recipient to get NUT11 errors. Fix by shadowing force_swap to true in internal_prepare when p2pk_signing_keys is non-empty, ensuring all proofs go through a real swap that signs and unlocks them before building the token. Add a regression test covering the exact-denomination case.
|
I think this is partly getting fixed here: #1466 This would not be a complete fix but I think it's more or less where you want to take it. |
|
oh cool! |
|
Sorry this dropped off my radar.
This would make it so most of the time you don't need to provide the keys manually. But we should probably still allow it as suggested in this pr. |
| /// Regression test for the exact-denomination short-circuit bug in `p2pk_signing_keys`. | ||
| /// | ||
| /// When a wallet holds P2PK-locked proofs whose total exactly equals the requested | ||
| /// send amount, `prepare_send` takes a short-circuit path: all proofs go directly | ||
| /// to `proofs_to_send` (no swap needed), so `proofs_to_swap` is empty. | ||
| /// `confirm` only signs `proofs_to_swap`, so signing is skipped entirely and the | ||
| /// P2PK-locked proofs flow out in the token unchanged. | ||
| /// | ||
| /// Fix: when `p2pk_signing_keys` is non-empty, force any proofs in `proofs_to_send` | ||
| /// into `proofs_to_swap` so they are always signed and unlocked via a real swap. |
There was a problem hiding this comment.
I think we should address this. Sending proofs with a pubkey lock as well as their signature can have a negative privacy implication. So we should not just fall back to including it. This should be an explicit option to include tokens that have locks.
|
closed in favor of #1835 on latest main |
Summary
Wallets that hold P2PK-locked proofs (received from another party, or minted with spending conditions) currently have no way to spend them through the standard prepare_send / confirm flow — they must manually call swap() and handle signing themselves. This PR closes that gap.
Changes:
p2pk_signing_keys: Vec<SecretKey>toSendOptions(defaults to empty, fully backward-compatible)receive/saga/mod.rsinto a newsign_proofs()utility inwallet/util.rs, and call it from both the receive and send sagassign_proofs(&mut proofs_to_swap, &opts.p2pk_signing_keys)inPreparedSend::confirm()before the swap, so the mint accepts the spendSendOptionsstruct and bothFromconversions (matching the existingReceiveOptionspattern)Usage
Test plan
wallet/util.rs: correct key signs, wrong key is a no-op, plain proof is a no-op, empty key vec is a no-optest_p2pk_send_options_signing_keys: funds a wallet, swaps clean proofs for P2PK-locked proofs via the raw mint API, then exercises the full prepare_send / confirm flow with signing keys — verifies the recipient receives the correct amount