Skip to content

Commit 76e17c5

Browse files
committed
doc(silentpayments): add example/silentpayments.rs
1 parent 84a9544 commit 76e17c5

File tree

2 files changed

+213
-0
lines changed

2 files changed

+213
-0
lines changed

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

0 commit comments

Comments
 (0)