feat!: rm persist submodule
Remove `PersistBackend`, `PersistBackendAsync`, `StageExt` and `StageExtAsync`. Remove `async` feature flag and dependency. Update examples and wallet.
This commit is contained in:
@@ -30,7 +30,6 @@ js-sys = "0.3"
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["bitcoin/std", "miniscript/std", "bdk_chain/std"]
|
||||
async = ["bdk_chain/async"]
|
||||
compiler = ["miniscript/compiler"]
|
||||
all-keys = ["keys-bip39"]
|
||||
keys-bip39 = ["bip39"]
|
||||
|
||||
@@ -26,7 +26,6 @@ use bdk_chain::{
|
||||
local_chain::{
|
||||
self, ApplyHeaderError, CannotConnectError, CheckPoint, CheckPointIter, LocalChain,
|
||||
},
|
||||
persist::{PersistBackend, StageExt},
|
||||
spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult},
|
||||
tx_graph::{CanonicalTx, TxGraph},
|
||||
Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeHeightAnchor, FullTxOut,
|
||||
@@ -85,12 +84,12 @@ const COINBASE_MATURITY: u32 = 100;
|
||||
/// 1. output *descriptors* from which it can derive addresses.
|
||||
/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
|
||||
///
|
||||
/// The user is responsible for loading and writing wallet changes using an implementation of
|
||||
/// [`PersistBackend`]. See individual functions and example for instructions on when [`Wallet`]
|
||||
/// state needs to be persisted.
|
||||
/// The user is responsible for loading and writing wallet changes which are represented as
|
||||
/// [`ChangeSet`]s (see [`take_staged`]). Also see individual functions and example for instructions
|
||||
/// on when [`Wallet`] state needs to be persisted.
|
||||
///
|
||||
/// [`PersistBackend`]: bdk_chain::persist::PersistBackend
|
||||
/// [`signer`]: crate::signer
|
||||
/// [`take_staged`]: Wallet::take_staged
|
||||
#[derive(Debug)]
|
||||
pub struct Wallet {
|
||||
signers: Arc<SignersContainer>,
|
||||
@@ -141,8 +140,7 @@ impl From<SyncResult> for Update {
|
||||
}
|
||||
|
||||
/// The changes made to a wallet by applying an [`Update`].
|
||||
pub type ChangeSet =
|
||||
bdk_chain::persist::CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>;
|
||||
pub type ChangeSet = bdk_chain::CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>;
|
||||
|
||||
/// A derived address and the index it was found at.
|
||||
/// For convenience this automatically derefs to `Address`
|
||||
@@ -408,14 +406,13 @@ impl Wallet {
|
||||
/// # use bdk_wallet::descriptor::Descriptor;
|
||||
/// # use bitcoin::key::Secp256k1;
|
||||
/// # use bdk_wallet::KeychainKind;
|
||||
/// # use bdk_sqlite::{Store, rusqlite::Connection};
|
||||
/// use bdk_sqlite::{Store, rusqlite::Connection};
|
||||
/// #
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use bdk_chain::persist::PersistBackend;
|
||||
/// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
|
||||
/// # let file_path = temp_dir.path().join("store.db");
|
||||
/// # let conn = Connection::open(file_path).expect("must open connection");
|
||||
/// # let mut db = Store::new(conn).expect("must create db");
|
||||
/// let conn = Connection::open(file_path).expect("must open connection");
|
||||
/// let mut db = Store::new(conn).expect("must create db");
|
||||
/// let secp = Secp256k1::new();
|
||||
///
|
||||
/// let (external_descriptor, external_keymap) = Descriptor::parse_descriptor(&secp, "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)").unwrap();
|
||||
@@ -423,7 +420,7 @@ impl Wallet {
|
||||
///
|
||||
/// let external_signer_container = SignersContainer::build(external_keymap, &external_descriptor, &secp);
|
||||
/// let internal_signer_container = SignersContainer::build(internal_keymap, &internal_descriptor, &secp);
|
||||
/// let changeset = db.load_changes()?.expect("there must be an existing changeset");
|
||||
/// let changeset = db.read()?.expect("there must be an existing changeset");
|
||||
/// let mut wallet = Wallet::load_from_changeset(changeset)?;
|
||||
///
|
||||
/// external_signer_container.signers().into_iter()
|
||||
@@ -482,13 +479,12 @@ impl Wallet {
|
||||
/// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use bdk_chain::persist::PersistBackend;
|
||||
/// # use bdk_wallet::Wallet;
|
||||
/// # use bdk_sqlite::{Store, rusqlite::Connection};
|
||||
/// use bdk_sqlite::{Store, rusqlite::Connection};
|
||||
/// # use bitcoin::Network::Testnet;
|
||||
/// # let conn = Connection::open_in_memory().expect("must open connection");
|
||||
/// let conn = Connection::open_in_memory().expect("must open connection");
|
||||
/// let mut db = Store::new(conn).expect("must create db");
|
||||
/// let changeset = db.load_changes()?;
|
||||
/// let changeset = db.read()?;
|
||||
///
|
||||
/// let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
|
||||
/// let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
|
||||
@@ -666,16 +662,17 @@ impl Wallet {
|
||||
/// calls to this method before closing the wallet. For example:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use bdk_chain::persist::PersistBackend;
|
||||
/// # use bdk_wallet::wallet::{Wallet, ChangeSet};
|
||||
/// # use bdk_wallet::KeychainKind;
|
||||
/// # use bdk_sqlite::{Store, rusqlite::Connection};
|
||||
/// # let conn = Connection::open_in_memory().expect("must open connection");
|
||||
/// # let mut db = Store::new(conn).expect("must create store");
|
||||
/// use bdk_sqlite::{rusqlite::Connection, Store};
|
||||
/// let conn = Connection::open_in_memory().expect("must open connection");
|
||||
/// let mut db = Store::new(conn).expect("must create store");
|
||||
/// # let changeset = ChangeSet::default();
|
||||
/// # let mut wallet = Wallet::load_from_changeset(changeset).expect("load wallet");
|
||||
/// let next_address = wallet.reveal_next_address(KeychainKind::External);
|
||||
/// wallet.commit_to(&mut db)?;
|
||||
/// if let Some(changeset) = wallet.take_staged() {
|
||||
/// db.write(&changeset)?;
|
||||
/// }
|
||||
///
|
||||
/// // Now it's safe to show the user their next address!
|
||||
/// println!("Next address: {}", next_address.address);
|
||||
@@ -2283,44 +2280,22 @@ impl Wallet {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Commits all currently [`staged`](Wallet::staged) changes to the `persist_backend`.
|
||||
///
|
||||
/// This returns whether anything was persisted.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns a backend-defined error if this fails.
|
||||
pub fn commit_to<B>(&mut self, persist_backend: &mut B) -> Result<bool, B::WriteError>
|
||||
where
|
||||
B: PersistBackend<ChangeSet>,
|
||||
{
|
||||
let committed = StageExt::commit_to(&mut self.stage, persist_backend)?;
|
||||
Ok(committed.is_some())
|
||||
/// Get a reference of the staged [`ChangeSet`] that are yet to be committed (if any).
|
||||
pub fn staged(&self) -> Option<&ChangeSet> {
|
||||
if self.stage.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&self.stage)
|
||||
}
|
||||
}
|
||||
|
||||
/// Commits all currently [`staged`](Wallet::staged) changes to the async `persist_backend`.
|
||||
///
|
||||
/// This returns whether anything was persisted.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns a backend-defined error if this fails.
|
||||
#[cfg(feature = "async")]
|
||||
pub async fn commit_to_async<B>(
|
||||
&mut self,
|
||||
persist_backend: &mut B,
|
||||
) -> Result<bool, B::WriteError>
|
||||
where
|
||||
B: bdk_chain::persist::PersistBackendAsync<ChangeSet> + Send + Sync,
|
||||
{
|
||||
let committed =
|
||||
bdk_chain::persist::StageExtAsync::commit_to(&mut self.stage, persist_backend).await?;
|
||||
Ok(committed.is_some())
|
||||
}
|
||||
|
||||
/// Get the staged [`ChangeSet`] that is yet to be committed.
|
||||
pub fn staged(&self) -> &ChangeSet {
|
||||
&self.stage
|
||||
/// Take the staged [`ChangeSet`] to be persisted now (if any).
|
||||
pub fn take_staged(&mut self) -> Option<ChangeSet> {
|
||||
if self.stage.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(core::mem::take(&mut self.stage))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the inner [`TxGraph`].
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use anyhow::anyhow;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use bdk_chain::collections::BTreeMap;
|
||||
use bdk_chain::COINBASE_MATURITY;
|
||||
use bdk_chain::{persist::PersistBackend, BlockId, ConfirmationTime};
|
||||
use bdk_chain::{BlockId, ConfirmationTime};
|
||||
use bdk_sqlite::rusqlite::Connection;
|
||||
use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
|
||||
use bdk_wallet::psbt::PsbtUtils;
|
||||
@@ -13,7 +12,7 @@ use bdk_wallet::signer::{SignOptions, SignerError};
|
||||
use bdk_wallet::wallet::coin_selection::{self, LargestFirstCoinSelection};
|
||||
use bdk_wallet::wallet::error::CreateTxError;
|
||||
use bdk_wallet::wallet::tx_builder::AddForeignUtxoError;
|
||||
use bdk_wallet::wallet::{AddressInfo, Balance, NewError, Wallet};
|
||||
use bdk_wallet::wallet::{AddressInfo, Balance, ChangeSet, NewError, Wallet};
|
||||
use bdk_wallet::KeychainKind;
|
||||
use bitcoin::hashes::Hash;
|
||||
use bitcoin::key::Secp256k1;
|
||||
@@ -72,11 +71,18 @@ const DB_MAGIC: &[u8] = &[0x21, 0x24, 0x48];
|
||||
|
||||
#[test]
|
||||
fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
fn run<B, FN, FR>(filename: &str, create_new: FN, recover: FR) -> anyhow::Result<()>
|
||||
fn run<Db, New, Recover, Read, Write>(
|
||||
filename: &str,
|
||||
create_new: New,
|
||||
recover: Recover,
|
||||
read: Read,
|
||||
write: Write,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
B: PersistBackend<bdk_wallet::wallet::ChangeSet> + Send + Sync + 'static,
|
||||
FN: Fn(&Path) -> anyhow::Result<B>,
|
||||
FR: Fn(&Path) -> anyhow::Result<B>,
|
||||
New: Fn(&Path) -> anyhow::Result<Db>,
|
||||
Recover: Fn(&Path) -> anyhow::Result<Db>,
|
||||
Read: Fn(&mut Db) -> anyhow::Result<Option<ChangeSet>>,
|
||||
Write: Fn(&mut Db, &ChangeSet) -> anyhow::Result<()>,
|
||||
{
|
||||
let temp_dir = tempfile::tempdir().expect("must create tempdir");
|
||||
let file_path = temp_dir.path().join(filename);
|
||||
@@ -91,9 +97,9 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
|
||||
// persist new wallet changes
|
||||
let mut db = create_new(&file_path).expect("must create db");
|
||||
wallet
|
||||
.commit_to(&mut db)
|
||||
.map_err(|e| anyhow!("write changes error: {}", e))?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
write(&mut db, &changeset)?;
|
||||
}
|
||||
wallet.spk_index().clone()
|
||||
};
|
||||
|
||||
@@ -101,10 +107,7 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
{
|
||||
// load persisted wallet changes
|
||||
let db = &mut recover(&file_path).expect("must recover db");
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.expect("must recover wallet")
|
||||
.expect("changeset");
|
||||
let changeset = read(db).expect("must recover wallet").expect("changeset");
|
||||
|
||||
let wallet = Wallet::load_from_changeset(changeset).expect("must recover wallet");
|
||||
assert_eq!(wallet.network(), Network::Testnet);
|
||||
@@ -132,11 +135,15 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
"store.db",
|
||||
|path| Ok(bdk_file_store::Store::create_new(DB_MAGIC, path)?),
|
||||
|path| Ok(bdk_file_store::Store::open(DB_MAGIC, path)?),
|
||||
|db| Ok(bdk_file_store::Store::aggregate_changesets(db)?),
|
||||
|db, changeset| Ok(bdk_file_store::Store::append_changeset(db, changeset)?),
|
||||
)?;
|
||||
run(
|
||||
"store.sqlite",
|
||||
|path| Ok(bdk_sqlite::Store::new(Connection::open(path)?)?),
|
||||
|path| Ok(bdk_sqlite::Store::new(Connection::open(path)?)?),
|
||||
|db| Ok(bdk_sqlite::Store::read(db)?),
|
||||
|db, changeset| Ok(bdk_sqlite::Store::write(db, changeset)?),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -144,10 +151,16 @@ fn load_recovers_wallet() -> anyhow::Result<()> {
|
||||
|
||||
#[test]
|
||||
fn new_or_load() -> anyhow::Result<()> {
|
||||
fn run<B, F>(filename: &str, new_or_load: F) -> anyhow::Result<()>
|
||||
fn run<Db, NewOrRecover, Read, Write>(
|
||||
filename: &str,
|
||||
new_or_load: NewOrRecover,
|
||||
read: Read,
|
||||
write: Write,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
B: PersistBackend<bdk_wallet::wallet::ChangeSet> + Send + Sync + 'static,
|
||||
F: Fn(&Path) -> anyhow::Result<B>,
|
||||
NewOrRecover: Fn(&Path) -> anyhow::Result<Db>,
|
||||
Read: Fn(&mut Db) -> anyhow::Result<Option<ChangeSet>>,
|
||||
Write: Fn(&mut Db, &ChangeSet) -> anyhow::Result<()>,
|
||||
{
|
||||
let temp_dir = tempfile::tempdir().expect("must create tempdir");
|
||||
let file_path = temp_dir.path().join(filename);
|
||||
@@ -158,18 +171,16 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
let wallet = &mut Wallet::new_or_load(desc, change_desc, None, Network::Testnet)
|
||||
.expect("must init wallet");
|
||||
let mut db = new_or_load(&file_path).expect("must create db");
|
||||
wallet
|
||||
.commit_to(&mut db)
|
||||
.map_err(|e| anyhow!("write changes error: {}", e))?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
write(&mut db, &changeset)?;
|
||||
}
|
||||
wallet.keychains().map(|(k, v)| (*k, v.clone())).collect()
|
||||
};
|
||||
|
||||
// wrong network
|
||||
{
|
||||
let mut db = new_or_load(&file_path).expect("must create db");
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.map_err(|e| anyhow!("load changes error: {}", e))?;
|
||||
let changeset = read(&mut db)?;
|
||||
let err = Wallet::new_or_load(desc, change_desc, changeset, Network::Bitcoin)
|
||||
.expect_err("wrong network");
|
||||
assert!(
|
||||
@@ -192,9 +203,7 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
bitcoin::blockdata::constants::genesis_block(Network::Testnet).block_hash();
|
||||
|
||||
let db = &mut new_or_load(&file_path).expect("must open db");
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.map_err(|e| anyhow!("load changes error: {}", e))?;
|
||||
let changeset = read(db)?;
|
||||
let err = Wallet::new_or_load_with_genesis_hash(
|
||||
desc,
|
||||
change_desc,
|
||||
@@ -223,9 +232,7 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
.0;
|
||||
|
||||
let db = &mut new_or_load(&file_path).expect("must open db");
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.map_err(|e| anyhow!("load changes error: {}", e))?;
|
||||
let changeset = read(db)?;
|
||||
let err =
|
||||
Wallet::new_or_load(exp_descriptor, exp_change_desc, changeset, Network::Testnet)
|
||||
.expect_err("wrong external descriptor");
|
||||
@@ -249,9 +256,7 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
.0;
|
||||
|
||||
let db = &mut new_or_load(&file_path).expect("must open db");
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.map_err(|e| anyhow!("load changes error: {}", e))?;
|
||||
let changeset = read(db)?;
|
||||
let err = Wallet::new_or_load(desc, exp_descriptor, changeset, Network::Testnet)
|
||||
.expect_err("wrong internal descriptor");
|
||||
assert!(
|
||||
@@ -268,9 +273,7 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
// all parameters match
|
||||
{
|
||||
let db = &mut new_or_load(&file_path).expect("must open db");
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.map_err(|e| anyhow!("load changes error: {}", e))?;
|
||||
let changeset = read(db)?;
|
||||
let wallet = Wallet::new_or_load(desc, change_desc, changeset, Network::Testnet)
|
||||
.expect("must recover wallet");
|
||||
assert_eq!(wallet.network(), Network::Testnet);
|
||||
@@ -282,12 +285,18 @@ fn new_or_load() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
run("store.db", |path| {
|
||||
Ok(bdk_file_store::Store::open_or_create_new(DB_MAGIC, path)?)
|
||||
})?;
|
||||
run("store.sqlite", |path| {
|
||||
Ok(bdk_sqlite::Store::new(Connection::open(path)?)?)
|
||||
})?;
|
||||
run(
|
||||
"store.db",
|
||||
|path| Ok(bdk_file_store::Store::open_or_create_new(DB_MAGIC, path)?),
|
||||
|db| Ok(bdk_file_store::Store::aggregate_changesets(db)?),
|
||||
|db, changeset| Ok(bdk_file_store::Store::append_changeset(db, changeset)?),
|
||||
)?;
|
||||
run(
|
||||
"store.sqlite",
|
||||
|path| Ok(bdk_sqlite::Store::new(Connection::open(path)?)?),
|
||||
|db| Ok(bdk_sqlite::Store::read(db)?),
|
||||
|db, changeset| Ok(bdk_sqlite::Store::write(db, changeset)?),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user