Make doctests work

This commit is contained in:
LLFourn 2023-02-21 12:38:16 +11:00 committed by Daniela Brozzoni
parent 3baf9721ec
commit e1eb0253cf
No known key found for this signature in database
GPG Key ID: 7DE4F1FDCED0AB87
18 changed files with 113 additions and 722 deletions

View File

@ -40,7 +40,6 @@ The `bdk` library aims to be the core building block for Bitcoin wallets of any
```rust,no_run
use bdk::Wallet;
use bdk::database::MemoryDatabase;
use bdk::blockchain::ElectrumBlockchain;
use bdk::SyncOptions;
use bdk::electrum_client::Client;
@ -52,7 +51,6 @@ fn main() -> Result<(), bdk::Error> {
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet,
MemoryDatabase::default(),
)?;
wallet.sync(&blockchain, SyncOptions::default())?;
@ -66,16 +64,15 @@ fn main() -> Result<(), bdk::Error> {
### 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));
@ -90,7 +87,6 @@ fn main() -> Result<(), bdk::Error> {
```rust,no_run
use bdk::{FeeRate, Wallet, SyncOptions};
use bdk::database::MemoryDatabase;
use bdk::blockchain::ElectrumBlockchain;
use bdk::electrum_client::Client;
@ -102,11 +98,10 @@ use bdk::bitcoin::Network;
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())?;
@ -132,18 +127,17 @@ fn main() -> Result<(), bdk::Error> {
### 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 = "...";

View File

@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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{}",

View File

@ -1,87 +0,0 @@
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());
}

View File

@ -1,112 +0,0 @@
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(())
}

View File

@ -1,93 +0,0 @@
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());
}

View File

@ -1,89 +0,0 @@
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());
}

View File

@ -1,6 +1,5 @@
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;
@ -42,7 +41,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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,

View File

@ -1,118 +0,0 @@
// 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(())
}

View File

@ -2,11 +2,11 @@ pub(crate) mod tx {
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 {
@ -15,6 +15,7 @@ pub(crate) mod tx {
let to_address = Address::from_str(recipient_address).unwrap();
// Set recipient of the transaction
tx_builder.set_recipients(vec![(to_address.script_pubkey(), amount)]);

View File

@ -203,8 +203,8 @@ macro_rules! impl_node_opcode_two {
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()?;
@ -234,9 +234,9 @@ macro_rules! impl_node_opcode_three {
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()?;
@ -263,7 +263,7 @@ macro_rules! impl_sortedmulti {
)*
];
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))
});
@ -274,7 +274,7 @@ macro_rules! impl_sortedmulti {
#[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
@ -318,7 +318,7 @@ macro_rules! parse_tap_tree {
// Single leaf
( $op:ident ( $( $minisc:tt )* ) ) => {{
use alloc::sync::Arc;
use $crate::alloc::sync::Arc;
use $crate::miniscript::descriptor::TapTree;
$crate::fragment!( $op ( $( $minisc )* ) )
@ -337,7 +337,7 @@ macro_rules! apply_modifier {
.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),
),
)?;
@ -374,8 +374,8 @@ macro_rules! apply_modifier {
$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))
})
@ -384,8 +384,8 @@ macro_rules! apply_modifier {
$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))
})
@ -394,8 +394,8 @@ macro_rules! apply_modifier {
$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))
})
@ -599,7 +599,7 @@ macro_rules! group_multi_keys {
)*
];
keys.into_iter().collect::<Result<alloc::vec::Vec<_>, _>>()
keys.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
.map_err($crate::descriptor::DescriptorError::Key)
}};
}
@ -744,8 +744,8 @@ macro_rules! fragment {
( 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());
@ -760,7 +760,7 @@ macro_rules! fragment {
( 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 ) ) => ({

View File

@ -73,19 +73,13 @@ impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
///
/// ```
/// # 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(),
@ -107,22 +101,16 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
///
/// ```
/// # 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>>(())
@ -143,18 +131,12 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
/// ```
/// # 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(),
@ -182,20 +164,18 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
/// # 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);
@ -221,21 +201,19 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
/// # 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);
@ -261,20 +239,18 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
/// # 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);
@ -300,21 +276,19 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
/// # 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);
@ -340,20 +314,18 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
/// # 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);
@ -379,21 +351,19 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
/// # 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);

View File

@ -31,64 +31,18 @@
//! * 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));
@ -98,52 +52,6 @@ fn main() -> Result<(), bdk::Error> {
//! 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
@ -152,14 +60,12 @@ fn main() -> Result<(), bdk::Error> {
//! 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 = "...";
@ -193,8 +99,9 @@ fn main() -> Result<(), bdk::Error> {
#[macro_use]
extern crate std;
#[doc(hidden)]
#[macro_use]
extern crate alloc;
pub extern crate alloc;
pub extern crate bitcoin;
#[cfg(feature = "hardware-signer")]

View File

@ -27,7 +27,6 @@
//! # 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;
@ -37,7 +36,6 @@
//! impl CoinSelectionAlgorithm for AlwaysSpendEverything {
//! fn coin_select(
//! &self,
//! database: &D,
//! required_utxos: Vec<WeightedUtxo>,
//! optional_utxos: Vec<WeightedUtxo>,
//! fee_rate: FeeRate,
@ -79,7 +77,7 @@
//! }
//! }
//!
//! # let wallet = doctest_wallet!();
//! # let mut wallet = doctest_wallet!();
//! // create wallet, sync, ...
//!
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();

View File

@ -20,7 +20,6 @@
//! ```
//! # use std::str::FromStr;
//! # use bitcoin::*;
//! # use bdk::database::*;
//! # use bdk::wallet::export::*;
//! # use bdk::*;
//! let import = r#"{
@ -30,11 +29,10 @@
//! }"#;
//!
//! 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>(())
//! ```
@ -42,14 +40,12 @@
//! ### 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)
@ -232,7 +228,7 @@ mod test {
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![],

View File

@ -15,7 +15,6 @@
//! 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;
@ -31,11 +30,10 @@
//! 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

View File

@ -25,17 +25,15 @@ use bdk_chain::{
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)]
@ -79,10 +77,8 @@ const COINBASE_MATURITY: u32 = 100;
/// 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 = ()> {
@ -573,8 +569,7 @@ impl<D> 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);
@ -599,9 +594,8 @@ impl<D> Wallet<D> {
/// # 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();
@ -1006,9 +1000,8 @@ impl<D> Wallet<D> {
/// # 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();
@ -1168,9 +1161,8 @@ impl<D> Wallet<D> {
/// # 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();
@ -1380,7 +1372,6 @@ impl<D> Wallet<D> {
/// 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;
@ -1397,7 +1388,7 @@ impl<D> Wallet<D> {
if keychain == KeychainKind::Internal
&& self.public_descriptor(KeychainKind::Internal).is_none()
{
return KeychainKind::External;
KeychainKind::External
} else {
keychain
}
@ -1765,3 +1756,40 @@ where
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
}}
}

View File

@ -21,7 +21,6 @@
//! # use bitcoin::*;
//! # use bitcoin::util::psbt;
//! # use bdk::signer::*;
//! # use bdk::database::*;
//! # use bdk::*;
//! # #[derive(Debug)]
//! # struct CustomHSM;
@ -70,7 +69,7 @@
//! 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),

View File

@ -19,7 +19,7 @@
//! # 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();
//!
@ -80,7 +80,7 @@ impl TxBuilderContext for BumpFee {}
/// # 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
@ -242,7 +242,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
/// # 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]);
///
@ -623,7 +623,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
/// # 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