@@ -44,6 +44,7 @@ use bitcoin::BlockHash;
4444
4545use std:: convert:: TryInto ;
4646use std:: default:: Default ;
47+ use std:: fmt;
4748use std:: fs;
4849use std:: sync:: { Arc , Mutex , RwLock } ;
4950use std:: time:: SystemTime ;
@@ -66,6 +67,48 @@ 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., have invalid length.
76+ InvalidSeedBytes ,
77+ /// The given seed file is invalid, e.g., has invalid length, or could not be read.
78+ InvalidSeedFile ,
79+ /// The current system time is invalid, clocks might have gone backwards.
80+ InvalidSystemTime ,
81+ /// We failed to read data from the [`KVStore`].
82+ ReadFailed ,
83+ /// We failed to write data to the [`KVStore`].
84+ WriteFailed ,
85+ /// We failed to access the given `storage_dir_path`.
86+ StoragePathAccessFailed ,
87+ /// We failed to setup the onchain wallet.
88+ WalletSetupFailed ,
89+ /// We failed to setup the logger.
90+ LoggerSetupFailed ,
91+ }
92+
93+ impl fmt:: Display for BuildError {
94+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
95+ match * self {
96+ Self :: InvalidSeedBytes => write ! ( f, "Given seed bytes are invalid." ) ,
97+ Self :: InvalidSeedFile => write ! ( f, "Given seed file is invalid or could not be read." ) ,
98+ Self :: InvalidSystemTime => {
99+ write ! ( f, "System time is invalid. Clocks might have gone back in time." )
100+ }
101+ Self :: ReadFailed => write ! ( f, "Failed to read from store." ) ,
102+ Self :: WriteFailed => write ! ( f, "Failed to write to store." ) ,
103+ Self :: StoragePathAccessFailed => write ! ( f, "Failed to access the given storage path." ) ,
104+ Self :: WalletSetupFailed => write ! ( f, "Failed to setup onchain wallet." ) ,
105+ Self :: LoggerSetupFailed => write ! ( f, "Failed to setup the logger." ) ,
106+ }
107+ }
108+ }
109+
110+ impl std:: error:: Error for BuildError { }
111+
69112/// A builder for an [`Node`] instance, allowing to set some configuration and module choices from
70113/// the getgo.
71114///
@@ -112,14 +155,14 @@ impl NodeBuilder {
112155 /// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
113156 ///
114157 /// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
115- pub fn set_entropy_seed_bytes ( & mut self , seed_bytes : Vec < u8 > ) -> & mut Self {
158+ pub fn set_entropy_seed_bytes ( & mut self , seed_bytes : Vec < u8 > ) -> Result < & mut Self , BuildError > {
116159 if seed_bytes. len ( ) != WALLET_KEYS_SEED_LEN {
117- panic ! ( "Failed to set seed due to invalid length." ) ;
160+ return Err ( BuildError :: InvalidSeedBytes ) ;
118161 }
119162 let mut bytes = [ 0u8 ; WALLET_KEYS_SEED_LEN ] ;
120163 bytes. copy_from_slice ( & seed_bytes) ;
121164 self . entropy_source_config = Some ( EntropySourceConfig :: SeedBytes ( bytes) ) ;
122- self
165+ Ok ( self )
123166 }
124167
125168 /// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
@@ -179,26 +222,28 @@ impl NodeBuilder {
179222
180223 /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
181224 /// previously configured.
182- pub fn build ( & self ) -> Node < SqliteStore > {
225+ pub fn build ( & self ) -> Result < Node < SqliteStore > , BuildError > {
183226 let storage_dir_path = self . config . storage_dir_path . clone ( ) ;
184- fs:: create_dir_all ( storage_dir_path. clone ( ) ) . expect ( "Failed to create LDK data directory" ) ;
227+ fs:: create_dir_all ( storage_dir_path. clone ( ) )
228+ . map_err ( |_| BuildError :: StoragePathAccessFailed ) ?;
185229 let kv_store = Arc :: new ( SqliteStore :: new ( storage_dir_path. into ( ) ) ) ;
186230 self . build_with_store ( kv_store)
187231 }
188232
189233 /// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
190234 /// previously configured.
191- pub fn build_with_fs_store ( & self ) -> Node < FilesystemStore > {
235+ pub fn build_with_fs_store ( & self ) -> Result < Node < FilesystemStore > , BuildError > {
192236 let storage_dir_path = self . config . storage_dir_path . clone ( ) ;
193- fs:: create_dir_all ( storage_dir_path. clone ( ) ) . expect ( "Failed to create LDK data directory" ) ;
237+ fs:: create_dir_all ( storage_dir_path. clone ( ) )
238+ . map_err ( |_| BuildError :: StoragePathAccessFailed ) ?;
194239 let kv_store = Arc :: new ( FilesystemStore :: new ( storage_dir_path. into ( ) ) ) ;
195240 self . build_with_store ( kv_store)
196241 }
197242
198243 /// Builds a [`Node`] instance according to the options previously configured.
199244 pub fn build_with_store < K : KVStore + Sync + Send + ' static > (
200245 & self , kv_store : Arc < K > ,
201- ) -> Node < K > {
246+ ) -> Result < Node < K > , BuildError > {
202247 let config = Arc :: new ( self . config . clone ( ) ) ;
203248
204249 let runtime = Arc :: new ( RwLock :: new ( None ) ) ;
@@ -251,8 +296,8 @@ impl ArcedNodeBuilder {
251296 /// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
252297 ///
253298 /// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
254- pub fn set_entropy_seed_bytes ( & self , seed_bytes : Vec < u8 > ) {
255- self . inner . write ( ) . unwrap ( ) . set_entropy_seed_bytes ( seed_bytes) ;
299+ pub fn set_entropy_seed_bytes ( & self , seed_bytes : Vec < u8 > ) -> Result < ( ) , BuildError > {
300+ self . inner . write ( ) . unwrap ( ) . set_entropy_seed_bytes ( seed_bytes) . map ( |_| ( ) )
256301 }
257302
258303 /// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
@@ -301,21 +346,21 @@ impl ArcedNodeBuilder {
301346
302347 /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
303348 /// previously configured.
304- pub fn build ( & self ) -> Arc < Node < SqliteStore > > {
305- Arc :: new ( self . inner . read ( ) . unwrap ( ) . build ( ) )
349+ pub fn build ( & self ) -> Result < Arc < Node < SqliteStore > > , BuildError > {
350+ self . inner . read ( ) . unwrap ( ) . build ( ) . map ( Arc :: new )
306351 }
307352
308353 /// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
309354 /// previously configured.
310- pub fn build_with_fs_store ( & self ) -> Arc < Node < FilesystemStore > > {
311- Arc :: new ( self . inner . read ( ) . unwrap ( ) . build_with_fs_store ( ) )
355+ pub fn build_with_fs_store ( & self ) -> Result < Arc < Node < FilesystemStore > > , BuildError > {
356+ self . inner . read ( ) . unwrap ( ) . build_with_fs_store ( ) . map ( Arc :: new )
312357 }
313358
314359 /// Builds a [`Node`] instance according to the options previously configured.
315360 pub fn build_with_store < K : KVStore + Sync + Send + ' static > (
316361 & self , kv_store : Arc < K > ,
317- ) -> Arc < Node < K > > {
318- Arc :: new ( self . inner . read ( ) . unwrap ( ) . build_with_store ( kv_store) )
362+ ) -> Result < Arc < Node < K > > , BuildError > {
363+ self . inner . read ( ) . unwrap ( ) . build_with_store ( kv_store) . map ( Arc :: new )
319364 }
320365}
321366
@@ -325,26 +370,30 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
325370 chain_data_source_config : Option < & ChainDataSourceConfig > ,
326371 gossip_source_config : Option < & GossipSourceConfig > , kv_store : Arc < K > ,
327372 runtime : Arc < RwLock < Option < tokio:: runtime:: Runtime > > > ,
328- ) -> Node < K > {
373+ ) -> Result < Node < K > , BuildError > {
329374 let ldk_data_dir = format ! ( "{}/ldk" , config. storage_dir_path) ;
330- fs:: create_dir_all ( ldk_data_dir. clone ( ) ) . expect ( "Failed to create LDK data directory" ) ;
375+ fs:: create_dir_all ( ldk_data_dir. clone ( ) ) . map_err ( |_| BuildError :: StoragePathAccessFailed ) ? ;
331376
332377 let bdk_data_dir = format ! ( "{}/bdk" , config. storage_dir_path) ;
333- fs:: create_dir_all ( bdk_data_dir. clone ( ) ) . expect ( "Failed to create BDK data directory" ) ;
378+ fs:: create_dir_all ( bdk_data_dir. clone ( ) ) . map_err ( |_| BuildError :: StoragePathAccessFailed ) ? ;
334379
335380 // Initialize the Logger
336381 let log_file_path = format ! (
337382 "{}/logs/ldk_node_{}.log" ,
338383 config. storage_dir_path,
339384 chrono:: offset:: Local :: now( ) . format( "%Y_%m_%d" )
340385 ) ;
341- let logger = Arc :: new ( FilesystemLogger :: new ( log_file_path. clone ( ) , config. log_level ) ) ;
386+ let logger = Arc :: new (
387+ FilesystemLogger :: new ( log_file_path. clone ( ) , config. log_level )
388+ . map_err ( |_| BuildError :: LoggerSetupFailed ) ?,
389+ ) ;
342390
343391 // Initialize the on-chain wallet and chain access
344392 let seed_bytes = match entropy_source_config {
345393 Some ( EntropySourceConfig :: SeedBytes ( bytes) ) => bytes. clone ( ) ,
346394 Some ( EntropySourceConfig :: SeedFile ( seed_path) ) => {
347- io:: utils:: read_or_generate_seed_file ( seed_path)
395+ io:: utils:: read_or_generate_seed_file ( seed_path, Arc :: clone ( & logger) )
396+ . map_err ( |_| BuildError :: InvalidSeedFile ) ?
348397 }
349398 Some ( EntropySourceConfig :: Bip39Mnemonic { mnemonic, passphrase } ) => match passphrase {
350399 Some ( passphrase) => mnemonic. to_seed ( passphrase) ,
@@ -353,20 +402,21 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
353402 None => {
354403 // Default to read or generate from the default location generate a seed file.
355404 let seed_path = format ! ( "{}/keys_seed" , config. storage_dir_path) ;
356- io:: utils:: read_or_generate_seed_file ( & seed_path)
405+ io:: utils:: read_or_generate_seed_file ( & seed_path, Arc :: clone ( & logger) )
406+ . map_err ( |_| BuildError :: InvalidSeedFile ) ?
357407 }
358408 } ;
359409
360410 let xprv = bitcoin:: util:: bip32:: ExtendedPrivKey :: new_master ( config. network , & seed_bytes)
361- . expect ( "Failed to read wallet master key" ) ;
411+ . map_err ( |_| BuildError :: InvalidSeedBytes ) ? ;
362412
363413 let wallet_name = bdk:: wallet:: wallet_name_from_descriptor (
364414 Bip84 ( xprv, bdk:: KeychainKind :: External ) ,
365415 Some ( Bip84 ( xprv, bdk:: KeychainKind :: Internal ) ) ,
366416 config. network ,
367417 & Secp256k1 :: new ( ) ,
368418 )
369- . expect ( "Failed to derive on-chain wallet name" ) ;
419+ . map_err ( |_| BuildError :: WalletSetupFailed ) ? ;
370420
371421 let database_path = format ! ( "{}/bdk_wallet_{}.sqlite" , config. storage_dir_path, wallet_name) ;
372422 let database = SqliteDatabase :: new ( database_path) ;
@@ -377,7 +427,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
377427 config. network ,
378428 database,
379429 )
380- . expect ( "Failed to set up on-chain wallet" ) ;
430+ . map_err ( |_| BuildError :: WalletSetupFailed ) ? ;
381431
382432 let ( blockchain, tx_sync) = match chain_data_source_config {
383433 Some ( ChainDataSourceConfig :: Esplora ( server_url) ) => {
@@ -413,13 +463,14 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
413463 // Initialize the KeysManager
414464 let cur_time = SystemTime :: now ( )
415465 . duration_since ( SystemTime :: UNIX_EPOCH )
416- . expect ( "System time error: Clock may have gone backwards" ) ;
466+ . map_err ( |_| BuildError :: InvalidSystemTime ) ? ;
417467 let ldk_seed_bytes: [ u8 ; 32 ] = xprv. private_key . secret_bytes ( ) ;
418468 let keys_manager = Arc :: new ( KeysManager :: new (
419469 & ldk_seed_bytes,
420470 cur_time. as_secs ( ) ,
421471 cur_time. subsec_nanos ( ) ,
422472 Arc :: clone ( & wallet) ,
473+ Arc :: clone ( & logger) ,
423474 ) ) ;
424475
425476 // Initialize the network graph, scorer, and router
@@ -430,7 +481,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
430481 if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
431482 Arc :: new ( NetworkGraph :: new ( config. network , Arc :: clone ( & logger) ) )
432483 } else {
433- panic ! ( "Failed to read network graph: {}" , e . to_string ( ) ) ;
484+ return Err ( BuildError :: ReadFailed ) ;
434485 }
435486 }
436487 } ;
@@ -450,7 +501,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
450501 Arc :: clone ( & logger) ,
451502 ) ) )
452503 } else {
453- panic ! ( "Failed to read scorer: {}" , e . to_string ( ) ) ;
504+ return Err ( BuildError :: ReadFailed ) ;
454505 }
455506 }
456507 } ;
@@ -474,7 +525,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
474525 Vec :: new ( )
475526 } else {
476527 log_error ! ( logger, "Failed to read channel monitors: {}" , e. to_string( ) ) ;
477- panic ! ( "Failed to read channel monitors: {}" , e . to_string ( ) ) ;
528+ return Err ( BuildError :: ReadFailed ) ;
478529 }
479530 }
480531 } ;
@@ -507,8 +558,10 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
507558 channel_monitor_references,
508559 ) ;
509560 let ( _hash, channel_manager) =
510- <( BlockHash , ChannelManager < K > ) >:: read ( & mut reader, read_args)
511- . expect ( "Failed to read channel manager from store" ) ;
561+ <( BlockHash , ChannelManager < K > ) >:: read ( & mut reader, read_args) . map_err ( |e| {
562+ log_error ! ( logger, "Failed to read channel manager from KVStore: {}" , e) ;
563+ BuildError :: ReadFailed
564+ } ) ?;
512565 channel_manager
513566 } else {
514567 // We're starting a fresh node.
@@ -553,7 +606,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
553606
554607 let cur_time = SystemTime :: now ( )
555608 . duration_since ( SystemTime :: UNIX_EPOCH )
556- . expect ( "System time error: Clock may have gone backwards" ) ;
609+ . map_err ( |_| BuildError :: InvalidSystemTime ) ? ;
557610
558611 // Initialize the GossipSource
559612 // Use the configured gossip source, if the user set one, otherwise default to P2PNetwork.
@@ -570,7 +623,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
570623 Arc :: clone ( & kv_store) ,
571624 Arc :: clone ( & logger) ,
572625 )
573- . expect ( "Persistence failed" ) ;
626+ . map_err ( |_| BuildError :: WriteFailed ) ? ;
574627 p2p_source
575628 }
576629 GossipSourceConfig :: RapidGossipSync ( rgs_server) => {
@@ -608,7 +661,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
608661
609662 let peer_manager = Arc :: new ( PeerManager :: new (
610663 msg_handler,
611- cur_time. as_secs ( ) . try_into ( ) . expect ( "System time error" ) ,
664+ cur_time. as_secs ( ) . try_into ( ) . map_err ( |_| BuildError :: InvalidSystemTime ) ? ,
612665 & ephemeral_bytes,
613666 Arc :: clone ( & logger) ,
614667 IgnoringMessageHandler { } ,
@@ -620,8 +673,8 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
620673 Ok ( payments) => {
621674 Arc :: new ( PaymentStore :: new ( payments, Arc :: clone ( & kv_store) , Arc :: clone ( & logger) ) )
622675 }
623- Err ( e ) => {
624- panic ! ( "Failed to read payment information: {}" , e . to_string ( ) ) ;
676+ Err ( _ ) => {
677+ return Err ( BuildError :: ReadFailed ) ;
625678 }
626679 } ;
627680
@@ -632,7 +685,7 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
632685 if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
633686 Arc :: new ( EventQueue :: new ( Arc :: clone ( & kv_store) , Arc :: clone ( & logger) ) )
634687 } else {
635- panic ! ( "Failed to read event queue: {}" , e . to_string ( ) ) ;
688+ return Err ( BuildError :: ReadFailed ) ;
636689 }
637690 }
638691 } ;
@@ -643,14 +696,14 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
643696 if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
644697 Arc :: new ( PeerStore :: new ( Arc :: clone ( & kv_store) , Arc :: clone ( & logger) ) )
645698 } else {
646- panic ! ( "Failed to read peer store: {}" , e . to_string ( ) ) ;
699+ return Err ( BuildError :: ReadFailed ) ;
647700 }
648701 }
649702 } ;
650703
651704 let ( stop_sender, stop_receiver) = tokio:: sync:: watch:: channel ( ( ) ) ;
652705
653- Node {
706+ Ok ( Node {
654707 runtime,
655708 stop_sender,
656709 stop_receiver,
@@ -669,5 +722,5 @@ fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
669722 scorer,
670723 peer_store,
671724 payment_store,
672- }
725+ } )
673726}
0 commit comments