Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions examples/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Common signing utilities for bdk_wallet examples
//!
//! This module provides the `SignerWrapper` struct and related utilities
//! that enable signing functionality for the wallet examples. These utilities
//! wrap the KeyMap type to implement the GetKey trait, allowing examples
//! to sign transactions and PSBTs.
//!
//! Note: This module is only required temporarily until miniscript 12.x is released,
//! which will include signing capabilities for KeyMap natively.

use miniscript::descriptor::{DescriptorSecretKey, KeyMap};
use std::collections::BTreeMap;

use bitcoin::{
key::Secp256k1,
psbt::{GetKey, GetKeyError, KeyRequest},
};

#[derive(Debug, Clone)]
/// A wrapper over the [`KeyMap`] type that has the `GetKey` trait implementation for signing.
pub struct SignerWrapper {
key_map: KeyMap,
}

impl SignerWrapper {
/// Creates a new [`SignerWrapper`] for the given [`KeyMap`].
pub fn new(key_map: KeyMap) -> Self {
Self { key_map }
}
}

impl GetKey for SignerWrapper {
type Error = GetKeyError;

fn get_key<C: bitcoin::secp256k1::Signing>(
&self,
key_request: KeyRequest,
secp: &bitcoin::key::Secp256k1<C>,
) -> Result<Option<bitcoin::PrivateKey>, Self::Error> {
for key_map in self.key_map.iter() {
let (_, desc_sk) = key_map;
let wrapper = DescriptorSecretKeyWrapper::new(desc_sk.clone());
match wrapper.get_key(key_request.clone(), secp) {
Ok(Some(private_key)) => return Ok(Some(private_key)),
Ok(None) => continue,
// TODO: (@leonardo) how should we handle this ?
// we can't error-out on this, because the valid signing key can be in the next
// iterations.
Err(_) => continue,
}
}
Ok(None)
}
}

/// Wrapper for DescriptorSecretKey to implement GetKey trait
pub struct DescriptorSecretKeyWrapper(DescriptorSecretKey);

impl DescriptorSecretKeyWrapper {
/// Creates a new DescriptorSecretKeyWrapper
pub fn new(desc_sk: DescriptorSecretKey) -> Self {
Self(desc_sk)
}
}

impl GetKey for DescriptorSecretKeyWrapper {
type Error = GetKeyError;

fn get_key<C: bitcoin::secp256k1::Signing>(
&self,
key_request: KeyRequest,
secp: &Secp256k1<C>,
) -> Result<Option<bitcoin::PrivateKey>, Self::Error> {
match (&self.0, key_request) {
(DescriptorSecretKey::Single(single_priv), key_request) => {
let private_key = single_priv.key;
let public_key = private_key.public_key(secp);
let pubkey_map = BTreeMap::from([(public_key, private_key)]);
return pubkey_map.get_key(key_request, secp);
}
(DescriptorSecretKey::XPrv(descriptor_xkey), KeyRequest::Pubkey(public_key)) => {
let private_key = descriptor_xkey.xkey.private_key;
let pk = private_key.public_key(secp);
if public_key.inner.eq(&pk) {
return Ok(Some(
descriptor_xkey
.xkey
.derive_priv(secp, &descriptor_xkey.derivation_path)
.map_err(GetKeyError::Bip32)?
.to_priv(),
));
}
}
(
DescriptorSecretKey::XPrv(descriptor_xkey),
ref key_request @ KeyRequest::Bip32(ref key_source),
) => {
if let Some(key) = descriptor_xkey.xkey.get_key(key_request.clone(), secp)? {
return Ok(Some(key));
}

if let Some(_derivation_path) = descriptor_xkey.matches(key_source, secp) {
let (_fp, derivation_path) = key_source;

if let Some((_fp, origin_derivation_path)) = &descriptor_xkey.origin {
let derivation_path = &derivation_path[origin_derivation_path.len()..];
return Ok(Some(
descriptor_xkey
.xkey
.derive_priv(secp, &derivation_path)
.map_err(GetKeyError::Bip32)?
.to_priv(),
));
} else {
return Ok(Some(
descriptor_xkey
.xkey
.derive_priv(secp, derivation_path)
.map_err(GetKeyError::Bip32)?
.to_priv(),
));
};
}
}
(DescriptorSecretKey::XPrv(_), KeyRequest::XOnlyPubkey(_)) => {
return Err(GetKeyError::NotSupported)
}
(DescriptorSecretKey::MultiXPrv(_), _) => unimplemented!(),
_ => unreachable!(),
}
Ok(None)
}
}
21 changes: 19 additions & 2 deletions examples/electrum.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
mod common;

use bdk_electrum::electrum_client;
use bdk_electrum::BdkElectrumClient;
use bdk_wallet::bitcoin::key::Secp256k1;
use bdk_wallet::bitcoin::Amount;
use bdk_wallet::bitcoin::FeeRate;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::chain::collections::HashSet;
use bdk_wallet::miniscript::Descriptor;
use bdk_wallet::psbt::PsbtUtils;
use bdk_wallet::rusqlite::Connection;
use bdk_wallet::Wallet;
Expand Down Expand Up @@ -89,7 +93,19 @@ fn main() -> Result<(), anyhow::Error> {
tx_builder.fee_rate(target_fee_rate);

let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;

let secp = Secp256k1::new();

let (_, external_keymap) = Descriptor::parse_descriptor(&secp, EXTERNAL_DESC)?;
let (_, internal_keymap) = Descriptor::parse_descriptor(&secp, INTERNAL_DESC)?;
let key_map = external_keymap.into_iter().chain(internal_keymap).collect();

// Using the signer implementation from the common module.
// Note: This is temporary until miniscript 12.x is released with native KeyMap signing.
let signer = common::SignerWrapper::new(key_map);
let _ = psbt.sign(&signer, &secp);

let finalized = wallet.finalize_psbt(&mut psbt, SignOptions::default())?;
assert!(finalized);
let original_fee = psbt.fee_amount().unwrap();
let tx_feerate = psbt.fee_rate().unwrap();
Expand Down Expand Up @@ -124,7 +140,8 @@ fn main() -> Result<(), anyhow::Error> {
let mut builder = wallet.build_fee_bump(txid).expect("failed to bump tx");
builder.fee_rate(feerate);
let mut bumped_psbt = builder.finish().unwrap();
let finalize_btx = wallet.sign(&mut bumped_psbt, SignOptions::default())?;
let _ = bumped_psbt.sign(&signer, &secp);
let finalize_btx = wallet.finalize_psbt(&mut bumped_psbt, SignOptions::default())?;
assert!(finalize_btx);
let new_fee = bumped_psbt.fee_amount().unwrap();
let bumped_tx = bumped_psbt.extract_tx()?;
Expand Down
20 changes: 18 additions & 2 deletions examples/esplora_async.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod common;

use bdk_esplora::{esplora_client, EsploraAsyncExt};
use bdk_wallet::{
bitcoin::{Amount, FeeRate, Network},
miniscript::Descriptor,
psbt::PsbtUtils,
rusqlite::Connection,
KeychainKind, SignOptions, Wallet,
Expand Down Expand Up @@ -83,7 +86,19 @@ async fn main() -> Result<(), anyhow::Error> {
tx_builder.fee_rate(target_fee_rate);

let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;

let secp = bdk_wallet::bitcoin::key::Secp256k1::new();

let (_, external_keymap) = Descriptor::parse_descriptor(&secp, EXTERNAL_DESC)?;
let (_, internal_keymap) = Descriptor::parse_descriptor(&secp, INTERNAL_DESC)?;
let key_map = external_keymap.into_iter().chain(internal_keymap).collect();

// Using the signer implementation from the common module.
// Note: This is temporary until miniscript 12.x is released with native KeyMap signing.
let signer = common::SignerWrapper::new(key_map);
let _ = psbt.sign(&signer, &secp);

let finalized = wallet.finalize_psbt(&mut psbt, SignOptions::default())?;
assert!(finalized);
let original_fee = psbt.fee_amount().unwrap();
let tx_feerate = psbt.fee_rate().unwrap();
Expand Down Expand Up @@ -117,7 +132,8 @@ async fn main() -> Result<(), anyhow::Error> {
let mut builder = wallet.build_fee_bump(txid).expect("failed to bump tx");
builder.fee_rate(feerate);
let mut bumped_psbt = builder.finish().unwrap();
let finalize_btx = wallet.sign(&mut bumped_psbt, SignOptions::default())?;
let _ = bumped_psbt.sign(&signer, &secp);
let finalize_btx = wallet.finalize_psbt(&mut bumped_psbt, SignOptions::default())?;
assert!(finalize_btx);
let new_fee = bumped_psbt.fee_amount().unwrap();
let bumped_tx = bumped_psbt.extract_tx()?;
Expand Down
20 changes: 18 additions & 2 deletions examples/esplora_blocking.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
mod common;

use bdk_esplora::{esplora_client, EsploraExt};
use bdk_wallet::miniscript::Descriptor;
use bdk_wallet::rusqlite::Connection;
use bdk_wallet::{
bitcoin::{Amount, FeeRate, Network},
Expand Down Expand Up @@ -78,7 +81,19 @@ fn main() -> Result<(), anyhow::Error> {
tx_builder.fee_rate(target_fee_rate);

let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;

let secp = bdk_wallet::bitcoin::key::Secp256k1::new();

let (_, external_keymap) = Descriptor::parse_descriptor(&secp, EXTERNAL_DESC)?;
let (_, internal_keymap) = Descriptor::parse_descriptor(&secp, INTERNAL_DESC)?;
let key_map = external_keymap.into_iter().chain(internal_keymap).collect();

// Using the signer implementation from the common module.
// Note: This is temporary until miniscript 12.x is released with native KeyMap signing.
let signer = common::SignerWrapper::new(key_map);
let _ = psbt.sign(&signer, &secp);

let finalized = wallet.finalize_psbt(&mut psbt, SignOptions::default())?;
assert!(finalized);
let original_fee = psbt.fee_amount().unwrap();
let tx_feerate = psbt.fee_rate().unwrap();
Expand Down Expand Up @@ -113,7 +128,8 @@ fn main() -> Result<(), anyhow::Error> {
let mut builder = wallet.build_fee_bump(txid).unwrap();
builder.fee_rate(feerate);
let mut new_psbt = builder.finish().unwrap();
let finalize_tx = wallet.sign(&mut new_psbt, SignOptions::default())?;
let _ = new_psbt.sign(&signer, &secp);
let finalize_tx = wallet.finalize_psbt(&mut new_psbt, SignOptions::default())?;
assert!(finalize_tx);
let new_fee = new_psbt.fee_amount().unwrap();
let bumped_tx = new_psbt.extract_tx()?;
Expand Down
Loading