@@ -173,4 +173,94 @@ int secp256k1_silentpayments_create_output_pubkey(const secp256k1_context *ctx,
173
173
return 1 ;
174
174
}
175
175
176
+ int secp256k1_silentpayments_sender_create_outputs (
177
+ const secp256k1_context * ctx ,
178
+ secp256k1_xonly_pubkey * * generated_outputs ,
179
+ const secp256k1_silentpayments_recipient * * recipients ,
180
+ size_t n_recipients ,
181
+ const unsigned char * outpoint_smallest36 ,
182
+ const secp256k1_keypair * const * taproot_seckeys ,
183
+ size_t n_taproot_seckeys ,
184
+ const unsigned char * const * plain_seckeys ,
185
+ size_t n_plain_seckeys
186
+ ) {
187
+ size_t i , k ;
188
+ secp256k1_scalar a_sum_scalar , addend ;
189
+ secp256k1_ge A_sum_ge ;
190
+ secp256k1_gej A_sum_gej ;
191
+ unsigned char input_hash [32 ];
192
+ unsigned char a_sum [32 ];
193
+ unsigned char shared_secret [33 ];
194
+ secp256k1_silentpayments_recipient last_recipient ;
195
+
196
+ /* Sanity check inputs. */
197
+ VERIFY_CHECK (ctx != NULL );
198
+ ARG_CHECK (recipients != NULL );
199
+ ARG_CHECK (secp256k1_ecmult_gen_context_is_built (& ctx -> ecmult_gen_ctx ));
200
+ ARG_CHECK (plain_seckeys == NULL || n_plain_seckeys >= 1 );
201
+ ARG_CHECK (taproot_seckeys == NULL || n_taproot_seckeys >= 1 );
202
+ ARG_CHECK ((plain_seckeys != NULL ) || (taproot_seckeys != NULL ));
203
+ ARG_CHECK ((n_plain_seckeys + n_taproot_seckeys ) >= 1 );
204
+ ARG_CHECK (outpoint_smallest36 != NULL );
205
+ /* ensure the index field is set correctly */
206
+ for (i = 0 ; i < n_recipients ; i ++ ) {
207
+ ARG_CHECK (recipients [i ]-> index == i );
208
+ }
209
+
210
+ /* Compute input private keys sum: a_sum = a_1 + a_2 + ... + a_n */
211
+ a_sum_scalar = secp256k1_scalar_zero ;
212
+ for (i = 0 ; i < n_plain_seckeys ; i ++ ) {
213
+ int ret = secp256k1_scalar_set_b32_seckey (& addend , plain_seckeys [i ]);
214
+ VERIFY_CHECK (ret );
215
+ (void )ret ;
216
+
217
+ secp256k1_scalar_add (& a_sum_scalar , & a_sum_scalar , & addend );
218
+ VERIFY_CHECK (!secp256k1_scalar_is_zero (& a_sum_scalar ));
219
+ }
220
+ /* private keys used for taproot outputs have to be negated if they resulted in an odd point */
221
+ for (i = 0 ; i < n_taproot_seckeys ; i ++ ) {
222
+ secp256k1_ge addend_point ;
223
+ int ret = secp256k1_keypair_load (ctx , & addend , & addend_point , taproot_seckeys [i ]);
224
+ VERIFY_CHECK (ret );
225
+ (void )ret ;
226
+ /* declassify addend_point to allow using it as a branch point (this is fine because addend_point is not a secret) */
227
+ secp256k1_declassify (ctx , & addend_point , sizeof (addend_point ));
228
+ secp256k1_fe_normalize_var (& addend_point .y );
229
+ if (secp256k1_fe_is_odd (& addend_point .y )) {
230
+ secp256k1_scalar_negate (& addend , & addend );
231
+ }
232
+
233
+ secp256k1_scalar_add (& a_sum_scalar , & a_sum_scalar , & addend );
234
+ VERIFY_CHECK (!secp256k1_scalar_is_zero (& a_sum_scalar ));
235
+ }
236
+ if (secp256k1_scalar_is_zero (& a_sum_scalar )) {
237
+ /* TODO: do we need a special error return code for this case? */
238
+ return 0 ;
239
+ }
240
+ secp256k1_scalar_get_b32 (a_sum , & a_sum_scalar );
241
+
242
+ /* Compute input_hash = hash(outpoint_L || (a_sum * G)) */
243
+ secp256k1_ecmult_gen (& ctx -> ecmult_gen_ctx , & A_sum_gej , & a_sum_scalar );
244
+ secp256k1_ge_set_gej (& A_sum_ge , & A_sum_gej );
245
+ secp256k1_silentpayments_calculate_input_hash (input_hash , outpoint_smallest36 , & A_sum_ge );
246
+ secp256k1_silentpayments_recipient_sort (ctx , recipients , n_recipients );
247
+ last_recipient = * recipients [0 ];
248
+ k = 0 ;
249
+ for (i = 0 ; i < n_recipients ; i ++ ) {
250
+ if ((secp256k1_ec_pubkey_cmp (ctx , & last_recipient .scan_pubkey , & recipients [i ]-> scan_pubkey ) != 0 ) || (i == 0 )) {
251
+ /* if we are on a different scan pubkey, its time to recreate the the shared secret and reset k to 0 */
252
+ if (!secp256k1_silentpayments_create_shared_secret (ctx , shared_secret , a_sum , & recipients [i ]-> scan_pubkey , input_hash )) {
253
+ return 0 ;
254
+ }
255
+ k = 0 ;
256
+ }
257
+ if (!secp256k1_silentpayments_create_output_pubkey (ctx , generated_outputs [recipients [i ]-> index ], shared_secret , & recipients [i ]-> spend_pubkey , k )) {
258
+ return 0 ;
259
+ }
260
+ k ++ ;
261
+ last_recipient = * recipients [i ];
262
+ }
263
+ return 1 ;
264
+ }
265
+
176
266
#endif
0 commit comments