Skip to content

Commit 0d0ac5b

Browse files
committed
feat: add utilities for testing persistence
Added the following functions to test if persistence of `bdk_chain` is happening correctly. - `persist_txgraph_changeset` - `persist_indexer_changeset` - `persist_local_chain_changeset` which leverage a more general `persist_changeset`. In order to not panic and instead return errors raised by the persistence backend being tested, introduced `PersistErr`. To ensure downcasting and to allow usage of anyhow, `PersisterErr::Persister` requires an error of with 'static + Send + Sync.
1 parent 249413e commit 0d0ac5b

8 files changed

Lines changed: 562 additions & 3 deletions

File tree

crates/chain/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ rand = "0.8"
2929
proptest = "1.2.0"
3030
bdk_testenv = { path = "../testenv" }
3131
criterion = { version = "0.7" }
32+
tempfile = "3"
33+
anyhow = "1.0.102"
3234

3335
[features]
3436
default = ["std", "miniscript"]
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#![cfg(feature = "rusqlite")]
2+
use bdk_chain::{keychain_txout, local_chain, tx_graph, ConfirmationBlockTime};
3+
use bdk_testenv::persist_test_utils::{
4+
persist_indexer_changeset, persist_local_chain_changeset, persist_txgraph_changeset,
5+
};
6+
7+
fn tx_graph_changeset_init(
8+
db: &mut rusqlite::Connection,
9+
) -> rusqlite::Result<tx_graph::ChangeSet<ConfirmationBlockTime>> {
10+
let db_tx = db.transaction()?;
11+
tx_graph::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(&db_tx)?;
12+
let changeset = tx_graph::ChangeSet::<ConfirmationBlockTime>::from_sqlite(&db_tx)?;
13+
db_tx.commit()?;
14+
Ok(changeset)
15+
}
16+
17+
fn tx_graph_changeset_persist(
18+
db: &mut rusqlite::Connection,
19+
changeset: &tx_graph::ChangeSet<ConfirmationBlockTime>,
20+
) -> rusqlite::Result<()> {
21+
let db_tx = db.transaction()?;
22+
changeset.persist_to_sqlite(&db_tx)?;
23+
db_tx.commit()
24+
}
25+
26+
fn keychain_txout_changeset_init(
27+
db: &mut rusqlite::Connection,
28+
) -> rusqlite::Result<keychain_txout::ChangeSet> {
29+
let db_tx = db.transaction()?;
30+
keychain_txout::ChangeSet::init_sqlite_tables(&db_tx)?;
31+
let changeset = keychain_txout::ChangeSet::from_sqlite(&db_tx)?;
32+
db_tx.commit()?;
33+
Ok(changeset)
34+
}
35+
36+
fn keychain_txout_changeset_persist(
37+
db: &mut rusqlite::Connection,
38+
changeset: &keychain_txout::ChangeSet,
39+
) -> rusqlite::Result<()> {
40+
let db_tx = db.transaction()?;
41+
changeset.persist_to_sqlite(&db_tx)?;
42+
db_tx.commit()
43+
}
44+
45+
fn local_chain_changeset_init(
46+
db: &mut rusqlite::Connection,
47+
) -> rusqlite::Result<local_chain::ChangeSet> {
48+
let db_tx = db.transaction()?;
49+
local_chain::ChangeSet::init_sqlite_tables(&db_tx)?;
50+
let changeset = local_chain::ChangeSet::from_sqlite(&db_tx)?;
51+
db_tx.commit()?;
52+
Ok(changeset)
53+
}
54+
55+
fn local_chain_changeset_persist(
56+
db: &mut rusqlite::Connection,
57+
changeset: &local_chain::ChangeSet,
58+
) -> rusqlite::Result<()> {
59+
let db_tx = db.transaction()?;
60+
changeset.persist_to_sqlite(&db_tx)?;
61+
db_tx.commit()
62+
}
63+
64+
#[test]
65+
fn txgraph_is_persisted() -> anyhow::Result<()> {
66+
let temp_dir = tempfile::tempdir().unwrap();
67+
Ok(persist_txgraph_changeset::<rusqlite::Connection, _, _, _>(
68+
|| {
69+
Ok(rusqlite::Connection::open(
70+
temp_dir.path().join("wallet.sqlite"),
71+
)?)
72+
},
73+
|db| Ok(tx_graph_changeset_init(db)?),
74+
|db, changeset| Ok(tx_graph_changeset_persist(db, changeset)?),
75+
)?)
76+
}
77+
78+
#[test]
79+
fn indexer_is_persisted() -> anyhow::Result<()> {
80+
let temp_dir = tempfile::tempdir().unwrap();
81+
Ok(persist_indexer_changeset::<rusqlite::Connection, _, _, _>(
82+
|| {
83+
Ok(rusqlite::Connection::open(
84+
temp_dir.path().join("wallet.sqlite"),
85+
)?)
86+
},
87+
|db| Ok(keychain_txout_changeset_init(db)?),
88+
|db, changeset| Ok(keychain_txout_changeset_persist(db, changeset)?),
89+
)?)
90+
}
91+
92+
#[test]
93+
fn local_chain_is_persisted() -> anyhow::Result<()> {
94+
let temp_dir = tempfile::tempdir().unwrap();
95+
Ok(persist_local_chain_changeset::<
96+
rusqlite::Connection,
97+
_,
98+
_,
99+
_,
100+
>(
101+
|| {
102+
Ok(rusqlite::Connection::open(
103+
temp_dir.path().join("wallet.sqlite"),
104+
)?)
105+
},
106+
|db| Ok(local_chain_changeset_init(db)?),
107+
|db, changeset| Ok(local_chain_changeset_persist(db, changeset)?),
108+
)?)
109+
}

crates/file_store/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ serde = { version = "1", features = ["derive"] }
2121

2222
[dev-dependencies]
2323
tempfile = "3"
24+
anyhow = { version = "1.0.102", default-features = false}
25+
bdk_testenv = {path = "../testenv"}
26+
bdk_chain = { path = "../chain", version = "0.23.1", default-features = false, features = ["serde"]}

crates/file_store/src/store.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ mod test {
295295
const TEST_MAGIC_BYTES: [u8; TEST_MAGIC_BYTES_LEN] =
296296
[98, 100, 107, 102, 115, 49, 49, 49, 49, 49, 49, 49];
297297

298+
use bdk_chain::{keychain_txout, local_chain, tx_graph, ConfirmationBlockTime};
299+
use bdk_testenv::persist_test_utils::{
300+
persist_indexer_changeset, persist_local_chain_changeset, persist_txgraph_changeset,
301+
};
302+
298303
type TestChangeSet = BTreeSet<String>;
299304

300305
/// Check behavior of [`Store::create`] and [`Store::load`].
@@ -599,4 +604,64 @@ mod test {
599604
// current position matches EOF
600605
assert_eq!(current_pointer, expected_pointer);
601606
}
607+
608+
#[test]
609+
fn txgraph_is_persisted() -> anyhow::Result<()> {
610+
let temp_dir = tempfile::tempdir().unwrap();
611+
Ok(persist_txgraph_changeset::<
612+
Store<tx_graph::ChangeSet<ConfirmationBlockTime>>,
613+
_,
614+
_,
615+
_,
616+
>(
617+
|| {
618+
Ok(Store::create(
619+
&TEST_MAGIC_BYTES,
620+
temp_dir.path().join("store.db"),
621+
)?)
622+
},
623+
|db| Ok(db.dump().map(Option::unwrap_or_default)?),
624+
|db, changeset| Ok(db.append(changeset)?),
625+
)?)
626+
}
627+
628+
#[test]
629+
fn indexer_is_persisted() -> anyhow::Result<()> {
630+
let temp_dir = tempfile::tempdir().unwrap();
631+
Ok(persist_indexer_changeset::<
632+
Store<keychain_txout::ChangeSet>,
633+
_,
634+
_,
635+
_,
636+
>(
637+
|| {
638+
Ok(Store::create(
639+
&TEST_MAGIC_BYTES,
640+
temp_dir.path().join("store.db"),
641+
)?)
642+
},
643+
|db| Ok(db.dump().map(Option::unwrap_or_default)?),
644+
|db, changeset| Ok(db.append(changeset)?),
645+
)?)
646+
}
647+
648+
#[test]
649+
fn local_chain_is_persisted() -> anyhow::Result<()> {
650+
let temp_dir = tempfile::tempdir().unwrap();
651+
Ok(persist_local_chain_changeset::<
652+
Store<local_chain::ChangeSet>,
653+
_,
654+
_,
655+
_,
656+
>(
657+
|| {
658+
Ok(Store::create(
659+
&TEST_MAGIC_BYTES,
660+
temp_dir.path().join("store.db"),
661+
)?)
662+
},
663+
|db| Ok(db.dump().map(Option::unwrap_or_default)?),
664+
|db, changeset| Ok(db.append(changeset)?),
665+
)?)
666+
}
602667
}

crates/testenv/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ bitcoin = { version = "0.32.0", default-features = false }
2424
bdk_testenv = { path = "." }
2525

2626
[features]
27-
default = ["std", "download"]
27+
default = ["std", "download", "miniscript"]
2828
download = ["electrsd/bitcoind_download", "electrsd/bitcoind_28_2", "electrsd/esplora_a33e97e1"]
2929
std = ["bdk_chain/std", "bitcoin/rand-std"]
3030
serde = ["bdk_chain/serde"]
31-
31+
miniscript = ["bdk_chain/miniscript"]
3232
[package.metadata.docs.rs]
3333
no-default-features = true

crates/testenv/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
22

3+
pub mod persist_test_utils;
34
pub mod utils;
45

56
use anyhow::Context;
@@ -14,6 +15,8 @@ use core::time::Duration;
1415
use electrsd::bitcoind::mtype::GetBlockTemplate;
1516
use electrsd::bitcoind::{TemplateRequest, TemplateRules};
1617

18+
extern crate alloc;
19+
1720
pub use electrsd;
1821
pub use electrsd::bitcoind;
1922
pub use electrsd::bitcoind::anyhow;

0 commit comments

Comments
 (0)