Make doctests work
This commit is contained in:
parent
3baf9721ec
commit
e1eb0253cf
16
README.md
16
README.md
@ -40,7 +40,6 @@ The `bdk` library aims to be the core building block for Bitcoin wallets of any
|
|||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use bdk::Wallet;
|
use bdk::Wallet;
|
||||||
use bdk::database::MemoryDatabase;
|
|
||||||
use bdk::blockchain::ElectrumBlockchain;
|
use bdk::blockchain::ElectrumBlockchain;
|
||||||
use bdk::SyncOptions;
|
use bdk::SyncOptions;
|
||||||
use bdk::electrum_client::Client;
|
use bdk::electrum_client::Client;
|
||||||
@ -52,7 +51,6 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
MemoryDatabase::default(),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
wallet.sync(&blockchain, SyncOptions::default())?;
|
wallet.sync(&blockchain, SyncOptions::default())?;
|
||||||
@ -66,16 +64,15 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
### Generate a few addresses
|
### Generate a few addresses
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use bdk::{Wallet, database::MemoryDatabase};
|
use bdk::Wallet;
|
||||||
use bdk::wallet::AddressIndex::New;
|
use bdk::wallet::AddressIndex::New;
|
||||||
use bdk::bitcoin::Network;
|
use bdk::bitcoin::Network;
|
||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let wallet = Wallet::new(
|
let wallet = Wallet::new_no_persist(
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
MemoryDatabase::default(),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
println!("Address #0: {}", wallet.get_address(New));
|
println!("Address #0: {}", wallet.get_address(New));
|
||||||
@ -90,7 +87,6 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use bdk::{FeeRate, Wallet, SyncOptions};
|
use bdk::{FeeRate, Wallet, SyncOptions};
|
||||||
use bdk::database::MemoryDatabase;
|
|
||||||
use bdk::blockchain::ElectrumBlockchain;
|
use bdk::blockchain::ElectrumBlockchain;
|
||||||
|
|
||||||
use bdk::electrum_client::Client;
|
use bdk::electrum_client::Client;
|
||||||
@ -102,11 +98,10 @@ use bdk::bitcoin::Network;
|
|||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
|
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/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
MemoryDatabase::default(),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
wallet.sync(&blockchain, SyncOptions::default())?;
|
wallet.sync(&blockchain, SyncOptions::default())?;
|
||||||
@ -132,18 +127,17 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
### Sign a transaction
|
### Sign a transaction
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use bdk::{Wallet, SignOptions, database::MemoryDatabase};
|
use bdk::{Wallet, SignOptions};
|
||||||
|
|
||||||
use base64;
|
use base64;
|
||||||
use bdk::bitcoin::consensus::deserialize;
|
use bdk::bitcoin::consensus::deserialize;
|
||||||
use bdk::bitcoin::Network;
|
use bdk::bitcoin::Network;
|
||||||
|
|
||||||
fn main() -> Result<(), bdk::Error> {
|
fn main() -> Result<(), bdk::Error> {
|
||||||
let wallet = Wallet::new(
|
let wallet = Wallet::new_no_persist(
|
||||||
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
||||||
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
MemoryDatabase::default(),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let psbt = "...";
|
let psbt = "...";
|
||||||
|
@ -54,7 +54,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
info!("Compiled into following Descriptor: \n{}", descriptor);
|
info!("Compiled into following Descriptor: \n{}", descriptor);
|
||||||
|
|
||||||
// Create a new wallet from this 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!(
|
info!(
|
||||||
"First derived address from the descriptor: \n{}",
|
"First derived address from the descriptor: \n{}",
|
||||||
|
@ -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());
|
|
||||||
}
|
|
@ -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(())
|
|
||||||
}
|
|
@ -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());
|
|
||||||
}
|
|
@ -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());
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
use bdk::bitcoin::{Address, Network};
|
use bdk::bitcoin::{Address, Network};
|
||||||
use bdk::blockchain::{Blockchain, ElectrumBlockchain};
|
use bdk::blockchain::{Blockchain, ElectrumBlockchain};
|
||||||
use bdk::database::MemoryDatabase;
|
|
||||||
use bdk::hwi::{types::HWIChain, HWIClient};
|
use bdk::hwi::{types::HWIChain, HWIClient};
|
||||||
use bdk::miniscript::{Descriptor, DescriptorPublicKey};
|
use bdk::miniscript::{Descriptor, DescriptorPublicKey};
|
||||||
use bdk::signer::SignerOrdering;
|
use bdk::signer::SignerOrdering;
|
||||||
@ -42,7 +41,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// Creating a custom signer from the device
|
// Creating a custom signer from the device
|
||||||
let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?;
|
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(),
|
descriptors.receive[0].clone(),
|
||||||
Some(descriptors.internal[0].clone()),
|
Some(descriptors.internal[0].clone()),
|
||||||
Network::Testnet,
|
Network::Testnet,
|
||||||
|
@ -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(())
|
|
||||||
}
|
|
@ -2,11 +2,11 @@ pub(crate) mod tx {
|
|||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use bdk::{database::BatchDatabase, SignOptions, Wallet};
|
use bdk::{SignOptions, Wallet, persist};
|
||||||
use bitcoin::{Address, Transaction};
|
use bitcoin::{Address, Transaction};
|
||||||
|
|
||||||
pub fn build_signed_tx<D: BatchDatabase>(
|
pub fn build_signed_tx<()>(
|
||||||
wallet: &Wallet<D>,
|
wallet: &Wallet<()>,
|
||||||
recipient_address: &str,
|
recipient_address: &str,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
) -> Transaction {
|
) -> Transaction {
|
||||||
@ -15,6 +15,7 @@ pub(crate) mod tx {
|
|||||||
|
|
||||||
let to_address = Address::from_str(recipient_address).unwrap();
|
let to_address = Address::from_str(recipient_address).unwrap();
|
||||||
|
|
||||||
|
|
||||||
// Set recipient of the transaction
|
// Set recipient of the transaction
|
||||||
tx_builder.set_recipients(vec![(to_address.script_pubkey(), amount)]);
|
tx_builder.set_recipients(vec![(to_address.script_pubkey(), amount)]);
|
||||||
|
|
||||||
|
@ -203,8 +203,8 @@ macro_rules! impl_node_opcode_two {
|
|||||||
a_keymap.extend(b_keymap.into_iter());
|
a_keymap.extend(b_keymap.into_iter());
|
||||||
|
|
||||||
let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
|
let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
|
||||||
alloc::sync::Arc::new(a_minisc),
|
$crate::alloc::sync::Arc::new(a_minisc),
|
||||||
alloc::sync::Arc::new(b_minisc),
|
$crate::alloc::sync::Arc::new(b_minisc),
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
minisc.check_miniscript()?;
|
minisc.check_miniscript()?;
|
||||||
@ -234,9 +234,9 @@ macro_rules! impl_node_opcode_three {
|
|||||||
let networks = $crate::keys::merge_networks(&networks, &c_networks);
|
let networks = $crate::keys::merge_networks(&networks, &c_networks);
|
||||||
|
|
||||||
let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
|
let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
|
||||||
alloc::sync::Arc::new(a_minisc),
|
$crate::alloc::sync::Arc::new(a_minisc),
|
||||||
alloc::sync::Arc::new(b_minisc),
|
$crate::alloc::sync::Arc::new(b_minisc),
|
||||||
alloc::sync::Arc::new(c_minisc),
|
$crate::alloc::sync::Arc::new(c_minisc),
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
minisc.check_miniscript()?;
|
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)
|
.map_err($crate::descriptor::DescriptorError::Key)
|
||||||
.and_then(|keys| $crate::keys::make_sortedmulti($thresh, keys, $build_desc, &secp))
|
.and_then(|keys| $crate::keys::make_sortedmulti($thresh, keys, $build_desc, &secp))
|
||||||
});
|
});
|
||||||
@ -274,7 +274,7 @@ macro_rules! impl_sortedmulti {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! parse_tap_tree {
|
macro_rules! parse_tap_tree {
|
||||||
( @merge $tree_a:expr, $tree_b:expr) => {{
|
( @merge $tree_a:expr, $tree_b:expr) => {{
|
||||||
use alloc::sync::Arc;
|
use $crate::alloc::sync::Arc;
|
||||||
use $crate::miniscript::descriptor::TapTree;
|
use $crate::miniscript::descriptor::TapTree;
|
||||||
|
|
||||||
$tree_a
|
$tree_a
|
||||||
@ -318,7 +318,7 @@ macro_rules! parse_tap_tree {
|
|||||||
|
|
||||||
// Single leaf
|
// Single leaf
|
||||||
( $op:ident ( $( $minisc:tt )* ) ) => {{
|
( $op:ident ( $( $minisc:tt )* ) ) => {{
|
||||||
use alloc::sync::Arc;
|
use $crate::alloc::sync::Arc;
|
||||||
use $crate::miniscript::descriptor::TapTree;
|
use $crate::miniscript::descriptor::TapTree;
|
||||||
|
|
||||||
$crate::fragment!( $op ( $( $minisc )* ) )
|
$crate::fragment!( $op ( $( $minisc )* ) )
|
||||||
@ -337,7 +337,7 @@ macro_rules! apply_modifier {
|
|||||||
.and_then(|(minisc, keymap, networks)| {
|
.and_then(|(minisc, keymap, networks)| {
|
||||||
let minisc = $crate::miniscript::Miniscript::from_ast(
|
let minisc = $crate::miniscript::Miniscript::from_ast(
|
||||||
$crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
|
$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)| {
|
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
|
||||||
$crate::impl_leaf_opcode_value_two!(
|
$crate::impl_leaf_opcode_value_two!(
|
||||||
AndV,
|
AndV,
|
||||||
alloc::sync::Arc::new(a_minisc),
|
$crate::alloc::sync::Arc::new(a_minisc),
|
||||||
alloc::sync::Arc::new($crate::fragment!(true).unwrap().0)
|
$crate::alloc::sync::Arc::new($crate::fragment!(true).unwrap().0)
|
||||||
)
|
)
|
||||||
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
|
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
|
||||||
})
|
})
|
||||||
@ -384,8 +384,8 @@ macro_rules! apply_modifier {
|
|||||||
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
|
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
|
||||||
$crate::impl_leaf_opcode_value_two!(
|
$crate::impl_leaf_opcode_value_two!(
|
||||||
OrI,
|
OrI,
|
||||||
alloc::sync::Arc::new($crate::fragment!(false).unwrap().0),
|
$crate::alloc::sync::Arc::new($crate::fragment!(false).unwrap().0),
|
||||||
alloc::sync::Arc::new(a_minisc)
|
$crate::alloc::sync::Arc::new(a_minisc)
|
||||||
)
|
)
|
||||||
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
|
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
|
||||||
})
|
})
|
||||||
@ -394,8 +394,8 @@ macro_rules! apply_modifier {
|
|||||||
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
|
$inner.and_then(|(a_minisc, a_keymap, a_networks)| {
|
||||||
$crate::impl_leaf_opcode_value_two!(
|
$crate::impl_leaf_opcode_value_two!(
|
||||||
OrI,
|
OrI,
|
||||||
alloc::sync::Arc::new(a_minisc),
|
$crate::alloc::sync::Arc::new(a_minisc),
|
||||||
alloc::sync::Arc::new($crate::fragment!(false).unwrap().0)
|
$crate::alloc::sync::Arc::new($crate::fragment!(false).unwrap().0)
|
||||||
)
|
)
|
||||||
.map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
|
.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)
|
.map_err($crate::descriptor::DescriptorError::Key)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -744,8 +744,8 @@ macro_rules! fragment {
|
|||||||
( thresh_vec ( $thresh:expr, $items:expr ) ) => ({
|
( thresh_vec ( $thresh:expr, $items:expr ) ) => ({
|
||||||
use $crate::miniscript::descriptor::KeyMap;
|
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, 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(alloc::sync::Arc::new).collect();
|
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)| {
|
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());
|
keys_acc.extend(key.into_iter());
|
||||||
@ -760,7 +760,7 @@ macro_rules! fragment {
|
|||||||
( thresh ( $thresh:expr, $( $inner:tt )* ) ) => ({
|
( thresh ( $thresh:expr, $( $inner:tt )* ) ) => ({
|
||||||
let items = $crate::fragment_internal!( @v $( $inner )* );
|
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)))
|
.and_then(|items| $crate::fragment!(thresh_vec($thresh, items)))
|
||||||
});
|
});
|
||||||
( multi_vec ( $thresh:expr, $keys:expr ) ) => ({
|
( multi_vec ( $thresh:expr, $keys:expr ) ) => ({
|
||||||
|
@ -73,19 +73,13 @@ impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet};
|
/// # use bdk::Wallet;
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::P2Pkh;
|
/// use bdk::template::P2Pkh;
|
||||||
///
|
///
|
||||||
/// let key =
|
/// let key =
|
||||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
/// let wallet = Wallet::new(
|
/// let mut wallet = Wallet::new_no_persist(P2Pkh(key), None, Network::Testnet)?;
|
||||||
/// P2Pkh(key),
|
|
||||||
/// None,
|
|
||||||
/// Network::Testnet,
|
|
||||||
/// MemoryDatabase::default(),
|
|
||||||
/// )?;
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// wallet.get_address(New).to_string(),
|
/// 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::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet};
|
/// # use bdk::Wallet;
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
|
||||||
/// use bdk::template::P2Wpkh_P2Sh;
|
/// use bdk::template::P2Wpkh_P2Sh;
|
||||||
|
/// use bdk::wallet::AddressIndex;
|
||||||
///
|
///
|
||||||
/// let key =
|
/// let key =
|
||||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
/// let wallet = Wallet::new(
|
/// let mut wallet = Wallet::new_no_persist(P2Wpkh_P2Sh(key), None, Network::Testnet)?;
|
||||||
/// P2Wpkh_P2Sh(key),
|
|
||||||
/// None,
|
|
||||||
/// Network::Testnet,
|
|
||||||
/// MemoryDatabase::default(),
|
|
||||||
/// )?;
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// wallet.get_address(New).to_string(),
|
/// wallet.get_address(AddressIndex::New).to_string(),
|
||||||
/// "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"
|
/// "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"
|
||||||
/// );
|
/// );
|
||||||
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
/// # 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::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet};
|
/// # use bdk::{Wallet};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
|
||||||
/// use bdk::template::P2Wpkh;
|
/// use bdk::template::P2Wpkh;
|
||||||
|
/// use bdk::wallet::AddressIndex::New;
|
||||||
///
|
///
|
||||||
/// let key =
|
/// let key =
|
||||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||||
/// let wallet = Wallet::new(
|
/// let mut wallet = Wallet::new_no_persist(P2Wpkh(key), None, Network::Testnet)?;
|
||||||
/// P2Wpkh(key),
|
|
||||||
/// None,
|
|
||||||
/// Network::Testnet,
|
|
||||||
/// MemoryDatabase::default(),
|
|
||||||
/// )?;
|
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// wallet.get_address(New).to_string(),
|
/// wallet.get_address(New).to_string(),
|
||||||
@ -182,20 +164,18 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
|
|||||||
/// # use std::str::FromStr;
|
/// # use std::str::FromStr;
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::Bip44;
|
/// use bdk::template::Bip44;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
/// 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),
|
/// Bip44(key.clone(), KeychainKind::External),
|
||||||
/// Some(Bip44(key, KeychainKind::Internal)),
|
/// Some(Bip44(key, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
/// MemoryDatabase::default()
|
|
||||||
/// )?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(wallet.get_address(New).to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89");
|
/// 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>>(())
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
|
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 std::str::FromStr;
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::Bip44Public;
|
/// use bdk::template::Bip44Public;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
|
||||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
/// 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),
|
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
|
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
/// MemoryDatabase::default()
|
|
||||||
/// )?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(wallet.get_address(New).to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
|
/// 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>>(())
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
|
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 std::str::FromStr;
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::Bip49;
|
/// use bdk::template::Bip49;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
/// 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),
|
/// Bip49(key.clone(), KeychainKind::External),
|
||||||
/// Some(Bip49(key, KeychainKind::Internal)),
|
/// Some(Bip49(key, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
/// MemoryDatabase::default()
|
|
||||||
/// )?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(wallet.get_address(New).to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB");
|
/// 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>>(())
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
|
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 std::str::FromStr;
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::Bip49Public;
|
/// use bdk::template::Bip49Public;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
|
||||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
/// 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),
|
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
|
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
/// MemoryDatabase::default()
|
|
||||||
/// )?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(wallet.get_address(New).to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
|
/// 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>>(())
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
|
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 std::str::FromStr;
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::Bip84;
|
/// use bdk::template::Bip84;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
/// 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),
|
/// Bip84(key.clone(), KeychainKind::External),
|
||||||
/// Some(Bip84(key, KeychainKind::Internal)),
|
/// Some(Bip84(key, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
/// MemoryDatabase::default()
|
|
||||||
/// )?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(wallet.get_address(New).to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n");
|
/// 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>>(())
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
|
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 std::str::FromStr;
|
||||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::database::MemoryDatabase;
|
|
||||||
/// # use bdk::wallet::AddressIndex::New;
|
/// # use bdk::wallet::AddressIndex::New;
|
||||||
/// use bdk::template::Bip84Public;
|
/// use bdk::template::Bip84Public;
|
||||||
///
|
///
|
||||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
/// 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),
|
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
|
||||||
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
|
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
|
||||||
/// Network::Testnet,
|
/// Network::Testnet,
|
||||||
/// MemoryDatabase::default()
|
|
||||||
/// )?;
|
/// )?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(wallet.get_address(New).to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
|
/// 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>>(())
|
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
|
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
|
||||||
|
101
src/lib.rs
101
src/lib.rs
@ -31,64 +31,18 @@
|
|||||||
//! * It is built to be cross-platform: the core logic works on desktop, mobile, and even WebAssembly.
|
//! * 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.
|
//! * 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
|
//! ## Generate a few addresses
|
||||||
//!
|
//!
|
||||||
//! ### Example
|
//! ### Example
|
||||||
//! ```
|
//! ```
|
||||||
//! use bdk::{Wallet};
|
//! use bdk::{Wallet};
|
||||||
//! use bdk::database::MemoryDatabase;
|
|
||||||
//! use bdk::wallet::AddressIndex::New;
|
//! use bdk::wallet::AddressIndex::New;
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), bdk::Error> {
|
//! fn main() -> Result<(), bdk::Error> {
|
||||||
//! let wallet = Wallet::new(
|
//! let mut wallet = Wallet::new_no_persist(
|
||||||
//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
//! "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
|
||||||
//! bitcoin::Network::Testnet,
|
//! bitcoin::Network::Testnet,
|
||||||
//! MemoryDatabase::default(),
|
|
||||||
//! )?;
|
//! )?;
|
||||||
//!
|
//!
|
||||||
//! println!("Address #0: {}", wallet.get_address(New));
|
//! println!("Address #0: {}", wallet.get_address(New));
|
||||||
@ -98,52 +52,6 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
//! Ok(())
|
//! 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
|
//! ## Sign a transaction
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
@ -152,14 +60,12 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
//! use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
|
//! use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
|
||||||
//!
|
//!
|
||||||
//! use bdk::{Wallet, SignOptions};
|
//! use bdk::{Wallet, SignOptions};
|
||||||
//! use bdk::database::MemoryDatabase;
|
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), bdk::Error> {
|
//! fn main() -> Result<(), bdk::Error> {
|
||||||
//! let wallet = Wallet::new(
|
//! let wallet = Wallet::new_no_persist(
|
||||||
//! "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
//! "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
|
||||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
//! Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
|
||||||
//! bitcoin::Network::Testnet,
|
//! bitcoin::Network::Testnet,
|
||||||
//! MemoryDatabase::default(),
|
|
||||||
//! )?;
|
//! )?;
|
||||||
//!
|
//!
|
||||||
//! let psbt = "...";
|
//! let psbt = "...";
|
||||||
@ -193,8 +99,9 @@ fn main() -> Result<(), bdk::Error> {
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
pub extern crate alloc;
|
||||||
|
|
||||||
pub extern crate bitcoin;
|
pub extern crate bitcoin;
|
||||||
#[cfg(feature = "hardware-signer")]
|
#[cfg(feature = "hardware-signer")]
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
//! # use std::str::FromStr;
|
//! # use std::str::FromStr;
|
||||||
//! # use bitcoin::*;
|
//! # use bitcoin::*;
|
||||||
//! # use bdk::wallet::{self, coin_selection::*};
|
//! # use bdk::wallet::{self, coin_selection::*};
|
||||||
//! # use bdk::database::Database;
|
|
||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! # use bdk::wallet::coin_selection::decide_change;
|
//! # use bdk::wallet::coin_selection::decide_change;
|
||||||
//! # const TXIN_BASE_WEIGHT: usize = (32 + 4 + 4) * 4;
|
//! # const TXIN_BASE_WEIGHT: usize = (32 + 4 + 4) * 4;
|
||||||
@ -37,7 +36,6 @@
|
|||||||
//! impl CoinSelectionAlgorithm for AlwaysSpendEverything {
|
//! impl CoinSelectionAlgorithm for AlwaysSpendEverything {
|
||||||
//! fn coin_select(
|
//! fn coin_select(
|
||||||
//! &self,
|
//! &self,
|
||||||
//! database: &D,
|
|
||||||
//! required_utxos: Vec<WeightedUtxo>,
|
//! required_utxos: Vec<WeightedUtxo>,
|
||||||
//! optional_utxos: Vec<WeightedUtxo>,
|
//! optional_utxos: Vec<WeightedUtxo>,
|
||||||
//! fee_rate: FeeRate,
|
//! fee_rate: FeeRate,
|
||||||
@ -79,7 +77,7 @@
|
|||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! # let wallet = doctest_wallet!();
|
//! # let mut wallet = doctest_wallet!();
|
||||||
//! // create wallet, sync, ...
|
//! // create wallet, sync, ...
|
||||||
//!
|
//!
|
||||||
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use std::str::FromStr;
|
//! # use std::str::FromStr;
|
||||||
//! # use bitcoin::*;
|
//! # use bitcoin::*;
|
||||||
//! # use bdk::database::*;
|
|
||||||
//! # use bdk::wallet::export::*;
|
//! # use bdk::wallet::export::*;
|
||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! let import = r#"{
|
//! let import = r#"{
|
||||||
@ -30,11 +29,10 @@
|
|||||||
//! }"#;
|
//! }"#;
|
||||||
//!
|
//!
|
||||||
//! let import = FullyNodedExport::from_str(import)?;
|
//! let import = FullyNodedExport::from_str(import)?;
|
||||||
//! let wallet = Wallet::new(
|
//! let wallet = Wallet::new_no_persist(
|
||||||
//! &import.descriptor(),
|
//! &import.descriptor(),
|
||||||
//! import.change_descriptor().as_ref(),
|
//! import.change_descriptor().as_ref(),
|
||||||
//! Network::Testnet,
|
//! Network::Testnet,
|
||||||
//! MemoryDatabase::default(),
|
|
||||||
//! )?;
|
//! )?;
|
||||||
//! # Ok::<_, bdk::Error>(())
|
//! # Ok::<_, bdk::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
@ -42,14 +40,12 @@
|
|||||||
//! ### Export a `Wallet`
|
//! ### Export a `Wallet`
|
||||||
//! ```
|
//! ```
|
||||||
//! # use bitcoin::*;
|
//! # use bitcoin::*;
|
||||||
//! # use bdk::database::*;
|
|
||||||
//! # use bdk::wallet::export::*;
|
//! # use bdk::wallet::export::*;
|
||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! let wallet = Wallet::new(
|
//! let wallet = Wallet::new_no_persist(
|
||||||
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
|
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
|
||||||
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
|
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
|
||||||
//! Network::Testnet,
|
//! Network::Testnet,
|
||||||
//! MemoryDatabase::default()
|
|
||||||
//! )?;
|
//! )?;
|
||||||
//! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true)
|
//! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true)
|
||||||
//! .map_err(ToString::to_string)
|
//! .map_err(ToString::to_string)
|
||||||
@ -232,7 +228,7 @@ mod test {
|
|||||||
change_descriptor: Option<&str>,
|
change_descriptor: Option<&str>,
|
||||||
network: Network,
|
network: Network,
|
||||||
) -> Wallet<()> {
|
) -> 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 {
|
let transaction = Transaction {
|
||||||
input: vec![],
|
input: vec![],
|
||||||
output: vec![],
|
output: vec![],
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
//! used with hardware wallets.
|
//! used with hardware wallets.
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use bdk::bitcoin::Network;
|
//! # use bdk::bitcoin::Network;
|
||||||
//! # use bdk::database::MemoryDatabase;
|
|
||||||
//! # use bdk::signer::SignerOrdering;
|
//! # use bdk::signer::SignerOrdering;
|
||||||
//! # use bdk::wallet::hardwaresigner::HWISigner;
|
//! # use bdk::wallet::hardwaresigner::HWISigner;
|
||||||
//! # use bdk::wallet::AddressIndex::New;
|
//! # use bdk::wallet::AddressIndex::New;
|
||||||
@ -31,11 +30,10 @@
|
|||||||
//! let first_device = devices.remove(0)?;
|
//! let first_device = devices.remove(0)?;
|
||||||
//! let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?;
|
//! let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?;
|
||||||
//!
|
//!
|
||||||
//! # let mut wallet = Wallet::new(
|
//! # let mut wallet = Wallet::new_no_persist(
|
||||||
//! # "",
|
//! # "",
|
||||||
//! # None,
|
//! # None,
|
||||||
//! # Network::Testnet,
|
//! # Network::Testnet,
|
||||||
//! # MemoryDatabase::default(),
|
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! #
|
//! #
|
||||||
//! // Adding the hardware signer to the BDK wallet
|
//! // Adding the hardware signer to the BDK wallet
|
||||||
|
@ -25,17 +25,15 @@ use bdk_chain::{
|
|||||||
keychain::{KeychainChangeSet, KeychainScan, KeychainTracker},
|
keychain::{KeychainChangeSet, KeychainScan, KeychainTracker},
|
||||||
sparse_chain, BlockId, ConfirmationTime, IntoOwned,
|
sparse_chain, BlockId, ConfirmationTime, IntoOwned,
|
||||||
};
|
};
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
|
||||||
use core::fmt;
|
|
||||||
use core::ops::Deref;
|
|
||||||
|
|
||||||
use bitcoin::consensus::encode::serialize;
|
use bitcoin::consensus::encode::serialize;
|
||||||
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::util::psbt;
|
use bitcoin::util::psbt;
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
Address, BlockHash, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script,
|
Address, BlockHash, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script,
|
||||||
Sequence, Transaction, TxOut, Txid, Witness,
|
Sequence, Transaction, TxOut, Txid, Witness,
|
||||||
};
|
};
|
||||||
|
use core::fmt;
|
||||||
|
use core::ops::Deref;
|
||||||
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
|
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -79,10 +77,8 @@ const COINBASE_MATURITY: u32 = 100;
|
|||||||
/// Its main components are:
|
/// Its main components are:
|
||||||
///
|
///
|
||||||
/// 1. output *descriptors* from which it can derive addresses.
|
/// 1. output *descriptors* from which it can derive addresses.
|
||||||
/// 2. A [`Database`] where it tracks transactions and utxos related to the descriptors.
|
/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
|
||||||
/// 3. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
|
|
||||||
///
|
///
|
||||||
/// [`Database`]: crate::database::Database
|
|
||||||
/// [`signer`]: crate::signer
|
/// [`signer`]: crate::signer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Wallet<D = ()> {
|
pub struct Wallet<D = ()> {
|
||||||
@ -573,8 +569,7 @@ impl<D> Wallet<D> {
|
|||||||
/// ```
|
/// ```
|
||||||
/// # use bdk::{Wallet, KeychainKind};
|
/// # use bdk::{Wallet, KeychainKind};
|
||||||
/// # use bdk::bitcoin::Network;
|
/// # use bdk::bitcoin::Network;
|
||||||
/// # use bdk::database::MemoryDatabase;
|
/// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet)?;
|
||||||
/// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet, MemoryDatabase::new())?;
|
|
||||||
/// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
|
/// 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/*
|
/// // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
|
||||||
/// println!("secret_key: {}", secret_key);
|
/// println!("secret_key: {}", secret_key);
|
||||||
@ -599,9 +594,8 @@ impl<D> Wallet<D> {
|
|||||||
/// # use std::str::FromStr;
|
/// # use std::str::FromStr;
|
||||||
/// # use bitcoin::*;
|
/// # use bitcoin::*;
|
||||||
/// # use bdk::*;
|
/// # use bdk::*;
|
||||||
/// # use bdk::database::*;
|
|
||||||
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||||
/// # let wallet = doctest_wallet!();
|
/// # let mut wallet = doctest_wallet!();
|
||||||
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
/// let (psbt, details) = {
|
/// let (psbt, details) = {
|
||||||
/// let mut builder = wallet.build_tx();
|
/// let mut builder = wallet.build_tx();
|
||||||
@ -1006,9 +1000,8 @@ impl<D> Wallet<D> {
|
|||||||
/// # use std::str::FromStr;
|
/// # use std::str::FromStr;
|
||||||
/// # use bitcoin::*;
|
/// # use bitcoin::*;
|
||||||
/// # use bdk::*;
|
/// # use bdk::*;
|
||||||
/// # use bdk::database::*;
|
|
||||||
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||||
/// # let wallet = doctest_wallet!();
|
/// # let mut wallet = doctest_wallet!();
|
||||||
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
/// let (mut psbt, _) = {
|
/// let (mut psbt, _) = {
|
||||||
/// let mut builder = wallet.build_tx();
|
/// let mut builder = wallet.build_tx();
|
||||||
@ -1168,9 +1161,8 @@ impl<D> Wallet<D> {
|
|||||||
/// # use std::str::FromStr;
|
/// # use std::str::FromStr;
|
||||||
/// # use bitcoin::*;
|
/// # use bitcoin::*;
|
||||||
/// # use bdk::*;
|
/// # use bdk::*;
|
||||||
/// # use bdk::database::*;
|
|
||||||
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||||
/// # let wallet = doctest_wallet!();
|
/// # let mut wallet = doctest_wallet!();
|
||||||
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
/// let (mut psbt, _) = {
|
/// let (mut psbt, _) = {
|
||||||
/// let mut builder = wallet.build_tx();
|
/// 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.
|
/// 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.
|
/// 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
|
// TODO: Make this free up reserved utxos when that's implemented
|
||||||
pub fn cancel_tx(&mut self, tx: &Transaction) {
|
pub fn cancel_tx(&mut self, tx: &Transaction) {
|
||||||
let txout_index = &mut self.keychain_tracker.txout_index;
|
let txout_index = &mut self.keychain_tracker.txout_index;
|
||||||
@ -1397,7 +1388,7 @@ impl<D> Wallet<D> {
|
|||||||
if keychain == KeychainKind::Internal
|
if keychain == KeychainKind::Internal
|
||||||
&& self.public_descriptor(KeychainKind::Internal).is_none()
|
&& self.public_descriptor(KeychainKind::Internal).is_none()
|
||||||
{
|
{
|
||||||
return KeychainKind::External;
|
KeychainKind::External
|
||||||
} else {
|
} else {
|
||||||
keychain
|
keychain
|
||||||
}
|
}
|
||||||
@ -1765,3 +1756,40 @@ where
|
|||||||
|
|
||||||
Ok(wallet_name)
|
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
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
//! # use bitcoin::*;
|
//! # use bitcoin::*;
|
||||||
//! # use bitcoin::util::psbt;
|
//! # use bitcoin::util::psbt;
|
||||||
//! # use bdk::signer::*;
|
//! # use bdk::signer::*;
|
||||||
//! # use bdk::database::*;
|
|
||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! # #[derive(Debug)]
|
//! # #[derive(Debug)]
|
||||||
//! # struct CustomHSM;
|
//! # struct CustomHSM;
|
||||||
@ -70,7 +69,7 @@
|
|||||||
//! let custom_signer = CustomSigner::connect();
|
//! let custom_signer = CustomSigner::connect();
|
||||||
//!
|
//!
|
||||||
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
//! 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(
|
//! wallet.add_signer(
|
||||||
//! KeychainKind::External,
|
//! KeychainKind::External,
|
||||||
//! SignerOrdering(200),
|
//! SignerOrdering(200),
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
//! # use bdk::*;
|
//! # use bdk::*;
|
||||||
//! # use bdk::wallet::tx_builder::CreateTx;
|
//! # use bdk::wallet::tx_builder::CreateTx;
|
||||||
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
//! # let wallet = doctest_wallet!();
|
//! # let mut wallet = doctest_wallet!();
|
||||||
//! // create a TxBuilder from a wallet
|
//! // create a TxBuilder from a wallet
|
||||||
//! let mut tx_builder = wallet.build_tx();
|
//! let mut tx_builder = wallet.build_tx();
|
||||||
//!
|
//!
|
||||||
@ -80,7 +80,7 @@ impl TxBuilderContext for BumpFee {}
|
|||||||
/// # use bdk::wallet::tx_builder::*;
|
/// # use bdk::wallet::tx_builder::*;
|
||||||
/// # use bitcoin::*;
|
/// # use bitcoin::*;
|
||||||
/// # use core::str::FromStr;
|
/// # use core::str::FromStr;
|
||||||
/// # let wallet = doctest_wallet!();
|
/// # let mut wallet = doctest_wallet!();
|
||||||
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
/// # let addr2 = addr1.clone();
|
/// # let addr2 = addr1.clone();
|
||||||
/// // chaining
|
/// // chaining
|
||||||
@ -242,7 +242,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
|
|||||||
/// # use bitcoin::*;
|
/// # use bitcoin::*;
|
||||||
/// # use bdk::*;
|
/// # use bdk::*;
|
||||||
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
/// # let wallet = doctest_wallet!();
|
/// # let mut wallet = doctest_wallet!();
|
||||||
/// let mut path = BTreeMap::new();
|
/// let mut path = BTreeMap::new();
|
||||||
/// path.insert("aabbccdd".to_string(), vec![0, 1]);
|
/// 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::*;
|
||||||
/// # use bdk::wallet::tx_builder::CreateTx;
|
/// # use bdk::wallet::tx_builder::CreateTx;
|
||||||
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||||
/// # let wallet = doctest_wallet!();
|
/// # let mut wallet = doctest_wallet!();
|
||||||
/// let mut tx_builder = wallet.build_tx();
|
/// let mut tx_builder = wallet.build_tx();
|
||||||
///
|
///
|
||||||
/// tx_builder
|
/// tx_builder
|
||||||
|
Loading…
x
Reference in New Issue
Block a user