Skip to content

Commit c0181a1

Browse files
committed
feat: add utilities for testing persistence
Added `persist_wallet_changeset` function and supporting functions.
1 parent c39ce79 commit c0181a1

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed

wallet/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ bitcoin = { version = "0.32.6", features = [ "serde", "base64" ], default-featur
2222
serde = { version = "^1.0", features = ["derive"] }
2323
serde_json = { version = "^1.0" }
2424
bdk_chain = { version = "0.23.0", features = [ "miniscript", "serde" ], default-features = false }
25+
anyhow = { version = "1.0.98", optional = true }
26+
tempfile = { version = "3.20.0", optional = true }
27+
bdk_testenv = { version = "0.13.0", optional = true}
2528

2629
# Optional dependencies
2730
bip39 = { version = "2.0", optional = true }
@@ -35,7 +38,7 @@ all-keys = ["keys-bip39"]
3538
keys-bip39 = ["bip39"]
3639
rusqlite = ["bdk_chain/rusqlite"]
3740
file_store = ["bdk_file_store"]
38-
test-utils = ["std"]
41+
test-utils = ["std", "anyhow", "tempfile", "bdk_testenv"]
3942

4043
[dev-dependencies]
4144
assert_matches = "1.5.0"

wallet/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ pub mod keys;
3131
pub mod psbt;
3232
#[cfg(feature = "test-utils")]
3333
pub mod test_utils;
34+
35+
#[cfg(feature = "test-utils")]
36+
pub mod persist_test_utils;
37+
3438
mod types;
3539
mod wallet;
3640

wallet/src/persist_test_utils.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
use crate::{
2+
bitcoin::{
3+
absolute, hashes::Hash, transaction, Amount, BlockHash, Network, OutPoint, ScriptBuf,
4+
Transaction, TxIn, TxOut, Txid,
5+
},
6+
chain::{keychain_txout, local_chain, tx_graph, ConfirmationBlockTime, DescriptorExt, Merge},
7+
miniscript::descriptor::{Descriptor, DescriptorPublicKey},
8+
ChangeSet, WalletPersister,
9+
};
10+
use std::collections::BTreeMap;
11+
use std::fmt::Debug;
12+
use std::path::Path;
13+
use std::sync::Arc;
14+
use bdk_testenv::{block_id, hash};
15+
16+
pub const DESCRIPTORS: [&str; 4] = [
17+
"tr([5940b9b9/86'/0'/0']tpubDDVNqmq75GNPWQ9UNKfP43UwjaHU4GYfoPavojQbfpyfZp2KetWgjGBRRAy4tYCrAA6SB11mhQAkqxjh1VtQHyKwT4oYxpwLaGHvoKmtxZf/1/*)#ypcpw2dr",
18+
"tr([5940b9b9/86'/0'/0']tpubDDVNqmq75GNPWQ9UNKfP43UwjaHU4GYfoPavojQbfpyfZp2KetWgjGBRRAy4tYCrAA6SB11mhQAkqxjh1VtQHyKwT4oYxpwLaGHvoKmtxZf/0/*)#44aqnlam",
19+
"wpkh([41f2aed0/84h/1h/0h]tpubDDFSdQWw75hk1ewbwnNpPp5DvXFRKt68ioPoyJDY752cNHKkFxPWqkqCyCf4hxrEfpuxh46QisehL3m8Bi6MsAv394QVLopwbtfvryFQNUH/1/*)#emtwewtk",
20+
"wpkh([41f2aed0/84h/1h/0h]tpubDDFSdQWw75hk1ewbwnNpPp5DvXFRKt68ioPoyJDY752cNHKkFxPWqkqCyCf4hxrEfpuxh46QisehL3m8Bi6MsAv394QVLopwbtfvryFQNUH/0/*)#g0w0ymmw",
21+
];
22+
23+
pub fn create_one_inp_one_out_tx(txid: Txid, amount: u64) -> Transaction {
24+
Transaction {
25+
version: transaction::Version::ONE,
26+
lock_time: absolute::LockTime::ZERO,
27+
input: vec![TxIn {
28+
previous_output: OutPoint::new(txid, 0),
29+
..TxIn::default()
30+
}],
31+
output: vec![TxOut {
32+
value: Amount::from_sat(amount),
33+
script_pubkey: ScriptBuf::new(),
34+
}],
35+
}
36+
}
37+
38+
pub fn run<Db, CreateDb>(filename: &str, create_db: CreateDb)
39+
where
40+
CreateDb: Fn(&Path) -> anyhow::Result<Db>,
41+
Db: WalletPersister,
42+
Db::Error: Debug,
43+
{
44+
let temp_dir = tempfile::tempdir().expect("must create tempdir");
45+
let file_path = temp_dir.path().join(filename);
46+
47+
let mut db = create_db(&file_path).expect("db should get created");
48+
49+
let changeset =
50+
WalletPersister::initialize(&mut db).expect("empty changeset should get loaded");
51+
assert_eq!(changeset, ChangeSet::default());
52+
53+
let descriptor: Descriptor<DescriptorPublicKey> = DESCRIPTORS[0].parse().unwrap();
54+
let change_descriptor: Descriptor<DescriptorPublicKey> = DESCRIPTORS[1].parse().unwrap();
55+
56+
let mut blocks: BTreeMap<u32, Option<BlockHash>> = BTreeMap::new();
57+
blocks.insert(0u32, Some(hash!("B")));
58+
blocks.insert(1u32, Some(hash!("T")));
59+
blocks.insert(2u32, Some(hash!("C")));
60+
let local_chain_changeset = local_chain::ChangeSet { blocks };
61+
62+
let tx1 = Arc::new(create_one_inp_one_out_tx(
63+
Txid::from_byte_array([0; 32]),
64+
30_000,
65+
));
66+
let tx2 = Arc::new(create_one_inp_one_out_tx(tx1.compute_txid(), 20_000));
67+
68+
let block_id = block_id!(1, "BDK");
69+
70+
let conf_anchor: ConfirmationBlockTime = ConfirmationBlockTime {
71+
block_id,
72+
confirmation_time: 123,
73+
};
74+
75+
let tx_graph_changeset = tx_graph::ChangeSet::<ConfirmationBlockTime> {
76+
txs: [tx1.clone()].into(),
77+
txouts: [].into(),
78+
anchors: [(conf_anchor, tx1.compute_txid())].into(),
79+
last_seen: [(tx1.compute_txid(), 100)].into(),
80+
first_seen: [(tx1.compute_txid(), 80)].into(),
81+
last_evicted: [(tx1.compute_txid(), 150)].into(),
82+
};
83+
84+
let keychain_txout_changeset = keychain_txout::ChangeSet {
85+
last_revealed: [
86+
(descriptor.descriptor_id(), 12),
87+
(change_descriptor.descriptor_id(), 10),
88+
]
89+
.into(),
90+
spk_cache: [
91+
(
92+
descriptor.descriptor_id(),
93+
[(0u32, ScriptBuf::from_bytes(vec![245, 123, 112]))].into(),
94+
),
95+
(
96+
change_descriptor.descriptor_id(),
97+
[
98+
(100u32, ScriptBuf::from_bytes(vec![145, 234, 98])),
99+
(1000u32, ScriptBuf::from_bytes(vec![5, 6, 8])),
100+
]
101+
.into(),
102+
),
103+
]
104+
.into(),
105+
};
106+
107+
let mut changeset = ChangeSet {
108+
descriptor: Some(descriptor.clone()),
109+
change_descriptor: Some(change_descriptor.clone()),
110+
network: Some(Network::Bitcoin),
111+
local_chain: local_chain_changeset,
112+
tx_graph: tx_graph_changeset,
113+
indexer: keychain_txout_changeset,
114+
};
115+
116+
WalletPersister::persist(&mut db, &changeset).expect("changeset should get persisted");
117+
118+
let changeset_read = WalletPersister::initialize(&mut db).expect("changeset should get loaded");
119+
120+
assert_eq!(changeset, changeset_read);
121+
122+
let mut blocks: BTreeMap<u32, Option<BlockHash>> = BTreeMap::new();
123+
blocks.insert(4u32, Some(hash!("RE")));
124+
blocks.insert(5u32, Some(hash!("DB")));
125+
let local_chain_changeset = local_chain::ChangeSet { blocks };
126+
127+
let block_id = block_id!(2, "Bitcoin");
128+
129+
let conf_anchor: ConfirmationBlockTime = ConfirmationBlockTime {
130+
block_id,
131+
confirmation_time: 214,
132+
};
133+
134+
let tx_graph_changeset = tx_graph::ChangeSet::<ConfirmationBlockTime> {
135+
txs: [tx2.clone()].into(),
136+
txouts: [].into(),
137+
anchors: [(conf_anchor, tx2.compute_txid())].into(),
138+
last_seen: [(tx2.compute_txid(), 200)].into(),
139+
first_seen: [(tx2.compute_txid(), 160)].into(),
140+
last_evicted: [(tx2.compute_txid(), 300)].into(),
141+
};
142+
143+
let keychain_txout_changeset = keychain_txout::ChangeSet {
144+
last_revealed: [(descriptor.descriptor_id(), 14)].into(),
145+
spk_cache: [(
146+
change_descriptor.descriptor_id(),
147+
[
148+
(102u32, ScriptBuf::from_bytes(vec![8, 45, 78])),
149+
(1001u32, ScriptBuf::from_bytes(vec![29, 56, 47])),
150+
]
151+
.into(),
152+
)]
153+
.into(),
154+
};
155+
156+
let changeset_new = ChangeSet {
157+
descriptor: Some(descriptor),
158+
change_descriptor: Some(change_descriptor),
159+
network: Some(Network::Bitcoin),
160+
local_chain: local_chain_changeset,
161+
tx_graph: tx_graph_changeset,
162+
indexer: keychain_txout_changeset,
163+
};
164+
165+
WalletPersister::persist(&mut db, &changeset_new).expect("changeset should get persisted");
166+
let changeset_read_new = WalletPersister::initialize(&mut db).unwrap();
167+
168+
changeset.merge(changeset_new);
169+
170+
assert_eq!(changeset, changeset_read_new);
171+
}

0 commit comments

Comments
 (0)