Skip to content

Commit b1a505a

Browse files
exploring bdk-tx with bdk_wallet
1 parent 6e2414e commit b1a505a

File tree

3 files changed

+135
-2
lines changed

3 files changed

+135
-2
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ bdk_tx = { path = "." }
1919
bitcoin = { version = "0.32", features = ["rand-std"] }
2020
bdk_testenv = "0.11.1"
2121
bdk_bitcoind_rpc = "0.18.0"
22+
bdk_wallet = "1.2.0"
23+
bdk_esplora = { version = "0.20.1", features = ["blocking"] }
2224

2325
[features]
2426
default = ["std"]

tests/bdk_wallet.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use bdk_chain::spk_client::{FullScanRequestBuilder, FullScanResponse, SyncResponse};
2+
use bdk_chain::KeychainIndexed;
3+
use bdk_esplora::esplora_client;
4+
use bdk_esplora::esplora_client::Builder;
5+
use bdk_esplora::EsploraExt;
6+
use bdk_testenv::{bitcoincore_rpc::RpcApi, TestEnv};
7+
use bdk_tx::{
8+
create_psbt, create_selection, CreatePsbtParams, CreateSelectionParams, InputCandidates,
9+
InputGroup, Output,
10+
};
11+
use bdk_wallet::{AddressInfo, KeychainKind, LocalOutput, SignOptions};
12+
use bitcoin::address::NetworkChecked;
13+
use bitcoin::{Address, Amount, FeeRate, Network, OutPoint};
14+
use miniscript::descriptor::KeyMap;
15+
use miniscript::plan::Assets;
16+
use miniscript::{Descriptor, DescriptorPublicKey};
17+
use std::collections::BTreeMap;
18+
use std::process::exit;
19+
use std::str::FromStr;
20+
21+
#[test]
22+
fn bdk_wallet_simple_tx() -> anyhow::Result<()> {
23+
const STOP_GAP: usize = 20;
24+
const PARALLEL_REQUESTS: usize = 1;
25+
let secp = bitcoin::secp256k1::Secp256k1::new();
26+
27+
let descriptor_private: &str = "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/0/*)#usy7l3tt";
28+
let change_descriptor_private: &str = "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/1/*)#dyplzymn";
29+
30+
let (descriptor, _): (Descriptor<DescriptorPublicKey>, KeyMap) = Descriptor::parse_descriptor(&secp, "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/0/*)#usy7l3tt")?;
31+
let (change_descriptor, _): (Descriptor<DescriptorPublicKey>, KeyMap) = Descriptor::parse_descriptor(&secp, "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/1/*)#dyplzymn")?;
32+
33+
// Create the wallet
34+
let mut wallet = bdk_wallet::Wallet::create(descriptor_private, change_descriptor_private)
35+
.network(Network::Regtest)
36+
.create_wallet_no_persist()?;
37+
38+
let client: esplora_client::BlockingClient =
39+
Builder::new("http://127.0.0.1:3002").build_blocking();
40+
41+
println!("Syncing wallet...");
42+
let full_scan_request: FullScanRequestBuilder<KeychainKind> = wallet.start_full_scan();
43+
let update: FullScanResponse<KeychainKind> =
44+
client.full_scan(full_scan_request, STOP_GAP, PARALLEL_REQUESTS)?;
45+
46+
// Apply the update from the full scan to the wallet
47+
wallet.apply_update(update)?;
48+
49+
let balance = wallet.balance();
50+
println!("Wallet balance: {} sat", balance.total().to_sat());
51+
52+
if balance.total().to_sat() < 300000 {
53+
println!("Your wallet does not have sufficient balance for the following steps!");
54+
// Reveal a new address from your external keychain
55+
let address: AddressInfo = wallet.reveal_next_address(KeychainKind::External);
56+
println!(
57+
"Send coins to {} (address generated at index {})",
58+
address.address, address.index
59+
);
60+
exit(0)
61+
}
62+
63+
let local_outputs: Vec<LocalOutput> = wallet.list_unspent().collect();
64+
dbg!(&local_outputs.len());
65+
// dbg!(&local_outputs);
66+
let outpoints: Vec<KeychainIndexed<KeychainKind, OutPoint>> = local_outputs
67+
.into_iter()
68+
.map(|o| ((o.keychain, o.derivation_index), o.outpoint.clone()))
69+
.collect();
70+
71+
let mut descriptors_map = BTreeMap::new();
72+
descriptors_map.insert(KeychainKind::External, descriptor.clone());
73+
descriptors_map.insert(KeychainKind::Internal, change_descriptor.clone());
74+
75+
let input_candidates: Vec<InputGroup> = InputCandidates::new(
76+
&wallet.tx_graph(),
77+
&wallet.local_chain(),
78+
wallet.local_chain().tip().block_id(),
79+
outpoints,
80+
descriptors_map,
81+
Assets::new(),
82+
)?
83+
.into_single_groups(|_| true);
84+
85+
let recipient_address: Address<NetworkChecked> =
86+
Address::from_str("bcrt1qe908k9zu8m4jgzdddgg0lkj73yctfqueg7pea9")?
87+
.require_network(Network::Regtest)?;
88+
89+
let (selection, metrics) = create_selection(CreateSelectionParams::new(
90+
input_candidates,
91+
change_descriptor.at_derivation_index(0)?,
92+
vec![Output::with_script(
93+
recipient_address.script_pubkey(),
94+
Amount::from_sat(200_000),
95+
)],
96+
FeeRate::from_sat_per_vb(5).unwrap(),
97+
))?;
98+
99+
dbg!(&selection);
100+
101+
let (mut psbt, _) = create_psbt(CreatePsbtParams::new(selection))?;
102+
let signed = wallet.sign(&mut psbt, SignOptions::default())?;
103+
assert!(signed);
104+
let tx = psbt.extract_tx()?;
105+
106+
client.broadcast(&tx)?;
107+
dbg!("tx broadcast: {}", tx.compute_txid());
108+
109+
println!("Syncing wallet again...");
110+
let sync_request = wallet.start_sync_with_revealed_spks();
111+
let update_2: SyncResponse = client.sync(sync_request, PARALLEL_REQUESTS)?;
112+
113+
wallet.apply_update(update_2)?;
114+
115+
let balance_2 = wallet.balance();
116+
println!("Wallet balance: {} sat", balance_2.total().to_sat());
117+
118+
Ok(())
119+
}

tests/synopsis.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
use bdk_bitcoind_rpc::Emitter;
2-
use bdk_chain::{bdk_core, Balance};
2+
use bdk_chain::spk_client::{
3+
FullScanRequestBuilder, FullScanResponse, SyncRequestBuilder, SyncResponse,
4+
};
5+
use bdk_chain::{bdk_core, Balance, KeychainIndexed};
6+
use bdk_esplora::esplora_client;
7+
use bdk_esplora::esplora_client::Builder;
8+
use bdk_esplora::EsploraExt;
39
use bdk_testenv::{bitcoincore_rpc::RpcApi, TestEnv};
410
use bdk_tx::{
511
create_psbt, create_selection, filter_unspendable_now, group_by_spk, CreatePsbtParams,
612
CreateSelectionParams, InputCandidates, InputGroup, Output, Signer,
713
};
8-
use bitcoin::{absolute, key::Secp256k1, Address, Amount, BlockHash, FeeRate};
14+
use bdk_wallet::{AddressInfo, KeychainKind, LocalOutput, SignOptions};
15+
use bitcoin::address::NetworkChecked;
16+
use bitcoin::{absolute, key::Secp256k1, Address, Amount, BlockHash, FeeRate, Network, OutPoint};
17+
use miniscript::descriptor::KeyMap;
918
use miniscript::{Descriptor, DescriptorPublicKey};
19+
use std::collections::BTreeMap;
20+
use std::process::exit;
21+
use std::str::FromStr;
1022

1123
const EXTERNAL: &str = "external";
1224
const INTERNAL: &str = "internal";

0 commit comments

Comments
 (0)