Skip to content

Commit b06254b

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 receiving API.
1 parent 3c9362d commit b06254b

File tree

3 files changed

+717
-1
lines changed

3 files changed

+717
-1
lines changed

include/secp256k1_silentpayments.h

Lines changed: 254 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef SECP256K1_SILENTPAYMENTS_H
22
#define SECP256K1_SILENTPAYMENTS_H
33

4+
#include <stdint.h>
45
#include "secp256k1.h"
56
#include "secp256k1_extrakeys.h"
67

@@ -26,6 +27,8 @@ extern "C" {
2627
* any further elliptic-curve operations from the wallet.
2728
*/
2829

30+
static const unsigned char secp256k1_silentpayments_public_data_magic[4] = { 0xa7, 0x1c, 0xd3, 0x5e };
31+
2932
/** This struct serves as an input parameter for passing the silent payment
3033
* address data to `silentpayments_sender_create_outputs`.
3134
*
@@ -154,7 +157,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipien
154157
* Out: labeled_spend_pubkey: pointer to the resulting labeled spend
155158
* public key
156159
* In: recipient_spend_pubkey: pointer to the recipient's spend pubkey
157-
* label: pointer to the the recipient's label public
160+
* label: pointer to the recipient's label public
158161
* key
159162
*/
160163
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(
@@ -164,6 +167,256 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipien
164167
const secp256k1_pubkey *label
165168
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
166169

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

0 commit comments

Comments
 (0)