Skip to content

Commit 15149b5

Browse files
committed
wip,feat(persist_test_utils): Add persist_wallet_changeset_async
1 parent de48edc commit 15149b5

1 file changed

Lines changed: 231 additions & 0 deletions

File tree

src/persist_test_utils.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,234 @@ where
432432
assert_eq!(changeset_read.descriptor.unwrap(), descriptor);
433433
assert_eq!(changeset_read.change_descriptor, None);
434434
}
435+
436+
// WIP: Add async support to `persist_test_utils`
437+
// TODO:
438+
// - Refactor persist_wallet_changeset (to remove tempfile, anyhow)
439+
// - Introduce PersistError, and handle more errors
440+
// - Extract changeset construction to be used by both sync/async
441+
442+
use alloc::string::{String, ToString};
443+
use core::fmt;
444+
445+
use crate::AsyncWalletPersister;
446+
447+
/// Error while testing a wallet persister.
448+
#[derive(Debug)]
449+
pub enum PersistError {
450+
/// Store error
451+
Store(String),
452+
}
453+
454+
impl fmt::Display for PersistError {
455+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456+
match self {
457+
Self::Store(e) => write!(f, "{e}"),
458+
}
459+
}
460+
}
461+
462+
#[cfg(feature = "std")]
463+
impl std::error::Error for PersistError {}
464+
465+
/// Creates a [`ChangeSet`].
466+
fn get_changeset(tx1: Transaction) -> ChangeSet {
467+
let descriptor: Descriptor<DescriptorPublicKey> = DESCRIPTORS[0].parse().unwrap();
468+
let change_descriptor: Descriptor<DescriptorPublicKey> = DESCRIPTORS[1].parse().unwrap();
469+
470+
let local_chain_changeset = local_chain::ChangeSet {
471+
blocks: [
472+
(910234, Some(hash!("B"))),
473+
(910233, Some(hash!("T"))),
474+
(910235, Some(hash!("C"))),
475+
]
476+
.into(),
477+
};
478+
479+
let txid1 = tx1.compute_txid();
480+
481+
let conf_anchor: ConfirmationBlockTime = ConfirmationBlockTime {
482+
block_id: block_id!(910234, "B"),
483+
confirmation_time: 1755317160,
484+
};
485+
486+
let outpoint = OutPoint::new(hash!("Rust"), 0);
487+
488+
let tx_graph_changeset = tx_graph::ChangeSet::<ConfirmationBlockTime> {
489+
txs: [Arc::new(tx1)].into(),
490+
txouts: [
491+
(
492+
outpoint,
493+
TxOut {
494+
value: Amount::from_sat(1300),
495+
script_pubkey: spk_at_index(&descriptor, 4),
496+
},
497+
),
498+
(
499+
OutPoint::new(hash!("REDB"), 0),
500+
TxOut {
501+
value: Amount::from_sat(1400),
502+
script_pubkey: spk_at_index(&descriptor, 10),
503+
},
504+
),
505+
]
506+
.into(),
507+
anchors: [(conf_anchor, txid1)].into(),
508+
last_seen: [(txid1, 1755317760)].into(),
509+
first_seen: [(txid1, 1755317750)].into(),
510+
last_evicted: [(txid1, 1755317760)].into(),
511+
};
512+
513+
let keychain_txout_changeset = keychain_txout::ChangeSet {
514+
last_revealed: [
515+
(descriptor.descriptor_id(), 12),
516+
(change_descriptor.descriptor_id(), 10),
517+
]
518+
.into(),
519+
spk_cache: [
520+
(
521+
descriptor.descriptor_id(),
522+
SpkIterator::new_with_range(&descriptor, 0..=37).collect(),
523+
),
524+
(
525+
change_descriptor.descriptor_id(),
526+
SpkIterator::new_with_range(&change_descriptor, 0..=35).collect(),
527+
),
528+
]
529+
.into(),
530+
};
531+
532+
let locked_outpoints_changeset = locked_outpoints::ChangeSet {
533+
outpoints: [(outpoint, true)].into(),
534+
};
535+
536+
ChangeSet {
537+
descriptor: Some(descriptor.clone()),
538+
change_descriptor: Some(change_descriptor.clone()),
539+
network: Some(Network::Testnet),
540+
local_chain: local_chain_changeset,
541+
tx_graph: tx_graph_changeset,
542+
indexer: keychain_txout_changeset,
543+
locked_outpoints: locked_outpoints_changeset,
544+
}
545+
}
546+
547+
/// Creates another [`ChangeSet`].
548+
fn get_changeset_two(tx2: Transaction) -> ChangeSet {
549+
let descriptor: Descriptor<DescriptorPublicKey> = DESCRIPTORS[0].parse().unwrap();
550+
551+
let local_chain_changeset = local_chain::ChangeSet {
552+
blocks: [(910236, Some(hash!("BDK")))].into(),
553+
};
554+
555+
let conf_anchor: ConfirmationBlockTime = ConfirmationBlockTime {
556+
block_id: block_id!(910236, "BDK"),
557+
confirmation_time: 1755317760,
558+
};
559+
560+
let txid2 = tx2.compute_txid();
561+
562+
let outpoint = OutPoint::new(hash!("Bitcoin_fixes_things"), 0);
563+
564+
let tx_graph_changeset = tx_graph::ChangeSet::<ConfirmationBlockTime> {
565+
txs: [Arc::new(tx2)].into(),
566+
txouts: [(
567+
outpoint,
568+
TxOut {
569+
value: Amount::from_sat(10000),
570+
script_pubkey: spk_at_index(&descriptor, 21),
571+
},
572+
)]
573+
.into(),
574+
anchors: [(conf_anchor, txid2)].into(),
575+
last_seen: [(txid2, 1755317700)].into(),
576+
first_seen: [(txid2, 1755317700)].into(),
577+
last_evicted: [(txid2, 1755317760)].into(),
578+
};
579+
580+
let keychain_txout_changeset = keychain_txout::ChangeSet {
581+
last_revealed: [(descriptor.descriptor_id(), 14)].into(),
582+
spk_cache: [(
583+
descriptor.descriptor_id(),
584+
SpkIterator::new_with_range(&descriptor, 37..=39).collect(),
585+
)]
586+
.into(),
587+
};
588+
589+
let locked_outpoints_changeset = locked_outpoints::ChangeSet {
590+
outpoints: [(outpoint, true)].into(),
591+
};
592+
593+
ChangeSet {
594+
descriptor: None,
595+
change_descriptor: None,
596+
network: None,
597+
local_chain: local_chain_changeset,
598+
tx_graph: tx_graph_changeset,
599+
indexer: keychain_txout_changeset,
600+
locked_outpoints: locked_outpoints_changeset,
601+
}
602+
}
603+
604+
/// Persist changeset async.
605+
#[allow(clippy::print_stderr)]
606+
pub async fn persist_wallet_changeset_async<F, P>(create_store: F) -> Result<(), PersistError>
607+
where
608+
F: AsyncFn() -> Result<P, P::Error>,
609+
P: AsyncWalletPersister,
610+
P::Error: fmt::Debug + fmt::Display,
611+
{
612+
// Create store
613+
let mut store = create_store().await.expect("store should get created");
614+
let changeset = AsyncWalletPersister::initialize(&mut store)
615+
.await
616+
.expect("empty changeset should get loaded");
617+
assert_eq!(changeset, ChangeSet::default());
618+
619+
let tx1 = create_one_inp_one_out_tx(hash!("We_are_all_Satoshi"), 30_000);
620+
let tx2 = create_one_inp_one_out_tx(tx1.compute_txid(), 20_000);
621+
622+
// Create changeset
623+
let mut changeset = get_changeset(tx1);
624+
625+
AsyncWalletPersister::persist(&mut store, &changeset)
626+
.await
627+
.expect("changeset should get persisted");
628+
629+
let changeset_read = AsyncWalletPersister::initialize(&mut store)
630+
.await
631+
.expect("should load persisted changeset");
632+
633+
if changeset != changeset_read {
634+
eprintln!("BUG: Changeset read does not match persisted, read: {changeset_read:#?}\npersisted: {changeset:#?}");
635+
return Err(PersistError::Store(
636+
"failed test changeset equality".to_string(),
637+
));
638+
}
639+
640+
assert_eq!(changeset_read, changeset);
641+
642+
// Create another changeset
643+
let changeset_2 = get_changeset_two(tx2);
644+
645+
AsyncWalletPersister::persist(&mut store, &changeset_2)
646+
.await
647+
.expect("changeset should get persisted");
648+
649+
let changeset_read = AsyncWalletPersister::initialize(&mut store)
650+
.await
651+
.expect("persister should not fail");
652+
653+
changeset.merge(changeset_2);
654+
655+
if changeset_read != changeset {
656+
eprintln!("BUG: Changeset read does not match persisted, read: {changeset_read:#?}\npersisted: {changeset:#?}");
657+
return Err(PersistError::Store(
658+
"failed test changeset equality".to_string(),
659+
));
660+
}
661+
662+
assert_eq!(changeset, changeset_read);
663+
664+
Ok(())
665+
}

0 commit comments

Comments
 (0)