## [Unreleased]
+- Remove `BlockchainMarker`, `OfflineClient` and `OfflineWallet` in favor of just using the unit
+ type to mark for a missing client.
+
## [v0.2.0] - [0.1.0-beta.1]
### Project
### Generate a few addresses
```rust
-use bdk::{Wallet, OfflineWallet};
-use bdk::database::MemoryDatabase;
+use bdk::{Wallet, database::MemoryDatabase};
fn main() -> Result<(), bdk::Error> {
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
bitcoin::Network::Testnet,
### Sign a transaction
```rust,no_run
-use bdk::{Wallet, OfflineWallet};
-use bdk::database::MemoryDatabase;
+use bdk::{Wallet, database::MemoryDatabase};
use bitcoin::consensus::deserialize;
fn main() -> Result<(), bdk::Error> {
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
bitcoin::Network::Testnet,
use bdk::descriptor::HDKeyPaths;
use bdk::wallet::address_validator::{AddressValidator, AddressValidatorError};
use bdk::KeychainKind;
-use bdk::{OfflineWallet, Wallet};
+use bdk::Wallet;
use bitcoin::hashes::hex::FromHex;
use bitcoin::util::bip32::Fingerprint;
fn main() -> Result<(), bdk::Error> {
let descriptor = "sh(and_v(v:pk(tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/*),after(630000)))";
- let mut wallet: OfflineWallet<_> =
+ let mut wallet =
Wallet::new_offline(descriptor, None, Network::Regtest, MemoryDatabase::new())?;
wallet.add_address_validator(Arc::new(DummyValidator));
use miniscript::Descriptor;
use bdk::database::memory::MemoryDatabase;
-use bdk::{KeychainKind, OfflineWallet, Wallet};
+use bdk::{KeychainKind, Wallet};
fn main() {
env_logger::init_from_env(
Some("regtest") => Network::Regtest,
Some("testnet") | _ => Network::Testnet,
};
- let wallet: OfflineWallet<_> =
- Wallet::new_offline(&format!("{}", descriptor), None, network, database).unwrap();
+ let wallet = Wallet::new_offline(&format!("{}", descriptor), None, network, database).unwrap();
info!("... First address: {}", wallet.get_new_address().unwrap());
AccurateFees,
}
-/// Marker trait for a blockchain backend
-///
-/// This is a marker trait for blockchain types. It is automatically implemented for types that
-/// implement [`Blockchain`], so as a user of the library you won't have to implement this
-/// manually.
-///
-/// Users of the library will probably never have to implement this trait manually, but they
-/// could still need to import it to define types and structs with generics;
-/// Implementing only the marker trait is pointless, since [`OfflineBlockchain`]
-/// already does that, and whenever [`Blockchain`] is implemented, the marker trait is also
-/// automatically implemented by the library.
-pub trait BlockchainMarker {}
-
-/// The [`BlockchainMarker`] marker trait is automatically implemented for [`Blockchain`] types
-impl<T: Blockchain> BlockchainMarker for T {}
-
-/// Type that only implements [`BlockchainMarker`] and is always "offline"
-pub struct OfflineBlockchain;
-impl BlockchainMarker for OfflineBlockchain {}
-
/// Trait that defines the actions that must be supported by a blockchain backend
#[maybe_async]
-pub trait Blockchain: BlockchainMarker {
+pub trait Blockchain {
/// Return the set of [`Capability`] supported by this backend
fn get_capabilities(&self) -> HashSet<Capability>;
//!
//! ## Example
//!
-//! In this example, `wallet_memory` and `wallet_sled` have the same type of `Wallet<OfflineBlockchain, AnyDatabase>`.
+//! In this example, `wallet_memory` and `wallet_sled` have the same type of `Wallet<(), AnyDatabase>`.
//!
//! ```no_run
//! # use bitcoin::Network;
//! # use bdk::database::{AnyDatabase, MemoryDatabase};
-//! # use bdk::{Wallet, OfflineWallet};
-//! let memory = MemoryDatabase::default().into();
-//! let wallet_memory: OfflineWallet<AnyDatabase> =
+//! # use bdk::{Wallet};
+//! let memory = MemoryDatabase::default();
+//! let wallet_memory =
//! Wallet::new_offline("...", None, Network::Testnet, memory)?;
//!
//! # #[cfg(feature = "key-value-db")]
//! # {
-//! let sled = sled::open("my-database")?.open_tree("default_tree")?.into();
-//! let wallet_sled: OfflineWallet<AnyDatabase> =
+//! let sled = sled::open("my-database")?.open_tree("default_tree")?;
+//! let wallet_sled =
//! Wallet::new_offline("...", None, Network::Testnet, sled)?;
//! # }
//! # Ok::<(), bdk::Error>(())
//! ```no_run
//! # use bitcoin::Network;
//! # use bdk::database::*;
-//! # use bdk::{Wallet, OfflineWallet};
+//! # use bdk::{Wallet};
//! let config = serde_json::from_str("...")?;
//! let database = AnyDatabase::from_config(&config)?;
-//! let wallet: OfflineWallet<_> = Wallet::new_offline("...", None, Network::Testnet, database)?;
+//! let wallet = Wallet::new_offline("...", None, Network::Testnet, database)?;
//! # Ok::<(), bdk::Error>(())
//! ```
///
/// ```
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet};
+/// # use bdk::{Wallet};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::P2PKH;
///
/// let key =
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// P2PKH(key),
/// None,
/// Network::Testnet,
///
/// ```
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet};
+/// # use bdk::{Wallet};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::P2WPKH_P2SH;
///
/// let key =
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// P2WPKH_P2SH(key),
/// None,
/// Network::Testnet,
///
/// ```
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet};
+/// # use bdk::{Wallet};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::P2WPKH;
///
/// let key =
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// P2WPKH(key),
/// None,
/// Network::Testnet,
/// ```
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet, KeychainKind};
+/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::BIP44;
///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// BIP44(key.clone(), KeychainKind::External),
/// Some(BIP44(key, KeychainKind::Internal)),
/// Network::Testnet,
/// ```
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet, KeychainKind};
+/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::BIP44Public;
///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// BIP44Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(BIP44Public(key, fingerprint, KeychainKind::Internal)),
/// Network::Testnet,
/// ```
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet, KeychainKind};
+/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::BIP49;
///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// BIP49(key.clone(), KeychainKind::External),
/// Some(BIP49(key, KeychainKind::Internal)),
/// Network::Testnet,
/// ```
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet, KeychainKind};
+/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::BIP49Public;
///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// BIP49Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(BIP49Public(key, fingerprint, KeychainKind::Internal)),
/// Network::Testnet,
/// ```
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet, KeychainKind};
+/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::BIP84;
///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// BIP84(key.clone(), KeychainKind::External),
/// Some(BIP84(key, KeychainKind::Internal)),
/// Network::Testnet,
/// ```
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet, OfflineWallet, KeychainKind};
+/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::database::MemoryDatabase;
/// use bdk::template::BIP84Public;
///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
-/// let wallet: OfflineWallet<_> = Wallet::new_offline(
+/// let wallet = Wallet::new_offline(
/// BIP84Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(BIP84Public(key, fingerprint, KeychainKind::Internal)),
/// Network::Testnet,
/// Signing error
Signer(crate::wallet::signer::SignerError),
- // Blockchain interface errors
- /// Thrown when trying to call a method that requires a network connection, [`Wallet::sync`](crate::Wallet::sync) and [`Wallet::broadcast`](crate::Wallet::broadcast)
- /// This error is thrown when creating the Client for the first time, while recovery attempts are tried
- /// during the sync
- OfflineClient,
/// Progress value must be between `0.0` (included) and `100.0` (included)
InvalidProgressValue(f32),
/// Progress update error (maybe the channel has been closed)
//!
//! ### Example
//! ```
-//! use bdk::{Wallet, OfflineWallet};
+//! use bdk::{Wallet};
//! use bdk::database::MemoryDatabase;
//!
//! fn main() -> Result<(), bdk::Error> {
-//! let wallet: OfflineWallet<_> = Wallet::new_offline(
+//! let wallet = Wallet::new_offline(
//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
//! bitcoin::Network::Testnet,
//! ### Example
//! ```ignore
//! use base64::decode;
-//! use bdk::{Wallet, OfflineWallet};
+//! use bdk::{Wallet};
//! use bdk::database::MemoryDatabase;
//!
//! use bitcoin::consensus::deserialize;
//!
//! fn main() -> Result<(), bdk::Error> {
-//! let wallet: OfflineWallet<_> = Wallet::new_offline(
+//! let wallet = Wallet::new_offline(
//! "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
//! Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
//! bitcoin::Network::Testnet,
pub use wallet::address_validator;
pub use wallet::signer;
pub use wallet::tx_builder::TxBuilder;
-pub use wallet::{OfflineWallet, Wallet};
+pub use wallet::Wallet;
//! }
//!
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
-//! let mut wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+//! let mut wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
//! wallet.add_address_validator(Arc::new(PrintAddressAndContinue));
//!
//! let address = wallet.get_new_address()?;
//! }
//! }
//!
-//! # let wallet: OfflineWallet<_> = Wallet::new_offline("", None, Network::Testnet, bdk::database::MemoryDatabase::default())?;
+//! # let wallet = Wallet::new_offline("", None, Network::Testnet, bdk::database::MemoryDatabase::default())?;
//! // create wallet, sync, ...
//!
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
//! }"#;
//!
//! let import = WalletExport::from_str(import)?;
-//! let wallet: OfflineWallet<_> = Wallet::new_offline(
+//! let wallet = Wallet::new_offline(
//! &import.descriptor(),
//! import.change_descriptor().as_ref(),
//! Network::Testnet,
//! # use bdk::database::*;
//! # use bdk::wallet::export::*;
//! # use bdk::*;
-//! let wallet: OfflineWallet<_> = Wallet::new_offline(
+//! let wallet = Wallet::new_offline(
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
//! Network::Testnet,
use miniscript::{Descriptor, DescriptorPublicKey, ScriptContext, Terminal};
-use crate::blockchain::BlockchainMarker;
use crate::database::BatchDatabase;
use crate::wallet::Wallet;
///
/// If the database is empty or `include_blockheight` is false, the `blockheight` field
/// returned will be `0`.
- pub fn export_wallet<B: BlockchainMarker, D: BatchDatabase>(
+ pub fn export_wallet<B, D: BatchDatabase>(
wallet: &Wallet<B, D>,
label: &str,
include_blockheight: bool,
use super::*;
use crate::database::{memory::MemoryDatabase, BatchOperations};
use crate::types::TransactionDetails;
- use crate::wallet::{OfflineWallet, Wallet};
+ use crate::wallet::Wallet;
fn get_test_db() -> MemoryDatabase {
let mut db = MemoryDatabase::new();
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
descriptor,
Some(change_descriptor),
Network::Bitcoin,
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
- let wallet: OfflineWallet<_> =
+ let wallet =
Wallet::new_offline(descriptor, None, Network::Bitcoin, get_test_db()).unwrap();
WalletExport::export_wallet(&wallet, "Test Label", true).unwrap();
}
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/50'/0'/1/*)";
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
descriptor,
Some(change_descriptor),
Network::Bitcoin,
[c98b1535/48'/0'/0'/2']tpubDCDi5W4sP6zSnzJeowy8rQDVhBdRARaPhK1axABi8V1661wEPeanpEXj4ZLAUEoikVtoWcyK26TKKJSecSfeKxwHCcRrge9k1ybuiL71z4a/1/*\
))";
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
descriptor,
Some(change_descriptor),
Network::Testnet,
let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
descriptor,
Some(change_descriptor),
Network::Bitcoin,
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxBuilderContext};
use utils::{check_nlocktime, check_nsequence_rbf, descriptor_to_pk_ctx, After, Older, SecpCtx};
-use crate::blockchain::{Blockchain, BlockchainMarker, OfflineBlockchain, Progress};
+use crate::blockchain::{Blockchain, Progress};
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::descriptor::{
get_checksum, DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy, Policy,
const CACHE_ADDR_BATCH_SIZE: u32 = 100;
-/// Type alias for a [`Wallet`] that uses [`OfflineBlockchain`]
-pub type OfflineWallet<D> = Wallet<OfflineBlockchain, D>;
-
/// A Bitcoin wallet
///
/// A wallet takes descriptors, a [`database`](trait@crate::database::Database) and a
/// [creating transactions](Wallet::create_tx), etc.
///
/// A wallet can be either "online" if the [`blockchain`](crate::blockchain) type provided
-/// implements [`Blockchain`], or "offline" [`OfflineBlockchain`] is used. Offline wallets only expose
+/// implements [`Blockchain`], or "offline" if it is the unit type `()`. Offline wallets only expose
/// methods that don't need any interaction with the blockchain to work.
pub struct Wallet<B, D> {
descriptor: ExtendedDescriptor,
current_height: Option<u32>,
- client: Option<B>,
+ client: B,
database: RefCell<D>,
secp: SecpCtx,
}
-// offline actions, always available
-impl<B, D> Wallet<B, D>
+impl<D> Wallet<(), D>
where
- B: BlockchainMarker,
D: BatchDatabase,
{
/// Create a new "offline" wallet
pub fn new_offline<E: ToWalletDescriptor>(
+ descriptor: E,
+ change_descriptor: Option<E>,
+ network: Network,
+ database: D,
+ ) -> Result<Self, Error> {
+ Self::_new(descriptor, change_descriptor, network, database, (), None)
+ }
+}
+
+impl<B, D> Wallet<B, D>
+where
+ D: BatchDatabase,
+{
+ fn _new<E: ToWalletDescriptor>(
descriptor: E,
change_descriptor: Option<E>,
network: Network,
mut database: D,
+ client: B,
+ current_height: Option<u32>,
) -> Result<Self, Error> {
let (descriptor, keymap) = descriptor.to_wallet_descriptor(network)?;
database.check_descriptor_checksum(
signers,
change_signers,
address_validators: Vec::new(),
-
network,
-
- current_height: None,
-
- client: None,
+ current_height,
+ client,
database: RefCell::new(database),
-
secp: Secp256k1::new(),
})
}
+}
+// offline actions, always available
+impl<B, D> Wallet<B, D>
+where
+ D: BatchDatabase,
+{
/// Return a newly generated address using the external descriptor
pub fn get_new_address(&self) -> Result<Address, Error> {
let index = self.fetch_and_increment_index(KeychainKind::External)?;
/// # use bdk::*;
/// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
- /// # let wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+ /// # let wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
/// let (psbt, details) = wallet.create_tx(
/// TxBuilder::with_recipients(vec![(to_address.script_pubkey(), 50_000)])
/// # use bdk::*;
/// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
- /// # let wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+ /// # let wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
/// let txid = Txid::from_str("faff0a466b70f5d5f92bd757a92c1371d4838bdd5bc53a06764e2488e51ce8f8").unwrap();
/// let (psbt, details) = wallet.bump_fee(
/// &txid,
/// # use bdk::*;
/// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
- /// # let wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+ /// # let wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
/// # let (psbt, _) = wallet.create_tx(TxBuilder::new())?;
/// let (signed_psbt, finalized) = wallet.sign(psbt, None)?;
/// # Ok::<(), bdk::Error>(())
database: D,
client: B,
) -> Result<Self, Error> {
- let mut wallet = Self::new_offline(descriptor, change_descriptor, network, database)?;
-
- wallet.current_height = Some(maybe_await!(client.get_height())? as u32);
- wallet.client = Some(client);
-
- Ok(wallet)
+ let current_height = Some(maybe_await!(client.get_height())? as u32);
+ Self::_new(
+ descriptor,
+ change_descriptor,
+ network,
+ database,
+ client,
+ current_height,
+ )
}
/// Sync the internal database with the blockchain
// TODO: what if i generate an address first and cache some addresses?
// TODO: we should sync if generating an address triggers a new batch to be stored
if run_setup {
- maybe_await!(self.client.as_ref().ok_or(Error::OfflineClient)?.setup(
+ maybe_await!(self.client.setup(
None,
self.database.borrow_mut().deref_mut(),
progress_update,
))
} else {
- maybe_await!(self.client.as_ref().ok_or(Error::OfflineClient)?.sync(
+ maybe_await!(self.client.sync(
None,
self.database.borrow_mut().deref_mut(),
progress_update,
}
/// Return a reference to the internal blockchain client
- pub fn client(&self) -> Option<&B> {
- self.client.as_ref()
+ pub fn client(&self) -> &B {
+ &self.client
}
/// Get the Bitcoin network the wallet is using.
/// Broadcast a transaction to the network
#[maybe_async]
pub fn broadcast(&self, tx: Transaction) -> Result<Txid, Error> {
- maybe_await!(self
- .client
- .as_ref()
- .ok_or(Error::OfflineClient)?
- .broadcast(&tx))?;
+ maybe_await!(self.client.broadcast(&tx))?;
Ok(tx.txid())
}
#[test]
fn test_cache_addresses_fixed() {
let db = MemoryDatabase::new();
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
"wpkh(L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6)",
None,
Network::Testnet,
#[test]
fn test_cache_addresses() {
let db = MemoryDatabase::new();
- let wallet: OfflineWallet<_> = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
+ let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
assert_eq!(
wallet.get_new_address().unwrap().to_string(),
#[test]
fn test_cache_addresses_refill() {
let db = MemoryDatabase::new();
- let wallet: OfflineWallet<_> = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
+ let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", None, Network::Testnet, db).unwrap();
assert_eq!(
wallet.get_new_address().unwrap().to_string(),
pub(crate) fn get_funded_wallet(
descriptor: &str,
) -> (
- OfflineWallet<MemoryDatabase>,
+ Wallet<(), MemoryDatabase>,
(String, Option<String>),
bitcoin::Txid,
) {
let descriptors = testutils!(@descriptors (descriptor));
- let wallet: OfflineWallet<_> = Wallet::new_offline(
+ let wallet = Wallet::new_offline(
&descriptors.0,
None,
Network::Regtest,
//! let custom_signer = CustomSigner::connect();
//!
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
-//! let mut wallet: OfflineWallet<_> = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+//! let mut wallet = Wallet::new_offline(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
//! wallet.add_signer(
//! KeychainKind::External,
//! Fingerprint::from_str("e30f11b8").unwrap().into(),