Merge bitcoindevkit/bdk#1023: Update rust bitcoin

1da3b304bb0aa29e3bb013e0a583c34ec390b32d ci: Pin rustls to keep the MSRV (Daniela Brozzoni)
792b39fa92bfa1218a33bd6ded257fc105aec67c Explicitly deny multipath keys (Daniela Brozzoni)
b73385dbd2e4c46d0553fa6990e564743b705507 Update wallet_electrum to rust-bitcoin 0.30.0 (Daniela Brozzoni)
3dac3f9bba790e8c40a157aa6c9dc3fdf026255e Update example_electrum to rust-bitcoin 0.30.0 (Daniela Brozzoni)
2949bdc7b8ab40c44803fad8fccb2a8f420d1566 Update example_cli to rust-bitcoin 0.30.0 (Daniela Brozzoni)
468d2a0a3b349adb12564847d50a5e5b30c9df3e Update tmp_plan to rust-bitcoin 0.30.0 (Daniela Brozzoni)
b8ac16d03c070ead550d4b458ddad3159cea0001 Update coin_select to rust-bitcoin 0.30.0 (Daniela Brozzoni)
6c29e53ee8b20bfa73a693ef88a14a925492a19b Update wallet_esplora and wallet_esplora_async to... ...rust-bitcoin 0.30.0 (Daniela Brozzoni)
6eb079576f4387db41ec1b9fe729e1d9ab4f8c01 Update crates/esplora to rust-bitcoin 0.30.0 (Daniela Brozzoni)
91b0f0ba2913287449925c86761f363583e59190 Update crates/electrum to bitcoin 0.30.0 (Daniela Brozzoni)
f4e3ba3265fc61bf61ffb54486abfe005ce236d7 Update bdk to bitcoin 0.30.0 (Daniela Brozzoni)
853d3617514edebd0d7b6542a78b5874225a5473 Update bdk_chain to bitcoin 0.30.0 (Daniela Brozzoni)

Pull request description:

  ### Description

  Updates to rust-bitcoin 0.30.0 and miniscript 0.10.0

  Not covered in this PR:
  - https://github.com/bitcoindevkit/bdk/issues/1036. Although the latter is deprecated, I think it's better if I update it in a separate PR, as this one is pretty big already.
  - https://github.com/bitcoindevkit/bdk/issues/1037
  - https://github.com/bitcoindevkit/bdk/issues/1038

  Heads up, I'm explicitly denying multipath descriptors until we have better tests for them. See the commit message of 23fba7aee9b55bf59db73c296b9fb025a256502e

  ### Changelog notice

  - Update to `rust-bitcoin` 0.30.0 and `miniscript` 10.0.0

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  evanlinjin:
    ACK 1da3b304bb0aa29e3bb013e0a583c34ec390b32d

Tree-SHA512: ff1457ed711f9f8cdb446ea10aaf124632f539c02406da94317d8dc38013b321217d3bdcb2df4bd72b2ed92116b52e9c6b98ee91d4d508a579c67449a7caa549
This commit is contained in:
志宇 2023-08-04 12:20:47 +08:00
commit feafaaca31
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
52 changed files with 800 additions and 552 deletions

View File

@ -29,7 +29,7 @@ jobs:
uses: Swatinem/rust-cache@v2.2.1 uses: Swatinem/rust-cache@v2.2.1
- name: Pin dependencies for MSRV - name: Pin dependencies for MSRV
if: matrix.rust.version == '1.57.0' if: matrix.rust.version == '1.57.0'
run: cargo update -p log --precise "0.4.18" && cargo update -p tempfile --precise "3.6.0" run: cargo update -p log --precise "0.4.18" && cargo update -p tempfile --precise "3.6.0" && cargo update -p rustls:0.21.6 --precise "0.21.1"
- name: Build - name: Build
run: cargo build ${{ matrix.features }} run: cargo build ${{ matrix.features }}
- name: Test - name: Test

View File

@ -61,6 +61,8 @@ To build with the MSRV you will need to pin dependencies as follows:
``` ```
# log 0.4.19 has MSRV 1.60.0+ # log 0.4.19 has MSRV 1.60.0+
cargo update -p log --precise "0.4.18" cargo update -p log --precise "0.4.18"
# tempfile 3.7.0 has MSRV 1.63.0 # tempfile 3.7.0 has MSRV 1.63.0+
cargo update -p tempfile --precise "3.6.0" cargo update -p tempfile --precise "3.6.0"
# rustls 0.21.2 has MSRV 1.60.0+
cargo update -p rustls:0.21.6 --precise "0.21.1"
``` ```

View File

@ -15,14 +15,14 @@ rust-version = "1.57"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
rand = "^0.8" rand = "^0.8"
miniscript = { version = "9", features = ["serde"], default-features = false } miniscript = { version = "10.0.0", features = ["serde"], default-features = false }
bitcoin = { version = "0.29", features = ["serde", "base64", "rand"], default-features = false } bitcoin = { version = "0.30.0", features = ["serde", "base64", "rand-std"], default-features = false }
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1.0" } serde_json = { version = "^1.0" }
bdk_chain = { path = "../chain", version = "0.5.0", features = ["miniscript", "serde"], default-features = false } bdk_chain = { path = "../chain", version = "0.5.0", features = ["miniscript", "serde"], default-features = false }
# Optional dependencies # Optional dependencies
hwi = { version = "0.5", optional = true, features = [ "use-miniscript"] } hwi = { version = "0.7.0", optional = true, features = [ "miniscript"] }
bip39 = { version = "1.0.1", optional = true } bip39 = { version = "1.0.1", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
@ -46,8 +46,6 @@ dev-getrandom-wasm = ["getrandom/js"]
[dev-dependencies] [dev-dependencies]
lazy_static = "1.4" lazy_static = "1.4"
env_logger = "0.7" env_logger = "0.7"
# Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released
base64 = "^0.13"
assert_matches = "1.5.0" assert_matches = "1.5.0"
[package.metadata.docs.rs] [package.metadata.docs.rs]

View File

@ -137,7 +137,7 @@ fn main() {
<!-- use bdk::electrum_client::Client; --> <!-- use bdk::electrum_client::Client; -->
<!-- use bdk::wallet::AddressIndex::New; --> <!-- use bdk::wallet::AddressIndex::New; -->
<!-- use base64; --> <!-- use bitcoin::base64; -->
<!-- use bdk::bitcoin::consensus::serialize; --> <!-- use bdk::bitcoin::consensus::serialize; -->
<!-- use bdk::bitcoin::Network; --> <!-- use bdk::bitcoin::Network; -->
@ -174,7 +174,7 @@ fn main() {
<!-- ```rust,no_run --> <!-- ```rust,no_run -->
<!-- use bdk::{Wallet, SignOptions}; --> <!-- use bdk::{Wallet, SignOptions}; -->
<!-- use base64; --> <!-- use bitcoin::base64; -->
<!-- use bdk::bitcoin::consensus::deserialize; --> <!-- use bdk::bitcoin::consensus::deserialize; -->
<!-- use bdk::bitcoin::Network; --> <!-- use bdk::bitcoin::Network; -->

View File

@ -6,8 +6,8 @@
// You may not use this file except in accordance with one or both of these // You may not use this file except in accordance with one or both of these
// licenses. // licenses.
use bdk::bitcoin::bip32::DerivationPath;
use bdk::bitcoin::secp256k1::Secp256k1; use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::DerivationPath;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::descriptor; use bdk::descriptor;
use bdk::descriptor::IntoWalletDescriptor; use bdk::descriptor::IntoWalletDescriptor;

View File

@ -516,13 +516,14 @@ macro_rules! descriptor {
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey}; use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
$crate::impl_top_level_pk!(Pkh, $crate::miniscript::Legacy, $key) $crate::impl_top_level_pk!(Pkh, $crate::miniscript::Legacy, $key)
.and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
.map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Pkh(a), b, c)) .map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Pkh(a), b, c))
}); });
( wpkh ( $key:expr ) ) => ({ ( wpkh ( $key:expr ) ) => ({
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey}; use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
$crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key) $crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key)
.and_then(|(a, b, c)| Ok((a?, b, c))) .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
.map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Wpkh(a), b, c)) .map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Wpkh(a), b, c))
}); });
( sh ( wpkh ( $key:expr ) ) ) => ({ ( sh ( wpkh ( $key:expr ) ) ) => ({
@ -532,7 +533,7 @@ macro_rules! descriptor {
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Sh}; use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Sh};
$crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key) $crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key)
.and_then(|(a, b, c)| Ok((a?, b, c))) .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
.and_then(|(a, b, c)| Ok((Descriptor::<DescriptorPublicKey>::Sh(Sh::new_wpkh(a.into_inner())?), b, c))) .and_then(|(a, b, c)| Ok((Descriptor::<DescriptorPublicKey>::Sh(Sh::new_wpkh(a.into_inner())?), b, c)))
}); });
( sh ( $( $minisc:tt )* ) ) => ({ ( sh ( $( $minisc:tt )* ) ) => ({
@ -702,7 +703,7 @@ macro_rules! fragment {
$crate::keys::make_pkh($key, &secp) $crate::keys::make_pkh($key, &secp)
}); });
( after ( $value:expr ) ) => ({ ( after ( $value:expr ) ) => ({
$crate::impl_leaf_opcode_value!(After, $crate::bitcoin::PackedLockTime($value)) // TODO!! https://github.com/rust-bitcoin/rust-bitcoin/issues/1302 $crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value))
}); });
( older ( $value:expr ) ) => ({ ( older ( $value:expr ) ) => ({
$crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!! $crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!!
@ -796,7 +797,6 @@ macro_rules! fragment {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use alloc::string::ToString; use alloc::string::ToString;
use bitcoin::hashes::hex::ToHex;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use miniscript::descriptor::{DescriptorPublicKey, KeyMap}; use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
use miniscript::{Descriptor, Legacy, Segwitv0}; use miniscript::{Descriptor, Legacy, Segwitv0};
@ -805,8 +805,8 @@ mod test {
use crate::descriptor::{DescriptorError, DescriptorMeta}; use crate::descriptor::{DescriptorError, DescriptorMeta};
use crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks}; use crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
use bitcoin::bip32;
use bitcoin::network::constants::Network::{Bitcoin, Regtest, Signet, Testnet}; use bitcoin::network::constants::Network::{Bitcoin, Regtest, Signet, Testnet};
use bitcoin::util::bip32;
use bitcoin::PrivateKey; use bitcoin::PrivateKey;
// test the descriptor!() macro // test the descriptor!() macro
@ -822,18 +822,15 @@ mod test {
assert_eq!(desc.is_witness(), is_witness); assert_eq!(desc.is_witness(), is_witness);
assert_eq!(!desc.has_wildcard(), is_fixed); assert_eq!(!desc.has_wildcard(), is_fixed);
for i in 0..expected.len() { for i in 0..expected.len() {
let index = i as u32; let child_desc = desc
let child_desc = if !desc.has_wildcard() { .at_derivation_index(i as u32)
desc.at_derivation_index(0) .expect("i is not hardened");
} else {
desc.at_derivation_index(index)
};
let address = child_desc.address(Regtest); let address = child_desc.address(Regtest);
if let Ok(address) = address { if let Ok(address) = address {
assert_eq!(address.to_string(), *expected.get(i).unwrap()); assert_eq!(address.to_string(), *expected.get(i).unwrap());
} else { } else {
let script = child_desc.script_pubkey(); let script = child_desc.script_pubkey();
assert_eq!(script.to_hex().as_str(), *expected.get(i).unwrap()); assert_eq!(script.to_hex_string(), *expected.get(i).unwrap());
} }
} }
} }
@ -1178,9 +1175,7 @@ mod test {
} }
#[test] #[test]
#[should_panic( #[should_panic(expected = "Miniscript(ContextError(UncompressedKeysNotAllowed))")]
expected = "Miniscript(ContextError(CompressedOnly(\"04b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a87378ec38ff91d43e8c2092ebda601780485263da089465619e0358a5c1be7ac91f4\")))"
)]
fn test_dsl_miniscript_checks() { fn test_dsl_miniscript_checks() {
let mut uncompressed_pk = let mut uncompressed_pk =
PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap(); PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap();

View File

@ -22,6 +22,8 @@ pub enum Error {
InvalidDescriptorChecksum, InvalidDescriptorChecksum,
/// The descriptor contains hardened derivation steps on public extended keys /// The descriptor contains hardened derivation steps on public extended keys
HardenedDerivationXpub, HardenedDerivationXpub,
/// The descriptor contains multipath keys
MultiPath,
/// Error thrown while working with [`keys`](crate::keys) /// Error thrown while working with [`keys`](crate::keys)
Key(crate::keys::KeyError), Key(crate::keys::KeyError),
@ -32,11 +34,11 @@ pub enum Error {
InvalidDescriptorCharacter(u8), InvalidDescriptorCharacter(u8),
/// BIP32 error /// BIP32 error
Bip32(bitcoin::util::bip32::Error), Bip32(bitcoin::bip32::Error),
/// Error during base58 decoding /// Error during base58 decoding
Base58(bitcoin::util::base58::Error), Base58(bitcoin::base58::Error),
/// Key-related error /// Key-related error
Pk(bitcoin::util::key::Error), Pk(bitcoin::key::Error),
/// Miniscript error /// Miniscript error
Miniscript(miniscript::Error), Miniscript(miniscript::Error),
/// Hex decoding error /// Hex decoding error
@ -64,6 +66,10 @@ impl fmt::Display for Error {
f, f,
"The descriptor contains hardened derivation steps on public extended keys" "The descriptor contains hardened derivation steps on public extended keys"
), ),
Self::MultiPath => write!(
f,
"The descriptor contains multipath keys, which are not supported yet"
),
Self::Key(err) => write!(f, "Key error: {}", err), Self::Key(err) => write!(f, "Key error: {}", err),
Self::Policy(err) => write!(f, "Policy error: {}", err), Self::Policy(err) => write!(f, "Policy error: {}", err),
Self::InvalidDescriptorCharacter(char) => { Self::InvalidDescriptorCharacter(char) => {
@ -81,9 +87,9 @@ impl fmt::Display for Error {
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl std::error::Error for Error {} impl std::error::Error for Error {}
impl_error!(bitcoin::util::bip32::Error, Bip32); impl_error!(bitcoin::bip32::Error, Bip32);
impl_error!(bitcoin::util::base58::Error, Base58); impl_error!(bitcoin::base58::Error, Base58);
impl_error!(bitcoin::util::key::Error, Pk); impl_error!(bitcoin::key::Error, Pk);
impl_error!(miniscript::Error, Miniscript); impl_error!(miniscript::Error, Miniscript);
impl_error!(bitcoin::hashes::hex::Error, Hex); impl_error!(bitcoin::hashes::hex::Error, Hex);
impl_error!(crate::descriptor::policy::PolicyError, Policy); impl_error!(crate::descriptor::policy::PolicyError, Policy);

View File

@ -18,17 +18,17 @@ use crate::collections::BTreeMap;
use alloc::string::String; use alloc::string::String;
use alloc::vec::Vec; use alloc::vec::Vec;
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource}; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
use bitcoin::util::{psbt, taproot}; use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
use bitcoin::{secp256k1, PublicKey, XOnlyPublicKey}; use bitcoin::{psbt, taproot};
use bitcoin::{Network, TxOut}; use bitcoin::{Network, TxOut};
use miniscript::descriptor::{ use miniscript::descriptor::{
DefiniteDescriptorKey, DescriptorSecretKey, DescriptorType, InnerXKey, SinglePubKey, DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
}; };
pub use miniscript::{ pub use miniscript::{
descriptor::DescriptorXKey, descriptor::KeyMap, descriptor::Wildcard, Descriptor, Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
}; };
use miniscript::{ForEachKey, MiniscriptKey, TranslatePk}; use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
@ -59,16 +59,16 @@ pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or /// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
/// [`psbt::Output`] /// [`psbt::Output`]
/// ///
/// [`psbt::Input`]: bitcoin::util::psbt::Input /// [`psbt::Input`]: bitcoin::psbt::Input
/// [`psbt::Output`]: bitcoin::util::psbt::Output /// [`psbt::Output`]: bitcoin::psbt::Output
pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>; pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
/// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or /// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or
/// [`psbt::Output`] /// [`psbt::Output`]
/// ///
/// [`psbt::Input`]: bitcoin::util::psbt::Input /// [`psbt::Input`]: bitcoin::psbt::Input
/// [`psbt::Output`]: bitcoin::util::psbt::Output /// [`psbt::Output`]: bitcoin::psbt::Output
pub type TapKeyOrigins = BTreeMap<bitcoin::XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>; pub type TapKeyOrigins = BTreeMap<XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
/// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`] /// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`]
pub trait IntoWalletDescriptor { pub trait IntoWalletDescriptor {
@ -136,14 +136,10 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
network: Network, network: Network,
} }
impl<'s, 'd> impl<'s, 'd> miniscript::Translator<DescriptorPublicKey, String, DescriptorError>
miniscript::Translator<DescriptorPublicKey, miniscript::DummyKey, DescriptorError>
for Translator<'s, 'd> for Translator<'s, 'd>
{ {
fn pk( fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
&mut self,
pk: &DescriptorPublicKey,
) -> Result<miniscript::DummyKey, DescriptorError> {
let secp = &self.secp; let secp = &self.secp;
let (_, _, networks) = if self.descriptor.is_taproot() { let (_, _, networks) = if self.descriptor.is_taproot() {
@ -161,7 +157,7 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
}; };
if networks.contains(&self.network) { if networks.contains(&self.network) {
Ok(miniscript::DummyKey) Ok(Default::default())
} else { } else {
Err(DescriptorError::Key(KeyError::InvalidNetwork)) Err(DescriptorError::Key(KeyError::InvalidNetwork))
} }
@ -169,35 +165,40 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
fn sha256( fn sha256(
&mut self, &mut self,
_sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256, _sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
) -> Result<miniscript::DummySha256Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
fn hash256( fn hash256(
&mut self, &mut self,
_hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256, _hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
) -> Result<miniscript::DummyHash256Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
fn ripemd160( fn ripemd160(
&mut self, &mut self,
_ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160, _ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
) -> Result<miniscript::DummyRipemd160Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
fn hash160( fn hash160(
&mut self, &mut self,
_hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160, _hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
) -> Result<miniscript::DummyHash160Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
} }
// check the network for the keys // check the network for the keys
self.0.translate_pk(&mut Translator { use miniscript::TranslateErr;
match self.0.translate_pk(&mut Translator {
secp, secp,
network, network,
descriptor: &self.0, descriptor: &self.0,
})?; }) {
Ok(_) => {}
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
}
Ok(self) Ok(self)
} }
@ -251,7 +252,12 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
} }
// fixup the network for keys that need it in the descriptor // fixup the network for keys that need it in the descriptor
let translated = desc.translate_pk(&mut Translator { network })?; use miniscript::TranslateErr;
let translated = match desc.translate_pk(&mut Translator { network }) {
Ok(descriptor) => descriptor,
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
};
// ...and in the key map // ...and in the key map
let fixed_keymap = keymap let fixed_keymap = keymap
.into_iter() .into_iter()
@ -302,6 +308,10 @@ pub(crate) fn into_wallet_descriptor_checked<T: IntoWalletDescriptor>(
return Err(DescriptorError::HardenedDerivationXpub); return Err(DescriptorError::HardenedDerivationXpub);
} }
if descriptor.is_multipath() {
return Err(DescriptorError::MultiPath);
}
// Run miniscript's sanity check, which will look for duplicated keys and other potential // Run miniscript's sanity check, which will look for duplicated keys and other potential
// issues // issues
descriptor.sanity_check()?; descriptor.sanity_check()?;
@ -340,6 +350,18 @@ pub(crate) trait XKeyUtils {
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint; fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
} }
impl<T> XKeyUtils for DescriptorMultiXKey<T>
where
T: InnerXKey,
{
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
match self.origin {
Some((fingerprint, _)) => fingerprint,
None => self.xkey.xkey_fingerprint(secp),
}
}
}
impl<T> XKeyUtils for DescriptorXKey<T> impl<T> XKeyUtils for DescriptorXKey<T>
where where
T: InnerXKey, T: InnerXKey,
@ -494,7 +516,10 @@ impl DescriptorMeta for ExtendedDescriptor {
false false
}); });
path_found.map(|path| self.at_derivation_index(path)) path_found.map(|path| {
self.at_derivation_index(path)
.expect("We ignore hardened wildcards")
})
} }
fn derive_from_hd_keypaths( fn derive_from_hd_keypaths(
@ -545,7 +570,7 @@ impl DescriptorMeta for ExtendedDescriptor {
return None; return None;
} }
let descriptor = self.at_derivation_index(0); let descriptor = self.at_derivation_index(0).expect("0 is not hardened");
match descriptor.desc_type() { match descriptor.desc_type() {
// TODO: add pk() here // TODO: add pk() here
DescriptorType::Pkh DescriptorType::Pkh
@ -585,11 +610,10 @@ mod test {
use core::str::FromStr; use core::str::FromStr;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::{bip32, psbt}; use bitcoin::ScriptBuf;
use bitcoin::Script; use bitcoin::{bip32, psbt::Psbt};
use super::*; use super::*;
use crate::psbt::PsbtUtils; use crate::psbt::PsbtUtils;
@ -600,7 +624,7 @@ mod test {
"wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)", "wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\ "70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\ 11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
@ -623,7 +647,7 @@ mod test {
"pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)", "pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\ "70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\ e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
@ -654,7 +678,7 @@ mod test {
"wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))", "wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\ "70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\ 67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
@ -678,7 +702,7 @@ mod test {
"sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))", "sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\ "70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\ a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
@ -845,6 +869,12 @@ mod test {
assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub)); assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub));
let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/<0;1>/*)";
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
assert_matches!(result, Err(DescriptorError::MultiPath));
// repeated pubkeys
let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))"; let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))";
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet); let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
@ -861,9 +891,9 @@ mod test {
let (descriptor, _) = let (descriptor, _) =
into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap(); into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap();
let descriptor = descriptor.at_derivation_index(0); let descriptor = descriptor.at_derivation_index(0).unwrap();
let script = Script::from_str("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap(); let script = ScriptBuf::from_hex("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
let mut psbt_input = psbt::Input::default(); let mut psbt_input = psbt::Input::default();
psbt_input psbt_input

View File

@ -45,9 +45,9 @@ use core::fmt;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use bitcoin::bip32::Fingerprint;
use bitcoin::hashes::{hash160, ripemd160, sha256}; use bitcoin::hashes::{hash160, ripemd160, sha256};
use bitcoin::util::bip32::Fingerprint; use bitcoin::{absolute, key::XOnlyPublicKey, PublicKey, Sequence};
use bitcoin::{LockTime, PublicKey, Sequence, XOnlyPublicKey};
use miniscript::descriptor::{ use miniscript::descriptor::{
DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner, DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
@ -68,7 +68,7 @@ use crate::wallet::utils::{After, Older, SecpCtx};
use super::checksum::calc_checksum; use super::checksum::calc_checksum;
use super::error::Error; use super::error::Error;
use super::XKeyUtils; use super::XKeyUtils;
use bitcoin::util::psbt::{Input as PsbtInput, PartiallySignedTransaction as Psbt}; use bitcoin::psbt::{self, Psbt};
use miniscript::psbt::PsbtInputSatisfier; use miniscript::psbt::PsbtInputSatisfier;
/// A unique identifier for a key /// A unique identifier for a key
@ -95,6 +95,9 @@ impl PkOrF {
.. ..
}) => PkOrF::XOnlyPubkey(*pk), }) => PkOrF::XOnlyPubkey(*pk),
DescriptorPublicKey::XPub(xpub) => PkOrF::Fingerprint(xpub.root_fingerprint(secp)), DescriptorPublicKey::XPub(xpub) => PkOrF::Fingerprint(xpub.root_fingerprint(secp)),
DescriptorPublicKey::MultiXPub(multi) => {
PkOrF::Fingerprint(multi.root_fingerprint(secp))
}
} }
} }
} }
@ -131,7 +134,7 @@ pub enum SatisfiableItem {
/// Absolute timeclock timestamp /// Absolute timeclock timestamp
AbsoluteTimelock { AbsoluteTimelock {
/// The timelock value /// The timelock value
value: LockTime, value: absolute::LockTime,
}, },
/// Relative timelock locktime /// Relative timelock locktime
RelativeTimelock { RelativeTimelock {
@ -451,11 +454,14 @@ pub struct Condition {
pub csv: Option<Sequence>, pub csv: Option<Sequence>,
/// Optional timelock condition /// Optional timelock condition
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub timelock: Option<LockTime>, pub timelock: Option<absolute::LockTime>,
} }
impl Condition { impl Condition {
fn merge_nlocktime(a: LockTime, b: LockTime) -> Result<LockTime, PolicyError> { fn merge_nlocktime(
a: absolute::LockTime,
b: absolute::LockTime,
) -> Result<absolute::LockTime, PolicyError> {
if !a.is_same_unit(b) { if !a.is_same_unit(b) {
Err(PolicyError::MixedTimelockUnits) Err(PolicyError::MixedTimelockUnits)
} else if a > b { } else if a > b {
@ -749,6 +755,7 @@ fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
.. ..
}) => pk.to_pubkeyhash(SigType::Ecdsa).into(), }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(), DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
DescriptorPublicKey::MultiXPub(xpub) => xpub.root_fingerprint(secp).into(),
} }
} }
@ -786,9 +793,9 @@ fn make_generic_signature<M: Fn() -> SatisfiableItem, F: Fn(&Psbt) -> bool>(
fn generic_sig_in_psbt< fn generic_sig_in_psbt<
// C is for "check", it's a closure we use to *check* if a psbt input contains the signature // C is for "check", it's a closure we use to *check* if a psbt input contains the signature
// for a specific key // for a specific key
C: Fn(&PsbtInput, &SinglePubKey) -> bool, C: Fn(&psbt::Input, &SinglePubKey) -> bool,
// E is for "extract", it extracts a key from the bip32 derivations found in the psbt input // E is for "extract", it extracts a key from the bip32 derivations found in the psbt input
E: Fn(&PsbtInput, Fingerprint) -> Option<SinglePubKey>, E: Fn(&psbt::Input, Fingerprint) -> Option<SinglePubKey>,
>( >(
psbt: &Psbt, psbt: &Psbt,
key: &DescriptorPublicKey, key: &DescriptorPublicKey,
@ -806,6 +813,13 @@ fn generic_sig_in_psbt<
None => false, None => false,
} }
} }
DescriptorPublicKey::MultiXPub(xpub) => {
//TODO check actual derivation matches
match extract(input, xpub.root_fingerprint(secp)) {
Some(pubkey) => check(input, &pubkey),
None => false,
}
}
}) })
} }
@ -911,12 +925,12 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
} }
Terminal::After(value) => { Terminal::After(value) => {
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { let mut policy: Policy = SatisfiableItem::AbsoluteTimelock {
value: value.into(), value: (*value).into(),
} }
.into(); .into();
policy.contribution = Satisfaction::Complete { policy.contribution = Satisfaction::Complete {
condition: Condition { condition: Condition {
timelock: Some(value.into()), timelock: Some((*value).into()),
csv: None, csv: None,
}, },
}; };
@ -928,9 +942,9 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
{ {
let after = After::new(Some(current_height), false); let after = After::new(Some(current_height), false);
let after_sat = let after_sat =
Satisfier::<bitcoin::PublicKey>::check_after(&after, value.into()); Satisfier::<bitcoin::PublicKey>::check_after(&after, (*value).into());
let inputs_sat = psbt_inputs_sat(psbt).all(|sat| { let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
Satisfier::<bitcoin::PublicKey>::check_after(&sat, value.into()) Satisfier::<bitcoin::PublicKey>::check_after(&sat, (*value).into())
}); });
if after_sat && inputs_sat { if after_sat && inputs_sat {
policy.satisfaction = policy.contribution.clone(); policy.satisfaction = policy.contribution.clone();
@ -1156,8 +1170,8 @@ mod test {
use crate::wallet::signer::SignersContainer; use crate::wallet::signer::SignersContainer;
use alloc::{string::ToString, sync::Arc}; use alloc::{string::ToString, sync::Arc};
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::bip32;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::bip32;
use bitcoin::Network; use bitcoin::Network;
use core::str::FromStr; use core::str::FromStr;
@ -1575,6 +1589,7 @@ mod test {
let addr = wallet_desc let addr = wallet_desc
.at_derivation_index(0) .at_derivation_index(0)
.unwrap()
.address(Network::Testnet) .address(Network::Testnet)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -1641,6 +1656,7 @@ mod test {
let addr = wallet_desc let addr = wallet_desc
.at_derivation_index(0) .at_derivation_index(0)
.unwrap()
.address(Network::Testnet) .address(Network::Testnet)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(

View File

@ -14,7 +14,7 @@
//! This module contains the definition of various common script templates that are ready to be //! This module contains the definition of various common script templates that are ready to be
//! used. See the documentation of each template for an example. //! used. See the documentation of each template for an example.
use bitcoin::util::bip32; use bitcoin::bip32;
use bitcoin::Network; use bitcoin::Network;
use miniscript::{Legacy, Segwitv0, Tap}; use miniscript::{Legacy, Segwitv0, Tap};
@ -195,7 +195,7 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip44; /// use bdk::template::Bip44;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip44(key.clone(), KeychainKind::External), /// Bip44(key.clone(), KeychainKind::External),
/// Some(Bip44(key, KeychainKind::Internal)), /// Some(Bip44(key, KeychainKind::Internal)),
@ -232,8 +232,8 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip44Public; /// use bdk::template::Bip44Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External), /// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
@ -270,7 +270,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip49; /// use bdk::template::Bip49;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip49(key.clone(), KeychainKind::External), /// Bip49(key.clone(), KeychainKind::External),
/// Some(Bip49(key, KeychainKind::Internal)), /// Some(Bip49(key, KeychainKind::Internal)),
@ -307,8 +307,8 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip49Public; /// use bdk::template::Bip49Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External), /// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
@ -345,7 +345,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip84; /// use bdk::template::Bip84;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip84(key.clone(), KeychainKind::External), /// Bip84(key.clone(), KeychainKind::External),
/// Some(Bip84(key, KeychainKind::Internal)), /// Some(Bip84(key, KeychainKind::Internal)),
@ -382,8 +382,8 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip84Public; /// use bdk::template::Bip84Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External), /// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
@ -420,7 +420,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip86; /// use bdk::template::Bip86;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip86(key.clone(), KeychainKind::External), /// Bip86(key.clone(), KeychainKind::External),
/// Some(Bip86(key, KeychainKind::Internal)), /// Some(Bip86(key, KeychainKind::Internal)),
@ -457,8 +457,8 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip86Public; /// use bdk::template::Bip86Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let mut wallet = Wallet::new_no_persist( /// let mut wallet = Wallet::new_no_persist(
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External), /// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
@ -565,30 +565,30 @@ mod test {
// BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)` // BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
#[test] #[test]
fn test_bip44_template_cointype() { fn test_bip44_template_cointype() {
use bitcoin::util::bip32::ChildNumber::{self, Hardened}; use bitcoin::bip32::ChildNumber::{self, Hardened};
let xprvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap(); let xprvkey = bitcoin::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap();
assert_eq!(Network::Bitcoin, xprvkey.network); assert_eq!(Network::Bitcoin, xprvkey.network);
let xdesc = Bip44(xprvkey, KeychainKind::Internal) let xdesc = Bip44(xprvkey, KeychainKind::Internal)
.build(Network::Bitcoin) .build(Network::Bitcoin)
.unwrap(); .unwrap();
if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 { if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into(); let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
let purpose = path.get(0).unwrap(); let purpose = path.get(0).unwrap();
assert_matches!(purpose, Hardened { index: 44 }); assert_matches!(purpose, Hardened { index: 44 });
let coin_type = path.get(1).unwrap(); let coin_type = path.get(1).unwrap();
assert_matches!(coin_type, Hardened { index: 0 }); assert_matches!(coin_type, Hardened { index: 0 });
} }
let tprvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let tprvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
assert_eq!(Network::Testnet, tprvkey.network); assert_eq!(Network::Testnet, tprvkey.network);
let tdesc = Bip44(tprvkey, KeychainKind::Internal) let tdesc = Bip44(tprvkey, KeychainKind::Internal)
.build(Network::Testnet) .build(Network::Testnet)
.unwrap(); .unwrap();
if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 { if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into(); let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
let purpose = path.get(0).unwrap(); let purpose = path.get(0).unwrap();
assert_matches!(purpose, Hardened { index: 44 }); assert_matches!(purpose, Hardened { index: 44 });
let coin_type = path.get(1).unwrap(); let coin_type = path.get(1).unwrap();
@ -612,9 +612,9 @@ mod test {
for i in 0..expected.len() { for i in 0..expected.len() {
let index = i as u32; let index = i as u32;
let child_desc = if !desc.has_wildcard() { let child_desc = if !desc.has_wildcard() {
desc.at_derivation_index(0) desc.at_derivation_index(0).unwrap()
} else { } else {
desc.at_derivation_index(index) desc.at_derivation_index(index).unwrap()
}; };
let address = child_desc.address(network).unwrap(); let address = child_desc.address(network).unwrap();
assert_eq!(address.to_string(), *expected.get(i).unwrap()); assert_eq!(address.to_string(), *expected.get(i).unwrap());
@ -740,7 +740,7 @@ mod test {
// BIP44 `pkh(key/44'/0'/0'/{0,1}/*)` // BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
#[test] #[test]
fn test_bip44_template() { fn test_bip44_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
check( check(
Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
false, false,
@ -770,8 +770,8 @@ mod test {
// BIP44 public `pkh(key/{0,1}/*)` // BIP44 public `pkh(key/{0,1}/*)`
#[test] #[test]
fn test_bip44_public_template() { fn test_bip44_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
check( check(
Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
false, false,
@ -801,7 +801,7 @@ mod test {
// BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))` // BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))`
#[test] #[test]
fn test_bip49_template() { fn test_bip49_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
check( check(
Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -831,8 +831,8 @@ mod test {
// BIP49 public `sh(wpkh(key/{0,1}/*))` // BIP49 public `sh(wpkh(key/{0,1}/*))`
#[test] #[test]
fn test_bip49_public_template() { fn test_bip49_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
check( check(
Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -862,7 +862,7 @@ mod test {
// BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)` // BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)`
#[test] #[test]
fn test_bip84_template() { fn test_bip84_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
check( check(
Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -892,8 +892,8 @@ mod test {
// BIP84 public `wpkh(key/{0,1}/*)` // BIP84 public `wpkh(key/{0,1}/*)`
#[test] #[test]
fn test_bip84_public_template() { fn test_bip84_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
check( check(
Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -924,7 +924,7 @@ mod test {
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
#[test] #[test]
fn test_bip86_template() { fn test_bip86_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
check( check(
Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
false, false,
@ -955,8 +955,8 @@ mod test {
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
#[test] #[test]
fn test_bip86_public_template() { fn test_bip86_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("73c5da0a").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("73c5da0a").unwrap();
check( check(
Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
false, false,

View File

@ -84,9 +84,9 @@ pub enum Error {
/// Miniscript PSBT error /// Miniscript PSBT error
MiniscriptPsbt(MiniscriptPsbtError), MiniscriptPsbt(MiniscriptPsbtError),
/// BIP32 error /// BIP32 error
Bip32(bitcoin::util::bip32::Error), Bip32(bitcoin::bip32::Error),
/// Partially signed bitcoin transaction error /// Partially signed bitcoin transaction error
Psbt(bitcoin::util::psbt::Error), Psbt(bitcoin::psbt::Error),
} }
/// Errors returned by miniscript when updating inconsistent PSBTs /// Errors returned by miniscript when updating inconsistent PSBTs
@ -197,5 +197,5 @@ impl From<crate::keys::KeyError> for Error {
impl_error!(miniscript::Error, Miniscript); impl_error!(miniscript::Error, Miniscript);
impl_error!(MiniscriptPsbtError, MiniscriptPsbt); impl_error!(MiniscriptPsbtError, MiniscriptPsbt);
impl_error!(bitcoin::util::bip32::Error, Bip32); impl_error!(bitcoin::bip32::Error, Bip32);
impl_error!(bitcoin::util::psbt::Error, Psbt); impl_error!(bitcoin::psbt::Error, Psbt);

View File

@ -15,7 +15,7 @@
// something that should be fairly simple to re-implement. // something that should be fairly simple to re-implement.
use alloc::string::String; use alloc::string::String;
use bitcoin::util::bip32; use bitcoin::bip32;
use bitcoin::Network; use bitcoin::Network;
use miniscript::ScriptContext; use miniscript::ScriptContext;
@ -142,7 +142,7 @@ impl<Ctx: ScriptContext> GeneratableKey<Ctx> for Mnemonic {
(word_count, language): Self::Options, (word_count, language): Self::Options,
entropy: Self::Entropy, entropy: Self::Entropy,
) -> Result<GeneratedKey<Self, Ctx>, Self::Error> { ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
let entropy = &entropy.as_ref()[..(word_count as usize / 8)]; let entropy = &entropy[..(word_count as usize / 8)];
let mnemonic = Mnemonic::from_entropy_in(language, entropy)?; let mnemonic = Mnemonic::from_entropy_in(language, entropy)?;
Ok(GeneratedKey::new(mnemonic, any_network())) Ok(GeneratedKey::new(mnemonic, any_network()))
@ -154,7 +154,7 @@ mod test {
use alloc::string::ToString; use alloc::string::ToString;
use core::str::FromStr; use core::str::FromStr;
use bitcoin::util::bip32; use bitcoin::bip32;
use bip39::{Language, Mnemonic}; use bip39::{Language, Mnemonic};

View File

@ -22,8 +22,8 @@ use core::str::FromStr;
use bitcoin::secp256k1::{self, Secp256k1, Signing}; use bitcoin::secp256k1::{self, Secp256k1, Signing};
use bitcoin::util::bip32; use bitcoin::bip32;
use bitcoin::{Network, PrivateKey, PublicKey, XOnlyPublicKey}; use bitcoin::{key::XOnlyPublicKey, Network, PrivateKey, PublicKey};
use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard}; use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard};
pub use miniscript::descriptor::{ pub use miniscript::descriptor::{
@ -388,12 +388,12 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// ///
/// ``` /// ```
/// use bdk::bitcoin; /// use bdk::bitcoin;
/// use bdk::bitcoin::util::bip32; /// use bdk::bitcoin::bip32;
/// use bdk::keys::{DerivableKey, ExtendedKey, KeyError, ScriptContext}; /// use bdk::keys::{DerivableKey, ExtendedKey, KeyError, ScriptContext};
/// ///
/// struct MyCustomKeyType { /// struct MyCustomKeyType {
/// key_data: bitcoin::PrivateKey, /// key_data: bitcoin::PrivateKey,
/// chain_code: Vec<u8>, /// chain_code: [u8; 32],
/// network: bitcoin::Network, /// network: bitcoin::Network,
/// } /// }
/// ///
@ -404,7 +404,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// depth: 0, /// depth: 0,
/// parent_fingerprint: bip32::Fingerprint::default(), /// parent_fingerprint: bip32::Fingerprint::default(),
/// private_key: self.key_data.inner, /// private_key: self.key_data.inner,
/// chain_code: bip32::ChainCode::from(self.chain_code.as_ref()), /// chain_code: bip32::ChainCode::from(&self.chain_code),
/// child_number: bip32::ChildNumber::Normal { index: 0 }, /// child_number: bip32::ChildNumber::Normal { index: 0 },
/// }; /// };
/// ///
@ -419,14 +419,14 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// ///
/// ``` /// ```
/// use bdk::bitcoin; /// use bdk::bitcoin;
/// use bdk::bitcoin::util::bip32; /// use bdk::bitcoin::bip32;
/// use bdk::keys::{ /// use bdk::keys::{
/// any_network, DerivableKey, DescriptorKey, ExtendedKey, KeyError, ScriptContext, /// any_network, DerivableKey, DescriptorKey, ExtendedKey, KeyError, ScriptContext,
/// }; /// };
/// ///
/// struct MyCustomKeyType { /// struct MyCustomKeyType {
/// key_data: bitcoin::PrivateKey, /// key_data: bitcoin::PrivateKey,
/// chain_code: Vec<u8>, /// chain_code: [u8; 32],
/// } /// }
/// ///
/// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType { /// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType {
@ -436,7 +436,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// depth: 0, /// depth: 0,
/// parent_fingerprint: bip32::Fingerprint::default(), /// parent_fingerprint: bip32::Fingerprint::default(),
/// private_key: self.key_data.inner, /// private_key: self.key_data.inner,
/// chain_code: bip32::ChainCode::from(self.chain_code.as_ref()), /// chain_code: bip32::ChainCode::from(&self.chain_code),
/// child_number: bip32::ChildNumber::Normal { index: 0 }, /// child_number: bip32::ChildNumber::Normal { index: 0 },
/// }; /// };
/// ///
@ -927,13 +927,13 @@ pub enum KeyError {
Message(String), Message(String),
/// BIP32 error /// BIP32 error
Bip32(bitcoin::util::bip32::Error), Bip32(bitcoin::bip32::Error),
/// Miniscript error /// Miniscript error
Miniscript(miniscript::Error), Miniscript(miniscript::Error),
} }
impl_error!(miniscript::Error, Miniscript, KeyError); impl_error!(miniscript::Error, Miniscript, KeyError);
impl_error!(bitcoin::util::bip32::Error, Bip32, KeyError); impl_error!(bitcoin::bip32::Error, Bip32, KeyError);
impl fmt::Display for KeyError { impl fmt::Display for KeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -953,7 +953,7 @@ impl std::error::Error for KeyError {}
#[cfg(test)] #[cfg(test)]
pub mod test { pub mod test {
use bitcoin::util::bip32; use bitcoin::bip32;
use super::*; use super::*;

View File

@ -13,7 +13,7 @@
use crate::FeeRate; use crate::FeeRate;
use alloc::vec::Vec; use alloc::vec::Vec;
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use bitcoin::psbt::PartiallySignedTransaction as Psbt;
use bitcoin::TxOut; use bitcoin::TxOut;
// TODO upstream the functions here to `rust-bitcoin`? // TODO upstream the functions here to `rust-bitcoin`?

View File

@ -15,7 +15,7 @@ use core::ops::Sub;
use bdk_chain::ConfirmationTime; use bdk_chain::ConfirmationTime;
use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut}; use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
use bitcoin::{hash_types::Txid, util::psbt}; use bitcoin::{hash_types::Txid, psbt, Weight};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -99,8 +99,8 @@ impl FeeRate {
} }
/// Calculate fee rate from `fee` and weight units (`wu`). /// Calculate fee rate from `fee` and weight units (`wu`).
pub fn from_wu(fee: u64, wu: usize) -> FeeRate { pub fn from_wu(fee: u64, wu: Weight) -> FeeRate {
Self::from_vb(fee, wu.vbytes()) Self::from_vb(fee, wu.to_vbytes_ceil() as usize)
} }
/// Calculate fee rate from `fee` and `vbytes`. /// Calculate fee rate from `fee` and `vbytes`.
@ -120,8 +120,8 @@ impl FeeRate {
} }
/// Calculate absolute fee in Satoshis using size in weight units. /// Calculate absolute fee in Satoshis using size in weight units.
pub fn fee_wu(&self, wu: usize) -> u64 { pub fn fee_wu(&self, wu: Weight) -> u64 {
self.fee_vb(wu.vbytes()) self.fee_vb(wu.to_vbytes_ceil() as usize)
} }
/// Calculate absolute fee in Satoshis using size in virtual bytes. /// Calculate absolute fee in Satoshis using size in virtual bytes.

View File

@ -38,12 +38,12 @@
//! &self, //! &self,
//! required_utxos: Vec<WeightedUtxo>, //! required_utxos: Vec<WeightedUtxo>,
//! optional_utxos: Vec<WeightedUtxo>, //! optional_utxos: Vec<WeightedUtxo>,
//! fee_rate: FeeRate, //! fee_rate: bdk::FeeRate,
//! target_amount: u64, //! target_amount: u64,
//! drain_script: &Script, //! drain_script: &Script,
//! ) -> Result<CoinSelectionResult, bdk::Error> { //! ) -> Result<CoinSelectionResult, bdk::Error> {
//! let mut selected_amount = 0; //! let mut selected_amount = 0;
//! let mut additional_weight = 0; //! let mut additional_weight = Weight::ZERO;
//! let all_utxos_selected = required_utxos //! let all_utxos_selected = required_utxos
//! .into_iter() //! .into_iter()
//! .chain(optional_utxos) //! .chain(optional_utxos)
@ -51,7 +51,9 @@
//! (&mut selected_amount, &mut additional_weight), //! (&mut selected_amount, &mut additional_weight),
//! |(selected_amount, additional_weight), weighted_utxo| { //! |(selected_amount, additional_weight), weighted_utxo| {
//! **selected_amount += weighted_utxo.utxo.txout().value; //! **selected_amount += weighted_utxo.utxo.txout().value;
//! **additional_weight += TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight; //! **additional_weight += Weight::from_wu(
//! (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
//! );
//! Some(weighted_utxo.utxo) //! Some(weighted_utxo.utxo)
//! }, //! },
//! ) //! )
@ -80,7 +82,10 @@
//! # let mut wallet = doctest_wallet!(); //! # let mut wallet = doctest_wallet!();
//! // create wallet, sync, ... //! // create wallet, sync, ...
//! //!
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); //! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
//! .unwrap()
//! .require_network(Network::Testnet)
//! .unwrap();
//! let (psbt, details) = { //! let (psbt, details) = {
//! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything); //! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything);
//! builder.add_recipient(to_address.script_pubkey(), 50_000); //! builder.add_recipient(to_address.script_pubkey(), 50_000);
@ -99,7 +104,7 @@ use crate::{error::Error, Utxo};
use alloc::vec::Vec; use alloc::vec::Vec;
use bitcoin::consensus::encode::serialize; use bitcoin::consensus::encode::serialize;
use bitcoin::Script; use bitcoin::{Script, Weight};
use core::convert::TryInto; use core::convert::TryInto;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
@ -302,8 +307,9 @@ fn select_sorted_utxos(
(&mut selected_amount, &mut fee_amount), (&mut selected_amount, &mut fee_amount),
|(selected_amount, fee_amount), (must_use, weighted_utxo)| { |(selected_amount, fee_amount), (must_use, weighted_utxo)| {
if must_use || **selected_amount < target_amount + **fee_amount { if must_use || **selected_amount < target_amount + **fee_amount {
**fee_amount += **fee_amount += fee_rate.fee_wu(Weight::from_wu(
fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight); (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
));
**selected_amount += weighted_utxo.utxo.txout().value; **selected_amount += weighted_utxo.utxo.txout().value;
log::debug!( log::debug!(
@ -351,7 +357,9 @@ struct OutputGroup {
impl OutputGroup { impl OutputGroup {
fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self { fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self {
let fee = fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight); let fee = fee_rate.fee_wu(Weight::from_wu(
(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
));
let effective_value = weighted_utxo.utxo.txout().value as i64 - fee as i64; let effective_value = weighted_utxo.utxo.txout().value as i64 - fee as i64;
OutputGroup { OutputGroup {
weighted_utxo, weighted_utxo,
@ -681,7 +689,7 @@ mod test {
use core::str::FromStr; use core::str::FromStr;
use bdk_chain::ConfirmationTime; use bdk_chain::ConfirmationTime;
use bitcoin::{OutPoint, Script, TxOut}; use bitcoin::{OutPoint, ScriptBuf, TxOut};
use super::*; use super::*;
use crate::types::*; use crate::types::*;
@ -710,7 +718,7 @@ mod test {
outpoint, outpoint,
txout: TxOut { txout: TxOut {
value, value,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
keychain: KeychainKind::External, keychain: KeychainKind::External,
is_spent: false, is_spent: false,
@ -773,7 +781,7 @@ mod test {
.unwrap(), .unwrap(),
txout: TxOut { txout: TxOut {
value: rng.gen_range(0..200000000), value: rng.gen_range(0..200000000),
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
keychain: KeychainKind::External, keychain: KeychainKind::External,
is_spent: false, is_spent: false,
@ -802,7 +810,7 @@ mod test {
.unwrap(), .unwrap(),
txout: TxOut { txout: TxOut {
value: utxos_value, value: utxos_value,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
keychain: KeychainKind::External, keychain: KeychainKind::External,
is_spent: false, is_spent: false,
@ -825,7 +833,7 @@ mod test {
#[test] #[test]
fn test_largest_first_coin_selection_success() { fn test_largest_first_coin_selection_success() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
let result = LargestFirstCoinSelection::default() let result = LargestFirstCoinSelection::default()
@ -846,7 +854,7 @@ mod test {
#[test] #[test]
fn test_largest_first_coin_selection_use_all() { fn test_largest_first_coin_selection_use_all() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = LargestFirstCoinSelection::default() let result = LargestFirstCoinSelection::default()
@ -867,7 +875,7 @@ mod test {
#[test] #[test]
fn test_largest_first_coin_selection_use_only_necessary() { fn test_largest_first_coin_selection_use_only_necessary() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = LargestFirstCoinSelection::default() let result = LargestFirstCoinSelection::default()
@ -889,7 +897,7 @@ mod test {
#[should_panic(expected = "InsufficientFunds")] #[should_panic(expected = "InsufficientFunds")]
fn test_largest_first_coin_selection_insufficient_funds() { fn test_largest_first_coin_selection_insufficient_funds() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 500_000 + FEE_AMOUNT; let target_amount = 500_000 + FEE_AMOUNT;
LargestFirstCoinSelection::default() LargestFirstCoinSelection::default()
@ -907,7 +915,7 @@ mod test {
#[should_panic(expected = "InsufficientFunds")] #[should_panic(expected = "InsufficientFunds")]
fn test_largest_first_coin_selection_insufficient_funds_high_fees() { fn test_largest_first_coin_selection_insufficient_funds_high_fees() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
LargestFirstCoinSelection::default() LargestFirstCoinSelection::default()
@ -924,7 +932,7 @@ mod test {
#[test] #[test]
fn test_oldest_first_coin_selection_success() { fn test_oldest_first_coin_selection_success() {
let utxos = get_oldest_first_test_utxos(); let utxos = get_oldest_first_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 180_000 + FEE_AMOUNT; let target_amount = 180_000 + FEE_AMOUNT;
let result = OldestFirstCoinSelection::default() let result = OldestFirstCoinSelection::default()
@ -945,7 +953,7 @@ mod test {
#[test] #[test]
fn test_oldest_first_coin_selection_use_all() { fn test_oldest_first_coin_selection_use_all() {
let utxos = get_oldest_first_test_utxos(); let utxos = get_oldest_first_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = OldestFirstCoinSelection::default() let result = OldestFirstCoinSelection::default()
@ -966,7 +974,7 @@ mod test {
#[test] #[test]
fn test_oldest_first_coin_selection_use_only_necessary() { fn test_oldest_first_coin_selection_use_only_necessary() {
let utxos = get_oldest_first_test_utxos(); let utxos = get_oldest_first_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = OldestFirstCoinSelection::default() let result = OldestFirstCoinSelection::default()
@ -988,7 +996,7 @@ mod test {
#[should_panic(expected = "InsufficientFunds")] #[should_panic(expected = "InsufficientFunds")]
fn test_oldest_first_coin_selection_insufficient_funds() { fn test_oldest_first_coin_selection_insufficient_funds() {
let utxos = get_oldest_first_test_utxos(); let utxos = get_oldest_first_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 600_000 + FEE_AMOUNT; let target_amount = 600_000 + FEE_AMOUNT;
OldestFirstCoinSelection::default() OldestFirstCoinSelection::default()
@ -1008,7 +1016,7 @@ mod test {
let utxos = get_oldest_first_test_utxos(); let utxos = get_oldest_first_test_utxos();
let target_amount: u64 = utxos.iter().map(|wu| wu.utxo.txout().value).sum::<u64>() - 50; let target_amount: u64 = utxos.iter().map(|wu| wu.utxo.txout().value).sum::<u64>() - 50;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
OldestFirstCoinSelection::default() OldestFirstCoinSelection::default()
.coin_select( .coin_select(
@ -1027,7 +1035,7 @@ mod test {
// select three outputs // select three outputs
let utxos = generate_same_value_utxos(100_000, 20); let utxos = generate_same_value_utxos(100_000, 20);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
@ -1049,7 +1057,7 @@ mod test {
#[test] #[test]
fn test_bnb_coin_selection_required_are_enough() { fn test_bnb_coin_selection_required_are_enough() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = BranchAndBoundCoinSelection::default() let result = BranchAndBoundCoinSelection::default()
@ -1070,7 +1078,7 @@ mod test {
#[test] #[test]
fn test_bnb_coin_selection_optional_are_enough() { fn test_bnb_coin_selection_optional_are_enough() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 299756 + FEE_AMOUNT; let target_amount = 299756 + FEE_AMOUNT;
let result = BranchAndBoundCoinSelection::default() let result = BranchAndBoundCoinSelection::default()
@ -1106,7 +1114,7 @@ mod test {
assert_eq!(amount, 100_000); assert_eq!(amount, 100_000);
let amount: u64 = optional.iter().map(|u| u.utxo.txout().value).sum(); let amount: u64 = optional.iter().map(|u| u.utxo.txout().value).sum();
assert!(amount > 150_000); assert!(amount > 150_000);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 150_000 + FEE_AMOUNT; let target_amount = 150_000 + FEE_AMOUNT;
@ -1129,7 +1137,7 @@ mod test {
#[should_panic(expected = "InsufficientFunds")] #[should_panic(expected = "InsufficientFunds")]
fn test_bnb_coin_selection_insufficient_funds() { fn test_bnb_coin_selection_insufficient_funds() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 500_000 + FEE_AMOUNT; let target_amount = 500_000 + FEE_AMOUNT;
BranchAndBoundCoinSelection::default() BranchAndBoundCoinSelection::default()
@ -1147,7 +1155,7 @@ mod test {
#[should_panic(expected = "InsufficientFunds")] #[should_panic(expected = "InsufficientFunds")]
fn test_bnb_coin_selection_insufficient_funds_high_fees() { fn test_bnb_coin_selection_insufficient_funds_high_fees() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
BranchAndBoundCoinSelection::default() BranchAndBoundCoinSelection::default()
@ -1164,7 +1172,7 @@ mod test {
#[test] #[test]
fn test_bnb_coin_selection_check_fee_rate() { fn test_bnb_coin_selection_check_fee_rate() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 99932; // first utxo's effective value let target_amount = 99932; // first utxo's effective value
let result = BranchAndBoundCoinSelection::new(0) let result = BranchAndBoundCoinSelection::new(0)
@ -1192,7 +1200,7 @@ mod test {
for _i in 0..200 { for _i in 0..200 {
let mut optional_utxos = generate_random_utxos(&mut rng, 16); let mut optional_utxos = generate_random_utxos(&mut rng, 16);
let target_amount = sum_random_utxos(&mut rng, &mut optional_utxos); let target_amount = sum_random_utxos(&mut rng, &mut optional_utxos);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::new(0) let result = BranchAndBoundCoinSelection::new(0)
.coin_select( .coin_select(
vec![], vec![],
@ -1220,7 +1228,7 @@ mod test {
let size_of_change = 31; let size_of_change = 31;
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb(); let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
BranchAndBoundCoinSelection::new(size_of_change) BranchAndBoundCoinSelection::new(size_of_change)
.bnb( .bnb(
@ -1251,7 +1259,7 @@ mod test {
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb(); let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
BranchAndBoundCoinSelection::new(size_of_change) BranchAndBoundCoinSelection::new(size_of_change)
.bnb( .bnb(
@ -1287,7 +1295,7 @@ mod test {
// cost_of_change + 5. // cost_of_change + 5.
let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.ceil() as i64 + 5; let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.ceil() as i64 + 5;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::new(size_of_change) let result = BranchAndBoundCoinSelection::new(size_of_change)
.bnb( .bnb(
@ -1327,7 +1335,7 @@ mod test {
let target_amount = let target_amount =
optional_utxos[3].effective_value + optional_utxos[23].effective_value; optional_utxos[3].effective_value + optional_utxos[23].effective_value;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::new(0) let result = BranchAndBoundCoinSelection::new(0)
.bnb( .bnb(
@ -1358,7 +1366,7 @@ mod test {
.map(|u| OutputGroup::new(u, fee_rate)) .map(|u| OutputGroup::new(u, fee_rate))
.collect(); .collect();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::default().single_random_draw( let result = BranchAndBoundCoinSelection::default().single_random_draw(
vec![], vec![],
@ -1376,7 +1384,7 @@ mod test {
#[test] #[test]
fn test_bnb_exclude_negative_effective_value() { fn test_bnb_exclude_negative_effective_value() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let selection = BranchAndBoundCoinSelection::default().coin_select( let selection = BranchAndBoundCoinSelection::default().coin_select(
vec![], vec![],
@ -1398,7 +1406,7 @@ mod test {
#[test] #[test]
fn test_bnb_include_negative_effective_value_when_required() { fn test_bnb_include_negative_effective_value_when_required() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let (required, optional) = utxos let (required, optional) = utxos
.into_iter() .into_iter()
@ -1424,7 +1432,7 @@ mod test {
#[test] #[test]
fn test_bnb_sum_of_effective_value_negative() { fn test_bnb_sum_of_effective_value_negative() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let selection = BranchAndBoundCoinSelection::default().coin_select( let selection = BranchAndBoundCoinSelection::default().coin_select(
utxos, utxos,

View File

@ -232,7 +232,7 @@ mod test {
input: vec![], input: vec![],
output: vec![], output: vec![],
version: 0, version: 0,
lock_time: bitcoin::PackedLockTime::ZERO, lock_time: bitcoin::absolute::LockTime::ZERO,
}; };
wallet wallet
.insert_checkpoint(BlockId { .insert_checkpoint(BlockId {

View File

@ -19,7 +19,7 @@
//! # use bdk::wallet::hardwaresigner::HWISigner; //! # use bdk::wallet::hardwaresigner::HWISigner;
//! # use bdk::wallet::AddressIndex::New; //! # use bdk::wallet::AddressIndex::New;
//! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet}; //! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet};
//! # use hwi::{types::HWIChain, HWIClient}; //! # use hwi::HWIClient;
//! # use std::sync::Arc; //! # use std::sync::Arc;
//! # //! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -28,7 +28,7 @@
//! panic!("No devices found!"); //! panic!("No devices found!");
//! } //! }
//! let first_device = devices.remove(0)?; //! let first_device = devices.remove(0)?;
//! let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?; //! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
//! //!
//! # let mut wallet = Wallet::new_no_persist( //! # let mut wallet = Wallet::new_no_persist(
//! # "", //! # "",
@ -47,9 +47,9 @@
//! # } //! # }
//! ``` //! ```
use bitcoin::bip32::Fingerprint;
use bitcoin::psbt::PartiallySignedTransaction; use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32::Fingerprint;
use hwi::error::Error; use hwi::error::Error;
use hwi::types::{HWIChain, HWIDevice}; use hwi::types::{HWIChain, HWIDevice};

View File

@ -29,11 +29,12 @@ use bdk_chain::{
IndexedTxGraph, Persist, PersistBackend, IndexedTxGraph, Persist, PersistBackend,
}; };
use bitcoin::consensus::encode::serialize; use bitcoin::consensus::encode::serialize;
use bitcoin::psbt;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::psbt; use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::{ use bitcoin::{
Address, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script, Sequence, absolute, Address, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxOut, Txid,
Transaction, TxOut, Txid, Witness, Weight, Witness,
}; };
use core::fmt; use core::fmt;
use core::ops::Deref; use core::ops::Deref;
@ -322,11 +323,11 @@ impl<D> Wallet<D> {
let (index, spk, additions) = match address_index { let (index, spk, additions) = match address_index {
AddressIndex::New => { AddressIndex::New => {
let ((index, spk), index_additions) = txout_index.reveal_next_spk(&keychain); let ((index, spk), index_additions) = txout_index.reveal_next_spk(&keychain);
(index, spk.clone(), Some(index_additions)) (index, spk.into(), Some(index_additions))
} }
AddressIndex::LastUnused => { AddressIndex::LastUnused => {
let ((index, spk), index_additions) = txout_index.next_unused_spk(&keychain); let ((index, spk), index_additions) = txout_index.next_unused_spk(&keychain);
(index, spk.clone(), Some(index_additions)) (index, spk.into(), Some(index_additions))
} }
AddressIndex::Peek(index) => { AddressIndex::Peek(index) => {
let (index, spk) = txout_index let (index, spk) = txout_index
@ -396,7 +397,7 @@ impl<D> Wallet<D> {
/// script pubkeys the wallet is storing internally). /// script pubkeys the wallet is storing internally).
pub fn spks_of_all_keychains( pub fn spks_of_all_keychains(
&self, &self,
) -> BTreeMap<KeychainKind, impl Iterator<Item = (u32, Script)> + Clone> { ) -> BTreeMap<KeychainKind, impl Iterator<Item = (u32, ScriptBuf)> + Clone> {
self.indexed_graph.index.spks_of_all_keychains() self.indexed_graph.index.spks_of_all_keychains()
} }
@ -408,7 +409,7 @@ impl<D> Wallet<D> {
pub fn spks_of_keychain( pub fn spks_of_keychain(
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
) -> impl Iterator<Item = (u32, Script)> + Clone { ) -> impl Iterator<Item = (u32, ScriptBuf)> + Clone {
self.indexed_graph.index.spks_of_keychain(&keychain) self.indexed_graph.index.spks_of_keychain(&keychain)
} }
@ -599,7 +600,7 @@ impl<D> Wallet<D> {
/// # use bdk::*; /// # use bdk::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let (psbt, details) = { /// let (psbt, details) = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder /// builder
@ -716,7 +717,7 @@ impl<D> Wallet<D> {
None => self None => self
.chain .chain
.tip() .tip()
.map(|cp| LockTime::from_height(cp.height()).expect("Invalid height")), .map(|cp| absolute::LockTime::from_height(cp.height()).expect("Invalid height")),
h => h, h => h,
}; };
@ -726,7 +727,7 @@ impl<D> Wallet<D> {
// Fee sniping can be partially prevented by setting the timelock // Fee sniping can be partially prevented by setting the timelock
// to current_height. If we don't know the current_height, // to current_height. If we don't know the current_height,
// we default to 0. // we default to 0.
let fee_sniping_height = current_height.unwrap_or(LockTime::ZERO); let fee_sniping_height = current_height.unwrap_or(absolute::LockTime::ZERO);
// We choose the biggest between the required nlocktime and the fee sniping // We choose the biggest between the required nlocktime and the fee sniping
// height // height
@ -734,7 +735,7 @@ impl<D> Wallet<D> {
// No requirement, just use the fee_sniping_height // No requirement, just use the fee_sniping_height
None => fee_sniping_height, None => fee_sniping_height,
// There's a block-based requirement, but the value is lower than the fee_sniping_height // There's a block-based requirement, but the value is lower than the fee_sniping_height
Some(value @ LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height, Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height,
// There's a time-based requirement or a block-based requirement greater // There's a time-based requirement or a block-based requirement greater
// than the fee_sniping_height use that value // than the fee_sniping_height use that value
Some(value) => value, Some(value) => value,
@ -750,7 +751,9 @@ impl<D> Wallet<D> {
let n_sequence = match (params.rbf, requirements.csv) { let n_sequence = match (params.rbf, requirements.csv) {
// No RBF or CSV but there's an nLockTime, so the nSequence cannot be final // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
(None, None) if lock_time != LockTime::ZERO => Sequence::ENABLE_LOCKTIME_NO_RBF, (None, None) if lock_time != absolute::LockTime::ZERO => {
Sequence::ENABLE_LOCKTIME_NO_RBF
}
// No RBF, CSV or nLockTime, make the transaction final // No RBF, CSV or nLockTime, make the transaction final
(None, None) => Sequence::MAX, (None, None) => Sequence::MAX,
@ -813,7 +816,7 @@ impl<D> Wallet<D> {
let mut tx = Transaction { let mut tx = Transaction {
version, version,
lock_time: lock_time.into(), lock_time,
input: vec![], input: vec![],
output: vec![], output: vec![],
}; };
@ -861,7 +864,7 @@ impl<D> Wallet<D> {
// end up with a transaction with a slightly higher fee rate than the requested one. // end up with a transaction with a slightly higher fee rate than the requested one.
// If, instead, we undershoot, we may end up with a feerate lower than the requested one // If, instead, we undershoot, we may end up with a feerate lower than the requested one
// - we might come up with non broadcastable txs! // - we might come up with non broadcastable txs!
fee_amount += fee_rate.fee_wu(2); fee_amount += fee_rate.fee_wu(Weight::from_wu(2));
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
&& internal_descriptor.is_none() && internal_descriptor.is_none()
@ -878,7 +881,7 @@ impl<D> Wallet<D> {
params.drain_wallet, params.drain_wallet,
params.manually_selected_only, params.manually_selected_only,
params.bumping_fee.is_some(), // we mandate confirmed transactions if we're bumping the fee params.bumping_fee.is_some(), // we mandate confirmed transactions if we're bumping the fee
current_height.map(LockTime::to_consensus_u32), current_height.map(absolute::LockTime::to_consensus_u32),
); );
// get drain script // get drain script
@ -888,7 +891,7 @@ impl<D> Wallet<D> {
let change_keychain = self.map_keychain(KeychainKind::Internal); let change_keychain = self.map_keychain(KeychainKind::Internal);
let ((index, spk), index_additions) = let ((index, spk), index_additions) =
self.indexed_graph.index.next_unused_spk(&change_keychain); self.indexed_graph.index.next_unused_spk(&change_keychain);
let spk = spk.clone(); let spk = spk.into();
self.indexed_graph.index.mark_used(&change_keychain, index); self.indexed_graph.index.mark_used(&change_keychain, index);
self.persist self.persist
.stage(ChangeSet::from(IndexedAdditions::from(index_additions))); .stage(ChangeSet::from(IndexedAdditions::from(index_additions)));
@ -912,7 +915,7 @@ impl<D> Wallet<D> {
.iter() .iter()
.map(|u| bitcoin::TxIn { .map(|u| bitcoin::TxIn {
previous_output: u.outpoint(), previous_output: u.outpoint(),
script_sig: Script::default(), script_sig: ScriptBuf::default(),
sequence: n_sequence, sequence: n_sequence,
witness: Witness::new(), witness: Witness::new(),
}) })
@ -1000,7 +1003,7 @@ impl<D> Wallet<D> {
/// # use bdk::*; /// # use bdk::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let (mut psbt, _) = { /// let (mut psbt, _) = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder /// builder
@ -1014,7 +1017,7 @@ impl<D> Wallet<D> {
/// let (mut psbt, _) = { /// let (mut psbt, _) = {
/// let mut builder = wallet.build_fee_bump(tx.txid())?; /// let mut builder = wallet.build_fee_bump(tx.txid())?;
/// builder /// builder
/// .fee_rate(FeeRate::from_sat_per_vb(5.0)); /// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0));
/// builder.finish()? /// builder.finish()?
/// }; /// };
/// ///
@ -1078,6 +1081,7 @@ impl<D> Wallet<D> {
let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) { let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
Some(&(keychain, derivation_index)) => { Some(&(keychain, derivation_index)) => {
#[allow(deprecated)]
let satisfaction_weight = self let satisfaction_weight = self
.get_descriptor_for_keychain(keychain) .get_descriptor_for_keychain(keychain)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -1170,7 +1174,7 @@ impl<D> Wallet<D> {
/// # use bdk::*; /// # use bdk::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let (mut psbt, _) = { /// let (mut psbt, _) = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder.add_recipient(to_address.script_pubkey(), 50_000); /// builder.add_recipient(to_address.script_pubkey(), 50_000);
@ -1207,8 +1211,8 @@ impl<D> Wallet<D> {
&& !psbt.inputs.iter().all(|i| { && !psbt.inputs.iter().all(|i| {
i.sighash_type.is_none() i.sighash_type.is_none()
|| i.sighash_type == Some(EcdsaSighashType::All.into()) || i.sighash_type == Some(EcdsaSighashType::All.into())
|| i.sighash_type == Some(SchnorrSighashType::All.into()) || i.sighash_type == Some(TapSighashType::All.into())
|| i.sighash_type == Some(SchnorrSighashType::Default.into()) || i.sighash_type == Some(TapSighashType::Default.into())
}) })
{ {
return Err(Error::Signer(signer::SignerError::NonStandardSighash)); return Err(Error::Signer(signer::SignerError::NonStandardSighash));
@ -1403,13 +1407,14 @@ impl<D> Wallet<D> {
.index .index
.index_of_spk(&txout.script_pubkey)?; .index_of_spk(&txout.script_pubkey)?;
let descriptor = self.get_descriptor_for_keychain(keychain); let descriptor = self.get_descriptor_for_keychain(keychain);
Some(descriptor.at_derivation_index(child)) descriptor.at_derivation_index(child).ok()
} }
fn get_available_utxos(&self) -> Vec<(LocalUtxo, usize)> { fn get_available_utxos(&self) -> Vec<(LocalUtxo, usize)> {
self.list_unspent() self.list_unspent()
.map(|utxo| { .map(|utxo| {
let keychain = utxo.keychain; let keychain = utxo.keychain;
#[allow(deprecated)]
( (
utxo, utxo,
self.get_descriptor_for_keychain(keychain) self.get_descriptor_for_keychain(keychain)
@ -1620,7 +1625,9 @@ impl<D> Wallet<D> {
}; };
let desc = self.get_descriptor_for_keychain(keychain); let desc = self.get_descriptor_for_keychain(keychain);
let derived_descriptor = desc.at_derivation_index(child); let derived_descriptor = desc
.at_derivation_index(child)
.expect("child can't be hardened");
psbt_input psbt_input
.update_with_descriptor_unchecked(&derived_descriptor) .update_with_descriptor_unchecked(&derived_descriptor)
@ -1669,7 +1676,9 @@ impl<D> Wallet<D> {
); );
let desc = self.get_descriptor_for_keychain(keychain); let desc = self.get_descriptor_for_keychain(keychain);
let desc = desc.at_derivation_index(child); let desc = desc
.at_derivation_index(child)
.expect("child can't be hardened");
if is_input { if is_input {
psbt.update_input_with_descriptor(index, &desc) psbt.update_input_with_descriptor(index, &desc)
@ -1871,7 +1880,7 @@ fn new_tx_details(
/// Macro for getting a wallet for use in a doctest /// Macro for getting a wallet for use in a doctest
macro_rules! doctest_wallet { macro_rules! doctest_wallet {
() => {{ () => {{
use $crate::bitcoin::{BlockHash, Transaction, PackedLockTime, TxOut, Network, hashes::Hash}; use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash};
use $crate::chain::{ConfirmationTime, BlockId}; use $crate::chain::{ConfirmationTime, BlockId};
use $crate::wallet::{AddressIndex, Wallet}; use $crate::wallet::{AddressIndex, Wallet};
let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)"; let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
@ -1886,7 +1895,7 @@ macro_rules! doctest_wallet {
let address = wallet.get_address(AddressIndex::New).address; let address = wallet.get_address(AddressIndex::New).address;
let tx = Transaction { let tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 500_000, value: 500_000,

View File

@ -19,7 +19,7 @@
//! # use core::str::FromStr; //! # use core::str::FromStr;
//! # use bitcoin::secp256k1::{Secp256k1, All}; //! # use bitcoin::secp256k1::{Secp256k1, All};
//! # use bitcoin::*; //! # use bitcoin::*;
//! # use bitcoin::util::psbt; //! # use bitcoin::psbt;
//! # use bdk::signer::*; //! # use bdk::signer::*;
//! # use bdk::*; //! # use bdk::*;
//! # #[derive(Debug)] //! # #[derive(Debug)]
@ -86,18 +86,17 @@ use core::cmp::Ordering;
use core::fmt; use core::fmt;
use core::ops::{Bound::Included, Deref}; use core::ops::{Bound::Included, Deref};
use bitcoin::blockdata::opcodes; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint};
use bitcoin::blockdata::script::Builder as ScriptBuilder; use bitcoin::hashes::hash160;
use bitcoin::hashes::{hash160, Hash};
use bitcoin::secp256k1::Message; use bitcoin::secp256k1::Message;
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}; use bitcoin::sighash::{EcdsaSighashType, TapSighash, TapSighashType};
use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot}; use bitcoin::{ecdsa, psbt, sighash, taproot};
use bitcoin::{secp256k1, XOnlyPublicKey}; use bitcoin::{key::TapTweak, key::XOnlyPublicKey, secp256k1};
use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script}; use bitcoin::{PrivateKey, PublicKey};
use miniscript::descriptor::{ use miniscript::descriptor::{
Descriptor, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, KeyMap, SinglePriv, Descriptor, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey,
SinglePubKey, InnerXKey, KeyMap, SinglePriv, SinglePubKey,
}; };
use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey}; use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey};
@ -130,7 +129,7 @@ impl From<Fingerprint> for SignerId {
} }
/// Signing error /// Signing error
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug)]
pub enum SignerError { pub enum SignerError {
/// The private key is missing for the required public key /// The private key is missing for the required public key
MissingKey, MissingKey,
@ -383,6 +382,48 @@ impl InputSigner for SignerWrapper<DescriptorXKey<ExtendedPrivKey>> {
} }
} }
fn multikey_to_xkeys<K: InnerXKey + Clone>(
multikey: DescriptorMultiXKey<K>,
) -> Vec<DescriptorXKey<K>> {
multikey
.derivation_paths
.into_paths()
.into_iter()
.map(|derivation_path| DescriptorXKey {
origin: multikey.origin.clone(),
xkey: multikey.xkey.clone(),
derivation_path,
wildcard: multikey.wildcard,
})
.collect()
}
impl SignerCommon for SignerWrapper<DescriptorMultiXKey<ExtendedPrivKey>> {
fn id(&self, secp: &SecpCtx) -> SignerId {
SignerId::from(self.root_fingerprint(secp))
}
fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
Some(DescriptorSecretKey::MultiXPrv(self.signer.clone()))
}
}
impl InputSigner for SignerWrapper<DescriptorMultiXKey<ExtendedPrivKey>> {
fn sign_input(
&self,
psbt: &mut psbt::PartiallySignedTransaction,
input_index: usize,
sign_options: &SignOptions,
secp: &SecpCtx,
) -> Result<(), SignerError> {
let xkeys = multikey_to_xkeys(self.signer.clone());
for xkey in xkeys {
SignerWrapper::new(xkey, self.ctx).sign_input(psbt, input_index, sign_options, secp)?
}
Ok(())
}
}
impl SignerCommon for SignerWrapper<PrivateKey> { impl SignerCommon for SignerWrapper<PrivateKey> {
fn id(&self, secp: &SecpCtx) -> SignerId { fn id(&self, secp: &SecpCtx) -> SignerId {
SignerId::from(self.public_key(secp).to_pubkeyhash(SigType::Ecdsa)) SignerId::from(self.public_key(secp).to_pubkeyhash(SigType::Ecdsa))
@ -477,8 +518,16 @@ impl InputSigner for SignerWrapper<PrivateKey> {
} }
let (hash, hash_ty) = match self.ctx { let (hash, hash_ty) = match self.ctx {
SignerContext::Segwitv0 => Segwitv0::sighash(psbt, input_index, ())?, SignerContext::Segwitv0 => {
SignerContext::Legacy => Legacy::sighash(psbt, input_index, ())?, let (h, t) = Segwitv0::sighash(psbt, input_index, ())?;
let h = h.to_raw_hash();
(h, t)
}
SignerContext::Legacy => {
let (h, t) = Legacy::sighash(psbt, input_index, ())?;
let h = h.to_raw_hash();
(h, t)
}
_ => return Ok(()), // handled above _ => return Ok(()), // handled above
}; };
sign_psbt_ecdsa( sign_psbt_ecdsa(
@ -499,12 +548,12 @@ fn sign_psbt_ecdsa(
secret_key: &secp256k1::SecretKey, secret_key: &secp256k1::SecretKey,
pubkey: PublicKey, pubkey: PublicKey,
psbt_input: &mut psbt::Input, psbt_input: &mut psbt::Input,
hash: bitcoin::Sighash, hash: impl bitcoin::hashes::Hash + bitcoin::secp256k1::ThirtyTwoByteHash,
hash_ty: EcdsaSighashType, hash_ty: EcdsaSighashType,
secp: &SecpCtx, secp: &SecpCtx,
allow_grinding: bool, allow_grinding: bool,
) { ) {
let msg = &Message::from_slice(&hash.into_inner()[..]).unwrap(); let msg = &Message::from(hash);
let sig = if allow_grinding { let sig = if allow_grinding {
secp.sign_ecdsa_low_r(msg, secret_key) secp.sign_ecdsa_low_r(msg, secret_key)
} else { } else {
@ -513,7 +562,7 @@ fn sign_psbt_ecdsa(
secp.verify_ecdsa(msg, &sig, &pubkey.inner) secp.verify_ecdsa(msg, &sig, &pubkey.inner)
.expect("invalid or corrupted ecdsa signature"); .expect("invalid or corrupted ecdsa signature");
let final_signature = ecdsa::EcdsaSig { sig, hash_ty }; let final_signature = ecdsa::Signature { sig, hash_ty };
psbt_input.partial_sigs.insert(pubkey, final_signature); psbt_input.partial_sigs.insert(pubkey, final_signature);
} }
@ -523,12 +572,10 @@ fn sign_psbt_schnorr(
pubkey: XOnlyPublicKey, pubkey: XOnlyPublicKey,
leaf_hash: Option<taproot::TapLeafHash>, leaf_hash: Option<taproot::TapLeafHash>,
psbt_input: &mut psbt::Input, psbt_input: &mut psbt::Input,
hash: taproot::TapSighashHash, hash: TapSighash,
hash_ty: SchnorrSighashType, hash_ty: TapSighashType,
secp: &SecpCtx, secp: &SecpCtx,
) { ) {
use schnorr::TapTweak;
let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap(); let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
let keypair = match leaf_hash { let keypair = match leaf_hash {
None => keypair None => keypair
@ -537,12 +584,12 @@ fn sign_psbt_schnorr(
Some(_) => keypair, // no tweak for script spend Some(_) => keypair, // no tweak for script spend
}; };
let msg = &Message::from_slice(&hash.into_inner()[..]).unwrap(); let msg = &Message::from(hash);
let sig = secp.sign_schnorr(msg, &keypair); let sig = secp.sign_schnorr(msg, &keypair);
secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0) secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
.expect("invalid or corrupted schnorr signature"); .expect("invalid or corrupted schnorr signature");
let final_signature = schnorr::SchnorrSig { sig, hash_ty }; let final_signature = taproot::Signature { sig, hash_ty };
if let Some(lh) = leaf_hash { if let Some(lh) = leaf_hash {
psbt_input psbt_input
@ -632,6 +679,11 @@ impl SignersContainer {
SignerOrdering::default(), SignerOrdering::default(),
Arc::new(SignerWrapper::new(xprv, ctx)), Arc::new(SignerWrapper::new(xprv, ctx)),
), ),
DescriptorSecretKey::MultiXPrv(xprv) => container.add_external(
SignerId::from(xprv.root_fingerprint(secp)),
SignerOrdering::default(),
Arc::new(SignerWrapper::new(xprv, ctx)),
),
}; };
} }
@ -802,7 +854,7 @@ pub(crate) trait ComputeSighash {
impl ComputeSighash for Legacy { impl ComputeSighash for Legacy {
type Extra = (); type Extra = ();
type Sighash = bitcoin::Sighash; type Sighash = sighash::LegacySighash;
type SighashType = EcdsaSighashType; type SighashType = EcdsaSighashType;
fn sighash( fn sighash(
@ -849,19 +901,9 @@ impl ComputeSighash for Legacy {
} }
} }
fn p2wpkh_script_code(script: &Script) -> Script {
ScriptBuilder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&script[2..])
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
impl ComputeSighash for Segwitv0 { impl ComputeSighash for Segwitv0 {
type Extra = (); type Extra = ();
type Sighash = bitcoin::Sighash; type Sighash = sighash::SegwitV0Sighash;
type SighashType = EcdsaSighashType; type SighashType = EcdsaSighashType;
fn sighash( fn sighash(
@ -908,14 +950,21 @@ impl ComputeSighash for Segwitv0 {
Some(ref witness_script) => witness_script.clone(), Some(ref witness_script) => witness_script.clone(),
None => { None => {
if utxo.script_pubkey.is_v0_p2wpkh() { if utxo.script_pubkey.is_v0_p2wpkh() {
p2wpkh_script_code(&utxo.script_pubkey) utxo.script_pubkey
.p2wpkh_script_code()
.expect("We check above that the spk is a p2wpkh")
} else if psbt_input } else if psbt_input
.redeem_script .redeem_script
.as_ref() .as_ref()
.map(Script::is_v0_p2wpkh) .map(|s| s.is_v0_p2wpkh())
.unwrap_or(false) .unwrap_or(false)
{ {
p2wpkh_script_code(psbt_input.redeem_script.as_ref().unwrap()) psbt_input
.redeem_script
.as_ref()
.unwrap()
.p2wpkh_script_code()
.expect("We check above that the spk is a p2wpkh")
} else { } else {
return Err(SignerError::MissingWitnessScript); return Err(SignerError::MissingWitnessScript);
} }
@ -936,14 +985,14 @@ impl ComputeSighash for Segwitv0 {
impl ComputeSighash for Tap { impl ComputeSighash for Tap {
type Extra = Option<taproot::TapLeafHash>; type Extra = Option<taproot::TapLeafHash>;
type Sighash = taproot::TapSighashHash; type Sighash = TapSighash;
type SighashType = SchnorrSighashType; type SighashType = TapSighashType;
fn sighash( fn sighash(
psbt: &psbt::PartiallySignedTransaction, psbt: &psbt::PartiallySignedTransaction,
input_index: usize, input_index: usize,
extra: Self::Extra, extra: Self::Extra,
) -> Result<(Self::Sighash, SchnorrSighashType), SignerError> { ) -> Result<(Self::Sighash, TapSighashType), SignerError> {
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() { if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
return Err(SignerError::InputIndexOutOfRange); return Err(SignerError::InputIndexOutOfRange);
} }
@ -952,8 +1001,8 @@ impl ComputeSighash for Tap {
let sighash_type = psbt_input let sighash_type = psbt_input
.sighash_type .sighash_type
.unwrap_or_else(|| SchnorrSighashType::Default.into()) .unwrap_or_else(|| TapSighashType::Default.into())
.schnorr_hash_ty() .taproot_hash_ty()
.map_err(|_| SignerError::InvalidSighash)?; .map_err(|_| SignerError::InvalidSighash)?;
let witness_utxos = (0..psbt.inputs.len()) let witness_utxos = (0..psbt.inputs.len())
.map(|i| psbt.get_utxo_for(i)) .map(|i| psbt.get_utxo_for(i))
@ -1015,8 +1064,8 @@ mod signers_container_tests {
use crate::descriptor::IntoWalletDescriptor; use crate::descriptor::IntoWalletDescriptor;
use crate::keys::{DescriptorKey, IntoDescriptorKey}; use crate::keys::{DescriptorKey, IntoDescriptorKey};
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::bip32;
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32;
use bitcoin::Network; use bitcoin::Network;
use core::str::FromStr; use core::str::FromStr;
use miniscript::ScriptContext; use miniscript::ScriptContext;

View File

@ -18,7 +18,7 @@
//! # use bitcoin::*; //! # use bitcoin::*;
//! # use bdk::*; //! # use bdk::*;
//! # use bdk::wallet::tx_builder::CreateTx; //! # use bdk::wallet::tx_builder::CreateTx;
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); //! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
//! # let mut wallet = doctest_wallet!(); //! # let mut wallet = doctest_wallet!();
//! // create a TxBuilder from a wallet //! // create a TxBuilder from a wallet
//! let mut tx_builder = wallet.build_tx(); //! let mut tx_builder = wallet.build_tx();
@ -27,7 +27,7 @@
//! // Create a transaction with one output to `to_address` of 50_000 satoshi //! // Create a transaction with one output to `to_address` of 50_000 satoshi
//! .add_recipient(to_address.script_pubkey(), 50_000) //! .add_recipient(to_address.script_pubkey(), 50_000)
//! // With a custom fee rate of 5.0 satoshi/vbyte //! // With a custom fee rate of 5.0 satoshi/vbyte
//! .fee_rate(FeeRate::from_sat_per_vb(5.0)) //! .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0))
//! // Only spend non-change outputs //! // Only spend non-change outputs
//! .do_not_spend_change() //! .do_not_spend_change()
//! // Turn on RBF signaling //! // Turn on RBF signaling
@ -43,8 +43,8 @@ use bdk_chain::PersistBackend;
use core::cell::RefCell; use core::cell::RefCell;
use core::marker::PhantomData; use core::marker::PhantomData;
use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt}; use bitcoin::psbt::{self, PartiallySignedTransaction as Psbt};
use bitcoin::{LockTime, OutPoint, Script, Sequence, Transaction}; use bitcoin::{absolute, script::PushBytes, OutPoint, ScriptBuf, Sequence, Transaction};
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm}; use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
use super::ChangeSet; use super::ChangeSet;
@ -82,7 +82,7 @@ impl TxBuilderContext for BumpFee {}
/// # use bitcoin::*; /// # use bitcoin::*;
/// # use core::str::FromStr; /// # use core::str::FromStr;
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// # let addr2 = addr1.clone(); /// # let addr2 = addr1.clone();
/// // chaining /// // chaining
/// let (psbt1, details) = { /// let (psbt1, details) = {
@ -129,9 +129,9 @@ pub struct TxBuilder<'a, D, Cs, Ctx> {
//TODO: TxParams should eventually be exposed publicly. //TODO: TxParams should eventually be exposed publicly.
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub(crate) struct TxParams { pub(crate) struct TxParams {
pub(crate) recipients: Vec<(Script, u64)>, pub(crate) recipients: Vec<(ScriptBuf, u64)>,
pub(crate) drain_wallet: bool, pub(crate) drain_wallet: bool,
pub(crate) drain_to: Option<Script>, pub(crate) drain_to: Option<ScriptBuf>,
pub(crate) fee_policy: Option<FeePolicy>, pub(crate) fee_policy: Option<FeePolicy>,
pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>, pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>, pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
@ -140,7 +140,7 @@ pub(crate) struct TxParams {
pub(crate) manually_selected_only: bool, pub(crate) manually_selected_only: bool,
pub(crate) sighash: Option<psbt::PsbtSighashType>, pub(crate) sighash: Option<psbt::PsbtSighashType>,
pub(crate) ordering: TxOrdering, pub(crate) ordering: TxOrdering,
pub(crate) locktime: Option<LockTime>, pub(crate) locktime: Option<absolute::LockTime>,
pub(crate) rbf: Option<RbfValue>, pub(crate) rbf: Option<RbfValue>,
pub(crate) version: Option<Version>, pub(crate) version: Option<Version>,
pub(crate) change_policy: ChangeSpendPolicy, pub(crate) change_policy: ChangeSpendPolicy,
@ -148,7 +148,7 @@ pub(crate) struct TxParams {
pub(crate) add_global_xpubs: bool, pub(crate) add_global_xpubs: bool,
pub(crate) include_output_redeem_witness_script: bool, pub(crate) include_output_redeem_witness_script: bool,
pub(crate) bumping_fee: Option<PreviousFee>, pub(crate) bumping_fee: Option<PreviousFee>,
pub(crate) current_height: Option<LockTime>, pub(crate) current_height: Option<absolute::LockTime>,
pub(crate) allow_dust: bool, pub(crate) allow_dust: bool,
} }
@ -245,7 +245,10 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
/// # use std::collections::BTreeMap; /// # use std::collections::BTreeMap;
/// # use bitcoin::*; /// # use bitcoin::*;
/// # use bdk::*; /// # use bdk::*;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address =
/// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
/// .unwrap()
/// .assume_checked();
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// let mut path = BTreeMap::new(); /// let mut path = BTreeMap::new();
/// path.insert("aabbccdd".to_string(), vec![0, 1]); /// path.insert("aabbccdd".to_string(), vec![0, 1]);
@ -287,6 +290,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
for utxo in utxos { for utxo in utxos {
let descriptor = wallet.get_descriptor_for_keychain(utxo.keychain); let descriptor = wallet.get_descriptor_for_keychain(utxo.keychain);
#[allow(deprecated)]
let satisfaction_weight = descriptor.max_satisfaction_weight().unwrap(); let satisfaction_weight = descriptor.max_satisfaction_weight().unwrap();
self.params.utxos.push(WeightedUtxo { self.params.utxos.push(WeightedUtxo {
satisfaction_weight, satisfaction_weight,
@ -431,7 +435,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
/// Use a specific nLockTime while creating the transaction /// Use a specific nLockTime while creating the transaction
/// ///
/// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator. /// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
pub fn nlocktime(&mut self, locktime: LockTime) -> &mut Self { pub fn nlocktime(&mut self, locktime: absolute::LockTime) -> &mut Self {
self.params.locktime = Some(locktime); self.params.locktime = Some(locktime);
self self
} }
@ -470,7 +474,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
self self
} }
/// Only Fill-in the [`psbt::Input::witness_utxo`](bitcoin::util::psbt::Input::witness_utxo) field when spending from /// Only Fill-in the [`psbt::Input::witness_utxo`](bitcoin::psbt::Input::witness_utxo) field when spending from
/// SegWit descriptors. /// SegWit descriptors.
/// ///
/// This reduces the size of the PSBT, but some signers might reject them due to the lack of /// This reduces the size of the PSBT, but some signers might reject them due to the lack of
@ -480,8 +484,8 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
self self
} }
/// Fill-in the [`psbt::Output::redeem_script`](bitcoin::util::psbt::Output::redeem_script) and /// Fill-in the [`psbt::Output::redeem_script`](bitcoin::psbt::Output::redeem_script) and
/// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields. /// [`psbt::Output::witness_script`](bitcoin::psbt::Output::witness_script) fields.
/// ///
/// This is useful for signers which always require it, like ColdCard hardware wallets. /// This is useful for signers which always require it, like ColdCard hardware wallets.
pub fn include_output_redeem_witness_script(&mut self) -> &mut Self { pub fn include_output_redeem_witness_script(&mut self) -> &mut Self {
@ -568,7 +572,8 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
/// ///
/// In both cases, if you don't provide a current height, we use the last sync height. /// In both cases, if you don't provide a current height, we use the last sync height.
pub fn current_height(&mut self, height: u32) -> &mut Self { pub fn current_height(&mut self, height: u32) -> &mut Self {
self.params.current_height = Some(LockTime::from_height(height).expect("Invalid height")); self.params.current_height =
Some(absolute::LockTime::from_height(height).expect("Invalid height"));
self self
} }
@ -583,20 +588,20 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> { impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
/// Replace the recipients already added with a new list /// Replace the recipients already added with a new list
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self { pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self {
self.params.recipients = recipients; self.params.recipients = recipients;
self self
} }
/// Add a recipient to the internal list /// Add a recipient to the internal list
pub fn add_recipient(&mut self, script_pubkey: Script, amount: u64) -> &mut Self { pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: u64) -> &mut Self {
self.params.recipients.push((script_pubkey, amount)); self.params.recipients.push((script_pubkey, amount));
self self
} }
/// Add data as an output, using OP_RETURN /// Add data as an output, using OP_RETURN
pub fn add_data(&mut self, data: &[u8]) -> &mut Self { pub fn add_data<T: AsRef<PushBytes>>(&mut self, data: &T) -> &mut Self {
let script = Script::new_op_return(data); let script = ScriptBuf::new_op_return(data);
self.add_recipient(script, 0u64); self.add_recipient(script, 0u64);
self self
} }
@ -626,7 +631,10 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
/// # use bitcoin::*; /// # use bitcoin::*;
/// # use bdk::*; /// # use bdk::*;
/// # use bdk::wallet::tx_builder::CreateTx; /// # use bdk::wallet::tx_builder::CreateTx;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address =
/// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
/// .unwrap()
/// .assume_checked();
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// let mut tx_builder = wallet.build_tx(); /// let mut tx_builder = wallet.build_tx();
/// ///
@ -635,7 +643,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
/// .drain_wallet() /// .drain_wallet()
/// // Send the excess (which is all the coins minus the fee) to this address. /// // Send the excess (which is all the coins minus the fee) to this address.
/// .drain_to(to_address.script_pubkey()) /// .drain_to(to_address.script_pubkey())
/// .fee_rate(FeeRate::from_sat_per_vb(5.0)) /// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0))
/// .enable_rbf(); /// .enable_rbf();
/// let (psbt, tx_details) = tx_builder.finish()?; /// let (psbt, tx_details) = tx_builder.finish()?;
/// # Ok::<(), bdk::Error>(()) /// # Ok::<(), bdk::Error>(())
@ -645,7 +653,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
/// [`add_recipient`]: Self::add_recipient /// [`add_recipient`]: Self::add_recipient
/// [`add_utxos`]: Self::add_utxos /// [`add_utxos`]: Self::add_utxos
/// [`drain_wallet`]: Self::drain_wallet /// [`drain_wallet`]: Self::drain_wallet
pub fn drain_to(&mut self, script_pubkey: Script) -> &mut Self { pub fn drain_to(&mut self, script_pubkey: ScriptBuf) -> &mut Self {
self.params.drain_to = Some(script_pubkey); self.params.drain_to = Some(script_pubkey);
self self
} }
@ -663,7 +671,7 @@ impl<'a, D> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpFee> {
/// ///
/// Returns an `Err` if `script_pubkey` can't be found among the recipients of the /// Returns an `Err` if `script_pubkey` can't be found among the recipients of the
/// transaction we are bumping. /// transaction we are bumping.
pub fn allow_shrinking(&mut self, script_pubkey: Script) -> Result<&mut Self, Error> { pub fn allow_shrinking(&mut self, script_pubkey: ScriptBuf) -> Result<&mut Self, Error> {
match self match self
.params .params
.recipients .recipients
@ -868,8 +876,11 @@ mod test {
); );
assert_eq!(tx.output[0].value, 800); assert_eq!(tx.output[0].value, 800);
assert_eq!(tx.output[1].script_pubkey, From::from(vec![0xAA])); assert_eq!(tx.output[1].script_pubkey, ScriptBuf::from(vec![0xAA]));
assert_eq!(tx.output[2].script_pubkey, From::from(vec![0xAA, 0xEE])); assert_eq!(
tx.output[2].script_pubkey,
ScriptBuf::from(vec![0xAA, 0xEE])
);
} }
fn get_test_utxos() -> Vec<LocalUtxo> { fn get_test_utxos() -> Vec<LocalUtxo> {
@ -878,7 +889,7 @@ mod test {
vec![ vec![
LocalUtxo { LocalUtxo {
outpoint: OutPoint { outpoint: OutPoint {
txid: bitcoin::Txid::from_inner([0; 32]), txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
vout: 0, vout: 0,
}, },
txout: Default::default(), txout: Default::default(),
@ -889,7 +900,7 @@ mod test {
}, },
LocalUtxo { LocalUtxo {
outpoint: OutPoint { outpoint: OutPoint {
txid: bitcoin::Txid::from_inner([0; 32]), txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
vout: 1, vout: 1,
}, },
txout: Default::default(), txout: Default::default(),

View File

@ -10,7 +10,7 @@
// licenses. // licenses.
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::{LockTime, Script, Sequence}; use bitcoin::{absolute, Script, Sequence};
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey}; use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
@ -65,7 +65,7 @@ pub(crate) fn check_nsequence_rbf(rbf: Sequence, csv: Sequence) -> bool {
} }
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After { impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
fn check_after(&self, n: LockTime) -> bool { fn check_after(&self, n: absolute::LockTime) -> bool {
if let Some(current_height) = self.current_height { if let Some(current_height) = self.current_height {
current_height >= n.to_consensus_u32() current_height >= n.to_consensus_u32()
} else { } else {
@ -119,12 +119,14 @@ mod test {
pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22; pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
use super::{check_nsequence_rbf, IsDust}; use super::{check_nsequence_rbf, IsDust};
use crate::bitcoin::{Address, Sequence}; use crate::bitcoin::{Address, Network, Sequence};
use core::str::FromStr; use core::str::FromStr;
#[test] #[test]
fn test_is_dust() { fn test_is_dust() {
let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe") let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe")
.unwrap()
.require_network(Network::Bitcoin)
.unwrap() .unwrap()
.script_pubkey(); .script_pubkey();
assert!(script_p2pkh.is_p2pkh()); assert!(script_p2pkh.is_p2pkh());
@ -132,6 +134,8 @@ mod test {
assert!(!546.is_dust(&script_p2pkh)); assert!(!546.is_dust(&script_p2pkh));
let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8") let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8")
.unwrap()
.require_network(Network::Bitcoin)
.unwrap() .unwrap()
.script_pubkey(); .script_pubkey();
assert!(script_p2wpkh.is_v0_p2wpkh()); assert!(script_p2wpkh.is_v0_p2wpkh());

View File

@ -14,7 +14,7 @@ pub fn get_funded_wallet_with_change(
let tx = Transaction { let tx = Transaction {
version: 1, version: 1,
lock_time: bitcoin::PackedLockTime(0), lock_time: bitcoin::absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 50_000, value: 50_000,

View File

@ -2,7 +2,7 @@ use bdk::bitcoin::TxIn;
use bdk::wallet::AddressIndex; use bdk::wallet::AddressIndex;
use bdk::wallet::AddressIndex::New; use bdk::wallet::AddressIndex::New;
use bdk::{psbt, FeeRate, SignOptions}; use bdk::{psbt, FeeRate, SignOptions};
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use bitcoin::psbt::PartiallySignedTransaction as Psbt;
use core::str::FromStr; use core::str::FromStr;
mod common; mod common;
use common::*; use common::*;

View File

@ -11,13 +11,14 @@ use bdk_chain::BlockId;
use bdk_chain::ConfirmationTime; use bdk_chain::ConfirmationTime;
use bdk_chain::COINBASE_MATURITY; use bdk_chain::COINBASE_MATURITY;
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::BlockHash; use bitcoin::BlockHash;
use bitcoin::Script; use bitcoin::ScriptBuf;
use bitcoin::{util::psbt, Network};
use bitcoin::{ use bitcoin::{
Address, EcdsaSighashType, LockTime, OutPoint, PackedLockTime, SchnorrSighashType, Sequence, absolute, script::PushBytesBuf, taproot::TapNodeHash, Address, OutPoint, Sequence, Transaction,
Transaction, TxIn, TxOut, TxIn, TxOut, Weight,
}; };
use bitcoin::{psbt, Network};
use core::str::FromStr; use core::str::FromStr;
mod common; mod common;
@ -26,7 +27,7 @@ use common::*;
fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint { fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint {
let tx = Transaction { let tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
script_pubkey: wallet.get_address(LastUnused).script_pubkey(), script_pubkey: wallet.get_address(LastUnused).script_pubkey(),
@ -210,7 +211,7 @@ fn test_create_tx_default_locktime_is_last_sync_height() {
// Since we never synced the wallet we don't have a last_sync_height // Since we never synced the wallet we don't have a last_sync_height
// we could use to try to prevent fee sniping. We default to 0. // we could use to try to prevent fee sniping. We default to 0.
assert_eq!(psbt.unsigned_tx.lock_time.0, 1_000); assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 1_000);
} }
#[test] #[test]
@ -224,7 +225,7 @@ fn test_create_tx_fee_sniping_locktime_last_sync() {
// If there's no current_height we're left with using the last sync height // If there's no current_height we're left with using the last sync height
assert_eq!( assert_eq!(
psbt.unsigned_tx.lock_time.0, psbt.unsigned_tx.lock_time.to_consensus_u32(),
wallet.latest_checkpoint().unwrap().height() wallet.latest_checkpoint().unwrap().height()
); );
} }
@ -237,7 +238,7 @@ fn test_create_tx_default_locktime_cltv() {
builder.add_recipient(addr.script_pubkey(), 25_000); builder.add_recipient(addr.script_pubkey(), 25_000);
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.unsigned_tx.lock_time.0, 100_000); assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 100_000);
} }
#[test] #[test]
@ -248,13 +249,13 @@ fn test_create_tx_custom_locktime() {
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
.current_height(630_001) .current_height(630_001)
.nlocktime(LockTime::from_height(630_000).unwrap()); .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
// When we explicitly specify a nlocktime // When we explicitly specify a nlocktime
// we don't try any fee sniping prevention trick // we don't try any fee sniping prevention trick
// (we ignore the current_height) // (we ignore the current_height)
assert_eq!(psbt.unsigned_tx.lock_time.0, 630_000); assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
} }
#[test] #[test]
@ -264,10 +265,10 @@ fn test_create_tx_custom_locktime_compatible_with_cltv() {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
.nlocktime(LockTime::from_height(630_000).unwrap()); .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.unsigned_tx.lock_time.0, 630_000); assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
} }
#[test] #[test]
@ -280,7 +281,7 @@ fn test_create_tx_custom_locktime_incompatible_with_cltv() {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
.nlocktime(LockTime::from_height(50000).unwrap()); .nlocktime(absolute::LockTime::from_height(50000).unwrap());
builder.finish().unwrap(); builder.finish().unwrap();
} }
@ -402,7 +403,9 @@ fn test_create_tx_drain_wallet_and_drain_to() {
#[test] #[test]
fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() { fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
.unwrap()
.assume_checked();
let drain_addr = wallet.get_address(New); let drain_addr = wallet.get_address(New);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
@ -614,18 +617,18 @@ fn test_create_tx_custom_sighash() {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
.sighash(bitcoin::EcdsaSighashType::Single.into()); .sighash(EcdsaSighashType::Single.into());
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
assert_eq!( assert_eq!(
psbt.inputs[0].sighash_type, psbt.inputs[0].sighash_type,
Some(bitcoin::EcdsaSighashType::Single.into()) Some(EcdsaSighashType::Single.into())
); );
} }
#[test] #[test]
fn test_create_tx_input_hd_keypaths() { fn test_create_tx_input_hd_keypaths() {
use bitcoin::util::bip32::{DerivationPath, Fingerprint}; use bitcoin::bip32::{DerivationPath, Fingerprint};
use core::str::FromStr; use core::str::FromStr;
let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)"); let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
@ -646,7 +649,7 @@ fn test_create_tx_input_hd_keypaths() {
#[test] #[test]
fn test_create_tx_output_hd_keypaths() { fn test_create_tx_output_hd_keypaths() {
use bitcoin::util::bip32::{DerivationPath, Fingerprint}; use bitcoin::bip32::{DerivationPath, Fingerprint};
use core::str::FromStr; use core::str::FromStr;
let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)"); let (mut wallet, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
@ -680,7 +683,7 @@ fn test_create_tx_set_redeem_script_p2sh() {
assert_eq!( assert_eq!(
psbt.inputs[0].redeem_script, psbt.inputs[0].redeem_script,
Some(Script::from( Some(ScriptBuf::from(
Vec::<u8>::from_hex( Vec::<u8>::from_hex(
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac" "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
) )
@ -704,7 +707,7 @@ fn test_create_tx_set_witness_script_p2wsh() {
assert_eq!(psbt.inputs[0].redeem_script, None); assert_eq!(psbt.inputs[0].redeem_script, None);
assert_eq!( assert_eq!(
psbt.inputs[0].witness_script, psbt.inputs[0].witness_script,
Some(Script::from( Some(ScriptBuf::from(
Vec::<u8>::from_hex( Vec::<u8>::from_hex(
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac" "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
) )
@ -715,8 +718,6 @@ fn test_create_tx_set_witness_script_p2wsh() {
#[test] #[test]
fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() { fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
use bitcoin::hashes::hex::FromHex;
let (mut wallet, _) = let (mut wallet, _) =
get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))"); get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))");
let addr = wallet.get_address(New); let addr = wallet.get_address(New);
@ -724,12 +725,10 @@ fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.drain_to(addr.script_pubkey()).drain_wallet();
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
let script = Script::from( let script = ScriptBuf::from_hex(
Vec::<u8>::from_hex( "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac", )
) .unwrap();
.unwrap(),
);
assert_eq!(psbt.inputs[0].redeem_script, Some(script.to_v0_p2wsh())); assert_eq!(psbt.inputs[0].redeem_script, Some(script.to_v0_p2wsh()));
assert_eq!(psbt.inputs[0].witness_script, Some(script)); assert_eq!(psbt.inputs[0].witness_script, Some(script));
@ -799,7 +798,7 @@ fn test_create_tx_add_utxo() {
script_pubkey: wallet.get_address(New).address.script_pubkey(), script_pubkey: wallet.get_address(New).address.script_pubkey(),
}], }],
version: 0, version: 0,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
}; };
wallet wallet
.insert_tx( .insert_tx(
@ -808,7 +807,9 @@ fn test_create_tx_add_utxo() {
) )
.unwrap(); .unwrap();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -838,7 +839,7 @@ fn test_create_tx_manually_selected_insufficient() {
script_pubkey: wallet.get_address(New).address.script_pubkey(), script_pubkey: wallet.get_address(New).address.script_pubkey(),
}], }],
version: 0, version: 0,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
}; };
wallet wallet
@ -848,7 +849,9 @@ fn test_create_tx_manually_selected_insufficient() {
) )
.unwrap(); .unwrap();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -866,7 +869,9 @@ fn test_create_tx_manually_selected_insufficient() {
fn test_create_tx_policy_path_required() { fn test_create_tx_policy_path_required() {
let (mut wallet, _) = get_funded_wallet(get_test_a_or_b_plus_csv()); let (mut wallet, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 30_000); builder.add_recipient(addr.script_pubkey(), 30_000);
builder.finish().unwrap(); builder.finish().unwrap();
@ -879,7 +884,7 @@ fn test_create_tx_policy_path_no_csv() {
let tx = Transaction { let tx = Transaction {
version: 0, version: 0,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 50_000, value: 50_000,
@ -895,7 +900,9 @@ fn test_create_tx_policy_path_no_csv() {
// child #0 is just the key "A" // child #0 is just the key "A"
let path = vec![(root_id, vec![0])].into_iter().collect(); let path = vec![(root_id, vec![0])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -914,7 +921,9 @@ fn test_create_tx_policy_path_use_csv() {
// child #1 is or(pk(B),older(144)) // child #1 is or(pk(B),older(144))
let path = vec![(root_id, vec![1])].into_iter().collect(); let path = vec![(root_id, vec![1])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -933,7 +942,9 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() {
// child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu) // child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)
let path = vec![(root_id, vec![0])].into_iter().collect(); let path = vec![(root_id, vec![0])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -945,8 +956,8 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() {
#[test] #[test]
fn test_create_tx_global_xpubs_with_origin() { fn test_create_tx_global_xpubs_with_origin() {
use bitcoin::bip32;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::bip32;
let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)"); let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
let addr = wallet.get_address(New); let addr = wallet.get_address(New);
@ -970,8 +981,11 @@ fn test_add_foreign_utxo() {
let (wallet2, _) = let (wallet2, _) =
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)"); get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let utxo = wallet2.list_unspent().next().expect("must take!"); let utxo = wallet2.list_unspent().next().expect("must take!");
#[allow(deprecated)]
let foreign_utxo_satisfaction = wallet2 let foreign_utxo_satisfaction = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -1036,6 +1050,7 @@ fn test_add_foreign_utxo() {
fn test_add_foreign_utxo_invalid_psbt_input() { fn test_add_foreign_utxo_invalid_psbt_input() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let outpoint = wallet.list_unspent().next().expect("must exist").outpoint; let outpoint = wallet.list_unspent().next().expect("must exist").outpoint;
#[allow(deprecated)]
let foreign_utxo_satisfaction = wallet let foreign_utxo_satisfaction = wallet
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -1057,6 +1072,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
let tx1 = wallet1.get_tx(txid1, true).unwrap().transaction.unwrap(); let tx1 = wallet1.get_tx(txid1, true).unwrap().transaction.unwrap();
let tx2 = wallet2.get_tx(txid2, true).unwrap().transaction.unwrap(); let tx2 = wallet2.get_tx(txid2, true).unwrap().transaction.unwrap();
#[allow(deprecated)]
let satisfaction_weight = wallet2 let satisfaction_weight = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -1096,9 +1112,12 @@ fn test_add_foreign_utxo_only_witness_utxo() {
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
let (wallet2, txid2) = let (wallet2, txid2) =
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)"); get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let utxo2 = wallet2.list_unspent().next().unwrap(); let utxo2 = wallet2.list_unspent().next().unwrap();
#[allow(deprecated)]
let satisfaction_weight = wallet2 let satisfaction_weight = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -1181,8 +1200,8 @@ fn test_create_tx_global_xpubs_origin_missing() {
#[test] #[test]
fn test_create_tx_global_xpubs_master_without_origin() { fn test_create_tx_global_xpubs_master_without_origin() {
use bitcoin::bip32;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::bip32;
let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)"); let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
let addr = wallet.get_address(New); let addr = wallet.get_address(New);
@ -1315,7 +1334,9 @@ fn test_bump_fee_zero_abs() {
#[test] #[test]
fn test_bump_fee_reduce_change() { fn test_bump_fee_reduce_change() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
@ -1401,7 +1422,9 @@ fn test_bump_fee_reduce_change() {
#[test] #[test]
fn test_bump_fee_reduce_single_recipient() { fn test_bump_fee_reduce_single_recipient() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -1434,7 +1457,9 @@ fn test_bump_fee_reduce_single_recipient() {
#[test] #[test]
fn test_bump_fee_absolute_reduce_single_recipient() { fn test_bump_fee_absolute_reduce_single_recipient() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -1470,7 +1495,7 @@ fn test_bump_fee_drain_wallet() {
// receive an extra tx so that our wallet has two utxos. // receive an extra tx so that our wallet has two utxos.
let tx = Transaction { let tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 25_000, value: 25_000,
@ -1486,7 +1511,9 @@ fn test_bump_fee_drain_wallet() {
}, },
) )
.unwrap(); .unwrap();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
@ -1530,7 +1557,7 @@ fn test_bump_fee_remove_output_manually_selected_only() {
// unless you've also set "allow_shrinking" OR there is a change output. // unless you've also set "allow_shrinking" OR there is a change output.
let init_tx = Transaction { let init_tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
script_pubkey: wallet.get_address(New).script_pubkey(), script_pubkey: wallet.get_address(New).script_pubkey(),
@ -1553,7 +1580,9 @@ fn test_bump_fee_remove_output_manually_selected_only() {
txid: init_tx.txid(), txid: init_tx.txid(),
vout: 0, vout: 0,
}; };
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -1581,7 +1610,7 @@ fn test_bump_fee_add_input() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let init_tx = Transaction { let init_tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
script_pubkey: wallet.get_address(New).script_pubkey(), script_pubkey: wallet.get_address(New).script_pubkey(),
@ -1597,7 +1626,9 @@ fn test_bump_fee_add_input() {
.into(); .into();
wallet.insert_tx(init_tx, pos).unwrap(); wallet.insert_tx(init_tx, pos).unwrap();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -1643,7 +1674,9 @@ fn test_bump_fee_add_input() {
fn test_bump_fee_absolute_add_input() { fn test_bump_fee_absolute_add_input() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
receive_output_in_latest_block(&mut wallet, 25_000); receive_output_in_latest_block(&mut wallet, 25_000);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -1691,7 +1724,9 @@ fn test_bump_fee_no_change_add_input_and_change() {
let op = receive_output_in_latest_block(&mut wallet, 25_000); let op = receive_output_in_latest_block(&mut wallet, 25_000);
// initially make a tx without change by using `drain_to` // initially make a tx without change by using `drain_to`
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -1747,7 +1782,9 @@ fn test_bump_fee_no_change_add_input_and_change() {
fn test_bump_fee_add_input_change_dust() { fn test_bump_fee_add_input_change_dust() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
receive_output_in_latest_block(&mut wallet, 25_000); receive_output_in_latest_block(&mut wallet, 25_000);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -1775,7 +1812,8 @@ fn test_bump_fee_add_input_change_dust() {
// + extra input weight: 160 WU = (32 (prevout) + 4 (vout) + 4 (nsequence)) * 4 // + extra input weight: 160 WU = (32 (prevout) + 4 (vout) + 4 (nsequence)) * 4
// + input satisfaction weight: 112 WU = 106 (witness) + 2 (witness len) + (1 (script len)) * 4 // + input satisfaction weight: 112 WU = 106 (witness) + 2 (witness len) + (1 (script len)) * 4
// - change output weight: 124 WU = (8 (value) + 1 (script len) + 22 (script)) * 4 // - change output weight: 124 WU = (8 (value) + 1 (script len) + 22 (script)) * 4
let new_tx_weight = original_tx_weight + 160 + 112 - 124; let new_tx_weight =
original_tx_weight + Weight::from_wu(160) + Weight::from_wu(112) - Weight::from_wu(124);
// two inputs (50k, 25k) and one output (45k) - epsilon // two inputs (50k, 25k) and one output (45k) - epsilon
// We use epsilon here to avoid asking for a slightly too high feerate // We use epsilon here to avoid asking for a slightly too high feerate
let fee_abs = 50_000 + 25_000 - 45_000 - 10; let fee_abs = 50_000 + 25_000 - 45_000 - 10;
@ -1811,7 +1849,9 @@ fn test_bump_fee_force_add_input() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000); let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -1865,7 +1905,9 @@ fn test_bump_fee_absolute_force_add_input() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000); let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection); let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -1923,7 +1965,9 @@ fn test_bump_fee_unconfirmed_inputs_only() {
// The replacement transaction may only include an unconfirmed input // The replacement transaction may only include an unconfirmed input
// if that input was included in one of the original transactions. // if that input was included in one of the original transactions.
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_wallet() .drain_wallet()
@ -1958,7 +2002,9 @@ fn test_bump_fee_unconfirmed_input() {
// always fee bump with an unconfirmed input if it was included in the // always fee bump with an unconfirmed input if it was included in the
// original transaction) // original transaction)
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
// We receive a tx with 0 confirmations, which will be used as an input // We receive a tx with 0 confirmations, which will be used as an input
// in the drain tx. // in the drain tx.
receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0)); receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));
@ -1996,7 +2042,9 @@ fn test_fee_amount_negative_drain_val() {
// for a transaction. // for a transaction.
// See https://github.com/bitcoindevkit/bdk/issues/660 // See https://github.com/bitcoindevkit/bdk/issues/660
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap(); let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
.unwrap()
.assume_checked();
let fee_rate = FeeRate::from_sat_per_vb(2.01); let fee_rate = FeeRate::from_sat_per_vb(2.01);
let incoming_op = receive_output_in_latest_block(&mut wallet, 8859); let incoming_op = receive_output_in_latest_block(&mut wallet, 8859);
@ -2110,7 +2158,9 @@ fn test_sign_single_xprv_no_hd_keypaths() {
#[test] #[test]
fn test_include_output_redeem_witness_script() { fn test_include_output_redeem_witness_script() {
let (mut wallet, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))"); let (mut wallet, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -2127,7 +2177,9 @@ fn test_include_output_redeem_witness_script() {
#[test] #[test]
fn test_signing_only_one_of_multiple_inputs() { fn test_signing_only_one_of_multiple_inputs() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -2135,7 +2187,7 @@ fn test_signing_only_one_of_multiple_inputs() {
let (mut psbt, _) = builder.finish().unwrap(); let (mut psbt, _) = builder.finish().unwrap();
// add another input to the psbt that is at least passable. // add another input to the psbt that is at least passable.
let dud_input = bitcoin::util::psbt::Input { let dud_input = bitcoin::psbt::Input {
witness_utxo: Some(TxOut { witness_utxo: Some(TxOut {
value: 100_000, value: 100_000,
script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str( script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
@ -2383,7 +2435,9 @@ fn test_returns_index_and_address() {
wallet.get_address(New), wallet.get_address(New),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a").unwrap(), address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -2393,7 +2447,9 @@ fn test_returns_index_and_address() {
wallet.get_address(New), wallet.get_address(New),
AddressInfo { AddressInfo {
index: 1, index: 1,
address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7").unwrap(), address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -2403,7 +2459,9 @@ fn test_returns_index_and_address() {
wallet.get_address(Peek(25)), wallet.get_address(Peek(25)),
AddressInfo { AddressInfo {
index: 25, index: 25,
address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2").unwrap(), address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -2413,7 +2471,9 @@ fn test_returns_index_and_address() {
wallet.get_address(New), wallet.get_address(New),
AddressInfo { AddressInfo {
index: 2, index: 2,
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2").unwrap(), address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -2423,7 +2483,8 @@ fn test_returns_index_and_address() {
fn test_sending_to_bip350_bech32m_address() { fn test_sending_to_bip350_bech32m_address() {
let (mut wallet, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c") let addr = Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
.unwrap(); .unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 45_000); builder.add_recipient(addr.script_pubkey(), 45_000);
builder.finish().unwrap(); builder.finish().unwrap();
@ -2432,7 +2493,7 @@ fn test_sending_to_bip350_bech32m_address() {
#[test] #[test]
fn test_get_address() { fn test_get_address() {
use bdk::descriptor::template::Bip84; use bdk::descriptor::template::Bip84;
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
let mut wallet = Wallet::new_no_persist( let mut wallet = Wallet::new_no_persist(
Bip84(key, KeychainKind::External), Bip84(key, KeychainKind::External),
Some(Bip84(key, KeychainKind::Internal)), Some(Bip84(key, KeychainKind::Internal)),
@ -2444,7 +2505,9 @@ fn test_get_address() {
wallet.get_address(AddressIndex::New), wallet.get_address(AddressIndex::New),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(), address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -2453,7 +2516,9 @@ fn test_get_address() {
wallet.get_internal_address(AddressIndex::New), wallet.get_internal_address(AddressIndex::New),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79").unwrap(), address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79")
.unwrap()
.assume_checked(),
keychain: KeychainKind::Internal, keychain: KeychainKind::Internal,
} }
); );
@ -2465,7 +2530,9 @@ fn test_get_address() {
wallet.get_internal_address(AddressIndex::New), wallet.get_internal_address(AddressIndex::New),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(), address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
}, },
"when there's no internal descriptor it should just use external" "when there's no internal descriptor it should just use external"
@ -2477,7 +2544,7 @@ fn test_get_address_no_reuse_single_descriptor() {
use bdk::descriptor::template::Bip84; use bdk::descriptor::template::Bip84;
use std::collections::HashSet; use std::collections::HashSet;
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
let mut wallet = let mut wallet =
Wallet::new_no_persist(Bip84(key, KeychainKind::External), None, Network::Regtest).unwrap(); Wallet::new_no_persist(Bip84(key, KeychainKind::External), None, Network::Regtest).unwrap();
@ -2532,7 +2599,7 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
let (mut wallet, _) = get_funded_wallet(get_test_tr_repeated_key()); let (mut wallet, _) = get_funded_wallet(get_test_tr_repeated_key());
let addr = wallet.get_address(AddressIndex::New); let addr = wallet.get_address(AddressIndex::New);
let path = vec![("e5mmg3xh".to_string(), vec![0])] let path = vec![("rn4nre9c".to_string(), vec![0])]
.into_iter() .into_iter()
.collect(); .collect();
@ -2552,13 +2619,6 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
assert_eq!( assert_eq!(
input_key_origins, input_key_origins,
vec![ vec![
(
from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
(
vec![],
(FromStr::from_str("871fd295").unwrap(), vec![].into())
)
),
( (
from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"), from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
( (
@ -2572,6 +2632,13 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
], ],
(FromStr::from_str("ece52657").unwrap(), vec![].into()) (FromStr::from_str("ece52657").unwrap(), vec![].into())
) )
),
(
from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
(
vec![],
(FromStr::from_str("871fd295").unwrap(), vec![].into())
)
) )
], ],
"Wrong input tap_key_origins" "Wrong input tap_key_origins"
@ -2592,10 +2659,8 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
#[test] #[test]
fn test_taproot_psbt_input_tap_tree() { fn test_taproot_psbt_input_tap_tree() {
use bdk::bitcoin::psbt::serialize::Deserialize;
use bdk::bitcoin::psbt::TapTree;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::taproot; use bitcoin::taproot;
let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree()); let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
let addr = wallet.get_address(AddressIndex::Peek(0)); let addr = wallet.get_address(AddressIndex::Peek(0));
@ -2607,15 +2672,17 @@ fn test_taproot_psbt_input_tap_tree() {
assert_eq!( assert_eq!(
psbt.inputs[0].tap_merkle_root, psbt.inputs[0].tap_merkle_root,
Some( Some(
FromHex::from_hex("61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986") TapNodeHash::from_str(
.unwrap() "61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986"
)
.unwrap()
), ),
); );
assert_eq!( assert_eq!(
psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(), psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(),
vec![ vec![
(taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (from_str!("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac"), taproot::LeafVersion::TapScript)), (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (ScriptBuf::from_hex("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac").unwrap(), taproot::LeafVersion::TapScript)),
(taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (from_str!("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac"), taproot::LeafVersion::TapScript)), (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (ScriptBuf::from_hex("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac").unwrap(), taproot::LeafVersion::TapScript)),
], ],
); );
assert_eq!( assert_eq!(
@ -2632,10 +2699,8 @@ fn test_taproot_psbt_input_tap_tree() {
psbt.outputs[0].tap_internal_key psbt.outputs[0].tap_internal_key
); );
assert_eq!( let tap_tree: bitcoin::taproot::TapTree = serde_json::from_str(r#"[1,{"Script":["2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",192]},1,{"Script":["208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac",192]}]"#).unwrap();
psbt.outputs[0].tap_tree, assert_eq!(psbt.outputs[0].tap_tree, Some(tap_tree));
Some(TapTree::deserialize(&Vec::<u8>::from_hex("01c022208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac01c0222051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",).unwrap()).unwrap())
);
} }
#[test] #[test]
@ -2706,9 +2771,12 @@ fn test_taproot_foreign_utxo() {
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh()); let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
let (wallet2, _) = get_funded_wallet(get_test_tr_single_sig()); let (wallet2, _) = get_funded_wallet(get_test_tr_single_sig());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let utxo = wallet2.list_unspent().next().unwrap(); let utxo = wallet2.list_unspent().next().unwrap();
let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap(); let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
#[allow(deprecated)]
let foreign_utxo_satisfaction = wallet2 let foreign_utxo_satisfaction = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -2829,7 +2897,7 @@ fn test_taproot_script_spend_sign_all_leaves() {
#[test] #[test]
fn test_taproot_script_spend_sign_include_some_leaves() { fn test_taproot_script_spend_sign_include_some_leaves() {
use bdk::signer::TapLeavesOptions; use bdk::signer::TapLeavesOptions;
use bitcoin::util::taproot::TapLeafHash; use bitcoin::taproot::TapLeafHash;
let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
let addr = wallet.get_address(AddressIndex::New); let addr = wallet.get_address(AddressIndex::New);
@ -2869,7 +2937,7 @@ fn test_taproot_script_spend_sign_include_some_leaves() {
#[test] #[test]
fn test_taproot_script_spend_sign_exclude_some_leaves() { fn test_taproot_script_spend_sign_exclude_some_leaves() {
use bdk::signer::TapLeavesOptions; use bdk::signer::TapLeavesOptions;
use bitcoin::util::taproot::TapLeafHash; use bitcoin::taproot::TapLeafHash;
let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
let addr = wallet.get_address(AddressIndex::New); let addr = wallet.get_address(AddressIndex::New);
@ -2958,7 +3026,7 @@ fn test_taproot_sign_explicit_sighash_all() {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
.sighash(SchnorrSighashType::All.into()) .sighash(TapSighashType::All.into())
.drain_wallet(); .drain_wallet();
let (mut psbt, _) = builder.finish().unwrap(); let (mut psbt, _) = builder.finish().unwrap();
@ -2971,7 +3039,7 @@ fn test_taproot_sign_explicit_sighash_all() {
#[test] #[test]
fn test_taproot_sign_non_default_sighash() { fn test_taproot_sign_non_default_sighash() {
let sighash = SchnorrSighashType::NonePlusAnyoneCanPay; let sighash = TapSighashType::NonePlusAnyoneCanPay;
let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig()); let (mut wallet, _) = get_funded_wallet(get_test_tr_single_sig());
let addr = wallet.get_address(New); let addr = wallet.get_address(New);
@ -3052,7 +3120,7 @@ fn test_spend_coinbase() {
.unwrap(); .unwrap();
let coinbase_tx = Transaction { let coinbase_tx = Transaction {
version: 1, version: 1,
lock_time: bitcoin::PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::null(), previous_output: OutPoint::null(),
..Default::default() ..Default::default()
@ -3088,7 +3156,9 @@ fn test_spend_coinbase() {
// We try to create a transaction, only to notice that all // We try to create a transaction, only to notice that all
// our funds are unspendable // our funds are unspendable
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), balance.immature / 2) .add_recipient(addr.script_pubkey(), balance.immature / 2)
@ -3167,7 +3237,7 @@ fn test_fee_rate_sign_no_grinding_high_r() {
let addr = wallet.get_address(New); let addr = wallet.get_address(New);
let fee_rate = FeeRate::from_sat_per_vb(1.0); let fee_rate = FeeRate::from_sat_per_vb(1.0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
let mut data = vec![0]; let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
.drain_wallet() .drain_wallet()
@ -3187,8 +3257,8 @@ fn test_fee_rate_sign_no_grinding_high_r() {
while sig_len < 71 { while sig_len < 71 {
// Changing the OP_RETURN data will make the signature change (but not the fee, until // Changing the OP_RETURN data will make the signature change (but not the fee, until
// data[0] is small enough) // data[0] is small enough)
data[0] += 1; data.as_mut_bytes()[0] += 1;
psbt.unsigned_tx.output[op_return_vout].script_pubkey = Script::new_op_return(&data); psbt.unsigned_tx.output[op_return_vout].script_pubkey = ScriptBuf::new_op_return(&data);
// Clearing the previous signature // Clearing the previous signature
psbt.inputs[0].partial_sigs.clear(); psbt.inputs[0].partial_sigs.clear();
// Signing // Signing
@ -3310,7 +3380,9 @@ fn test_taproot_load_descriptor_duplicated_keys() {
fn test_tx_cancellation() { fn test_tx_cancellation() {
macro_rules! new_tx { macro_rules! new_tx {
($wallet:expr) => {{ ($wallet:expr) => {{
let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
.unwrap()
.assume_checked();
let mut builder = $wallet.build_tx(); let mut builder = $wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 10_000); builder.add_recipient(addr.script_pubkey(), 10_000);

View File

@ -14,13 +14,13 @@ readme = "README.md"
[dependencies] [dependencies]
# For no-std, remember to enable the bitcoin/no-std feature # For no-std, remember to enable the bitcoin/no-std feature
bitcoin = { version = "0.29", default-features = false } bitcoin = { version = "0.30.0", default-features = false }
serde_crate = { package = "serde", version = "1", optional = true, features = ["derive"] } serde_crate = { package = "serde", version = "1", optional = true, features = ["derive"] }
# Use hashbrown as a feature flag to have HashSet and HashMap from it. # Use hashbrown as a feature flag to have HashSet and HashMap from it.
# note version 0.13 breaks outs MSRV. # note version 0.13 breaks outs MSRV.
hashbrown = { version = "0.11", optional = true, features = ["serde"] } hashbrown = { version = "0.11", optional = true, features = ["serde"] }
miniscript = { version = "9.0.0", optional = true, default-features = false } miniscript = { version = "10.0.0", optional = true, default-features = false }
[dev-dependencies] [dev-dependencies]
rand = "0.8" rand = "0.8"

View File

@ -104,7 +104,7 @@ impl Default for BlockId {
fn default() -> Self { fn default() -> Self {
Self { Self {
height: Default::default(), height: Default::default(),
hash: BlockHash::from_inner([0u8; 32]), hash: BlockHash::all_zeros(),
} }
} }
} }

View File

@ -3,12 +3,14 @@ use crate::miniscript::{Descriptor, DescriptorPublicKey};
/// A trait to extend the functionality of a miniscript descriptor. /// A trait to extend the functionality of a miniscript descriptor.
pub trait DescriptorExt { pub trait DescriptorExt {
/// Returns the minimum value (in satoshis) at which an output is broadcastable. /// Returns the minimum value (in satoshis) at which an output is broadcastable.
/// Panics if the descriptor wildcard is hardened.
fn dust_value(&self) -> u64; fn dust_value(&self) -> u64;
} }
impl DescriptorExt for Descriptor<DescriptorPublicKey> { impl DescriptorExt for Descriptor<DescriptorPublicKey> {
fn dust_value(&self) -> u64 { fn dust_value(&self) -> u64 {
self.at_derivation_index(0) self.at_derivation_index(0)
.expect("descriptor can't have hardened derivation")
.script_pubkey() .script_pubkey()
.dust_value() .dust_value()
.to_sat() .to_sat()

View File

@ -313,7 +313,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
self.inner self.inner
.all_spks() .all_spks()
.range((keychain.clone(), u32::MIN)..(keychain.clone(), next_index)) .range((keychain.clone(), u32::MIN)..(keychain.clone(), next_index))
.map(|((_, derivation_index), spk)| (*derivation_index, spk)) .map(|((_, derivation_index), spk)| (*derivation_index, spk.as_script()))
} }
/// Get the next derivation index for `keychain`. The next index is the index after the last revealed /// Get the next derivation index for `keychain`. The next index is the index after the last revealed

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
bitcoin::{secp256k1::Secp256k1, Script}, bitcoin::{secp256k1::Secp256k1, ScriptBuf},
miniscript::{Descriptor, DescriptorPublicKey}, miniscript::{Descriptor, DescriptorPublicKey},
}; };
use core::{borrow::Borrow, ops::Bound, ops::RangeBounds}; use core::{borrow::Borrow, ops::Bound, ops::RangeBounds};
@ -22,9 +22,9 @@ pub const BIP32_MAX_INDEX: u32 = (1 << 31) - 1;
/// # use std::str::FromStr; /// # use std::str::FromStr;
/// # let secp = bitcoin::secp256k1::Secp256k1::signing_only(); /// # let secp = bitcoin::secp256k1::Secp256k1::signing_only();
/// # let (descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap(); /// # let (descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap();
/// # let external_spk_0 = descriptor.at_derivation_index(0).script_pubkey(); /// # let external_spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
/// # let external_spk_3 = descriptor.at_derivation_index(3).script_pubkey(); /// # let external_spk_3 = descriptor.at_derivation_index(3).unwrap().script_pubkey();
/// # let external_spk_4 = descriptor.at_derivation_index(4).script_pubkey(); /// # let external_spk_4 = descriptor.at_derivation_index(4).unwrap().script_pubkey();
/// ///
/// // Creates a new script pubkey iterator starting at 0 from a descriptor. /// // Creates a new script pubkey iterator starting at 0 from a descriptor.
/// let mut spk_iter = SpkIterator::new(&descriptor); /// let mut spk_iter = SpkIterator::new(&descriptor);
@ -84,7 +84,7 @@ impl<D> Iterator for SpkIterator<D>
where where
D: Borrow<Descriptor<DescriptorPublicKey>>, D: Borrow<Descriptor<DescriptorPublicKey>>,
{ {
type Item = (u32, Script); type Item = (u32, ScriptBuf);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// For non-wildcard descriptors, we expect the first element to be Some((0, spk)), then None after. // For non-wildcard descriptors, we expect the first element to be Some((0, spk)), then None after.
@ -96,8 +96,7 @@ where
let script = self let script = self
.descriptor .descriptor
.borrow() .borrow()
.at_derivation_index(self.next_index) .derived_descriptor(&self.secp, self.next_index)
.derived_descriptor(&self.secp)
.expect("the descriptor cannot need hardened derivation") .expect("the descriptor cannot need hardened derivation")
.script_pubkey(); .script_pubkey();
let output = (self.next_index, script); let output = (self.next_index, script);
@ -149,15 +148,14 @@ mod test {
#[test] #[test]
#[allow(clippy::iter_nth_zero)] #[allow(clippy::iter_nth_zero)]
#[rustfmt::skip]
fn test_spkiterator_wildcard() { fn test_spkiterator_wildcard() {
let (_, external_desc, _) = init_txout_index(); let (_, external_desc, _) = init_txout_index();
let external_spk_0 = external_desc.at_derivation_index(0).script_pubkey(); let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey();
let external_spk_16 = external_desc.at_derivation_index(16).script_pubkey(); let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey();
let external_spk_20 = external_desc.at_derivation_index(20).script_pubkey(); let external_spk_20 = external_desc.at_derivation_index(20).unwrap().script_pubkey();
let external_spk_21 = external_desc.at_derivation_index(21).script_pubkey(); let external_spk_21 = external_desc.at_derivation_index(21).unwrap().script_pubkey();
let external_spk_max = external_desc let external_spk_max = external_desc.at_derivation_index(BIP32_MAX_INDEX).unwrap().script_pubkey();
.at_derivation_index(BIP32_MAX_INDEX)
.script_pubkey();
let mut external_spk = SpkIterator::new(&external_desc); let mut external_spk = SpkIterator::new(&external_desc);
let max_index = BIP32_MAX_INDEX - 22; let max_index = BIP32_MAX_INDEX - 22;
@ -187,6 +185,7 @@ mod test {
let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap(); let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap();
let external_spk_0 = no_wildcard_descriptor let external_spk_0 = no_wildcard_descriptor
.at_derivation_index(0) .at_derivation_index(0)
.unwrap()
.script_pubkey(); .script_pubkey();
let mut external_spk = SpkIterator::new(&no_wildcard_descriptor); let mut external_spk = SpkIterator::new(&no_wildcard_descriptor);

View File

@ -5,7 +5,7 @@ use crate::{
indexed_tx_graph::Indexer, indexed_tx_graph::Indexer,
ForEachTxOut, ForEachTxOut,
}; };
use bitcoin::{self, OutPoint, Script, Transaction, TxOut, Txid}; use bitcoin::{self, OutPoint, Script, ScriptBuf, Transaction, TxOut, Txid};
/// An index storing [`TxOut`]s that have a script pubkey that matches those in a list. /// An index storing [`TxOut`]s that have a script pubkey that matches those in a list.
/// ///
@ -30,9 +30,9 @@ use bitcoin::{self, OutPoint, Script, Transaction, TxOut, Txid};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SpkTxOutIndex<I> { pub struct SpkTxOutIndex<I> {
/// script pubkeys ordered by index /// script pubkeys ordered by index
spks: BTreeMap<I, Script>, spks: BTreeMap<I, ScriptBuf>,
/// A reverse lookup from spk to spk index /// A reverse lookup from spk to spk index
spk_indices: HashMap<Script, I>, spk_indices: HashMap<ScriptBuf, I>,
/// The set of unused indexes. /// The set of unused indexes.
unused: BTreeSet<I>, unused: BTreeSet<I>,
/// Lookup index and txout by outpoint. /// Lookup index and txout by outpoint.
@ -152,11 +152,11 @@ impl<I: Clone + Ord> SpkTxOutIndex<I> {
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use core::ops::Bound::*; use core::ops::Bound::*;
let min_op = OutPoint { let min_op = OutPoint {
txid: Txid::from_inner([0x00; 32]), txid: Txid::all_zeros(),
vout: u32::MIN, vout: u32::MIN,
}; };
let max_op = OutPoint { let max_op = OutPoint {
txid: Txid::from_inner([0xff; 32]), txid: Txid::from_byte_array([0xff; Txid::LEN]),
vout: u32::MAX, vout: u32::MAX,
}; };
@ -188,18 +188,18 @@ impl<I: Clone + Ord> SpkTxOutIndex<I> {
/// ///
/// If that index hasn't been inserted yet, it will return `None`. /// If that index hasn't been inserted yet, it will return `None`.
pub fn spk_at_index(&self, index: &I) -> Option<&Script> { pub fn spk_at_index(&self, index: &I) -> Option<&Script> {
self.spks.get(index) self.spks.get(index).map(|s| s.as_script())
} }
/// The script pubkeys that are being tracked by the index. /// The script pubkeys that are being tracked by the index.
pub fn all_spks(&self) -> &BTreeMap<I, Script> { pub fn all_spks(&self) -> &BTreeMap<I, ScriptBuf> {
&self.spks &self.spks
} }
/// Adds a script pubkey to scan for. Returns `false` and does nothing if spk already exists in the map /// Adds a script pubkey to scan for. Returns `false` and does nothing if spk already exists in the map
/// ///
/// the index will look for outputs spending to this spk whenever it scans new data. /// the index will look for outputs spending to this spk whenever it scans new data.
pub fn insert_spk(&mut self, index: I, spk: Script) -> bool { pub fn insert_spk(&mut self, index: I, spk: ScriptBuf) -> bool {
match self.spk_indices.entry(spk.clone()) { match self.spk_indices.entry(spk.clone()) {
Entry::Vacant(value) => { Entry::Vacant(value) => {
value.insert(index.clone()); value.insert(index.clone());

View File

@ -56,7 +56,7 @@ macro_rules! changeset {
pub fn new_tx(lt: u32) -> bitcoin::Transaction { pub fn new_tx(lt: u32) -> bitcoin::Transaction {
bitcoin::Transaction { bitcoin::Transaction {
version: 0x00, version: 0x00,
lock_time: bitcoin::PackedLockTime(lt), lock_time: bitcoin::absolute::LockTime::from_consensus(lt),
input: vec![], input: vec![],
output: vec![], output: vec![],
} }

View File

@ -10,7 +10,9 @@ use bdk_chain::{
tx_graph::Additions, tx_graph::Additions,
BlockId, ChainPosition, ConfirmationHeightAnchor, BlockId, ChainPosition, ConfirmationHeightAnchor,
}; };
use bitcoin::{secp256k1::Secp256k1, BlockHash, OutPoint, Script, Transaction, TxIn, TxOut}; use bitcoin::{
secp256k1::Secp256k1, BlockHash, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut,
};
use miniscript::Descriptor; use miniscript::Descriptor;
/// Ensure [`IndexedTxGraph::insert_relevant_txs`] can successfully index transactions NOT presented /// Ensure [`IndexedTxGraph::insert_relevant_txs`] can successfully index transactions NOT presented
@ -25,8 +27,8 @@ fn insert_relevant_txs() {
const DESCRIPTOR: &str = "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)"; const DESCRIPTOR: &str = "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)";
let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTOR) let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTOR)
.expect("must be valid"); .expect("must be valid");
let spk_0 = descriptor.at_derivation_index(0).script_pubkey(); let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
let spk_1 = descriptor.at_derivation_index(9).script_pubkey(); let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::default(); let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::default();
graph.index.add_keychain((), descriptor); graph.index.add_keychain((), descriptor);
@ -127,21 +129,21 @@ fn test_list_owned_txouts() {
// Get trusted and untrusted addresses // Get trusted and untrusted addresses
let mut trusted_spks = Vec::new(); let mut trusted_spks: Vec<ScriptBuf> = Vec::new();
let mut untrusted_spks = Vec::new(); let mut untrusted_spks: Vec<ScriptBuf> = Vec::new();
{ {
// we need to scope here to take immutanble reference of the graph // we need to scope here to take immutanble reference of the graph
for _ in 0..10 { for _ in 0..10 {
let ((_, script), _) = graph.index.reveal_next_spk(&"keychain_1".to_string()); let ((_, script), _) = graph.index.reveal_next_spk(&"keychain_1".to_string());
// TODO Assert indexes // TODO Assert indexes
trusted_spks.push(script.clone()); trusted_spks.push(script.to_owned());
} }
} }
{ {
for _ in 0..10 { for _ in 0..10 {
let ((_, script), _) = graph.index.reveal_next_spk(&"keychain_2".to_string()); let ((_, script), _) = graph.index.reveal_next_spk(&"keychain_2".to_string());
untrusted_spks.push(script.clone()); untrusted_spks.push(script.to_owned());
} }
} }
@ -155,7 +157,7 @@ fn test_list_owned_txouts() {
}], }],
output: vec![TxOut { output: vec![TxOut {
value: 70000, value: 70000,
script_pubkey: trusted_spks[0].clone(), script_pubkey: trusted_spks[0].to_owned(),
}], }],
..common::new_tx(0) ..common::new_tx(0)
}; };
@ -164,7 +166,7 @@ fn test_list_owned_txouts() {
let tx2 = Transaction { let tx2 = Transaction {
output: vec![TxOut { output: vec![TxOut {
value: 30000, value: 30000,
script_pubkey: untrusted_spks[0].clone(), script_pubkey: untrusted_spks[0].to_owned(),
}], }],
..common::new_tx(0) ..common::new_tx(0)
}; };
@ -177,7 +179,7 @@ fn test_list_owned_txouts() {
}], }],
output: vec![TxOut { output: vec![TxOut {
value: 10000, value: 10000,
script_pubkey: trusted_spks[1].clone(), script_pubkey: trusted_spks[1].to_owned(),
}], }],
..common::new_tx(0) ..common::new_tx(0)
}; };
@ -186,7 +188,7 @@ fn test_list_owned_txouts() {
let tx4 = Transaction { let tx4 = Transaction {
output: vec![TxOut { output: vec![TxOut {
value: 20000, value: 20000,
script_pubkey: untrusted_spks[1].clone(), script_pubkey: untrusted_spks[1].to_owned(),
}], }],
..common::new_tx(0) ..common::new_tx(0)
}; };
@ -195,7 +197,7 @@ fn test_list_owned_txouts() {
let tx5 = Transaction { let tx5 = Transaction {
output: vec![TxOut { output: vec![TxOut {
value: 15000, value: 15000,
script_pubkey: trusted_spks[2].clone(), script_pubkey: trusted_spks[2].to_owned(),
}], }],
..common::new_tx(0) ..common::new_tx(0)
}; };
@ -258,7 +260,7 @@ fn test_list_owned_txouts() {
&local_chain, &local_chain,
chain_tip, chain_tip,
graph.index.outpoints().iter().cloned(), graph.index.outpoints().iter().cloned(),
|_, spk: &Script| trusted_spks.contains(spk), |_, spk: &Script| trusted_spks.contains(&spk.to_owned()),
); );
assert_eq!(txouts.len(), 5); assert_eq!(txouts.len(), 5);

View File

@ -8,7 +8,7 @@ use bdk_chain::{
Append, Append,
}; };
use bitcoin::{secp256k1::Secp256k1, OutPoint, Script, Transaction, TxOut}; use bitcoin::{secp256k1::Secp256k1, OutPoint, ScriptBuf, Transaction, TxOut};
use miniscript::{Descriptor, DescriptorPublicKey}; use miniscript::{Descriptor, DescriptorPublicKey};
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
@ -34,7 +34,7 @@ fn init_txout_index() -> (
(txout_index, external_descriptor, internal_descriptor) (txout_index, external_descriptor, internal_descriptor)
} }
fn spk_at_index(descriptor: &Descriptor<DescriptorPublicKey>, index: u32) -> Script { fn spk_at_index(descriptor: &Descriptor<DescriptorPublicKey>, index: u32) -> ScriptBuf {
descriptor descriptor
.derived_descriptor(&Secp256k1::verification_only(), index) .derived_descriptor(&Secp256k1::verification_only(), index)
.expect("must derive") .expect("must derive")
@ -177,12 +177,14 @@ fn test_lookahead() {
TxOut { TxOut {
script_pubkey: external_desc script_pubkey: external_desc
.at_derivation_index(external_index) .at_derivation_index(external_index)
.unwrap()
.script_pubkey(), .script_pubkey(),
value: 10_000, value: 10_000,
}, },
TxOut { TxOut {
script_pubkey: internal_desc script_pubkey: internal_desc
.at_derivation_index(internal_index) .at_derivation_index(internal_index)
.unwrap()
.script_pubkey(), .script_pubkey(),
value: 10_000, value: 10_000,
}, },
@ -223,9 +225,17 @@ fn test_scan_with_lookahead() {
let (mut txout_index, external_desc, _) = init_txout_index(); let (mut txout_index, external_desc, _) = init_txout_index();
txout_index.set_lookahead_for_all(10); txout_index.set_lookahead_for_all(10);
let spks: BTreeMap<u32, Script> = [0, 10, 20, 30] let spks: BTreeMap<u32, ScriptBuf> = [0, 10, 20, 30]
.into_iter() .into_iter()
.map(|i| (i, external_desc.at_derivation_index(i).script_pubkey())) .map(|i| {
(
i,
external_desc
.at_derivation_index(i)
.unwrap()
.script_pubkey(),
)
})
.collect(); .collect();
for (&spk_i, spk) in &spks { for (&spk_i, spk) in &spks {
@ -251,7 +261,10 @@ fn test_scan_with_lookahead() {
} }
// now try with index 41 (lookahead surpassed), we expect that the txout to not be indexed // now try with index 41 (lookahead surpassed), we expect that the txout to not be indexed
let spk_41 = external_desc.at_derivation_index(41).script_pubkey(); let spk_41 = external_desc
.at_derivation_index(41)
.unwrap()
.script_pubkey();
let op = OutPoint::new(h!("fake tx"), 41); let op = OutPoint::new(h!("fake tx"), 41);
let txout = TxOut { let txout = TxOut {
script_pubkey: spk_41, script_pubkey: spk_41,
@ -262,12 +275,13 @@ fn test_scan_with_lookahead() {
} }
#[test] #[test]
#[rustfmt::skip]
fn test_wildcard_derivations() { fn test_wildcard_derivations() {
let (mut txout_index, external_desc, _) = init_txout_index(); let (mut txout_index, external_desc, _) = init_txout_index();
let external_spk_0 = external_desc.at_derivation_index(0).script_pubkey(); let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey();
let external_spk_16 = external_desc.at_derivation_index(16).script_pubkey(); let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey();
let external_spk_26 = external_desc.at_derivation_index(26).script_pubkey(); let external_spk_26 = external_desc.at_derivation_index(26).unwrap().script_pubkey();
let external_spk_27 = external_desc.at_derivation_index(27).script_pubkey(); let external_spk_27 = external_desc.at_derivation_index(27).unwrap().script_pubkey();
// - nothing is derived // - nothing is derived
// - unused list is also empty // - unused list is also empty
@ -277,10 +291,10 @@ fn test_wildcard_derivations() {
// - next_unused() == ((0, <spk>), DerivationAdditions:is_empty()) // - next_unused() == ((0, <spk>), DerivationAdditions:is_empty())
assert_eq!(txout_index.next_index(&TestKeychain::External), (0, true)); assert_eq!(txout_index.next_index(&TestKeychain::External), (0, true));
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External); let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
assert_eq!(spk, (0_u32, &external_spk_0)); assert_eq!(spk, (0_u32, external_spk_0.as_script()));
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 0)].into()); assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 0)].into());
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
assert_eq!(spk, (0_u32, &external_spk_0)); assert_eq!(spk, (0_u32, external_spk_0.as_script()));
assert_eq!(changeset.as_inner(), &[].into()); assert_eq!(changeset.as_inner(), &[].into());
// - derived till 25 // - derived till 25
@ -300,12 +314,12 @@ fn test_wildcard_derivations() {
assert_eq!(txout_index.next_index(&TestKeychain::External), (26, true)); assert_eq!(txout_index.next_index(&TestKeychain::External), (26, true));
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External); let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
assert_eq!(spk, (26, &external_spk_26)); assert_eq!(spk, (26, external_spk_26.as_script()));
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 26)].into()); assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 26)].into());
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
assert_eq!(spk, (16, &external_spk_16)); assert_eq!(spk, (16, external_spk_16.as_script()));
assert_eq!(changeset.as_inner(), &[].into()); assert_eq!(changeset.as_inner(), &[].into());
// - Use all the derived till 26. // - Use all the derived till 26.
@ -315,7 +329,7 @@ fn test_wildcard_derivations() {
}); });
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
assert_eq!(spk, (27, &external_spk_27)); assert_eq!(spk, (27, external_spk_27.as_script()));
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 27)].into()); assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 27)].into());
} }
@ -327,6 +341,7 @@ fn test_non_wildcard_derivations() {
let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap(); let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap();
let external_spk = no_wildcard_descriptor let external_spk = no_wildcard_descriptor
.at_derivation_index(0) .at_derivation_index(0)
.unwrap()
.script_pubkey(); .script_pubkey();
txout_index.add_keychain(TestKeychain::External, no_wildcard_descriptor); txout_index.add_keychain(TestKeychain::External, no_wildcard_descriptor);
@ -339,11 +354,11 @@ fn test_non_wildcard_derivations() {
// - when we get the next unused script, script @ index 0 // - when we get the next unused script, script @ index 0
assert_eq!(txout_index.next_index(&TestKeychain::External), (0, true)); assert_eq!(txout_index.next_index(&TestKeychain::External), (0, true));
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External); let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
assert_eq!(spk, (0, &external_spk)); assert_eq!(spk, (0, external_spk.as_script()));
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 0)].into()); assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 0)].into());
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
assert_eq!(spk, (0, &external_spk)); assert_eq!(spk, (0, external_spk.as_script()));
assert_eq!(changeset.as_inner(), &[].into()); assert_eq!(changeset.as_inner(), &[].into());
// given: // given:
@ -356,11 +371,11 @@ fn test_non_wildcard_derivations() {
txout_index.mark_used(&TestKeychain::External, 0); txout_index.mark_used(&TestKeychain::External, 0);
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External); let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
assert_eq!(spk, (0, &external_spk)); assert_eq!(spk, (0, external_spk.as_script()));
assert_eq!(changeset.as_inner(), &[].into()); assert_eq!(changeset.as_inner(), &[].into());
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External); let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
assert_eq!(spk, (0, &external_spk)); assert_eq!(spk, (0, external_spk.as_script()));
assert_eq!(changeset.as_inner(), &[].into()); assert_eq!(changeset.as_inner(), &[].into());
let (revealed_spks, revealed_additions) = let (revealed_spks, revealed_additions) =
txout_index.reveal_to_target(&TestKeychain::External, 200); txout_index.reveal_to_target(&TestKeychain::External, 200);

View File

@ -1,10 +1,10 @@
use bdk_chain::SpkTxOutIndex; use bdk_chain::SpkTxOutIndex;
use bitcoin::{hashes::hex::FromHex, OutPoint, PackedLockTime, Script, Transaction, TxIn, TxOut}; use bitcoin::{absolute, OutPoint, ScriptBuf, Transaction, TxIn, TxOut};
#[test] #[test]
fn spk_txout_sent_and_received() { fn spk_txout_sent_and_received() {
let spk1 = Script::from_hex("001404f1e52ce2bab3423c6a8c63b7cd730d8f12542c").unwrap(); let spk1 = ScriptBuf::from_hex("001404f1e52ce2bab3423c6a8c63b7cd730d8f12542c").unwrap();
let spk2 = Script::from_hex("00142b57404ae14f08c3a0c903feb2af7830605eb00f").unwrap(); let spk2 = ScriptBuf::from_hex("00142b57404ae14f08c3a0c903feb2af7830605eb00f").unwrap();
let mut index = SpkTxOutIndex::default(); let mut index = SpkTxOutIndex::default();
index.insert_spk(0, spk1.clone()); index.insert_spk(0, spk1.clone());
@ -12,7 +12,7 @@ fn spk_txout_sent_and_received() {
let tx1 = Transaction { let tx1 = Transaction {
version: 0x02, version: 0x02,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 42_000, value: 42_000,
@ -31,7 +31,7 @@ fn spk_txout_sent_and_received() {
let tx2 = Transaction { let tx2 = Transaction {
version: 0x1, version: 0x1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint { previous_output: OutPoint {
txid: tx1.txid(), txid: tx1.txid(),
@ -57,8 +57,8 @@ fn spk_txout_sent_and_received() {
#[test] #[test]
fn mark_used() { fn mark_used() {
let spk1 = Script::from_hex("001404f1e52ce2bab3423c6a8c63b7cd730d8f12542c").unwrap(); let spk1 = ScriptBuf::from_hex("001404f1e52ce2bab3423c6a8c63b7cd730d8f12542c").unwrap();
let spk2 = Script::from_hex("00142b57404ae14f08c3a0c903feb2af7830605eb00f").unwrap(); let spk2 = ScriptBuf::from_hex("00142b57404ae14f08c3a0c903feb2af7830605eb00f").unwrap();
let mut spk_index = SpkTxOutIndex::default(); let mut spk_index = SpkTxOutIndex::default();
spk_index.insert_spk(1, spk1.clone()); spk_index.insert_spk(1, spk1.clone());
@ -74,7 +74,7 @@ fn mark_used() {
let tx1 = Transaction { let tx1 = Transaction {
version: 0x02, version: 0x02,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 42_000, value: 42_000,

View File

@ -7,7 +7,7 @@ use bdk_chain::{
Anchor, Append, BlockId, ChainPosition, ConfirmationHeightAnchor, Anchor, Append, BlockId, ChainPosition, ConfirmationHeightAnchor,
}; };
use bitcoin::{ use bitcoin::{
hashes::Hash, BlockHash, OutPoint, PackedLockTime, Script, Transaction, TxIn, TxOut, Txid, absolute, hashes::Hash, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid,
}; };
use core::iter; use core::iter;
use std::vec; use std::vec;
@ -20,14 +20,14 @@ fn insert_txouts() {
OutPoint::new(h!("tx1"), 1), OutPoint::new(h!("tx1"), 1),
TxOut { TxOut {
value: 10_000, value: 10_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
), ),
( (
OutPoint::new(h!("tx1"), 2), OutPoint::new(h!("tx1"), 2),
TxOut { TxOut {
value: 20_000, value: 20_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
), ),
]; ];
@ -37,21 +37,21 @@ fn insert_txouts() {
OutPoint::new(h!("tx2"), 0), OutPoint::new(h!("tx2"), 0),
TxOut { TxOut {
value: 20_000, value: 20_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
)]; )];
// One full transaction to be included in the update // One full transaction to be included in the update
let update_txs = Transaction { let update_txs = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::null(), previous_output: OutPoint::null(),
..Default::default() ..Default::default()
}], }],
output: vec![TxOut { output: vec![TxOut {
value: 30_000, value: 30_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}], }],
}; };
@ -161,14 +161,14 @@ fn insert_txouts() {
1u32, 1u32,
&TxOut { &TxOut {
value: 10_000, value: 10_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
} }
), ),
( (
2u32, 2u32,
&TxOut { &TxOut {
value: 20_000, value: 20_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
} }
) )
] ]
@ -181,7 +181,7 @@ fn insert_txouts() {
0u32, 0u32,
&TxOut { &TxOut {
value: 30_000, value: 30_000,
script_pubkey: Script::new() script_pubkey: ScriptBuf::new()
} }
)] )]
.into() .into()
@ -192,7 +192,7 @@ fn insert_txouts() {
fn insert_tx_graph_doesnt_count_coinbase_as_spent() { fn insert_tx_graph_doesnt_count_coinbase_as_spent() {
let tx = Transaction { let tx = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::null(), previous_output: OutPoint::null(),
..Default::default() ..Default::default()
@ -210,7 +210,7 @@ fn insert_tx_graph_doesnt_count_coinbase_as_spent() {
fn insert_tx_graph_keeps_track_of_spend() { fn insert_tx_graph_keeps_track_of_spend() {
let tx1 = Transaction { let tx1 = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut::default()], output: vec![TxOut::default()],
}; };
@ -222,7 +222,7 @@ fn insert_tx_graph_keeps_track_of_spend() {
let tx2 = Transaction { let tx2 = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: op, previous_output: op,
..Default::default() ..Default::default()
@ -251,7 +251,7 @@ fn insert_tx_graph_keeps_track_of_spend() {
fn insert_tx_can_retrieve_full_tx_from_graph() { fn insert_tx_can_retrieve_full_tx_from_graph() {
let tx = Transaction { let tx = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::null(), previous_output: OutPoint::null(),
..Default::default() ..Default::default()
@ -269,11 +269,11 @@ fn insert_tx_displaces_txouts() {
let mut tx_graph = TxGraph::<()>::default(); let mut tx_graph = TxGraph::<()>::default();
let tx = Transaction { let tx = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 42_000, value: 42_000,
script_pubkey: Script::default(), script_pubkey: ScriptBuf::default(),
}], }],
}; };
@ -284,7 +284,7 @@ fn insert_tx_displaces_txouts() {
}, },
TxOut { TxOut {
value: 1_337_000, value: 1_337_000,
script_pubkey: Script::default(), script_pubkey: ScriptBuf::default(),
}, },
); );
@ -295,7 +295,7 @@ fn insert_tx_displaces_txouts() {
}, },
TxOut { TxOut {
value: 1_000_000_000, value: 1_000_000_000,
script_pubkey: Script::default(), script_pubkey: ScriptBuf::default(),
}, },
); );
@ -325,11 +325,11 @@ fn insert_txout_does_not_displace_tx() {
let mut tx_graph = TxGraph::<()>::default(); let mut tx_graph = TxGraph::<()>::default();
let tx = Transaction { let tx = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 42_000, value: 42_000,
script_pubkey: Script::default(), script_pubkey: ScriptBuf::default(),
}], }],
}; };
@ -342,7 +342,7 @@ fn insert_txout_does_not_displace_tx() {
}, },
TxOut { TxOut {
value: 1_337_000, value: 1_337_000,
script_pubkey: Script::default(), script_pubkey: ScriptBuf::default(),
}, },
); );
@ -353,7 +353,7 @@ fn insert_txout_does_not_displace_tx() {
}, },
TxOut { TxOut {
value: 1_000_000_000, value: 1_000_000_000,
script_pubkey: Script::default(), script_pubkey: ScriptBuf::default(),
}, },
); );
@ -381,7 +381,7 @@ fn test_calculate_fee() {
let mut graph = TxGraph::<()>::default(); let mut graph = TxGraph::<()>::default();
let intx1 = Transaction { let intx1 = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 100, value: 100,
@ -390,7 +390,7 @@ fn test_calculate_fee() {
}; };
let intx2 = Transaction { let intx2 = Transaction {
version: 0x02, version: 0x02,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![], input: vec![],
output: vec![TxOut { output: vec![TxOut {
value: 200, value: 200,
@ -415,7 +415,7 @@ fn test_calculate_fee() {
let mut tx = Transaction { let mut tx = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![ input: vec![
TxIn { TxIn {
previous_output: OutPoint { previous_output: OutPoint {
@ -464,7 +464,7 @@ fn test_calculate_fee() {
fn test_calculate_fee_on_coinbase() { fn test_calculate_fee_on_coinbase() {
let tx = Transaction { let tx = Transaction {
version: 0x01, version: 0x01,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::null(), previous_output: OutPoint::null(),
..Default::default() ..Default::default()
@ -636,11 +636,11 @@ fn test_chain_spends() {
output: vec![ output: vec![
TxOut { TxOut {
value: 10_000, value: 10_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
TxOut { TxOut {
value: 20_000, value: 20_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
], ],
..common::new_tx(0) ..common::new_tx(0)
@ -655,11 +655,11 @@ fn test_chain_spends() {
output: vec![ output: vec![
TxOut { TxOut {
value: 5_000, value: 5_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
TxOut { TxOut {
value: 5_000, value: 5_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
], ],
..common::new_tx(0) ..common::new_tx(0)
@ -674,11 +674,11 @@ fn test_chain_spends() {
output: vec![ output: vec![
TxOut { TxOut {
value: 10_000, value: 10_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
TxOut { TxOut {
value: 10_000, value: 10_000,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
], ],
..common::new_tx(0) ..common::new_tx(0)

View File

@ -13,4 +13,5 @@ readme = "README.md"
[dependencies] [dependencies]
bdk_chain = { path = "../chain", version = "0.5.0", features = ["serde", "miniscript"] } bdk_chain = { path = "../chain", version = "0.5.0", features = ["serde", "miniscript"] }
electrum-client = { version = "0.12" } electrum-client = { version = "0.17" }
#rustls = { version = "=0.21.1", optional = true, features = ["dangerous_configuration"] }

View File

@ -1,5 +1,5 @@
use bdk_chain::{ use bdk_chain::{
bitcoin::{hashes::hex::FromHex, OutPoint, Script, Transaction, Txid}, bitcoin::{OutPoint, ScriptBuf, Transaction, Txid},
keychain::LocalUpdate, keychain::LocalUpdate,
local_chain::{self, CheckPoint}, local_chain::{self, CheckPoint},
tx_graph::{self, TxGraph}, tx_graph::{self, TxGraph},
@ -9,6 +9,7 @@ use electrum_client::{Client, ElectrumApi, Error, HeaderNotification};
use std::{ use std::{
collections::{BTreeMap, BTreeSet, HashMap, HashSet}, collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fmt::Debug, fmt::Debug,
str::FromStr,
}; };
/// We assume that a block of this depth and deeper cannot be reorged. /// We assume that a block of this depth and deeper cannot be reorged.
@ -167,7 +168,7 @@ pub trait ElectrumExt<A> {
fn scan<K: Ord + Clone>( fn scan<K: Ord + Clone>(
&self, &self,
prev_tip: Option<CheckPoint>, prev_tip: Option<CheckPoint>,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, Script)>>, keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
txids: impl IntoIterator<Item = Txid>, txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>, outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize, stop_gap: usize,
@ -180,7 +181,7 @@ pub trait ElectrumExt<A> {
fn scan_without_keychain( fn scan_without_keychain(
&self, &self,
prev_tip: Option<CheckPoint>, prev_tip: Option<CheckPoint>,
misc_spks: impl IntoIterator<Item = Script>, misc_spks: impl IntoIterator<Item = ScriptBuf>,
txids: impl IntoIterator<Item = Txid>, txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>, outpoints: impl IntoIterator<Item = OutPoint>,
batch_size: usize, batch_size: usize,
@ -205,7 +206,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
fn scan<K: Ord + Clone>( fn scan<K: Ord + Clone>(
&self, &self,
prev_tip: Option<CheckPoint>, prev_tip: Option<CheckPoint>,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, Script)>>, keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
txids: impl IntoIterator<Item = Txid>, txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>, outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize, stop_gap: usize,
@ -215,7 +216,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
.into_iter() .into_iter()
.map(|(k, s)| (k, s.into_iter())) .map(|(k, s)| (k, s.into_iter()))
.collect::<BTreeMap<K, _>>(); .collect::<BTreeMap<K, _>>();
let mut scanned_spks = BTreeMap::<(K, u32), (Script, bool)>::new(); let mut scanned_spks = BTreeMap::<(K, u32), (ScriptBuf, bool)>::new();
let txids = txids.into_iter().collect::<Vec<_>>(); let txids = txids.into_iter().collect::<Vec<_>>();
let outpoints = outpoints.into_iter().collect::<Vec<_>>(); let outpoints = outpoints.into_iter().collect::<Vec<_>>();
@ -375,7 +376,7 @@ fn determine_tx_anchor(
// transactions residing in the genesis block to have height 0, then interpret a height of 0 as // transactions residing in the genesis block to have height 0, then interpret a height of 0 as
// unconfirmed for all other transactions. // unconfirmed for all other transactions.
if txid if txid
== Txid::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b") == Txid::from_str("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")
.expect("must deserialize genesis coinbase txid") .expect("must deserialize genesis coinbase txid")
{ {
let anchor_block = cps.values().next()?.block_id(); let anchor_block = cps.values().next()?.block_id();
@ -506,10 +507,10 @@ fn populate_with_spks<K, I: Ord + Clone>(
client: &Client, client: &Client,
cps: &BTreeMap<u32, CheckPoint>, cps: &BTreeMap<u32, CheckPoint>,
update: &mut ElectrumUpdate<K, ConfirmationHeightAnchor>, update: &mut ElectrumUpdate<K, ConfirmationHeightAnchor>,
spks: &mut impl Iterator<Item = (I, Script)>, spks: &mut impl Iterator<Item = (I, ScriptBuf)>,
stop_gap: usize, stop_gap: usize,
batch_size: usize, batch_size: usize,
) -> Result<BTreeMap<I, (Script, bool)>, Error> { ) -> Result<BTreeMap<I, (ScriptBuf, bool)>, Error> {
let mut unused_spk_count = 0_usize; let mut unused_spk_count = 0_usize;
let mut scanned_spks = BTreeMap::new(); let mut scanned_spks = BTreeMap::new();
@ -521,7 +522,8 @@ fn populate_with_spks<K, I: Ord + Clone>(
return Ok(scanned_spks); return Ok(scanned_spks);
} }
let spk_histories = client.batch_script_get_history(spks.iter().map(|(_, s)| s))?; let spk_histories =
client.batch_script_get_history(spks.iter().map(|(_, s)| s.as_script()))?;
for ((spk_index, spk), spk_history) in spks.into_iter().zip(spk_histories) { for ((spk_index, spk), spk_history) in spks.into_iter().zip(spk_histories) {
if spk_history.is_empty() { if spk_history.is_empty() {

View File

@ -13,13 +13,13 @@ readme = "README.md"
[dependencies] [dependencies]
bdk_chain = { path = "../chain", version = "0.5.0", default-features = false, features = ["serde", "miniscript"] } bdk_chain = { path = "../chain", version = "0.5.0", default-features = false, features = ["serde", "miniscript"] }
esplora-client = { version = "0.5", default-features = false } esplora-client = { version = "0.6.0", default-features = false }
async-trait = { version = "0.1.66", optional = true } async-trait = { version = "0.1.66", optional = true }
futures = { version = "0.3.26", optional = true } futures = { version = "0.3.26", optional = true }
# use these dependencies if you need to enable their /no-std features # use these dependencies if you need to enable their /no-std features
bitcoin = { version = "0.29", optional = true, default-features = false } bitcoin = { version = "0.30.0", optional = true, default-features = false }
miniscript = { version = "9.0.0", optional = true, default-features = false } miniscript = { version = "10.0.0", optional = true, default-features = false }
[features] [features]
default = ["std", "async-https", "blocking"] default = ["std", "async-https", "blocking"]

View File

@ -1,7 +1,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use bdk_chain::collections::btree_map; use bdk_chain::collections::btree_map;
use bdk_chain::{ use bdk_chain::{
bitcoin::{BlockHash, OutPoint, Script, Txid}, bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid},
collections::{BTreeMap, BTreeSet}, collections::{BTreeMap, BTreeSet},
local_chain::{self, CheckPoint}, local_chain::{self, CheckPoint},
BlockId, ConfirmationTimeAnchor, TxGraph, BlockId, ConfirmationTimeAnchor, TxGraph,
@ -52,7 +52,7 @@ pub trait EsploraAsyncExt {
&self, &self,
keychain_spks: BTreeMap< keychain_spks: BTreeMap<
K, K,
impl IntoIterator<IntoIter = impl Iterator<Item = (u32, Script)> + Send> + Send, impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,
>, >,
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send, txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send, outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
@ -66,7 +66,7 @@ pub trait EsploraAsyncExt {
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
async fn update_tx_graph_without_keychain( async fn update_tx_graph_without_keychain(
&self, &self,
misc_spks: impl IntoIterator<IntoIter = impl Iterator<Item = Script> + Send> + Send, misc_spks: impl IntoIterator<IntoIter = impl Iterator<Item = ScriptBuf> + Send> + Send,
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send, txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send, outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,
parallel_requests: usize, parallel_requests: usize,
@ -205,7 +205,7 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
&self, &self,
keychain_spks: BTreeMap< keychain_spks: BTreeMap<
K, K,
impl IntoIterator<IntoIter = impl Iterator<Item = (u32, Script)> + Send> + Send, impl IntoIterator<IntoIter = impl Iterator<Item = (u32, ScriptBuf)> + Send> + Send,
>, >,
txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send, txids: impl IntoIterator<IntoIter = impl Iterator<Item = Txid> + Send> + Send,
outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send, outpoints: impl IntoIterator<IntoIter = impl Iterator<Item = OutPoint> + Send> + Send,

View File

@ -1,10 +1,9 @@
use std::thread::JoinHandle; use std::thread::JoinHandle;
use bdk_chain::bitcoin::{OutPoint, Txid};
use bdk_chain::collections::btree_map; use bdk_chain::collections::btree_map;
use bdk_chain::collections::{BTreeMap, BTreeSet}; use bdk_chain::collections::{BTreeMap, BTreeSet};
use bdk_chain::{ use bdk_chain::{
bitcoin::{BlockHash, Script}, bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid},
local_chain::{self, CheckPoint}, local_chain::{self, CheckPoint},
BlockId, ConfirmationTimeAnchor, TxGraph, BlockId, ConfirmationTimeAnchor, TxGraph,
}; };
@ -49,7 +48,7 @@ pub trait EsploraExt {
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
fn update_tx_graph<K: Ord + Clone>( fn update_tx_graph<K: Ord + Clone>(
&self, &self,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, Script)>>, keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
txids: impl IntoIterator<Item = Txid>, txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>, outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize, stop_gap: usize,
@ -62,7 +61,7 @@ pub trait EsploraExt {
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
fn update_tx_graph_without_keychain( fn update_tx_graph_without_keychain(
&self, &self,
misc_spks: impl IntoIterator<Item = Script>, misc_spks: impl IntoIterator<Item = ScriptBuf>,
txids: impl IntoIterator<Item = Txid>, txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>, outpoints: impl IntoIterator<Item = OutPoint>,
parallel_requests: usize, parallel_requests: usize,
@ -195,7 +194,7 @@ impl EsploraExt for esplora_client::BlockingClient {
fn update_tx_graph<K: Ord + Clone>( fn update_tx_graph<K: Ord + Clone>(
&self, &self,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, Script)>>, keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
txids: impl IntoIterator<Item = Txid>, txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>, outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize, stop_gap: usize,

View File

@ -7,7 +7,7 @@ use std::{cmp::Reverse, collections::HashMap, path::PathBuf, sync::Mutex, time::
use bdk_chain::{ use bdk_chain::{
bitcoin::{ bitcoin::{
psbt::Prevouts, secp256k1::Secp256k1, util::sighash::SighashCache, Address, LockTime, absolute, address, psbt::Prevouts, secp256k1::Secp256k1, sighash::SighashCache, Address,
Network, Sequence, Transaction, TxIn, TxOut, Network, Sequence, Transaction, TxIn, TxOut,
}, },
indexed_tx_graph::{IndexedAdditions, IndexedTxGraph}, indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
@ -70,7 +70,7 @@ pub enum Commands<S: clap::Subcommand> {
/// Send coins to an address. /// Send coins to an address.
Send { Send {
value: u64, value: u64,
address: Address, address: Address<address::NetworkUnchecked>,
#[clap(short, default_value = "bnb")] #[clap(short, default_value = "bnb")]
coin_select: CoinSelectionAlgo, coin_select: CoinSelectionAlgo,
}, },
@ -457,7 +457,7 @@ where
additions.append(change_additions); additions.append(change_additions);
// Clone to drop the immutable reference. // Clone to drop the immutable reference.
let change_script = change_script.clone(); let change_script = change_script.into();
let change_plan = bdk_tmp_plan::plan_satisfaction( let change_plan = bdk_tmp_plan::plan_satisfaction(
&graph &graph
@ -465,7 +465,8 @@ where
.keychains() .keychains()
.get(&internal_keychain) .get(&internal_keychain)
.expect("must exist") .expect("must exist")
.at_derivation_index(change_index), .at_derivation_index(change_index)
.expect("change_index can't be hardened"),
&assets, &assets,
) )
.expect("failed to obtain change plan"); .expect("failed to obtain change plan");
@ -520,9 +521,8 @@ where
// tip as the `lock_time` for anti-fee-sniping purposes // tip as the `lock_time` for anti-fee-sniping purposes
lock_time: chain lock_time: chain
.get_chain_tip()? .get_chain_tip()?
.and_then(|block_id| LockTime::from_height(block_id.height).ok()) .and_then(|block_id| absolute::LockTime::from_height(block_id.height).ok())
.unwrap_or(LockTime::ZERO) .unwrap_or(absolute::LockTime::ZERO),
.into(),
input: selected_txos input: selected_txos
.iter() .iter()
.map(|(_, utxo)| TxIn { .map(|(_, utxo)| TxIn {
@ -625,7 +625,8 @@ pub fn planned_utxos<A: Anchor, O: ChainOracle, K: Clone + bdk_tmp_plan::CanDeri
.keychains() .keychains()
.get(&k) .get(&k)
.expect("keychain must exist") .expect("keychain must exist")
.at_derivation_index(i); .at_derivation_index(i)
.expect("i can't be hardened");
let plan = bdk_tmp_plan::plan_satisfaction(&desc, assets)?; let plan = bdk_tmp_plan::plan_satisfaction(&desc, assets)?;
Some(Ok((plan, full_txo))) Some(Ok((plan, full_txo)))
}, },
@ -668,6 +669,7 @@ where
coin_select, coin_select,
} => { } => {
let chain = &*chain.lock().unwrap(); let chain = &*chain.lock().unwrap();
let address = address.require_network(network)?;
run_send_cmd( run_send_cmd(
graph, graph,
db, db,

View File

@ -5,7 +5,7 @@ use std::{
}; };
use bdk_chain::{ use bdk_chain::{
bitcoin::{Address, Network, OutPoint, Txid}, bitcoin::{Address, Network, OutPoint, ScriptBuf, Txid},
indexed_tx_graph::{IndexedAdditions, IndexedTxGraph}, indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
keychain::LocalChangeSet, keychain::LocalChangeSet,
local_chain::LocalChain, local_chain::LocalChain,
@ -79,6 +79,7 @@ fn main() -> anyhow::Result<()> {
Network::Testnet => "ssl://electrum.blockstream.info:60002", Network::Testnet => "ssl://electrum.blockstream.info:60002",
Network::Regtest => "tcp://localhost:60401", Network::Regtest => "tcp://localhost:60401",
Network::Signet => "tcp://signet-electrumx.wakiyamap.dev:50001", Network::Signet => "tcp://signet-electrumx.wakiyamap.dev:50001",
_ => panic!("Unknown network"),
}; };
let config = electrum_client::Config::builder() let config = electrum_client::Config::builder()
.validate_domain(matches!(args.network, Network::Bitcoin)) .validate_domain(matches!(args.network, Network::Bitcoin))
@ -172,7 +173,7 @@ fn main() -> anyhow::Result<()> {
unused_spks = false; unused_spks = false;
} }
let mut spks: Box<dyn Iterator<Item = bdk_chain::bitcoin::Script>> = let mut spks: Box<dyn Iterator<Item = bdk_chain::bitcoin::ScriptBuf>> =
Box::new(core::iter::empty()); Box::new(core::iter::empty());
if all_spks { if all_spks {
let all_spks = graph let all_spks = graph
@ -190,7 +191,7 @@ fn main() -> anyhow::Result<()> {
let unused_spks = graph let unused_spks = graph
.index .index
.unused_spks(..) .unused_spks(..)
.map(|(k, v)| (*k, v.clone())) .map(|(k, v)| (*k, ScriptBuf::from(v)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
spks = Box::new(spks.chain(unused_spks.into_iter().map(|(index, script)| { spks = Box::new(spks.chain(unused_spks.into_iter().map(|(index, script)| {
eprintln!( eprintln!(

View File

@ -73,7 +73,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
std::process::exit(0); std::process::exit(0);
} }
let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?; let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?
.require_network(Network::Testnet)?;
let mut tx_builder = wallet.build_tx(); let mut tx_builder = wallet.build_tx();
tx_builder tx_builder

View File

@ -79,7 +79,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
std::process::exit(0); std::process::exit(0);
} }
let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?; let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?
.require_network(Network::Testnet)?;
let mut tx_builder = wallet.build_tx(); let mut tx_builder = wallet.build_tx();
tx_builder tx_builder

View File

@ -79,7 +79,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
std::process::exit(0); std::process::exit(0);
} }
let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?; let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?
.require_network(Network::Testnet)?;
let mut tx_builder = wallet.build_tx(); let mut tx_builder = wallet.build_tx();
tx_builder tx_builder

View File

@ -97,11 +97,13 @@ impl CoinSelectorOpt {
let mut tx = Transaction { let mut tx = Transaction {
input: vec![], input: vec![],
version: 1, version: 1,
lock_time: LockTime::ZERO.into(), lock_time: absolute::LockTime::ZERO,
output: txouts.to_vec(), output: txouts.to_vec(),
}; };
let base_weight = tx.weight(); let base_weight = tx.weight();
// this awkward calculation is necessary since TxOut doesn't have \.weight() // Calculating drain_weight like this instead of using .weight()
// allows us to take into account the output len varint increase that
// might happen when adding a new output
let drain_weight = { let drain_weight = {
tx.output.push(drain_output.clone()); tx.output.push(drain_output.clone());
tx.weight() - base_weight tx.weight() - base_weight
@ -113,8 +115,8 @@ impl CoinSelectorOpt {
Some(txouts.iter().map(|txout| txout.value).sum()) Some(txouts.iter().map(|txout| txout.value).sum())
}, },
..Self::from_weights( ..Self::from_weights(
base_weight as u32, base_weight.to_wu() as u32,
drain_weight as u32, drain_weight.to_wu() as u32,
TXIN_BASE_WEIGHT + drain_satisfaction_weight, TXIN_BASE_WEIGHT + drain_satisfaction_weight,
) )
} }

View File

@ -12,7 +12,7 @@ use bdk_chain::{
bitcoin, bitcoin,
collections::{BTreeSet, HashMap}, collections::{BTreeSet, HashMap},
}; };
use bitcoin::{LockTime, Transaction, TxOut}; use bitcoin::{absolute, Transaction, TxOut};
use core::fmt::{Debug, Display}; use core::fmt::{Debug, Display};
mod coin_selector; mod coin_selector;

View File

@ -16,15 +16,15 @@
//! witness/script_sig for the input. //! witness/script_sig for the input.
use bdk_chain::{bitcoin, collections::*, miniscript}; use bdk_chain::{bitcoin, collections::*, miniscript};
use bitcoin::{ use bitcoin::{
blockdata::{locktime::LockTime, transaction::Sequence}, absolute,
address::WitnessVersion,
bip32::{DerivationPath, Fingerprint, KeySource},
blockdata::transaction::Sequence,
ecdsa,
hashes::{hash160, ripemd160, sha256}, hashes::{hash160, ripemd160, sha256},
secp256k1::Secp256k1, secp256k1::Secp256k1,
util::{ taproot::{self, LeafVersion, TapLeafHash},
address::WitnessVersion, ScriptBuf, TxIn, Witness,
bip32::{DerivationPath, Fingerprint, KeySource},
taproot::{LeafVersion, TapBranchHash, TapLeafHash},
},
EcdsaSig, SchnorrSig, Script, TxIn, Witness,
}; };
use miniscript::{ use miniscript::{
descriptor::{InnerXKey, Tr}, descriptor::{InnerXKey, Tr},
@ -46,7 +46,7 @@ use template::TemplateItem;
enum TrSpend { enum TrSpend {
KeySpend, KeySpend,
LeafSpend { LeafSpend {
script: Script, script: ScriptBuf,
leaf_version: LeafVersion, leaf_version: LeafVersion,
}, },
} }
@ -55,7 +55,7 @@ enum TrSpend {
enum Target { enum Target {
Legacy, Legacy,
Segwitv0 { Segwitv0 {
script_code: Script, script_code: ScriptBuf,
}, },
Segwitv1 { Segwitv1 {
tr: Tr<DefiniteDescriptorKey>, tr: Tr<DefiniteDescriptorKey>,
@ -72,7 +72,7 @@ impl Target {}
pub struct Plan<AK> { pub struct Plan<AK> {
template: Vec<TemplateItem<AK>>, template: Vec<TemplateItem<AK>>,
target: Target, target: Target,
set_locktime: Option<LockTime>, set_locktime: Option<absolute::LockTime>,
set_sequence: Option<Sequence>, set_sequence: Option<Sequence>,
} }
@ -86,9 +86,9 @@ impl Default for Target {
/// Signatures and hash pre-images that can be used to complete a plan. /// Signatures and hash pre-images that can be used to complete a plan.
pub struct SatisfactionMaterial { pub struct SatisfactionMaterial {
/// Schnorr signautres under their keys /// Schnorr signautres under their keys
pub schnorr_sigs: BTreeMap<DefiniteDescriptorKey, SchnorrSig>, pub schnorr_sigs: BTreeMap<DefiniteDescriptorKey, taproot::Signature>,
/// ECDSA signatures under their keys /// ECDSA signatures under their keys
pub ecdsa_sigs: BTreeMap<DefiniteDescriptorKey, EcdsaSig>, pub ecdsa_sigs: BTreeMap<DefiniteDescriptorKey, ecdsa::Signature>,
/// SHA256 pre-images under their images /// SHA256 pre-images under their images
pub sha256_preimages: BTreeMap<sha256::Hash, Vec<u8>>, pub sha256_preimages: BTreeMap<sha256::Hash, Vec<u8>>,
/// hash160 pre-images under their images /// hash160 pre-images under their images
@ -201,7 +201,7 @@ where
.. ..
} => PlanState::Complete { } => PlanState::Complete {
final_script_sig: None, final_script_sig: None,
final_script_witness: Some(Witness::from_vec(witness)), final_script_witness: Some(Witness::from(witness)),
}, },
Target::Segwitv1 { Target::Segwitv1 {
tr, tr,
@ -220,7 +220,7 @@ where
PlanState::Complete { PlanState::Complete {
final_script_sig: None, final_script_sig: None,
final_script_witness: Some(Witness::from_vec(witness)), final_script_witness: Some(Witness::from(witness)),
} }
} }
} }
@ -306,7 +306,7 @@ where
} }
/// The minimum required locktime height or time on the transaction using the plan. /// The minimum required locktime height or time on the transaction using the plan.
pub fn required_locktime(&self) -> Option<LockTime> { pub fn required_locktime(&self) -> Option<absolute::LockTime> {
self.set_locktime.clone() self.set_locktime.clone()
} }
@ -330,7 +330,7 @@ pub enum PlanState<Ak> {
/// The plan is complete /// The plan is complete
Complete { Complete {
/// The script sig that should be set on the input /// The script sig that should be set on the input
final_script_sig: Option<Script>, final_script_sig: Option<ScriptBuf>,
/// The witness that should be set on the input /// The witness that should be set on the input
final_script_witness: Option<Witness>, final_script_witness: Option<Witness>,
}, },
@ -341,7 +341,7 @@ pub enum PlanState<Ak> {
pub struct Assets<K> { pub struct Assets<K> {
pub keys: Vec<K>, pub keys: Vec<K>,
pub txo_age: Option<Sequence>, pub txo_age: Option<Sequence>,
pub max_locktime: Option<LockTime>, pub max_locktime: Option<absolute::LockTime>,
pub sha256: Vec<sha256::Hash>, pub sha256: Vec<sha256::Hash>,
pub hash256: Vec<hash256::Hash>, pub hash256: Vec<hash256::Hash>,
pub ripemd160: Vec<ripemd160::Hash>, pub ripemd160: Vec<ripemd160::Hash>,
@ -380,6 +380,11 @@ impl CanDerive for KeySource {
path_to_child(self, &origin, Some(&dxk.derivation_path)) path_to_child(self, &origin, Some(&dxk.derivation_path))
} }
DescriptorPublicKey::MultiXPub(_) => {
// This crate will be replaced by
// https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways
todo!();
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
use bdk_chain::{bitcoin, miniscript}; use bdk_chain::{bitcoin, miniscript};
use bitcoin::locktime::{Height, Time}; use bitcoin::locktime::absolute;
use miniscript::Terminal; use miniscript::Terminal;
use super::*; use super::*;
@ -154,7 +154,7 @@ where
#[derive(Debug)] #[derive(Debug)]
struct TermPlan<Ak> { struct TermPlan<Ak> {
pub min_locktime: Option<LockTime>, pub min_locktime: Option<absolute::LockTime>,
pub min_sequence: Option<Sequence>, pub min_sequence: Option<Sequence>,
pub template: Vec<TemplateItem<Ak>>, pub template: Vec<TemplateItem<Ak>>,
} }
@ -216,10 +216,12 @@ fn plan_steps<Ak: Clone + CanDerive, Ctx: ScriptContext>(
} }
Terminal::After(locktime) => { Terminal::After(locktime) => {
let max_locktime = assets.max_locktime?; let max_locktime = assets.max_locktime?;
let locktime = LockTime::from(locktime); let locktime = absolute::LockTime::from(*locktime);
let (height, time) = match max_locktime { let (height, time) = match max_locktime {
LockTime::Blocks(height) => (height, Time::from_consensus(0).unwrap()), absolute::LockTime::Blocks(height) => {
LockTime::Seconds(seconds) => (Height::from_consensus(0).unwrap(), seconds), (height, absolute::Time::from_consensus(0).unwrap())
}
absolute::LockTime::Seconds(seconds) => (absolute::Height::ZERO, seconds),
}; };
if max_locktime.is_satisfied_by(height, time) { if max_locktime.is_satisfied_by(height, time) {
Some(TermPlan { Some(TermPlan {

View File

@ -2,11 +2,14 @@ use bdk_chain::{bitcoin, collections::*, miniscript};
use core::ops::Deref; use core::ops::Deref;
use bitcoin::{ use bitcoin::{
bip32,
hashes::{hash160, ripemd160, sha256}, hashes::{hash160, ripemd160, sha256},
key::XOnlyPublicKey,
psbt::Prevouts, psbt::Prevouts,
secp256k1::{KeyPair, Message, PublicKey, Signing, Verification}, secp256k1::{KeyPair, Message, PublicKey, Signing, Verification},
util::{bip32, sighash, sighash::SighashCache, taproot}, sighash,
EcdsaSighashType, SchnorrSighashType, Transaction, TxOut, XOnlyPublicKey, sighash::{EcdsaSighashType, SighashCache, TapSighashType},
taproot, Transaction, TxOut,
}; };
use super::*; use super::*;
@ -72,7 +75,7 @@ pub enum RequiredSignatures<Ak> {
/// the internal key /// the internal key
plan_key: PlanKey<Ak>, plan_key: PlanKey<Ak>,
/// The merkle root of the taproot output /// The merkle root of the taproot output
merkle_root: Option<TapBranchHash>, merkle_root: Option<taproot::TapNodeHash>,
}, },
/// Taproot script path signatures are required /// Taproot script path signatures are required
TapScript { TapScript {
@ -114,12 +117,12 @@ impl From<bip32::Error> for SigningError {
impl std::error::Error for SigningError {} impl std::error::Error for SigningError {}
impl RequiredSignatures<DescriptorPublicKey> { impl RequiredSignatures<DescriptorPublicKey> {
pub fn sign_with_keymap<T: Deref<Target = Transaction>>( pub fn sign_with_keymap<T: core::borrow::Borrow<Transaction>>(
&self, &self,
input_index: usize, input_index: usize,
keymap: &KeyMap, keymap: &KeyMap,
prevouts: &Prevouts<'_, impl core::borrow::Borrow<TxOut>>, prevouts: &Prevouts<'_, impl core::borrow::Borrow<TxOut>>,
schnorr_sighashty: Option<SchnorrSighashType>, schnorr_sighashty: Option<TapSighashType>,
_ecdsa_sighashty: Option<EcdsaSighashType>, _ecdsa_sighashty: Option<EcdsaSighashType>,
sighash_cache: &mut SighashCache<T>, sighash_cache: &mut SighashCache<T>,
auth_data: &mut SatisfactionMaterial, auth_data: &mut SatisfactionMaterial,
@ -131,7 +134,7 @@ impl RequiredSignatures<DescriptorPublicKey> {
plan_key, plan_key,
merkle_root, merkle_root,
} => { } => {
let schnorr_sighashty = schnorr_sighashty.unwrap_or(SchnorrSighashType::Default); let schnorr_sighashty = schnorr_sighashty.unwrap_or(TapSighashType::Default);
let sighash = sighash_cache.taproot_key_spend_signature_hash( let sighash = sighash_cache.taproot_key_spend_signature_hash(
input_index, input_index,
prevouts, prevouts,
@ -148,6 +151,11 @@ impl RequiredSignatures<DescriptorPublicKey> {
.derive_priv(&secp, &plan_key.derivation_hint)? .derive_priv(&secp, &plan_key.derivation_hint)?
.private_key .private_key
} }
DescriptorSecretKey::MultiXPrv(_) => {
// This crate will be replaced by
// https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways
todo!();
}
}; };
let pubkey = PublicKey::from_secret_key(&secp, &secret_key); let pubkey = PublicKey::from_secret_key(&secp, &secret_key);
@ -162,7 +170,7 @@ impl RequiredSignatures<DescriptorPublicKey> {
let msg = Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes"); let msg = Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes");
let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair);
let bitcoin_sig = SchnorrSig { let bitcoin_sig = taproot::Signature {
sig, sig,
hash_ty: schnorr_sighashty, hash_ty: schnorr_sighashty,
}; };
@ -176,7 +184,7 @@ impl RequiredSignatures<DescriptorPublicKey> {
leaf_hash, leaf_hash,
plan_keys, plan_keys,
} => { } => {
let sighash_type = schnorr_sighashty.unwrap_or(SchnorrSighashType::Default); let sighash_type = schnorr_sighashty.unwrap_or(TapSighashType::Default);
let sighash = sighash_cache.taproot_script_spend_signature_hash( let sighash = sighash_cache.taproot_script_spend_signature_hash(
input_index, input_index,
prevouts, prevouts,
@ -195,12 +203,17 @@ impl RequiredSignatures<DescriptorPublicKey> {
.derive_priv(&secp, &plan_key.derivation_hint)? .derive_priv(&secp, &plan_key.derivation_hint)?
.private_key .private_key
} }
DescriptorSecretKey::MultiXPrv(_) => {
// This crate will be replaced by
// https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways
todo!();
}
}; };
let keypair = KeyPair::from_secret_key(&secp, &secret_key.clone()); let keypair = KeyPair::from_secret_key(&secp, &secret_key.clone());
let msg = let msg =
Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes"); Message::from_slice(sighash.as_ref()).expect("Sighashes are 32 bytes");
let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair);
let bitcoin_sig = SchnorrSig { let bitcoin_sig = taproot::Signature {
sig, sig,
hash_ty: sighash_type, hash_ty: sighash_type,
}; };

View File

@ -1,7 +1,7 @@
use bdk_chain::{bitcoin, miniscript}; use bdk_chain::{bitcoin, miniscript};
use bitcoin::{ use bitcoin::{
bip32::DerivationPath,
hashes::{hash160, ripemd160, sha256}, hashes::{hash160, ripemd160, sha256},
util::bip32::DerivationPath,
}; };
use super::*; use super::*;