-
Notifications
You must be signed in to change notification settings - Fork 135
Expand file tree
/
Copy pathpayments.rs
More file actions
196 lines (168 loc) · 5.43 KB
/
payments.rs
File metadata and controls
196 lines (168 loc) · 5.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#[path = "../tests/common/mod.rs"]
mod common;
use std::sync::Arc;
use std::time::{Duration, Instant};
use bitcoin::hex::DisplayHex;
use bitcoin::Amount;
use common::{
expect_channel_ready_event, generate_blocks_and_wait, premine_and_distribute_funds,
random_chain_source, setup_bitcoind_and_electrsd, setup_two_nodes_with_store,
};
use criterion::{criterion_group, criterion_main, Criterion};
use ldk_node::{Event, Node};
use lightning_types::payment::{PaymentHash, PaymentPreimage};
use rand::RngCore;
use tokio::task::{self};
use crate::common::open_channel_push_amt;
fn spawn_payment(node_a: Arc<Node>, node_b: Arc<Node>, amount_msat: u64) {
let mut preimage_bytes = [0u8; 32];
rand::rng().fill_bytes(&mut preimage_bytes);
let preimage = PaymentPreimage(preimage_bytes);
let payment_hash: PaymentHash = preimage.into();
// Spawn each payment as a separate async task
task::spawn(async move {
println!("{}: Starting payment", payment_hash.0.as_hex());
loop {
// Pre-check the HTLC slots to try to avoid the performance impact of a failed payment.
while node_a.list_channels()[0].next_outbound_htlc_limit_msat == 0 {
println!("{}: Waiting for HTLC slots to free up", payment_hash.0.as_hex());
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
let payment_id = node_a.spontaneous_payment().send_with_preimage(
amount_msat,
node_b.node_id(),
preimage,
None,
);
match payment_id {
Ok(payment_id) => {
println!(
"{}: Awaiting payment with id {}",
payment_hash.0.as_hex(),
payment_id
);
break;
},
Err(e) => {
println!("{}: Payment attempt failed: {:?}", payment_hash.0.as_hex(), e);
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
},
}
}
});
}
async fn send_payments(node_a: Arc<Node>, node_b: Arc<Node>) -> std::time::Duration {
let start = Instant::now();
let total_payments = 1000;
let amount_msat = 10_000_000;
let mut success_count = 0;
for _ in 0..total_payments {
spawn_payment(node_a.clone(), node_b.clone(), amount_msat);
}
while success_count < total_payments {
match node_a.next_event_async().await {
Event::PaymentSuccessful { payment_id, payment_hash, .. } => {
if let Some(id) = payment_id {
success_count += 1;
println!("{}: Payment with id {:?} completed", payment_hash.0.as_hex(), id);
} else {
println!("Payment completed (no payment_id)");
}
},
Event::PaymentFailed { payment_id, payment_hash, .. } => {
println!("{}: Payment {:?} failed", payment_hash.unwrap().0.as_hex(), payment_id);
// The payment failed, so we need to respawn it.
spawn_payment(node_a.clone(), node_b.clone(), amount_msat);
},
ref e => {
println!("Received non-payment event: {:?}", e);
},
}
node_a.event_handled().unwrap();
}
let duration = start.elapsed();
println!("Time elapsed: {:?}", duration);
// Send back the money for the next iteration.
let mut preimage_bytes = [0u8; 32];
rand::rng().fill_bytes(&mut preimage_bytes);
node_b
.spontaneous_payment()
.send_with_preimage(
amount_msat * total_payments,
node_a.node_id(),
PaymentPreimage(preimage_bytes),
None,
)
.ok()
.unwrap();
duration
}
fn payment_benchmark(c: &mut Criterion) {
// Set up two nodes. Because this is slow, we reuse the same nodes for each sample.
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);
let (node_a, node_b) = setup_two_nodes_with_store(
&chain_source,
false,
true,
false,
common::TestStoreType::Sqlite,
common::TestStoreType::Sqlite,
);
let runtime =
tokio::runtime::Builder::new_multi_thread().worker_threads(4).enable_all().build().unwrap();
let node_a = Arc::new(node_a);
let node_b = Arc::new(node_b);
// Fund the nodes and setup a channel between them. The criterion function cannot be async, so we need to execute
// the setup using a runtime.
let node_a_cloned = Arc::clone(&node_a);
let node_b_cloned = Arc::clone(&node_b);
runtime.block_on(async move {
let address_a = node_a_cloned.onchain_payment().new_address().unwrap();
let premine_sat = 25_000_000;
premine_and_distribute_funds(
&bitcoind.client,
&electrsd.client,
vec![address_a],
Amount::from_sat(premine_sat),
)
.await;
node_a_cloned.sync_wallets().unwrap();
node_b_cloned.sync_wallets().unwrap();
open_channel_push_amt(
&node_a_cloned,
&node_b_cloned,
16_000_000,
Some(1_000_000_000),
false,
&electrsd,
)
.await;
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
node_a_cloned.sync_wallets().unwrap();
node_b_cloned.sync_wallets().unwrap();
expect_channel_ready_event!(node_a_cloned, node_b_cloned.node_id());
expect_channel_ready_event!(node_b_cloned, node_a_cloned.node_id());
});
let mut group = c.benchmark_group("payments");
group.sample_size(10);
group.bench_function("payments", |b| {
// Use custom timing so that sending back the money at the end of each iteration isn't included in the
// measurement.
b.to_async(&runtime).iter_custom(|iter| {
let node_a = Arc::clone(&node_a);
let node_b = Arc::clone(&node_b);
async move {
let mut total = Duration::ZERO;
for _i in 0..iter {
let node_a = Arc::clone(&node_a);
let node_b = Arc::clone(&node_b);
total += send_payments(node_a, node_b).await;
}
total
}
});
});
}
criterion_group!(benches, payment_benchmark);
criterion_main!(benches);