Skip to content

Commit ec34fda

Browse files
committed
Add example file
1 parent ce7e0c2 commit ec34fda

File tree

2 files changed

+309
-0
lines changed

2 files changed

+309
-0
lines changed

Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ global-context = ["std"]
3232
# if you are doing a no-std build, then this feature does nothing
3333
# and is not necessary.)
3434
global-context-less-secure = ["global-context"]
35+
silentpayments-example = ["rand", "std", "libc", "cc"]
3536

3637
[dependencies]
3738
secp256k1-sys = { version = "0.10.0", default-features = false, path = "./secp256k1-sys" }
@@ -40,6 +41,9 @@ serde = { version = "1.0.103", default-features = false, optional = true }
4041
hashes = { package = "bitcoin_hashes", version = ">= 0.12, <= 0.14", default-features = false, optional = true }
4142
rand = { version = "0.8", default-features = false, optional = true }
4243

44+
libc = { version = "0.2", optional = true }
45+
cc = { version = "1.0", optional = true }
46+
4347
[dev-dependencies]
4448
rand_core = "0.6"
4549
serde_cbor = "0.10.0"
@@ -66,6 +70,10 @@ required-features = ["hashes", "std"]
6670
name = "generate_keys"
6771
required-features = ["rand", "std"]
6872

73+
[[example]]
74+
name = "silentpayments"
75+
required-features = ["rand", "std"]
76+
6977
[workspace]
7078
members = ["secp256k1-sys"]
7179
exclude = ["no_std_test"]

examples/silentpayments.rs

+301
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
extern crate secp256k1;
2+
3+
use secp256k1::{Keypair, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey};
4+
use secp256k1::silentpayments::{
5+
silentpayments_recipient_public_data_create,
6+
silentpayments_recipient_create_label_tweak,
7+
silentpayments_sender_create_outputs,
8+
SilentpaymentsRecipient,
9+
silentpayments_recipient_scan_outputs,
10+
SilentpaymentsPublicData,
11+
silentpayments_recipient_create_output_pubkey,
12+
silentpayments_recipient_create_labelled_spend_pubkey
13+
};
14+
15+
use libc::{c_uchar, c_void, size_t};
16+
use std::slice;
17+
18+
#[repr(C)]
19+
#[derive(Debug, Copy, Clone)]
20+
struct LabelCacheEntry {
21+
label: [u8; 33],
22+
label_tweak: [u8; 32],
23+
}
24+
25+
#[repr(C)]
26+
#[derive(Debug, Copy, Clone)]
27+
struct LabelsCache {
28+
entries_used: size_t,
29+
entries: [LabelCacheEntry; 5],
30+
}
31+
32+
#[no_mangle]
33+
pub extern "C" fn rust_secp256k1_silentpayments_label_lookup(
34+
label33: *const c_uchar,
35+
cache_ptr: *const c_void,
36+
) -> *const c_uchar {
37+
// Safety checks
38+
if label33.is_null() || cache_ptr.is_null() {
39+
return std::ptr::null();
40+
}
41+
42+
unsafe {
43+
let cache = &*(cache_ptr as *const LabelsCache);
44+
let label33_slice = slice::from_raw_parts(label33, 33);
45+
46+
for i in 0..cache.entries_used {
47+
if cache.entries[i].label == *label33_slice {
48+
return cache.entries[i].label_tweak.as_ptr();
49+
}
50+
}
51+
52+
std::ptr::null()
53+
}
54+
}
55+
56+
fn main() {
57+
58+
let secp = Secp256k1::new();
59+
60+
let sender_secret_keys: [[u8; 32]; 2] = [
61+
[
62+
0x34, 0x18, 0x5f, 0xd2, 0xc0, 0xc3, 0x71, 0x19,
63+
0x73, 0x46, 0x2e, 0xc7, 0x7b, 0x65, 0x69, 0x95,
64+
0x43, 0x20, 0x5a, 0xee, 0x4f, 0x30, 0xf4, 0xee,
65+
0x32, 0x5b, 0xd8, 0x37, 0x6a, 0x1b, 0x36, 0xf3
66+
],
67+
[
68+
0xcf, 0x3e, 0x69, 0x66, 0x58, 0xa9, 0x6e, 0x45,
69+
0x70, 0x96, 0xcb, 0x2e, 0xc9, 0xa9, 0x7c, 0x27,
70+
0x8c, 0x1b, 0xf0, 0xc6, 0x0d, 0x1d, 0xc3, 0x13,
71+
0x92, 0x7d, 0xef, 0xac, 0xc2, 0x86, 0xae, 0x88
72+
]
73+
];
74+
75+
let smallest_outpoint: [u8; 36] = [
76+
0x16, 0x9e, 0x1e, 0x83, 0xe9, 0x30, 0x85, 0x33, 0x91,
77+
0xbc, 0x6f, 0x35, 0xf6, 0x05, 0xc6, 0x75, 0x4c, 0xfe,
78+
0xad, 0x57, 0xcf, 0x83, 0x87, 0x63, 0x9d, 0x3b, 0x40,
79+
0x96, 0xc5, 0x4f, 0x18, 0xf4, 0x00, 0x00, 0x00, 0x00
80+
];
81+
82+
let bob_scan_seckey: [u8; 32] = [
83+
0xa8, 0x90, 0x54, 0xc9, 0x5b, 0xe3, 0xc3, 0x01,
84+
0x56, 0x65, 0x74, 0xf2, 0xaa, 0x93, 0xad, 0xe0,
85+
0x51, 0x85, 0x09, 0x03, 0xa6, 0x9c, 0xbd, 0xd1,
86+
0xd4, 0x7e, 0xae, 0x26, 0x3d, 0x7b, 0xc0, 0x31
87+
];
88+
89+
let bob_spend_pubkey: [u8; 33] = [
90+
0x02, 0xee, 0x97, 0xdf, 0x83, 0xb2, 0x54, 0x6a,
91+
0xf5, 0xa7, 0xd0, 0x62, 0x15, 0xd9, 0x8b, 0xcb,
92+
0x63, 0x7f, 0xe0, 0x5d, 0xd0, 0xfa, 0x37, 0x3b,
93+
0xd8, 0x20, 0xe6, 0x64, 0xd3, 0x72, 0xde, 0x9a, 0x01
94+
];
95+
96+
let bob_address: [[u8; 33]; 2] = [
97+
[
98+
0x02, 0x15, 0x40, 0xae, 0xa8, 0x97, 0x54, 0x7a,
99+
0xd4, 0x39, 0xb4, 0xe0, 0xf6, 0x09, 0xe5, 0xf0,
100+
0xfa, 0x63, 0xde, 0x89, 0xab, 0x11, 0xed, 0xe3,
101+
0x1e, 0x8c, 0xde, 0x4b, 0xe2, 0x19, 0x42, 0x5f, 0x23
102+
],
103+
[
104+
0x02, 0x3e, 0xff, 0xf8, 0x18, 0x51, 0x65, 0xea,
105+
0x63, 0xa9, 0x92, 0xb3, 0x9f, 0x31, 0xd8, 0xfd,
106+
0x8e, 0x0e, 0x64, 0xae, 0xf9, 0xd3, 0x88, 0x07,
107+
0x34, 0x97, 0x37, 0x14, 0xa5, 0x3d, 0x83, 0x11, 0x8d
108+
]
109+
];
110+
111+
let carol_scan_key: [u8; 32] = [
112+
0x04, 0xb2, 0xa4, 0x11, 0x63, 0x5c, 0x09, 0x77,
113+
0x59, 0xaa, 0xcd, 0x0f, 0x00, 0x5a, 0x4c, 0x82,
114+
0xc8, 0xc9, 0x28, 0x62, 0xc6, 0xfc, 0x28, 0x4b,
115+
0x80, 0xb8, 0xef, 0xeb, 0xc2, 0x0c, 0x3d, 0x17
116+
];
117+
118+
let carol_address: [[u8; 33]; 2] = [
119+
[
120+
0x03, 0xbb, 0xc6, 0x3f, 0x12, 0x74, 0x5d, 0x3b,
121+
0x9e, 0x9d, 0x24, 0xc6, 0xcd, 0x7a, 0x1e, 0xfe,
122+
0xba, 0xd0, 0xa7, 0xf4, 0x69, 0x23, 0x2f, 0xbe,
123+
0xcf, 0x31, 0xfb, 0xa7, 0xb4, 0xf7, 0xdd, 0xed, 0xa8
124+
],
125+
[
126+
0x03, 0x81, 0xeb, 0x9a, 0x9a, 0x9e, 0xc7, 0x39,
127+
0xd5, 0x27, 0xc1, 0x63, 0x1b, 0x31, 0xb4, 0x21,
128+
0x56, 0x6f, 0x5c, 0x2a, 0x47, 0xb4, 0xab, 0x5b,
129+
0x1f, 0x6a, 0x68, 0x6d, 0xfb, 0x68, 0xea, 0xb7, 0x16
130+
]
131+
];
132+
133+
let address_amounts = ["1.0 BTC", "2.0 BTC", "3.0 BTC"];
134+
135+
let n_tx_outputs = 3;
136+
137+
let mut sp_addresses: [&[[u8; 33]; 2]; 3] = [&[[0; 33]; 2]; 3];
138+
139+
// Assign references to the addresses
140+
sp_addresses[0] = &carol_address; // : 1.0 BTC
141+
sp_addresses[1] = &bob_address; // : 2.0 BTC
142+
sp_addresses[2] = &carol_address;
143+
144+
let mut recipients = Vec::<SilentpaymentsRecipient>::new();
145+
146+
let mut tx_inputs = Vec::<XOnlyPublicKey>::new();
147+
148+
for i in 0..n_tx_outputs {
149+
let recipient_index = i;
150+
151+
let recipient_scan_pubkey = PublicKey::from_slice(&sp_addresses[i][0]).unwrap();
152+
let recipient_spend_pubkey = PublicKey::from_slice(&sp_addresses[i][1]).unwrap();
153+
154+
let silentpayment_recipient = SilentpaymentsRecipient::new(
155+
&recipient_scan_pubkey,
156+
&recipient_spend_pubkey,
157+
recipient_index
158+
);
159+
160+
recipients.push(silentpayment_recipient);
161+
}
162+
163+
let recipients = recipients.as_slice();
164+
165+
let mut taproot_seckeys = Vec::<Keypair>::new();
166+
167+
for &key in sender_secret_keys.iter() {
168+
let seckey: [u8; 32] = key;
169+
170+
let keypair = Keypair::from_seckey_slice(&secp, &seckey).unwrap();
171+
172+
taproot_seckeys.push(keypair);
173+
174+
tx_inputs.push(keypair.x_only_public_key().0);
175+
}
176+
177+
let taproot_seckeys = taproot_seckeys.as_slice();
178+
179+
let mut tx_outputs: Vec<XOnlyPublicKey> = Vec::new();
180+
181+
let out_pubkeys = silentpayments_sender_create_outputs(
182+
&secp,
183+
recipients,
184+
&smallest_outpoint,
185+
Some(taproot_seckeys),
186+
None
187+
).unwrap();
188+
189+
println!("{}:", "Alice created the following outputs for Bob and Carol:");
190+
for (i, out_pubkey) in out_pubkeys.iter().enumerate() {
191+
print!("\t{} : 0x", address_amounts[i]);
192+
for byte in out_pubkey.serialize().iter().cloned() {
193+
print!("{:02x}", byte);
194+
}
195+
println!();
196+
197+
tx_outputs.push(out_pubkey.clone());
198+
}
199+
200+
let bob_scan_secretkey = SecretKey::from_slice(&bob_scan_seckey).unwrap();
201+
let m: u32 = 1;
202+
203+
let label_tweak_result = silentpayments_recipient_create_label_tweak(&secp, &bob_scan_secretkey, m).unwrap();
204+
205+
let bob_spend_publickey = PublicKey::from_slice(&bob_spend_pubkey).unwrap();
206+
207+
let _labelled_spend_pubkey = silentpayments_recipient_create_labelled_spend_pubkey(
208+
&secp,
209+
&bob_spend_publickey,
210+
&label_tweak_result.pubkey
211+
).unwrap();
212+
213+
let public_data = silentpayments_recipient_public_data_create(
214+
&secp,
215+
&smallest_outpoint,
216+
Some(&tx_inputs),
217+
None
218+
).unwrap();
219+
220+
let mut cache = LabelsCache {
221+
entries_used: 0,
222+
entries: [LabelCacheEntry {
223+
label: [0; 33],
224+
label_tweak: [0; 32]
225+
}; 5]
226+
};
227+
228+
cache.entries[0].label = label_tweak_result.pubkey.serialize();
229+
cache.entries[0].label_tweak = label_tweak_result.label_tweak;
230+
cache.entries_used += 1;
231+
232+
let _label_tweak = rust_secp256k1_silentpayments_label_lookup(
233+
label_tweak_result.pubkey.serialize().as_ptr(),
234+
&cache as *const LabelsCache as *const c_void
235+
);
236+
237+
let tx_outputs_slice = tx_outputs.as_slice();
238+
239+
let bob_spend_publickey = PublicKey::from_slice(&bob_spend_pubkey).unwrap();
240+
241+
let found_output = silentpayments_recipient_scan_outputs(
242+
&secp,
243+
tx_outputs_slice,
244+
&bob_scan_secretkey,
245+
&public_data,
246+
&bob_spend_publickey,
247+
rust_secp256k1_silentpayments_label_lookup,
248+
cache
249+
).unwrap();
250+
251+
println!();
252+
println!("{} :", "Bob found the following outputs:");
253+
for output in found_output.iter() {
254+
println!("\t{}", output);
255+
}
256+
257+
let light_client_data33 = public_data.serialize(&secp).unwrap();
258+
259+
let carol_public_data = SilentpaymentsPublicData::parse(&secp, &light_client_data33).unwrap();
260+
261+
let carol_scan_seckey = SecretKey::from_slice(&carol_scan_key).unwrap();
262+
263+
let shared_secret = carol_public_data.recipient_create_shared_secret(&secp, &carol_scan_seckey).unwrap();
264+
265+
let mut found: bool;
266+
let mut k: u32 = 0;
267+
let mut ser_found_outputs: Vec<XOnlyPublicKey> = Vec::new();
268+
269+
let carol_spend_pubkey = PublicKey::from_slice(&carol_address[1]).unwrap();
270+
271+
println!();
272+
273+
loop {
274+
275+
let potential_output =
276+
silentpayments_recipient_create_output_pubkey(&secp, &shared_secret, &carol_spend_pubkey, k).unwrap();
277+
278+
found = false;
279+
for i in 0..n_tx_outputs {
280+
if tx_outputs[i] == potential_output {
281+
ser_found_outputs.push(potential_output);
282+
found = true;
283+
k += 1;
284+
break;
285+
}
286+
}
287+
288+
if !found {
289+
break;
290+
}
291+
}
292+
293+
println!("{}:", "Carol found the following outputs");
294+
for output in ser_found_outputs.iter() {
295+
print!("\t{}", "0x");
296+
for byte in output.serialize().iter().cloned() {
297+
print!("{:02x}", byte);
298+
}
299+
println!();
300+
}
301+
}

0 commit comments

Comments
 (0)