Skip to content

Commit 956dc6c

Browse files
committed
silentpayments: receiving
Add routine for scanning a transaction and returning the necessary spending data for any found outputs. This function works with labels via a lookup callback and requires access to the transaction outputs. Requiring access to the transaction outputs is not suitable for light clients, but light client support is enabled by exposing the `_create_shared_secret` and `_create_output_pubkey` functions in the API. This means the light client will need to manage their own scanning state, so wherever possible it is preferrable to use the `_recipient_scan_ouputs` function. Add an opaque data type for passing around the summed input public key (A_sum) and the input hash tweak (input_hash). This data is passed to the scanner before the ECDH step as two separate elements so that the scanner can multiply b_scan * input_hash before doing ECDH. Add functions for deserializing / serializing a public_data object to and from a public key. When serializing a public_data object, the input_hash is multplied into A_sum. This is so the object can be stored as public key for wallet rescanning later, or to vend to light clients. For the light client, a `_parse` function is added which parses the compressed public key serialization into a `public_data` object. Finally, add test coverage for the recieiving API.
1 parent 6eeeb17 commit 956dc6c

File tree

3 files changed

+660
-0
lines changed

3 files changed

+660
-0
lines changed

include/secp256k1_silentpayments.h

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,252 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipien
163163
const secp256k1_pubkey *label
164164
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
165165

166+
/** Opaque data structure that holds silent payments public input data.
167+
*
168+
* This structure does not contain secret data. Guaranteed to be 98 bytes in
169+
* size. It can be safely copied/moved. Created with
170+
* `secp256k1_silentpayments_recipient_public_data_create`. Can be serialized as a
171+
* compressed public key using
172+
* `secp256k1_silentpayments_recipient_public_data_serialize`. The serialization is
173+
* intended for sending the public input data to light clients. Light clients
174+
* can use this serialization with
175+
* `secp256k1_silentpayments_recipient_public_data_parse`.
176+
*/
177+
typedef struct {
178+
unsigned char data[98];
179+
} secp256k1_silentpayments_recipient_public_data;
180+
181+
/** Compute Silent Payment public data from input public keys and transaction
182+
* inputs.
183+
*
184+
* Given a list of n public keys A_1...A_n (one for each silent payment
185+
* eligible input to spend) and a serialized outpoint_smallest, create a
186+
* `public_data` object. This object summarizes the public data from the
187+
* transaction inputs needed for scanning.
188+
*
189+
* `outpoint_smallest36` refers to the smallest outpoint lexicographically
190+
* from the transaction inputs (both silent payments eligible and non-eligible
191+
* inputs). This value MUST be the smallest outpoint out of all of the
192+
* transaction inputs, otherwise the recipient will be unable to find the the
193+
* payment.
194+
*
195+
* The public keys have to be passed in via two different parameter pairs, one
196+
* for regular and one for x-only public keys, in order to avoid the need of
197+
* users converting to a common pubkey format before calling this function.
198+
* The resulting data can be used for scanning on the recipient side, or
199+
* stored in an index for later use (e.g. wallet rescanning, vending data to
200+
* light clients).
201+
*
202+
* If calling this function for simply aggregating the public transaction data
203+
* for later use, the caller can save the result with
204+
* `silentpayments_recipient_public_data_serialize`.
205+
*
206+
* Returns: 1 if tweak data creation was successful. 0 if an error occured.
207+
* Args: ctx: pointer to a context object
208+
* Out: public_data: pointer to public_data object containing the
209+
* summed public key and input_hash.
210+
* In: outpoint_smallest36: serialized smallest outpoint (lexicographically)
211+
* from the transaction inputs
212+
* xonly_pubkeys: pointer to an array of pointers to taproot
213+
* x-only public keys (can be NULL if no taproot
214+
* inputs are used)
215+
* n_xonly_pubkeys: the number of taproot input public keys
216+
* plain_pubkeys: pointer to an array of pointers to non-taproot
217+
* public keys (can be NULL if no non-taproot
218+
* inputs are used)
219+
* n_plain_pubkeys: the number of non-taproot input public keys
220+
*/
221+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_create(
222+
const secp256k1_context *ctx,
223+
secp256k1_silentpayments_recipient_public_data *public_data,
224+
const unsigned char *outpoint_smallest36,
225+
const secp256k1_xonly_pubkey * const *xonly_pubkeys,
226+
size_t n_xonly_pubkeys,
227+
const secp256k1_pubkey * const *plain_pubkeys,
228+
size_t n_plain_pubkeys
229+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
230+
231+
/** Serialize a silentpayments_recipient_public_data object into a 33-byte sequence.
232+
*
233+
* Returns: 1 always.
234+
*
235+
* Args: ctx: pointer to a context object
236+
* Out: output33: pointer to a 33-byte array to place the serialized
237+
* `silentpayments_recipient_public_data` in
238+
* In: public_data: pointer to an initialized silentpayments_recipient_public_data
239+
* object
240+
*/
241+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_serialize(
242+
const secp256k1_context *ctx,
243+
unsigned char *output33,
244+
const secp256k1_silentpayments_recipient_public_data *public_data
245+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
246+
247+
/** Parse a 33-byte sequence into a silent_payments_public_data object.
248+
*
249+
* Returns: 1 if the data was able to be parsed.
250+
* 0 if the sequence is invalid (e.g. does not represent a valid
251+
* public key).
252+
*
253+
* Args: ctx: pointer to a context object.
254+
* Out: public_data: pointer to a silentpayments_recipient_public_data object. If 1 is
255+
* returned, it is set to a parsed version of input33.
256+
* In: input33: pointer to a serialized silentpayments_recipient_public_data.
257+
*/
258+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_parse(
259+
const secp256k1_context *ctx,
260+
secp256k1_silentpayments_recipient_public_data *public_data,
261+
const unsigned char *input33
262+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
263+
264+
/** Callback function for label lookups
265+
*
266+
* This function is implemented by the recipient to check if a value exists in
267+
* the recipients label cache during scanning.
268+
*
269+
* For creating the labels cache,
270+
* `secp256k1_silentpayments_recipient_create_label_tweak` can be used.
271+
*
272+
* Returns: pointer to the 32-byte label tweak if there is a match.
273+
* NULL pointer if there is no match.
274+
*
275+
* In: label: pointer to the label pubkey to check (computed during
276+
* scanning)
277+
* label_context: pointer to the recipients label cache.
278+
*/
279+
typedef const unsigned char* (*secp256k1_silentpayments_label_lookup)(const unsigned char* label33, const void* label_context);
280+
281+
/** Found outputs struct
282+
*
283+
* Struct for holding a found output along with data needed to spend it later.
284+
*
285+
* output: the x-only public key for the taproot output
286+
* tweak: the 32-byte tweak needed to spend the output
287+
* found_with_label: boolean value to indicate if the output was sent to a
288+
* labelled address. If true, label will be set with a valid
289+
* public key.
290+
* label: public key representing the label used.
291+
* If found_with_label = false, this is set to an invalid
292+
* public key.
293+
*/
294+
typedef struct {
295+
secp256k1_xonly_pubkey output;
296+
unsigned char tweak[32];
297+
int found_with_label;
298+
secp256k1_pubkey label;
299+
} secp256k1_silentpayments_found_output;
300+
301+
/** Scan for Silent Payment transaction outputs.
302+
*
303+
* Given a public_data object, a recipient's scan key and spend public key,
304+
* and the relevant transaction outputs, scan for outputs belonging to
305+
* the recipient and return the tweak(s) needed for spending the output(s). An
306+
* optional label_lookup callback function and label_context can be passed if
307+
* the recipient uses labels. This allows for checking if a label exists in
308+
* the recipients label cache and retrieving the label tweak during scanning.
309+
*
310+
* For the labels cache, `secp256k1_silentpayments_recipient_create_label_tweak`
311+
* can be used.
312+
*
313+
* Returns: 1 if output scanning was successful.
314+
* 0 if an error occured.
315+
*
316+
* Args: ctx: pointer to a context object
317+
* Out: found_outputs: pointer to an array of pointers to found
318+
* output objects. The found outputs array MUST
319+
* be initialized to be the same length as the
320+
* tx_outputs array
321+
* n_found_outputs: pointer to an integer indicating the final
322+
* size of the found outputs array. This number
323+
* represents the number of outputs found while
324+
* scanning (0 if none are found)
325+
* In: tx_outputs: pointer to the tx's x-only public key outputs
326+
* n_tx_outputs: the number of tx_outputs being scanned
327+
* recipient_scan_key: pointer to the recipient's scan key
328+
* public_data: pointer to the transaction public data
329+
* (see `_recipient_public_data_create`).
330+
* recipient_spend_pubkey: pointer to the recipient's spend pubkey
331+
* label_lookup: pointer to a callback function for looking up
332+
* a label value. This function takes a label
333+
* pubkey as an argument and returns a pointer to
334+
* the label tweak if the label exists, otherwise
335+
* returns a NULL pointer (NULL if labels are not
336+
* used)
337+
* label_context: pointer to a label context object (NULL if
338+
* labels are not used)
339+
*/
340+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_scan_outputs(
341+
const secp256k1_context *ctx,
342+
secp256k1_silentpayments_found_output **found_outputs,
343+
size_t *n_found_outputs,
344+
const secp256k1_xonly_pubkey * const *tx_outputs,
345+
size_t n_tx_outputs,
346+
const unsigned char *recipient_scan_key,
347+
const secp256k1_silentpayments_recipient_public_data *public_data,
348+
const secp256k1_pubkey *recipient_spend_pubkey,
349+
const secp256k1_silentpayments_label_lookup label_lookup,
350+
const void *label_context
351+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
352+
SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
353+
354+
/** Create Silent Payment shared secret.
355+
*
356+
* Given the public input data (secp256k1_silentpayments_recipient_public_data),
357+
* and the recipient's scan key, calculate the shared secret.
358+
*
359+
* The resulting shared secret is needed as input for creating silent payments
360+
* outputs belonging to the same recipient scan public key. This function is
361+
* intended for light clients, i.e., scenarios where the caller does not have
362+
* access to the full transaction. If the caller does have access to the full
363+
* transaction, `secp256k1_silentpayments_recipient_scan_outputs` should be
364+
* used instead.
365+
*
366+
* Returns: 1 if shared secret creation was successful. 0 if an error occured.
367+
* Args: ctx: pointer to a context object
368+
* Out: shared_secret33: pointer to the resulting 33-byte shared secret
369+
* In: recipient_scan_key: pointer to the recipient's scan key
370+
* public_data: pointer to the input public key sum, tweaked
371+
* with the input_hash (see
372+
* `_recipient_public_data_create`)
373+
*/
374+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_shared_secret(
375+
const secp256k1_context *ctx,
376+
unsigned char *shared_secret33,
377+
const unsigned char *recipient_scan_key,
378+
const secp256k1_silentpayments_recipient_public_data *public_data
379+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
380+
381+
/** Create Silent Payment output public key.
382+
*
383+
* Given a shared_secret, a public key B_spend, and an output counter k,
384+
* create an output public key.
385+
*
386+
* This function is used by the recipient when scanning for outputs without
387+
* access to the transaction outputs (e.g. using BIP158 block filters). When
388+
* scanning with this function, it is the scanners responsibility to determine
389+
* if the generated output exists in a block before proceeding to the next
390+
* value of `k`.
391+
*
392+
* Returns: 1 if output creation was successful. 0 if an error occured.
393+
* Args: ctx: pointer to a context object
394+
* Out: P_output_xonly: pointer to the resulting output x-only pubkey
395+
* In: shared_secret33: shared secret, derived from either sender's
396+
* or recipient's perspective with routines from
397+
* above
398+
* recipient_spend_pubkey: pointer to the recipient's spend pubkey
399+
* (labelled or unlabelled)
400+
* k: output counter (initially set to 0, must be
401+
* incremented for each additional output created
402+
* or after each output found when scanning)
403+
*/
404+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_output_pubkey(
405+
const secp256k1_context *ctx,
406+
secp256k1_xonly_pubkey *P_output_xonly,
407+
const unsigned char *shared_secret33,
408+
const secp256k1_pubkey *recipient_spend_pubkey,
409+
unsigned int k
410+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
411+
166412
#ifdef __cplusplus
167413
}
168414
#endif

0 commit comments

Comments
 (0)