Skip to content

Commit acccb59

Browse files
committed
Merge #1547: Simplify wallet persistence
340808e docs(wallet): fixes/improvements for `persisted` and `params` types (志宇) 9600293 feat(wallet)!: add persister (`P`) type param to `PersistedWallet<P>` (志宇) 0616057 revert(chain)!: rm `persit` module (志宇) a9c5f76 feat(wallet)!: remove dependency on `bdk_chain::Staged` (志宇) 06a9d6c feat(chain,wallet)!: publicize `.init_sqlite_tables` changeset methods (志宇) 039622f feat(wallet)!: introduce `WalletPersister` (志宇) Pull request description: ### Description Removed the persistence module in `bdk_chain` (which contained the `PersistWith` and `PersistAsyncWith` traits and a `Persisted<T>` wrapper). Replaced it with simplified versions that are `Wallet`-specific. The new traits (`WalletPersister` and `AsyncWalletPersister`) are simpler since they have less type-parameters (being wallet-specific). The old versions were more complex as they were intended to be used with any type. However, we need more time to finalize a works-for-all-bdk-types persistence API. Additionally, `WalletPersister` and `AsyncWalletPersister` also introduce the `initialize` method. It is handy to contain db-initialization (i.e. create tables for SQL) here. We can call `initialize` in `PersistedWallet` constructors. So the `PersistedWallet::persist` method does not need to ensure the database is initialized. To accommodate this, I made the `.init_sqlite_table` methods on changeset-types public, and removed the call of this in `from_sqlite` and `.persist_to_sqlite`. The `initialize` method now loads from the persister, so the `load` associated function is removed. There was a bug in the old `PersistAsyncWith` trait where the lifetime bounds were not strict enough (refer to the conversation in #1552). This is now fixed in the new `AsyncWalletPersister` trait. Docs for the new types are clearer (hopefully). I mentioned implementation details about the new traits and what safety checks were guaranteed in `PersistedWallet`. I think it makes sense just to have a wallet-specific persistence API which we will maintain alongside wallet for v1.0. This is less baggage for users and maintainers alike. ### Notes to the reviewers **How breaking are these changes?** Unless if you were implementing your own persistence, then not breaking at all! If you were implementing your own persistence for BDK, the breakages are minimal. The main change is introducing the `initialize` method to persistence traits (which replaces `load`). **Acknowledgements** I came up with the idea of this while responding to @tnull's suggestions on Discord. Unfortunately we cannot fulfill all of his requests. However, I do believe that this will make things easier for our users. ### Changelog notice * Removed the `persist` module in `bdk_chain`. * Added `WalletPersister`/`AsyncWalletPersister` traits and `PersistedWallet` struct to `bdk_wallet`. These are simplified and safer versions of old structs provided by the `persist` module in `bdk_chain`. * Change `.init_sqlite_tables` method to be public on changeset types. `from_sqlite` and `persist_into_sqlite` methods no longer call `.init_sqlite_tables` internally. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### What is left to do: - [x] Better docs. ACKs for top commit: notmandatory: ACK 340808e Tree-SHA512: 77643196543ad0a741f9ec51de4aad3893c6d608234ad6709c8688043bf1eef1e5e97e71681be0a6b29dec3fa255e2f0e63e08511c79ea4c0a6ef3286653d40c
2 parents e0822d7 + 340808e commit acccb59

File tree

8 files changed

+384
-350
lines changed

8 files changed

+384
-350
lines changed

crates/chain/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ pub use tx_data_traits::*;
3737
pub use tx_graph::TxGraph;
3838
mod chain_oracle;
3939
pub use chain_oracle::*;
40-
mod persist;
41-
pub use persist::*;
4240

4341
#[doc(hidden)]
4442
pub mod example_utils;

crates/chain/src/persist.rs

Lines changed: 0 additions & 169 deletions
This file was deleted.

crates/chain/src/rusqlite_impl.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ where
225225
pub const ANCHORS_TABLE_NAME: &'static str = "bdk_anchors";
226226

227227
/// Initialize sqlite tables.
228-
fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
228+
pub fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
229229
let schema_v0: &[&str] = &[
230230
// full transactions
231231
&format!(
@@ -264,9 +264,9 @@ where
264264
}
265265

266266
/// Construct a [`TxGraph`] from an sqlite database.
267+
///
268+
/// Remember to call [`Self::init_sqlite_tables`] beforehand.
267269
pub fn from_sqlite(db_tx: &rusqlite::Transaction) -> rusqlite::Result<Self> {
268-
Self::init_sqlite_tables(db_tx)?;
269-
270270
let mut changeset = Self::default();
271271

272272
let mut statement = db_tx.prepare(&format!(
@@ -332,9 +332,9 @@ where
332332
}
333333

334334
/// Persist `changeset` to the sqlite database.
335+
///
336+
/// Remember to call [`Self::init_sqlite_tables`] beforehand.
335337
pub fn persist_to_sqlite(&self, db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
336-
Self::init_sqlite_tables(db_tx)?;
337-
338338
let mut statement = db_tx.prepare_cached(&format!(
339339
"INSERT INTO {}(txid, raw_tx) VALUES(:txid, :raw_tx) ON CONFLICT(txid) DO UPDATE SET raw_tx=:raw_tx",
340340
Self::TXS_TABLE_NAME,
@@ -396,7 +396,7 @@ impl local_chain::ChangeSet {
396396
pub const BLOCKS_TABLE_NAME: &'static str = "bdk_blocks";
397397

398398
/// Initialize sqlite tables for persisting [`local_chain::LocalChain`].
399-
fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
399+
pub fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
400400
let schema_v0: &[&str] = &[
401401
// blocks
402402
&format!(
@@ -411,9 +411,9 @@ impl local_chain::ChangeSet {
411411
}
412412

413413
/// Construct a [`LocalChain`](local_chain::LocalChain) from sqlite database.
414+
///
415+
/// Remember to call [`Self::init_sqlite_tables`] beforehand.
414416
pub fn from_sqlite(db_tx: &rusqlite::Transaction) -> rusqlite::Result<Self> {
415-
Self::init_sqlite_tables(db_tx)?;
416-
417417
let mut changeset = Self::default();
418418

419419
let mut statement = db_tx.prepare(&format!(
@@ -435,9 +435,9 @@ impl local_chain::ChangeSet {
435435
}
436436

437437
/// Persist `changeset` to the sqlite database.
438+
///
439+
/// Remember to call [`Self::init_sqlite_tables`] beforehand.
438440
pub fn persist_to_sqlite(&self, db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
439-
Self::init_sqlite_tables(db_tx)?;
440-
441441
let mut replace_statement = db_tx.prepare_cached(&format!(
442442
"REPLACE INTO {}(block_height, block_hash) VALUES(:block_height, :block_hash)",
443443
Self::BLOCKS_TABLE_NAME,
@@ -471,7 +471,7 @@ impl keychain_txout::ChangeSet {
471471

472472
/// Initialize sqlite tables for persisting
473473
/// [`KeychainTxOutIndex`](keychain_txout::KeychainTxOutIndex).
474-
fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
474+
pub fn init_sqlite_tables(db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
475475
let schema_v0: &[&str] = &[
476476
// last revealed
477477
&format!(
@@ -487,9 +487,9 @@ impl keychain_txout::ChangeSet {
487487

488488
/// Construct [`KeychainTxOutIndex`](keychain_txout::KeychainTxOutIndex) from sqlite database
489489
/// and given parameters.
490+
///
491+
/// Remember to call [`Self::init_sqlite_tables`] beforehand.
490492
pub fn from_sqlite(db_tx: &rusqlite::Transaction) -> rusqlite::Result<Self> {
491-
Self::init_sqlite_tables(db_tx)?;
492-
493493
let mut changeset = Self::default();
494494

495495
let mut statement = db_tx.prepare(&format!(
@@ -511,9 +511,9 @@ impl keychain_txout::ChangeSet {
511511
}
512512

513513
/// Persist `changeset` to the sqlite database.
514+
///
515+
/// Remember to call [`Self::init_sqlite_tables`] beforehand.
514516
pub fn persist_to_sqlite(&self, db_tx: &rusqlite::Transaction) -> rusqlite::Result<()> {
515-
Self::init_sqlite_tables(db_tx)?;
516-
517517
let mut statement = db_tx.prepare_cached(&format!(
518518
"REPLACE INTO {}(descriptor_id, last_revealed) VALUES(:descriptor_id, :last_revealed)",
519519
Self::LAST_REVEALED_TABLE_NAME,

crates/wallet/src/wallet/changeset.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,8 @@ impl ChangeSet {
7272
/// Name of table to store wallet descriptors and network.
7373
pub const WALLET_TABLE_NAME: &'static str = "bdk_wallet";
7474

75-
/// Initialize sqlite tables for wallet schema & table.
76-
fn init_wallet_sqlite_tables(
77-
db_tx: &chain::rusqlite::Transaction,
78-
) -> chain::rusqlite::Result<()> {
75+
/// Initialize sqlite tables for wallet tables.
76+
pub fn init_sqlite_tables(db_tx: &chain::rusqlite::Transaction) -> chain::rusqlite::Result<()> {
7977
let schema_v0: &[&str] = &[&format!(
8078
"CREATE TABLE {} ( \
8179
id INTEGER PRIMARY KEY NOT NULL CHECK (id = 0), \
@@ -85,12 +83,17 @@ impl ChangeSet {
8583
) STRICT;",
8684
Self::WALLET_TABLE_NAME,
8785
)];
88-
crate::rusqlite_impl::migrate_schema(db_tx, Self::WALLET_SCHEMA_NAME, &[schema_v0])
86+
crate::rusqlite_impl::migrate_schema(db_tx, Self::WALLET_SCHEMA_NAME, &[schema_v0])?;
87+
88+
bdk_chain::local_chain::ChangeSet::init_sqlite_tables(db_tx)?;
89+
bdk_chain::tx_graph::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(db_tx)?;
90+
bdk_chain::keychain_txout::ChangeSet::init_sqlite_tables(db_tx)?;
91+
92+
Ok(())
8993
}
9094

9195
/// Recover a [`ChangeSet`] from sqlite database.
9296
pub fn from_sqlite(db_tx: &chain::rusqlite::Transaction) -> chain::rusqlite::Result<Self> {
93-
Self::init_wallet_sqlite_tables(db_tx)?;
9497
use chain::rusqlite::OptionalExtension;
9598
use chain::Impl;
9699

@@ -129,7 +132,6 @@ impl ChangeSet {
129132
&self,
130133
db_tx: &chain::rusqlite::Transaction,
131134
) -> chain::rusqlite::Result<()> {
132-
Self::init_wallet_sqlite_tables(db_tx)?;
133135
use chain::rusqlite::named_params;
134136
use chain::Impl;
135137

crates/wallet/src/wallet/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ use bitcoin::{
4545
use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
4646
use bitcoin::{constants::genesis_block, Amount};
4747
use bitcoin::{secp256k1::Secp256k1, Weight};
48-
use chain::Staged;
4948
use core::fmt;
5049
use core::mem;
5150
use core::ops::Deref;
@@ -123,14 +122,6 @@ pub struct Wallet {
123122
secp: SecpCtx,
124123
}
125124

126-
impl Staged for Wallet {
127-
type ChangeSet = ChangeSet;
128-
129-
fn staged(&mut self) -> &mut Self::ChangeSet {
130-
&mut self.stage
131-
}
132-
}
133-
134125
/// An update to [`Wallet`].
135126
///
136127
/// It updates [`KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
@@ -2303,7 +2294,7 @@ impl Wallet {
23032294
Ok(())
23042295
}
23052296

2306-
/// Get a reference of the staged [`ChangeSet`] that are yet to be committed (if any).
2297+
/// Get a reference of the staged [`ChangeSet`] that is yet to be committed (if any).
23072298
pub fn staged(&self) -> Option<&ChangeSet> {
23082299
if self.stage.is_empty() {
23092300
None
@@ -2312,6 +2303,15 @@ impl Wallet {
23122303
}
23132304
}
23142305

2306+
/// Get a mutable reference of the staged [`ChangeSet`] that is yet to be commited (if any).
2307+
pub fn staged_mut(&mut self) -> Option<&mut ChangeSet> {
2308+
if self.stage.is_empty() {
2309+
None
2310+
} else {
2311+
Some(&mut self.stage)
2312+
}
2313+
}
2314+
23152315
/// Take the staged [`ChangeSet`] to be persisted now (if any).
23162316
pub fn take_staged(&mut self) -> Option<ChangeSet> {
23172317
self.stage.take()

0 commit comments

Comments
 (0)