Skip to main content

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