Skip to content

Commit 80f64db

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 2ceb197 commit 80f64db

File tree

6 files changed

+54
-112
lines changed

6 files changed

+54
-112
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, None, 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, None, 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 & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use bitcoin::secp256k1::PublicKey;
21
use std::fmt::Write;
32

43
pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
3029
}
3130
res
3231
}
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 & 23 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, None, 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, None, 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
}
@@ -819,9 +821,9 @@ impl Node {
819821
self.channel_manager.get_our_node_id()
820822
}
821823

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

827829
/// Retrieve a new on-chain/funding address.
@@ -851,7 +853,7 @@ impl Node {
851853
///
852854
/// Returns a temporary channel id.
853855
pub fn connect_open_channel(
854-
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
856+
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
855857
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
856858
) -> Result<(), Error> {
857859
let runtime_lock = self.running.read().unwrap();
@@ -867,7 +869,7 @@ impl Node {
867869
return Err(Error::InsufficientFunds);
868870
}
869871

870-
let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
872+
let peer_info = PeerInfo { pubkey: node_id, address };
871873

872874
let con_peer_pubkey = peer_info.pubkey.clone();
873875
let con_peer_addr = peer_info.address.clone();
@@ -1005,7 +1007,7 @@ impl Node {
10051007
}
10061008

10071009
/// Send a payement given an invoice.
1008-
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
1010+
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
10091011
if self.running.read().unwrap().is_none() {
10101012
return Err(Error::NotRunning);
10111013
}
@@ -1070,7 +1072,7 @@ impl Node {
10701072
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
10711073
/// amount paid to be determined by the user.
10721074
pub fn send_payment_using_amount(
1073-
&self, invoice: Invoice, amount_msat: u64,
1075+
&self, invoice: &Invoice, amount_msat: u64,
10741076
) -> Result<PaymentHash, Error> {
10751077
if self.running.read().unwrap().is_none() {
10761078
return Err(Error::NotRunning);
@@ -1158,20 +1160,18 @@ impl Node {
11581160

11591161
/// Send a spontaneous, aka. "keysend", payment
11601162
pub fn send_spontaneous_payment(
1161-
&self, amount_msat: u64, node_id: &str,
1163+
&self, amount_msat: u64, node_id: &PublicKey,
11621164
) -> Result<PaymentHash, Error> {
11631165
if self.running.read().unwrap().is_none() {
11641166
return Err(Error::NotRunning);
11651167
}
11661168

1167-
let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;
1168-
11691169
let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
11701170
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
11711171

11721172
let route_params = RouteParameters {
11731173
payment_params: PaymentParameters::from_node_id(
1174-
pubkey,
1174+
*node_id,
11751175
self.config.default_cltv_expiry_delta,
11761176
),
11771177
final_value_msat: amount_msat,
@@ -1330,15 +1330,15 @@ async fn do_connect_peer(
13301330
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
13311331
logger: Arc<FilesystemLogger>,
13321332
) -> Result<(), Error> {
1333-
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
1333+
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
13341334
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
13351335
{
13361336
Some(connection_closed_future) => {
13371337
let mut connection_closed_future = Box::pin(connection_closed_future);
13381338
loop {
13391339
match futures::poll!(&mut connection_closed_future) {
13401340
std::task::Poll::Ready(_) => {
1341-
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
1341+
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
13421342
return Err(Error::ConnectionFailed);
13431343
}
13441344
std::task::Poll::Pending => {}
@@ -1351,7 +1351,7 @@ async fn do_connect_peer(
13511351
}
13521352
}
13531353
None => {
1354-
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
1354+
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
13551355
Err(Error::ConnectionFailed)
13561356
}
13571357
}

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::{
32
KVStore, TransactionalWrite, PEER_INFO_PERSISTENCE_KEY, PEER_INFO_PERSISTENCE_NAMESPACE,
43
};
@@ -10,8 +9,7 @@ use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
109
use bitcoin::secp256k1::PublicKey;
1110

1211
use std::collections::HashMap;
13-
use std::convert::TryFrom;
14-
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
12+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
1513
use std::ops::Deref;
1614
use std::sync::RwLock;
1715

@@ -196,28 +194,10 @@ impl Writeable for PeerInfo {
196194
}
197195
}
198196

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

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

src/test/functional_tests.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,15 @@ fn channel_full_cycle() {
3636
println!("\nA -- connect_open_channel -> B");
3737
let funding_amount_sat = 80_000;
3838
let push_msat = (funding_amount_sat / 2) * 1000; // balance the channel
39-
let node_b_addr = format!("{}@{}", node_b.node_id(), node_b.listening_address().unwrap());
40-
node_a.connect_open_channel(&node_b_addr, funding_amount_sat, Some(push_msat), true).unwrap();
39+
node_a
40+
.connect_open_channel(
41+
node_b.node_id(),
42+
*node_b.listening_address().unwrap(),
43+
funding_amount_sat,
44+
Some(push_msat),
45+
true,
46+
)
47+
.unwrap();
4148

4249
expect_event!(node_a, ChannelPending);
4350

@@ -85,7 +92,7 @@ fn channel_full_cycle() {
8592
let invoice = node_b.receive_payment(invoice_amount_1_msat, &"asdf", 9217).unwrap();
8693

8794
println!("\nA send_payment");
88-
let payment_hash = node_a.send_payment(invoice.clone()).unwrap();
95+
let payment_hash = node_a.send_payment(&invoice).unwrap();
8996

9097
let outbound_payments_a =
9198
node_a.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound);
@@ -113,7 +120,7 @@ fn channel_full_cycle() {
113120
assert_eq!(node_b.payment(&payment_hash).unwrap().amount_msat, Some(invoice_amount_1_msat));
114121

115122
// Assert we fail duplicate outbound payments.
116-
assert_eq!(Err(Error::NonUniquePaymentHash), node_a.send_payment(invoice));
123+
assert_eq!(Err(Error::NonUniquePaymentHash), node_a.send_payment(&invoice));
117124

118125
// Test under-/overpayment
119126
let invoice_amount_2_msat = 1000_000;
@@ -122,12 +129,12 @@ fn channel_full_cycle() {
122129
let underpaid_amount = invoice_amount_2_msat - 1;
123130
assert_eq!(
124131
Err(Error::InvalidAmount),
125-
node_a.send_payment_using_amount(invoice, underpaid_amount)
132+
node_a.send_payment_using_amount(&invoice, underpaid_amount)
126133
);
127134

128135
let invoice = node_b.receive_payment(invoice_amount_2_msat, &"asdf", 9217).unwrap();
129136
let overpaid_amount_msat = invoice_amount_2_msat + 100;
130-
let payment_hash = node_a.send_payment_using_amount(invoice, overpaid_amount_msat).unwrap();
137+
let payment_hash = node_a.send_payment_using_amount(&invoice, overpaid_amount_msat).unwrap();
131138
expect_event!(node_a, PaymentSuccessful);
132139
let received_amount = match node_b.next_event() {
133140
ref e @ Event::PaymentReceived { amount_msat, .. } => {
@@ -150,9 +157,9 @@ fn channel_full_cycle() {
150157
// Test "zero-amount" invoice payment
151158
let variable_amount_invoice = node_b.receive_variable_amount_payment(&"asdf", 9217).unwrap();
152159
let determined_amount_msat = 1234_567;
153-
assert_eq!(Err(Error::InvalidInvoice), node_a.send_payment(variable_amount_invoice.clone()));
160+
assert_eq!(Err(Error::InvalidInvoice), node_a.send_payment(&variable_amount_invoice));
154161
let payment_hash =
155-
node_a.send_payment_using_amount(variable_amount_invoice, determined_amount_msat).unwrap();
162+
node_a.send_payment_using_amount(&variable_amount_invoice, determined_amount_msat).unwrap();
156163

157164
expect_event!(node_a, PaymentSuccessful);
158165
let received_amount = match node_b.next_event() {
@@ -232,10 +239,15 @@ fn channel_open_fails_when_funds_insufficient() {
232239
assert_eq!(node_b.on_chain_balance().unwrap().get_spendable(), premine_amount_sat);
233240

234241
println!("\nA -- connect_open_channel -> B");
235-
let node_b_addr = format!("{}@{}", node_b.node_id(), node_b.listening_address().unwrap());
236242
assert_eq!(
237243
Err(Error::InsufficientFunds),
238-
node_a.connect_open_channel(&node_b_addr, 120000, None, true)
244+
node_a.connect_open_channel(
245+
node_b.node_id(),
246+
*node_b.listening_address().unwrap(),
247+
120000,
248+
None,
249+
true
250+
)
239251
);
240252
}
241253

src/test/utils.rs

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

295295
let rand_port = random_port();
296296
println!("Setting random LDK listening port: {}", rand_port);
297-
let listening_address = format!("127.0.0.1:{}", rand_port);
298-
config.listening_address = Some(listening_address);
297+
let listening_address_str = format!("127.0.0.1:{}", rand_port);
298+
config.listening_address = Some(listening_address_str.parse().unwrap());
299299

300300
config
301301
}

0 commit comments

Comments
 (0)