1use crate::error::{Result, SilentPaymentError};
4use crate::label::{Label, LabelManager};
5use secp256k1::{PublicKey, Secp256k1, SecretKey};
6use sha2::{Digest, Sha256};
7
8const SHARED_SECRET_TAG: &[u8] = b"BIP0352/SharedSecret";
10
11#[derive(Debug, Clone)]
13pub struct DetectedPayment {
14 pub output_pubkey: [u8; 32],
16 pub spending_key: [u8; 32],
18 pub output_index: usize,
20 pub label: Option<u32>,
22}
23
24pub struct SilentPaymentScanner {
26 scan_privkey: [u8; 32],
28 spend_privkey: [u8; 32],
30 spend_pubkey: [u8; 33],
32 labels: LabelManager,
34}
35
36impl SilentPaymentScanner {
37 pub fn new(scan_privkey: &[u8; 32], spend_privkey: &[u8; 32]) -> Result<Self> {
39 let secp = Secp256k1::new();
40
41 let spend_sk = SecretKey::from_slice(spend_privkey)
42 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
43 let spend_pk = PublicKey::from_secret_key(&secp, &spend_sk);
44
45 Ok(Self {
46 scan_privkey: *scan_privkey,
47 spend_privkey: *spend_privkey,
48 spend_pubkey: spend_pk.serialize(),
49 labels: LabelManager::new(),
50 })
51 }
52
53 pub fn add_label(&mut self, index: u32) {
55 self.labels.add(index);
56 }
57
58 pub fn add_labels(&mut self, count: u32) {
60 self.labels.generate_range(count);
61 }
62
63 pub fn scan(
70 &self,
71 output_pubkeys: &[[u8; 32]],
72 input_pubkeys: &[[u8; 33]],
73 outpoints: &[([u8; 32], u32)],
74 ) -> Result<Vec<DetectedPayment>> {
75 if input_pubkeys.is_empty() || outpoints.is_empty() {
76 return Ok(Vec::new());
77 }
78
79 let secp = Secp256k1::new();
80
81 let a_sum = sum_public_keys(input_pubkeys)?;
83
84 let input_hash = compute_input_hash(outpoints, &a_sum)?;
86
87 let a_sum_pk = PublicKey::from_slice(&a_sum)
89 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
90 let input_hash_sk = SecretKey::from_slice(&input_hash)
91 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
92 let tweaked_a = a_sum_pk
93 .mul_tweak(&secp, &input_hash_sk.into())
94 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
95
96 let b_scan = SecretKey::from_slice(&self.scan_privkey)
98 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
99 let shared_secret_point = tweaked_a
100 .mul_tweak(&secp, &b_scan.into())
101 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
102
103 let mut detected = Vec::new();
104
105 for (output_idx, output_pk) in output_pubkeys.iter().enumerate() {
107 for k in 0..100 {
109 let t_k = compute_output_tweak(&shared_secret_point.serialize(), k);
111
112 if let Some(payment) =
114 self.try_match_output(output_pk, &t_k, output_idx, None, &secp)?
115 {
116 detected.push(payment);
117 break;
118 }
119
120 let mut found_label = false;
122 for label in self.labels.labels() {
123 if let Some(payment) =
124 self.try_match_labeled_output(output_pk, &t_k, output_idx, label, &secp)?
125 {
126 detected.push(payment);
127 found_label = true;
128 break;
129 }
130 }
131
132 if found_label {
133 break;
134 }
135
136 if k > 0 {
138 break;
139 }
140 }
141 }
142
143 Ok(detected)
144 }
145
146 fn try_match_output(
148 &self,
149 output_pk: &[u8; 32],
150 t_k: &[u8; 32],
151 output_idx: usize,
152 label: Option<u32>,
153 secp: &Secp256k1<secp256k1::All>,
154 ) -> Result<Option<DetectedPayment>> {
155 let b_spend = PublicKey::from_slice(&self.spend_pubkey)
156 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
157
158 let t_k_sk = SecretKey::from_slice(t_k)
159 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
160 let t_k_point = PublicKey::from_secret_key(secp, &t_k_sk);
161
162 let expected_pk = b_spend
163 .combine(&t_k_point)
164 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
165
166 let (expected_xonly, _parity) = expected_pk.x_only_public_key();
167
168 if expected_xonly.serialize() == *output_pk {
169 let b_spend_sk = SecretKey::from_slice(&self.spend_privkey)
171 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
172 let spending_key = b_spend_sk
173 .add_tweak(&t_k_sk.into())
174 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
175
176 return Ok(Some(DetectedPayment {
177 output_pubkey: *output_pk,
178 spending_key: spending_key.secret_bytes(),
179 output_index: output_idx,
180 label,
181 }));
182 }
183
184 Ok(None)
185 }
186
187 fn try_match_labeled_output(
189 &self,
190 output_pk: &[u8; 32],
191 t_k: &[u8; 32],
192 output_idx: usize,
193 label: &Label,
194 secp: &Secp256k1<secp256k1::All>,
195 ) -> Result<Option<DetectedPayment>> {
196 let b_m = label.apply_to_pubkey(&self.spend_pubkey)?;
198
199 let b_m_pk = PublicKey::from_slice(&b_m)
200 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
201
202 let t_k_sk = SecretKey::from_slice(t_k)
203 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
204 let t_k_point = PublicKey::from_secret_key(secp, &t_k_sk);
205
206 let expected_pk = b_m_pk
207 .combine(&t_k_point)
208 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
209
210 let (expected_xonly, _parity) = expected_pk.x_only_public_key();
211
212 if expected_xonly.serialize() == *output_pk {
213 let b_m_sk = label.apply_to_privkey(&self.spend_privkey)?;
215 let b_m_secret = SecretKey::from_slice(&b_m_sk)
216 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
217 let spending_key = b_m_secret
218 .add_tweak(&t_k_sk.into())
219 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
220
221 return Ok(Some(DetectedPayment {
222 output_pubkey: *output_pk,
223 spending_key: spending_key.secret_bytes(),
224 output_index: output_idx,
225 label: Some(label.index()),
226 }));
227 }
228
229 Ok(None)
230 }
231}
232
233pub struct LightScanner {
235 scan_privkey: [u8; 32],
237 spend_pubkey: [u8; 33],
239}
240
241impl LightScanner {
242 pub fn new(scan_privkey: &[u8; 32], spend_pubkey: &[u8; 33]) -> Result<Self> {
244 SecretKey::from_slice(scan_privkey)
246 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
247 PublicKey::from_slice(spend_pubkey)
248 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
249
250 Ok(Self {
251 scan_privkey: *scan_privkey,
252 spend_pubkey: *spend_pubkey,
253 })
254 }
255
256 pub fn check_output(
258 &self,
259 output_pk: &[u8; 32],
260 input_pubkeys: &[[u8; 33]],
261 outpoints: &[([u8; 32], u32)],
262 ) -> Result<bool> {
263 if input_pubkeys.is_empty() || outpoints.is_empty() {
264 return Ok(false);
265 }
266
267 let secp = Secp256k1::new();
268
269 let a_sum = sum_public_keys(input_pubkeys)?;
270 let input_hash = compute_input_hash(outpoints, &a_sum)?;
271
272 let a_sum_pk = PublicKey::from_slice(&a_sum)
273 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
274 let input_hash_sk = SecretKey::from_slice(&input_hash)
275 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
276 let tweaked_a = a_sum_pk
277 .mul_tweak(&secp, &input_hash_sk.into())
278 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
279
280 let b_scan = SecretKey::from_slice(&self.scan_privkey)
281 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
282 let shared_secret_point = tweaked_a
283 .mul_tweak(&secp, &b_scan.into())
284 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
285
286 let t_0 = compute_output_tweak(&shared_secret_point.serialize(), 0);
287
288 let b_spend = PublicKey::from_slice(&self.spend_pubkey)
289 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
290 let t_0_sk = SecretKey::from_slice(&t_0)
291 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
292 let t_0_point = PublicKey::from_secret_key(&secp, &t_0_sk);
293
294 let expected_pk = b_spend
295 .combine(&t_0_point)
296 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
297
298 let (expected_xonly, _parity) = expected_pk.x_only_public_key();
299
300 Ok(expected_xonly.serialize() == *output_pk)
301 }
302}
303
304fn sum_public_keys(keys: &[[u8; 33]]) -> Result<[u8; 33]> {
306 if keys.is_empty() {
307 return Err(SilentPaymentError::NoInputs);
308 }
309
310 let mut sum = PublicKey::from_slice(&keys[0])
311 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
312
313 for key in &keys[1..] {
314 let pk = PublicKey::from_slice(key)
315 .map_err(|e| SilentPaymentError::InvalidPublicKey(e.to_string()))?;
316 sum = sum
317 .combine(&pk)
318 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
319 }
320
321 Ok(sum.serialize())
322}
323
324fn compute_input_hash(outpoints: &[([u8; 32], u32)], a_sum: &[u8; 33]) -> Result<[u8; 32]> {
326 let mut sorted: Vec<_> = outpoints.to_vec();
327 sorted.sort_by(|a, b| {
328 let cmp = a.0.cmp(&b.0);
329 if cmp == std::cmp::Ordering::Equal {
330 a.1.cmp(&b.1)
331 } else {
332 cmp
333 }
334 });
335
336 let mut hasher = Sha256::new();
337 hasher.update(sorted[0].0);
338 hasher.update(sorted[0].1.to_le_bytes());
339 hasher.update(a_sum);
340
341 let result = hasher.finalize();
342 let mut hash = [0u8; 32];
343 hash.copy_from_slice(&result);
344
345 Ok(hash)
346}
347
348fn compute_output_tweak(shared_secret: &[u8; 33], k: u32) -> [u8; 32] {
350 let tag_hash = Sha256::digest(SHARED_SECRET_TAG);
351
352 let mut hasher = Sha256::new();
353 hasher.update(tag_hash);
354 hasher.update(tag_hash);
355 hasher.update(shared_secret);
356 hasher.update(k.to_be_bytes());
357
358 let result = hasher.finalize();
359 let mut tweak = [0u8; 32];
360 tweak.copy_from_slice(&result);
361 tweak
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367 use crate::address::SilentPaymentAddress;
368 use crate::network::Network;
369 use crate::sender::create_outputs;
370 use rustywallet_keys::private_key::PrivateKey;
371
372 #[test]
373 fn test_scanner_creation() {
374 let scan_key = PrivateKey::random();
375 let spend_key = PrivateKey::random();
376
377 let scanner =
378 SilentPaymentScanner::new(&scan_key.to_bytes(), &spend_key.to_bytes()).unwrap();
379
380 assert!(scanner.labels.labels().is_empty());
381 }
382
383 #[test]
384 fn test_light_scanner() {
385 let scan_key = PrivateKey::random();
386 let spend_key = PrivateKey::random();
387
388 let scanner = LightScanner::new(
389 &scan_key.to_bytes(),
390 &spend_key.public_key().to_compressed().try_into().unwrap(),
391 )
392 .unwrap();
393
394 assert_eq!(scanner.spend_pubkey.len(), 33);
396 }
397
398 #[test]
399 fn test_end_to_end_payment() {
400 let sender_key = PrivateKey::random();
402 let sender_pubkey: [u8; 33] = sender_key
403 .public_key()
404 .to_compressed()
405 .try_into()
406 .unwrap();
407
408 let scan_key = PrivateKey::random();
410 let spend_key = PrivateKey::random();
411
412 let sp_address = SilentPaymentAddress::new(
413 &scan_key.public_key(),
414 &spend_key.public_key(),
415 Network::Mainnet,
416 )
417 .unwrap();
418
419 let outpoints = vec![([1u8; 32], 0u32)];
421 let outputs =
422 create_outputs(&[sender_key.to_bytes()], &outpoints, &[sp_address]).unwrap();
423
424 assert_eq!(outputs.len(), 1);
425
426 let scanner =
428 SilentPaymentScanner::new(&scan_key.to_bytes(), &spend_key.to_bytes()).unwrap();
429
430 let detected = scanner
431 .scan(&[outputs[0].output_pubkey], &[sender_pubkey], &outpoints)
432 .unwrap();
433
434 assert_eq!(detected.len(), 1);
435 assert_eq!(detected[0].output_pubkey, outputs[0].output_pubkey);
436 assert!(detected[0].label.is_none());
437
438 let secp = Secp256k1::new();
440 let spending_sk = SecretKey::from_slice(&detected[0].spending_key).unwrap();
441 let spending_pk = PublicKey::from_secret_key(&secp, &spending_sk);
442 let (xonly, _) = spending_pk.x_only_public_key();
443
444 assert_eq!(xonly.serialize(), outputs[0].output_pubkey);
445 }
446}