Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1/*************************************************************************2* To the extent possible under law, the author(s) have dedicated all *3* copyright and related and neighboring rights to the software in this *4* file to the public domain worldwide. This software is distributed *5* without any warranty. For the CC0 Public Domain Dedication, see *6* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *7*************************************************************************/9#include <assert.h>10#include <stdio.h>11#include <stdlib.h>12#include <string.h>14#include <secp256k1_extrakeys.h>15#include <secp256k1_silentpayments.h>17#include "examples_util.h"19#define N_INPUTS 220#define N_OUTPUTS 322/* Static data for Bob and Carol's Silent Payments addresses */23static unsigned char smallest_outpoint[36] = {240x16, 0x9e, 0x1e, 0x83, 0xe9, 0x30, 0x85, 0x33, 0x91,250xbc, 0x6f, 0x35, 0xf6, 0x05, 0xc6, 0x75, 0x4c, 0xfe,260xad, 0x57, 0xcf, 0x83, 0x87, 0x63, 0x9d, 0x3b, 0x40,270x96, 0xc5, 0x4f, 0x18, 0xf4, 0x00, 0x00, 0x00, 0x0028};29static unsigned char bob_scan_key[32] = {300xa8, 0x90, 0x54, 0xc9, 0x5b, 0xe3, 0xc3, 0x01,310x56, 0x65, 0x74, 0xf2, 0xaa, 0x93, 0xad, 0xe0,320x51, 0x85, 0x09, 0x03, 0xa6, 0x9c, 0xbd, 0xd1,330xd4, 0x7e, 0xae, 0x26, 0x3d, 0x7b, 0xc0, 0x3134};35static unsigned char bob_spend_key[32] = {360x9d, 0x6a, 0xd8, 0x55, 0xce, 0x34, 0x17, 0xef,370x84, 0xe8, 0x36, 0x89, 0x2e, 0x5a, 0x56, 0x39,380x2b, 0xfb, 0xa0, 0x5f, 0xa5, 0xd9, 0x7c, 0xce,390xa3, 0x0e, 0x26, 0x6f, 0x54, 0x0e, 0x08, 0xb340};41static unsigned char bob_scan_and_spend_pubkeys[2][33] = {42{430x02, 0x15, 0x40, 0xae, 0xa8, 0x97, 0x54, 0x7a,440xd4, 0x39, 0xb4, 0xe0, 0xf6, 0x09, 0xe5, 0xf0,450xfa, 0x63, 0xde, 0x89, 0xab, 0x11, 0xed, 0xe3,460x1e, 0x8c, 0xde, 0x4b, 0xe2, 0x19, 0x42, 0x5f, 0x2347},48{490x02, 0x5c, 0xc9, 0x85, 0x6d, 0x6f, 0x83, 0x75,500x35, 0x0e, 0x12, 0x39, 0x78, 0xda, 0xac, 0x20,510x0c, 0x26, 0x0c, 0xb5, 0xb5, 0xae, 0x83, 0x10,520x6c, 0xab, 0x90, 0x48, 0x4d, 0xcd, 0x8f, 0xcf, 0x3653}54};55static unsigned char carol_scan_key[32] = {560x04, 0xb2, 0xa4, 0x11, 0x63, 0x5c, 0x09, 0x77,570x59, 0xaa, 0xcd, 0x0f, 0x00, 0x5a, 0x4c, 0x82,580xc8, 0xc9, 0x28, 0x62, 0xc6, 0xfc, 0x28, 0x4b,590x80, 0xb8, 0xef, 0xeb, 0xc2, 0x0c, 0x3d, 0x1760};61static unsigned char carol_address[2][33] = {62{630x03, 0xbb, 0xc6, 0x3f, 0x12, 0x74, 0x5d, 0x3b,640x9e, 0x9d, 0x24, 0xc6, 0xcd, 0x7a, 0x1e, 0xfe,650xba, 0xd0, 0xa7, 0xf4, 0x69, 0x23, 0x2f, 0xbe,660xcf, 0x31, 0xfb, 0xa7, 0xb4, 0xf7, 0xdd, 0xed, 0xa867},68{690x03, 0x81, 0xeb, 0x9a, 0x9a, 0x9e, 0xc7, 0x39,700xd5, 0x27, 0xc1, 0x63, 0x1b, 0x31, 0xb4, 0x21,710x56, 0x6f, 0x5c, 0x2a, 0x47, 0xb4, 0xab, 0x5b,720x1f, 0x6a, 0x68, 0x6d, 0xfb, 0x68, 0xea, 0xb7, 0x1673}74};76/** Labels77*78* The structs and callback function are implemented here as a demonstration79* of how the label lookup callback is meant to query a label cache and return80* the label tweak when a match is found. This is for demonstration purposes81* only and not optimized. In production, it is expected that the82* caller will be using a much more performant data structure for storing and83* querying labels, like e.g. a hash table with O(1) lookup cost.84*85* Recipients not using labels can ignore these steps and simply pass `NULL`86* for the label_lookup and label_context arguments:87*88* secp256k1_silentpayments_recipient_scan_outputs(..., NULL, NULL);89*/91struct label_cache_entry {92unsigned char label[33];93unsigned char label_tweak[32];94};96struct labels_cache {97size_t entries_used;98struct label_cache_entry entries[5];99};101const unsigned char* label_lookup(102const unsigned char* label33,103const void* cache_ptr104) {105const struct labels_cache* cache = (const struct labels_cache*)cache_ptr;106size_t i;107for (i = 0; i < cache->entries_used; i++) {108if (memcmp(cache->entries[i].label, label33, 33) == 0) {109return cache->entries[i].label_tweak;110}111}112return NULL;113}115int main(void) {116unsigned char randomize[32];117unsigned char serialized_xonly[32];118secp256k1_xonly_pubkey tx_inputs[N_INPUTS];119const secp256k1_xonly_pubkey *tx_input_ptrs[N_INPUTS];120secp256k1_xonly_pubkey tx_outputs[N_OUTPUTS];121secp256k1_xonly_pubkey *tx_output_ptrs[N_OUTPUTS];122secp256k1_silentpayments_found_output found_outputs[N_OUTPUTS];123secp256k1_silentpayments_found_output *found_output_ptrs[N_OUTPUTS];124secp256k1_silentpayments_prevouts_summary prevouts_summary;125secp256k1_pubkey unlabeled_spend_pubkey;126struct labels_cache bob_labels_cache;127unsigned char bob_address[2][33];128int ret;129size_t i;130uint32_t n_found_outputs;132/* Before we can call actual API functions, we need to create a "context" */133secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);134if (!fill_random(randomize, sizeof(randomize))) {135printf("Failed to generate randomness\n");136return EXIT_FAILURE;137}138/* Randomizing the context is recommended to protect against side-channel139* leakage. See `secp256k1_context_randomize` in secp256k1.h for more140* information about it. This should never fail. */141ret = secp256k1_context_randomize(ctx, randomize);142assert(ret);144/* Set up the pointer arrays. These will be used for sending and scanning. */145for (i = 0; i < N_INPUTS; i++) {146tx_input_ptrs[i] = &tx_inputs[i];147}148for (i = 0; i < N_OUTPUTS; i++) {149tx_output_ptrs[i] = &tx_outputs[i];150found_output_ptrs[i] = &found_outputs[i];151}153/*** Create a labeled Silent Payments address (Bob) ***/154{155size_t len = 33;156secp256k1_silentpayments_label label;157secp256k1_pubkey labeled_spend_pubkey;158/* Per BIP-352, the label integer value m = 0 is reserved for the receiver's own159* change, so only values m >= 1 must be used for publishing addresses. */160uint32_t m = 1;162/* Load Bob's spend public key */163ret = secp256k1_ec_pubkey_parse(ctx,164&unlabeled_spend_pubkey,165bob_scan_and_spend_pubkeys[1],16633167);168assert(ret);169/* Create a (label_tweak, label) pair and add them to the labels cache.170*171* Bob MUST keep track of all of the labels he has used by adding them172* to the labels cache. Otherwise, he will not find outputs sent to his173* labeled addresses when scanning. */174ret = secp256k1_silentpayments_recipient_label_create(ctx,175&label,176bob_labels_cache.entries[0].label_tweak,177bob_scan_key,178m179);180if (!ret) {181printf("Something went wrong, event with negligible probability happened.\n");182return EXIT_FAILURE;183}184ret = secp256k1_silentpayments_recipient_label_serialize(ctx, bob_labels_cache.entries[0].label, &label);185assert(ret);186bob_labels_cache.entries_used = 1;187/* Now that the labels cache has been updated, Bob creates his labeled188* Silent Payments address and publishes it.189*/190ret = secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(ctx, &labeled_spend_pubkey, &unlabeled_spend_pubkey, &label);191if (!ret) {192printf("Something went wrong, event with negligible probability happened.\n");193return EXIT_FAILURE;194}195memcpy(bob_address[0], bob_scan_and_spend_pubkeys[0], 33);196ret = secp256k1_ec_pubkey_serialize(ctx,197bob_address[1],198&len,199&labeled_spend_pubkey,200SECP256K1_EC_COMPRESSED201);202assert(ret);203}204/*** Sending (Alice) ***/205{206secp256k1_keypair sender_keypairs[N_INPUTS];207const secp256k1_keypair *sender_keypair_ptrs[N_INPUTS];208secp256k1_silentpayments_recipient recipients[N_OUTPUTS];209const secp256k1_silentpayments_recipient *recipient_ptrs[N_OUTPUTS];210/* 2D array for holding multiple public key pairs. The second index, i.e., [2],211* is to represent the spend and scan public keys. */212unsigned char (*sp_addresses[N_OUTPUTS])[2][33];213unsigned char seckey[32];215/*** Generate secret keys for the sender ***216*217* In this example, only taproot inputs are used but the function can be218* called with a mix of taproot seckeys and plain seckeys. Taproot219* seckeys are passed as keypairs to allow the sending function to check220* if the secret keys need to be negated without needing to do an221* expensive pubkey generation. This is not needed for plain seckeys222* since there is no need for negation.223*224* The public key from each input keypair is saved in the `tx_inputs`225* array. This array will be used later in the example to represent the226* public keys the recipient will extract from the transaction inputs.227*228* If the secret key is zero or out of range (bigger than secp256k1's229* order), fail. Note that the probability of this happening is230* negligible. */231for (i = 0; i < N_INPUTS; i++) {232if (!fill_random(seckey, sizeof(seckey))) {233printf("Failed to generate randomness\n");234return EXIT_FAILURE;235}236/* Try to create a keypair with a valid context, it should only fail237* if the secret key is zero or out of range. */238if (secp256k1_keypair_create(ctx, &sender_keypairs[i], seckey)) {239sender_keypair_ptrs[i] = &sender_keypairs[i];240ret = secp256k1_keypair_xonly_pub(241ctx,242&tx_inputs[i],243NULL,244&sender_keypairs[i]245);246assert(ret);247} else {248printf("Failed to create keypair\n");249return EXIT_FAILURE;250}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.