@@ -173,4 +173,91 @@ 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 (n_recipients >= 1 );
200
+ ARG_CHECK (secp256k1_ecmult_gen_context_is_built (& ctx -> ecmult_gen_ctx ));
201
+ ARG_CHECK (plain_seckeys == NULL || n_plain_seckeys >= 1 );
202
+ ARG_CHECK (taproot_seckeys == NULL || n_taproot_seckeys >= 1 );
203
+ ARG_CHECK ((plain_seckeys != NULL ) || (taproot_seckeys != NULL ));
204
+ ARG_CHECK ((n_plain_seckeys + n_taproot_seckeys ) >= 1 );
205
+ ARG_CHECK (outpoint_smallest36 != NULL );
206
+ /* ensure the index field is set correctly */
207
+ for (i = 0 ; i < n_recipients ; i ++ ) {
208
+ ARG_CHECK (recipients [i ]-> index == i );
209
+ }
210
+
211
+ /* Compute input private keys sum: a_sum = a_1 + a_2 + ... + a_n */
212
+ a_sum_scalar = secp256k1_scalar_zero ;
213
+ for (i = 0 ; i < n_plain_seckeys ; i ++ ) {
214
+ if (!secp256k1_scalar_set_b32_seckey (& addend , plain_seckeys [i ])) {
215
+ return 0 ;
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
+ if (!secp256k1_keypair_load (ctx , & addend , & addend_point , taproot_seckeys [i ])) {
224
+ return 0 ;
225
+ }
226
+ if (secp256k1_fe_is_odd (& addend_point .y )) {
227
+ secp256k1_scalar_negate (& addend , & addend );
228
+ }
229
+
230
+ secp256k1_scalar_add (& a_sum_scalar , & a_sum_scalar , & addend );
231
+ VERIFY_CHECK (!secp256k1_scalar_is_zero (& a_sum_scalar ));
232
+ }
233
+ if (secp256k1_scalar_is_zero (& a_sum_scalar )) {
234
+ /* TODO: do we need a special error return code for this case? */
235
+ return 0 ;
236
+ }
237
+ secp256k1_scalar_get_b32 (a_sum , & a_sum_scalar );
238
+
239
+ /* Compute input_hash = hash(outpoint_L || (a_sum * G)) */
240
+ secp256k1_ecmult_gen (& ctx -> ecmult_gen_ctx , & A_sum_gej , & a_sum_scalar );
241
+ secp256k1_ge_set_gej (& A_sum_ge , & A_sum_gej );
242
+ secp256k1_silentpayments_calculate_input_hash (input_hash , outpoint_smallest36 , & A_sum_ge );
243
+ secp256k1_silentpayments_recipient_sort (ctx , recipients , n_recipients );
244
+ last_recipient = * recipients [0 ];
245
+ k = 0 ;
246
+ for (i = 0 ; i < n_recipients ; i ++ ) {
247
+ if ((secp256k1_ec_pubkey_cmp (ctx , & last_recipient .scan_pubkey , & recipients [i ]-> scan_pubkey ) != 0 ) || (i == 0 )) {
248
+ /* if we are on a different scan pubkey, its time to recreate the the shared secret and reset k to 0 */
249
+ if (!secp256k1_silentpayments_create_shared_secret (ctx , shared_secret , a_sum , & recipients [i ]-> scan_pubkey , input_hash )) {
250
+ return 0 ;
251
+ }
252
+ k = 0 ;
253
+ }
254
+ if (!secp256k1_silentpayments_create_output_pubkey (ctx , generated_outputs [recipients [i ]-> index ], shared_secret , & recipients [i ]-> spend_pubkey , k )) {
255
+ return 0 ;
256
+ }
257
+ k ++ ;
258
+ last_recipient = * recipients [i ];
259
+ }
260
+ return 1 ;
261
+ }
262
+
176
263
#endif
0 commit comments