Skip to content

Commit 8d85649

Browse files
committed
Let Builder emit BuildError rather than panic
1 parent 2d61979 commit 8d85649

File tree

5 files changed

+105
-50
lines changed

5 files changed

+105
-50
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323
builder.set_esplora_server("https://blockstream.info/testnet/api".to_string());
2424
builder.set_gossip_source_rgs("https://rapidsync.lightningdevkit.org/testnet/snapshot".to_string());
2525

26-
let node = builder.build();
26+
let node = builder.build().unwrap();
2727

2828
node.start().unwrap();
2929

bindings/ldk_node.udl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface Builder {
1717
[Name=from_config]
1818
constructor(Config config);
1919
void set_entropy_seed_path(string seed_path);
20+
[Throws=BuildError]
2021
void set_entropy_seed_bytes(sequence<u8> seed_bytes);
2122
void set_entropy_bip39_mnemonic(Mnemonic mnemonic, string? passphrase);
2223
void set_esplora_server(string esplora_server_url);
@@ -25,6 +26,7 @@ interface Builder {
2526
void set_storage_dir_path(string storage_dir_path);
2627
void set_network(Network network);
2728
void set_listening_address(NetAddress listening_address);
29+
[Throws=BuildError]
2830
LDKNode build();
2931
};
3032

@@ -110,6 +112,16 @@ enum NodeError {
110112
"InsufficientFunds",
111113
};
112114

115+
[Error]
116+
enum BuildError {
117+
"InvalidSeedBytes",
118+
"InvalidSystemTime",
119+
"IOReadFailed",
120+
"IOWriteFailed",
121+
"StoragePathAccessFailed",
122+
"WalletSetupFailed",
123+
};
124+
113125
[Enum]
114126
interface Event {
115127
PaymentSuccessful( PaymentHash payment_hash );

src/builder.rs

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use bitcoin::BlockHash;
4444

4545
use std::convert::TryInto;
4646
use std::default::Default;
47+
use std::fmt;
4748
use std::fs;
4849
use std::sync::{Arc, Mutex, RwLock};
4950
use std::time::SystemTime;
@@ -66,6 +67,42 @@ enum GossipSourceConfig {
6667
RapidGossipSync(String),
6768
}
6869

70+
/// An error encountered during building a [`Node`].
71+
///
72+
/// [`Node`]: crate::Node
73+
#[derive(Debug, Clone)]
74+
pub enum BuildError {
75+
/// The given seed bytes are invalid, e.g, are of invalid length.
76+
InvalidSeedBytes,
77+
/// The current system time is invalid, clocks might have gone backwards.
78+
InvalidSystemTime,
79+
/// We failed to read data from the [`KVStore`].
80+
IOReadFailed,
81+
/// We failed to write data to the [`KVStore`].
82+
IOWriteFailed,
83+
/// We failed to access the given `storage_dir_path`.
84+
StoragePathAccessFailed,
85+
/// We failed to setup the onchain wallet.
86+
WalletSetupFailed,
87+
}
88+
89+
impl fmt::Display for BuildError {
90+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91+
match *self {
92+
Self::InvalidSeedBytes => write!(f, "Given seed bytes are invalid."),
93+
Self::InvalidSystemTime => {
94+
write!(f, "System time is invalid. Clocks might have gone back in time.")
95+
}
96+
Self::IOReadFailed => write!(f, "Failed to read from store."),
97+
Self::IOWriteFailed => write!(f, "Failed to write to store."),
98+
Self::StoragePathAccessFailed => write!(f, "Failed to access the given storage path."),
99+
Self::WalletSetupFailed => write!(f, "Failed to setup onchain wallet."),
100+
}
101+
}
102+
}
103+
104+
impl std::error::Error for BuildError {}
105+
69106
/// A builder for an [`Node`] instance, allowing to set some configuration and module choices from
70107
/// the getgo.
71108
///
@@ -111,13 +148,14 @@ impl NodeBuilder {
111148
/// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
112149
///
113150
/// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
114-
pub fn set_entropy_seed_bytes(&mut self, seed_bytes: Vec<u8>) {
151+
pub fn set_entropy_seed_bytes(&mut self, seed_bytes: Vec<u8>) -> Result<(), BuildError> {
115152
if seed_bytes.len() != WALLET_KEYS_SEED_LEN {
116-
panic!("Failed to set seed due to invalid length.");
153+
return Err(BuildError::InvalidSeedBytes);
117154
}
118155
let mut bytes = [0u8; WALLET_KEYS_SEED_LEN];
119156
bytes.copy_from_slice(&seed_bytes);
120157
self.entropy_source_config = Some(EntropySourceConfig::SeedBytes(bytes));
158+
Ok(())
121159
}
122160

123161
/// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
@@ -167,26 +205,28 @@ impl NodeBuilder {
167205

168206
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
169207
/// previously configured.
170-
pub fn build(&self) -> Node<SqliteStore> {
208+
pub fn build(&self) -> Result<Node<SqliteStore>, BuildError> {
171209
let storage_dir_path = self.config.storage_dir_path.clone();
172-
fs::create_dir_all(storage_dir_path.clone()).expect("Failed to create LDK data directory");
210+
fs::create_dir_all(storage_dir_path.clone())
211+
.map_err(|_| BuildError::StoragePathAccessFailed)?;
173212
let kv_store = Arc::new(SqliteStore::new(storage_dir_path.into()));
174213
self.build_with_store(kv_store)
175214
}
176215

177216
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
178217
/// previously configured.
179-
pub fn build_with_fs_store(&self) -> Node<FilesystemStore> {
218+
pub fn build_with_fs_store(&self) -> Result<Node<FilesystemStore>, BuildError> {
180219
let storage_dir_path = self.config.storage_dir_path.clone();
181-
fs::create_dir_all(storage_dir_path.clone()).expect("Failed to create LDK data directory");
220+
fs::create_dir_all(storage_dir_path.clone())
221+
.map_err(|_| BuildError::StoragePathAccessFailed)?;
182222
let kv_store = Arc::new(FilesystemStore::new(storage_dir_path.into()));
183223
self.build_with_store(kv_store)
184224
}
185225

186226
/// Builds a [`Node`] instance according to the options previously configured.
187227
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
188228
&self, kv_store: Arc<K>,
189-
) -> Node<K> {
229+
) -> Result<Node<K>, BuildError> {
190230
let config = Arc::new(self.config.clone());
191231

192232
let runtime = Arc::new(RwLock::new(None));
@@ -239,7 +279,7 @@ impl ArcedNodeBuilder {
239279
/// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
240280
///
241281
/// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
242-
pub fn set_entropy_seed_bytes(&self, seed_bytes: Vec<u8>) {
282+
pub fn set_entropy_seed_bytes(&self, seed_bytes: Vec<u8>) -> Result<(), BuildError> {
243283
self.inner.write().unwrap().set_entropy_seed_bytes(seed_bytes)
244284
}
245285

@@ -289,21 +329,21 @@ impl ArcedNodeBuilder {
289329

290330
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
291331
/// previously configured.
292-
pub fn build(&self) -> Arc<Node<SqliteStore>> {
293-
Arc::new(self.inner.read().unwrap().build())
332+
pub fn build(&self) -> Result<Arc<Node<SqliteStore>>, BuildError> {
333+
self.inner.read().unwrap().build().map(Arc::new)
294334
}
295335

296336
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
297337
/// previously configured.
298-
pub fn build_with_fs_store(&self) -> Arc<Node<FilesystemStore>> {
299-
Arc::new(self.inner.read().unwrap().build_with_fs_store())
338+
pub fn build_with_fs_store(&self) -> Result<Arc<Node<FilesystemStore>>, BuildError> {
339+
self.inner.read().unwrap().build_with_fs_store().map(Arc::new)
300340
}
301341

302342
/// Builds a [`Node`] instance according to the options previously configured.
303343
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
304344
&self, kv_store: Arc<K>,
305-
) -> Arc<Node<K>> {
306-
Arc::new(self.inner.read().unwrap().build_with_store(kv_store))
345+
) -> Result<Arc<Node<K>>, BuildError> {
346+
self.inner.read().unwrap().build_with_store(kv_store).map(Arc::new)
307347
}
308348
}
309349

@@ -313,12 +353,12 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
313353
chain_data_source_config: Option<&'a ChainDataSourceConfig>,
314354
gossip_source_config: Option<&'a GossipSourceConfig>, kv_store: Arc<K>,
315355
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
316-
) -> Node<K> {
356+
) -> Result<Node<K>, BuildError> {
317357
let ldk_data_dir = format!("{}/ldk", config.storage_dir_path);
318-
fs::create_dir_all(ldk_data_dir.clone()).expect("Failed to create LDK data directory");
358+
fs::create_dir_all(ldk_data_dir.clone()).map_err(|_| BuildError::StoragePathAccessFailed)?;
319359

320360
let bdk_data_dir = format!("{}/bdk", config.storage_dir_path);
321-
fs::create_dir_all(bdk_data_dir.clone()).expect("Failed to create BDK data directory");
361+
fs::create_dir_all(bdk_data_dir.clone()).map_err(|_| BuildError::StoragePathAccessFailed)?;
322362

323363
// Initialize the Logger
324364
let log_file_path = format!(
@@ -346,15 +386,15 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
346386
};
347387

348388
let xprv = bitcoin::util::bip32::ExtendedPrivKey::new_master(config.network, &seed_bytes)
349-
.expect("Failed to read wallet master key");
389+
.map_err(|_| BuildError::InvalidSeedBytes)?;
350390

351391
let wallet_name = bdk::wallet::wallet_name_from_descriptor(
352392
Bip84(xprv, bdk::KeychainKind::External),
353393
Some(Bip84(xprv, bdk::KeychainKind::Internal)),
354394
config.network,
355395
&Secp256k1::new(),
356396
)
357-
.expect("Failed to derive on-chain wallet name");
397+
.map_err(|_| BuildError::WalletSetupFailed)?;
358398

359399
let database_path = format!("{}/bdk_wallet_{}.sqlite", config.storage_dir_path, wallet_name);
360400
let database = SqliteDatabase::new(database_path);
@@ -365,7 +405,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
365405
config.network,
366406
database,
367407
)
368-
.expect("Failed to set up on-chain wallet");
408+
.map_err(|_| BuildError::WalletSetupFailed)?;
369409

370410
let (blockchain, tx_sync) = match chain_data_source_config {
371411
Some(ChainDataSourceConfig::Esplora(server_url)) => {
@@ -401,7 +441,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
401441
// Initialize the KeysManager
402442
let cur_time = SystemTime::now()
403443
.duration_since(SystemTime::UNIX_EPOCH)
404-
.expect("System time error: Clock may have gone backwards");
444+
.map_err(|_| BuildError::InvalidSystemTime)?;
405445
let ldk_seed_bytes: [u8; 32] = xprv.private_key.secret_bytes();
406446
let keys_manager = Arc::new(KeysManager::new(
407447
&ldk_seed_bytes,
@@ -418,7 +458,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
418458
if e.kind() == std::io::ErrorKind::NotFound {
419459
Arc::new(NetworkGraph::new(config.network, Arc::clone(&logger)))
420460
} else {
421-
panic!("Failed to read network graph: {}", e.to_string());
461+
return Err(BuildError::IOReadFailed);
422462
}
423463
}
424464
};
@@ -438,7 +478,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
438478
Arc::clone(&logger),
439479
)))
440480
} else {
441-
panic!("Failed to read scorer: {}", e.to_string());
481+
return Err(BuildError::IOReadFailed);
442482
}
443483
}
444484
};
@@ -462,7 +502,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
462502
Vec::new()
463503
} else {
464504
log_error!(logger, "Failed to read channel monitors: {}", e.to_string());
465-
panic!("Failed to read channel monitors: {}", e.to_string());
505+
return Err(BuildError::IOReadFailed);
466506
}
467507
}
468508
};
@@ -489,8 +529,10 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
489529
channel_monitor_references,
490530
);
491531
let (_hash, channel_manager) =
492-
<(BlockHash, ChannelManager<K>)>::read(&mut reader, read_args)
493-
.expect("Failed to read channel manager from store");
532+
<(BlockHash, ChannelManager<K>)>::read(&mut reader, read_args).map_err(|e| {
533+
log_error!(logger, "Failed to read channel manager from KVStore: {}", e);
534+
BuildError::IOReadFailed
535+
})?;
494536
channel_manager
495537
} else {
496538
// We're starting a fresh node.
@@ -535,7 +577,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
535577

536578
let cur_time = SystemTime::now()
537579
.duration_since(SystemTime::UNIX_EPOCH)
538-
.expect("System time error: Clock may have gone backwards");
580+
.map_err(|_| BuildError::InvalidSystemTime)?;
539581

540582
// Initialize the GossipSource
541583
// Use the configured gossip source, if the user set one, otherwise default to P2PNetwork.
@@ -552,7 +594,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
552594
Arc::clone(&kv_store),
553595
Arc::clone(&logger),
554596
)
555-
.expect("Persistence failed");
597+
.map_err(|_| BuildError::IOWriteFailed)?;
556598
p2p_source
557599
}
558600
GossipSourceConfig::RapidGossipSync(rgs_server) => {
@@ -590,7 +632,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
590632

591633
let peer_manager = Arc::new(PeerManager::new(
592634
msg_handler,
593-
cur_time.as_secs().try_into().expect("System time error"),
635+
cur_time.as_secs().try_into().map_err(|_| BuildError::InvalidSystemTime)?,
594636
&ephemeral_bytes,
595637
Arc::clone(&logger),
596638
IgnoringMessageHandler {},
@@ -602,8 +644,8 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
602644
Ok(payments) => {
603645
Arc::new(PaymentStore::new(payments, Arc::clone(&kv_store), Arc::clone(&logger)))
604646
}
605-
Err(e) => {
606-
panic!("Failed to read payment information: {}", e.to_string());
647+
Err(_) => {
648+
return Err(BuildError::IOReadFailed);
607649
}
608650
};
609651

@@ -614,7 +656,7 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
614656
if e.kind() == std::io::ErrorKind::NotFound {
615657
Arc::new(EventQueue::new(Arc::clone(&kv_store), Arc::clone(&logger)))
616658
} else {
617-
panic!("Failed to read event queue: {}", e.to_string());
659+
return Err(BuildError::IOReadFailed);
618660
}
619661
}
620662
};
@@ -625,14 +667,14 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
625667
if e.kind() == std::io::ErrorKind::NotFound {
626668
Arc::new(PeerStore::new(Arc::clone(&kv_store), Arc::clone(&logger)))
627669
} else {
628-
panic!("Failed to read peer store: {}", e.to_string());
670+
return Err(BuildError::IOReadFailed);
629671
}
630672
}
631673
};
632674

633675
let (stop_sender, stop_receiver) = tokio::sync::watch::channel(());
634676

635-
Node {
677+
Ok(Node {
636678
runtime,
637679
stop_sender,
638680
stop_receiver,
@@ -651,5 +693,5 @@ fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
651693
scorer,
652694
peer_store,
653695
payment_store,
654-
}
696+
})
655697
}

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
//! builder.set_esplora_server("https://blockstream.info/testnet/api".to_string());
3939
//! builder.set_gossip_source_rgs("https://rapidsync.lightningdevkit.org/testnet/snapshot".to_string());
4040
//!
41-
//! let node = builder.build();
41+
//! let node = builder.build().unwrap();
4242
//!
4343
//! node.start().unwrap();
4444
//!
@@ -111,6 +111,7 @@ use {bip39::Mnemonic, bitcoin::OutPoint, lightning::ln::PaymentSecret, uniffi_ty
111111

112112
#[cfg(feature = "uniffi")]
113113
pub use builder::ArcedNodeBuilder as Builder;
114+
pub use builder::BuildError;
114115
#[cfg(not(feature = "uniffi"))]
115116
pub use builder::NodeBuilder as Builder;
116117

@@ -1312,7 +1313,7 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
13121313
/// # config.network = Network::Regtest;
13131314
/// # config.storage_dir_path = "/tmp/ldk_node_test/".to_string();
13141315
/// # let builder = Builder::from_config(config);
1315-
/// # let node = builder.build();
1316+
/// # let node = builder.build().unwrap();
13161317
/// node.list_payments_with_filter(|p| p.direction == PaymentDirection::Outbound);
13171318
/// ```
13181319
pub fn list_payments_with_filter<F: FnMut(&&PaymentDetails) -> bool>(

0 commit comments

Comments
 (0)