Skip to content

Commit 41c931e

Browse files
committed
silentpayments: add public tweak data creation routine
1 parent 58104d7 commit 41c931e

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-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 input to spend)
95+
* and a serialized outpoint_lowest, compute the corresponding input
96+
* public keys tweak data:
97+
*
98+
* A_tweaked = (A_1 + A_2 + ... + A_n) * hash(outpoint_lowest || A)
99+
*
100+
* If necessary, the public keys are negated to enforce the right y-parity.
101+
* For that reason, the public keys have to be passed in via two different parameter
102+
* pairs, depending on whether they were used for creating taproot outputs or not.
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: tweak_data33: pointer to the resulting 33-byte 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_lowest36: serialized lowest outpoint
115+
*/
116+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_create_public_tweak_data(
117+
const secp256k1_context *ctx,
118+
unsigned char *tweak_data33,
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_lowest36
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: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,60 @@ int secp256k1_silentpayments_send_create_shared_secret(const secp256k1_context *
141141
return 1;
142142
}
143143

144+
int secp256k1_silentpayments_create_public_tweak_data(const secp256k1_context *ctx, unsigned char *tweak_data33, 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_lowest36) {
145+
size_t i;
146+
secp256k1_ge A_sum_ge;
147+
secp256k1_gej A_sum_gej;
148+
secp256k1_scalar input_hash_scalar;
149+
size_t ser_size;
150+
int ser_ret;
151+
152+
/* Sanity check inputs */
153+
VERIFY_CHECK(ctx != NULL);
154+
ARG_CHECK(tweak_data33 != NULL);
155+
memset(tweak_data33, 0, 33);
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_lowest36 != 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_ge addend;
166+
secp256k1_pubkey_load(ctx, &addend, &plain_pubkeys[i]);
167+
secp256k1_gej_add_ge(&A_sum_gej, &A_sum_gej, &addend);
168+
}
169+
170+
/* X-only public keys have to be converted to regular public keys (assuming even parity) */
171+
for (i = 0; i < n_xonly_pubkeys; i++) {
172+
secp256k1_ge addend;
173+
secp256k1_xonly_pubkey_load(ctx, &addend, &xonly_pubkeys[i]);
174+
if (secp256k1_fe_is_odd(&addend.y)) {
175+
secp256k1_fe_negate(&addend.y, &addend.y, 1);
176+
}
177+
VERIFY_CHECK(!secp256k1_fe_is_odd(&addend.y));
178+
secp256k1_gej_add_ge(&A_sum_gej, &A_sum_gej, &addend);
179+
}
180+
secp256k1_ge_set_gej(&A_sum_ge, &A_sum_gej);
181+
182+
/* Compute input_hash = hash(outpoint_L || A_sum) */
183+
secp256k1_silentpayments_calculate_input_hash(&input_hash_scalar, outpoint_lowest36, &A_sum_ge);
184+
185+
/* Compute A_tweaked = A_sum * input_hash */
186+
if (!secp256k1_eckey_pubkey_tweak_mul(&A_sum_ge, &input_hash_scalar)) {
187+
return 0;
188+
}
189+
190+
/* Serialize tweak_data */
191+
ser_ret = secp256k1_eckey_pubkey_serialize(&A_sum_ge, tweak_data33, &ser_size, 1);
192+
VERIFY_CHECK(ser_ret && ser_size == 33);
193+
(void)ser_ret;
194+
195+
return 1;
196+
}
197+
144198
/* TODO: implement functions for receiver side. */
145199

146200
#endif

0 commit comments

Comments
 (0)