-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add BIP352 silentpayments
module
#1519
Open
josibake
wants to merge
10
commits into
bitcoin-core:master
Choose a base branch
from
josibake:bip352-silentpayments-module
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
6c8eafb
build: add skeleton for new silentpayments (BIP352) module
theStack e35d188
silentpayments: sending
josibake 7cde4ac
silentpayments: recipient label support
theStack ae316e2
silentpayments: receiving
josibake 58294b2
silentpayments: add examples/silentpayments.c
josibake 01e605d
silentpayments: add benchmarks for scanning
josibake 15ff6f6
tests: add BIP-352 test vectors
josibake 09a6aa6
tests: add constant time tests
josibake 7af65a8
ci: enable silentpayments module
theStack 71df073
docs: update README
josibake File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -163,6 +163,252 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipien | |||||
const secp256k1_pubkey *label | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||||||
|
||||||
/** Opaque data structure that holds silent payments public input data. | ||||||
* | ||||||
* This structure does not contain secret data. Guaranteed to be 98 bytes in | ||||||
* size. It can be safely copied/moved. Created with | ||||||
* `secp256k1_silentpayments_recipient_public_data_create`. Can be serialized as a | ||||||
* compressed public key using | ||||||
* `secp256k1_silentpayments_recipient_public_data_serialize`. The serialization is | ||||||
* intended for sending the public input data to light clients. Light clients | ||||||
* can use this serialization with | ||||||
* `secp256k1_silentpayments_recipient_public_data_parse`. | ||||||
*/ | ||||||
typedef struct { | ||||||
unsigned char data[98]; | ||||||
} secp256k1_silentpayments_recipient_public_data; | ||||||
|
||||||
/** Compute Silent Payment public data from input public keys and transaction | ||||||
* inputs. | ||||||
* | ||||||
* Given a list of n public keys A_1...A_n (one for each silent payment | ||||||
* eligible input to spend) and a serialized outpoint_smallest, create a | ||||||
* `public_data` object. This object summarizes the public data from the | ||||||
* transaction inputs needed for scanning. | ||||||
* | ||||||
* `outpoint_smallest36` refers to the smallest outpoint lexicographically | ||||||
* from the transaction inputs (both silent payments eligible and non-eligible | ||||||
* inputs). This value MUST be the smallest outpoint out of all of the | ||||||
* transaction inputs, otherwise the recipient will be unable to find the the | ||||||
* payment. | ||||||
* | ||||||
* The public keys have to be passed in via two different parameter pairs, one | ||||||
* for regular and one for x-only public keys, in order to avoid the need of | ||||||
* users converting to a common pubkey format before calling this function. | ||||||
* The resulting data can be used for scanning on the recipient side, or | ||||||
* stored in an index for later use (e.g. wallet rescanning, vending data to | ||||||
* light clients). | ||||||
* | ||||||
* If calling this function for simply aggregating the public transaction data | ||||||
* for later use, the caller can save the result with | ||||||
* `silentpayments_recipient_public_data_serialize`. | ||||||
* | ||||||
* Returns: 1 if tweak data creation was successful. 0 if an error occured. | ||||||
* Args: ctx: pointer to a context object | ||||||
* Out: public_data: pointer to public_data object containing the | ||||||
* summed public key and input_hash. | ||||||
* In: outpoint_smallest36: serialized smallest outpoint (lexicographically) | ||||||
* from the transaction inputs | ||||||
* xonly_pubkeys: pointer to an array of pointers to taproot | ||||||
* x-only public keys (can be NULL if no taproot | ||||||
* inputs are used) | ||||||
* n_xonly_pubkeys: the number of taproot input public keys | ||||||
* plain_pubkeys: pointer to an array of pointers to non-taproot | ||||||
* public keys (can be NULL if no non-taproot | ||||||
* inputs are used) | ||||||
* n_plain_pubkeys: the number of non-taproot input public keys | ||||||
*/ | ||||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_create( | ||||||
josibake marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const secp256k1_context *ctx, | ||||||
secp256k1_silentpayments_recipient_public_data *public_data, | ||||||
const unsigned char *outpoint_smallest36, | ||||||
const secp256k1_xonly_pubkey * const *xonly_pubkeys, | ||||||
size_t n_xonly_pubkeys, | ||||||
const secp256k1_pubkey * const *plain_pubkeys, | ||||||
size_t n_plain_pubkeys | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); | ||||||
|
||||||
/** Serialize a silentpayments_recipient_public_data object into a 33-byte sequence. | ||||||
* | ||||||
* Returns: 1 always. | ||||||
* | ||||||
* Args: ctx: pointer to a context object | ||||||
* Out: output33: pointer to a 33-byte array to place the serialized | ||||||
* `silentpayments_recipient_public_data` in | ||||||
* In: public_data: pointer to an initialized silentpayments_recipient_public_data | ||||||
* object | ||||||
*/ | ||||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_serialize( | ||||||
josibake marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const secp256k1_context *ctx, | ||||||
unsigned char *output33, | ||||||
const secp256k1_silentpayments_recipient_public_data *public_data | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); | ||||||
|
||||||
/** Parse a 33-byte sequence into a silent_payments_public_data object. | ||||||
* | ||||||
* Returns: 1 if the data was able to be parsed. | ||||||
* 0 if the sequence is invalid (e.g. does not represent a valid | ||||||
* public key). | ||||||
* | ||||||
* Args: ctx: pointer to a context object. | ||||||
* Out: public_data: pointer to a silentpayments_recipient_public_data object. If 1 is | ||||||
* returned, it is set to a parsed version of input33. | ||||||
* In: input33: pointer to a serialized silentpayments_recipient_public_data. | ||||||
*/ | ||||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_public_data_parse( | ||||||
const secp256k1_context *ctx, | ||||||
secp256k1_silentpayments_recipient_public_data *public_data, | ||||||
const unsigned char *input33 | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); | ||||||
|
||||||
/** Callback function for label lookups | ||||||
* | ||||||
josibake marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* This function is implemented by the recipient to check if a value exists in | ||||||
* the recipients label cache during scanning. | ||||||
josibake marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* | ||||||
* For creating the labels cache, | ||||||
* `secp256k1_silentpayments_recipient_create_label_tweak` can be used. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* | ||||||
* Returns: pointer to the 32-byte label tweak if there is a match. | ||||||
* NULL pointer if there is no match. | ||||||
* | ||||||
* In: label: pointer to the label pubkey to check (computed during | ||||||
* scanning) | ||||||
* label_context: pointer to the recipients label cache. | ||||||
*/ | ||||||
typedef const unsigned char* (*secp256k1_silentpayments_label_lookup)(const unsigned char* label33, const void* label_context); | ||||||
|
||||||
/** Found outputs struct | ||||||
* | ||||||
* Struct for holding a found output along with data needed to spend it later. | ||||||
* | ||||||
* output: the x-only public key for the taproot output | ||||||
* tweak: the 32-byte tweak needed to spend the output | ||||||
* found_with_label: boolean value to indicate if the output was sent to a | ||||||
* labelled address. If true, label will be set with a valid | ||||||
* public key. | ||||||
* label: public key representing the label used. | ||||||
* If found_with_label = false, this is set to an invalid | ||||||
* public key. | ||||||
*/ | ||||||
typedef struct { | ||||||
secp256k1_xonly_pubkey output; | ||||||
unsigned char tweak[32]; | ||||||
int found_with_label; | ||||||
secp256k1_pubkey label; | ||||||
} secp256k1_silentpayments_found_output; | ||||||
|
||||||
/** Scan for Silent Payment transaction outputs. | ||||||
* | ||||||
* Given a public_data object, a recipient's scan key and spend public key, | ||||||
* and the relevant transaction outputs, scan for outputs belonging to | ||||||
* the recipient and return the tweak(s) needed for spending the output(s). An | ||||||
* optional label_lookup callback function and label_context can be passed if | ||||||
* the recipient uses labels. This allows for checking if a label exists in | ||||||
* the recipients label cache and retrieving the label tweak during scanning. | ||||||
* | ||||||
* For the labels cache, `secp256k1_silentpayments_recipient_create_label_tweak` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* can be used. | ||||||
* | ||||||
* Returns: 1 if output scanning was successful. | ||||||
* 0 if an error occured. | ||||||
* | ||||||
* Args: ctx: pointer to a context object | ||||||
* Out: found_outputs: pointer to an array of pointers to found | ||||||
* output objects. The found outputs array MUST | ||||||
* be initialized to be the same length as the | ||||||
* tx_outputs array | ||||||
* n_found_outputs: pointer to an integer indicating the final | ||||||
* size of the found outputs array. This number | ||||||
* represents the number of outputs found while | ||||||
* scanning (0 if none are found) | ||||||
* In: tx_outputs: pointer to the tx's x-only public key outputs | ||||||
* n_tx_outputs: the number of tx_outputs being scanned | ||||||
* recipient_scan_key: pointer to the recipient's scan key | ||||||
* public_data: pointer to the transaction public data | ||||||
* (see `_recipient_public_data_create`). | ||||||
* recipient_spend_pubkey: pointer to the recipient's spend pubkey | ||||||
* label_lookup: pointer to a callback function for looking up | ||||||
* a label value. This function takes a label | ||||||
* pubkey as an argument and returns a pointer to | ||||||
* the label tweak if the label exists, otherwise | ||||||
* returns a NULL pointer (NULL if labels are not | ||||||
* used) | ||||||
* label_context: pointer to a label context object (NULL if | ||||||
* labels are not used) | ||||||
*/ | ||||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_scan_outputs( | ||||||
const secp256k1_context *ctx, | ||||||
secp256k1_silentpayments_found_output **found_outputs, | ||||||
size_t *n_found_outputs, | ||||||
const secp256k1_xonly_pubkey * const *tx_outputs, | ||||||
size_t n_tx_outputs, | ||||||
const unsigned char *recipient_scan_key, | ||||||
const secp256k1_silentpayments_recipient_public_data *public_data, | ||||||
const secp256k1_pubkey *recipient_spend_pubkey, | ||||||
const secp256k1_silentpayments_label_lookup label_lookup, | ||||||
const void *label_context | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) | ||||||
SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8); | ||||||
|
||||||
/** Create Silent Payment shared secret. | ||||||
* | ||||||
* Given the public input data (secp256k1_silentpayments_recipient_public_data), | ||||||
* and the recipient's scan key, calculate the shared secret. | ||||||
* | ||||||
* The resulting shared secret is needed as input for creating silent payments | ||||||
josibake marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* outputs belonging to the same recipient scan public key. This function is | ||||||
* intended for light clients, i.e., scenarios where the caller does not have | ||||||
* access to the full transaction. If the caller does have access to the full | ||||||
* transaction, `secp256k1_silentpayments_recipient_scan_outputs` should be | ||||||
* used instead. | ||||||
* | ||||||
* Returns: 1 if shared secret creation was successful. 0 if an error occured. | ||||||
* Args: ctx: pointer to a context object | ||||||
* Out: shared_secret33: pointer to the resulting 33-byte shared secret | ||||||
* In: recipient_scan_key: pointer to the recipient's scan key | ||||||
* public_data: pointer to the input public key sum, tweaked | ||||||
* with the input_hash (see | ||||||
* `_recipient_public_data_create`) | ||||||
*/ | ||||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_shared_secret( | ||||||
josibake marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const secp256k1_context *ctx, | ||||||
unsigned char *shared_secret33, | ||||||
const unsigned char *recipient_scan_key, | ||||||
const secp256k1_silentpayments_recipient_public_data *public_data | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||||||
|
||||||
/** Create Silent Payment output public key. | ||||||
* | ||||||
* Given a shared_secret, a public key B_spend, and an output counter k, | ||||||
* create an output public key. | ||||||
* | ||||||
* This function is used by the recipient when scanning for outputs without | ||||||
* access to the transaction outputs (e.g. using BIP158 block filters). When | ||||||
* scanning with this function, it is the scanners responsibility to determine | ||||||
* if the generated output exists in a block before proceeding to the next | ||||||
* value of `k`. | ||||||
* | ||||||
* Returns: 1 if output creation was successful. 0 if an error occured. | ||||||
* Args: ctx: pointer to a context object | ||||||
* Out: P_output_xonly: pointer to the resulting output x-only pubkey | ||||||
* In: shared_secret33: shared secret, derived from either sender's | ||||||
* or recipient's perspective with routines from | ||||||
* above | ||||||
* recipient_spend_pubkey: pointer to the recipient's spend pubkey | ||||||
* (labelled or unlabelled) | ||||||
* k: output counter (initially set to 0, must be | ||||||
* incremented for each additional output created | ||||||
* or after each output found when scanning) | ||||||
*/ | ||||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_output_pubkey( | ||||||
const secp256k1_context *ctx, | ||||||
secp256k1_xonly_pubkey *P_output_xonly, | ||||||
const unsigned char *shared_secret33, | ||||||
const secp256k1_pubkey *recipient_spend_pubkey, | ||||||
unsigned int k | ||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); | ||||||
|
||||||
#ifdef __cplusplus | ||||||
} | ||||||
#endif | ||||||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.