Skip to content

Commit 3918196

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

File tree

2 files changed

+124
-6
lines changed

2 files changed

+124
-6
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: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
use std::collections::BTreeMap;
2+
use std::process::exit;
3+
use std::str::FromStr;
14
use bdk_bitcoind_rpc::Emitter;
2-
use bdk_chain::{bdk_core, Balance};
5+
use bdk_chain::{bdk_core, Balance, KeychainIndexed};
6+
use bdk_chain::spk_client::{FullScanRequestBuilder, FullScanResponse, SyncRequestBuilder, SyncResponse};
37
use bdk_testenv::{bitcoincore_rpc::RpcApi, TestEnv};
4-
use bdk_tx::{
5-
create_psbt, create_selection, get_candidate_inputs, CreatePsbtParams, CreateSelectionParams,
6-
InputGroup, Output, Signer,
7-
};
8-
use bitcoin::{key::Secp256k1, Address, Amount, BlockHash, FeeRate};
8+
use bdk_wallet::{AddressInfo, KeychainKind, LocalOutput, SignOptions};
9+
use bdk_tx::{create_psbt, create_selection, get_candidate_inputs, CreatePsbtParams, CreateSelectionParams, InputGroup, Output, Signer};
10+
use bitcoin::{key::Secp256k1, Address, Amount, BlockHash, FeeRate, Network, OutPoint};
911
use miniscript::{plan::Assets, Descriptor, DescriptorPublicKey};
12+
use bdk_esplora::esplora_client;
13+
use bdk_esplora::esplora_client::Builder;
14+
use bdk_esplora::EsploraExt;
15+
use bitcoin::address::NetworkChecked;
16+
use miniscript::descriptor::KeyMap;
1017

1118
const EXTERNAL: &str = "external";
1219
const INTERNAL: &str = "internal";
@@ -148,3 +155,112 @@ fn synopsis() -> anyhow::Result<()> {
148155

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

0 commit comments

Comments
 (0)