Skip to content
Open
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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfS
let mut wallet = match Wallet::load()
.descriptor(KeychainKind::External, Some(descriptor))
.descriptor(KeychainKind::Internal, Some(change_descriptor))
.extract_keys()
.check_network(network)
.load_wallet(&mut conn)?
{
Expand Down
1 change: 0 additions & 1 deletion examples/bitcoind_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ fn main() -> anyhow::Result<()> {
let wallet_opt = Wallet::load()
.descriptor(KeychainKind::External, Some(args.descriptor.clone()))
.descriptor(KeychainKind::Internal, args.change_descriptor.clone())
.extract_keys()
.check_network(args.network)
.load_wallet(&mut db)?;
let mut wallet = match wallet_opt {
Expand Down
10 changes: 9 additions & 1 deletion examples/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ use bitcoin::Network;
use miniscript::Descriptor;
use miniscript::policy::Concrete;

use bdk_wallet::descriptor::ExtractPolicy;
use bdk_wallet::descriptor::policy::BuildSatisfaction;
use bdk_wallet::signer::SignersContainer;
use bdk_wallet::{KeychainKind, Wallet};

/// Miniscript policy is a high level abstraction of spending conditions. Defined in the
Expand Down Expand Up @@ -68,7 +71,12 @@ fn main() -> Result<(), Box<dyn Error>> {

// BDK also has it's own `Policy` structure to represent the spending condition in a more
// human readable json format.
let spending_policy = wallet.policies(KeychainKind::External)?;
// Signers are caller-owned. This descriptor is compiled from a policy and holds no
// secrets, so an empty container is enough to extract the spending policy.
let signers = SignersContainer::default();
let spending_policy = wallet
.public_descriptor(KeychainKind::External)
.extract_policy(&signers, BuildSatisfaction::None, wallet.secp_ctx())?;
println!(
"The BDK spending policy: \n{}",
serde_json::to_string_pretty(&spending_policy)?
Expand Down
28 changes: 22 additions & 6 deletions examples/electrum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use bdk_wallet::Wallet;
use bdk_wallet::bitcoin::Amount;
use bdk_wallet::bitcoin::FeeRate;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::bitcoin::secp256k1::Secp256k1;
use bdk_wallet::chain::collections::HashSet;
use bdk_wallet::descriptor::IntoWalletDescriptor;
use bdk_wallet::miniscript::descriptor::KeyMapWrapper;
use bdk_wallet::psbt::PsbtUtils;
use bdk_wallet::rusqlite::Connection;
use bdk_wallet::{KeychainKind, SignOptions};
Expand All @@ -23,16 +26,24 @@ const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7
const ELECTRUM_URL: &str = "ssl://mempool.space:40002";

fn main() -> Result<(), anyhow::Error> {
// Keys are caller-owned: parse the descriptors ourselves and keep the KeyMap.
let secp = Secp256k1::new();
let (external_descriptor, mut keymap) =
EXTERNAL_DESC.into_wallet_descriptor(&secp, NETWORK.into())?;
let (internal_descriptor, internal_keymap) =
INTERNAL_DESC.into_wallet_descriptor(&secp, NETWORK.into())?;
keymap.extend(internal_keymap);
let signer = KeyMapWrapper::from(keymap);

let mut db = Connection::open(DB_PATH)?;
let wallet_opt = Wallet::load()
.descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
.descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
.extract_keys()
.descriptor(KeychainKind::External, Some(external_descriptor.clone()))
.descriptor(KeychainKind::Internal, Some(internal_descriptor.clone()))
.check_network(NETWORK)
.load_wallet(&mut db)?;
let mut wallet = match wallet_opt {
Some(wallet) => wallet,
None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
None => Wallet::create(external_descriptor, internal_descriptor)
.network(NETWORK)
.create_wallet(&mut db)?,
};
Expand Down Expand Up @@ -89,7 +100,9 @@ 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())?;
psbt.sign(&signer, wallet.secp_ctx())
.map_err(|(_, e)| anyhow::anyhow!("failed to sign PSBT: {e:?}"))?;
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 +137,10 @@ 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())?;
bumped_psbt
.sign(&signer, wallet.secp_ctx())
.map_err(|(_, e)| anyhow::anyhow!("failed to sign PSBT: {e:?}"))?;
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
28 changes: 22 additions & 6 deletions examples/esplora_async.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use bdk_esplora::{EsploraAsyncExt, esplora_client};
use bdk_wallet::bitcoin::secp256k1::Secp256k1;
use bdk_wallet::descriptor::IntoWalletDescriptor;
use bdk_wallet::miniscript::descriptor::KeyMapWrapper;
use bdk_wallet::{
KeychainKind, SignOptions, Wallet,
bitcoin::{Amount, FeeRate, Network},
Expand All @@ -20,16 +23,24 @@ const ESPLORA_URL: &str = "https://mempool.space/testnet4/api";

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
// Keys are caller-owned: parse the descriptors ourselves and keep the KeyMap.
let secp = Secp256k1::new();
let (external_descriptor, mut keymap) =
EXTERNAL_DESC.into_wallet_descriptor(&secp, NETWORK.into())?;
let (internal_descriptor, internal_keymap) =
INTERNAL_DESC.into_wallet_descriptor(&secp, NETWORK.into())?;
keymap.extend(internal_keymap);
let signer = KeyMapWrapper::from(keymap);

let mut db = Connection::open(DB_PATH)?;
let wallet_opt = Wallet::load()
.descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
.descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
.extract_keys()
.descriptor(KeychainKind::External, Some(external_descriptor.clone()))
.descriptor(KeychainKind::Internal, Some(internal_descriptor.clone()))
.check_network(NETWORK)
.load_wallet(&mut db)?;
let mut wallet = match wallet_opt {
Some(wallet) => wallet,
None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
None => Wallet::create(external_descriptor, internal_descriptor)
.network(NETWORK)
.create_wallet(&mut db)?,
};
Expand Down Expand Up @@ -83,7 +94,9 @@ 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())?;
psbt.sign(&signer, wallet.secp_ctx())
.map_err(|(_, e)| anyhow::anyhow!("failed to sign PSBT: {e:?}"))?;
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 +130,10 @@ 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())?;
bumped_psbt
.sign(&signer, wallet.secp_ctx())
.map_err(|(_, e)| anyhow::anyhow!("failed to sign PSBT: {e:?}"))?;
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
28 changes: 22 additions & 6 deletions examples/esplora_blocking.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use bdk_esplora::{EsploraExt, esplora_client};
use bdk_wallet::bitcoin::secp256k1::Secp256k1;
use bdk_wallet::descriptor::IntoWalletDescriptor;
use bdk_wallet::miniscript::descriptor::KeyMapWrapper;
use bdk_wallet::rusqlite::Connection;
use bdk_wallet::{
KeychainKind, SignOptions, Wallet,
Expand All @@ -20,16 +23,24 @@ const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7
const ESPLORA_URL: &str = "https://mempool.space/testnet4/api";

fn main() -> Result<(), anyhow::Error> {
// Keys are caller-owned: parse the descriptors ourselves and keep the KeyMap.
let secp = Secp256k1::new();
let (external_descriptor, mut keymap) =
EXTERNAL_DESC.into_wallet_descriptor(&secp, NETWORK.into())?;
let (internal_descriptor, internal_keymap) =
INTERNAL_DESC.into_wallet_descriptor(&secp, NETWORK.into())?;
keymap.extend(internal_keymap);
let signer = KeyMapWrapper::from(keymap);

let mut db = Connection::open(DB_PATH)?;
let wallet_opt = Wallet::load()
.descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
.descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
.extract_keys()
.descriptor(KeychainKind::External, Some(external_descriptor.clone()))
.descriptor(KeychainKind::Internal, Some(internal_descriptor.clone()))
.check_network(NETWORK)
.load_wallet(&mut db)?;
let mut wallet = match wallet_opt {
Some(wallet) => wallet,
None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
None => Wallet::create(external_descriptor, internal_descriptor)
.network(NETWORK)
.create_wallet(&mut db)?,
};
Expand Down Expand Up @@ -78,7 +89,9 @@ 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())?;
psbt.sign(&signer, wallet.secp_ctx())
.map_err(|(_, e)| anyhow::anyhow!("failed to sign PSBT: {e:?}"))?;
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 +126,10 @@ 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())?;
new_psbt
.sign(&signer, wallet.secp_ctx())
.map_err(|(_, e)| anyhow::anyhow!("failed to sign PSBT: {e:?}"))?;
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
2 changes: 1 addition & 1 deletion src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub mod template;

pub use self::checksum::calc_checksum;
pub use self::error::Error as DescriptorError;
pub use self::policy::Policy;
pub use self::policy::{Condition, Policy};
use self::template::DescriptorTemplateOut;
use crate::keys::{IntoDescriptorKey, KeyError};
use crate::wallet::{signer::SignersContainer, utils::SecpCtx};
Expand Down
11 changes: 10 additions & 1 deletion src/descriptor/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,16 @@ impl Condition {
}
}

pub(crate) fn merge(mut self, other: &Condition) -> Result<Self, PolicyError> {
/// Merge two conditions, returning the strictest requirement satisfying both.
///
/// Use this to combine the conditions of every keychain from which inputs may be selected,
/// before handing the result to
/// [`TxBuilder::set_condition`](crate::wallet::tx_builder::TxBuilder::set_condition).
///
/// # Errors
///
/// If the two conditions are incompatible, e.g. a height-based and a time-based timelock.
pub fn merge(mut self, other: &Condition) -> Result<Self, PolicyError> {
match (self.csv, other.csv) {
(Some(a), Some(b)) => self.csv = Some(Self::merge_nsequence(a, b)?),
(None, any) => self.csv = any,
Expand Down
Loading