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