@@ -115,3 +115,167 @@ pub fn create_silentpayment_scriptpubkeys(
115115
116116 Ok ( payments)
117117}
118+
119+ #[ cfg( test) ]
120+ #[ cfg_attr( coverage_nightly, coverage( off) ) ]
121+ mod tests {
122+ use super :: * ;
123+ use bitcoin:: secp256k1:: { PublicKey , SecretKey } ;
124+ use std:: str:: FromStr ;
125+
126+ const SCAN_PK_1 : & str = "03f95241dfb00d1d42e2f48fb72e31a06b9fd166c1d6bd12648b41977dd51b9a0b" ;
127+ const SPEND_PK_1 : & str = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af" ;
128+ const SCAN_PK_2 : & str = "03c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5" ;
129+ const SPEND_PK_2 : & str = "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9" ;
130+ const PARTIAL_SECRET_1 : & str =
131+ "d5c68eccb3ddd0fab0bf504209b8b6ce3f51832beb136a5f91ade54bc059f9b8" ;
132+ const PARTIAL_SECRET_2 : & str =
133+ "e9b700555d60a8c4a874128c68b07ed7234248910db80d073d298e058df1786f" ;
134+
135+ fn setup_test_data ( ) -> ( SecretKey , Vec < SilentPaymentCode > ) {
136+ let partial_secret = SecretKey :: from_str ( PARTIAL_SECRET_1 ) . expect ( "reading from constant" ) ;
137+
138+ // Create some test SilentPaymentCodes
139+ let scan_1 = PublicKey :: from_str ( SCAN_PK_1 ) . expect ( "reading from constant" ) ;
140+ let spend_1 = PublicKey :: from_str ( SPEND_PK_1 ) . expect ( "reading from constant" ) ;
141+
142+ let scan_2 = PublicKey :: from_str ( SCAN_PK_2 ) . expect ( "reading from constant" ) ;
143+ let spend_2 = PublicKey :: from_str ( SPEND_PK_2 ) . expect ( "reading from constant" ) ;
144+
145+ let sp_code_1 = SilentPaymentCode :: new_v0 ( scan_1, spend_1, bitcoin:: Network :: Bitcoin ) ;
146+
147+ let sp_code_2 = SilentPaymentCode :: new_v0 ( scan_2, spend_2, bitcoin:: Network :: Bitcoin ) ;
148+
149+ let sp_code_3 = sp_code_1. add_label ( Scalar :: MAX ) . expect ( "should succeed" ) ;
150+
151+ ( partial_secret, vec ! [ sp_code_1, sp_code_2, sp_code_3] )
152+ }
153+
154+ #[ test]
155+ fn test_create_silentpayment_spk_base ( ) {
156+ let ( partial_secret, sp_codes) = setup_test_data ( ) ;
157+
158+ let result =
159+ create_silentpayment_scriptpubkeys ( partial_secret, & sp_codes) . expect ( "should succeed" ) ;
160+
161+ assert_eq ! ( result. len( ) , 3 ) ;
162+
163+ for sp_code in & sp_codes {
164+ assert ! ( result. contains_key( sp_code) ) ;
165+ assert_eq ! ( result[ sp_code] . len( ) , 1 ) ;
166+ }
167+ }
168+
169+ #[ test]
170+ fn test_create_silentpayment_spk_with_empty_outputs ( ) {
171+ let ( partial_secret, _) = setup_test_data ( ) ;
172+ let empty_outputs: Vec < SilentPaymentCode > = vec ! [ ] ;
173+
174+ let result = create_silentpayment_scriptpubkeys ( partial_secret, & empty_outputs)
175+ . expect ( "should succeed with empty outputs" ) ;
176+
177+ assert ! ( result. is_empty( ) ) ;
178+ }
179+
180+ #[ test]
181+ fn test_create_silentpayment_spk_cache_behavior ( ) {
182+ let ( partial_secret, sp_codes) = setup_test_data ( ) ;
183+
184+ assert_eq ! ( sp_codes[ 0 ] . scan, sp_codes[ 2 ] . scan) ;
185+
186+ let result =
187+ create_silentpayment_scriptpubkeys ( partial_secret, & sp_codes) . expect ( "should succeed" ) ;
188+
189+ // Get the pubkeys for codes with the same scan key
190+ let pubkeys_1 = & result[ & sp_codes[ 0 ] ] ;
191+ let pubkeys_3 = & result[ & sp_codes[ 2 ] ] ;
192+
193+ // They should be different despite having the same scan key
194+ // because the k value is incremented and the spend keys differ
195+ assert_ne ! ( pubkeys_1[ 0 ] , pubkeys_3[ 0 ] ) ;
196+ }
197+
198+ #[ test]
199+ fn test_create_silentpayment_spk_multiple_calls_deterministic ( ) {
200+ let ( partial_secret, sp_codes) = setup_test_data ( ) ;
201+
202+ // Generate sp_codes twice with the same inputs
203+ let result_1 =
204+ create_silentpayment_scriptpubkeys ( partial_secret, & sp_codes) . expect ( "should succeed" ) ;
205+ let result_2 =
206+ create_silentpayment_scriptpubkeys ( partial_secret, & sp_codes) . expect ( "should succeed" ) ;
207+
208+ // Results should be identical
209+ assert_eq ! ( result_1. len( ) , result_2. len( ) ) ;
210+
211+ for sp_code in & sp_codes {
212+ assert_eq ! ( result_1[ sp_code] , result_2[ sp_code] ) ;
213+ }
214+ }
215+
216+ #[ test]
217+ fn test_create_silentpayment_spk_duplicate_payment_codes ( ) {
218+ let ( partial_secret, mut sp_codes) = setup_test_data ( ) ;
219+
220+ // Add a duplicate of the first code
221+ sp_codes. push ( sp_codes[ 0 ] . clone ( ) ) ;
222+
223+ let result = create_silentpayment_scriptpubkeys ( partial_secret, & sp_codes)
224+ . expect ( "should succeed with duplicates" ) ;
225+
226+ // Should still have only 3 unique entries
227+ assert_eq ! ( result. len( ) , 3 ) ;
228+
229+ // First code should have 2 pubkeys now
230+ assert_eq ! ( result[ & sp_codes[ 0 ] ] . len( ) , 2 ) ;
231+
232+ // And the pubkeys should be different due to k incrementing
233+ let pubkeys = & result[ & sp_codes[ 0 ] ] ;
234+ assert_ne ! ( pubkeys[ 0 ] , pubkeys[ 1 ] ) ;
235+ }
236+
237+ #[ test]
238+ fn test_create_silentpayment_spk_large_number_of_sp_codes ( ) {
239+ let ( partial_secret, sp_codes) = setup_test_data ( ) ;
240+
241+ let base_code = & sp_codes[ 0 ] ;
242+
243+ // Create many codes with the same scan key but different spend keys
244+ let mut sp_codes = Vec :: new ( ) ;
245+ for _ in 0 ..100 {
246+ let label = Scalar :: random ( ) ;
247+
248+ let code = base_code. add_label ( label) . expect ( "should succeed" ) ;
249+
250+ sp_codes. push ( code) ;
251+ }
252+
253+ let result = create_silentpayment_scriptpubkeys ( partial_secret, & sp_codes)
254+ . expect ( "should succeed with many sp_codes" ) ;
255+
256+ // Should have generated the correct number of sp_codes
257+ assert_eq ! ( result. len( ) , 100 ) ;
258+
259+ // All generated pubkeys should be unique
260+ let all_pubkeys: Vec < _ > = result. values ( ) . flat_map ( |v| v. iter ( ) . cloned ( ) ) . collect ( ) ;
261+ let unique_pubkeys: std:: collections:: HashSet < _ > = all_pubkeys. iter ( ) . cloned ( ) . collect ( ) ;
262+ assert_eq ! ( all_pubkeys. len( ) , unique_pubkeys. len( ) ) ;
263+ }
264+
265+ #[ test]
266+ fn test_create_silentpayment_spk_different_partial_secrets ( ) {
267+ let ( partial_secret_1, sp_codes) = setup_test_data ( ) ;
268+ let partial_secret_2 =
269+ SecretKey :: from_str ( PARTIAL_SECRET_2 ) . expect ( "creating from constant" ) ;
270+
271+ let result_1 = create_silentpayment_scriptpubkeys ( partial_secret_1, & sp_codes)
272+ . expect ( "should succeed" ) ;
273+ let result_2 = create_silentpayment_scriptpubkeys ( partial_secret_2, & sp_codes)
274+ . expect ( "should succeed" ) ;
275+
276+ // Results should be different with different partial secrets
277+ for sp_code in & sp_codes {
278+ assert_ne ! ( result_1[ sp_code] , result_2[ sp_code] ) ;
279+ }
280+ }
281+ }
0 commit comments