ldk_node/io/
utils.rs

1// This file is Copyright its original authors, visible in version control history.
2//
3// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
6// accordance with one or both of these licenses.
7
8use std::fs;
9use std::io::Write;
10use std::ops::Deref;
11use std::path::Path;
12use std::sync::Arc;
13
14use bdk_chain::indexer::keychain_txout::ChangeSet as BdkIndexerChangeSet;
15use bdk_chain::local_chain::ChangeSet as BdkLocalChainChangeSet;
16use bdk_chain::miniscript::{Descriptor, DescriptorPublicKey};
17use bdk_chain::tx_graph::ChangeSet as BdkTxGraphChangeSet;
18use bdk_chain::ConfirmationBlockTime;
19use bdk_wallet::ChangeSet as BdkWalletChangeSet;
20use bip39::Mnemonic;
21use bitcoin::Network;
22use lightning::io::Cursor;
23use lightning::ln::msgs::DecodeError;
24use lightning::routing::gossip::NetworkGraph;
25use lightning::routing::scoring::{
26	ChannelLiquidities, ProbabilisticScorer, ProbabilisticScoringDecayParameters,
27};
28use lightning::util::persist::{
29	KVStore, KVStoreSync, KVSTORE_NAMESPACE_KEY_ALPHABET, KVSTORE_NAMESPACE_KEY_MAX_LEN,
30	NETWORK_GRAPH_PERSISTENCE_KEY, NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE,
31	NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_KEY,
32	OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE,
33	SCORER_PERSISTENCE_KEY, SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
34	SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
35};
36use lightning::util::ser::{Readable, ReadableArgs, Writeable};
37use lightning_types::string::PrintableString;
38use rand::rngs::OsRng;
39use rand::TryRngCore;
40
41use super::*;
42use crate::chain::ChainSource;
43use crate::config::WALLET_KEYS_SEED_LEN;
44use crate::fee_estimator::OnchainFeeEstimator;
45use crate::io::{
46	NODE_METRICS_KEY, NODE_METRICS_PRIMARY_NAMESPACE, NODE_METRICS_SECONDARY_NAMESPACE,
47};
48use crate::logger::{log_error, LdkLogger, Logger};
49use crate::peer_store::PeerStore;
50use crate::types::{Broadcaster, DynStore, KeysManager, Sweeper, WordCount};
51use crate::wallet::ser::{ChangeSetDeserWrapper, ChangeSetSerWrapper};
52use crate::{Error, EventQueue, NodeMetrics, PaymentDetails};
53
54pub const EXTERNAL_PATHFINDING_SCORES_CACHE_KEY: &str = "external_pathfinding_scores_cache";
55
56/// Generates a random [BIP 39] mnemonic with the specified word count.
57///
58/// If no word count is specified, defaults to 24 words (256-bit entropy).
59///
60/// The result may be used to initialize the [`Node`] entropy, i.e., can be given to
61/// [`Builder::set_entropy_bip39_mnemonic`].
62///
63/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
64/// [`Node`]: crate::Node
65/// [`Builder::set_entropy_bip39_mnemonic`]: crate::Builder::set_entropy_bip39_mnemonic
66pub fn generate_entropy_mnemonic(word_count: Option<WordCount>) -> Mnemonic {
67	let word_count = word_count.unwrap_or(WordCount::Words24).word_count();
68	Mnemonic::generate(word_count).expect("Failed to generate mnemonic")
69}
70
71pub(crate) fn read_or_generate_seed_file<L: Deref>(
72	keys_seed_path: &str, logger: L,
73) -> std::io::Result<[u8; WALLET_KEYS_SEED_LEN]>
74where
75	L::Target: LdkLogger,
76{
77	if Path::new(&keys_seed_path).exists() {
78		let seed = fs::read(keys_seed_path).map_err(|e| {
79			log_error!(logger, "Failed to read keys seed file: {}", keys_seed_path);
80			e
81		})?;
82
83		if seed.len() != WALLET_KEYS_SEED_LEN {
84			log_error!(
85				logger,
86				"Failed to read keys seed file due to invalid length: {}",
87				keys_seed_path
88			);
89			return Err(std::io::Error::new(
90				std::io::ErrorKind::InvalidData,
91				"Failed to read keys seed file due to invalid length",
92			));
93		}
94
95		let mut key = [0; WALLET_KEYS_SEED_LEN];
96		key.copy_from_slice(&seed);
97		Ok(key)
98	} else {
99		let mut key = [0; WALLET_KEYS_SEED_LEN];
100		OsRng.try_fill_bytes(&mut key).map_err(|e| {
101			log_error!(logger, "Failed to generate entropy: {}", e);
102			std::io::Error::new(std::io::ErrorKind::Other, "Failed to generate seed bytes")
103		})?;
104
105		if let Some(parent_dir) = Path::new(&keys_seed_path).parent() {
106			fs::create_dir_all(parent_dir).map_err(|e| {
107				log_error!(
108					logger,
109					"Failed to create parent directory for key seed file: {}.",
110					keys_seed_path
111				);
112				e
113			})?;
114		}
115
116		let mut f = fs::File::create(keys_seed_path).map_err(|e| {
117			log_error!(logger, "Failed to create keys seed file: {}", keys_seed_path);
118			e
119		})?;
120
121		f.write_all(&key).map_err(|e| {
122			log_error!(logger, "Failed to write node keys seed to disk: {}", keys_seed_path);
123			e
124		})?;
125
126		f.sync_all().map_err(|e| {
127			log_error!(logger, "Failed to sync node keys seed to disk: {}", keys_seed_path);
128			e
129		})?;
130
131		Ok(key)
132	}
133}
134
135/// Read a previously persisted [`NetworkGraph`] from the store.
136pub(crate) fn read_network_graph<L: Deref + Clone>(
137	kv_store: Arc<DynStore>, logger: L,
138) -> Result<NetworkGraph<L>, std::io::Error>
139where
140	L::Target: LdkLogger,
141{
142	let mut reader = Cursor::new(KVStoreSync::read(
143		&*kv_store,
144		NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE,
145		NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE,
146		NETWORK_GRAPH_PERSISTENCE_KEY,
147	)?);
148	NetworkGraph::read(&mut reader, logger.clone()).map_err(|e| {
149		log_error!(logger, "Failed to deserialize NetworkGraph: {}", e);
150		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize NetworkGraph")
151	})
152}
153
154/// Read a previously persisted [`ProbabilisticScorer`] from the store.
155pub(crate) fn read_scorer<G: Deref<Target = NetworkGraph<L>>, L: Deref + Clone>(
156	kv_store: Arc<DynStore>, network_graph: G, logger: L,
157) -> Result<ProbabilisticScorer<G, L>, std::io::Error>
158where
159	L::Target: LdkLogger,
160{
161	let params = ProbabilisticScoringDecayParameters::default();
162	let mut reader = Cursor::new(KVStoreSync::read(
163		&*kv_store,
164		SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
165		SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
166		SCORER_PERSISTENCE_KEY,
167	)?);
168	let args = (params, network_graph, logger.clone());
169	ProbabilisticScorer::read(&mut reader, args).map_err(|e| {
170		log_error!(logger, "Failed to deserialize scorer: {}", e);
171		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize Scorer")
172	})
173}
174
175/// Read previously persisted external pathfinding scores from the cache.
176pub(crate) fn read_external_pathfinding_scores_from_cache<L: Deref>(
177	kv_store: Arc<DynStore>, logger: L,
178) -> Result<ChannelLiquidities, std::io::Error>
179where
180	L::Target: LdkLogger,
181{
182	let mut reader = Cursor::new(KVStoreSync::read(
183		&*kv_store,
184		SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
185		SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
186		EXTERNAL_PATHFINDING_SCORES_CACHE_KEY,
187	)?);
188	ChannelLiquidities::read(&mut reader).map_err(|e| {
189		log_error!(logger, "Failed to deserialize scorer: {}", e);
190		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize Scorer")
191	})
192}
193
194/// Persist external pathfinding scores to the cache.
195pub(crate) async fn write_external_pathfinding_scores_to_cache<L: Deref>(
196	kv_store: Arc<DynStore>, data: &ChannelLiquidities, logger: L,
197) -> Result<(), Error>
198where
199	L::Target: LdkLogger,
200{
201	KVStore::write(
202		&*kv_store,
203		SCORER_PERSISTENCE_PRIMARY_NAMESPACE,
204		SCORER_PERSISTENCE_SECONDARY_NAMESPACE,
205		EXTERNAL_PATHFINDING_SCORES_CACHE_KEY,
206		data.encode(),
207	)
208	.await
209	.map_err(|e| {
210		log_error!(
211			logger,
212			"Writing data to key {}/{}/{} failed due to: {}",
213			NODE_METRICS_PRIMARY_NAMESPACE,
214			NODE_METRICS_SECONDARY_NAMESPACE,
215			EXTERNAL_PATHFINDING_SCORES_CACHE_KEY,
216			e
217		);
218		Error::PersistenceFailed
219	})
220}
221
222/// Read previously persisted events from the store.
223pub(crate) fn read_event_queue<L: Deref + Clone>(
224	kv_store: Arc<DynStore>, logger: L,
225) -> Result<EventQueue<L>, std::io::Error>
226where
227	L::Target: LdkLogger,
228{
229	let mut reader = Cursor::new(KVStoreSync::read(
230		&*kv_store,
231		EVENT_QUEUE_PERSISTENCE_PRIMARY_NAMESPACE,
232		EVENT_QUEUE_PERSISTENCE_SECONDARY_NAMESPACE,
233		EVENT_QUEUE_PERSISTENCE_KEY,
234	)?);
235	EventQueue::read(&mut reader, (kv_store, logger.clone())).map_err(|e| {
236		log_error!(logger, "Failed to deserialize event queue: {}", e);
237		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize EventQueue")
238	})
239}
240
241/// Read previously persisted peer info from the store.
242pub(crate) fn read_peer_info<L: Deref + Clone>(
243	kv_store: Arc<DynStore>, logger: L,
244) -> Result<PeerStore<L>, std::io::Error>
245where
246	L::Target: LdkLogger,
247{
248	let mut reader = Cursor::new(KVStoreSync::read(
249		&*kv_store,
250		PEER_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
251		PEER_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
252		PEER_INFO_PERSISTENCE_KEY,
253	)?);
254	PeerStore::read(&mut reader, (kv_store, logger.clone())).map_err(|e| {
255		log_error!(logger, "Failed to deserialize peer store: {}", e);
256		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize PeerStore")
257	})
258}
259
260/// Read previously persisted payments information from the store.
261pub(crate) fn read_payments<L: Deref>(
262	kv_store: Arc<DynStore>, logger: L,
263) -> Result<Vec<PaymentDetails>, std::io::Error>
264where
265	L::Target: LdkLogger,
266{
267	let mut res = Vec::new();
268
269	for stored_key in KVStoreSync::list(
270		&*kv_store,
271		PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
272		PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
273	)? {
274		let mut reader = Cursor::new(KVStoreSync::read(
275			&*kv_store,
276			PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
277			PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
278			&stored_key,
279		)?);
280		let payment = PaymentDetails::read(&mut reader).map_err(|e| {
281			log_error!(logger, "Failed to deserialize PaymentDetails: {}", e);
282			std::io::Error::new(
283				std::io::ErrorKind::InvalidData,
284				"Failed to deserialize PaymentDetails",
285			)
286		})?;
287		res.push(payment);
288	}
289	Ok(res)
290}
291
292/// Read `OutputSweeper` state from the store.
293pub(crate) fn read_output_sweeper(
294	broadcaster: Arc<Broadcaster>, fee_estimator: Arc<OnchainFeeEstimator>,
295	chain_data_source: Arc<ChainSource>, keys_manager: Arc<KeysManager>, kv_store: Arc<DynStore>,
296	logger: Arc<Logger>,
297) -> Result<Sweeper, std::io::Error> {
298	let mut reader = Cursor::new(KVStoreSync::read(
299		&*kv_store,
300		OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE,
301		OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE,
302		OUTPUT_SWEEPER_PERSISTENCE_KEY,
303	)?);
304	let args = (
305		broadcaster,
306		fee_estimator,
307		Some(chain_data_source),
308		Arc::clone(&keys_manager),
309		keys_manager,
310		kv_store,
311		logger.clone(),
312	);
313	let (_, sweeper) = <(_, Sweeper)>::read(&mut reader, args).map_err(|e| {
314		log_error!(logger, "Failed to deserialize OutputSweeper: {}", e);
315		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize OutputSweeper")
316	})?;
317	Ok(sweeper)
318}
319
320pub(crate) fn read_node_metrics<L: Deref>(
321	kv_store: Arc<DynStore>, logger: L,
322) -> Result<NodeMetrics, std::io::Error>
323where
324	L::Target: LdkLogger,
325{
326	let mut reader = Cursor::new(KVStoreSync::read(
327		&*kv_store,
328		NODE_METRICS_PRIMARY_NAMESPACE,
329		NODE_METRICS_SECONDARY_NAMESPACE,
330		NODE_METRICS_KEY,
331	)?);
332	NodeMetrics::read(&mut reader).map_err(|e| {
333		log_error!(logger, "Failed to deserialize NodeMetrics: {}", e);
334		std::io::Error::new(std::io::ErrorKind::InvalidData, "Failed to deserialize NodeMetrics")
335	})
336}
337
338pub(crate) fn write_node_metrics<L: Deref>(
339	node_metrics: &NodeMetrics, kv_store: Arc<DynStore>, logger: L,
340) -> Result<(), Error>
341where
342	L::Target: LdkLogger,
343{
344	let data = node_metrics.encode();
345	KVStoreSync::write(
346		&*kv_store,
347		NODE_METRICS_PRIMARY_NAMESPACE,
348		NODE_METRICS_SECONDARY_NAMESPACE,
349		NODE_METRICS_KEY,
350		data,
351	)
352	.map_err(|e| {
353		log_error!(
354			logger,
355			"Writing data to key {}/{}/{} failed due to: {}",
356			NODE_METRICS_PRIMARY_NAMESPACE,
357			NODE_METRICS_SECONDARY_NAMESPACE,
358			NODE_METRICS_KEY,
359			e
360		);
361		Error::PersistenceFailed
362	})
363}
364
365pub(crate) fn is_valid_kvstore_str(key: &str) -> bool {
366	key.len() <= KVSTORE_NAMESPACE_KEY_MAX_LEN
367		&& key.chars().all(|c| KVSTORE_NAMESPACE_KEY_ALPHABET.contains(c))
368}
369
370pub(crate) fn check_namespace_key_validity(
371	primary_namespace: &str, secondary_namespace: &str, key: Option<&str>, operation: &str,
372) -> Result<(), std::io::Error> {
373	if let Some(key) = key {
374		if key.is_empty() {
375			debug_assert!(
376				false,
377				"Failed to {} {}/{}/{}: key may not be empty.",
378				operation,
379				PrintableString(primary_namespace),
380				PrintableString(secondary_namespace),
381				PrintableString(key)
382			);
383			let msg = format!(
384				"Failed to {} {}/{}/{}: key may not be empty.",
385				operation,
386				PrintableString(primary_namespace),
387				PrintableString(secondary_namespace),
388				PrintableString(key)
389			);
390			return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
391		}
392
393		if primary_namespace.is_empty() && !secondary_namespace.is_empty() {
394			debug_assert!(false,
395				"Failed to {} {}/{}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.",
396				operation,
397				PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key));
398			let msg = format!(
399				"Failed to {} {}/{}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.", operation,
400				PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key));
401			return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
402		}
403
404		if !is_valid_kvstore_str(primary_namespace)
405			|| !is_valid_kvstore_str(secondary_namespace)
406			|| !is_valid_kvstore_str(key)
407		{
408			debug_assert!(
409				false,
410				"Failed to {} {}/{}/{}: primary namespace, secondary namespace, and key must be valid.",
411				operation,
412				PrintableString(primary_namespace),
413				PrintableString(secondary_namespace),
414				PrintableString(key)
415			);
416			let msg = format!(
417				"Failed to {} {}/{}/{}: primary namespace, secondary namespace, and key must be valid.",
418				operation,
419				PrintableString(primary_namespace),
420				PrintableString(secondary_namespace),
421				PrintableString(key)
422			);
423			return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
424		}
425	} else {
426		if primary_namespace.is_empty() && !secondary_namespace.is_empty() {
427			debug_assert!(false,
428				"Failed to {} {}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.",
429				operation, PrintableString(primary_namespace), PrintableString(secondary_namespace));
430			let msg = format!(
431				"Failed to {} {}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.",
432				operation, PrintableString(primary_namespace), PrintableString(secondary_namespace));
433			return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
434		}
435		if !is_valid_kvstore_str(primary_namespace) || !is_valid_kvstore_str(secondary_namespace) {
436			debug_assert!(
437				false,
438				"Failed to {} {}/{}: primary namespace and secondary namespace must be valid.",
439				operation,
440				PrintableString(primary_namespace),
441				PrintableString(secondary_namespace)
442			);
443			let msg = format!(
444				"Failed to {} {}/{}: primary namespace and secondary namespace must be valid.",
445				operation,
446				PrintableString(primary_namespace),
447				PrintableString(secondary_namespace)
448			);
449			return Err(std::io::Error::new(std::io::ErrorKind::Other, msg));
450		}
451	}
452
453	Ok(())
454}
455
456macro_rules! impl_read_write_change_set_type {
457	(
458		$read_name:ident,
459		$write_name:ident,
460		$change_set_type:ty,
461		$primary_namespace:expr,
462		$secondary_namespace:expr,
463		$key:expr
464	) => {
465		pub(crate) fn $read_name<L: Deref>(
466			kv_store: Arc<DynStore>, logger: L,
467		) -> Result<Option<$change_set_type>, std::io::Error>
468		where
469			L::Target: LdkLogger,
470		{
471			let bytes =
472				match KVStoreSync::read(&*kv_store, $primary_namespace, $secondary_namespace, $key)
473				{
474					Ok(bytes) => bytes,
475					Err(e) => {
476						if e.kind() == lightning::io::ErrorKind::NotFound {
477							return Ok(None);
478						} else {
479							log_error!(
480								logger,
481								"Reading data from key {}/{}/{} failed due to: {}",
482								$primary_namespace,
483								$secondary_namespace,
484								$key,
485								e
486							);
487							return Err(e.into());
488						}
489					},
490				};
491
492			let mut reader = Cursor::new(bytes);
493			let res: Result<ChangeSetDeserWrapper<$change_set_type>, DecodeError> =
494				Readable::read(&mut reader);
495			match res {
496				Ok(res) => Ok(Some(res.0)),
497				Err(e) => {
498					log_error!(logger, "Failed to deserialize BDK wallet field: {}", e);
499					Err(std::io::Error::new(
500						std::io::ErrorKind::InvalidData,
501						"Failed to deserialize BDK wallet field",
502					))
503				},
504			}
505		}
506
507		pub(crate) fn $write_name<L: Deref>(
508			value: &$change_set_type, kv_store: Arc<DynStore>, logger: L,
509		) -> Result<(), std::io::Error>
510		where
511			L::Target: LdkLogger,
512		{
513			let data = ChangeSetSerWrapper(value).encode();
514			KVStoreSync::write(&*kv_store, $primary_namespace, $secondary_namespace, $key, data)
515				.map_err(|e| {
516					log_error!(
517						logger,
518						"Writing data to key {}/{}/{} failed due to: {}",
519						$primary_namespace,
520						$secondary_namespace,
521						$key,
522						e
523					);
524					e.into()
525				})
526		}
527	};
528}
529
530impl_read_write_change_set_type!(
531	read_bdk_wallet_descriptor,
532	write_bdk_wallet_descriptor,
533	Descriptor<DescriptorPublicKey>,
534	BDK_WALLET_DESCRIPTOR_PRIMARY_NAMESPACE,
535	BDK_WALLET_DESCRIPTOR_SECONDARY_NAMESPACE,
536	BDK_WALLET_DESCRIPTOR_KEY
537);
538
539impl_read_write_change_set_type!(
540	read_bdk_wallet_change_descriptor,
541	write_bdk_wallet_change_descriptor,
542	Descriptor<DescriptorPublicKey>,
543	BDK_WALLET_CHANGE_DESCRIPTOR_PRIMARY_NAMESPACE,
544	BDK_WALLET_CHANGE_DESCRIPTOR_SECONDARY_NAMESPACE,
545	BDK_WALLET_CHANGE_DESCRIPTOR_KEY
546);
547
548impl_read_write_change_set_type!(
549	read_bdk_wallet_network,
550	write_bdk_wallet_network,
551	Network,
552	BDK_WALLET_NETWORK_PRIMARY_NAMESPACE,
553	BDK_WALLET_NETWORK_SECONDARY_NAMESPACE,
554	BDK_WALLET_NETWORK_KEY
555);
556
557impl_read_write_change_set_type!(
558	read_bdk_wallet_local_chain,
559	write_bdk_wallet_local_chain,
560	BdkLocalChainChangeSet,
561	BDK_WALLET_LOCAL_CHAIN_PRIMARY_NAMESPACE,
562	BDK_WALLET_LOCAL_CHAIN_SECONDARY_NAMESPACE,
563	BDK_WALLET_LOCAL_CHAIN_KEY
564);
565
566impl_read_write_change_set_type!(
567	read_bdk_wallet_tx_graph,
568	write_bdk_wallet_tx_graph,
569	BdkTxGraphChangeSet<ConfirmationBlockTime>,
570	BDK_WALLET_TX_GRAPH_PRIMARY_NAMESPACE,
571	BDK_WALLET_TX_GRAPH_SECONDARY_NAMESPACE,
572	BDK_WALLET_TX_GRAPH_KEY
573);
574
575impl_read_write_change_set_type!(
576	read_bdk_wallet_indexer,
577	write_bdk_wallet_indexer,
578	BdkIndexerChangeSet,
579	BDK_WALLET_INDEXER_PRIMARY_NAMESPACE,
580	BDK_WALLET_INDEXER_SECONDARY_NAMESPACE,
581	BDK_WALLET_INDEXER_KEY
582);
583
584// Reads the full BdkWalletChangeSet or returns default fields
585pub(crate) fn read_bdk_wallet_change_set(
586	kv_store: Arc<DynStore>, logger: Arc<Logger>,
587) -> Result<Option<BdkWalletChangeSet>, std::io::Error> {
588	let mut change_set = BdkWalletChangeSet::default();
589
590	// We require a descriptor and return `None` to signal creation of a new wallet otherwise.
591	if let Some(descriptor) =
592		read_bdk_wallet_descriptor(Arc::clone(&kv_store), Arc::clone(&logger))?
593	{
594		change_set.descriptor = Some(descriptor);
595	} else {
596		return Ok(None);
597	}
598
599	// We require a change_descriptor and return `None` to signal creation of a new wallet otherwise.
600	if let Some(change_descriptor) =
601		read_bdk_wallet_change_descriptor(Arc::clone(&kv_store), Arc::clone(&logger))?
602	{
603		change_set.change_descriptor = Some(change_descriptor);
604	} else {
605		return Ok(None);
606	}
607
608	// We require a network and return `None` to signal creation of a new wallet otherwise.
609	if let Some(network) = read_bdk_wallet_network(Arc::clone(&kv_store), Arc::clone(&logger))? {
610		change_set.network = Some(network);
611	} else {
612		return Ok(None);
613	}
614
615	read_bdk_wallet_local_chain(Arc::clone(&kv_store), Arc::clone(&logger))?
616		.map(|local_chain| change_set.local_chain = local_chain);
617	read_bdk_wallet_tx_graph(Arc::clone(&kv_store), Arc::clone(&logger))?
618		.map(|tx_graph| change_set.tx_graph = tx_graph);
619	read_bdk_wallet_indexer(Arc::clone(&kv_store), Arc::clone(&logger))?
620		.map(|indexer| change_set.indexer = indexer);
621	Ok(Some(change_set))
622}
623
624#[cfg(test)]
625mod tests {
626	use super::*;
627
628	#[test]
629	fn mnemonic_to_entropy_to_mnemonic() {
630		// Test default (24 words)
631		let mnemonic = generate_entropy_mnemonic(None);
632		let entropy = mnemonic.to_entropy();
633		assert_eq!(mnemonic, Mnemonic::from_entropy(&entropy).unwrap());
634		assert_eq!(mnemonic.word_count(), 24);
635
636		// Test with different word counts
637		let word_counts = [
638			WordCount::Words12,
639			WordCount::Words15,
640			WordCount::Words18,
641			WordCount::Words21,
642			WordCount::Words24,
643		];
644
645		for word_count in word_counts {
646			let mnemonic = generate_entropy_mnemonic(Some(word_count));
647			let entropy = mnemonic.to_entropy();
648			assert_eq!(mnemonic, Mnemonic::from_entropy(&entropy).unwrap());
649
650			// Verify expected word count
651			let expected_words = match word_count {
652				WordCount::Words12 => 12,
653				WordCount::Words15 => 15,
654				WordCount::Words18 => 18,
655				WordCount::Words21 => 21,
656				WordCount::Words24 => 24,
657			};
658			assert_eq!(mnemonic.word_count(), expected_words);
659		}
660	}
661}