Skip to content

Commit adff4de

Browse files
committed
silentpayments: add public tweak data creation routine
1 parent 03148a1 commit adff4de

File tree

2 files changed

+100
-1
lines changed

2 files changed

+100
-1
lines changed

include/secp256k1_silentpayments.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,38 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_send_cre
8686
const secp256k1_pubkey *receiver_scan_pubkey
8787
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
8888

89-
/* TODO: add function API for receiver side. */
89+
/** Create Silent Payment tweak data from input public keys.
90+
*
91+
* Given a list of n public keys A_0...A_(n-1) (one for each input to spend)
92+
* and an outpoints_hash, compute the corresponding input public keys tweak data:
93+
*
94+
* A_tweaked = (A_0 + A_1 + ... A_(n-1)) * outpoints_hash
95+
*
96+
* If necessary, the public keys are negated to enforce the right y-parity.
97+
* For that reason, the public keys have to be passed in via two different parameter
98+
* pairs, depending on whether they were used for creating taproot outputs or not.
99+
* The resulting data is needed to create a shared secret for the receiver's side.
100+
*
101+
* Returns: 1 if tweak data creation was successful. 0 if an error occured.
102+
* Args: ctx: pointer to a context object
103+
* Out: tweak_data33: pointer to the resulting 33-byte tweak data
104+
* In: plain_pubkeys: pointer to an array of non-taproot public keys
105+
* (can be NULL if no non-taproot inputs are used)
106+
* n_plain_pubkeys: the number of non-taproot input public keys
107+
* xonly_pubkeys: pointer to an array of taproot x-only public keys
108+
* (can be NULL if no taproot input public keys are used)
109+
* n_xonly_pubkeys: the number of taproot input public keys
110+
* outpoints_hash32: hash of the sorted serialized outpoints
111+
*/
112+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_create_public_tweak_data(
113+
const secp256k1_context *ctx,
114+
unsigned char *tweak_data33,
115+
const secp256k1_pubkey *plain_pubkeys,
116+
size_t n_plain_pubkeys,
117+
const secp256k1_xonly_pubkey *xonly_pubkeys,
118+
size_t n_xonly_pubkeys,
119+
const unsigned char *outpoints_hash32
120+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(7);
90121

91122
#ifdef __cplusplus
92123
}

src/modules/silentpayments/main_impl.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,74 @@ int secp256k1_silentpayments_send_create_shared_secret(const secp256k1_context *
111111
return 1;
112112
}
113113

114+
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 *outpoints_hash32) {
115+
size_t i;
116+
secp256k1_pubkey A_tweaked;
117+
size_t outputlen = 33;
118+
119+
/* Sanity check inputs */
120+
VERIFY_CHECK(ctx != NULL);
121+
ARG_CHECK(tweak_data33 != NULL);
122+
memset(tweak_data33, 0, 33);
123+
ARG_CHECK(plain_pubkeys == NULL || n_plain_pubkeys >= 1);
124+
ARG_CHECK(xonly_pubkeys == NULL || n_xonly_pubkeys >= 1);
125+
ARG_CHECK((plain_pubkeys != NULL) || (xonly_pubkeys != NULL));
126+
ARG_CHECK((n_plain_pubkeys + n_xonly_pubkeys) >= 1);
127+
ARG_CHECK(outpoints_hash32 != NULL);
128+
129+
/* Compute input public keys tweak: A_tweaked = (A_0 + A_1 + ... + A_n) * outpoints_hash */
130+
for (i = 0; i < n_plain_pubkeys; i++) {
131+
secp256k1_pubkey combined;
132+
const secp256k1_pubkey *addends[2];
133+
if (i == 0) {
134+
A_tweaked = plain_pubkeys[0];
135+
continue;
136+
}
137+
addends[0] = &A_tweaked;
138+
addends[1] = &plain_pubkeys[i];
139+
if (!secp256k1_ec_pubkey_combine(ctx, &combined, addends, 2)) {
140+
return 0;
141+
}
142+
A_tweaked = combined;
143+
}
144+
/* X-only public keys have to be converted to regular public keys (assuming even parity) */
145+
for (i = 0; i < n_xonly_pubkeys; i++) {
146+
unsigned char pubkey_to_add_ser[33];
147+
secp256k1_pubkey combined, pubkey_to_add;
148+
const secp256k1_pubkey *addends[2];
149+
150+
pubkey_to_add_ser[0] = 0x02;
151+
if (!secp256k1_xonly_pubkey_serialize(ctx, &pubkey_to_add_ser[1], &xonly_pubkeys[i])) {
152+
return 0;
153+
}
154+
if (!secp256k1_ec_pubkey_parse(ctx, &pubkey_to_add, pubkey_to_add_ser, 33)) {
155+
return 0;
156+
}
157+
158+
if (i == 0 && n_plain_pubkeys == 0) {
159+
A_tweaked = pubkey_to_add;
160+
continue;
161+
}
162+
addends[0] = &A_tweaked;
163+
addends[1] = &pubkey_to_add;
164+
if (!secp256k1_ec_pubkey_combine(ctx, &combined, addends, 2)) {
165+
return 0;
166+
}
167+
A_tweaked = combined;
168+
}
169+
170+
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &A_tweaked, outpoints_hash32)) {
171+
return 0;
172+
}
173+
174+
/* Serialize tweak_data */
175+
if (!secp256k1_ec_pubkey_serialize(ctx, tweak_data33, &outputlen, &A_tweaked, SECP256K1_EC_COMPRESSED)) {
176+
return 0;
177+
}
178+
179+
return 1;
180+
}
181+
114182
/* TODO: implement functions for receiver side. */
115183

116184
#endif

0 commit comments

Comments
 (0)