From 1ab269e54881189b5f149d1941f804d780fd29d7 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Thu, 6 Feb 2025 18:07:38 -0500 Subject: [PATCH 1/4] example_cli: fix collect assets with `for_each_key` which allows creating txs for more descriptor types. --- example-crates/example_cli/src/lib.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index e965495e0..2e5670907 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -17,7 +17,7 @@ use bdk_chain::miniscript::{ descriptor::{DescriptorSecretKey, SinglePubKey}, plan::{Assets, Plan}, psbt::PsbtExt, - Descriptor, DescriptorPublicKey, + Descriptor, DescriptorPublicKey, ForEachKey, }; use bdk_chain::ConfirmationBlockTime; use bdk_chain::{ @@ -596,24 +596,20 @@ pub fn handle_commands( let chain = chain.lock().unwrap(); // collect assets we can sign for - let mut assets = Assets::new(); + let mut pks = vec![]; + for (_, desc) in graph.index.keychains() { + desc.for_each_key(|k| { + pks.push(k.clone()); + true + }); + } + let mut assets = Assets::new().add(pks); if let Some(n) = after { assets = assets.after(absolute::LockTime::from_consensus(n)); } if let Some(n) = older { assets = assets.older(relative::LockTime::from_consensus(n)?); } - for (_, desc) in graph.index.keychains() { - match desc { - Descriptor::Wpkh(wpkh) => { - assets = assets.add(wpkh.clone().into_inner()); - } - Descriptor::Tr(tr) => { - assets = assets.add(tr.internal_key().clone()); - } - _ => bail!("unsupported descriptor type"), - } - } create_tx(&mut graph, &*chain, &assets, coin_select, address, value)? }; From 3e640ff93d375a06678052befa45e6356949bff4 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Sun, 16 Feb 2025 09:40:41 -0500 Subject: [PATCH 2/4] feat(example_cli): add feerate option to psbt new cmd --- example-crates/example_cli/src/lib.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index 2e5670907..03f9141d7 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -150,6 +150,9 @@ pub enum PsbtCmd { value: u64, /// Recipient address address: Address, + /// Set the feerate of the tx (sat/vbyte) + #[clap(long, short, default_value = "1.0")] + feerate: Option, /// Set max absolute timelock (from consensus value) #[clap(long, short)] after: Option, @@ -260,6 +263,7 @@ pub fn create_tx( cs_algorithm: CoinSelectionAlgo, address: Address, value: u64, + feerate: f32, ) -> anyhow::Result<(Psbt, Option)> where O::Error: std::error::Error + Send + Sync + 'static, @@ -332,7 +336,10 @@ where .iter() .map(|output| (output.weight().to_wu() as u32, output.value.to_sat())), ), - fee: TargetFee::default(), + fee: TargetFee { + rate: FeeRate::from_sat_per_vb(feerate), + ..Default::default() + }, }; let change_policy = ChangePolicy { @@ -584,6 +591,7 @@ pub fn handle_commands( PsbtCmd::New { value, address, + feerate, after, older, coin_select, @@ -611,7 +619,15 @@ pub fn handle_commands( assets = assets.older(relative::LockTime::from_consensus(n)?); } - create_tx(&mut graph, &*chain, &assets, coin_select, address, value)? + create_tx( + &mut graph, + &*chain, + &assets, + coin_select, + address, + value, + feerate.expect("must have feerate"), + )? }; if let Some(ChangeInfo { From b9be1f43eeb4c822242962082de86ebd64f7410c Mon Sep 17 00:00:00 2001 From: valued mammal Date: Sun, 16 Feb 2025 09:41:21 -0500 Subject: [PATCH 3/4] deps(example_cli): bump `bdk_coin_select` to 0.4 --- example-crates/example_cli/Cargo.toml | 2 +- example-crates/example_cli/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example-crates/example_cli/Cargo.toml b/example-crates/example_cli/Cargo.toml index 290908a6a..0a467db84 100644 --- a/example-crates/example_cli/Cargo.toml +++ b/example-crates/example_cli/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bdk_chain = { path = "../../crates/chain", features = ["serde", "miniscript"]} -bdk_coin_select = "0.3.0" +bdk_coin_select = "0.4" bdk_file_store = { path = "../../crates/file_store" } bitcoin = { version = "0.32.0", features = ["base64"], default-features = false } diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index 03f9141d7..b60a88f76 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -292,7 +292,7 @@ where .map(|(plan, utxo)| { Candidate::new( utxo.txout.value.to_sat(), - plan.satisfaction_weight() as u32, + plan.satisfaction_weight() as u64, plan.witness_version().is_some(), ) }) @@ -334,7 +334,7 @@ where outputs: TargetOutputs::fund_outputs( outputs .iter() - .map(|output| (output.weight().to_wu() as u32, output.value.to_sat())), + .map(|output| (output.weight().to_wu(), output.value.to_sat())), ), fee: TargetFee { rate: FeeRate::from_sat_per_vb(feerate), From c855a0942bafe1211308d887e8bc0de82c95cc68 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Sun, 16 Feb 2025 10:35:31 -0500 Subject: [PATCH 4/4] refactor(example_cli): clean up CLI docs `--psbt` is a required arg for both Sign, Extract. Change example_esplora --parallel-requests to 2 for easier testing, debugging. --- example-crates/example_cli/src/lib.rs | 29 ++++++++++++---------- example-crates/example_esplora/src/main.rs | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index b60a88f76..2cb278491 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -147,8 +147,10 @@ pub enum PsbtCmd { /// Create a new PSBT. New { /// Amount to send in satoshis + #[clap(required = true)] value: u64, /// Recipient address + #[clap(required = true)] address: Address, /// Set the feerate of the tx (sat/vbyte) #[clap(long, short, default_value = "1.0")] @@ -168,20 +170,21 @@ pub enum PsbtCmd { }, /// Sign with a hot signer Sign { - /// PSBT - #[clap(long)] - psbt: Option, - /// Private descriptor - #[clap(long, short = 'd')] + /// Private descriptor [env: DESCRIPTOR=] + #[clap(long, short)] descriptor: Option, + /// PSBT + #[clap(long, short, required = true)] + psbt: String, }, /// Extract transaction Extract { /// PSBT + #[clap(long, short, required = true)] psbt: String, /// Whether to try broadcasting the tx - #[clap(long, short = 'b')] - try_broadcast: bool, + #[clap(long, short)] + broadcast: bool, #[clap(flatten)] chain_specific: S, }, @@ -451,7 +454,7 @@ pub fn handle_commands( chain: &Mutex, db: &Mutex>, network: Network, - broadcast: impl FnOnce(S, &Transaction) -> anyhow::Result<()>, + broadcast_fn: impl FnOnce(S, &Transaction) -> anyhow::Result<()>, cmd: Commands, ) -> anyhow::Result<()> { match cmd { @@ -671,7 +674,7 @@ pub fn handle_commands( Ok(()) } PsbtCmd::Sign { psbt, descriptor } => { - let mut psbt = Psbt::from_str(psbt.unwrap_or_default().as_str())?; + let mut psbt = Psbt::from_str(&psbt)?; let desc_str = match descriptor { Some(s) => s, @@ -709,20 +712,20 @@ pub fn handle_commands( Ok(()) } PsbtCmd::Extract { - try_broadcast, + broadcast, chain_specific, psbt, } => { - let mut psbt = Psbt::from_str(psbt.as_str())?; + let mut psbt = Psbt::from_str(&psbt)?; psbt.finalize_mut(&Secp256k1::new()) .map_err(|errors| anyhow::anyhow!("failed to finalize PSBT {errors:?}"))?; let tx = psbt.extract_tx()?; - if try_broadcast { + if broadcast { let mut graph = graph.lock().unwrap(); - match broadcast(chain_specific, &tx) { + match broadcast_fn(chain_specific, &tx) { Ok(_) => { println!("Broadcasted Tx: {}", tx.compute_txid()); diff --git a/example-crates/example_esplora/src/main.rs b/example-crates/example_esplora/src/main.rs index b6a58e4cf..8ef39c2fb 100644 --- a/example-crates/example_esplora/src/main.rs +++ b/example-crates/example_esplora/src/main.rs @@ -87,7 +87,7 @@ impl EsploraArgs { #[derive(Parser, Debug, Clone, PartialEq)] pub struct ScanOptions { /// Max number of concurrent esplora server requests. - #[clap(long, default_value = "5")] + #[clap(long, default_value = "2")] pub parallel_requests: usize, }