mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
687155df6b Merge bitcoin-core/secp256k1#1897: tests: check results before using outputs 8a700a355e Merge bitcoin-core/secp256k1#1907: release cleanup: bump version after 0.8.0 78657bf28b release cleanup: bump version after 0.8.0 6e2c8bc4ec Merge bitcoin-core/secp256k1#1906: release: prepare for 0.8.0 5840c19b4e release: prepare for 0.8.0 3873647bfb Merge bitcoin-core/secp256k1#1904: sha256: cross-check caller supplied compression function c84ea46561 sha256: cross-check caller supplied compression function f8c75f89a5 Merge bitcoin-core/secp256k1#1903: changelog: add entry for #1821 2076b06a42 changelog: add entry for #1821 7fecac74ae Merge bitcoin-core/secp256k1#1709: release-process: Add signing of tarball plus minor improvements 12d9cfd86e release-process: Add attaching output of check-abi.sh to PR b0a0ae8246 release-process: Add "cleaning up" 4a73b1ae27 release-process: Fix nits fae22e777e release-process: Add signing of tarball 34f00ca9d9 release-process: Refactor 1c2933bb87 Merge bitcoin-core/secp256k1#1902: changelog: add entry for #1859 51fc633e3e changelog: add entry for #1859 d77f44e9cd Merge bitcoin-core/secp256k1#1863: ellswift: don't declassify or leave sk in sha256 buffer 0ae17e304e ellswift: don't declassify or leave sk in sha256 buffer 0a9e788901 Merge bitcoin-core/secp256k1#1900: Use SHA256 override for pointers to known aux functions 300c9bb26d Merge bitcoin-core/secp256k1#1901: changelog: Add entry for #1869 44ba8cd7df changelog: Add entry for #1869 4147f8bdf6 header: Add note on SHA256 override and aux functions ed091bc49d ecdsa/ecdh: Use SHA256 override if known noncefp/hashfp is passed 209ed1025b Merge bitcoin-core/secp256k1#1886: Remove deprecated `secp256k1_context_no_precomp` pointer bf435856bb Remove deprecated `secp256k1_context_no_precomp` pointer 528863e61f Merge bitcoin-core/secp256k1#1869: Remove SECP256K1_GNUC_PREREQ macro 3a73d473f2 Merge bitcoin-core/secp256k1#1776: Remove deprecated `secp256k1_schnorrsig_sign` alias e14756bd25 Remove deprecated `secp256k1_schnorrsig_sign` alias 7151e3b843 Merge bitcoin-core/secp256k1#1899: changelog: add missing entries for #1777 and #1860 f52eb393c4 changelog: add missing entries for #1777 and #1860 0f6baf319f Merge bitcoin-core/secp256k1#1896: silentpayments: address #1765 follow-ups, add changelog entry a2ad68cd81 ec: check pubkey sort test results 93280c2291 silentpayments: check test serialization b8de1bc30f musig: check infinity test setup 0618af8131 extrakeys: check test pubkey loads 1d3f72d3fa recovery: check exhaustive API results 564afb0b06 ellswift: check test operation results 658c7edc24 tests: check exhaustive ecmult success d9ac2ee5e6 Add changelog entry for silentpayments module 0fa38f3d29 silentpayments: API docs and internal comment followups ae075d7cbd hash: Include secp256k1.h directly dba4d937a9 include: Remove SECP256K1_GNUC_PREREQ macro 09870e9c54 Use __GNUC__ instead of SECP256K1_GNUC_PREREQ git-subtree-dir: src/secp256k1 git-subtree-split: 687155df6b76f1da1639a6f0923b69e6beaa3a09
466 lines
19 KiB
C
466 lines
19 KiB
C
/*************************************************************************
|
|
* To the extent possible under law, the author(s) have dedicated all *
|
|
* copyright and related and neighboring rights to the software in this *
|
|
* file to the public domain worldwide. This software is distributed *
|
|
* without any warranty. For the CC0 Public Domain Dedication, see *
|
|
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
|
|
*************************************************************************/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <secp256k1_extrakeys.h>
|
|
#include <secp256k1_silentpayments.h>
|
|
|
|
#include "examples_util.h"
|
|
|
|
#define N_INPUTS 2
|
|
#define N_OUTPUTS 3
|
|
|
|
/* Static data for Bob and Carol's Silent Payments addresses */
|
|
static unsigned char smallest_outpoint[36] = {
|
|
0x16, 0x9e, 0x1e, 0x83, 0xe9, 0x30, 0x85, 0x33, 0x91,
|
|
0xbc, 0x6f, 0x35, 0xf6, 0x05, 0xc6, 0x75, 0x4c, 0xfe,
|
|
0xad, 0x57, 0xcf, 0x83, 0x87, 0x63, 0x9d, 0x3b, 0x40,
|
|
0x96, 0xc5, 0x4f, 0x18, 0xf4, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
static unsigned char bob_scan_key[32] = {
|
|
0xa8, 0x90, 0x54, 0xc9, 0x5b, 0xe3, 0xc3, 0x01,
|
|
0x56, 0x65, 0x74, 0xf2, 0xaa, 0x93, 0xad, 0xe0,
|
|
0x51, 0x85, 0x09, 0x03, 0xa6, 0x9c, 0xbd, 0xd1,
|
|
0xd4, 0x7e, 0xae, 0x26, 0x3d, 0x7b, 0xc0, 0x31
|
|
};
|
|
static unsigned char bob_spend_key[32] = {
|
|
0x9d, 0x6a, 0xd8, 0x55, 0xce, 0x34, 0x17, 0xef,
|
|
0x84, 0xe8, 0x36, 0x89, 0x2e, 0x5a, 0x56, 0x39,
|
|
0x2b, 0xfb, 0xa0, 0x5f, 0xa5, 0xd9, 0x7c, 0xce,
|
|
0xa3, 0x0e, 0x26, 0x6f, 0x54, 0x0e, 0x08, 0xb3
|
|
};
|
|
static unsigned char bob_scan_and_spend_pubkeys[2][33] = {
|
|
{
|
|
0x02, 0x15, 0x40, 0xae, 0xa8, 0x97, 0x54, 0x7a,
|
|
0xd4, 0x39, 0xb4, 0xe0, 0xf6, 0x09, 0xe5, 0xf0,
|
|
0xfa, 0x63, 0xde, 0x89, 0xab, 0x11, 0xed, 0xe3,
|
|
0x1e, 0x8c, 0xde, 0x4b, 0xe2, 0x19, 0x42, 0x5f, 0x23
|
|
},
|
|
{
|
|
0x02, 0x5c, 0xc9, 0x85, 0x6d, 0x6f, 0x83, 0x75,
|
|
0x35, 0x0e, 0x12, 0x39, 0x78, 0xda, 0xac, 0x20,
|
|
0x0c, 0x26, 0x0c, 0xb5, 0xb5, 0xae, 0x83, 0x10,
|
|
0x6c, 0xab, 0x90, 0x48, 0x4d, 0xcd, 0x8f, 0xcf, 0x36
|
|
}
|
|
};
|
|
static unsigned char carol_scan_key[32] = {
|
|
0x04, 0xb2, 0xa4, 0x11, 0x63, 0x5c, 0x09, 0x77,
|
|
0x59, 0xaa, 0xcd, 0x0f, 0x00, 0x5a, 0x4c, 0x82,
|
|
0xc8, 0xc9, 0x28, 0x62, 0xc6, 0xfc, 0x28, 0x4b,
|
|
0x80, 0xb8, 0xef, 0xeb, 0xc2, 0x0c, 0x3d, 0x17
|
|
};
|
|
static unsigned char carol_address[2][33] = {
|
|
{
|
|
0x03, 0xbb, 0xc6, 0x3f, 0x12, 0x74, 0x5d, 0x3b,
|
|
0x9e, 0x9d, 0x24, 0xc6, 0xcd, 0x7a, 0x1e, 0xfe,
|
|
0xba, 0xd0, 0xa7, 0xf4, 0x69, 0x23, 0x2f, 0xbe,
|
|
0xcf, 0x31, 0xfb, 0xa7, 0xb4, 0xf7, 0xdd, 0xed, 0xa8
|
|
},
|
|
{
|
|
0x03, 0x81, 0xeb, 0x9a, 0x9a, 0x9e, 0xc7, 0x39,
|
|
0xd5, 0x27, 0xc1, 0x63, 0x1b, 0x31, 0xb4, 0x21,
|
|
0x56, 0x6f, 0x5c, 0x2a, 0x47, 0xb4, 0xab, 0x5b,
|
|
0x1f, 0x6a, 0x68, 0x6d, 0xfb, 0x68, 0xea, 0xb7, 0x16
|
|
}
|
|
};
|
|
|
|
/** Labels
|
|
*
|
|
* The structs and callback function are implemented here as a demonstration
|
|
* of how the label lookup callback is meant to query a label cache and return
|
|
* the label tweak when a match is found. This is for demonstration purposes
|
|
* only and not optimized. In production, it is expected that the
|
|
* caller will be using a much more performant data structure for storing and
|
|
* querying labels, like e.g. a hash table with O(1) lookup cost.
|
|
*
|
|
* Recipients not using labels can ignore these steps and simply pass `NULL`
|
|
* for the label_lookup and label_context arguments:
|
|
*
|
|
* secp256k1_silentpayments_recipient_scan_outputs(..., NULL, NULL);
|
|
*/
|
|
|
|
struct label_cache_entry {
|
|
unsigned char label[33];
|
|
unsigned char label_tweak[32];
|
|
};
|
|
|
|
struct labels_cache {
|
|
size_t entries_used;
|
|
struct label_cache_entry entries[5];
|
|
};
|
|
|
|
const unsigned char* label_lookup(
|
|
const unsigned char* label33,
|
|
const void* cache_ptr
|
|
) {
|
|
const struct labels_cache* cache = (const struct labels_cache*)cache_ptr;
|
|
size_t i;
|
|
for (i = 0; i < cache->entries_used; i++) {
|
|
if (memcmp(cache->entries[i].label, label33, 33) == 0) {
|
|
return cache->entries[i].label_tweak;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int main(void) {
|
|
unsigned char randomize[32];
|
|
unsigned char serialized_xonly[32];
|
|
secp256k1_xonly_pubkey tx_inputs[N_INPUTS];
|
|
const secp256k1_xonly_pubkey *tx_input_ptrs[N_INPUTS];
|
|
secp256k1_xonly_pubkey tx_outputs[N_OUTPUTS];
|
|
secp256k1_xonly_pubkey *tx_output_ptrs[N_OUTPUTS];
|
|
secp256k1_silentpayments_found_output found_outputs[N_OUTPUTS];
|
|
secp256k1_silentpayments_found_output *found_output_ptrs[N_OUTPUTS];
|
|
secp256k1_silentpayments_prevouts_summary prevouts_summary;
|
|
secp256k1_pubkey unlabeled_spend_pubkey;
|
|
struct labels_cache bob_labels_cache;
|
|
unsigned char bob_address[2][33];
|
|
int ret;
|
|
size_t i;
|
|
uint32_t n_found_outputs;
|
|
|
|
/* Before we can call actual API functions, we need to create a "context" */
|
|
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
|
if (!fill_random(randomize, sizeof(randomize))) {
|
|
printf("Failed to generate randomness\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
/* Randomizing the context is recommended to protect against side-channel
|
|
* leakage. See `secp256k1_context_randomize` in secp256k1.h for more
|
|
* information about it. This should never fail. */
|
|
ret = secp256k1_context_randomize(ctx, randomize);
|
|
assert(ret);
|
|
|
|
/* Set up the pointer arrays. These will be used for sending and scanning. */
|
|
for (i = 0; i < N_INPUTS; i++) {
|
|
tx_input_ptrs[i] = &tx_inputs[i];
|
|
}
|
|
for (i = 0; i < N_OUTPUTS; i++) {
|
|
tx_output_ptrs[i] = &tx_outputs[i];
|
|
found_output_ptrs[i] = &found_outputs[i];
|
|
}
|
|
|
|
/*** Create a labeled Silent Payments address (Bob) ***/
|
|
{
|
|
size_t len = 33;
|
|
secp256k1_silentpayments_label label;
|
|
secp256k1_pubkey labeled_spend_pubkey;
|
|
/* Per BIP-352, the label integer value m = 0 is reserved for the receiver's own
|
|
* change, so only values m >= 1 must be used for publishing addresses. */
|
|
uint32_t m = 1;
|
|
|
|
/* Load Bob's spend public key */
|
|
ret = secp256k1_ec_pubkey_parse(ctx,
|
|
&unlabeled_spend_pubkey,
|
|
bob_scan_and_spend_pubkeys[1],
|
|
33
|
|
);
|
|
assert(ret);
|
|
/* Create a (label_tweak, label) pair and add them to the labels cache.
|
|
*
|
|
* Bob MUST keep track of all of the labels he has used by adding them
|
|
* to the labels cache. Otherwise, he will not find outputs sent to his
|
|
* labeled addresses when scanning. */
|
|
ret = secp256k1_silentpayments_recipient_label_create(ctx,
|
|
&label,
|
|
bob_labels_cache.entries[0].label_tweak,
|
|
bob_scan_key,
|
|
m
|
|
);
|
|
if (!ret) {
|
|
printf("Something went wrong, event with negligible probability happened.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
ret = secp256k1_silentpayments_recipient_label_serialize(ctx, bob_labels_cache.entries[0].label, &label);
|
|
assert(ret);
|
|
bob_labels_cache.entries_used = 1;
|
|
/* Now that the labels cache has been updated, Bob creates his labeled
|
|
* Silent Payments address and publishes it.
|
|
*/
|
|
ret = secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(ctx, &labeled_spend_pubkey, &unlabeled_spend_pubkey, &label);
|
|
if (!ret) {
|
|
printf("Something went wrong, event with negligible probability happened.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
memcpy(bob_address[0], bob_scan_and_spend_pubkeys[0], 33);
|
|
ret = secp256k1_ec_pubkey_serialize(ctx,
|
|
bob_address[1],
|
|
&len,
|
|
&labeled_spend_pubkey,
|
|
SECP256K1_EC_COMPRESSED
|
|
);
|
|
assert(ret);
|
|
}
|
|
/*** Sending (Alice) ***/
|
|
{
|
|
secp256k1_keypair sender_keypairs[N_INPUTS];
|
|
const secp256k1_keypair *sender_keypair_ptrs[N_INPUTS];
|
|
secp256k1_silentpayments_recipient recipients[N_OUTPUTS];
|
|
const secp256k1_silentpayments_recipient *recipient_ptrs[N_OUTPUTS];
|
|
/* 2D array for holding multiple public key pairs. The second index, i.e., [2],
|
|
* is to represent the spend and scan public keys. */
|
|
unsigned char (*sp_addresses[N_OUTPUTS])[2][33];
|
|
unsigned char seckey[32];
|
|
|
|
/*** Generate secret keys for the sender ***
|
|
*
|
|
* In this example, only taproot inputs are used but the function can be
|
|
* called with a mix of taproot seckeys and plain seckeys. Taproot
|
|
* seckeys are passed as keypairs to allow the sending function to check
|
|
* if the secret keys need to be negated without needing to do an
|
|
* expensive pubkey generation. This is not needed for plain seckeys
|
|
* since there is no need for negation.
|
|
*
|
|
* The public key from each input keypair is saved in the `tx_inputs`
|
|
* array. This array will be used later in the example to represent the
|
|
* public keys the recipient will extract from the transaction inputs.
|
|
*
|
|
* If the secret key is zero or out of range (bigger than secp256k1's
|
|
* order), fail. Note that the probability of this happening is
|
|
* negligible. */
|
|
for (i = 0; i < N_INPUTS; i++) {
|
|
if (!fill_random(seckey, sizeof(seckey))) {
|
|
printf("Failed to generate randomness\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
/* Try to create a keypair with a valid context, it should only fail
|
|
* if the secret key is zero or out of range. */
|
|
if (secp256k1_keypair_create(ctx, &sender_keypairs[i], seckey)) {
|
|
sender_keypair_ptrs[i] = &sender_keypairs[i];
|
|
ret = secp256k1_keypair_xonly_pub(
|
|
ctx,
|
|
&tx_inputs[i],
|
|
NULL,
|
|
&sender_keypairs[i]
|
|
);
|
|
assert(ret);
|
|
} else {
|
|
printf("Failed to create keypair\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
/*** Create the recipient objects ***/
|
|
|
|
/* Alice is sending to Bob and Carol in this transaction:
|
|
*
|
|
* 1. One output to Bob's labeled address
|
|
* 2. Two outputs for Carol
|
|
*
|
|
* To create multiple outputs for Carol, Alice simply passes Carol's
|
|
* Silent Payments address multiple times.
|
|
*/
|
|
sp_addresses[0] = &carol_address;
|
|
sp_addresses[1] = &bob_address;
|
|
sp_addresses[2] = &carol_address;
|
|
for (i = 0; i < N_OUTPUTS; i++) {
|
|
ret = secp256k1_ec_pubkey_parse(ctx,
|
|
&recipients[i].scan_pubkey,
|
|
(*(sp_addresses[i]))[0],
|
|
33
|
|
);
|
|
ret &= secp256k1_ec_pubkey_parse(ctx,
|
|
&recipients[i].spend_pubkey,
|
|
(*(sp_addresses[i]))[1],
|
|
33
|
|
);
|
|
if (!ret) {
|
|
printf("Something went wrong, this is not a valid Silent Payments address.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Alice creates the recipient objects and adds the index of the
|
|
* original ordering (the ordering of the `sp_addresses` array) to
|
|
* each object. This index is used to return the generated outputs
|
|
* in the original ordering so that Alice can match up the generated
|
|
* outputs with the correct amounts.
|
|
*/
|
|
recipients[i].index = i;
|
|
recipient_ptrs[i] = &recipients[i];
|
|
}
|
|
ret = secp256k1_silentpayments_sender_create_outputs(ctx,
|
|
tx_output_ptrs,
|
|
recipient_ptrs, N_OUTPUTS,
|
|
smallest_outpoint,
|
|
sender_keypair_ptrs, N_INPUTS,
|
|
NULL, 0
|
|
);
|
|
if (!ret) {
|
|
printf("Something went wrong, group limit exceeded or input secret keys sum to zero.\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
printf("Alice created the following outputs for Bob and Carol:\n");
|
|
for (i = 0; i < N_OUTPUTS; i++) {
|
|
printf(" ");
|
|
ret = secp256k1_xonly_pubkey_serialize(ctx,
|
|
serialized_xonly,
|
|
&tx_outputs[i]
|
|
);
|
|
assert(ret);
|
|
print_hex(serialized_xonly, sizeof(serialized_xonly));
|
|
}
|
|
/* It's best practice to try to clear secrets from memory after using
|
|
* them. This is done because some bugs can allow an attacker to leak
|
|
* memory, for example through "out of bounds" array access (see
|
|
* Heartbleed), or the OS swapping them to disk. Hence, we overwrite the
|
|
* secret key buffer with zeros.
|
|
*
|
|
* Here we are preventing these writes from being optimized out, as any
|
|
* good compiler will remove any writes that aren't used. */
|
|
secure_erase(seckey, sizeof(seckey));
|
|
for (i = 0; i < N_INPUTS; i++) {
|
|
secure_erase(&sender_keypairs[i], sizeof(sender_keypairs[i]));
|
|
}
|
|
}
|
|
|
|
/*** Receiving ***/
|
|
{
|
|
{
|
|
/*** Scanning as a full node (Bob) ***
|
|
*
|
|
* Since Bob has access to the full transaction, scanning is simple:
|
|
*
|
|
* 1. Collect the relevant prevouts from the transaction and call
|
|
* `secp256k1_silentpayments_recipient_prevouts_summary_create`
|
|
* 2. Call `secp256k1_silentpayments_recipient_scan_outputs`
|
|
*/
|
|
ret = secp256k1_silentpayments_recipient_prevouts_summary_create(ctx,
|
|
&prevouts_summary,
|
|
smallest_outpoint,
|
|
tx_input_ptrs, N_INPUTS,
|
|
NULL, 0
|
|
);
|
|
if (!ret) {
|
|
/* We need to always check that the prevouts data object is valid
|
|
* before proceeding.
|
|
*/
|
|
printf("This transaction is not valid for Silent Payments, skipping.\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* Scan the transaction */
|
|
n_found_outputs = 0;
|
|
ret = secp256k1_silentpayments_recipient_scan_outputs(ctx,
|
|
found_output_ptrs, &n_found_outputs,
|
|
(const secp256k1_xonly_pubkey**)tx_output_ptrs, N_OUTPUTS,
|
|
bob_scan_key,
|
|
&prevouts_summary,
|
|
&unlabeled_spend_pubkey,
|
|
label_lookup, &bob_labels_cache /* NULL, NULL for no labels */
|
|
);
|
|
if (!ret) {
|
|
printf("This transaction is not valid for Silent Payments, skipping.\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
if (n_found_outputs > 0) {
|
|
secp256k1_keypair kp;
|
|
secp256k1_xonly_pubkey xonly_output;
|
|
unsigned char full_seckey[32];
|
|
|
|
printf("\n");
|
|
printf("Bob found the following outputs: \n");
|
|
for (i = 0; i < n_found_outputs; i++) {
|
|
printf(" ");
|
|
ret = secp256k1_xonly_pubkey_serialize(ctx,
|
|
serialized_xonly,
|
|
&found_outputs[i].output
|
|
);
|
|
assert(ret);
|
|
print_hex(serialized_xonly, sizeof(serialized_xonly));
|
|
|
|
/* Verify that this output is spendable by Bob by reconstructing the full
|
|
* secret key for the xonly output.
|
|
*
|
|
* This is done by adding the tweak from the transaction to Bob's spend key.
|
|
* If the output was sent to a labeled address, the label tweak has already
|
|
* been added to the tweak in `secp256k1_silentpayments_found_output`.
|
|
*
|
|
* To verify that we are able to sign for this output, it is sufficient to
|
|
* check that the public key generated from `full_seckey` matches the output
|
|
* in the transaction. For a full example on signing for a taproot ouput,
|
|
* see `examples/schnorr.c`.
|
|
*/
|
|
memcpy(&full_seckey, &bob_spend_key, 32);
|
|
ret = secp256k1_ec_seckey_tweak_add(ctx, full_seckey, found_outputs[i].tweak);
|
|
ret &= secp256k1_keypair_create(ctx, &kp, full_seckey);
|
|
ret &= secp256k1_keypair_xonly_pub(
|
|
ctx,
|
|
&xonly_output,
|
|
NULL,
|
|
&kp
|
|
);
|
|
/* We assert here because the only way the seckey_tweak_add operation can fail
|
|
* is if the tweak is the negation of Bob's spend key.
|
|
*
|
|
* We also assert that the generated public key matches the transaction output,
|
|
* as it should be impossible for a mismatch at this point considering the
|
|
* scanning function completed without errors and indicated found outputs.
|
|
*/
|
|
assert(ret);
|
|
assert(secp256k1_xonly_pubkey_cmp(ctx, &xonly_output, &found_outputs[i].output) == 0);
|
|
secure_erase(full_seckey, sizeof(full_seckey));
|
|
}
|
|
} else {
|
|
printf("Bob did not find any outputs in this transaction.\n");
|
|
}
|
|
}
|
|
{
|
|
/*** Scanning as a full node (Carol) ***/
|
|
/* TODO: switch this part to light client scanning once it is supported */
|
|
|
|
/* Load Carol's spend public key. */
|
|
ret = secp256k1_ec_pubkey_parse(ctx,
|
|
&unlabeled_spend_pubkey,
|
|
carol_address[1],
|
|
33
|
|
);
|
|
assert(ret);
|
|
|
|
n_found_outputs = 0;
|
|
ret = secp256k1_silentpayments_recipient_scan_outputs(ctx,
|
|
found_output_ptrs, &n_found_outputs,
|
|
(const secp256k1_xonly_pubkey**)tx_output_ptrs, N_OUTPUTS,
|
|
carol_scan_key,
|
|
&prevouts_summary,
|
|
&unlabeled_spend_pubkey,
|
|
NULL, NULL /* NULL, NULL for no labels */
|
|
);
|
|
if (!ret) {
|
|
printf("This transaction is not valid for Silent Payments, skipping.\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
if (n_found_outputs > 0) {
|
|
/* Carol would spend these outputs the same as Bob, by tweaking her
|
|
* spend key with the tweak corresponding to the found output. See above
|
|
* for an example for Bob's outputs. */
|
|
printf("\n");
|
|
printf("Carol found the following outputs: \n");
|
|
for (i = 0; i < n_found_outputs; i++) {
|
|
printf(" ");
|
|
ret = secp256k1_xonly_pubkey_serialize(ctx,
|
|
serialized_xonly,
|
|
&found_outputs[i].output
|
|
);
|
|
assert(ret);
|
|
print_hex(serialized_xonly, sizeof(serialized_xonly));
|
|
}
|
|
} else {
|
|
printf("Carol did not find any outputs in this transaction.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This will clear everything from the context and free the memory */
|
|
secp256k1_context_destroy(ctx);
|
|
return EXIT_SUCCESS;
|
|
}
|