1use 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
56pub 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
135pub(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
154pub(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
175pub(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
194pub(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
222pub(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
241pub(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
260pub(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
292pub(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
584pub(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 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 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 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 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 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 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}