Skip to content

Commit eac7175

Browse files
exploring bdk-tx with bdk_wallet
1 parent 3e1290b commit eac7175

File tree

2 files changed

+127
-2
lines changed

2 files changed

+127
-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/synopsis.rs

Lines changed: 125 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, get_candidate_inputs, CreatePsbtParams, CreateSelectionParams,
612
InputGroup, Output, Signer,
713
};
8-
use bitcoin::{key::Secp256k1, Address, Amount, BlockHash, FeeRate};
14+
use bdk_wallet::{AddressInfo, KeychainKind, LocalOutput, SignOptions};
15+
use bitcoin::address::NetworkChecked;
16+
use bitcoin::{key::Secp256k1, Address, Amount, BlockHash, FeeRate, Network, OutPoint};
17+
use miniscript::descriptor::KeyMap;
918
use miniscript::{plan::Assets, 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";
@@ -148,3 +160,114 @@ fn synopsis() -> anyhow::Result<()> {
148160

149161
Ok(())
150162
}
163+
164+
#[test]
165+
fn bdk_wallet_simple_tx() -> anyhow::Result<()> {
166+
const STOP_GAP: usize = 20;
167+
const PARALLEL_REQUESTS: usize = 1;
168+
let secp = bitcoin::secp256k1::Secp256k1::new();
169+
170+
let descriptor_private: &str = "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/0/*)#usy7l3tt";
171+
let change_descriptor_private: &str = "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/1/*)#dyplzymn";
172+
173+
let (descriptor, _): (Descriptor<DescriptorPublicKey>, KeyMap) = Descriptor::parse_descriptor(&secp, "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/0/*)#usy7l3tt")?;
174+
let (change_descriptor, _): (Descriptor<DescriptorPublicKey>, KeyMap) = Descriptor::parse_descriptor(&secp, "tr(tprv8ZgxMBicQKsPdNRGG6HuFapxQCFxsDDf7TDsV8tdUgZDdiiyA6dB2ssN4RSXyp52V3MRBm4KqAps3Txng59rNMUtUEtMPDphKkKDXmamd2T/86'/1'/0'/1/*)#dyplzymn")?;
175+
176+
// Create the wallet
177+
let mut wallet = bdk_wallet::Wallet::create(descriptor_private, change_descriptor_private)
178+
.network(Network::Regtest)
179+
.create_wallet_no_persist()?;
180+
181+
let client: esplora_client::BlockingClient =
182+
Builder::new("http://127.0.0.1:3002").build_blocking();
183+
184+
println!("Syncing wallet...");
185+
let full_scan_request: FullScanRequestBuilder<KeychainKind> = wallet.start_full_scan();
186+
let update: FullScanResponse<KeychainKind> =
187+
client.full_scan(full_scan_request, STOP_GAP, PARALLEL_REQUESTS)?;
188+
189+
// Apply the update from the full scan to the wallet
190+
wallet.apply_update(update)?;
191+
192+
let balance = wallet.balance();
193+
println!("Wallet balance: {} sat", balance.total().to_sat());
194+
195+
if balance.total().to_sat() < 300000 {
196+
println!("Your wallet does not have sufficient balance for the following steps!");
197+
// Reveal a new address from your external keychain
198+
let address: AddressInfo = wallet.reveal_next_address(KeychainKind::External);
199+
println!(
200+
"Send coins to {} (address generated at index {})",
201+
address.address, address.index
202+
);
203+
exit(0)
204+
}
205+
206+
let local_outputs: Vec<LocalOutput> = wallet.list_unspent().collect();
207+
dbg!(&local_outputs.len());
208+
// dbg!(&local_outputs);
209+
let outpoints: Vec<KeychainIndexed<KeychainKind, OutPoint>> = local_outputs
210+
.into_iter()
211+
.map(|o| ((o.keychain, o.derivation_index), o.outpoint.clone()))
212+
.collect();
213+
214+
let mut descriptors_map = BTreeMap::new();
215+
descriptors_map.insert(KeychainKind::External, descriptor.clone());
216+
descriptors_map.insert(KeychainKind::Internal, change_descriptor.clone());
217+
218+
let input_candidates_individual = get_candidate_inputs(
219+
&wallet.tx_graph(),
220+
&wallet.local_chain(),
221+
outpoints,
222+
descriptors_map,
223+
Assets::new(),
224+
)?;
225+
// dbg!(&input_candidates_0);
226+
227+
let recipient_address: Address<NetworkChecked> =
228+
Address::from_str("bcrt1qe908k9zu8m4jgzdddgg0lkj73yctfqueg7pea9")?
229+
.require_network(Network::Regtest)?;
230+
231+
let input_candidates_groups: Vec<InputGroup> = input_candidates_individual
232+
.into_iter()
233+
.map(|i| i.into())
234+
.collect();
235+
236+
let selection = create_selection(CreateSelectionParams {
237+
input_candidates: input_candidates_groups,
238+
must_spend: Default::default(),
239+
change_descriptor: change_descriptor.at_derivation_index(0)?,
240+
target_feerate: FeeRate::from_sat_per_vb(5).unwrap(),
241+
long_term_feerate: Some(FeeRate::from_sat_per_vb(1).unwrap()),
242+
target_outputs: vec![Output::with_script(
243+
recipient_address.script_pubkey(),
244+
Amount::from_sat(200_000),
245+
)],
246+
max_rounds: 100_000,
247+
})?;
248+
249+
dbg!(&selection.inputs.len());
250+
251+
let (mut psbt, _) = create_psbt(CreatePsbtParams {
252+
inputs: selection.inputs,
253+
outputs: selection.outputs,
254+
..Default::default()
255+
})?;
256+
let signed = wallet.sign(&mut psbt, SignOptions::default())?;
257+
assert!(signed);
258+
let tx = psbt.extract_tx()?;
259+
260+
client.broadcast(&tx)?;
261+
dbg!("tx broadcast: {}", tx.compute_txid());
262+
263+
println!("Syncing wallet again...");
264+
let sync_request = wallet.start_sync_with_revealed_spks();
265+
let update_2: SyncResponse = client.sync(sync_request, PARALLEL_REQUESTS)?;
266+
267+
wallet.apply_update(update_2)?;
268+
269+
let balance_2 = wallet.balance();
270+
println!("Wallet balance: {} sat", balance_2.total().to_sat());
271+
272+
Ok(())
273+
}

0 commit comments

Comments
 (0)