rustywallet_silent/
change.rs1use crate::address::SilentPaymentAddress;
4use crate::error::{Result, SilentPaymentError};
5use crate::network::Network;
6use secp256k1::{PublicKey, Secp256k1, SecretKey};
7use sha2::{Digest, Sha256};
8
9const CHANGE_TAG: &[u8] = b"BIP0352/Change";
11
12pub struct ChangeAddressGenerator {
18 scan_privkey: [u8; 32],
20 spend_privkey: [u8; 32],
22 network: Network,
24}
25
26impl ChangeAddressGenerator {
27 pub fn new(
29 scan_privkey: &[u8; 32],
30 spend_privkey: &[u8; 32],
31 network: Network,
32 ) -> Result<Self> {
33 SecretKey::from_slice(scan_privkey)
35 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
36 SecretKey::from_slice(spend_privkey)
37 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
38
39 Ok(Self {
40 scan_privkey: *scan_privkey,
41 spend_privkey: *spend_privkey,
42 network,
43 })
44 }
45
46 pub fn address(&self) -> Result<SilentPaymentAddress> {
48 let secp = Secp256k1::new();
49
50 let scan_sk = SecretKey::from_slice(&self.scan_privkey)
51 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
52 let spend_sk = SecretKey::from_slice(&self.spend_privkey)
53 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
54
55 let scan_pk = PublicKey::from_secret_key(&secp, &scan_sk);
56 let spend_pk = PublicKey::from_secret_key(&secp, &spend_sk);
57
58 SilentPaymentAddress::from_bytes(scan_pk.serialize(), spend_pk.serialize(), self.network)
59 }
60
61 pub fn generate_change(
70 &self,
71 outpoints: &[([u8; 32], u32)],
72 index: u32,
73 ) -> Result<([u8; 32], [u8; 32])> {
74 if outpoints.is_empty() {
75 return Err(SilentPaymentError::NoInputs);
76 }
77
78 let secp = Secp256k1::new();
79
80 let tweak = self.compute_change_tweak(outpoints, index)?;
82
83 let spend_sk = SecretKey::from_slice(&self.spend_privkey)
85 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
86 let spend_pk = PublicKey::from_secret_key(&secp, &spend_sk);
87
88 let tweak_sk = SecretKey::from_slice(&tweak)
89 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
90 let tweak_point = PublicKey::from_secret_key(&secp, &tweak_sk);
91
92 let output_pk = spend_pk
93 .combine(&tweak_point)
94 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
95
96 let (xonly, _parity) = output_pk.x_only_public_key();
97
98 let spending_key = spend_sk
100 .add_tweak(&tweak_sk.into())
101 .map_err(|e| SilentPaymentError::CryptoError(e.to_string()))?;
102
103 Ok((xonly.serialize(), spending_key.secret_bytes()))
104 }
105
106 fn compute_change_tweak(&self, outpoints: &[([u8; 32], u32)], index: u32) -> Result<[u8; 32]> {
108 let secp = Secp256k1::new();
109
110 let mut sorted: Vec<_> = outpoints.to_vec();
112 sorted.sort_by(|a, b| {
113 let cmp = a.0.cmp(&b.0);
114 if cmp == std::cmp::Ordering::Equal {
115 a.1.cmp(&b.1)
116 } else {
117 cmp
118 }
119 });
120
121 let scan_sk = SecretKey::from_slice(&self.scan_privkey)
123 .map_err(|e| SilentPaymentError::InvalidPrivateKey(e.to_string()))?;
124 let scan_pk = PublicKey::from_secret_key(&secp, &scan_sk);
125
126 let tag_hash = Sha256::digest(CHANGE_TAG);
128
129 let mut hasher = Sha256::new();
130 hasher.update(tag_hash);
131 hasher.update(tag_hash);
132 hasher.update(sorted[0].0);
133 hasher.update(sorted[0].1.to_le_bytes());
134 hasher.update(scan_pk.serialize());
135 hasher.update(index.to_be_bytes());
136
137 let result = hasher.finalize();
138 let mut tweak = [0u8; 32];
139 tweak.copy_from_slice(&result);
140
141 Ok(tweak)
142 }
143
144 pub fn is_change_output(
146 &self,
147 output_pk: &[u8; 32],
148 outpoints: &[([u8; 32], u32)],
149 max_index: u32,
150 ) -> Result<Option<(u32, [u8; 32])>> {
151 for index in 0..max_index {
152 let (expected_pk, spending_key) = self.generate_change(outpoints, index)?;
153 if &expected_pk == output_pk {
154 return Ok(Some((index, spending_key)));
155 }
156 }
157 Ok(None)
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use rustywallet_keys::private_key::PrivateKey;
165
166 #[test]
167 fn test_change_generator_creation() {
168 let scan_key = PrivateKey::random();
169 let spend_key = PrivateKey::random();
170
171 let generator = ChangeAddressGenerator::new(
172 &scan_key.to_bytes(),
173 &spend_key.to_bytes(),
174 Network::Mainnet,
175 )
176 .unwrap();
177
178 let addr = generator.address().unwrap();
179 assert_eq!(addr.network(), Network::Mainnet);
180 }
181
182 #[test]
183 fn test_generate_change() {
184 let scan_key = PrivateKey::random();
185 let spend_key = PrivateKey::random();
186
187 let generator = ChangeAddressGenerator::new(
188 &scan_key.to_bytes(),
189 &spend_key.to_bytes(),
190 Network::Mainnet,
191 )
192 .unwrap();
193
194 let outpoints = vec![([1u8; 32], 0u32)];
195 let (output_pk, spending_key) = generator.generate_change(&outpoints, 0).unwrap();
196
197 let secp = Secp256k1::new();
199 let sk = SecretKey::from_slice(&spending_key).unwrap();
200 let pk = PublicKey::from_secret_key(&secp, &sk);
201 let (xonly, _) = pk.x_only_public_key();
202
203 assert_eq!(xonly.serialize(), output_pk);
204 }
205
206 #[test]
207 fn test_change_deterministic() {
208 let scan_key = PrivateKey::random();
209 let spend_key = PrivateKey::random();
210
211 let generator = ChangeAddressGenerator::new(
212 &scan_key.to_bytes(),
213 &spend_key.to_bytes(),
214 Network::Mainnet,
215 )
216 .unwrap();
217
218 let outpoints = vec![([1u8; 32], 0u32)];
219
220 let (pk1, _) = generator.generate_change(&outpoints, 0).unwrap();
221 let (pk2, _) = generator.generate_change(&outpoints, 0).unwrap();
222
223 assert_eq!(pk1, pk2);
224 }
225
226 #[test]
227 fn test_different_indices() {
228 let scan_key = PrivateKey::random();
229 let spend_key = PrivateKey::random();
230
231 let generator = ChangeAddressGenerator::new(
232 &scan_key.to_bytes(),
233 &spend_key.to_bytes(),
234 Network::Mainnet,
235 )
236 .unwrap();
237
238 let outpoints = vec![([1u8; 32], 0u32)];
239
240 let (pk0, _) = generator.generate_change(&outpoints, 0).unwrap();
241 let (pk1, _) = generator.generate_change(&outpoints, 1).unwrap();
242
243 assert_ne!(pk0, pk1);
244 }
245
246 #[test]
247 fn test_is_change_output() {
248 let scan_key = PrivateKey::random();
249 let spend_key = PrivateKey::random();
250
251 let generator = ChangeAddressGenerator::new(
252 &scan_key.to_bytes(),
253 &spend_key.to_bytes(),
254 Network::Mainnet,
255 )
256 .unwrap();
257
258 let outpoints = vec![([1u8; 32], 0u32)];
259 let (output_pk, _) = generator.generate_change(&outpoints, 2).unwrap();
260
261 let result = generator.is_change_output(&output_pk, &outpoints, 5).unwrap();
262 assert!(result.is_some());
263 assert_eq!(result.unwrap().0, 2);
264 }
265}