Skip to content

Commit 60c8dbe

Browse files
committed
Unify Node API around Rust types
So far the interface sometimes took `String`s, `&str`s, or actual Rust types. Moreover we sometimes took the arguments by reference and sometimes not. In order to make the interface more uniform, we here take a step towards the Rust types and taking arguments by reference. Note that some of the affected parts are due to change in the future, e.g., once we transition to using upstream's `NetAddress` for peer addresses.
1 parent 1c0b501 commit 60c8dbe

File tree

6 files changed

+47
-110
lines changed

6 files changed

+47
-110
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
1010
```rust
1111
use ldk_node::Builder;
1212
use ldk_node::lightning_invoice::Invoice;
13+
use ldk_node::bitcoin::secp256k1::PublicKey;
1314
use std::str::FromStr;
1415

1516
fn main() {
@@ -23,13 +24,15 @@ fn main() {
2324
let _funding_address = node.new_funding_address();
2425

2526
// .. fund address ..
26-
27+
2728
node.sync_wallets().unwrap();
2829

29-
node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, false).unwrap();
30+
let node_id = PublicKey::from_str("NODE_ID").unwrap();
31+
let node_addr = "IP_ADDR:PORT".parse().unwrap();
32+
node.connect_open_channel(&node_id, &node_addr, 10000, false).unwrap();
3033

3134
let invoice = Invoice::from_str("INVOICE_STR").unwrap();
32-
node.send_payment(invoice).unwrap();
35+
node.send_payment(&invoice).unwrap();
3336

3437
node.stop().unwrap();
3538
}

src/hex_utils.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,3 @@ pub fn to_string(value: &[u8]) -> String {
3030
}
3131
res
3232
}
33-
34-
pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
35-
if hex.len() != 33 * 2 {
36-
return None;
37-
}
38-
let data = match to_vec(&hex[0..33 * 2]) {
39-
Some(bytes) => bytes,
40-
None => return None,
41-
};
42-
match PublicKey::from_slice(&data) {
43-
Ok(pk) => Some(pk),
44-
Err(_) => None,
45-
}
46-
}

src/lib.rs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
//! ```no_run
2929
//! use ldk_node::Builder;
3030
//! use ldk_node::lightning_invoice::Invoice;
31+
//! use ldk_node::bitcoin::secp256k1::PublicKey;
3132
//! use std::str::FromStr;
3233
//!
3334
//! fn main() {
@@ -44,10 +45,12 @@
4445
//!
4546
//! node.sync_wallets().unwrap();
4647
//!
47-
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, false).unwrap();
48+
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
49+
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
50+
//! node.connect_open_channel(&node_id, &node_addr, 10000, false).unwrap();
4851
//!
4952
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
50-
//! node.send_payment(invoice).unwrap();
53+
//! node.send_payment(&invoice).unwrap();
5154
//!
5255
//! node.stop().unwrap();
5356
//! }
@@ -60,8 +63,8 @@
6063
//! [`send_payment`]: Node::send_payment
6164
//!
6265
#![deny(missing_docs)]
63-
#![deny(broken_intra_doc_links)]
64-
#![deny(private_intra_doc_links)]
66+
#![deny(rustdoc::broken_intra_doc_links)]
67+
#![deny(rustdoc::private_intra_doc_links)]
6568
#![allow(bare_trait_objects)]
6669
#![allow(ellipsis_inclusive_range_patterns)]
6770
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
@@ -133,7 +136,7 @@ use bitcoin::BlockHash;
133136

134137
use rand::Rng;
135138

136-
use std::convert::{TryFrom, TryInto};
139+
use std::convert::TryInto;
137140
use std::default::Default;
138141
use std::fs;
139142
use std::net::SocketAddr;
@@ -167,7 +170,7 @@ pub struct Config {
167170
/// The used Bitcoin network.
168171
pub network: bitcoin::Network,
169172
/// The IP address and TCP port the node will listen on.
170-
pub listening_address: Option<String>,
173+
pub listening_address: Option<SocketAddr>,
171174
/// The default CLTV expiry delta to be used for payments.
172175
pub default_cltv_expiry_delta: u32,
173176
}
@@ -178,7 +181,7 @@ impl Default for Config {
178181
storage_dir_path: "/tmp/ldk_node/".to_string(),
179182
esplora_server_url: "http://localhost:3002".to_string(),
180183
network: bitcoin::Network::Regtest,
181-
listening_address: Some("0.0.0.0:9735".to_string()),
184+
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
182185
default_cltv_expiry_delta: 144,
183186
}
184187
}
@@ -262,9 +265,8 @@ impl Builder {
262265

263266
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
264267
///
265-
/// Format: `ADDR:PORT`
266268
/// Default: `0.0.0.0:9735`
267-
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
269+
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
268270
self.config.listening_address = Some(listening_address);
269271
self
270272
}
@@ -821,8 +823,8 @@ impl Node {
821823
self.channel_manager.get_our_node_id()
822824
}
823825

824-
/// Returns our own listening address and port.
825-
pub fn listening_address(&self) -> Option<String> {
826+
/// Returns our own listening address.
827+
pub fn listening_address(&self) -> Option<SocketAddr> {
826828
self.config.listening_address.clone()
827829
}
828830

@@ -847,7 +849,8 @@ impl Node {
847849
///
848850
/// Returns a temporary channel id
849851
pub fn connect_open_channel(
850-
&self, node_pubkey_and_address: &str, channel_amount_sats: u64, announce_channel: bool,
852+
&self, pubkey: &PublicKey, address: &SocketAddr, channel_amount_sats: u64,
853+
announce_channel: bool,
851854
) -> Result<(), Error> {
852855
let runtime_lock = self.running.read().unwrap();
853856
if runtime_lock.is_none() {
@@ -862,7 +865,7 @@ impl Node {
862865
return Err(Error::InsufficientFunds);
863866
}
864867

865-
let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
868+
let peer_info = PeerInfo { pubkey: pubkey.clone(), address: address.clone() };
866869

867870
let con_peer_pubkey = peer_info.pubkey.clone();
868871
let con_peer_addr = peer_info.address.clone();
@@ -999,7 +1002,7 @@ impl Node {
9991002
}
10001003

10011004
/// Send a payement given an invoice.
1002-
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
1005+
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
10031006
if self.running.read().unwrap().is_none() {
10041007
return Err(Error::NotRunning);
10051008
}
@@ -1055,7 +1058,7 @@ impl Node {
10551058
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
10561059
/// amount paid to be determined by the user.
10571060
pub fn send_payment_using_amount(
1058-
&self, invoice: Invoice, amount_msat: u64,
1061+
&self, invoice: &Invoice, amount_msat: u64,
10591062
) -> Result<PaymentHash, Error> {
10601063
if self.running.read().unwrap().is_none() {
10611064
return Err(Error::NotRunning);
@@ -1139,20 +1142,18 @@ impl Node {
11391142

11401143
/// Send a spontaneous, aka. "keysend", payment
11411144
pub fn send_spontaneous_payment(
1142-
&self, amount_msat: u64, node_id: &str,
1145+
&self, amount_msat: u64, node_id: &PublicKey,
11431146
) -> Result<PaymentHash, Error> {
11441147
if self.running.read().unwrap().is_none() {
11451148
return Err(Error::NotRunning);
11461149
}
11471150

1148-
let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;
1149-
11501151
let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
11511152
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
11521153

11531154
let route_params = RouteParameters {
11541155
payment_params: PaymentParameters::from_node_id(
1155-
pubkey,
1156+
*node_id,
11561157
self.config.default_cltv_expiry_delta,
11571158
),
11581159
final_value_msat: amount_msat,
@@ -1281,15 +1282,15 @@ async fn do_connect_peer(
12811282
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
12821283
logger: Arc<FilesystemLogger>,
12831284
) -> Result<(), Error> {
1284-
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
1285+
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
12851286
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
12861287
{
12871288
Some(connection_closed_future) => {
12881289
let mut connection_closed_future = Box::pin(connection_closed_future);
12891290
loop {
12901291
match futures::poll!(&mut connection_closed_future) {
12911292
std::task::Poll::Ready(_) => {
1292-
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
1293+
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
12931294
return Err(Error::ConnectionFailed);
12941295
}
12951296
std::task::Poll::Pending => {}
@@ -1302,7 +1303,7 @@ async fn do_connect_peer(
13021303
}
13031304
}
13041305
None => {
1305-
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
1306+
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
13061307
Err(Error::ConnectionFailed)
13071308
}
13081309
}

src/peer_store.rs

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::hex_utils;
21
use crate::io::{KVStore, PEER_INFO_PERSISTENCE_KEY, PEER_INFO_PERSISTENCE_NAMESPACE};
32
use crate::logger::{log_error, Logger};
43
use crate::Error;
@@ -8,8 +7,7 @@ use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
87
use bitcoin::secp256k1::PublicKey;
98

109
use std::collections::HashMap;
11-
use std::convert::TryFrom;
12-
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
10+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
1311
use std::ops::Deref;
1412
use std::sync::RwLock;
1513

@@ -195,28 +193,10 @@ impl Writeable for PeerInfo {
195193
}
196194
}
197195

198-
impl TryFrom<String> for PeerInfo {
199-
type Error = Error;
200-
201-
fn try_from(peer_pubkey_and_ip_addr: String) -> Result<Self, Self::Error> {
202-
if let Some((pubkey_str, peer_str)) = peer_pubkey_and_ip_addr.split_once('@') {
203-
if let Some(pubkey) = hex_utils::to_compressed_pubkey(pubkey_str) {
204-
if let Some(peer_addr) =
205-
peer_str.to_socket_addrs().ok().and_then(|mut r| r.next()).map(|pa| pa)
206-
{
207-
return Ok(PeerInfo { pubkey, address: peer_addr });
208-
}
209-
}
210-
}
211-
Err(Error::PeerInfoParseFailed)
212-
}
213-
}
214-
215196
#[cfg(test)]
216197
mod tests {
217198
use super::*;
218199
use crate::test::utils::{TestLogger, TestStore};
219-
use proptest::prelude::*;
220200
use std::str::FromStr;
221201
use std::sync::Arc;
222202

@@ -248,42 +228,4 @@ mod tests {
248228
assert_eq!(deser_peer_store.get_peer(&pubkey), Some(expected_peer_info));
249229
assert!(!store.get_and_clear_did_persist());
250230
}
251-
252-
#[test]
253-
fn peer_info_parsing() {
254-
let valid_peer_info_str =
255-
"0276607124ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993@127.0.0.1:9738"
256-
.to_string();
257-
258-
let pubkey = PublicKey::from_str(
259-
"0276607124ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993",
260-
)
261-
.unwrap();
262-
let address: SocketAddr = "127.0.0.1:9738".parse().unwrap();
263-
let expected_peer_info = PeerInfo { pubkey, address };
264-
265-
assert_eq!(Ok(expected_peer_info), PeerInfo::try_from(valid_peer_info_str));
266-
267-
let invalid_peer_info_str1 =
268-
"02-76607124-ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993@127.0.0.1:9738"
269-
.to_string();
270-
assert_eq!(Err(Error::PeerInfoParseFailed), PeerInfo::try_from(invalid_peer_info_str1));
271-
272-
let invalid_peer_info_str2 =
273-
"0276607124ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993@333.0.0.1:9738"
274-
.to_string();
275-
assert_eq!(Err(Error::PeerInfoParseFailed), PeerInfo::try_from(invalid_peer_info_str2));
276-
277-
let invalid_peer_info_str3 =
278-
"0276607124ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993@127.0.0.19738"
279-
.to_string();
280-
assert_eq!(Err(Error::PeerInfoParseFailed), PeerInfo::try_from(invalid_peer_info_str3));
281-
}
282-
283-
proptest! {
284-
#[test]
285-
fn peer_info_parsing_doesnt_crash(s in "\\PC*") {
286-
let _ = PeerInfo::try_from(s.to_string());
287-
}
288-
}
289231
}

src/test/functional_tests.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ fn channel_full_cycle() {
3333
assert_eq!(node_b.on_chain_balance().unwrap().get_spendable(), 100000);
3434

3535
println!("\nA -- connect_open_channel -> B");
36-
let node_b_addr = format!("{}@{}", node_b.node_id(), node_b.listening_address().unwrap());
37-
node_a.connect_open_channel(&node_b_addr, 50000, true).unwrap();
36+
node_a
37+
.connect_open_channel(&node_b.node_id(), &node_b.listening_address().unwrap(), 50000, true)
38+
.unwrap();
3839

3940
let funding_txo = loop {
4041
let details = node_a.list_channels();
@@ -76,7 +77,7 @@ fn channel_full_cycle() {
7677
let invoice = node_b.receive_payment(invoice_amount, &"asdf", 9217).unwrap();
7778

7879
println!("\nA send_payment");
79-
let payment_hash = node_a.send_payment(invoice.clone()).unwrap();
80+
let payment_hash = node_a.send_payment(&invoice).unwrap();
8081

8182
expect_event!(node_a, PaymentSuccessful);
8283
expect_event!(node_b, PaymentReceived);
@@ -88,7 +89,7 @@ fn channel_full_cycle() {
8889
assert_eq!(node_b.payment_info(&payment_hash).unwrap().amount_msat, Some(invoice_amount));
8990

9091
// Assert we fail duplicate outbound payments.
91-
assert_eq!(Err(Error::NonUniquePaymentHash), node_a.send_payment(invoice));
92+
assert_eq!(Err(Error::NonUniquePaymentHash), node_a.send_payment(&invoice));
9293

9394
// Test under-/overpayment
9495
let invoice_amount = 1000000;
@@ -97,12 +98,12 @@ fn channel_full_cycle() {
9798
let underpaid_amount = invoice_amount - 1;
9899
assert_eq!(
99100
Err(Error::InvalidAmount),
100-
node_a.send_payment_using_amount(invoice, underpaid_amount)
101+
node_a.send_payment_using_amount(&invoice, underpaid_amount)
101102
);
102103

103104
let invoice = node_b.receive_payment(invoice_amount, &"asdf", 9217).unwrap();
104105
let overpaid_amount = invoice_amount + 100;
105-
let payment_hash = node_a.send_payment_using_amount(invoice, overpaid_amount).unwrap();
106+
let payment_hash = node_a.send_payment_using_amount(&invoice, overpaid_amount).unwrap();
106107
expect_event!(node_a, PaymentSuccessful);
107108
let received_amount = match node_b.next_event() {
108109
ref e @ Event::PaymentReceived { amount_msat, .. } => {
@@ -125,9 +126,9 @@ fn channel_full_cycle() {
125126
// Test "zero-amount" invoice payment
126127
let variable_amount_invoice = node_b.receive_variable_amount_payment(&"asdf", 9217).unwrap();
127128
let determined_amount = 1234567;
128-
assert_eq!(Err(Error::InvalidInvoice), node_a.send_payment(variable_amount_invoice.clone()));
129+
assert_eq!(Err(Error::InvalidInvoice), node_a.send_payment(&variable_amount_invoice));
129130
let payment_hash =
130-
node_a.send_payment_using_amount(variable_amount_invoice, determined_amount).unwrap();
131+
node_a.send_payment_using_amount(&variable_amount_invoice, determined_amount).unwrap();
131132

132133
expect_event!(node_a, PaymentSuccessful);
133134
let received_amount = match node_b.next_event() {
@@ -195,10 +196,14 @@ fn channel_open_fails_when_funds_insufficient() {
195196
assert_eq!(node_b.on_chain_balance().unwrap().get_spendable(), 100000);
196197

197198
println!("\nA -- connect_open_channel -> B");
198-
let node_b_addr = format!("{}@{}", node_b.node_id(), node_b.listening_address().unwrap());
199199
assert_eq!(
200200
Err(Error::InsufficientFunds),
201-
node_a.connect_open_channel(&node_b_addr, 120000, true)
201+
node_a.connect_open_channel(
202+
&node_b.node_id(),
203+
&node_b.listening_address().unwrap(),
204+
120000,
205+
true
206+
)
202207
);
203208
}
204209

src/test/utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ pub fn random_config(esplora_url: &str) -> Config {
291291

292292
let rand_port = random_port();
293293
println!("Setting random LDK listening port: {}", rand_port);
294-
let listening_address = format!("127.0.0.1:{}", rand_port);
295-
config.listening_address = Some(listening_address);
294+
let listening_address_str = format!("127.0.0.1:{}", rand_port);
295+
config.listening_address = Some(listening_address_str.parse().unwrap());
296296

297297
config
298298
}

0 commit comments

Comments
 (0)