Skip to content

Commit 2290e80

Browse files
committed
silentpayments: add public tweak data creation routine
1 parent 74351a1 commit 2290e80

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

include/secp256k1_silentpayments.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,39 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_send_cre
8989
const secp256k1_pubkey *receiver_scan_pubkey
9090
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
9191

92-
/* TODO: add function API for receiver side. */
92+
/** Create Silent Payment tweak data from input public keys.
93+
*
94+
* Given a list of n public keys A_1...A_n (one for each silent payment
95+
* eligible input to spend) and a serialized outpoint_smallest, compute
96+
* the corresponding input public keys tweak data:
97+
*
98+
* A_tweaked = (A_1 + A_2 + ... + A_n) * hash(outpoint_lowest || A)
99+
*
100+
* The public keys have to be passed in via two different parameter pairs,
101+
* one for regular and one for x-only public keys, in order to avoid the need
102+
* of users converting to a common pubkey format before calling this function.
103+
* The resulting data is needed to create a shared secret for the receiver's side.
104+
*
105+
* Returns: 1 if tweak data creation was successful. 0 if an error occured.
106+
* Args: ctx: pointer to a context object
107+
* Out: public_tweak_data: pointer to the resulting public keys tweak data
108+
* In: plain_pubkeys: pointer to an array of non-taproot public keys
109+
* (can be NULL if no non-taproot inputs are used)
110+
* n_plain_pubkeys: the number of non-taproot input public keys
111+
* xonly_pubkeys: pointer to an array of taproot x-only public keys
112+
* (can be NULL if no taproot input public keys are used)
113+
* n_xonly_pubkeys: the number of taproot input public keys
114+
* outpoint_smallest36: serialized smallest outpoint
115+
*/
116+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_create_public_tweak_data(
117+
const secp256k1_context *ctx,
118+
secp256k1_pubkey *public_tweak_data,
119+
const secp256k1_pubkey *plain_pubkeys,
120+
size_t n_plain_pubkeys,
121+
const secp256k1_xonly_pubkey *xonly_pubkeys,
122+
size_t n_xonly_pubkeys,
123+
const unsigned char *outpoint_smallest36
124+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(7);
93125

94126
#ifdef __cplusplus
95127
}

src/modules/silentpayments/main_impl.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,49 @@ int secp256k1_silentpayments_send_create_shared_secret(const secp256k1_context *
144144
return 1;
145145
}
146146

147+
int secp256k1_silentpayments_create_public_tweak_data(const secp256k1_context *ctx, secp256k1_pubkey *public_tweak_data, const secp256k1_pubkey *plain_pubkeys, size_t n_plain_pubkeys, const secp256k1_xonly_pubkey *xonly_pubkeys, size_t n_xonly_pubkeys, const unsigned char *outpoint_smallest36) {
148+
size_t i;
149+
secp256k1_ge A_sum_ge, addend;
150+
secp256k1_gej A_sum_gej;
151+
secp256k1_scalar input_hash_scalar;
152+
153+
/* Sanity check inputs */
154+
VERIFY_CHECK(ctx != NULL);
155+
ARG_CHECK(public_tweak_data != NULL);
156+
ARG_CHECK(plain_pubkeys == NULL || n_plain_pubkeys >= 1);
157+
ARG_CHECK(xonly_pubkeys == NULL || n_xonly_pubkeys >= 1);
158+
ARG_CHECK((plain_pubkeys != NULL) || (xonly_pubkeys != NULL));
159+
ARG_CHECK((n_plain_pubkeys + n_xonly_pubkeys) >= 1);
160+
ARG_CHECK(outpoint_smallest36 != NULL);
161+
162+
/* Compute input public keys sum: A_sum = A_1 + A_2 + ... + A_n */
163+
secp256k1_gej_set_infinity(&A_sum_gej);
164+
for (i = 0; i < n_plain_pubkeys; i++) {
165+
secp256k1_pubkey_load(ctx, &addend, &plain_pubkeys[i]);
166+
secp256k1_gej_add_ge(&A_sum_gej, &A_sum_gej, &addend);
167+
}
168+
for (i = 0; i < n_xonly_pubkeys; i++) {
169+
secp256k1_xonly_pubkey_load(ctx, &addend, &xonly_pubkeys[i]);
170+
secp256k1_gej_add_ge(&A_sum_gej, &A_sum_gej, &addend);
171+
}
172+
if (secp256k1_gej_is_infinity(&A_sum_gej)) {
173+
/* TODO: do we need a special error return code for this case? */
174+
return 0;
175+
}
176+
secp256k1_ge_set_gej(&A_sum_ge, &A_sum_gej);
177+
178+
/* Compute input_hash = hash(outpoint_L || A_sum) */
179+
secp256k1_silentpayments_calculate_input_hash(&input_hash_scalar, outpoint_smallest36, &A_sum_ge);
180+
181+
/* Compute A_tweaked = A_sum * input_hash */
182+
if (!secp256k1_eckey_pubkey_tweak_mul(&A_sum_ge, &input_hash_scalar)) {
183+
return 0;
184+
}
185+
secp256k1_pubkey_save(public_tweak_data, &A_sum_ge);
186+
187+
return 1;
188+
}
189+
147190
/* TODO: implement functions for receiver side. */
148191

149192
#endif

0 commit comments

Comments
 (0)