Skip to content

Commit eadfa06

Browse files
committed
doc(silentpayments): add example/silentpayments.rs
1 parent 2095c0c commit eadfa06

2 files changed

Lines changed: 201 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ required-features = ["rand", "std"]
7272
name = "musig"
7373
required-features = ["rand", "std"]
7474

75+
[[example]]
76+
name = "silentpayments"
77+
required-features = ["rand", "silentpayments"]
78+
7579
[workspace]
7680
members = ["secp256k1-sys"]
7781
exclude = ["no_std_test"]

examples/silentpayments.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
use std::collections::HashMap;
2+
3+
use secp256k1::{silentpayments, Keypair, PublicKey, Scalar, SecretKey, XOnlyPublicKey};
4+
5+
const N_INPUTS: usize = 2;
6+
const N_OUTPUTS: usize = 3;
7+
8+
/* Static data for Bob and Carol's silent payment addresses */
9+
static SMALLEST_OUTPOINT: [u8; 36] = [
10+
0x16, 0x9e, 0x1e, 0x83, 0xe9, 0x30, 0x85, 0x33, 0x91, 0xbc, 0x6f, 0x35, 0xf6, 0x05, 0xc6, 0x75,
11+
0x4c, 0xfe, 0xad, 0x57, 0xcf, 0x83, 0x87, 0x63, 0x9d, 0x3b, 0x40, 0x96, 0xc5, 0x4f, 0x18, 0xf4,
12+
0x00, 0x00, 0x00, 0x00,
13+
];
14+
15+
static BOB_SCAN_KEY: [u8; 32] = [
16+
0xa8, 0x90, 0x54, 0xc9, 0x5b, 0xe3, 0xc3, 0x01, 0x56, 0x65, 0x74, 0xf2, 0xaa, 0x93, 0xad, 0xe0,
17+
0x51, 0x85, 0x09, 0x03, 0xa6, 0x9c, 0xbd, 0xd1, 0xd4, 0x7e, 0xae, 0x26, 0x3d, 0x7b, 0xc0, 0x31,
18+
];
19+
20+
static BOB_SPEND_KEY: [u8; 32] = [
21+
0x9d, 0x6a, 0xd8, 0x55, 0xce, 0x34, 0x17, 0xef, 0x84, 0xe8, 0x36, 0x89, 0x2e, 0x5a, 0x56, 0x39,
22+
0x2b, 0xfb, 0xa0, 0x5f, 0xa5, 0xd9, 0x7c, 0xce, 0xa3, 0x0e, 0x26, 0x6f, 0x54, 0x0e, 0x08, 0xb3,
23+
];
24+
25+
static BOB_SCAN_AND_SPEND_PUBKEYS: [[u8; 33]; 2] = [
26+
[
27+
0x02, 0x15, 0x40, 0xae, 0xa8, 0x97, 0x54, 0x7a, 0xd4, 0x39, 0xb4, 0xe0, 0xf6, 0x09, 0xe5,
28+
0xf0, 0xfa, 0x63, 0xde, 0x89, 0xab, 0x11, 0xed, 0xe3, 0x1e, 0x8c, 0xde, 0x4b, 0xe2, 0x19,
29+
0x42, 0x5f, 0x23,
30+
],
31+
[
32+
0x02, 0x5c, 0xc9, 0x85, 0x6d, 0x6f, 0x83, 0x75, 0x35, 0x0e, 0x12, 0x39, 0x78, 0xda, 0xac,
33+
0x20, 0x0c, 0x26, 0x0c, 0xb5, 0xb5, 0xae, 0x83, 0x10, 0x6c, 0xab, 0x90, 0x48, 0x4d, 0xcd,
34+
0x8f, 0xcf, 0x36,
35+
],
36+
];
37+
38+
static CAROL_SCAN_KEY: [u8; 32] = [
39+
0x04, 0xb2, 0xa4, 0x11, 0x63, 0x5c, 0x09, 0x77, 0x59, 0xaa, 0xcd, 0x0f, 0x00, 0x5a, 0x4c, 0x82,
40+
0xc8, 0xc9, 0x28, 0x62, 0xc6, 0xfc, 0x28, 0x4b, 0x80, 0xb8, 0xef, 0xeb, 0xc2, 0x0c, 0x3d, 0x17,
41+
];
42+
43+
static CAROL_ADDRESS: [[u8; 33]; 2] = [
44+
[
45+
0x03, 0xbb, 0xc6, 0x3f, 0x12, 0x74, 0x5d, 0x3b, 0x9e, 0x9d, 0x24, 0xc6, 0xcd, 0x7a, 0x1e,
46+
0xfe, 0xba, 0xd0, 0xa7, 0xf4, 0x69, 0x23, 0x2f, 0xbe, 0xcf, 0x31, 0xfb, 0xa7, 0xb4, 0xf7,
47+
0xdd, 0xed, 0xa8,
48+
],
49+
[
50+
0x03, 0x81, 0xeb, 0x9a, 0x9a, 0x9e, 0xc7, 0x39, 0xd5, 0x27, 0xc1, 0x63, 0x1b, 0x31, 0xb4,
51+
0x21, 0x56, 0x6f, 0x5c, 0x2a, 0x47, 0xb4, 0xab, 0x5b, 0x1f, 0x6a, 0x68, 0x6d, 0xfb, 0x68,
52+
0xea, 0xb7, 0x16,
53+
],
54+
];
55+
56+
fn main() {
57+
let mut sender_keypairs = Vec::<Keypair>::new();
58+
let mut recipients = Vec::<silentpayments::sender::Recipient>::new();
59+
60+
let unlabeled_spend_pubkey =
61+
PublicKey::from_byte_array_compressed(BOB_SCAN_AND_SPEND_PUBKEYS[1])
62+
.expect("reading from constant, should not fail");
63+
64+
let (bob_address, label_context) = {
65+
let bob_scan_key = SecretKey::from_secret_bytes(BOB_SCAN_KEY)
66+
.expect("reading from constant, should not fail");
67+
68+
let m = 1;
69+
let (label, label_tweak) = silentpayments::recipient::Label::create(&bob_scan_key, m)
70+
.expect("transitively deterministic, should not fail");
71+
72+
let mut tweak_map = HashMap::<[u8; 33], [u8; 32]>::new();
73+
74+
tweak_map.insert(label.serialize(), label_tweak);
75+
76+
let labeled_spend_pubkey =
77+
silentpayments::recipient::create_labeled_spend_pubkey(&unlabeled_spend_pubkey, &label)
78+
.expect("transitively deterministic, should not fail");
79+
80+
let bob_address: [[u8; 33]; 2] =
81+
[BOB_SCAN_AND_SPEND_PUBKEYS[0], labeled_spend_pubkey.serialize()];
82+
83+
(bob_address, tweak_map)
84+
};
85+
86+
let (tx_inputs, tx_outputs) = {
87+
let mut tx_inputs = Vec::<XOnlyPublicKey>::new();
88+
89+
for _ in 0..N_INPUTS {
90+
let rand_keypair = Keypair::new(&mut rand::rng());
91+
sender_keypairs.push(rand_keypair);
92+
tx_inputs.push(rand_keypair.x_only_public_key().0);
93+
}
94+
95+
let sp_addresses = [&CAROL_ADDRESS, &bob_address, &CAROL_ADDRESS];
96+
97+
for (index, address) in (0u32..).zip(sp_addresses.iter()) {
98+
let scan_pubkey = PublicKey::from_byte_array_compressed(address[0])
99+
.expect("reading from constant, should not fail");
100+
let spend_pubkey = PublicKey::from_byte_array_compressed(address[1])
101+
.expect("reading from constant, should not fail");
102+
103+
let silentpayment_recipient =
104+
silentpayments::sender::Recipient::new(&scan_pubkey, &spend_pubkey, index);
105+
106+
recipients.push(silentpayment_recipient);
107+
}
108+
109+
let sender_keypairs: Vec<&_> = sender_keypairs.iter().collect();
110+
111+
let tx_outputs = silentpayments::sender::create_outputs(
112+
&recipients,
113+
&SMALLEST_OUTPOINT,
114+
Some(&sender_keypairs),
115+
None,
116+
)
117+
.expect("negligible probability of error, should not fail");
118+
119+
assert_eq!(tx_outputs.len(), N_OUTPUTS);
120+
121+
println!("Alice created the following outputs for Bob and Carol:");
122+
for tx_output in tx_outputs.iter() {
123+
println!("\t0x{}", &tx_output.to_string());
124+
}
125+
println!();
126+
127+
(tx_inputs, tx_outputs)
128+
};
129+
130+
let tx_inputs_ref: Vec<&XOnlyPublicKey> = tx_inputs.iter().collect();
131+
let tx_outputs_ref: Vec<&XOnlyPublicKey> = tx_outputs.iter().collect();
132+
133+
let prevouts_summary = silentpayments::recipient::PrevoutsSummary::create(
134+
&SMALLEST_OUTPOINT,
135+
Some(&tx_inputs_ref),
136+
None,
137+
)
138+
.expect("all arguments are valid and and all inputs are xonly inputs, should not fail");
139+
140+
let bob_scan_key =
141+
SecretKey::from_secret_bytes(BOB_SCAN_KEY).expect("reading from constant, should not fail");
142+
143+
let label_lookup = |key: &[u8; 33]| -> Option<[u8; 32]> { label_context.get(key).copied() };
144+
let found_outputs = silentpayments::recipient::scan_outputs(
145+
&tx_outputs_ref,
146+
&bob_scan_key,
147+
&prevouts_summary,
148+
&unlabeled_spend_pubkey,
149+
Some(&label_lookup),
150+
)
151+
.expect("all arguments are valid, should not fail");
152+
153+
if !found_outputs.is_empty() {
154+
println!("Bob found the following outputs:");
155+
for xonly_output in found_outputs {
156+
println!("\t0x{}", &xonly_output.to_string());
157+
let bob_spend_key = SecretKey::from_secret_bytes(BOB_SPEND_KEY)
158+
.expect("reading from constant, should not fail");
159+
let bob_tweaked_key = bob_spend_key
160+
.add_tweak(
161+
&Scalar::from_be_bytes(xonly_output.tweak())
162+
.expect("generated by sender, should be less than curve"),
163+
)
164+
.expect("negligible probability of error, should not fail");
165+
let bob_spend_keypair = Keypair::from_secret_key(&bob_tweaked_key);
166+
let (bob_tweaked_xonly_pubkey, _parity) = bob_spend_keypair.x_only_public_key();
167+
assert_eq!(xonly_output.output(), bob_tweaked_xonly_pubkey);
168+
}
169+
println!();
170+
} else {
171+
println!("Bob did not find any outputs in this transaction.\n");
172+
}
173+
174+
let unlabeled_spend_pubkey = PublicKey::from_byte_array_compressed(CAROL_ADDRESS[1])
175+
.expect("reading from constant, should not fail");
176+
177+
let carol_scan_key = SecretKey::from_secret_bytes(CAROL_SCAN_KEY)
178+
.expect("reading from constant, should not fail");
179+
180+
let found_outputs = silentpayments::recipient::scan_outputs(
181+
&tx_outputs_ref,
182+
&carol_scan_key,
183+
&prevouts_summary,
184+
&unlabeled_spend_pubkey,
185+
None::<fn(&[u8; 33]) -> Option<[u8; 32]>>,
186+
)
187+
.expect("arguments are valid and tx outputs are silent payment outputs, should not fail");
188+
189+
if !found_outputs.is_empty() {
190+
println!("Carol found the following outputs:");
191+
for xonly_output in found_outputs {
192+
println!("\t0x{}", &xonly_output.to_string());
193+
}
194+
} else {
195+
println!("Carol did not find any outputs in this transaction.\n");
196+
}
197+
}

0 commit comments

Comments
 (0)