@@ -169,4 +169,92 @@ static int secp256k1_silentpayments_create_output_pubkey(const secp256k1_context
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 (secp256k1_ecmult_gen_context_is_built (& ctx -> ecmult_gen_ctx ));
195
+ ARG_CHECK (generated_outputs != NULL );
196
+ ARG_CHECK (recipients != NULL );
197
+ ARG_CHECK (n_recipients >= 1 );
198
+ ARG_CHECK (plain_seckeys == NULL || n_plain_seckeys >= 1 );
199
+ ARG_CHECK (taproot_seckeys == NULL || n_taproot_seckeys >= 1 );
200
+ ARG_CHECK ((plain_seckeys != NULL ) || (taproot_seckeys != NULL ));
201
+ ARG_CHECK ((n_plain_seckeys + n_taproot_seckeys ) >= 1 );
202
+ ARG_CHECK (outpoint_smallest36 != NULL );
203
+ /* ensure the index field is set correctly */
204
+ for (i = 0 ; i < n_recipients ; i ++ ) {
205
+ ARG_CHECK (recipients [i ]-> index == i );
206
+ }
207
+
208
+ /* Compute input private keys sum: a_sum = a_1 + a_2 + ... + a_n */
209
+ a_sum_scalar = secp256k1_scalar_zero ;
210
+ for (i = 0 ; i < n_plain_seckeys ; i ++ ) {
211
+ if (!secp256k1_scalar_set_b32_seckey (& addend , plain_seckeys [i ])) {
212
+ return 0 ;
213
+ }
214
+ secp256k1_scalar_add (& a_sum_scalar , & a_sum_scalar , & addend );
215
+ VERIFY_CHECK (!secp256k1_scalar_is_zero (& a_sum_scalar ));
216
+ }
217
+ /* private keys used for taproot outputs have to be negated if they resulted in an odd point */
218
+ for (i = 0 ; i < n_taproot_seckeys ; i ++ ) {
219
+ secp256k1_ge addend_point ;
220
+ if (!secp256k1_keypair_load (ctx , & addend , & addend_point , taproot_seckeys [i ])) {
221
+ return 0 ;
222
+ }
223
+ if (secp256k1_fe_is_odd (& addend_point .y )) {
224
+ secp256k1_scalar_negate (& addend , & addend );
225
+ }
226
+
227
+ secp256k1_scalar_add (& a_sum_scalar , & a_sum_scalar , & addend );
228
+ VERIFY_CHECK (!secp256k1_scalar_is_zero (& a_sum_scalar ));
229
+ }
230
+ /* If the caller passes in seckeys that sum up to zero, there is nothing
231
+ * we can do here except have the caller try again with different seckeys,
232
+ * e.g. run coin selection again */
233
+ ARG_CHECK (secp256k1_scalar_is_zero (& a_sum_scalar ) == 0 );
234
+ secp256k1_scalar_get_b32 (a_sum , & a_sum_scalar );
235
+
236
+ /* Compute input_hash = hash(outpoint_L || (a_sum * G)) */
237
+ secp256k1_ecmult_gen (& ctx -> ecmult_gen_ctx , & A_sum_gej , & a_sum_scalar );
238
+ secp256k1_ge_set_gej (& A_sum_ge , & A_sum_gej );
239
+ secp256k1_silentpayments_calculate_input_hash (input_hash , outpoint_smallest36 , & A_sum_ge );
240
+ secp256k1_silentpayments_recipient_sort (ctx , recipients , n_recipients );
241
+ last_recipient = * recipients [0 ];
242
+ k = 0 ;
243
+ for (i = 0 ; i < n_recipients ; i ++ ) {
244
+ if ((secp256k1_ec_pubkey_cmp (ctx , & last_recipient .scan_pubkey , & recipients [i ]-> scan_pubkey ) != 0 ) || (i == 0 )) {
245
+ /* if we are on a different scan pubkey, its time to recreate the the shared secret and reset k to 0 */
246
+ if (!secp256k1_silentpayments_create_shared_secret (ctx , shared_secret , a_sum , & recipients [i ]-> scan_pubkey , input_hash )) {
247
+ return 0 ;
248
+ }
249
+ k = 0 ;
250
+ }
251
+ if (!secp256k1_silentpayments_create_output_pubkey (ctx , generated_outputs [recipients [i ]-> index ], shared_secret , & recipients [i ]-> spend_pubkey , k )) {
252
+ return 0 ;
253
+ }
254
+ k ++ ;
255
+ last_recipient = * recipients [i ];
256
+ }
257
+ return 1 ;
258
+ }
259
+
172
260
#endif
0 commit comments