forked from lightningdevkit/ldk-node
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathintegration_tests_lnd.rs
More file actions
executable file
·227 lines (188 loc) · 6.65 KB
/
integration_tests_lnd.rs
File metadata and controls
executable file
·227 lines (188 loc) · 6.65 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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#![cfg(lnd_test)]
mod common;
use ldk_node::bitcoin::secp256k1::PublicKey;
use ldk_node::bitcoin::Amount;
use ldk_node::lightning::ln::msgs::SocketAddress;
use ldk_node::{Builder, Event};
use lnd_grpc_rust::lnrpc::{
invoice::InvoiceState::Settled as LndInvoiceStateSettled, GetInfoRequest as LndGetInfoRequest,
GetInfoResponse as LndGetInfoResponse, Invoice as LndInvoice,
ListInvoiceRequest as LndListInvoiceRequest, QueryRoutesRequest as LndQueryRoutesRequest,
Route as LndRoute, SendRequest as LndSendRequest,
};
use lnd_grpc_rust::{connect, LndClient};
use bitcoincore_rpc::Auth;
use bitcoincore_rpc::Client as BitcoindClient;
use electrum_client::Client as ElectrumClient;
use lightning_invoice::{Bolt11InvoiceDescription, Description};
use bitcoin::hex::DisplayHex;
use std::default::Default;
use std::str::FromStr;
use tokio::fs;
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_lnd() {
// Setup bitcoind / electrs clients
let bitcoind_client = BitcoindClient::new(
"127.0.0.1:18443",
Auth::UserPass("user".to_string(), "pass".to_string()),
)
.unwrap();
let electrs_client = ElectrumClient::new("tcp://127.0.0.1:50001").unwrap();
// Give electrs a kick.
common::generate_blocks_and_wait(&bitcoind_client, &electrs_client, 1);
// Setup LDK Node
let config = common::random_config(true);
let mut builder = Builder::from_config(config.node_config);
builder.set_chain_source_esplora("http://127.0.0.1:3002".to_string(), None);
let node = builder.build().unwrap();
node.start().unwrap();
// Premine some funds and distribute
let address = node.onchain_payment().new_address().unwrap();
let premine_amount = Amount::from_sat(5_000_000);
common::premine_and_distribute_funds(
&bitcoind_client,
&electrs_client,
vec![address],
premine_amount,
);
// Setup LND
let endpoint = "127.0.0.1:8081";
let cert_path = std::env::var("LND_CERT_PATH").expect("LND_CERT_PATH not set");
let macaroon_path = std::env::var("LND_MACAROON_PATH").expect("LND_MACAROON_PATH not set");
let mut lnd = TestLndClient::new(cert_path, macaroon_path, endpoint.to_string()).await;
let lnd_node_info = lnd.get_node_info().await;
let lnd_node_id = PublicKey::from_str(&lnd_node_info.identity_pubkey).unwrap();
let lnd_address: SocketAddress = "127.0.0.1:9735".parse().unwrap();
node.sync_wallets().unwrap();
// Open the channel
let funding_amount_sat = 1_000_000;
node.open_channel(lnd_node_id, lnd_address, funding_amount_sat, Some(500_000_000), None)
.unwrap();
let funding_txo = common::expect_channel_pending_event!(node, lnd_node_id);
common::wait_for_tx(&electrs_client, funding_txo.txid);
common::generate_blocks_and_wait(&bitcoind_client, &electrs_client, 6);
node.sync_wallets().unwrap();
let user_channel_id = common::expect_channel_ready_event!(node, lnd_node_id);
// Send a payment to LND
let lnd_invoice = lnd.create_invoice(100_000_000).await;
let parsed_invoice = lightning_invoice::Bolt11Invoice::from_str(&lnd_invoice).unwrap();
node.bolt11_payment().send(&parsed_invoice, None).unwrap();
common::expect_event!(node, PaymentSuccessful);
let lnd_listed_invoices = lnd.list_invoices().await;
assert_eq!(lnd_listed_invoices.len(), 1);
assert_eq!(lnd_listed_invoices.first().unwrap().state, LndInvoiceStateSettled as i32);
// Check route LND -> LDK
let amount_msat = 9_000_000;
let max_retries = 7;
for attempt in 1..=max_retries {
match lnd.query_routes(&node.node_id().to_string(), amount_msat).await {
Ok(routes) => {
if !routes.is_empty() {
break;
}
},
Err(err) => {
if attempt == max_retries {
panic!("Failed to find route from LND to LDK: {}", err);
}
},
};
// wait for the payment process
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
}
// Send a payment to LDK
let invoice_description =
Bolt11InvoiceDescription::Direct(Description::new("lndTest".to_string()).unwrap());
let ldk_invoice =
node.bolt11_payment().receive(amount_msat, &invoice_description, 3600).unwrap();
lnd.pay_invoice(&ldk_invoice.to_string()).await;
common::expect_event!(node, PaymentReceived);
node.close_channel(&user_channel_id, lnd_node_id).unwrap();
common::expect_event!(node, ChannelClosed);
node.stop().unwrap();
}
struct TestLndClient {
client: LndClient,
}
impl TestLndClient {
async fn new(cert_path: String, macaroon_path: String, socket: String) -> Self {
// Read the contents of the file into a vector of bytes
let cert_bytes = fs::read(cert_path).await.expect("Failed to read tls cert file");
let mac_bytes = fs::read(macaroon_path).await.expect("Failed to read macaroon file");
// Convert the bytes to a hex string
let cert = cert_bytes.as_hex().to_string();
let macaroon = mac_bytes.as_hex().to_string();
let client = connect(cert, macaroon, socket).await.expect("Failed to connect to Lnd");
TestLndClient { client }
}
async fn get_node_info(&mut self) -> LndGetInfoResponse {
let response = self
.client
.lightning()
.get_info(LndGetInfoRequest {})
.await
.expect("Failed to fetch node info from LND")
.into_inner();
response
}
async fn create_invoice(&mut self, amount_msat: u64) -> String {
let invoice = LndInvoice { value_msat: amount_msat as i64, ..Default::default() };
self.client
.lightning()
.add_invoice(invoice)
.await
.expect("Failed to create invoice on LND")
.into_inner()
.payment_request
}
async fn list_invoices(&mut self) -> Vec<LndInvoice> {
self.client
.lightning()
.list_invoices(LndListInvoiceRequest { ..Default::default() })
.await
.expect("Failed to list invoices from LND")
.into_inner()
.invoices
}
async fn query_routes(
&mut self, pubkey: &str, amount_msat: u64,
) -> Result<Vec<LndRoute>, String> {
let request = LndQueryRoutesRequest {
pub_key: pubkey.to_string(),
amt_msat: amount_msat as i64,
..Default::default()
};
let response = self
.client
.lightning()
.query_routes(request)
.await
.map_err(|err| format!("Failed to query routes from LND: {:?}", err))?
.into_inner();
if response.routes.is_empty() {
return Err(format!("No routes found for pubkey: {}", pubkey));
}
Ok(response.routes)
}
async fn pay_invoice(&mut self, invoice_str: &str) {
let send_req =
LndSendRequest { payment_request: invoice_str.to_string(), ..Default::default() };
let response = self
.client
.lightning()
.send_payment_sync(send_req)
.await
.expect("Failed to pay invoice on LND")
.into_inner();
if !response.payment_error.is_empty() || response.payment_preimage.is_empty() {
panic!(
"LND payment failed: {}",
if response.payment_error.is_empty() {
"No preimage returned"
} else {
&response.payment_error
}
);
}
}
}