```rust,no_run
use bdk::Wallet;
-use bdk::database::MemoryDatabase;
use bdk::blockchain::ElectrumBlockchain;
use bdk::SyncOptions;
use bdk::electrum_client::Client;
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet,
- MemoryDatabase::default(),
)?;
wallet.sync(&blockchain, SyncOptions::default())?;
### Generate a few addresses
```rust
-use bdk::{Wallet, database::MemoryDatabase};
+use bdk::Wallet;
use bdk::wallet::AddressIndex::New;
use bdk::bitcoin::Network;
fn main() -> Result<(), bdk::Error> {
- let wallet = Wallet::new(
+ let wallet = Wallet::new_no_persist(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet,
- MemoryDatabase::default(),
)?;
println!("Address #0: {}", wallet.get_address(New));
```rust,no_run
use bdk::{FeeRate, Wallet, SyncOptions};
-use bdk::database::MemoryDatabase;
use bdk::blockchain::ElectrumBlockchain;
use bdk::electrum_client::Client;
fn main() -> Result<(), bdk::Error> {
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
- let wallet = Wallet::new(
+ let wallet = Wallet::new_no_persist(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet,
- MemoryDatabase::default(),
)?;
wallet.sync(&blockchain, SyncOptions::default())?;
### Sign a transaction
```rust,no_run
-use bdk::{Wallet, SignOptions, database::MemoryDatabase};
+use bdk::{Wallet, SignOptions};
use base64;
use bdk::bitcoin::consensus::deserialize;
use bdk::bitcoin::Network;
fn main() -> Result<(), bdk::Error> {
- let wallet = Wallet::new(
+ let wallet = Wallet::new_no_persist(
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
Network::Testnet,
- MemoryDatabase::default(),
)?;
let psbt = "...";
info!("Compiled into following Descriptor: \n{}", descriptor);
// Create a new wallet from this descriptor
- let wallet = Wallet::new(&format!("{}", descriptor), None, Network::Regtest)?;
+ let mut wallet = Wallet::new_no_persist(&format!("{}", descriptor), None, Network::Regtest)?;
info!(
"First derived address from the descriptor: \n{}",
+++ /dev/null
-use std::str::FromStr;
-
-use bdk::bitcoin::util::bip32::ExtendedPrivKey;
-use bdk::bitcoin::Network;
-use bdk::blockchain::{Blockchain, ElectrumBlockchain};
-use bdk::database::MemoryDatabase;
-use bdk::template::Bip84;
-use bdk::wallet::export::FullyNodedExport;
-use bdk::{KeychainKind, SyncOptions, Wallet};
-
-use bdk::electrum_client::Client;
-use bdk::wallet::AddressIndex;
-use bitcoin::util::bip32;
-
-pub mod utils;
-
-use crate::utils::tx::build_signed_tx;
-
-/// This will create a wallet from an xpriv and get the balance by connecting to an Electrum server.
-/// If enough amount is available, this will send a transaction to an address.
-/// Otherwise, this will display a wallet address to receive funds.
-///
-/// This can be run with `cargo run --example electrum_backend` in the root folder.
-fn main() {
- let network = Network::Testnet;
-
- let xpriv = "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy";
-
- let electrum_url = "ssl://electrum.blockstream.info:60002";
-
- run(&network, electrum_url, xpriv);
-}
-
-fn create_wallet(network: &Network, xpriv: &ExtendedPrivKey) -> Wallet<MemoryDatabase> {
- Wallet::new(
- Bip84(*xpriv, KeychainKind::External),
- Some(Bip84(*xpriv, KeychainKind::Internal)),
- *network,
- MemoryDatabase::default(),
- )
- .unwrap()
-}
-
-fn run(network: &Network, electrum_url: &str, xpriv: &str) {
- let xpriv = bip32::ExtendedPrivKey::from_str(xpriv).unwrap();
-
- // Apparently it works only with Electrs (not EletrumX)
- let blockchain = ElectrumBlockchain::from(Client::new(electrum_url).unwrap());
-
- let wallet = create_wallet(network, &xpriv);
-
- wallet.sync(&blockchain, SyncOptions::default()).unwrap();
-
- let address = wallet.get_address(AddressIndex::New).unwrap().address;
-
- println!("address: {}", address);
-
- let balance = wallet.get_balance().unwrap();
-
- println!("Available coins in BDK wallet : {} sats", balance);
-
- if balance.confirmed > 6500 {
- // the wallet sends the amount to itself.
- let recipient_address = wallet
- .get_address(AddressIndex::New)
- .unwrap()
- .address
- .to_string();
-
- let amount = 5359;
-
- let tx = build_signed_tx(&wallet, &recipient_address, amount);
-
- blockchain.broadcast(&tx).unwrap();
-
- println!("tx id: {}", tx.txid());
- } else {
- println!("Insufficient Funds. Fund the wallet with the address above");
- }
-
- let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true)
- .map_err(ToString::to_string)
- .map_err(bdk::Error::Generic)
- .unwrap();
-
- println!("------\nWallet Backup: {}", export.to_string());
-}
+++ /dev/null
-use bdk::{
- blockchain::esplora::{esplora_client, BlockingClientExt},
- wallet::AddressIndex,
- Wallet,
-};
-use bdk_test_client::{RpcApi, TestClient};
-use bitcoin::{Amount, Network};
-use rand::Rng;
-use std::error::Error;
-
-fn main() -> Result<(), Box<dyn Error>> {
- let _ = env_logger::init();
- const DESCRIPTOR: &'static str ="tr([73c5da0a/86'/0'/0']tprv8cSrHfiTQQWzKVejDHvBcvW4pdLEDLMvtVdbUXFfceQ4kbZKMsuFWbd3LUN3omNrQfafQaPwXUFXtcofkE9UjFZ3i9deezBHQTGvYV2xUzz/0/*)";
- const CHANGE_DESCRIPTOR: &'static str = "tr(tprv8ZgxMBicQKsPeQe98SGJ53vEJ7MNEFkQ4CkZmrr6PNom3vn6GqxuyoE78smkzpuP347zR9MXPg38PoZ8tbxLqSx4CufufHAGbQ9Hf7yTTwn/44'/0'/0'/1/*)#pxy2d75a";
-
- let mut test_client = TestClient::default();
- let esplora_url = format!(
- "http://{}",
- test_client.electrsd.esplora_url.as_ref().unwrap()
- );
- let client = esplora_client::Builder::new(&esplora_url).build_blocking()?;
-
- let wallet = Wallet::new(DESCRIPTOR, Some(CHANGE_DESCRIPTOR), Network::Regtest)
- .expect("parsing descriptors failed");
- // note we don't *need* the Mutex for this example but it helps to show when the wallet does and
- // doesn't need to be mutablek
- let wallet = std::sync::Mutex::new(wallet);
- let n_initial_transactions = 10;
-
- let addresses = {
- // we need it to be mutable to get a new address.
- // This incremenents the derivatoin index of the keychain.
- let mut wallet = wallet.lock().unwrap();
- core::iter::repeat_with(|| wallet.get_address(AddressIndex::New))
- .filter(|_| rand::thread_rng().gen_bool(0.5))
- .take(n_initial_transactions)
- .collect::<Vec<_>>()
- };
-
- // get some coins for the internal node
- test_client.generate(100, None);
-
- for address in addresses {
- let exp_txid = test_client
- .send_to_address(
- &address,
- Amount::from_sat(10_000),
- None,
- None,
- None,
- None,
- None,
- None,
- )
- .expect("tx should send");
- eprintln!(
- "💸 sending some coins to: {} (index {}) in tx {}",
- address, address.index, exp_txid
- );
- // sometimes generate a block after we send coins to the address
- if rand::thread_rng().gen_bool(0.3) {
- let height = test_client.generate(1, None);
- eprintln!("📦 created a block at height {}", height);
- }
- }
-
- let wait_for_esplora_sync = std::time::Duration::from_secs(5);
-
- println!("⏳ waiting {}s for esplora to catch up..", wait_for_esplora_sync.as_secs());
- std::thread::sleep(wait_for_esplora_sync);
-
-
- let wallet_scan_input = {
- let wallet = wallet.lock().unwrap();
- wallet.start_wallet_scan()
- };
-
- let start = std::time::Instant::now();
- let stop_gap = 5;
- eprintln!(
- "🔎 startig scanning all keychains with stop gap of {}",
- stop_gap
- );
- let wallet_scan = client.wallet_scan(wallet_scan_input, stop_gap, &Default::default(), 5)?;
-
- // we've got an update so briefly take a lock the wallet to apply it
- {
- let mut wallet = wallet.lock().unwrap();
- match wallet.apply_wallet_scan(wallet_scan) {
- Ok(changes) => {
- eprintln!("🎉 success! ({}ms)", start.elapsed().as_millis());
- eprintln!("wallet balance after: {:?}", wallet.get_balance());
- //XXX: esplora is not indexing mempool transactions right now (or not doing it fast enough)
- eprintln!(
- "wallet found {} new transactions",
- changes.tx_additions().count(),
- );
- if changes.tx_additions().count() != n_initial_transactions {
- eprintln!(
- "(it should have found {} but maybe stop gap wasn't large enough?)",
- n_initial_transactions
- );
- }
- }
- Err(reason) => {
- eprintln!("❌ esplora produced invalid wallet scan {}", reason);
- }
- }
- }
-
- Ok(())
-}
+++ /dev/null
-use std::str::FromStr;
-
-use bdk::blockchain::Blockchain;
-use bdk::{
- blockchain::esplora::EsploraBlockchain,
- database::MemoryDatabase,
- template::Bip84,
- wallet::{export::FullyNodedExport, AddressIndex},
- KeychainKind, SyncOptions, Wallet,
-};
-use bitcoin::{
- util::bip32::{self, ExtendedPrivKey},
- Network,
-};
-
-pub mod utils;
-
-use crate::utils::tx::build_signed_tx;
-
-/// This will create a wallet from an xpriv and get the balance by connecting to an Esplora server,
-/// using non blocking asynchronous calls with `reqwest`.
-/// If enough amount is available, this will send a transaction to an address.
-/// Otherwise, this will display a wallet address to receive funds.
-///
-/// This can be run with `cargo run --no-default-features --features="use-esplora-reqwest, reqwest-default-tls, async-interface" --example esplora_backend_asynchronous`
-/// in the root folder.
-#[tokio::main(flavor = "current_thread")]
-async fn main() {
- let network = Network::Signet;
-
- let xpriv = "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy";
-
- let esplora_url = "https://explorer.bc-2.jp/api";
-
- run(&network, esplora_url, xpriv).await;
-}
-
-fn create_wallet(network: &Network, xpriv: &ExtendedPrivKey) -> Wallet<MemoryDatabase> {
- Wallet::new(
- Bip84(*xpriv, KeychainKind::External),
- Some(Bip84(*xpriv, KeychainKind::Internal)),
- *network,
- MemoryDatabase::default(),
- )
- .unwrap()
-}
-
-async fn run(network: &Network, esplora_url: &str, xpriv: &str) {
- let xpriv = bip32::ExtendedPrivKey::from_str(xpriv).unwrap();
-
- let blockchain = EsploraBlockchain::new(esplora_url, 20);
-
- let wallet = create_wallet(network, &xpriv);
-
- wallet
- .sync(&blockchain, SyncOptions::default())
- .await
- .unwrap();
-
- let address = wallet.get_address(AddressIndex::New).unwrap().address;
-
- println!("address: {}", address);
-
- let balance = wallet.get_balance().unwrap();
-
- println!("Available coins in BDK wallet : {} sats", balance);
-
- if balance.confirmed > 10500 {
- // the wallet sends the amount to itself.
- let recipient_address = wallet
- .get_address(AddressIndex::New)
- .unwrap()
- .address
- .to_string();
-
- let amount = 9359;
-
- let tx = build_signed_tx(&wallet, &recipient_address, amount);
-
- let _ = blockchain.broadcast(&tx);
-
- println!("tx id: {}", tx.txid());
- } else {
- println!("Insufficient Funds. Fund the wallet with the address above");
- }
-
- let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true)
- .map_err(ToString::to_string)
- .map_err(bdk::Error::Generic)
- .unwrap();
-
- println!("------\nWallet Backup: {}", export.to_string());
-}
+++ /dev/null
-use std::str::FromStr;
-
-use bdk::blockchain::Blockchain;
-use bdk::{
- blockchain::esplora::EsploraBlockchain,
- database::MemoryDatabase,
- template::Bip84,
- wallet::{export::FullyNodedExport, AddressIndex},
- KeychainKind, SyncOptions, Wallet,
-};
-use bitcoin::{
- util::bip32::{self, ExtendedPrivKey},
- Network,
-};
-
-pub mod utils;
-
-use crate::utils::tx::build_signed_tx;
-
-/// This will create a wallet from an xpriv and get the balance by connecting to an Esplora server,
-/// using blocking calls with `ureq`.
-/// If enough amount is available, this will send a transaction to an address.
-/// Otherwise, this will display a wallet address to receive funds.
-///
-/// This can be run with `cargo run --features=use-esplora-ureq --example esplora_backend_synchronous`
-/// in the root folder.
-fn main() {
- let network = Network::Signet;
-
- let xpriv = "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy";
-
- let esplora_url = "https://explorer.bc-2.jp/api";
-
- run(&network, esplora_url, xpriv);
-}
-
-fn create_wallet(network: &Network, xpriv: &ExtendedPrivKey) -> Wallet<MemoryDatabase> {
- Wallet::new(
- Bip84(*xpriv, KeychainKind::External),
- Some(Bip84(*xpriv, KeychainKind::Internal)),
- *network,
- MemoryDatabase::default(),
- )
- .unwrap()
-}
-
-fn run(network: &Network, esplora_url: &str, xpriv: &str) {
- let xpriv = bip32::ExtendedPrivKey::from_str(xpriv).unwrap();
-
- let blockchain = EsploraBlockchain::new(esplora_url, 20);
-
- let wallet = create_wallet(network, &xpriv);
-
- wallet.sync(&blockchain, SyncOptions::default()).unwrap();
-
- let address = wallet.get_address(AddressIndex::New).unwrap().address;
-
- println!("address: {}", address);
-
- let balance = wallet.get_balance().unwrap();
-
- println!("Available coins in BDK wallet : {} sats", balance);
-
- if balance.confirmed > 10500 {
- // the wallet sends the amount to itself.
- let recipient_address = wallet
- .get_address(AddressIndex::New)
- .unwrap()
- .address
- .to_string();
-
- let amount = 9359;
-
- let tx = build_signed_tx(&wallet, &recipient_address, amount);
-
- blockchain.broadcast(&tx).unwrap();
-
- println!("tx id: {}", tx.txid());
- } else {
- println!("Insufficient Funds. Fund the wallet with the address above");
- }
-
- let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true)
- .map_err(ToString::to_string)
- .map_err(bdk::Error::Generic)
- .unwrap();
-
- println!("------\nWallet Backup: {}", export.to_string());
-}
use bdk::bitcoin::{Address, Network};
use bdk::blockchain::{Blockchain, ElectrumBlockchain};
-use bdk::database::MemoryDatabase;
use bdk::hwi::{types::HWIChain, HWIClient};
use bdk::miniscript::{Descriptor, DescriptorPublicKey};
use bdk::signer::SignerOrdering;
// Creating a custom signer from the device
let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?;
- let mut wallet = Wallet::new(
+ let mut wallet = Wallet::new_no_persist(
descriptors.receive[0].clone(),
Some(descriptors.internal[0].clone()),
Network::Testnet,
+++ /dev/null
-// Copyright (c) 2020-2022 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-use bdk::blockchain::{Blockchain, ElectrumBlockchain};
-use bdk::database::MemoryDatabase;
-use bdk::wallet::AddressIndex;
-use bdk::{descriptor, SyncOptions};
-use bdk::{FeeRate, SignOptions, Wallet};
-use bitcoin::secp256k1::Secp256k1;
-use bitcoin::{Address, Network};
-use electrum_client::Client;
-use miniscript::descriptor::DescriptorSecretKey;
-use std::error::Error;
-use std::str::FromStr;
-
-/// This example shows how to sign and broadcast the transaction for a PSBT (Partially Signed
-/// Bitcoin Transaction) for a single key, witness public key hash (WPKH) based descriptor wallet.
-/// The electrum protocol is used to sync blockchain data from the testnet bitcoin network and
-/// wallet data is stored in an ephemeral in-memory database. The process steps are:
-/// 1. Create a "signing" wallet and a "watch-only" wallet based on the same private keys.
-/// 2. Deposit testnet funds into the watch only wallet.
-/// 3. Sync the watch only wallet and create a spending transaction to return all funds to the testnet faucet.
-/// 4. Sync the signing wallet and sign and finalize the PSBT created by the watch only wallet.
-/// 5. Broadcast the transactions from the finalized PSBT.
-fn main() -> Result<(), Box<dyn Error>> {
- // test key created with `bdk-cli key generate` and `bdk-cli key derive` commands
- let external_secret_xkey = DescriptorSecretKey::from_str("[e9824965/84'/1'/0']tprv8fvem7qWxY3SGCQczQpRpqTKg455wf1zgixn6MZ4ze8gRfHjov5gXBQTadNfDgqs9ERbZZ3Bi1PNYrCCusFLucT39K525MWLpeURjHwUsfX/0/*").unwrap();
- let internal_secret_xkey = DescriptorSecretKey::from_str("[e9824965/84'/1'/0']tprv8fvem7qWxY3SGCQczQpRpqTKg455wf1zgixn6MZ4ze8gRfHjov5gXBQTadNfDgqs9ERbZZ3Bi1PNYrCCusFLucT39K525MWLpeURjHwUsfX/1/*").unwrap();
-
- let secp = Secp256k1::new();
- let external_public_xkey = external_secret_xkey.to_public(&secp).unwrap();
- let internal_public_xkey = internal_secret_xkey.to_public(&secp).unwrap();
-
- let signing_external_descriptor = descriptor!(wpkh(external_secret_xkey)).unwrap();
- let signing_internal_descriptor = descriptor!(wpkh(internal_secret_xkey)).unwrap();
-
- let watch_only_external_descriptor = descriptor!(wpkh(external_public_xkey)).unwrap();
- let watch_only_internal_descriptor = descriptor!(wpkh(internal_public_xkey)).unwrap();
-
- // create client for Blockstream's testnet electrum server
- let blockchain =
- ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
-
- // create watch only wallet
- let watch_only_wallet: Wallet = Wallet::new(
- watch_only_external_descriptor,
- Some(watch_only_internal_descriptor),
- Network::Testnet,
- )?;
-
- // create signing wallet
- let signing_wallet: Wallet = Wallet::new(
- signing_external_descriptor,
- Some(signing_internal_descriptor),
- Network::Testnet,
- )?;
-
- println!("Syncing watch only wallet.");
- watch_only_wallet.sync(&blockchain, SyncOptions::default())?;
-
- // get deposit address
- let deposit_address = watch_only_wallet.get_address(AddressIndex::New);
-
- let balance = watch_only_wallet.get_balance()?;
- println!("Watch only wallet balances in SATs: {}", balance);
-
- if balance.get_total() < 10000 {
- println!(
- "Send at least 10000 SATs (0.0001 BTC) from the u01.net testnet faucet to address '{addr}'.\nFaucet URL: https://bitcoinfaucet.uo1.net/?to={addr}",
- addr = deposit_address.address
- );
- } else if balance.get_spendable() < 10000 {
- println!(
- "Wait for at least 10000 SATs of your wallet transactions to be confirmed...\nBe patient, this could take 10 mins or longer depending on how testnet is behaving."
- );
- for tx_details in watch_only_wallet
- .transactions()
- .iter()
- .filter(|txd| txd.received > 0 && txd.confirmation_time.is_none())
- {
- println!(
- "See unconfirmed tx for {} SATs: https://mempool.space/testnet/tx/{}",
- tx_details.received, tx_details.txid
- );
- }
- } else {
- println!("Creating a PSBT sending 9800 SATs plus fee to the u01.net testnet faucet return address 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt'.");
- let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?;
- let mut builder = watch_only_wallet.build_tx();
- builder
- .add_recipient(return_address.script_pubkey(), 9_800)
- .enable_rbf()
- .fee_rate(FeeRate::from_sat_per_vb(1.0));
-
- let (mut psbt, details) = builder.finish()?;
- println!("Transaction details: {:#?}", details);
- println!("Unsigned PSBT: {}", psbt);
-
- // Sign and finalize the PSBT with the signing wallet
- let finalized = signing_wallet.sign(&mut psbt, SignOptions::default())?;
- assert!(finalized, "The PSBT was not finalized!");
- println!("The PSBT has been signed and finalized.");
-
- // Broadcast the transaction
- let raw_transaction = psbt.extract_tx();
- let txid = raw_transaction.txid();
-
- blockchain.broadcast(&raw_transaction)?;
- println!("Transaction broadcast! TXID: {txid}.\nExplorer URL: https://mempool.space/testnet/tx/{txid}", txid = txid);
- }
-
- Ok(())
-}
use std::str::FromStr;
- use bdk::{database::BatchDatabase, SignOptions, Wallet};
+ use bdk::{SignOptions, Wallet, persist};
use bitcoin::{Address, Transaction};
- pub fn build_signed_tx<D: BatchDatabase>(
- wallet: &Wallet<D>,
+ pub fn build_signed_tx<()>(
+ wallet: &Wallet<()>,
recipient_address: &str,
amount: u64,
) -> Transaction {
let to_address = Address::from_str(recipient_address).unwrap();
+
// Set recipient of the transaction
tx_builder.set_recipients(vec![(to_address.script_pubkey(), amount)]);
a_keymap.extend(b_keymap.into_iter());
let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
- alloc::sync::Arc::new(a_minisc),
- alloc::sync::Arc::new(b_minisc),
+ $crate::alloc::sync::Arc::new(a_minisc),
+ $crate::alloc::sync::Arc::new(b_minisc),
))?;
minisc.check_miniscript()?;
let networks = $crate::keys::merge_networks(&networks, &c_networks);
let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
- alloc::sync::Arc::new(a_minisc),
- alloc::sync::Arc::new(b_minisc),
- alloc::sync::Arc::new(c_minisc),
+ $crate::alloc::sync::Arc::new(a_minisc),
+ $crate::alloc::sync::Arc::new(b_minisc),
+ $crate::alloc::sync::Arc::new(c_minisc),
))?;
minisc.check_miniscript()?;
)*
];
- keys.into_iter().collect::<Result<alloc::vec::Vec<_>, _>>()
+ keys.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
.map_err($crate::descriptor::DescriptorError::Key)
.and_then(|keys| $crate::keys::make_sortedmulti($thresh, keys, $build_desc, &secp))
});
#[macro_export]
macro_rules! parse_tap_tree {
( @merge $tree_a:expr, $tree_b:expr) => {{
- use alloc::sync::Arc;
+ use $crate::alloc::sync::Arc;
use $crate::miniscript::descriptor::TapTree;
$tree_a
// Single leaf
( $op:ident ( $( $minisc:tt )* ) ) => {{
- use alloc::sync::Arc;
+ use $crate::alloc::sync::Arc;
use $crate::miniscript::descriptor::TapTree;
$crate::fragment!( $op ( $( $minisc )* ) )
.and_then(|(minisc, keymap, networks)| {
let minisc = $crate::miniscript::Miniscript::from_ast(
$crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
- alloc::sync::Arc::new(minisc),
+ $crate::alloc::sync::Arc::new(minisc),
),
)?;
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
$crate::impl_leaf_opcode_value_two!(
AndV,
- alloc::sync::Arc::new(a_minisc),
- alloc::sync::Arc::new($crate::fragment!(true).unwrap().0)
+ $crate::alloc::sync::Arc::new(a_minisc),
+ $crate::alloc::sync::Arc::new($crate::fragment!(true).unwrap().0)
)
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
})
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
$crate::impl_leaf_opcode_value_two!(
OrI,
- alloc::sync::Arc::new($crate::fragment!(false).unwrap().0),
- alloc::sync::Arc::new(a_minisc)
+ $crate::alloc::sync::Arc::new($crate::fragment!(false).unwrap().0),
+ $crate::alloc::sync::Arc::new(a_minisc)
)
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
})
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
$crate::impl_leaf_opcode_value_two!(
OrI,
- alloc::sync::Arc::new(a_minisc),
- alloc::sync::Arc::new($crate::fragment!(false).unwrap().0)
+ $crate::alloc::sync::Arc::new(a_minisc),
+ $crate::alloc::sync::Arc::new($crate::fragment!(false).unwrap().0)
)
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
})
)*
];
- keys.into_iter().collect::<Result<alloc::vec::Vec<_>, _>>()
+ keys.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
.map_err($crate::descriptor::DescriptorError::Key)
}};
}
( thresh_vec ( $thresh:expr, $items:expr ) ) => ({
use $crate::miniscript::descriptor::KeyMap;
- let (items, key_maps_networks): (alloc::vec::Vec<_>, alloc::vec::Vec<_>) = $items.into_iter().map(|(a, b, c)| (a, (b, c))).unzip();
- let items = items.into_iter().map(alloc::sync::Arc::new).collect();
+ let (items, key_maps_networks): ($crate::alloc::vec::Vec<_>, $crate::alloc::vec::Vec<_>) = $items.into_iter().map(|(a, b, c)| (a, (b, c))).unzip();
+ let items = items.into_iter().map($crate::alloc::sync::Arc::new).collect();
let (key_maps, valid_networks) = key_maps_networks.into_iter().fold((KeyMap::default(), $crate::keys::any_network()), |(mut keys_acc, net_acc), (key, net)| {
keys_acc.extend(key.into_iter());
( thresh ( $thresh:expr, $( $inner:tt )* ) ) => ({
let items = $crate::fragment_internal!( @v $( $inner )* );
- items.into_iter().collect::<Result<alloc::vec::Vec<_>, _>>()
+ items.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
.and_then(|items| $crate::fragment!(thresh_vec($thresh, items)))
});
( multi_vec ( $thresh:expr, $keys:expr ) ) => ({
///
/// ```
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet};
-/// # use bdk::database::MemoryDatabase;
+/// # use bdk::Wallet;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::P2Pkh;
///
/// let key =
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let wallet = Wallet::new(
-/// P2Pkh(key),
-/// None,
-/// Network::Testnet,
-/// MemoryDatabase::default(),
-/// )?;
+/// let mut wallet = Wallet::new_no_persist(P2Pkh(key), None, Network::Testnet)?;
///
/// assert_eq!(
/// wallet.get_address(New).to_string(),
///
/// ```
/// # use bdk::bitcoin::{PrivateKey, Network};
-/// # use bdk::{Wallet};
-/// # use bdk::database::MemoryDatabase;
-/// # use bdk::wallet::AddressIndex::New;
+/// # use bdk::Wallet;
/// use bdk::template::P2Wpkh_P2Sh;
+/// use bdk::wallet::AddressIndex;
///
/// let key =
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let wallet = Wallet::new(
-/// P2Wpkh_P2Sh(key),
-/// None,
-/// Network::Testnet,
-/// MemoryDatabase::default(),
-/// )?;
+/// let mut wallet = Wallet::new_no_persist(P2Wpkh_P2Sh(key), None, Network::Testnet)?;
///
/// assert_eq!(
-/// wallet.get_address(New).to_string(),
+/// wallet.get_address(AddressIndex::New).to_string(),
/// "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"
/// );
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet};
-/// # use bdk::database::MemoryDatabase;
-/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::P2Wpkh;
+/// use bdk::wallet::AddressIndex::New;
///
/// let key =
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let wallet = Wallet::new(
-/// P2Wpkh(key),
-/// None,
-/// Network::Testnet,
-/// MemoryDatabase::default(),
-/// )?;
+/// let mut wallet = Wallet::new_no_persist(P2Wpkh(key), None, Network::Testnet)?;
///
/// assert_eq!(
/// wallet.get_address(New).to_string(),
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet, KeychainKind};
-/// # use bdk::database::MemoryDatabase;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip44;
///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let wallet = Wallet::new(
+/// let mut wallet = Wallet::new_no_persist(
/// Bip44(key.clone(), KeychainKind::External),
/// Some(Bip44(key, KeychainKind::Internal)),
/// Network::Testnet,
-/// MemoryDatabase::default()
/// )?;
///
/// assert_eq!(wallet.get_address(New).to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDCuorCpzvYS2LCD75BR46KHE8GdDeg1wsAgNZeNr6DaB5gQK1o14uErKwKLuFmeemkQ6N2m3rNgvctdJLyr7nwu2yia7413Hhg8WWE44cgT/0/*)#5wrnv0xt");
+/// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDCuorCpzvYS2LCD75BR46KHE8GdDeg1wsAgNZeNr6DaB5gQK1o14uErKwKLuFmeemkQ6N2m3rNgvctdJLyr7nwu2yia7413Hhg8WWE44cgT/0/*)#5wrnv0xt");
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet, KeychainKind};
-/// # use bdk::database::MemoryDatabase;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip44Public;
///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
-/// let wallet = Wallet::new(
+/// let mut wallet = Wallet::new_no_persist(
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
/// Network::Testnet,
-/// MemoryDatabase::default()
/// )?;
///
/// assert_eq!(wallet.get_address(New).to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#xgaaevjx");
+/// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz");
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet, KeychainKind};
-/// # use bdk::database::MemoryDatabase;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip49;
///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let wallet = Wallet::new(
+/// let mut wallet = Wallet::new_no_persist(
/// Bip49(key.clone(), KeychainKind::External),
/// Some(Bip49(key, KeychainKind::Internal)),
/// Network::Testnet,
-/// MemoryDatabase::default()
/// )?;
///
/// assert_eq!(wallet.get_address(New).to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDDYr4kdnZgjjShzYNjZUZXUUtpXaofdkMaipyS8ThEh45qFmhT4hKYways7UXmg6V7het1QiFo9kf4kYUXyDvV4rHEyvSpys9pjCB3pukxi/0/*))#s9vxlc8e");
+/// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDDYr4kdnZgjjShzYNjZUZXUUtpXaofdkMaipyS8ThEh45qFmhT4hKYways7UXmg6V7het1QiFo9kf4kYUXyDvV4rHEyvSpys9pjCB3pukxi/0/*))#s9vxlc8e");
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet, KeychainKind};
-/// # use bdk::database::MemoryDatabase;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip49Public;
///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
-/// let wallet = Wallet::new(
+/// let mut wallet = Wallet::new_no_persist(
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
/// Network::Testnet,
-/// MemoryDatabase::default()
/// )?;
///
/// assert_eq!(wallet.get_address(New).to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#gsmdv4xr");
+/// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q");
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet, KeychainKind};
-/// # use bdk::database::MemoryDatabase;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip84;
///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let wallet = Wallet::new(
+/// let mut wallet = Wallet::new_no_persist(
/// Bip84(key.clone(), KeychainKind::External),
/// Some(Bip84(key, KeychainKind::Internal)),
/// Network::Testnet,
-/// MemoryDatabase::default()
/// )?;
///
/// assert_eq!(wallet.get_address(New).to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDDc5mum24DekpNw92t6fHGp8Gr2JjF9J7i4TZBtN6Vp8xpAULG5CFaKsfugWa5imhrQQUZKXe261asP5koDHo5bs3qNTmf3U3o4v9SaB8gg/0/*)#6kfecsmr");
+/// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDDc5mum24DekpNw92t6fHGp8Gr2JjF9J7i4TZBtN6Vp8xpAULG5CFaKsfugWa5imhrQQUZKXe261asP5koDHo5bs3qNTmf3U3o4v9SaB8gg/0/*)#6kfecsmr");
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
/// # use std::str::FromStr;
/// # use bdk::bitcoin::{PrivateKey, Network};
/// # use bdk::{Wallet, KeychainKind};
-/// # use bdk::database::MemoryDatabase;
/// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip84Public;
///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
-/// let wallet = Wallet::new(
+/// let mut wallet = Wallet::new_no_persist(
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
/// Network::Testnet,
-/// MemoryDatabase::default()
/// )?;
///
/// assert_eq!(wallet.get_address(New).to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#nkk5dtkg");
+/// assert_eq!(wallet.public_descriptor(KeychainKind::External).unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv");
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
//! * It is built to be cross-platform: the core logic works on desktop, mobile, and even WebAssembly.
//! * It is very easy to extend: developers can implement customized logic for blockchain backends, databases, signers, coin selection, and more, without having to fork and modify this library.
//!
-//! # A Tour of BDK
-//!
-//! BDK consists of a number of modules that provide a range of functionality
-//! essential for implementing descriptor based Bitcoin wallet applications in Rust. In this
-//! section, we will take a brief tour of BDK, summarizing the major APIs and
-//! their uses.
-//!
-//! The easiest way to get started is to add bdk to your dependencies with the default features.
-//! The default features include a simple key-value database ([`sled`](sled)) to cache
-//! blockchain data and an [electrum](https://docs.rs/electrum-client/) blockchain client to
-//! interact with the bitcoin P2P network.
-//!
-//! # Examples
-#![cfg_attr(
- feature = "electrum",
- doc = r##"
-## Sync the balance of a descriptor
-
-```no_run
-use bdk::{Wallet, SyncOptions};
-use bdk::database::MemoryDatabase;
-use bdk::blockchain::ElectrumBlockchain;
-use bdk::electrum_client::Client;
-
-fn main() -> Result<(), bdk::Error> {
- let client = Client::new("ssl://electrum.blockstream.info:60002")?;
- let blockchain = ElectrumBlockchain::from(client);
- let wallet = Wallet::new(
- "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
- Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
- bitcoin::Network::Testnet,
- MemoryDatabase::default(),
- )?;
-
- wallet.sync(&blockchain, SyncOptions::default())?;
-
- println!("Descriptor balance: {} SAT", wallet.get_balance()?);
-
- Ok(())
-}
-```
-"##
-)]
-//!
//! ## Generate a few addresses
//!
//! ### Example
//! ```
//! use bdk::{Wallet};
-//! use bdk::database::MemoryDatabase;
//! use bdk::wallet::AddressIndex::New;
//!
//! fn main() -> Result<(), bdk::Error> {
-//! let wallet = Wallet::new(
-//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
-//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
-//! bitcoin::Network::Testnet,
-//! MemoryDatabase::default(),
+//! let mut wallet = Wallet::new_no_persist(
+//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
+//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
+//! bitcoin::Network::Testnet,
//! )?;
//!
//! println!("Address #0: {}", wallet.get_address(New));
//! Ok(())
//! }
//! ```
-#![cfg_attr(
- feature = "electrum",
- doc = r##"
-## Create a transaction
-
-```no_run
-use bdk::{FeeRate, Wallet, SyncOptions};
-use bdk::database::MemoryDatabase;
-use bdk::blockchain::ElectrumBlockchain;
-use bdk::electrum_client::Client;
-
-use bitcoin::consensus::serialize;
-use bdk::wallet::AddressIndex::New;
-
-fn main() -> Result<(), bdk::Error> {
- let client = Client::new("ssl://electrum.blockstream.info:60002")?;
- let wallet = Wallet::new(
- "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
- Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
- bitcoin::Network::Testnet,
- MemoryDatabase::default(),
- )?;
- let blockchain = ElectrumBlockchain::from(client);
-
- wallet.sync(&blockchain, SyncOptions::default())?;
-
- let send_to = wallet.get_address(New);
- let (psbt, details) = {
- let mut builder = wallet.build_tx();
- builder
- .add_recipient(send_to.script_pubkey(), 50_000)
- .enable_rbf()
- .do_not_spend_change()
- .fee_rate(FeeRate::from_sat_per_vb(5.0));
- builder.finish()?
- };
-
- println!("Transaction details: {:#?}", details);
- println!("Unsigned PSBT: {}", &psbt);
-
- Ok(())
-}
-```
-"##
-)]
-//!
//! ## Sign a transaction
//!
//! ```no_run
//! use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
//!
//! use bdk::{Wallet, SignOptions};
-//! use bdk::database::MemoryDatabase;
//!
//! fn main() -> Result<(), bdk::Error> {
-//! let wallet = Wallet::new(
+//! let wallet = Wallet::new_no_persist(
//! "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
//! Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
//! bitcoin::Network::Testnet,
-//! MemoryDatabase::default(),
//! )?;
//!
//! let psbt = "...";
#[macro_use]
extern crate std;
+#[doc(hidden)]
#[macro_use]
-extern crate alloc;
+pub extern crate alloc;
pub extern crate bitcoin;
#[cfg(feature = "hardware-signer")]
//! # use std::str::FromStr;
//! # use bitcoin::*;
//! # use bdk::wallet::{self, coin_selection::*};
-//! # use bdk::database::Database;
//! # use bdk::*;
//! # use bdk::wallet::coin_selection::decide_change;
//! # const TXIN_BASE_WEIGHT: usize = (32 + 4 + 4) * 4;
//! impl CoinSelectionAlgorithm for AlwaysSpendEverything {
//! fn coin_select(
//! &self,
-//! database: &D,
//! required_utxos: Vec<WeightedUtxo>,
//! optional_utxos: Vec<WeightedUtxo>,
//! fee_rate: FeeRate,
//! }
//! }
//!
-//! # let wallet = doctest_wallet!();
+//! # let mut wallet = doctest_wallet!();
//! // create wallet, sync, ...
//!
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
//! ```
//! # use std::str::FromStr;
//! # use bitcoin::*;
-//! # use bdk::database::*;
//! # use bdk::wallet::export::*;
//! # use bdk::*;
//! let import = r#"{
//! }"#;
//!
//! let import = FullyNodedExport::from_str(import)?;
-//! let wallet = Wallet::new(
+//! let wallet = Wallet::new_no_persist(
//! &import.descriptor(),
//! import.change_descriptor().as_ref(),
//! Network::Testnet,
-//! MemoryDatabase::default(),
//! )?;
//! # Ok::<_, bdk::Error>(())
//! ```
//! ### Export a `Wallet`
//! ```
//! # use bitcoin::*;
-//! # use bdk::database::*;
//! # use bdk::wallet::export::*;
//! # use bdk::*;
-//! let wallet = Wallet::new(
+//! let wallet = Wallet::new_no_persist(
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
//! Network::Testnet,
-//! MemoryDatabase::default()
//! )?;
//! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true)
//! .map_err(ToString::to_string)
change_descriptor: Option<&str>,
network: Network,
) -> Wallet<()> {
- let mut wallet = Wallet::new(descriptor, change_descriptor, (), network).unwrap();
+ let mut wallet = Wallet::new_no_persist(descriptor, change_descriptor, network).unwrap();
let transaction = Transaction {
input: vec![],
output: vec![],
//! used with hardware wallets.
//! ```no_run
//! # use bdk::bitcoin::Network;
-//! # use bdk::database::MemoryDatabase;
//! # use bdk::signer::SignerOrdering;
//! # use bdk::wallet::hardwaresigner::HWISigner;
//! # use bdk::wallet::AddressIndex::New;
//! let first_device = devices.remove(0)?;
//! let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?;
//!
-//! # let mut wallet = Wallet::new(
+//! # let mut wallet = Wallet::new_no_persist(
//! # "",
//! # None,
//! # Network::Testnet,
-//! # MemoryDatabase::default(),
//! # )?;
//! #
//! // Adding the hardware signer to the BDK wallet
keychain::{KeychainChangeSet, KeychainScan, KeychainTracker},
sparse_chain, BlockId, ConfirmationTime, IntoOwned,
};
-use bitcoin::secp256k1::Secp256k1;
-use core::fmt;
-use core::ops::Deref;
-
use bitcoin::consensus::encode::serialize;
+use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::psbt;
use bitcoin::{
Address, BlockHash, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script,
Sequence, Transaction, TxOut, Txid, Witness,
};
-
+use core::fmt;
+use core::ops::Deref;
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
#[allow(unused_imports)]
/// Its main components are:
///
/// 1. output *descriptors* from which it can derive addresses.
-/// 2. A [`Database`] where it tracks transactions and utxos related to the descriptors.
-/// 3. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
+/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
///
-/// [`Database`]: crate::database::Database
/// [`signer`]: crate::signer
#[derive(Debug)]
pub struct Wallet<D = ()> {
/// ```
/// # use bdk::{Wallet, KeychainKind};
/// # use bdk::bitcoin::Network;
- /// # use bdk::database::MemoryDatabase;
- /// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet, MemoryDatabase::new())?;
+ /// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet)?;
/// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
/// // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
/// println!("secret_key: {}", secret_key);
/// # use std::str::FromStr;
/// # use bitcoin::*;
/// # use bdk::*;
- /// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
- /// # let wallet = doctest_wallet!();
+ /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
/// let (psbt, details) = {
/// let mut builder = wallet.build_tx();
/// # use std::str::FromStr;
/// # use bitcoin::*;
/// # use bdk::*;
- /// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
- /// # let wallet = doctest_wallet!();
+ /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
/// let (mut psbt, _) = {
/// let mut builder = wallet.build_tx();
/// # use std::str::FromStr;
/// # use bitcoin::*;
/// # use bdk::*;
- /// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
- /// # let wallet = doctest_wallet!();
+ /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
/// let (mut psbt, _) = {
/// let mut builder = wallet.build_tx();
/// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
///
/// This frees up the change address used when creating the tx for use in future transactions.
- ///
// TODO: Make this free up reserved utxos when that's implemented
pub fn cancel_tx(&mut self, tx: &Transaction) {
let txout_index = &mut self.keychain_tracker.txout_index;
if keychain == KeychainKind::Internal
&& self.public_descriptor(KeychainKind::Internal).is_none()
{
- return KeychainKind::External;
+ KeychainKind::External
} else {
keychain
}
Ok(wallet_name)
}
+
+#[macro_export]
+#[doc(hidden)]
+/// Macro for getting a wallet for use in a doctest
+macro_rules! doctest_wallet {
+ () => {{
+ use $crate::bitcoin::{BlockHash, Transaction, PackedLockTime, TxOut, Network, hashes::Hash};
+ use $crate::chain::{ConfirmationTime, BlockId};
+ use $crate::wallet::{AddressIndex, Wallet};
+ let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
+ let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
+
+ let mut wallet = Wallet::new_no_persist(
+ descriptor,
+ Some(change_descriptor),
+ Network::Regtest,
+ )
+ .unwrap();
+ let address = wallet.get_address(AddressIndex::New).address;
+ let tx = Transaction {
+ version: 1,
+ lock_time: PackedLockTime(0),
+ input: vec![],
+ output: vec![TxOut {
+ value: 500_000,
+ script_pubkey: address.script_pubkey(),
+ }],
+ };
+ let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() });
+ let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed {
+ height: 500,
+ time: 50_000
+ });
+
+ wallet
+ }}
+}
//! # use bitcoin::*;
//! # use bitcoin::util::psbt;
//! # use bdk::signer::*;
-//! # use bdk::database::*;
//! # use bdk::*;
//! # #[derive(Debug)]
//! # struct CustomHSM;
//! let custom_signer = CustomSigner::connect();
//!
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
-//! let mut wallet = Wallet::new(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
+//! let mut wallet = Wallet::new_no_persist(descriptor, None, Network::Testnet)?;
//! wallet.add_signer(
//! KeychainKind::External,
//! SignerOrdering(200),
//! # use bdk::*;
//! # use bdk::wallet::tx_builder::CreateTx;
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
-//! # let wallet = doctest_wallet!();
+//! # let mut wallet = doctest_wallet!();
//! // create a TxBuilder from a wallet
//! let mut tx_builder = wallet.build_tx();
//!
/// # use bdk::wallet::tx_builder::*;
/// # use bitcoin::*;
/// # use core::str::FromStr;
-/// # let wallet = doctest_wallet!();
+/// # let mut wallet = doctest_wallet!();
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
/// # let addr2 = addr1.clone();
/// // chaining
/// # use bitcoin::*;
/// # use bdk::*;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
- /// # let wallet = doctest_wallet!();
+ /// # let mut wallet = doctest_wallet!();
/// let mut path = BTreeMap::new();
/// path.insert("aabbccdd".to_string(), vec![0, 1]);
///
/// # use bdk::*;
/// # use bdk::wallet::tx_builder::CreateTx;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
- /// # let wallet = doctest_wallet!();
+ /// # let mut wallet = doctest_wallet!();
/// let mut tx_builder = wallet.build_tx();
///
/// tx_builder