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:
parent
782eb56bd4
commit
1eca568be5
@ -19,7 +19,6 @@ serde_crate = { package = "serde", version = "1", optional = true, features = ["
|
||||
# Use hashbrown as a feature flag to have HashSet and HashMap from it.
|
||||
hashbrown = { version = "0.9.1", optional = true, features = ["serde"] }
|
||||
miniscript = { version = "12.0.0", optional = true, default-features = false }
|
||||
async-trait = { version = "0.1.80", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8"
|
||||
@ -29,4 +28,3 @@ proptest = "1.2.0"
|
||||
default = ["std", "miniscript"]
|
||||
std = ["bitcoin/std", "miniscript?/std"]
|
||||
serde = ["serde_crate", "bitcoin/serde", "miniscript?/serde"]
|
||||
async = ["async-trait"]
|
||||
|
95
crates/chain/src/changeset.rs
Normal file
95
crates/chain/src/changeset.rs
Normal file
@ -0,0 +1,95 @@
|
||||
//! This module is home to the [`PersistBackend`] trait which defines the behavior of a data store
|
||||
//! required to persist changes made to BDK data structures.
|
||||
//!
|
||||
//! The [`CombinedChangeSet`] type encapsulates a combination of [`crate`] structures that are
|
||||
//! typically persisted together.
|
||||
|
||||
/// A changeset containing [`crate`] structures typically persisted together.
|
||||
#[cfg(feature = "miniscript")]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(crate::serde::Deserialize, crate::serde::Serialize),
|
||||
serde(
|
||||
crate = "crate::serde",
|
||||
bound(
|
||||
deserialize = "A: Ord + crate::serde::Deserialize<'de>, K: Ord + crate::serde::Deserialize<'de>",
|
||||
serialize = "A: Ord + crate::serde::Serialize, K: Ord + crate::serde::Serialize",
|
||||
),
|
||||
)
|
||||
)]
|
||||
pub struct CombinedChangeSet<K, A> {
|
||||
/// Changes to the [`LocalChain`](crate::local_chain::LocalChain).
|
||||
pub chain: crate::local_chain::ChangeSet,
|
||||
/// Changes to [`IndexedTxGraph`](crate::indexed_tx_graph::IndexedTxGraph).
|
||||
pub indexed_tx_graph: crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>,
|
||||
/// Stores the network type of the transaction data.
|
||||
pub network: Option<bitcoin::Network>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> core::default::Default for CombinedChangeSet<K, A> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
chain: core::default::Default::default(),
|
||||
indexed_tx_graph: core::default::Default::default(),
|
||||
network: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K: Ord, A: crate::Anchor> crate::Append for CombinedChangeSet<K, A> {
|
||||
fn append(&mut self, other: Self) {
|
||||
crate::Append::append(&mut self.chain, other.chain);
|
||||
crate::Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
|
||||
if other.network.is_some() {
|
||||
debug_assert!(
|
||||
self.network.is_none() || self.network == other.network,
|
||||
"network type must either be just introduced or remain the same"
|
||||
);
|
||||
self.network = other.network;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.chain.is_empty() && self.indexed_tx_graph.is_empty() && self.network.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> From<crate::local_chain::ChangeSet> for CombinedChangeSet<K, A> {
|
||||
fn from(chain: crate::local_chain::ChangeSet) -> Self {
|
||||
Self {
|
||||
chain,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> From<crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>>
|
||||
for CombinedChangeSet<K, A>
|
||||
{
|
||||
fn from(
|
||||
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
indexed_tx_graph,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> From<crate::keychain::ChangeSet<K>> for CombinedChangeSet<K, A> {
|
||||
fn from(indexer: crate::keychain::ChangeSet<K>) -> Self {
|
||||
Self {
|
||||
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet {
|
||||
indexer,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,8 @@ pub use descriptor_ext::{DescriptorExt, DescriptorId};
|
||||
mod spk_iter;
|
||||
#[cfg(feature = "miniscript")]
|
||||
pub use spk_iter::*;
|
||||
pub mod persist;
|
||||
mod changeset;
|
||||
pub use changeset::*;
|
||||
pub mod spk_client;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
|
@ -1,279 +0,0 @@
|
||||
//! This module is home to the [`PersistBackend`] trait which defines the behavior of a data store
|
||||
//! required to persist changes made to BDK data structures.
|
||||
//!
|
||||
//! The [`CombinedChangeSet`] type encapsulates a combination of [`crate`] structures that are
|
||||
//! typically persisted together.
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "async")]
|
||||
use async_trait::async_trait;
|
||||
use core::convert::Infallible;
|
||||
use core::fmt::{Debug, Display};
|
||||
|
||||
use crate::Append;
|
||||
|
||||
/// A changeset containing [`crate`] structures typically persisted together.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg(feature = "miniscript")]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(crate::serde::Deserialize, crate::serde::Serialize),
|
||||
serde(
|
||||
crate = "crate::serde",
|
||||
bound(
|
||||
deserialize = "A: Ord + crate::serde::Deserialize<'de>, K: Ord + crate::serde::Deserialize<'de>",
|
||||
serialize = "A: Ord + crate::serde::Serialize, K: Ord + crate::serde::Serialize",
|
||||
),
|
||||
)
|
||||
)]
|
||||
pub struct CombinedChangeSet<K, A> {
|
||||
/// Changes to the [`LocalChain`](crate::local_chain::LocalChain).
|
||||
pub chain: crate::local_chain::ChangeSet,
|
||||
/// Changes to [`IndexedTxGraph`](crate::indexed_tx_graph::IndexedTxGraph).
|
||||
pub indexed_tx_graph: crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>,
|
||||
/// Stores the network type of the transaction data.
|
||||
pub network: Option<bitcoin::Network>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> core::default::Default for CombinedChangeSet<K, A> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
chain: core::default::Default::default(),
|
||||
indexed_tx_graph: core::default::Default::default(),
|
||||
network: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K: Ord, A: crate::Anchor> crate::Append for CombinedChangeSet<K, A> {
|
||||
fn append(&mut self, other: Self) {
|
||||
crate::Append::append(&mut self.chain, other.chain);
|
||||
crate::Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
|
||||
if other.network.is_some() {
|
||||
debug_assert!(
|
||||
self.network.is_none() || self.network == other.network,
|
||||
"network type must either be just introduced or remain the same"
|
||||
);
|
||||
self.network = other.network;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.chain.is_empty() && self.indexed_tx_graph.is_empty() && self.network.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> From<crate::local_chain::ChangeSet> for CombinedChangeSet<K, A> {
|
||||
fn from(chain: crate::local_chain::ChangeSet) -> Self {
|
||||
Self {
|
||||
chain,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> From<crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>>
|
||||
for CombinedChangeSet<K, A>
|
||||
{
|
||||
fn from(
|
||||
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet<A, crate::keychain::ChangeSet<K>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
indexed_tx_graph,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "miniscript")]
|
||||
impl<K, A> From<crate::keychain::ChangeSet<K>> for CombinedChangeSet<K, A> {
|
||||
fn from(indexer: crate::keychain::ChangeSet<K>) -> Self {
|
||||
Self {
|
||||
indexed_tx_graph: crate::indexed_tx_graph::ChangeSet {
|
||||
indexer,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A persistence backend for writing and loading changesets.
|
||||
///
|
||||
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
|
||||
/// that are to be persisted, or retrieved from persistence.
|
||||
pub trait PersistBackend<C> {
|
||||
/// The error the backend returns when it fails to write.
|
||||
type WriteError: Debug + Display;
|
||||
|
||||
/// The error the backend returns when it fails to load changesets `C`.
|
||||
type LoadError: Debug + Display;
|
||||
|
||||
/// Writes a changeset to the persistence backend.
|
||||
///
|
||||
/// It is up to the backend what it does with this. It could store every changeset in a list or
|
||||
/// it inserts the actual changes into a more structured database. All it needs to guarantee is
|
||||
/// that [`load_from_persistence`] restores a keychain tracker to what it should be if all
|
||||
/// changesets had been applied sequentially.
|
||||
///
|
||||
/// [`load_from_persistence`]: Self::load_changes
|
||||
fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError>;
|
||||
|
||||
/// Return the aggregate changeset `C` from persistence.
|
||||
fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError>;
|
||||
}
|
||||
|
||||
impl<C> PersistBackend<C> for () {
|
||||
type WriteError = Infallible;
|
||||
type LoadError = Infallible;
|
||||
|
||||
fn write_changes(&mut self, _changeset: &C) -> Result<(), Self::WriteError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
/// An async persistence backend for writing and loading changesets.
|
||||
///
|
||||
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
|
||||
/// that are to be persisted, or retrieved from persistence.
|
||||
#[async_trait]
|
||||
pub trait PersistBackendAsync<C> {
|
||||
/// The error the backend returns when it fails to write.
|
||||
type WriteError: Debug + Display;
|
||||
|
||||
/// The error the backend returns when it fails to load changesets `C`.
|
||||
type LoadError: Debug + Display;
|
||||
|
||||
/// Writes a changeset to the persistence backend.
|
||||
///
|
||||
/// It is up to the backend what it does with this. It could store every changeset in a list or
|
||||
/// it inserts the actual changes into a more structured database. All it needs to guarantee is
|
||||
/// that [`load_from_persistence`] restores a keychain tracker to what it should be if all
|
||||
/// changesets had been applied sequentially.
|
||||
///
|
||||
/// [`load_from_persistence`]: Self::load_changes
|
||||
async fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError>;
|
||||
|
||||
/// Return the aggregate changeset `C` from persistence.
|
||||
async fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
impl<C> PersistBackendAsync<C> for () {
|
||||
type WriteError = Infallible;
|
||||
type LoadError = Infallible;
|
||||
|
||||
async fn write_changes(&mut self, _changeset: &C) -> Result<(), Self::WriteError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends a changeset so that it acts as a convenient staging area for any [`PersistBackend`].
|
||||
///
|
||||
/// Not all changes to the in-memory representation needs to be written to disk right away.
|
||||
/// [`Append::append`] can be used to *stage* changes first and then [`StageExt::commit_to`] can be
|
||||
/// used to write changes to disk.
|
||||
pub trait StageExt: Append + Default + Sized {
|
||||
/// Commit the staged changes to the persistence `backend`.
|
||||
///
|
||||
/// Changes that are committed (if any) are returned.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns a backend-defined error if this fails.
|
||||
fn commit_to<B>(&mut self, backend: &mut B) -> Result<Option<Self>, B::WriteError>
|
||||
where
|
||||
B: PersistBackend<Self>,
|
||||
{
|
||||
// do not do anything if changeset is empty
|
||||
if self.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
backend.write_changes(&*self)?;
|
||||
// only clear if changes are written successfully to backend
|
||||
Ok(Some(core::mem::take(self)))
|
||||
}
|
||||
|
||||
/// Stages a new `changeset` and commits it (alongside any other previously staged changes) to
|
||||
/// the persistence `backend`.
|
||||
///
|
||||
/// Convenience method for calling [`Append::append`] and then [`StageExt::commit_to`].
|
||||
fn append_and_commit_to<B>(
|
||||
&mut self,
|
||||
changeset: Self,
|
||||
backend: &mut B,
|
||||
) -> Result<Option<Self>, B::WriteError>
|
||||
where
|
||||
B: PersistBackend<Self>,
|
||||
{
|
||||
Append::append(self, changeset);
|
||||
self.commit_to(backend)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Append + Default> StageExt for C {}
|
||||
|
||||
/// Extends a changeset so that it acts as a convenient staging area for any
|
||||
/// [`PersistBackendAsync`].
|
||||
///
|
||||
/// Not all changes to the in-memory representation needs to be written to disk right away.
|
||||
/// [`Append::append`] can be used to *stage* changes first and then [`StageExtAsync::commit_to`]
|
||||
/// can be used to write changes to disk.
|
||||
#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
pub trait StageExtAsync: Append + Default + Sized + Send + Sync {
|
||||
/// Commit the staged changes to the persistence `backend`.
|
||||
///
|
||||
/// Changes that are committed (if any) are returned.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns a backend-defined error if this fails.
|
||||
async fn commit_to<B>(&mut self, backend: &mut B) -> Result<Option<Self>, B::WriteError>
|
||||
where
|
||||
B: PersistBackendAsync<Self> + Send + Sync,
|
||||
{
|
||||
// do not do anything if changeset is empty
|
||||
if self.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
backend.write_changes(&*self).await?;
|
||||
// only clear if changes are written successfully to backend
|
||||
Ok(Some(core::mem::take(self)))
|
||||
}
|
||||
|
||||
/// Stages a new `changeset` and commits it (alongside any other previously staged changes) to
|
||||
/// the persistence `backend`.
|
||||
///
|
||||
/// Convenience method for calling [`Append::append`] and then [`StageExtAsync::commit_to`].
|
||||
async fn append_and_commit_to<B>(
|
||||
&mut self,
|
||||
changeset: Self,
|
||||
backend: &mut B,
|
||||
) -> Result<Option<Self>, B::WriteError>
|
||||
where
|
||||
B: PersistBackendAsync<Self> + Send + Sync,
|
||||
{
|
||||
Append::append(self, changeset);
|
||||
self.commit_to(backend).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[async_trait]
|
||||
impl<C: Append + Default + Send + Sync> StageExtAsync for C {}
|
@ -1,6 +1,6 @@
|
||||
# BDK File Store
|
||||
|
||||
This is a simple append-only flat file implementation of [`PersistBackend`](bdk_chain::persist::PersistBackend).
|
||||
This is a simple append-only flat file database for persisting [`bdk_chain`] changesets.
|
||||
|
||||
The main structure is [`Store`] which works with any [`bdk_chain`] based changesets to persist data into a flat file.
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::{bincode_options, EntryIter, FileError, IterError};
|
||||
use bdk_chain::persist::PersistBackend;
|
||||
use bdk_chain::Append;
|
||||
use bincode::Options;
|
||||
use std::{
|
||||
@ -21,27 +20,6 @@ where
|
||||
marker: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C> PersistBackend<C> for Store<C>
|
||||
where
|
||||
C: Append
|
||||
+ Debug
|
||||
+ serde::Serialize
|
||||
+ serde::de::DeserializeOwned
|
||||
+ core::marker::Send
|
||||
+ core::marker::Sync,
|
||||
{
|
||||
type WriteError = io::Error;
|
||||
type LoadError = AggregateChangesetsError<C>;
|
||||
|
||||
fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
|
||||
self.append_changeset(changeset)
|
||||
}
|
||||
|
||||
fn load_changes(&mut self) -> Result<Option<C>, Self::LoadError> {
|
||||
self.aggregate_changesets()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> Store<C>
|
||||
where
|
||||
C: Append
|
||||
@ -448,7 +426,7 @@ mod test {
|
||||
acc
|
||||
});
|
||||
// We write after a short read.
|
||||
db.write_changes(&last_changeset)
|
||||
db.append_changeset(&last_changeset)
|
||||
.expect("last write must succeed");
|
||||
Append::append(&mut exp_aggregation, last_changeset.clone());
|
||||
drop(db);
|
||||
|
@ -12,7 +12,7 @@ use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::Error;
|
||||
use bdk_chain::persist::{CombinedChangeSet, PersistBackend};
|
||||
use bdk_chain::CombinedChangeSet;
|
||||
use bdk_chain::{
|
||||
indexed_tx_graph, keychain, local_chain, tx_graph, Anchor, Append, DescriptorExt, DescriptorId,
|
||||
};
|
||||
@ -57,26 +57,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, A> PersistBackend<CombinedChangeSet<K, A>> for Store<K, A>
|
||||
where
|
||||
K: Ord + for<'de> Deserialize<'de> + Serialize + Send,
|
||||
A: Anchor + for<'de> Deserialize<'de> + Serialize + Send,
|
||||
{
|
||||
type WriteError = Error;
|
||||
type LoadError = Error;
|
||||
|
||||
fn write_changes(
|
||||
&mut self,
|
||||
changeset: &CombinedChangeSet<K, A>,
|
||||
) -> Result<(), Self::WriteError> {
|
||||
self.write(changeset)
|
||||
}
|
||||
|
||||
fn load_changes(&mut self) -> Result<Option<CombinedChangeSet<K, A>>, Self::LoadError> {
|
||||
self.read()
|
||||
}
|
||||
}
|
||||
|
||||
/// Network table related functions.
|
||||
impl<K, A> Store<K, A> {
|
||||
/// Insert [`Network`] for which all other tables data is valid.
|
||||
@ -482,13 +462,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions to read and write all [`ChangeSet`] data.
|
||||
/// Functions to read and write all [`CombinedChangeSet`] data.
|
||||
impl<K, A> Store<K, A>
|
||||
where
|
||||
K: Ord + for<'de> Deserialize<'de> + Serialize + Send,
|
||||
A: Anchor + for<'de> Deserialize<'de> + Serialize + Send,
|
||||
{
|
||||
fn write(&mut self, changeset: &CombinedChangeSet<K, A>) -> Result<(), Error> {
|
||||
/// Write the given `changeset` atomically.
|
||||
pub fn write(&mut self, changeset: &CombinedChangeSet<K, A>) -> Result<(), Error> {
|
||||
// no need to write anything if changeset is empty
|
||||
if changeset.is_empty() {
|
||||
return Ok(());
|
||||
@ -513,7 +494,8 @@ where
|
||||
db_transaction.commit().map_err(Error::Sqlite)
|
||||
}
|
||||
|
||||
fn read(&mut self) -> Result<Option<CombinedChangeSet<K, A>>, Error> {
|
||||
/// Read the entire database and return the aggregate [`CombinedChangeSet`].
|
||||
pub fn read(&mut self) -> Result<Option<CombinedChangeSet<K, A>>, Error> {
|
||||
let db_transaction = self.db_transaction()?;
|
||||
|
||||
let network = Self::select_network(&db_transaction)?;
|
||||
@ -563,7 +545,7 @@ mod test {
|
||||
use bdk_chain::bitcoin::Network::Testnet;
|
||||
use bdk_chain::bitcoin::{secp256k1, BlockHash, OutPoint};
|
||||
use bdk_chain::miniscript::Descriptor;
|
||||
use bdk_chain::persist::{CombinedChangeSet, PersistBackend};
|
||||
use bdk_chain::CombinedChangeSet;
|
||||
use bdk_chain::{
|
||||
indexed_tx_graph, keychain, tx_graph, BlockId, ConfirmationHeightAnchor,
|
||||
ConfirmationTimeHeightAnchor, DescriptorExt,
|
||||
@ -591,10 +573,10 @@ mod test {
|
||||
.expect("create new memory db store");
|
||||
|
||||
test_changesets.iter().for_each(|changeset| {
|
||||
store.write_changes(changeset).expect("write changeset");
|
||||
store.write(changeset).expect("write changeset");
|
||||
});
|
||||
|
||||
let agg_changeset = store.load_changes().expect("aggregated changeset");
|
||||
let agg_changeset = store.read().expect("aggregated changeset");
|
||||
|
||||
assert_eq!(agg_changeset, Some(agg_test_changesets));
|
||||
}
|
||||
@ -612,10 +594,10 @@ mod test {
|
||||
.expect("create new memory db store");
|
||||
|
||||
test_changesets.iter().for_each(|changeset| {
|
||||
store.write_changes(changeset).expect("write changeset");
|
||||
store.write(changeset).expect("write changeset");
|
||||
});
|
||||
|
||||
let agg_changeset = store.load_changes().expect("aggregated changeset");
|
||||
let agg_changeset = store.read().expect("aggregated changeset");
|
||||
|
||||
assert_eq!(agg_changeset, Some(agg_test_changesets));
|
||||
}
|
||||
@ -629,10 +611,10 @@ mod test {
|
||||
let mut store = Store::<Keychain, BlockId>::new(conn).expect("create new memory db store");
|
||||
|
||||
test_changesets.iter().for_each(|changeset| {
|
||||
store.write_changes(changeset).expect("write changeset");
|
||||
store.write(changeset).expect("write changeset");
|
||||
});
|
||||
|
||||
let agg_changeset = store.load_changes().expect("aggregated changeset");
|
||||
let agg_changeset = store.read().expect("aggregated changeset");
|
||||
|
||||
assert_eq!(agg_changeset, Some(agg_test_changesets));
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ use bdk_bitcoind_rpc::{
|
||||
bitcoincore_rpc::{Auth, Client, RpcApi},
|
||||
Emitter,
|
||||
};
|
||||
use bdk_chain::persist::{PersistBackend, StageExt};
|
||||
use bdk_chain::{
|
||||
bitcoin::{constants::genesis_block, Block, Transaction},
|
||||
indexed_tx_graph, keychain,
|
||||
@ -138,7 +137,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let genesis_hash = genesis_block(args.network).block_hash();
|
||||
let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
|
||||
let mut db = db.lock().unwrap();
|
||||
db.write_changes(&(chain_changeset, Default::default()))?;
|
||||
db.append_changeset(&(chain_changeset, Default::default()))?;
|
||||
chain
|
||||
} else {
|
||||
LocalChain::from_changeset(init_chain_changeset)?
|
||||
@ -197,7 +196,9 @@ fn main() -> anyhow::Result<()> {
|
||||
if last_db_commit.elapsed() >= DB_COMMIT_DELAY {
|
||||
let db = &mut *db.lock().unwrap();
|
||||
last_db_commit = Instant::now();
|
||||
db_stage.commit_to(db)?;
|
||||
if !db_stage.is_empty() {
|
||||
db.append_changeset(&core::mem::take(&mut db_stage))?;
|
||||
}
|
||||
println!(
|
||||
"[{:>10}s] committed to db (took {}s)",
|
||||
start.elapsed().as_secs_f32(),
|
||||
@ -233,10 +234,10 @@ fn main() -> anyhow::Result<()> {
|
||||
);
|
||||
{
|
||||
let db = &mut *db.lock().unwrap();
|
||||
db_stage.append_and_commit_to(
|
||||
(local_chain::ChangeSet::default(), graph_changeset),
|
||||
db,
|
||||
)?;
|
||||
db_stage.append((local_chain::ChangeSet::default(), graph_changeset));
|
||||
if !db_stage.is_empty() {
|
||||
db.append_changeset(&core::mem::take(&mut db_stage))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
RpcCommands::Live { rpc_args } => {
|
||||
@ -324,7 +325,9 @@ fn main() -> anyhow::Result<()> {
|
||||
if last_db_commit.elapsed() >= DB_COMMIT_DELAY {
|
||||
let db = &mut *db.lock().unwrap();
|
||||
last_db_commit = Instant::now();
|
||||
db_stage.commit_to(db)?;
|
||||
if !db_stage.is_empty() {
|
||||
db.append_changeset(&core::mem::take(&mut db_stage))?;
|
||||
}
|
||||
println!(
|
||||
"[{:>10}s] committed to db (took {}s)",
|
||||
start.elapsed().as_secs_f32(),
|
||||
|
@ -25,7 +25,6 @@ use bdk_chain::{
|
||||
pub use bdk_file_store;
|
||||
pub use clap;
|
||||
|
||||
use bdk_chain::persist::PersistBackend;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
pub type KeychainTxGraph<A> = IndexedTxGraph<A, KeychainTxOutIndex<Keychain>>;
|
||||
@ -482,7 +481,7 @@ where
|
||||
let ((spk_i, spk), index_changeset) =
|
||||
spk_chooser(index, &Keychain::External).expect("Must exist");
|
||||
let db = &mut *db.lock().unwrap();
|
||||
db.write_changes(&C::from((
|
||||
db.append_changeset(&C::from((
|
||||
local_chain::ChangeSet::default(),
|
||||
indexed_tx_graph::ChangeSet::from(index_changeset),
|
||||
)))?;
|
||||
@ -630,7 +629,7 @@ where
|
||||
// If we're unable to persist this, then we don't want to broadcast.
|
||||
{
|
||||
let db = &mut *db.lock().unwrap();
|
||||
db.write_changes(&C::from((
|
||||
db.append_changeset(&C::from((
|
||||
local_chain::ChangeSet::default(),
|
||||
indexed_tx_graph::ChangeSet::from(index_changeset),
|
||||
)))?;
|
||||
@ -655,7 +654,7 @@ where
|
||||
// We know the tx is at least unconfirmed now. Note if persisting here fails,
|
||||
// it's not a big deal since we can always find it again form
|
||||
// blockchain.
|
||||
db.lock().unwrap().write_changes(&C::from((
|
||||
db.lock().unwrap().append_changeset(&C::from((
|
||||
local_chain::ChangeSet::default(),
|
||||
keychain_changeset,
|
||||
)))?;
|
||||
@ -736,7 +735,7 @@ where
|
||||
Err(err) => return Err(anyhow::anyhow!("failed to init db backend: {:?}", err)),
|
||||
};
|
||||
|
||||
let init_changeset = db_backend.load_changes()?.unwrap_or_default();
|
||||
let init_changeset = db_backend.aggregate_changesets()?.unwrap_or_default();
|
||||
|
||||
Ok(Init {
|
||||
args,
|
||||
|
@ -3,7 +3,6 @@ use std::{
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use bdk_chain::persist::PersistBackend;
|
||||
use bdk_chain::{
|
||||
bitcoin::{constants::genesis_block, Address, Network, Txid},
|
||||
collections::BTreeSet,
|
||||
@ -352,6 +351,6 @@ fn main() -> anyhow::Result<()> {
|
||||
};
|
||||
|
||||
let mut db = db.lock().unwrap();
|
||||
db.write_changes(&db_changeset)?;
|
||||
db.append_changeset(&db_changeset)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use std::{
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use bdk_chain::persist::PersistBackend;
|
||||
use bdk_chain::{
|
||||
bitcoin::{constants::genesis_block, Address, Network, Txid},
|
||||
indexed_tx_graph::{self, IndexedTxGraph},
|
||||
@ -362,6 +361,6 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
// We persist the changes
|
||||
let mut db = db.lock().unwrap();
|
||||
db.write_changes(&(local_chain_changeset, indexed_tx_graph_changeset))?;
|
||||
db.append_changeset(&(local_chain_changeset, indexed_tx_graph_changeset))?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use bdk_electrum::BdkElectrumClient;
|
||||
use bdk_file_store::Store;
|
||||
use bdk_wallet::bitcoin::{Address, Amount};
|
||||
use bdk_wallet::chain::collections::HashSet;
|
||||
use bdk_wallet::chain::persist::PersistBackend;
|
||||
use bdk_wallet::{bitcoin::Network, Wallet};
|
||||
use bdk_wallet::{KeychainKind, SignOptions};
|
||||
|
||||
@ -23,7 +22,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
|
||||
let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
|
||||
let changeset = db
|
||||
.load_changes()
|
||||
.aggregate_changesets()
|
||||
.map_err(|e| anyhow!("load changes error: {}", e))?;
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
external_descriptor,
|
||||
@ -33,7 +32,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
)?;
|
||||
|
||||
let address = wallet.next_unused_address(KeychainKind::External);
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.append_changeset(&changeset)?;
|
||||
}
|
||||
println!("Generated Address: {}", address);
|
||||
|
||||
let balance = wallet.balance();
|
||||
@ -72,7 +73,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
println!();
|
||||
|
||||
wallet.apply_update(update)?;
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.append_changeset(&changeset)?;
|
||||
}
|
||||
|
||||
let balance = wallet.balance();
|
||||
println!("Wallet balance after syncing: {} sats", balance.total());
|
||||
|
@ -7,7 +7,6 @@ use bdk_wallet::{
|
||||
};
|
||||
|
||||
use bdk_sqlite::{rusqlite::Connection, Store};
|
||||
use bdk_wallet::chain::persist::PersistBackend;
|
||||
|
||||
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
|
||||
const STOP_GAP: usize = 50;
|
||||
@ -20,7 +19,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
let mut db = Store::new(conn)?;
|
||||
let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
|
||||
let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
|
||||
let changeset = db.load_changes()?;
|
||||
let changeset = db.read()?;
|
||||
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
external_descriptor,
|
||||
@ -30,7 +29,9 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
)?;
|
||||
|
||||
let address = wallet.next_unused_address(KeychainKind::External);
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.write(&changeset)?;
|
||||
}
|
||||
println!("Generated Address: {}", address);
|
||||
|
||||
let balance = wallet.balance();
|
||||
@ -78,7 +79,9 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
let _ = update.graph_update.update_last_seen_unconfirmed(now);
|
||||
|
||||
wallet.apply_update(update)?;
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.write(&changeset)?;
|
||||
}
|
||||
println!();
|
||||
|
||||
let balance = wallet.balance();
|
||||
|
@ -7,7 +7,6 @@ use std::{collections::BTreeSet, io::Write, str::FromStr};
|
||||
|
||||
use bdk_esplora::{esplora_client, EsploraExt};
|
||||
use bdk_file_store::Store;
|
||||
use bdk_wallet::chain::persist::PersistBackend;
|
||||
use bdk_wallet::{
|
||||
bitcoin::{Address, Amount, Network},
|
||||
KeychainKind, SignOptions, Wallet,
|
||||
@ -19,7 +18,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
Store::<bdk_wallet::wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
|
||||
let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
|
||||
let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
|
||||
let changeset = db.load_changes()?;
|
||||
let changeset = db.aggregate_changesets()?;
|
||||
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
external_descriptor,
|
||||
@ -29,7 +28,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
)?;
|
||||
|
||||
let address = wallet.next_unused_address(KeychainKind::External);
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.append_changeset(&changeset)?;
|
||||
}
|
||||
println!("Generated Address: {}", address);
|
||||
|
||||
let balance = wallet.balance();
|
||||
@ -55,7 +56,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let _ = update.graph_update.update_last_seen_unconfirmed(now);
|
||||
|
||||
wallet.apply_update(update)?;
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.append_changeset(&changeset)?;
|
||||
}
|
||||
println!();
|
||||
|
||||
let balance = wallet.balance();
|
||||
|
@ -3,7 +3,6 @@ use bdk_bitcoind_rpc::{
|
||||
Emitter,
|
||||
};
|
||||
use bdk_file_store::Store;
|
||||
use bdk_wallet::chain::persist::PersistBackend;
|
||||
use bdk_wallet::{
|
||||
bitcoin::{Block, Network, Transaction},
|
||||
wallet::Wallet,
|
||||
@ -91,7 +90,7 @@ fn main() -> anyhow::Result<()> {
|
||||
DB_MAGIC.as_bytes(),
|
||||
args.db_path,
|
||||
)?;
|
||||
let changeset = db.load_changes()?;
|
||||
let changeset = db.aggregate_changesets()?;
|
||||
|
||||
let mut wallet = Wallet::new_or_load(
|
||||
&args.descriptor,
|
||||
@ -147,7 +146,9 @@ fn main() -> anyhow::Result<()> {
|
||||
let connected_to = block_emission.connected_to();
|
||||
let start_apply_block = Instant::now();
|
||||
wallet.apply_block_connected_to(&block_emission.block, height, connected_to)?;
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.append_changeset(&changeset)?;
|
||||
}
|
||||
let elapsed = start_apply_block.elapsed().as_secs_f32();
|
||||
println!(
|
||||
"Applied block {} at height {} in {}s",
|
||||
@ -157,7 +158,9 @@ fn main() -> anyhow::Result<()> {
|
||||
Emission::Mempool(mempool_emission) => {
|
||||
let start_apply_mempool = Instant::now();
|
||||
wallet.apply_unconfirmed_txs(mempool_emission.iter().map(|(tx, time)| (tx, *time)));
|
||||
wallet.commit_to(&mut db)?;
|
||||
if let Some(changeset) = wallet.take_staged() {
|
||||
db.append_changeset(&changeset)?;
|
||||
}
|
||||
println!(
|
||||
"Applied unconfirmed transactions in {}s",
|
||||
start_apply_mempool.elapsed().as_secs_f32()
|
||||
|
Loading…
x
Reference in New Issue
Block a user