rustywallet_silent/
lib.rs

1//! # rustywallet-silent
2//!
3//! Silent Payments (BIP352) implementation for rustywallet.
4//!
5//! Silent Payments allow receivers to publish a static address that senders
6//! can use to derive unique output addresses, providing privacy without
7//! requiring interaction between sender and receiver.
8//!
9//! ## Features
10//!
11//! - **Address Generation**: Create Silent Payment addresses with scan and spend keys
12//! - **Sending**: Derive unique output addresses for recipients
13//! - **Scanning**: Detect incoming payments using scan key
14//! - **Labels**: Support multiple addresses from a single Silent Payment address
15//! - **Change Handling**: Generate deterministic change outputs
16//!
17//! ## Quick Start
18//!
19//! ### Creating a Silent Payment Address
20//!
21//! ```rust
22//! use rustywallet_silent::prelude::*;
23//! use rustywallet_keys::private_key::PrivateKey;
24//!
25//! // Generate keys
26//! let scan_key = PrivateKey::random();
27//! let spend_key = PrivateKey::random();
28//!
29//! // Create address
30//! let address = SilentPaymentAddress::new(
31//!     &scan_key.public_key(),
32//!     &spend_key.public_key(),
33//!     Network::Mainnet,
34//! ).unwrap();
35//!
36//! println!("Address: {}", address);
37//! ```
38//!
39//! ### Sending a Payment
40//!
41//! ```rust
42//! use rustywallet_silent::prelude::*;
43//! use rustywallet_keys::private_key::PrivateKey;
44//!
45//! // Sender's input key
46//! let sender_key = PrivateKey::random();
47//!
48//! // Recipient's address (normally parsed from string)
49//! let scan_key = PrivateKey::random();
50//! let spend_key = PrivateKey::random();
51//! let recipient = SilentPaymentAddress::new(
52//!     &scan_key.public_key(),
53//!     &spend_key.public_key(),
54//!     Network::Mainnet,
55//! ).unwrap();
56//!
57//! // Create outputs
58//! let outpoints = vec![([0u8; 32], 0u32)]; // txid, vout
59//! let outputs = create_outputs(
60//!     &[sender_key.to_bytes()],
61//!     &outpoints,
62//!     &[recipient],
63//! ).unwrap();
64//!
65//! // Use outputs[0].output_pubkey as the taproot output key
66//! ```
67//!
68//! ### Scanning for Payments
69//!
70//! ```rust
71//! use rustywallet_silent::prelude::*;
72//! use rustywallet_keys::private_key::PrivateKey;
73//!
74//! // Receiver's keys
75//! let scan_key = PrivateKey::random();
76//! let spend_key = PrivateKey::random();
77//!
78//! // Create scanner
79//! let scanner = SilentPaymentScanner::new(
80//!     &scan_key.to_bytes(),
81//!     &spend_key.to_bytes(),
82//! ).unwrap();
83//!
84//! // Scan transaction outputs
85//! // let detected = scanner.scan(&output_pubkeys, &input_pubkeys, &outpoints).unwrap();
86//! ```
87//!
88//! ## BIP352 Compliance
89//!
90//! This implementation follows BIP352 specification:
91//! - Bech32m encoding with `sp` (mainnet) and `tsp` (testnet) prefixes
92//! - ECDH-based shared secret derivation
93//! - Tagged hashing for domain separation
94//! - Support for labeled addresses
95//!
96//! ## Security Considerations
97//!
98//! - Keep scan and spend private keys secure
99//! - Scan key can be shared with a light client for detection
100//! - Spend key is required to actually spend received funds
101//! - Labels provide address separation without additional key material
102
103pub mod address;
104pub mod change;
105pub mod error;
106pub mod label;
107pub mod network;
108pub mod prelude;
109pub mod scanner;
110pub mod sender;
111
112pub use address::SilentPaymentAddress;
113pub use change::ChangeAddressGenerator;
114pub use error::{Result, SilentPaymentError};
115pub use label::{Label, LabelManager};
116pub use network::Network;
117pub use scanner::{DetectedPayment, LightScanner, SilentPaymentScanner};
118pub use sender::{create_multiple_outputs, create_outputs, SilentPaymentOutput};
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use rustywallet_keys::private_key::PrivateKey;
124
125    #[test]
126    fn test_full_workflow() {
127        // === Receiver Setup ===
128        let scan_key = PrivateKey::random();
129        let spend_key = PrivateKey::random();
130
131        let sp_address = SilentPaymentAddress::new(
132            &scan_key.public_key(),
133            &spend_key.public_key(),
134            Network::Mainnet,
135        )
136        .unwrap();
137
138        // Encode and share address
139        let encoded = sp_address.encode().unwrap();
140        assert!(encoded.starts_with("sp1"));
141
142        // === Sender Creates Payment ===
143        let sender_key = PrivateKey::random();
144        let sender_pubkey: [u8; 33] = sender_key
145            .public_key()
146            .to_compressed()
147            .try_into()
148            .unwrap();
149
150        let outpoints = vec![([1u8; 32], 0u32)];
151
152        // Parse recipient address
153        let recipient: SilentPaymentAddress = encoded.parse().unwrap();
154
155        // Create output
156        let outputs = create_outputs(
157            &[sender_key.to_bytes()],
158            &outpoints,
159            &[recipient],
160        )
161        .unwrap();
162
163        assert_eq!(outputs.len(), 1);
164
165        // === Receiver Scans ===
166        let scanner = SilentPaymentScanner::new(
167            &scan_key.to_bytes(),
168            &spend_key.to_bytes(),
169        )
170        .unwrap();
171
172        let detected = scanner
173            .scan(&[outputs[0].output_pubkey], &[sender_pubkey], &outpoints)
174            .unwrap();
175
176        assert_eq!(detected.len(), 1);
177
178        // Verify spending key
179        let secp = secp256k1::Secp256k1::new();
180        let sk = secp256k1::SecretKey::from_slice(&detected[0].spending_key).unwrap();
181        let pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
182        let (xonly, _) = pk.x_only_public_key();
183
184        assert_eq!(xonly.serialize(), outputs[0].output_pubkey);
185    }
186
187    #[test]
188    fn test_labeled_payment() {
189        // Receiver with labels
190        let scan_key = PrivateKey::random();
191        let spend_key = PrivateKey::random();
192
193        let mut scanner = SilentPaymentScanner::new(
194            &scan_key.to_bytes(),
195            &spend_key.to_bytes(),
196        )
197        .unwrap();
198
199        // Add labels
200        scanner.add_labels(5);
201
202        // Create labeled address
203        let label = Label::new(2);
204        let labeled_spend = label
205            .apply_to_pubkey(
206                &spend_key
207                    .public_key()
208                    .to_compressed()
209                    .try_into()
210                    .unwrap(),
211            )
212            .unwrap();
213
214        let labeled_spend_pk = secp256k1::PublicKey::from_slice(&labeled_spend).unwrap();
215        let labeled_address = SilentPaymentAddress::from_bytes(
216            scan_key
217                .public_key()
218                .to_compressed()
219                .try_into()
220                .unwrap(),
221            labeled_spend_pk.serialize(),
222            Network::Mainnet,
223        )
224        .unwrap();
225
226        // Sender pays to labeled address
227        let sender_key = PrivateKey::random();
228        let sender_pubkey: [u8; 33] = sender_key
229            .public_key()
230            .to_compressed()
231            .try_into()
232            .unwrap();
233
234        let outpoints = vec![([2u8; 32], 0u32)];
235
236        let outputs = create_outputs(
237            &[sender_key.to_bytes()],
238            &outpoints,
239            &[labeled_address],
240        )
241        .unwrap();
242
243        // Scan should detect with label
244        let detected = scanner
245            .scan(&[outputs[0].output_pubkey], &[sender_pubkey], &outpoints)
246            .unwrap();
247
248        assert_eq!(detected.len(), 1);
249        assert_eq!(detected[0].label, Some(2));
250    }
251
252    #[test]
253    fn test_multiple_outputs() {
254        let scan_key = PrivateKey::random();
255        let spend_key = PrivateKey::random();
256
257        let recipient = SilentPaymentAddress::new(
258            &scan_key.public_key(),
259            &spend_key.public_key(),
260            Network::Mainnet,
261        )
262        .unwrap();
263
264        let sender_key = PrivateKey::random();
265        let outpoints = vec![([3u8; 32], 0u32)];
266
267        // Create 3 outputs to same recipient
268        let outputs = create_multiple_outputs(
269            &[sender_key.to_bytes()],
270            &outpoints,
271            &recipient,
272            3,
273        )
274        .unwrap();
275
276        assert_eq!(outputs.len(), 3);
277
278        // All outputs should be unique
279        let mut pubkeys: Vec<_> = outputs.iter().map(|o| o.output_pubkey).collect();
280        pubkeys.sort();
281        pubkeys.dedup();
282        assert_eq!(pubkeys.len(), 3);
283    }
284
285    #[test]
286    fn test_change_address() {
287        let scan_key = PrivateKey::random();
288        let spend_key = PrivateKey::random();
289
290        let generator = ChangeAddressGenerator::new(
291            &scan_key.to_bytes(),
292            &spend_key.to_bytes(),
293            Network::Mainnet,
294        )
295        .unwrap();
296
297        let outpoints = vec![([4u8; 32], 0u32)];
298
299        // Generate change
300        let (change_pk, spending_key) = generator.generate_change(&outpoints, 0).unwrap();
301
302        // Verify it's a valid change output
303        let result = generator
304            .is_change_output(&change_pk, &outpoints, 5)
305            .unwrap();
306
307        assert!(result.is_some());
308        let (index, recovered_key) = result.unwrap();
309        assert_eq!(index, 0);
310        assert_eq!(recovered_key, spending_key);
311    }
312}