Merge bitcoindevkit/bdk#1387: fix(wallet): remove the generic from wallet
e51af49ffa951e14203ac2b465ade351dd90f6cd fix(wallet): remove generic from wallet (Rob N) Pull request description: ### Description The `PersistenceBackend` uses generics to describe errors returned while applying the change set to the persistence layer. This change removes generics wherever possible and introduces a new public error enum. Removing the generics from `PersistenceBackend` errors is the first step towards #1363 *Update*: I proceeded with removing the generics from `Wallet` by introducing a `Box<dyn PersistenceBackend>` . ### Notes to the reviewers This one sort of blew up in the number of changes due to the use of generics for most of the `Wallet` error variants. The generics were only used for the persistence errors, so I removed the generics from higher level errors whenever possible. The error variants of `PersistenceBackend` may also be more expressive, but I will level that up for discussion and make any changes required. ### Changelog notice - Changed `PersistenceBackend` errors to depend on the `anyhow` crate. - Remove the generic `T` from `Wallet` ### 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 #### New Features: * [ ] I've added tests for the new feature * [x] I've added docs for the new feature #### Bugfixes: * [x] This pull request breaks the existing API * [ ] I've added tests to reproduce the issue which are now passing * [x] I'm linking the issue being fixed by this PR ACKs for top commit: evanlinjin: ACK e51af49ffa951e14203ac2b465ade351dd90f6cd Tree-SHA512: 8ce4f1c495310e16145555f4a6a29a0f42cf8944eda68004595c3532580767f64f779185022147a00d75001c40d69fdf8f8de2d348eb68484b170d2a181117ff
This commit is contained in:
commit
f00de9e0c1
@ -13,6 +13,7 @@ edition = "2021"
|
|||||||
rust-version = "1.63"
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = { version = "1", default-features = false }
|
||||||
rand = "^0.8"
|
rand = "^0.8"
|
||||||
miniscript = { version = "11.0.0", features = ["serde"], default-features = false }
|
miniscript = { version = "11.0.0", features = ["serde"], default-features = false }
|
||||||
bitcoin = { version = "0.31.0", features = ["serde", "base64", "rand-std"], default-features = false }
|
bitcoin = { version = "0.31.0", features = ["serde", "base64", "rand-std"], default-features = false }
|
||||||
|
@ -47,11 +47,11 @@ impl std::error::Error for MiniscriptPsbtError {}
|
|||||||
/// Error returned from [`TxBuilder::finish`]
|
/// Error returned from [`TxBuilder::finish`]
|
||||||
///
|
///
|
||||||
/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
|
/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
|
||||||
pub enum CreateTxError<P> {
|
pub enum CreateTxError {
|
||||||
/// There was a problem with the descriptors passed in
|
/// There was a problem with the descriptors passed in
|
||||||
Descriptor(DescriptorError),
|
Descriptor(DescriptorError),
|
||||||
/// We were unable to write wallet data to the persistence backend
|
/// We were unable to load wallet data from or write wallet data to the persistence backend
|
||||||
Persist(P),
|
Persist(anyhow::Error),
|
||||||
/// There was a problem while extracting and manipulating policies
|
/// There was a problem while extracting and manipulating policies
|
||||||
Policy(PolicyError),
|
Policy(PolicyError),
|
||||||
/// Spending policy is not compatible with this [`KeychainKind`]
|
/// Spending policy is not compatible with this [`KeychainKind`]
|
||||||
@ -119,17 +119,14 @@ pub enum CreateTxError<P> {
|
|||||||
MiniscriptPsbt(MiniscriptPsbtError),
|
MiniscriptPsbt(MiniscriptPsbtError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> fmt::Display for CreateTxError<P>
|
impl fmt::Display for CreateTxError {
|
||||||
where
|
|
||||||
P: fmt::Display,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Descriptor(e) => e.fmt(f),
|
Self::Descriptor(e) => e.fmt(f),
|
||||||
Self::Persist(e) => {
|
Self::Persist(e) => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"failed to write wallet data to persistence backend: {}",
|
"failed to load wallet data from or write wallet data to persistence backend: {}",
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -214,38 +211,38 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> From<descriptor::error::Error> for CreateTxError<P> {
|
impl From<descriptor::error::Error> for CreateTxError {
|
||||||
fn from(err: descriptor::error::Error) -> Self {
|
fn from(err: descriptor::error::Error) -> Self {
|
||||||
CreateTxError::Descriptor(err)
|
CreateTxError::Descriptor(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> From<PolicyError> for CreateTxError<P> {
|
impl From<PolicyError> for CreateTxError {
|
||||||
fn from(err: PolicyError) -> Self {
|
fn from(err: PolicyError) -> Self {
|
||||||
CreateTxError::Policy(err)
|
CreateTxError::Policy(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> From<MiniscriptPsbtError> for CreateTxError<P> {
|
impl From<MiniscriptPsbtError> for CreateTxError {
|
||||||
fn from(err: MiniscriptPsbtError) -> Self {
|
fn from(err: MiniscriptPsbtError) -> Self {
|
||||||
CreateTxError::MiniscriptPsbt(err)
|
CreateTxError::MiniscriptPsbt(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> From<psbt::Error> for CreateTxError<P> {
|
impl From<psbt::Error> for CreateTxError {
|
||||||
fn from(err: psbt::Error) -> Self {
|
fn from(err: psbt::Error) -> Self {
|
||||||
CreateTxError::Psbt(err)
|
CreateTxError::Psbt(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> From<coin_selection::Error> for CreateTxError<P> {
|
impl From<coin_selection::Error> for CreateTxError {
|
||||||
fn from(err: coin_selection::Error) -> Self {
|
fn from(err: coin_selection::Error) -> Self {
|
||||||
CreateTxError::CoinSelection(err)
|
CreateTxError::CoinSelection(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl<P: core::fmt::Display + core::fmt::Debug> std::error::Error for CreateTxError<P> {}
|
impl std::error::Error for CreateTxError {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Error returned from [`Wallet::build_fee_bump`]
|
/// Error returned from [`Wallet::build_fee_bump`]
|
||||||
|
@ -53,9 +53,8 @@
|
|||||||
//! # Ok::<_, Box<dyn std::error::Error>>(())
|
//! # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use core::str::FromStr;
|
|
||||||
|
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
|
use core::str::FromStr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use miniscript::descriptor::{ShInner, WshInner};
|
use miniscript::descriptor::{ShInner, WshInner};
|
||||||
@ -110,8 +109,8 @@ impl FullyNodedExport {
|
|||||||
///
|
///
|
||||||
/// If the database is empty or `include_blockheight` is false, the `blockheight` field
|
/// If the database is empty or `include_blockheight` is false, the `blockheight` field
|
||||||
/// returned will be `0`.
|
/// returned will be `0`.
|
||||||
pub fn export_wallet<D>(
|
pub fn export_wallet(
|
||||||
wallet: &Wallet<D>,
|
wallet: &Wallet,
|
||||||
label: &str,
|
label: &str,
|
||||||
include_blockheight: bool,
|
include_blockheight: bool,
|
||||||
) -> Result<Self, &'static str> {
|
) -> Result<Self, &'static str> {
|
||||||
@ -225,7 +224,7 @@ mod test {
|
|||||||
descriptor: &str,
|
descriptor: &str,
|
||||||
change_descriptor: Option<&str>,
|
change_descriptor: Option<&str>,
|
||||||
network: Network,
|
network: Network,
|
||||||
) -> Wallet<()> {
|
) -> Wallet {
|
||||||
let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, network).unwrap();
|
let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, network).unwrap();
|
||||||
let transaction = Transaction {
|
let transaction = Transaction {
|
||||||
input: vec![],
|
input: vec![],
|
||||||
|
@ -82,12 +82,12 @@ const COINBASE_MATURITY: u32 = 100;
|
|||||||
///
|
///
|
||||||
/// [`signer`]: crate::signer
|
/// [`signer`]: crate::signer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Wallet<D = ()> {
|
pub struct Wallet {
|
||||||
signers: Arc<SignersContainer>,
|
signers: Arc<SignersContainer>,
|
||||||
change_signers: Arc<SignersContainer>,
|
change_signers: Arc<SignersContainer>,
|
||||||
chain: LocalChain,
|
chain: LocalChain,
|
||||||
indexed_graph: IndexedTxGraph<ConfirmationTimeHeightAnchor, KeychainTxOutIndex<KeychainKind>>,
|
indexed_graph: IndexedTxGraph<ConfirmationTimeHeightAnchor, KeychainTxOutIndex<KeychainKind>>,
|
||||||
persist: Persist<D, ChangeSet>,
|
persist: Persist<ChangeSet>,
|
||||||
network: Network,
|
network: Network,
|
||||||
secp: SecpCtx,
|
secp: SecpCtx,
|
||||||
}
|
}
|
||||||
@ -236,7 +236,7 @@ impl Wallet {
|
|||||||
Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
|
Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
|
||||||
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
|
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
|
||||||
NewError::Descriptor(e) => e,
|
NewError::Descriptor(e) => e,
|
||||||
NewError::Write(_) => unreachable!("mock-write must always succeed"),
|
NewError::Persist(_) => unreachable!("mock-write must always succeed"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,15 +251,12 @@ impl Wallet {
|
|||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
|
NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
|
||||||
NewError::Descriptor(e) => e,
|
NewError::Descriptor(e) => e,
|
||||||
NewError::Write(_) => unreachable!("mock-write must always succeed"),
|
NewError::Persist(_) => unreachable!("mock-write must always succeed"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> Wallet<D>
|
impl Wallet {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet, WriteError = core::convert::Infallible>,
|
|
||||||
{
|
|
||||||
/// Infallibly return a derived address using the external descriptor, see [`AddressIndex`] for
|
/// Infallibly return a derived address using the external descriptor, see [`AddressIndex`] for
|
||||||
/// available address index selection strategies. If none of the keys in the descriptor are derivable
|
/// available address index selection strategies. If none of the keys in the descriptor are derivable
|
||||||
/// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
|
/// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
|
||||||
@ -296,19 +293,16 @@ where
|
|||||||
/// [`new`]: Wallet::new
|
/// [`new`]: Wallet::new
|
||||||
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
|
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NewError<W> {
|
pub enum NewError {
|
||||||
/// Database already has data.
|
/// Database already has data.
|
||||||
NonEmptyDatabase,
|
NonEmptyDatabase,
|
||||||
/// There was problem with the passed-in descriptor(s).
|
/// There was problem with the passed-in descriptor(s).
|
||||||
Descriptor(crate::descriptor::DescriptorError),
|
Descriptor(crate::descriptor::DescriptorError),
|
||||||
/// We were unable to write the wallet's data to the persistence backend.
|
/// We were unable to write the wallet's data to the persistence backend.
|
||||||
Write(W),
|
Persist(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> fmt::Display for NewError<W>
|
impl fmt::Display for NewError {
|
||||||
where
|
|
||||||
W: fmt::Display,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
NewError::NonEmptyDatabase => write!(
|
NewError::NonEmptyDatabase => write!(
|
||||||
@ -316,13 +310,13 @@ where
|
|||||||
"database already has data - use `load` or `new_or_load` methods instead"
|
"database already has data - use `load` or `new_or_load` methods instead"
|
||||||
),
|
),
|
||||||
NewError::Descriptor(e) => e.fmt(f),
|
NewError::Descriptor(e) => e.fmt(f),
|
||||||
NewError::Write(e) => e.fmt(f),
|
NewError::Persist(e) => e.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl<W> std::error::Error for NewError<W> where W: core::fmt::Display + core::fmt::Debug {}
|
impl std::error::Error for NewError {}
|
||||||
|
|
||||||
/// The error type when loading a [`Wallet`] from persistence.
|
/// The error type when loading a [`Wallet`] from persistence.
|
||||||
///
|
///
|
||||||
@ -330,11 +324,11 @@ impl<W> std::error::Error for NewError<W> where W: core::fmt::Display + core::fm
|
|||||||
///
|
///
|
||||||
/// [`load`]: Wallet::load
|
/// [`load`]: Wallet::load
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LoadError<L> {
|
pub enum LoadError {
|
||||||
/// There was a problem with the passed-in descriptor(s).
|
/// There was a problem with the passed-in descriptor(s).
|
||||||
Descriptor(crate::descriptor::DescriptorError),
|
Descriptor(crate::descriptor::DescriptorError),
|
||||||
/// Loading data from the persistence backend failed.
|
/// Loading data from the persistence backend failed.
|
||||||
Load(L),
|
Persist(anyhow::Error),
|
||||||
/// Wallet not initialized, persistence backend is empty.
|
/// Wallet not initialized, persistence backend is empty.
|
||||||
NotInitialized,
|
NotInitialized,
|
||||||
/// Data loaded from persistence is missing network type.
|
/// Data loaded from persistence is missing network type.
|
||||||
@ -343,14 +337,11 @@ pub enum LoadError<L> {
|
|||||||
MissingGenesis,
|
MissingGenesis,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L> fmt::Display for LoadError<L>
|
impl fmt::Display for LoadError {
|
||||||
where
|
|
||||||
L: fmt::Display,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
LoadError::Descriptor(e) => e.fmt(f),
|
LoadError::Descriptor(e) => e.fmt(f),
|
||||||
LoadError::Load(e) => e.fmt(f),
|
LoadError::Persist(e) => e.fmt(f),
|
||||||
LoadError::NotInitialized => {
|
LoadError::NotInitialized => {
|
||||||
write!(f, "wallet is not initialized, persistence backend is empty")
|
write!(f, "wallet is not initialized, persistence backend is empty")
|
||||||
}
|
}
|
||||||
@ -361,7 +352,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl<L> std::error::Error for LoadError<L> where L: core::fmt::Display + core::fmt::Debug {}
|
impl std::error::Error for LoadError {}
|
||||||
|
|
||||||
/// Error type for when we try load a [`Wallet`] from persistence and creating it if non-existent.
|
/// Error type for when we try load a [`Wallet`] from persistence and creating it if non-existent.
|
||||||
///
|
///
|
||||||
@ -370,13 +361,11 @@ impl<L> std::error::Error for LoadError<L> where L: core::fmt::Display + core::f
|
|||||||
/// [`new_or_load`]: Wallet::new_or_load
|
/// [`new_or_load`]: Wallet::new_or_load
|
||||||
/// [`new_or_load_with_genesis_hash`]: Wallet::new_or_load_with_genesis_hash
|
/// [`new_or_load_with_genesis_hash`]: Wallet::new_or_load_with_genesis_hash
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NewOrLoadError<W, L> {
|
pub enum NewOrLoadError {
|
||||||
/// There is a problem with the passed-in descriptor.
|
/// There is a problem with the passed-in descriptor.
|
||||||
Descriptor(crate::descriptor::DescriptorError),
|
Descriptor(crate::descriptor::DescriptorError),
|
||||||
/// Writing to the persistence backend failed.
|
/// Either writing to or loading from the persistence backend failed.
|
||||||
Write(W),
|
Persist(anyhow::Error),
|
||||||
/// Loading from the persistence backend failed.
|
|
||||||
Load(L),
|
|
||||||
/// Wallet is not initialized, persistence backend is empty.
|
/// Wallet is not initialized, persistence backend is empty.
|
||||||
NotInitialized,
|
NotInitialized,
|
||||||
/// The loaded genesis hash does not match what was provided.
|
/// The loaded genesis hash does not match what was provided.
|
||||||
@ -395,16 +384,15 @@ pub enum NewOrLoadError<W, L> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W, L> fmt::Display for NewOrLoadError<W, L>
|
impl fmt::Display for NewOrLoadError {
|
||||||
where
|
|
||||||
W: fmt::Display,
|
|
||||||
L: fmt::Display,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
NewOrLoadError::Descriptor(e) => e.fmt(f),
|
NewOrLoadError::Descriptor(e) => e.fmt(f),
|
||||||
NewOrLoadError::Write(e) => write!(f, "failed to write to persistence: {}", e),
|
NewOrLoadError::Persist(e) => write!(
|
||||||
NewOrLoadError::Load(e) => write!(f, "failed to load from persistence: {}", e),
|
f,
|
||||||
|
"failed to either write to or load from persistence, {}",
|
||||||
|
e
|
||||||
|
),
|
||||||
NewOrLoadError::NotInitialized => {
|
NewOrLoadError::NotInitialized => {
|
||||||
write!(f, "wallet is not initialized, persistence backend is empty")
|
write!(f, "wallet is not initialized, persistence backend is empty")
|
||||||
}
|
}
|
||||||
@ -419,12 +407,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl<W, L> std::error::Error for NewOrLoadError<W, L>
|
impl std::error::Error for NewOrLoadError {}
|
||||||
where
|
|
||||||
W: core::fmt::Display + core::fmt::Debug,
|
|
||||||
L: core::fmt::Display + core::fmt::Debug,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error that may occur when inserting a transaction into [`Wallet`].
|
/// An error that may occur when inserting a transaction into [`Wallet`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -488,17 +471,14 @@ impl fmt::Display for ApplyBlockError {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl std::error::Error for ApplyBlockError {}
|
impl std::error::Error for ApplyBlockError {}
|
||||||
|
|
||||||
impl<D> Wallet<D> {
|
impl Wallet {
|
||||||
/// Initialize an empty [`Wallet`].
|
/// Initialize an empty [`Wallet`].
|
||||||
pub fn new<E: IntoWalletDescriptor>(
|
pub fn new<E: IntoWalletDescriptor>(
|
||||||
descriptor: E,
|
descriptor: E,
|
||||||
change_descriptor: Option<E>,
|
change_descriptor: Option<E>,
|
||||||
db: D,
|
db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||||
network: Network,
|
network: Network,
|
||||||
) -> Result<Self, NewError<D::WriteError>>
|
) -> Result<Self, NewError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let genesis_hash = genesis_block(network).block_hash();
|
let genesis_hash = genesis_block(network).block_hash();
|
||||||
Self::new_with_genesis_hash(descriptor, change_descriptor, db, network, genesis_hash)
|
Self::new_with_genesis_hash(descriptor, change_descriptor, db, network, genesis_hash)
|
||||||
}
|
}
|
||||||
@ -510,13 +490,10 @@ impl<D> Wallet<D> {
|
|||||||
pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
|
pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
|
||||||
descriptor: E,
|
descriptor: E,
|
||||||
change_descriptor: Option<E>,
|
change_descriptor: Option<E>,
|
||||||
mut db: D,
|
mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||||
network: Network,
|
network: Network,
|
||||||
genesis_hash: BlockHash,
|
genesis_hash: BlockHash,
|
||||||
) -> Result<Self, NewError<D::WriteError>>
|
) -> Result<Self, NewError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
if let Ok(changeset) = db.load_from_persistence() {
|
if let Ok(changeset) = db.load_from_persistence() {
|
||||||
if changeset.is_some() {
|
if changeset.is_some() {
|
||||||
return Err(NewError::NonEmptyDatabase);
|
return Err(NewError::NonEmptyDatabase);
|
||||||
@ -538,7 +515,7 @@ impl<D> Wallet<D> {
|
|||||||
indexed_tx_graph: indexed_graph.initial_changeset(),
|
indexed_tx_graph: indexed_graph.initial_changeset(),
|
||||||
network: Some(network),
|
network: Some(network),
|
||||||
});
|
});
|
||||||
persist.commit().map_err(NewError::Write)?;
|
persist.commit().map_err(NewError::Persist)?;
|
||||||
|
|
||||||
Ok(Wallet {
|
Ok(Wallet {
|
||||||
signers,
|
signers,
|
||||||
@ -555,14 +532,11 @@ impl<D> Wallet<D> {
|
|||||||
pub fn load<E: IntoWalletDescriptor>(
|
pub fn load<E: IntoWalletDescriptor>(
|
||||||
descriptor: E,
|
descriptor: E,
|
||||||
change_descriptor: Option<E>,
|
change_descriptor: Option<E>,
|
||||||
mut db: D,
|
mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||||
) -> Result<Self, LoadError<D::LoadError>>
|
) -> Result<Self, LoadError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let changeset = db
|
let changeset = db
|
||||||
.load_from_persistence()
|
.load_from_persistence()
|
||||||
.map_err(LoadError::Load)?
|
.map_err(LoadError::Persist)?
|
||||||
.ok_or(LoadError::NotInitialized)?;
|
.ok_or(LoadError::NotInitialized)?;
|
||||||
Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
|
Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
|
||||||
}
|
}
|
||||||
@ -570,12 +544,9 @@ impl<D> Wallet<D> {
|
|||||||
fn load_from_changeset<E: IntoWalletDescriptor>(
|
fn load_from_changeset<E: IntoWalletDescriptor>(
|
||||||
descriptor: E,
|
descriptor: E,
|
||||||
change_descriptor: Option<E>,
|
change_descriptor: Option<E>,
|
||||||
db: D,
|
db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||||
changeset: ChangeSet,
|
changeset: ChangeSet,
|
||||||
) -> Result<Self, LoadError<D::LoadError>>
|
) -> Result<Self, LoadError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let secp = Secp256k1::new();
|
let secp = Secp256k1::new();
|
||||||
let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
|
let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
|
||||||
let chain =
|
let chain =
|
||||||
@ -608,12 +579,9 @@ impl<D> Wallet<D> {
|
|||||||
pub fn new_or_load<E: IntoWalletDescriptor>(
|
pub fn new_or_load<E: IntoWalletDescriptor>(
|
||||||
descriptor: E,
|
descriptor: E,
|
||||||
change_descriptor: Option<E>,
|
change_descriptor: Option<E>,
|
||||||
db: D,
|
db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||||
network: Network,
|
network: Network,
|
||||||
) -> Result<Self, NewOrLoadError<D::WriteError, D::LoadError>>
|
) -> Result<Self, NewOrLoadError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let genesis_hash = genesis_block(network).block_hash();
|
let genesis_hash = genesis_block(network).block_hash();
|
||||||
Self::new_or_load_with_genesis_hash(
|
Self::new_or_load_with_genesis_hash(
|
||||||
descriptor,
|
descriptor,
|
||||||
@ -633,21 +601,20 @@ impl<D> Wallet<D> {
|
|||||||
pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
|
pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
|
||||||
descriptor: E,
|
descriptor: E,
|
||||||
change_descriptor: Option<E>,
|
change_descriptor: Option<E>,
|
||||||
mut db: D,
|
mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
|
||||||
network: Network,
|
network: Network,
|
||||||
genesis_hash: BlockHash,
|
genesis_hash: BlockHash,
|
||||||
) -> Result<Self, NewOrLoadError<D::WriteError, D::LoadError>>
|
) -> Result<Self, NewOrLoadError> {
|
||||||
where
|
let changeset = db
|
||||||
D: PersistBackend<ChangeSet>,
|
.load_from_persistence()
|
||||||
{
|
.map_err(NewOrLoadError::Persist)?;
|
||||||
let changeset = db.load_from_persistence().map_err(NewOrLoadError::Load)?;
|
|
||||||
match changeset {
|
match changeset {
|
||||||
Some(changeset) => {
|
Some(changeset) => {
|
||||||
let wallet =
|
let wallet =
|
||||||
Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
|
Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
|
LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
|
||||||
LoadError::Load(e) => NewOrLoadError::Load(e),
|
LoadError::Persist(e) => NewOrLoadError::Persist(e),
|
||||||
LoadError::NotInitialized => NewOrLoadError::NotInitialized,
|
LoadError::NotInitialized => NewOrLoadError::NotInitialized,
|
||||||
LoadError::MissingNetwork => {
|
LoadError::MissingNetwork => {
|
||||||
NewOrLoadError::LoadedNetworkDoesNotMatch {
|
NewOrLoadError::LoadedNetworkDoesNotMatch {
|
||||||
@ -688,7 +655,7 @@ impl<D> Wallet<D> {
|
|||||||
unreachable!("database is already checked to have no data")
|
unreachable!("database is already checked to have no data")
|
||||||
}
|
}
|
||||||
NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
|
NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
|
||||||
NewError::Write(e) => NewOrLoadError::Write(e),
|
NewError::Persist(e) => NewOrLoadError::Persist(e),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -714,13 +681,7 @@ impl<D> Wallet<D> {
|
|||||||
///
|
///
|
||||||
/// This panics when the caller requests for an address of derivation index greater than the
|
/// This panics when the caller requests for an address of derivation index greater than the
|
||||||
/// BIP32 max index.
|
/// BIP32 max index.
|
||||||
pub fn try_get_address(
|
pub fn try_get_address(&mut self, address_index: AddressIndex) -> anyhow::Result<AddressInfo> {
|
||||||
&mut self,
|
|
||||||
address_index: AddressIndex,
|
|
||||||
) -> Result<AddressInfo, D::WriteError>
|
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
self._get_address(KeychainKind::External, address_index)
|
self._get_address(KeychainKind::External, address_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,10 +703,7 @@ impl<D> Wallet<D> {
|
|||||||
pub fn try_get_internal_address(
|
pub fn try_get_internal_address(
|
||||||
&mut self,
|
&mut self,
|
||||||
address_index: AddressIndex,
|
address_index: AddressIndex,
|
||||||
) -> Result<AddressInfo, D::WriteError>
|
) -> anyhow::Result<AddressInfo> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
self._get_address(KeychainKind::Internal, address_index)
|
self._get_address(KeychainKind::Internal, address_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,10 +728,7 @@ impl<D> Wallet<D> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
keychain: KeychainKind,
|
keychain: KeychainKind,
|
||||||
address_index: AddressIndex,
|
address_index: AddressIndex,
|
||||||
) -> Result<AddressInfo, D::WriteError>
|
) -> anyhow::Result<AddressInfo> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let keychain = self.map_keychain(keychain);
|
let keychain = self.map_keychain(keychain);
|
||||||
let txout_index = &mut self.indexed_graph.index;
|
let txout_index = &mut self.indexed_graph.index;
|
||||||
let (index, spk, changeset) = match address_index {
|
let (index, spk, changeset) = match address_index {
|
||||||
@ -918,10 +873,7 @@ impl<D> Wallet<D> {
|
|||||||
/// [`list_unspent`]: Self::list_unspent
|
/// [`list_unspent`]: Self::list_unspent
|
||||||
/// [`list_output`]: Self::list_output
|
/// [`list_output`]: Self::list_output
|
||||||
/// [`commit`]: Self::commit
|
/// [`commit`]: Self::commit
|
||||||
pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut)
|
pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let additions = self.indexed_graph.insert_txout(outpoint, txout);
|
let additions = self.indexed_graph.insert_txout(outpoint, txout);
|
||||||
self.persist.stage(ChangeSet::from(additions));
|
self.persist.stage(ChangeSet::from(additions));
|
||||||
}
|
}
|
||||||
@ -938,7 +890,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// # use bitcoin::Txid;
|
/// # use bitcoin::Txid;
|
||||||
/// # use bdk::Wallet;
|
/// # use bdk::Wallet;
|
||||||
/// # let mut wallet: Wallet<()> = todo!();
|
/// # let mut wallet: Wallet = todo!();
|
||||||
/// # let txid:Txid = todo!();
|
/// # let txid:Txid = todo!();
|
||||||
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
|
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
|
||||||
/// let fee = wallet.calculate_fee(&tx).expect("fee");
|
/// let fee = wallet.calculate_fee(&tx).expect("fee");
|
||||||
@ -947,7 +899,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// # use bitcoin::Psbt;
|
/// # use bitcoin::Psbt;
|
||||||
/// # use bdk::Wallet;
|
/// # use bdk::Wallet;
|
||||||
/// # let mut wallet: Wallet<()> = todo!();
|
/// # let mut wallet: Wallet = todo!();
|
||||||
/// # let mut psbt: Psbt = todo!();
|
/// # let mut psbt: Psbt = todo!();
|
||||||
/// let tx = &psbt.clone().extract_tx().expect("tx");
|
/// let tx = &psbt.clone().extract_tx().expect("tx");
|
||||||
/// let fee = wallet.calculate_fee(tx).expect("fee");
|
/// let fee = wallet.calculate_fee(tx).expect("fee");
|
||||||
@ -969,7 +921,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// # use bitcoin::Txid;
|
/// # use bitcoin::Txid;
|
||||||
/// # use bdk::Wallet;
|
/// # use bdk::Wallet;
|
||||||
/// # let mut wallet: Wallet<()> = todo!();
|
/// # let mut wallet: Wallet = todo!();
|
||||||
/// # let txid:Txid = todo!();
|
/// # let txid:Txid = todo!();
|
||||||
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
|
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
|
||||||
/// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
|
/// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
|
||||||
@ -978,7 +930,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// # use bitcoin::Psbt;
|
/// # use bitcoin::Psbt;
|
||||||
/// # use bdk::Wallet;
|
/// # use bdk::Wallet;
|
||||||
/// # let mut wallet: Wallet<()> = todo!();
|
/// # let mut wallet: Wallet = todo!();
|
||||||
/// # let mut psbt: Psbt = todo!();
|
/// # let mut psbt: Psbt = todo!();
|
||||||
/// let tx = &psbt.clone().extract_tx().expect("tx");
|
/// let tx = &psbt.clone().extract_tx().expect("tx");
|
||||||
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
|
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
|
||||||
@ -1000,7 +952,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// # use bitcoin::Txid;
|
/// # use bitcoin::Txid;
|
||||||
/// # use bdk::Wallet;
|
/// # use bdk::Wallet;
|
||||||
/// # let mut wallet: Wallet<()> = todo!();
|
/// # let mut wallet: Wallet = todo!();
|
||||||
/// # let txid:Txid = todo!();
|
/// # let txid:Txid = todo!();
|
||||||
/// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
|
/// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
|
||||||
/// let (sent, received) = wallet.sent_and_received(&tx);
|
/// let (sent, received) = wallet.sent_and_received(&tx);
|
||||||
@ -1009,7 +961,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// # use bitcoin::Psbt;
|
/// # use bitcoin::Psbt;
|
||||||
/// # use bdk::Wallet;
|
/// # use bdk::Wallet;
|
||||||
/// # let mut wallet: Wallet<()> = todo!();
|
/// # let mut wallet: Wallet = todo!();
|
||||||
/// # let mut psbt: Psbt = todo!();
|
/// # let mut psbt: Psbt = todo!();
|
||||||
/// let tx = &psbt.clone().extract_tx().expect("tx");
|
/// let tx = &psbt.clone().extract_tx().expect("tx");
|
||||||
/// let (sent, received) = wallet.sent_and_received(tx);
|
/// let (sent, received) = wallet.sent_and_received(tx);
|
||||||
@ -1031,7 +983,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```rust, no_run
|
/// ```rust, no_run
|
||||||
/// use bdk::{chain::ChainPosition, Wallet};
|
/// use bdk::{chain::ChainPosition, Wallet};
|
||||||
/// use bdk_chain::Anchor;
|
/// use bdk_chain::Anchor;
|
||||||
/// # let wallet: Wallet<()> = todo!();
|
/// # let wallet: Wallet = todo!();
|
||||||
/// # let my_txid: bitcoin::Txid = todo!();
|
/// # let my_txid: bitcoin::Txid = todo!();
|
||||||
///
|
///
|
||||||
/// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
|
/// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
|
||||||
@ -1087,10 +1039,7 @@ impl<D> Wallet<D> {
|
|||||||
pub fn insert_checkpoint(
|
pub fn insert_checkpoint(
|
||||||
&mut self,
|
&mut self,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
) -> Result<bool, local_chain::AlterCheckPointError>
|
) -> Result<bool, local_chain::AlterCheckPointError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let changeset = self.chain.insert_block(block_id)?;
|
let changeset = self.chain.insert_block(block_id)?;
|
||||||
let changed = !changeset.is_empty();
|
let changed = !changeset.is_empty();
|
||||||
self.persist.stage(changeset.into());
|
self.persist.stage(changeset.into());
|
||||||
@ -1118,10 +1067,7 @@ impl<D> Wallet<D> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
position: ConfirmationTime,
|
position: ConfirmationTime,
|
||||||
) -> Result<bool, InsertTxError>
|
) -> Result<bool, InsertTxError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let (anchor, last_seen) = match position {
|
let (anchor, last_seen) = match position {
|
||||||
ConfirmationTime::Confirmed { height, time } => {
|
ConfirmationTime::Confirmed { height, time } => {
|
||||||
// anchor tx to checkpoint with lowest height that is >= position's height
|
// anchor tx to checkpoint with lowest height that is >= position's height
|
||||||
@ -1248,7 +1194,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`TxBuilder`]: crate::TxBuilder
|
/// [`TxBuilder`]: crate::TxBuilder
|
||||||
pub fn build_tx(&mut self) -> TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, CreateTx> {
|
pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm, CreateTx> {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
|
wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
|
||||||
params: TxParams::default(),
|
params: TxParams::default(),
|
||||||
@ -1261,10 +1207,7 @@ impl<D> Wallet<D> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
coin_selection: Cs,
|
coin_selection: Cs,
|
||||||
params: TxParams,
|
params: TxParams,
|
||||||
) -> Result<Psbt, CreateTxError<D::WriteError>>
|
) -> Result<Psbt, CreateTxError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let external_descriptor = self
|
let external_descriptor = self
|
||||||
.indexed_graph
|
.indexed_graph
|
||||||
.index
|
.index
|
||||||
@ -1283,7 +1226,7 @@ impl<D> Wallet<D> {
|
|||||||
let internal_policy = internal_descriptor
|
let internal_policy = internal_descriptor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|desc| {
|
.map(|desc| {
|
||||||
Ok::<_, CreateTxError<D::WriteError>>(
|
Ok::<_, CreateTxError>(
|
||||||
desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
|
desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
@ -1320,7 +1263,7 @@ impl<D> Wallet<D> {
|
|||||||
)?;
|
)?;
|
||||||
let internal_requirements = internal_policy
|
let internal_requirements = internal_policy
|
||||||
.map(|policy| {
|
.map(|policy| {
|
||||||
Ok::<_, CreateTxError<D::WriteError>>(
|
Ok::<_, CreateTxError>(
|
||||||
policy.get_condition(
|
policy.get_condition(
|
||||||
params
|
params
|
||||||
.internal_policy_path
|
.internal_policy_path
|
||||||
@ -1647,7 +1590,7 @@ impl<D> Wallet<D> {
|
|||||||
pub fn build_fee_bump(
|
pub fn build_fee_bump(
|
||||||
&mut self,
|
&mut self,
|
||||||
txid: Txid,
|
txid: Txid,
|
||||||
) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, BuildFeeBumpError> {
|
) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm, BumpFee>, BuildFeeBumpError> {
|
||||||
let graph = self.indexed_graph.graph();
|
let graph = self.indexed_graph.graph();
|
||||||
let txout_index = &self.indexed_graph.index;
|
let txout_index = &self.indexed_graph.index;
|
||||||
let chain_tip = self.chain.tip().block_id();
|
let chain_tip = self.chain.tip().block_id();
|
||||||
@ -2158,10 +2101,7 @@ impl<D> Wallet<D> {
|
|||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
selected: Vec<Utxo>,
|
selected: Vec<Utxo>,
|
||||||
params: TxParams,
|
params: TxParams,
|
||||||
) -> Result<Psbt, CreateTxError<D::WriteError>>
|
) -> Result<Psbt, CreateTxError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let mut psbt = Psbt::from_unsigned_tx(tx)?;
|
let mut psbt = Psbt::from_unsigned_tx(tx)?;
|
||||||
|
|
||||||
if params.add_global_xpubs {
|
if params.add_global_xpubs {
|
||||||
@ -2242,10 +2182,7 @@ impl<D> Wallet<D> {
|
|||||||
utxo: LocalOutput,
|
utxo: LocalOutput,
|
||||||
sighash_type: Option<psbt::PsbtSighashType>,
|
sighash_type: Option<psbt::PsbtSighashType>,
|
||||||
only_witness_utxo: bool,
|
only_witness_utxo: bool,
|
||||||
) -> Result<psbt::Input, CreateTxError<D::WriteError>>
|
) -> Result<psbt::Input, CreateTxError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
// Try to find the prev_script in our db to figure out if this is internal or external,
|
// Try to find the prev_script in our db to figure out if this is internal or external,
|
||||||
// and the derivation index
|
// and the derivation index
|
||||||
let (keychain, child) = self
|
let (keychain, child) = self
|
||||||
@ -2335,10 +2272,7 @@ impl<D> Wallet<D> {
|
|||||||
/// transactions related to your wallet into it.
|
/// transactions related to your wallet into it.
|
||||||
///
|
///
|
||||||
/// [`commit`]: Self::commit
|
/// [`commit`]: Self::commit
|
||||||
pub fn apply_update(&mut self, update: Update) -> Result<(), CannotConnectError>
|
pub fn apply_update(&mut self, update: Update) -> Result<(), CannotConnectError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let mut changeset = match update.chain {
|
let mut changeset = match update.chain {
|
||||||
Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
|
Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
|
||||||
None => ChangeSet::default(),
|
None => ChangeSet::default(),
|
||||||
@ -2354,7 +2288,6 @@ impl<D> Wallet<D> {
|
|||||||
changeset.append(ChangeSet::from(
|
changeset.append(ChangeSet::from(
|
||||||
self.indexed_graph.apply_update(update.graph),
|
self.indexed_graph.apply_update(update.graph),
|
||||||
));
|
));
|
||||||
|
|
||||||
self.persist.stage(changeset);
|
self.persist.stage(changeset);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -2365,20 +2298,14 @@ impl<D> Wallet<D> {
|
|||||||
/// This returns whether the `update` resulted in any changes.
|
/// This returns whether the `update` resulted in any changes.
|
||||||
///
|
///
|
||||||
/// [`staged`]: Self::staged
|
/// [`staged`]: Self::staged
|
||||||
pub fn commit(&mut self) -> Result<bool, D::WriteError>
|
pub fn commit(&mut self) -> anyhow::Result<bool> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
self.persist.commit().map(|c| c.is_some())
|
self.persist.commit().map(|c| c.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the changes that will be committed with the next call to [`commit`].
|
/// Returns the changes that will be committed with the next call to [`commit`].
|
||||||
///
|
///
|
||||||
/// [`commit`]: Self::commit
|
/// [`commit`]: Self::commit
|
||||||
pub fn staged(&self) -> &ChangeSet
|
pub fn staged(&self) -> &ChangeSet {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
self.persist.staged()
|
self.persist.staged()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2404,10 +2331,7 @@ impl<D> Wallet<D> {
|
|||||||
/// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
|
/// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
|
||||||
///
|
///
|
||||||
/// [`apply_block_connected_to`]: Self::apply_block_connected_to
|
/// [`apply_block_connected_to`]: Self::apply_block_connected_to
|
||||||
pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError>
|
pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let connected_to = match height.checked_sub(1) {
|
let connected_to = match height.checked_sub(1) {
|
||||||
Some(prev_height) => BlockId {
|
Some(prev_height) => BlockId {
|
||||||
height: prev_height,
|
height: prev_height,
|
||||||
@ -2438,10 +2362,7 @@ impl<D> Wallet<D> {
|
|||||||
block: &Block,
|
block: &Block,
|
||||||
height: u32,
|
height: u32,
|
||||||
connected_to: BlockId,
|
connected_to: BlockId,
|
||||||
) -> Result<(), ApplyHeaderError>
|
) -> Result<(), ApplyHeaderError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let mut changeset = ChangeSet::default();
|
let mut changeset = ChangeSet::default();
|
||||||
changeset.append(
|
changeset.append(
|
||||||
self.chain
|
self.chain
|
||||||
@ -2468,9 +2389,7 @@ impl<D> Wallet<D> {
|
|||||||
pub fn apply_unconfirmed_txs<'t>(
|
pub fn apply_unconfirmed_txs<'t>(
|
||||||
&mut self,
|
&mut self,
|
||||||
unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
|
unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
|
||||||
) where
|
) {
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
let indexed_graph_changeset = self
|
let indexed_graph_changeset = self
|
||||||
.indexed_graph
|
.indexed_graph
|
||||||
.batch_insert_relevant_unconfirmed(unconfirmed_txs);
|
.batch_insert_relevant_unconfirmed(unconfirmed_txs);
|
||||||
@ -2478,7 +2397,7 @@ impl<D> Wallet<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet<D> {
|
impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet {
|
||||||
fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor> {
|
fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor> {
|
||||||
self.indexed_graph.graph()
|
self.indexed_graph.graph()
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,12 @@ use core::cell::RefCell;
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use bdk_chain::PersistBackend;
|
|
||||||
use bitcoin::psbt::{self, Psbt};
|
use bitcoin::psbt::{self, Psbt};
|
||||||
use bitcoin::script::PushBytes;
|
use bitcoin::script::PushBytes;
|
||||||
use bitcoin::{absolute, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid};
|
use bitcoin::{absolute, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid};
|
||||||
|
|
||||||
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
||||||
use super::{ChangeSet, CreateTxError, Wallet};
|
use super::{CreateTxError, Wallet};
|
||||||
use crate::collections::{BTreeMap, HashSet};
|
use crate::collections::{BTreeMap, HashSet};
|
||||||
use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
|
use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
|
||||||
|
|
||||||
@ -124,8 +123,8 @@ impl TxBuilderContext for BumpFee {}
|
|||||||
/// [`finish`]: Self::finish
|
/// [`finish`]: Self::finish
|
||||||
/// [`coin_selection`]: Self::coin_selection
|
/// [`coin_selection`]: Self::coin_selection
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TxBuilder<'a, D, Cs, Ctx> {
|
pub struct TxBuilder<'a, Cs, Ctx> {
|
||||||
pub(crate) wallet: Rc<RefCell<&'a mut Wallet<D>>>,
|
pub(crate) wallet: Rc<RefCell<&'a mut Wallet>>,
|
||||||
pub(crate) params: TxParams,
|
pub(crate) params: TxParams,
|
||||||
pub(crate) coin_selection: Cs,
|
pub(crate) coin_selection: Cs,
|
||||||
pub(crate) phantom: PhantomData<Ctx>,
|
pub(crate) phantom: PhantomData<Ctx>,
|
||||||
@ -176,7 +175,7 @@ impl Default for FeePolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D, Cs: Clone, Ctx> Clone for TxBuilder<'a, D, Cs, Ctx> {
|
impl<'a, Cs: Clone, Ctx> Clone for TxBuilder<'a, Cs, Ctx> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
wallet: self.wallet.clone(),
|
wallet: self.wallet.clone(),
|
||||||
@ -188,7 +187,7 @@ impl<'a, D, Cs: Clone, Ctx> Clone for TxBuilder<'a, D, Cs, Ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
// methods supported by both contexts, for any CoinSelectionAlgorithm
|
||||||
impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
impl<'a, Cs, Ctx> TxBuilder<'a, Cs, Ctx> {
|
||||||
/// Set a custom fee rate.
|
/// Set a custom fee rate.
|
||||||
///
|
///
|
||||||
/// This method sets the mining fee paid by the transaction as a rate on its size.
|
/// This method sets the mining fee paid by the transaction as a rate on its size.
|
||||||
@ -560,7 +559,7 @@ impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
|||||||
pub fn coin_selection<P: CoinSelectionAlgorithm>(
|
pub fn coin_selection<P: CoinSelectionAlgorithm>(
|
||||||
self,
|
self,
|
||||||
coin_selection: P,
|
coin_selection: P,
|
||||||
) -> TxBuilder<'a, D, P, Ctx> {
|
) -> TxBuilder<'a, P, Ctx> {
|
||||||
TxBuilder {
|
TxBuilder {
|
||||||
wallet: self.wallet,
|
wallet: self.wallet,
|
||||||
params: self.params,
|
params: self.params,
|
||||||
@ -615,16 +614,13 @@ impl<'a, D, Cs, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx> TxBuilder<'a, D, Cs, Ctx> {
|
impl<'a, Cs: CoinSelectionAlgorithm, Ctx> TxBuilder<'a, Cs, Ctx> {
|
||||||
/// Finish building the transaction.
|
/// Finish building the transaction.
|
||||||
///
|
///
|
||||||
/// Returns a new [`Psbt`] per [`BIP174`].
|
/// Returns a new [`Psbt`] per [`BIP174`].
|
||||||
///
|
///
|
||||||
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
|
||||||
pub fn finish(self) -> Result<Psbt, CreateTxError<D::WriteError>>
|
pub fn finish(self) -> Result<Psbt, CreateTxError> {
|
||||||
where
|
|
||||||
D: PersistBackend<ChangeSet>,
|
|
||||||
{
|
|
||||||
self.wallet
|
self.wallet
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.create_tx(self.coin_selection, self.params)
|
.create_tx(self.coin_selection, self.params)
|
||||||
@ -715,7 +711,7 @@ impl fmt::Display for AllowShrinkingError {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl std::error::Error for AllowShrinkingError {}
|
impl std::error::Error for AllowShrinkingError {}
|
||||||
|
|
||||||
impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
|
impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs, CreateTx> {
|
||||||
/// Replace the recipients already added with a new list
|
/// Replace the recipients already added with a new list
|
||||||
pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self {
|
pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self {
|
||||||
self.params.recipients = recipients;
|
self.params.recipients = recipients;
|
||||||
@ -793,7 +789,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// methods supported only by bump_fee
|
// methods supported only by bump_fee
|
||||||
impl<'a, D> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
impl<'a> TxBuilder<'a, DefaultCoinSelectionAlgorithm, BumpFee> {
|
||||||
/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this
|
/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this
|
||||||
/// `script_pubkey` in order to bump the transaction fee. Without specifying this the wallet
|
/// `script_pubkey` in order to bump the transaction fee. Without specifying this the wallet
|
||||||
/// will attempt to find a change output to shrink instead.
|
/// will attempt to find a change output to shrink instead.
|
||||||
|
@ -14,6 +14,7 @@ readme = "README.md"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# For no-std, remember to enable the bitcoin/no-std feature
|
# For no-std, remember to enable the bitcoin/no-std feature
|
||||||
|
anyhow = { version = "1", default-features = false }
|
||||||
bitcoin = { version = "0.31.0", default-features = false }
|
bitcoin = { version = "0.31.0", default-features = false }
|
||||||
serde_crate = { package = "serde", version = "1", optional = true, features = ["derive", "rc"] }
|
serde_crate = { package = "serde", version = "1", optional = true, features = ["derive", "rc"] }
|
||||||
|
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
use core::convert::Infallible;
|
|
||||||
|
|
||||||
use crate::Append;
|
use crate::Append;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
/// `Persist` wraps a [`PersistBackend`] (`B`) to create a convenient staging area for changes (`C`)
|
/// `Persist` wraps a [`PersistBackend`] to create a convenient staging area for changes (`C`)
|
||||||
/// before they are persisted.
|
/// before they are persisted.
|
||||||
///
|
///
|
||||||
/// Not all changes to the in-memory representation needs to be written to disk right away, so
|
/// Not all changes to the in-memory representation needs to be written to disk right away, so
|
||||||
/// [`Persist::stage`] can be used to *stage* changes first and then [`Persist::commit`] can be used
|
/// [`Persist::stage`] can be used to *stage* changes first and then [`Persist::commit`] can be used
|
||||||
/// to write changes to disk.
|
/// to write changes to disk.
|
||||||
#[derive(Debug)]
|
pub struct Persist<C> {
|
||||||
pub struct Persist<B, C> {
|
backend: Box<dyn PersistBackend<C> + Send + Sync>,
|
||||||
backend: B,
|
|
||||||
stage: C,
|
stage: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, C> Persist<B, C>
|
impl<C: fmt::Debug> fmt::Debug for Persist<C> {
|
||||||
|
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||||
|
write!(fmt, "{:?}", self.stage)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> Persist<C>
|
||||||
where
|
where
|
||||||
B: PersistBackend<C>,
|
|
||||||
C: Default + Append,
|
C: Default + Append,
|
||||||
{
|
{
|
||||||
/// Create a new [`Persist`] from [`PersistBackend`].
|
/// Create a new [`Persist`] from [`PersistBackend`].
|
||||||
pub fn new(backend: B) -> Self {
|
pub fn new(backend: impl PersistBackend<C> + Send + Sync + 'static) -> Self {
|
||||||
|
let backend = Box::new(backend);
|
||||||
Self {
|
Self {
|
||||||
backend,
|
backend,
|
||||||
stage: Default::default(),
|
stage: Default::default(),
|
||||||
@ -46,7 +52,7 @@ where
|
|||||||
/// # Error
|
/// # Error
|
||||||
///
|
///
|
||||||
/// Returns a backend-defined error if this fails.
|
/// Returns a backend-defined error if this fails.
|
||||||
pub fn commit(&mut self) -> Result<Option<C>, B::WriteError> {
|
pub fn commit(&mut self) -> anyhow::Result<Option<C>> {
|
||||||
if self.stage.is_empty() {
|
if self.stage.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@ -63,7 +69,7 @@ where
|
|||||||
///
|
///
|
||||||
/// [`stage`]: Self::stage
|
/// [`stage`]: Self::stage
|
||||||
/// [`commit`]: Self::commit
|
/// [`commit`]: Self::commit
|
||||||
pub fn stage_and_commit(&mut self, changeset: C) -> Result<Option<C>, B::WriteError> {
|
pub fn stage_and_commit(&mut self, changeset: C) -> anyhow::Result<Option<C>> {
|
||||||
self.stage(changeset);
|
self.stage(changeset);
|
||||||
self.commit()
|
self.commit()
|
||||||
}
|
}
|
||||||
@ -74,12 +80,6 @@ where
|
|||||||
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
|
/// `C` represents the changeset; a datatype that records changes made to in-memory data structures
|
||||||
/// that are to be persisted, or retrieved from persistence.
|
/// that are to be persisted, or retrieved from persistence.
|
||||||
pub trait PersistBackend<C> {
|
pub trait PersistBackend<C> {
|
||||||
/// The error the backend returns when it fails to write.
|
|
||||||
type WriteError: core::fmt::Debug;
|
|
||||||
|
|
||||||
/// The error the backend returns when it fails to load changesets `C`.
|
|
||||||
type LoadError: core::fmt::Debug;
|
|
||||||
|
|
||||||
/// Writes a changeset to the persistence backend.
|
/// 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 is up to the backend what it does with this. It could store every changeset in a list or
|
||||||
@ -88,22 +88,18 @@ pub trait PersistBackend<C> {
|
|||||||
/// changesets had been applied sequentially.
|
/// changesets had been applied sequentially.
|
||||||
///
|
///
|
||||||
/// [`load_from_persistence`]: Self::load_from_persistence
|
/// [`load_from_persistence`]: Self::load_from_persistence
|
||||||
fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError>;
|
fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()>;
|
||||||
|
|
||||||
/// Return the aggregate changeset `C` from persistence.
|
/// Return the aggregate changeset `C` from persistence.
|
||||||
fn load_from_persistence(&mut self) -> Result<Option<C>, Self::LoadError>;
|
fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> PersistBackend<C> for () {
|
impl<C> PersistBackend<C> for () {
|
||||||
type WriteError = Infallible;
|
fn write_changes(&mut self, _changeset: &C) -> anyhow::Result<()> {
|
||||||
|
|
||||||
type LoadError = Infallible;
|
|
||||||
|
|
||||||
fn write_changes(&mut self, _changeset: &C) -> Result<(), Self::WriteError> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_persistence(&mut self) -> Result<Option<C>, Self::LoadError> {
|
fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ authors = ["Bitcoin Dev Kit Developers"]
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = { version = "1", default-features = false }
|
||||||
bdk_chain = { path = "../chain", version = "0.12.0", features = [ "serde", "miniscript" ] }
|
bdk_chain = { path = "../chain", version = "0.12.0", features = [ "serde", "miniscript" ] }
|
||||||
bincode = { version = "1" }
|
bincode = { version = "1" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
|
use crate::{bincode_options, EntryIter, FileError, IterError};
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use bdk_chain::{Append, PersistBackend};
|
||||||
|
use bincode::Options;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::{self, Debug},
|
||||||
fs::{File, OpenOptions},
|
fs::{File, OpenOptions},
|
||||||
io::{self, Read, Seek, Write},
|
io::{self, Read, Seek, Write},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bdk_chain::{Append, PersistBackend};
|
|
||||||
use bincode::Options;
|
|
||||||
|
|
||||||
use crate::{bincode_options, EntryIter, FileError, IterError};
|
|
||||||
|
|
||||||
/// Persists an append-only list of changesets (`C`) to a single file.
|
/// Persists an append-only list of changesets (`C`) to a single file.
|
||||||
///
|
///
|
||||||
/// The changesets are the results of altering a tracker implementation (`T`).
|
/// The changesets are the results of altering a tracker implementation (`T`).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Store<C> {
|
pub struct Store<C>
|
||||||
|
where
|
||||||
|
C: Sync + Send,
|
||||||
|
{
|
||||||
magic_len: usize,
|
magic_len: usize,
|
||||||
db_file: File,
|
db_file: File,
|
||||||
marker: PhantomData<C>,
|
marker: PhantomData<C>,
|
||||||
@ -23,24 +25,30 @@ pub struct Store<C> {
|
|||||||
|
|
||||||
impl<C> PersistBackend<C> for Store<C>
|
impl<C> PersistBackend<C> for Store<C>
|
||||||
where
|
where
|
||||||
C: Append + serde::Serialize + serde::de::DeserializeOwned,
|
C: Append
|
||||||
|
+ serde::Serialize
|
||||||
|
+ serde::de::DeserializeOwned
|
||||||
|
+ core::marker::Send
|
||||||
|
+ core::marker::Sync,
|
||||||
{
|
{
|
||||||
type WriteError = std::io::Error;
|
fn write_changes(&mut self, changeset: &C) -> anyhow::Result<()> {
|
||||||
|
|
||||||
type LoadError = IterError;
|
|
||||||
|
|
||||||
fn write_changes(&mut self, changeset: &C) -> Result<(), Self::WriteError> {
|
|
||||||
self.append_changeset(changeset)
|
self.append_changeset(changeset)
|
||||||
|
.map_err(|e| anyhow!(e).context("failed to write changes to persistence backend"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_persistence(&mut self) -> Result<Option<C>, Self::LoadError> {
|
fn load_from_persistence(&mut self) -> anyhow::Result<Option<C>> {
|
||||||
self.aggregate_changesets().map_err(|e| e.iter_error)
|
self.aggregate_changesets()
|
||||||
|
.map_err(|e| anyhow!(e.iter_error).context("error loading from persistence backend"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Store<C>
|
impl<C> Store<C>
|
||||||
where
|
where
|
||||||
C: Append + serde::Serialize + serde::de::DeserializeOwned,
|
C: Append
|
||||||
|
+ serde::Serialize
|
||||||
|
+ serde::de::DeserializeOwned
|
||||||
|
+ core::marker::Send
|
||||||
|
+ core::marker::Sync,
|
||||||
{
|
{
|
||||||
/// Create a new [`Store`] file in write-only mode; error if the file exists.
|
/// Create a new [`Store`] file in write-only mode; error if the file exists.
|
||||||
///
|
///
|
||||||
@ -182,7 +190,7 @@ where
|
|||||||
bincode_options()
|
bincode_options()
|
||||||
.serialize_into(&mut self.db_file, changeset)
|
.serialize_into(&mut self.db_file, changeset)
|
||||||
.map_err(|e| match *e {
|
.map_err(|e| match *e {
|
||||||
bincode::ErrorKind::Io(inner) => inner,
|
bincode::ErrorKind::Io(error) => error,
|
||||||
unexpected_err => panic!("unexpected bincode error: {}", unexpected_err),
|
unexpected_err => panic!("unexpected bincode error: {}", unexpected_err),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -212,7 +220,7 @@ impl<C> std::fmt::Display for AggregateChangesetsError<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: std::fmt::Debug> std::error::Error for AggregateChangesetsError<C> {}
|
impl<C: fmt::Debug> std::error::Error for AggregateChangesetsError<C> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -31,7 +31,6 @@ pub type KeychainChangeSet<A> = (
|
|||||||
local_chain::ChangeSet,
|
local_chain::ChangeSet,
|
||||||
indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<Keychain>>,
|
indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<Keychain>>,
|
||||||
);
|
);
|
||||||
pub type Database<C> = Persist<Store<C>, C>;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
@ -440,7 +439,7 @@ pub fn planned_utxos<A: Anchor, O: ChainOracle, K: Clone + bdk_tmp_plan::CanDeri
|
|||||||
|
|
||||||
pub fn handle_commands<CS: clap::Subcommand, S: clap::Args, A: Anchor, O: ChainOracle, C>(
|
pub fn handle_commands<CS: clap::Subcommand, S: clap::Args, A: Anchor, O: ChainOracle, C>(
|
||||||
graph: &Mutex<KeychainTxGraph<A>>,
|
graph: &Mutex<KeychainTxGraph<A>>,
|
||||||
db: &Mutex<Database<C>>,
|
db: &Mutex<Persist<C>>,
|
||||||
chain: &Mutex<O>,
|
chain: &Mutex<O>,
|
||||||
keymap: &BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
|
keymap: &BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
|
||||||
network: Network,
|
network: Network,
|
||||||
@ -667,7 +666,7 @@ pub struct Init<CS: clap::Subcommand, S: clap::Args, C> {
|
|||||||
/// Keychain-txout index.
|
/// Keychain-txout index.
|
||||||
pub index: KeychainTxOutIndex<Keychain>,
|
pub index: KeychainTxOutIndex<Keychain>,
|
||||||
/// Persistence backend.
|
/// Persistence backend.
|
||||||
pub db: Mutex<Database<C>>,
|
pub db: Mutex<Persist<C>>,
|
||||||
/// Initial changeset.
|
/// Initial changeset.
|
||||||
pub init_changeset: C,
|
pub init_changeset: C,
|
||||||
}
|
}
|
||||||
@ -679,7 +678,13 @@ pub fn init<CS: clap::Subcommand, S: clap::Args, C>(
|
|||||||
db_default_path: &str,
|
db_default_path: &str,
|
||||||
) -> anyhow::Result<Init<CS, S, C>>
|
) -> anyhow::Result<Init<CS, S, C>>
|
||||||
where
|
where
|
||||||
C: Default + Append + Serialize + DeserializeOwned,
|
C: Default
|
||||||
|
+ Append
|
||||||
|
+ Serialize
|
||||||
|
+ DeserializeOwned
|
||||||
|
+ core::marker::Send
|
||||||
|
+ core::marker::Sync
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
if std::env::var("BDK_DB_PATH").is_err() {
|
if std::env::var("BDK_DB_PATH").is_err() {
|
||||||
std::env::set_var("BDK_DB_PATH", db_default_path);
|
std::env::set_var("BDK_DB_PATH", db_default_path);
|
||||||
@ -715,7 +720,7 @@ where
|
|||||||
args,
|
args,
|
||||||
keymap,
|
keymap,
|
||||||
index,
|
index,
|
||||||
db: Mutex::new(Database::new(db_backend)),
|
db: Mutex::new(Persist::new(db_backend)),
|
||||||
init_changeset,
|
init_changeset,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user