Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ alloc = ["secp256k1-sys/alloc"]
recovery = ["secp256k1-sys/recovery"]
lowmemory = ["secp256k1-sys/lowmemory"]
global-context = ["std"]
silentpayments = ["secp256k1-sys/silentpayments"]
# disable re-randomization of the global context, which provides some
# defense-in-depth against sidechannel attacks. You should only use
# this feature if you expect the `rand` crate's thread_rng to panic.
Expand All @@ -31,7 +32,7 @@ global-context-less-secure = ["global-context"]
arbitrary = ["dep:arbitrary"]

[dependencies]
secp256k1-sys = { version = "0.12.0", default-features = false, path = "./secp256k1-sys" }
secp256k1-sys = { version = "0.13.0", default-features = false, path = "./secp256k1-sys" }

arbitrary = { version = "1.4", optional = true }
rand = { version = "0.9", default-features = false, optional = true }
Expand Down Expand Up @@ -71,6 +72,10 @@ required-features = ["rand", "std"]
name = "musig"
required-features = ["rand", "std"]

[[example]]
name = "silentpayments"
required-features = ["rand", "silentpayments"]

[workspace]
members = ["secp256k1-sys"]
exclude = ["no_std_test"]
Expand Down
210 changes: 210 additions & 0 deletions examples/silentpayments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use secp256k1::{
silentpayments, Keypair, PublicKey, Scalar, SecretKey, XOnlyPublicKey,
};
use std::collections::HashMap;

const N_INPUTS: usize = 2;
const N_OUTPUTS: usize = 3;

/* Static data for Bob and Carol's silent payment addresses */
static SMALLEST_OUTPOINT: [u8; 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 BOB_SCAN_KEY: [u8; 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 BOB_SPEND_KEY: [u8; 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 BOB_SCAN_AND_SPEND_PUBKEYS: [[u8; 33]; 2] = [
[
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 CAROL_SCAN_KEY: [u8; 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 CAROL_ADDRESS: [[u8; 33]; 2] = [
[
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,
],
];

fn main() {
let mut sender_keypairs = Vec::<Keypair>::new();
let mut recipients = Vec::<silentpayments::sender::Recipient>::new();

let unlabeled_spend_pubkey =
PublicKey::from_byte_array_compressed(BOB_SCAN_AND_SPEND_PUBKEYS[1])
.expect("reading from constant, should not fail");

let (bob_address, label_context) = {
let bob_scan_key = SecretKey::from_secret_bytes(BOB_SCAN_KEY)
.expect("reading from constant, should not fail");

let m = 1;
let (label, label_tweak) = silentpayments::recipient::create_label(&bob_scan_key, m)
.expect("transitively deterministic, should not fail");

let mut tweak_map = HashMap::<[u8; 33], [u8; 32]>::new();

tweak_map.insert(label.serialize(), label_tweak);

let labeled_spend_pubkey =
silentpayments::recipient::create_labeled_spend_pubkey(&unlabeled_spend_pubkey, &label)
.expect("transitively deterministic, should not fail");

let bob_address: [[u8; 33]; 2] =
[BOB_SCAN_AND_SPEND_PUBKEYS[0], labeled_spend_pubkey.serialize()];

(bob_address, tweak_map)
};

let (tx_inputs, tx_outputs) = {
let mut tx_inputs = Vec::<XOnlyPublicKey>::new();

for _ in 0..N_INPUTS {
let rand_keypair = Keypair::new(&mut rand::rng());
sender_keypairs.push(rand_keypair);
tx_inputs.push(rand_keypair.x_only_public_key().0);
}

let sp_addresses = [&CAROL_ADDRESS, &bob_address, &CAROL_ADDRESS];

for (index, address) in sp_addresses.iter().enumerate() {
let scan_pubkey = PublicKey::from_byte_array_compressed(address[0])
.expect("reading from constant, should not fail");
let spend_pubkey = PublicKey::from_byte_array_compressed(address[1])
.expect("reading from constant, should not fail");

let silentpayment_recipient =
silentpayments::sender::Recipient::new(&scan_pubkey, &spend_pubkey, index);

recipients.push(silentpayment_recipient);
}

let recipients: Vec<&mut _> = recipients.iter_mut().collect();
let sender_keypairs: Vec<&_> = sender_keypairs.iter().collect();

let tx_outputs = silentpayments::sender::create_outputs(
&recipients,
&SMALLEST_OUTPOINT,
Some(&sender_keypairs),
None,
)
.expect("negligible probability of error, should not fail");

assert_eq!(tx_outputs.len(), N_OUTPUTS);

println!("Alice created the following outputs for Bob and Carol:");
for tx_output in tx_outputs.iter() {
println!("\t0x{}", &tx_output.to_string());
}
println!();

(tx_inputs, tx_outputs)
};

let tx_inputs_ref: Vec<&XOnlyPublicKey> = tx_inputs.iter().collect();
let tx_outputs_ref: Vec<&XOnlyPublicKey> = tx_outputs.iter().collect();

let prevouts_summary = silentpayments::recipient::PrevoutsSummary::create(
&SMALLEST_OUTPOINT,
Some(&tx_inputs_ref),
None,
)
.expect("all arguments are valid and and all inputs are xonly inputs, should not fail");

let bob_scan_key =
SecretKey::from_secret_bytes(BOB_SCAN_KEY).expect("reading from constant, should not fail");

let label_lookup = |key: &[u8; 33]| -> Option<[u8; 32]> { label_context.get(key).copied() };
let found_outputs = silentpayments::recipient::scan_outputs(
&tx_outputs_ref,
&bob_scan_key,
&prevouts_summary,
&unlabeled_spend_pubkey,
Some(&label_lookup),
)
.expect("all arguments are valid, should not fail");

if !found_outputs.is_empty() {
println!("Bob found the following outputs:");
for xonly_output in found_outputs {
println!("\t0x{}", &xonly_output.to_string());
let bob_spend_key = SecretKey::from_secret_bytes(BOB_SPEND_KEY)
.expect("reading from constant, should not fail");
let bob_tweaked_key = bob_spend_key
.add_tweak(
&Scalar::from_be_bytes(xonly_output.tweak())
.expect("generated by sender, should be less than curve"),
)
.expect("negligible probability of error, should not fail");
let bob_spend_keypair = Keypair::from_secret_key(&bob_tweaked_key);
let (bob_tweaked_xonly_pubkey, _parity) = bob_spend_keypair.x_only_public_key();
assert_eq!(xonly_output.output(), bob_tweaked_xonly_pubkey);
}
println!();
} else {
println!("Bob did not find any outputs in this transaction.\n");
}

let unlabeled_spend_pubkey = PublicKey::from_byte_array_compressed(CAROL_ADDRESS[1])
.expect("reading from constant, should not fail");

let carol_scan_key = SecretKey::from_secret_bytes(CAROL_SCAN_KEY)
.expect("reading from constant, should not fail");

let found_outputs = silentpayments::recipient::scan_outputs(
&tx_outputs_ref,
&carol_scan_key,
&prevouts_summary,
&unlabeled_spend_pubkey,
None::<fn(&[u8; 33]) -> Option<[u8; 32]>>,
)
.expect("arguments are valid and tx outputs are silent payment outputs, should not fail");

if !found_outputs.is_empty() {
println!("Carol found the following outputs:");
for xonly_output in found_outputs {
println!("\t0x{}", &xonly_output.to_string());
}
} else {
println!("Carol did not find any outputs in this transaction.\n");
}
}
5 changes: 3 additions & 2 deletions secp256k1-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "secp256k1-sys"
version = "0.12.0"
version = "0.13.0"
authors = [ "Dawid Ciężarkiewicz <[email protected]>",
"Andrew Poelstra <[email protected]>",
"Steven Roose <[email protected]>" ]
Expand All @@ -12,7 +12,7 @@ description = "FFI for Pieter Wuille's `libsecp256k1` library."
keywords = [ "secp256k1", "libsecp256k1", "ffi" ]
readme = "README.md"
build = "build.rs"
links = "rustsecp256k1_v0_12"
links = "rustsecp256k1_v0_13"
edition = "2021"
rust-version = "1.63.0"

Expand All @@ -32,6 +32,7 @@ recovery = []
lowmemory = []
std = ["alloc"]
alloc = []
silentpayments = []

[lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(secp256k1_fuzz)', 'cfg(rust_secp_no_symbol_renaming)'] }
Expand Down
5 changes: 4 additions & 1 deletion secp256k1-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ fn main() {
.include("depend/secp256k1/src")
.flag_if_supported("-Wno-unused-function") // some ecmult stuff is defined but not used upstream
.flag_if_supported("-Wno-unused-parameter") // patching out printf causes this warning
.define("SECP256K1_API", Some(""))
.define("ENABLE_MODULE_ECDH", Some("1"))
.define("ENABLE_MODULE_SCHNORRSIG", Some("1"))
.define("ENABLE_MODULE_EXTRAKEYS", Some("1"))
Expand All @@ -33,6 +32,10 @@ fn main() {
// just #define it away.
.define("printf(...)", Some(""));

if cfg!(feature = "silentpayments") {
base_config.define("ENABLE_MODULE_SILENTPAYMENTS", Some("1"));
}

if cfg!(feature = "lowmemory") {
base_config.define("ECMULT_WINDOW_SIZE", Some("4")); // A low-enough value to consume negligible memory
base_config.define("COMB_BLOCKS", Some("2"));
Expand Down
2 changes: 1 addition & 1 deletion secp256k1-sys/depend/secp256k1-HEAD-revision.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# This file was automatically created by vendor-libsecp.sh
0cdc758a56360bf58a851fe91085a327ec97685a
9103229d27d85fa8b199705f29bd7dada54ebaa7
101 changes: 0 additions & 101 deletions secp256k1-sys/depend/secp256k1/.cirrus.yml

This file was deleted.

Loading
Loading