Update bdk to bitcoin 0.30.0
This commit is contained in:
parent
853d361751
commit
f4e3ba3265
@ -15,14 +15,14 @@ rust-version = "1.57"
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
rand = "^0.8"
|
||||
miniscript = { version = "9", features = ["serde"], default-features = false }
|
||||
bitcoin = { version = "0.29", features = ["serde", "base64", "rand"], default-features = false }
|
||||
miniscript = { version = "10.0.0", features = ["serde"], default-features = false }
|
||||
bitcoin = { version = "0.30.0", features = ["serde", "base64", "rand-std"], default-features = false }
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_json = { version = "^1.0" }
|
||||
bdk_chain = { path = "../chain", version = "0.5.0", features = ["miniscript", "serde"], default-features = false }
|
||||
|
||||
# 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 }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
@ -46,8 +46,6 @@ dev-getrandom-wasm = ["getrandom/js"]
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4"
|
||||
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"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
@ -137,7 +137,7 @@ fn main() {
|
||||
<!-- use bdk::electrum_client::Client; -->
|
||||
<!-- use bdk::wallet::AddressIndex::New; -->
|
||||
|
||||
<!-- use base64; -->
|
||||
<!-- use bitcoin::base64; -->
|
||||
<!-- use bdk::bitcoin::consensus::serialize; -->
|
||||
<!-- use bdk::bitcoin::Network; -->
|
||||
|
||||
@ -174,7 +174,7 @@ fn main() {
|
||||
<!-- ```rust,no_run -->
|
||||
<!-- use bdk::{Wallet, SignOptions}; -->
|
||||
|
||||
<!-- use base64; -->
|
||||
<!-- use bitcoin::base64; -->
|
||||
<!-- use bdk::bitcoin::consensus::deserialize; -->
|
||||
<!-- use bdk::bitcoin::Network; -->
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
// You may not use this file except in accordance with one or both of these
|
||||
// licenses.
|
||||
|
||||
use bdk::bitcoin::bip32::DerivationPath;
|
||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||
use bdk::bitcoin::util::bip32::DerivationPath;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor;
|
||||
use bdk::descriptor::IntoWalletDescriptor;
|
||||
|
@ -516,13 +516,14 @@ macro_rules! descriptor {
|
||||
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
|
||||
|
||||
$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))
|
||||
});
|
||||
( wpkh ( $key:expr ) ) => ({
|
||||
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
|
||||
|
||||
$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))
|
||||
});
|
||||
( sh ( wpkh ( $key:expr ) ) ) => ({
|
||||
@ -532,7 +533,7 @@ macro_rules! descriptor {
|
||||
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Sh};
|
||||
|
||||
$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)))
|
||||
});
|
||||
( sh ( $( $minisc:tt )* ) ) => ({
|
||||
@ -702,7 +703,7 @@ macro_rules! fragment {
|
||||
$crate::keys::make_pkh($key, &secp)
|
||||
});
|
||||
( 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 ) ) => ({
|
||||
$crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!!
|
||||
@ -796,7 +797,6 @@ macro_rules! fragment {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use alloc::string::ToString;
|
||||
use bitcoin::hashes::hex::ToHex;
|
||||
use bitcoin::secp256k1::Secp256k1;
|
||||
use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
|
||||
use miniscript::{Descriptor, Legacy, Segwitv0};
|
||||
@ -805,8 +805,8 @@ mod test {
|
||||
|
||||
use crate::descriptor::{DescriptorError, DescriptorMeta};
|
||||
use crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::network::constants::Network::{Bitcoin, Regtest, Signet, Testnet};
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::PrivateKey;
|
||||
|
||||
// test the descriptor!() macro
|
||||
@ -822,18 +822,15 @@ mod test {
|
||||
assert_eq!(desc.is_witness(), is_witness);
|
||||
assert_eq!(!desc.has_wildcard(), is_fixed);
|
||||
for i in 0..expected.len() {
|
||||
let index = i as u32;
|
||||
let child_desc = if !desc.has_wildcard() {
|
||||
desc.at_derivation_index(0)
|
||||
} else {
|
||||
desc.at_derivation_index(index)
|
||||
};
|
||||
let child_desc = desc
|
||||
.at_derivation_index(i as u32)
|
||||
.expect("i is not hardened");
|
||||
let address = child_desc.address(Regtest);
|
||||
if let Ok(address) = address {
|
||||
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
||||
} else {
|
||||
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]
|
||||
#[should_panic(
|
||||
expected = "Miniscript(ContextError(CompressedOnly(\"04b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a87378ec38ff91d43e8c2092ebda601780485263da089465619e0358a5c1be7ac91f4\")))"
|
||||
)]
|
||||
#[should_panic(expected = "Miniscript(ContextError(UncompressedKeysNotAllowed))")]
|
||||
fn test_dsl_miniscript_checks() {
|
||||
let mut uncompressed_pk =
|
||||
PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap();
|
||||
|
@ -32,11 +32,11 @@ pub enum Error {
|
||||
InvalidDescriptorCharacter(u8),
|
||||
|
||||
/// BIP32 error
|
||||
Bip32(bitcoin::util::bip32::Error),
|
||||
Bip32(bitcoin::bip32::Error),
|
||||
/// Error during base58 decoding
|
||||
Base58(bitcoin::util::base58::Error),
|
||||
Base58(bitcoin::base58::Error),
|
||||
/// Key-related error
|
||||
Pk(bitcoin::util::key::Error),
|
||||
Pk(bitcoin::key::Error),
|
||||
/// Miniscript error
|
||||
Miniscript(miniscript::Error),
|
||||
/// Hex decoding error
|
||||
@ -81,9 +81,9 @@ impl fmt::Display for Error {
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl_error!(bitcoin::util::bip32::Error, Bip32);
|
||||
impl_error!(bitcoin::util::base58::Error, Base58);
|
||||
impl_error!(bitcoin::util::key::Error, Pk);
|
||||
impl_error!(bitcoin::bip32::Error, Bip32);
|
||||
impl_error!(bitcoin::base58::Error, Base58);
|
||||
impl_error!(bitcoin::key::Error, Pk);
|
||||
impl_error!(miniscript::Error, Miniscript);
|
||||
impl_error!(bitcoin::hashes::hex::Error, Hex);
|
||||
impl_error!(crate::descriptor::policy::PolicyError, Policy);
|
||||
|
@ -18,17 +18,17 @@ use crate::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
|
||||
use bitcoin::util::{psbt, taproot};
|
||||
use bitcoin::{secp256k1, PublicKey, XOnlyPublicKey};
|
||||
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
|
||||
use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
|
||||
use bitcoin::{psbt, taproot};
|
||||
use bitcoin::{Network, TxOut};
|
||||
|
||||
use miniscript::descriptor::{
|
||||
DefiniteDescriptorKey, DescriptorSecretKey, DescriptorType, InnerXKey, SinglePubKey,
|
||||
DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
|
||||
DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
|
||||
};
|
||||
pub use miniscript::{
|
||||
descriptor::DescriptorXKey, descriptor::KeyMap, descriptor::Wildcard, Descriptor,
|
||||
DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
|
||||
Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
|
||||
};
|
||||
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
|
||||
/// [`psbt::Output`]
|
||||
///
|
||||
/// [`psbt::Input`]: bitcoin::util::psbt::Input
|
||||
/// [`psbt::Output`]: bitcoin::util::psbt::Output
|
||||
/// [`psbt::Input`]: bitcoin::psbt::Input
|
||||
/// [`psbt::Output`]: bitcoin::psbt::Output
|
||||
pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
|
||||
|
||||
/// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or
|
||||
/// [`psbt::Output`]
|
||||
///
|
||||
/// [`psbt::Input`]: bitcoin::util::psbt::Input
|
||||
/// [`psbt::Output`]: bitcoin::util::psbt::Output
|
||||
pub type TapKeyOrigins = BTreeMap<bitcoin::XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
|
||||
/// [`psbt::Input`]: bitcoin::psbt::Input
|
||||
/// [`psbt::Output`]: bitcoin::psbt::Output
|
||||
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`]
|
||||
pub trait IntoWalletDescriptor {
|
||||
@ -136,14 +136,10 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
|
||||
network: Network,
|
||||
}
|
||||
|
||||
impl<'s, 'd>
|
||||
miniscript::Translator<DescriptorPublicKey, miniscript::DummyKey, DescriptorError>
|
||||
impl<'s, 'd> miniscript::Translator<DescriptorPublicKey, String, DescriptorError>
|
||||
for Translator<'s, 'd>
|
||||
{
|
||||
fn pk(
|
||||
&mut self,
|
||||
pk: &DescriptorPublicKey,
|
||||
) -> Result<miniscript::DummyKey, DescriptorError> {
|
||||
fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
|
||||
let secp = &self.secp;
|
||||
|
||||
let (_, _, networks) = if self.descriptor.is_taproot() {
|
||||
@ -161,7 +157,7 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
|
||||
};
|
||||
|
||||
if networks.contains(&self.network) {
|
||||
Ok(miniscript::DummyKey)
|
||||
Ok(Default::default())
|
||||
} else {
|
||||
Err(DescriptorError::Key(KeyError::InvalidNetwork))
|
||||
}
|
||||
@ -169,35 +165,40 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
|
||||
fn sha256(
|
||||
&mut self,
|
||||
_sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
|
||||
) -> Result<miniscript::DummySha256Hash, DescriptorError> {
|
||||
) -> Result<String, DescriptorError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
fn hash256(
|
||||
&mut self,
|
||||
_hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
|
||||
) -> Result<miniscript::DummyHash256Hash, DescriptorError> {
|
||||
) -> Result<String, DescriptorError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
fn ripemd160(
|
||||
&mut self,
|
||||
_ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
|
||||
) -> Result<miniscript::DummyRipemd160Hash, DescriptorError> {
|
||||
) -> Result<String, DescriptorError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
fn hash160(
|
||||
&mut self,
|
||||
_hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
|
||||
) -> Result<miniscript::DummyHash160Hash, DescriptorError> {
|
||||
) -> Result<String, DescriptorError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
// check the network for the keys
|
||||
self.0.translate_pk(&mut Translator {
|
||||
use miniscript::TranslateErr;
|
||||
match self.0.translate_pk(&mut Translator {
|
||||
secp,
|
||||
network,
|
||||
descriptor: &self.0,
|
||||
})?;
|
||||
}) {
|
||||
Ok(_) => {}
|
||||
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
|
||||
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
@ -251,7 +252,12 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
|
||||
}
|
||||
|
||||
// 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
|
||||
let fixed_keymap = keymap
|
||||
.into_iter()
|
||||
@ -340,6 +346,18 @@ pub(crate) trait XKeyUtils {
|
||||
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>
|
||||
where
|
||||
T: InnerXKey,
|
||||
@ -494,7 +512,10 @@ impl DescriptorMeta for ExtendedDescriptor {
|
||||
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(
|
||||
@ -545,7 +566,7 @@ impl DescriptorMeta for ExtendedDescriptor {
|
||||
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() {
|
||||
// TODO: add pk() here
|
||||
DescriptorType::Pkh
|
||||
@ -585,11 +606,10 @@ mod test {
|
||||
use core::str::FromStr;
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use bitcoin::consensus::encode::deserialize;
|
||||
use bitcoin::hashes::hex::FromHex;
|
||||
use bitcoin::secp256k1::Secp256k1;
|
||||
use bitcoin::util::{bip32, psbt};
|
||||
use bitcoin::Script;
|
||||
use bitcoin::ScriptBuf;
|
||||
use bitcoin::{bip32, psbt::Psbt};
|
||||
|
||||
use super::*;
|
||||
use crate::psbt::PsbtUtils;
|
||||
@ -600,7 +620,7 @@ mod test {
|
||||
"wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
|
||||
)
|
||||
.unwrap();
|
||||
let psbt: psbt::PartiallySignedTransaction = deserialize(
|
||||
let psbt = Psbt::deserialize(
|
||||
&Vec::<u8>::from_hex(
|
||||
"70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
|
||||
11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
|
||||
@ -623,7 +643,7 @@ mod test {
|
||||
"pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
|
||||
)
|
||||
.unwrap();
|
||||
let psbt: psbt::PartiallySignedTransaction = deserialize(
|
||||
let psbt = Psbt::deserialize(
|
||||
&Vec::<u8>::from_hex(
|
||||
"70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
|
||||
e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
|
||||
@ -654,7 +674,7 @@ mod test {
|
||||
"wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
|
||||
)
|
||||
.unwrap();
|
||||
let psbt: psbt::PartiallySignedTransaction = deserialize(
|
||||
let psbt = Psbt::deserialize(
|
||||
&Vec::<u8>::from_hex(
|
||||
"70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
|
||||
67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
|
||||
@ -678,7 +698,7 @@ mod test {
|
||||
"sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
|
||||
)
|
||||
.unwrap();
|
||||
let psbt: psbt::PartiallySignedTransaction = deserialize(
|
||||
let psbt = Psbt::deserialize(
|
||||
&Vec::<u8>::from_hex(
|
||||
"70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
|
||||
a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
|
||||
@ -861,9 +881,9 @@ mod test {
|
||||
let (descriptor, _) =
|
||||
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();
|
||||
psbt_input
|
||||
|
@ -45,9 +45,9 @@ use core::fmt;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use bitcoin::bip32::Fingerprint;
|
||||
use bitcoin::hashes::{hash160, ripemd160, sha256};
|
||||
use bitcoin::util::bip32::Fingerprint;
|
||||
use bitcoin::{LockTime, PublicKey, Sequence, XOnlyPublicKey};
|
||||
use bitcoin::{absolute, key::XOnlyPublicKey, PublicKey, Sequence};
|
||||
|
||||
use miniscript::descriptor::{
|
||||
DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
|
||||
@ -68,7 +68,7 @@ use crate::wallet::utils::{After, Older, SecpCtx};
|
||||
use super::checksum::calc_checksum;
|
||||
use super::error::Error;
|
||||
use super::XKeyUtils;
|
||||
use bitcoin::util::psbt::{Input as PsbtInput, PartiallySignedTransaction as Psbt};
|
||||
use bitcoin::psbt::{self, Psbt};
|
||||
use miniscript::psbt::PsbtInputSatisfier;
|
||||
|
||||
/// A unique identifier for a key
|
||||
@ -95,6 +95,9 @@ impl PkOrF {
|
||||
..
|
||||
}) => PkOrF::XOnlyPubkey(*pk),
|
||||
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
|
||||
AbsoluteTimelock {
|
||||
/// The timelock value
|
||||
value: LockTime,
|
||||
value: absolute::LockTime,
|
||||
},
|
||||
/// Relative timelock locktime
|
||||
RelativeTimelock {
|
||||
@ -451,11 +454,14 @@ pub struct Condition {
|
||||
pub csv: Option<Sequence>,
|
||||
/// Optional timelock condition
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub timelock: Option<LockTime>,
|
||||
pub timelock: Option<absolute::LockTime>,
|
||||
}
|
||||
|
||||
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) {
|
||||
Err(PolicyError::MixedTimelockUnits)
|
||||
} else if a > b {
|
||||
@ -749,6 +755,7 @@ fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
|
||||
..
|
||||
}) => pk.to_pubkeyhash(SigType::Ecdsa).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<
|
||||
// C is for "check", it's a closure we use to *check* if a psbt input contains the signature
|
||||
// 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: Fn(&PsbtInput, Fingerprint) -> Option<SinglePubKey>,
|
||||
E: Fn(&psbt::Input, Fingerprint) -> Option<SinglePubKey>,
|
||||
>(
|
||||
psbt: &Psbt,
|
||||
key: &DescriptorPublicKey,
|
||||
@ -806,6 +813,13 @@ fn generic_sig_in_psbt<
|
||||
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) => {
|
||||
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock {
|
||||
value: value.into(),
|
||||
value: (*value).into(),
|
||||
}
|
||||
.into();
|
||||
policy.contribution = Satisfaction::Complete {
|
||||
condition: Condition {
|
||||
timelock: Some(value.into()),
|
||||
timelock: Some((*value).into()),
|
||||
csv: None,
|
||||
},
|
||||
};
|
||||
@ -928,9 +942,9 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
|
||||
{
|
||||
let after = After::new(Some(current_height), false);
|
||||
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| {
|
||||
Satisfier::<bitcoin::PublicKey>::check_after(&sat, value.into())
|
||||
Satisfier::<bitcoin::PublicKey>::check_after(&sat, (*value).into())
|
||||
});
|
||||
if after_sat && inputs_sat {
|
||||
policy.satisfaction = policy.contribution.clone();
|
||||
@ -1156,8 +1170,8 @@ mod test {
|
||||
use crate::wallet::signer::SignersContainer;
|
||||
use alloc::{string::ToString, sync::Arc};
|
||||
use assert_matches::assert_matches;
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::secp256k1::Secp256k1;
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::Network;
|
||||
use core::str::FromStr;
|
||||
|
||||
@ -1575,6 +1589,7 @@ mod test {
|
||||
|
||||
let addr = wallet_desc
|
||||
.at_derivation_index(0)
|
||||
.unwrap()
|
||||
.address(Network::Testnet)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -1641,6 +1656,7 @@ mod test {
|
||||
|
||||
let addr = wallet_desc
|
||||
.at_derivation_index(0)
|
||||
.unwrap()
|
||||
.address(Network::Testnet)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
|
@ -14,7 +14,7 @@
|
||||
//! 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.
|
||||
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::Network;
|
||||
|
||||
use miniscript::{Legacy, Segwitv0, Tap};
|
||||
@ -195,7 +195,7 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// 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(
|
||||
/// Bip44(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip44(key, KeychainKind::Internal)),
|
||||
@ -232,8 +232,8 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::Bip44Public;
|
||||
///
|
||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
|
||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// 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::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(
|
||||
/// Bip49(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip49(key, KeychainKind::Internal)),
|
||||
@ -307,8 +307,8 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::Bip49Public;
|
||||
///
|
||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
|
||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// 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::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(
|
||||
/// Bip84(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip84(key, KeychainKind::Internal)),
|
||||
@ -382,8 +382,8 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::Bip84Public;
|
||||
///
|
||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// 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::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(
|
||||
/// Bip86(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip86(key, KeychainKind::Internal)),
|
||||
@ -457,8 +457,8 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::Bip86Public;
|
||||
///
|
||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new_no_persist(
|
||||
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
|
||||
@ -565,30 +565,30 @@ mod test {
|
||||
// BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
|
||||
#[test]
|
||||
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);
|
||||
let xdesc = Bip44(xprvkey, KeychainKind::Internal)
|
||||
.build(Network::Bitcoin)
|
||||
.unwrap();
|
||||
|
||||
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();
|
||||
assert_matches!(purpose, Hardened { index: 44 });
|
||||
let coin_type = path.get(1).unwrap();
|
||||
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);
|
||||
let tdesc = Bip44(tprvkey, KeychainKind::Internal)
|
||||
.build(Network::Testnet)
|
||||
.unwrap();
|
||||
|
||||
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();
|
||||
assert_matches!(purpose, Hardened { index: 44 });
|
||||
let coin_type = path.get(1).unwrap();
|
||||
@ -612,9 +612,9 @@ mod test {
|
||||
for i in 0..expected.len() {
|
||||
let index = i as u32;
|
||||
let child_desc = if !desc.has_wildcard() {
|
||||
desc.at_derivation_index(0)
|
||||
desc.at_derivation_index(0).unwrap()
|
||||
} else {
|
||||
desc.at_derivation_index(index)
|
||||
desc.at_derivation_index(index).unwrap()
|
||||
};
|
||||
let address = child_desc.address(network).unwrap();
|
||||
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
||||
@ -740,7 +740,7 @@ mod test {
|
||||
// BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
|
||||
#[test]
|
||||
fn test_bip44_template() {
|
||||
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
check(
|
||||
Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
@ -770,8 +770,8 @@ mod test {
|
||||
// BIP44 public `pkh(key/{0,1}/*)`
|
||||
#[test]
|
||||
fn test_bip44_public_template() {
|
||||
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap();
|
||||
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap();
|
||||
let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap();
|
||||
let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
|
||||
check(
|
||||
Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
@ -801,7 +801,7 @@ mod test {
|
||||
// BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))`
|
||||
#[test]
|
||||
fn test_bip49_template() {
|
||||
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
check(
|
||||
Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
@ -831,8 +831,8 @@ mod test {
|
||||
// BIP49 public `sh(wpkh(key/{0,1}/*))`
|
||||
#[test]
|
||||
fn test_bip49_public_template() {
|
||||
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap();
|
||||
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap();
|
||||
let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap();
|
||||
let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
|
||||
check(
|
||||
Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
@ -862,7 +862,7 @@ mod test {
|
||||
// BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)`
|
||||
#[test]
|
||||
fn test_bip84_template() {
|
||||
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
check(
|
||||
Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
@ -892,8 +892,8 @@ mod test {
|
||||
// BIP84 public `wpkh(key/{0,1}/*)`
|
||||
#[test]
|
||||
fn test_bip84_public_template() {
|
||||
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap();
|
||||
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap();
|
||||
let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap();
|
||||
let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
|
||||
check(
|
||||
Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
@ -924,7 +924,7 @@ mod test {
|
||||
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
||||
#[test]
|
||||
fn test_bip86_template() {
|
||||
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
|
||||
let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
|
||||
check(
|
||||
Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
@ -955,8 +955,8 @@ mod test {
|
||||
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
||||
#[test]
|
||||
fn test_bip86_public_template() {
|
||||
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
|
||||
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("73c5da0a").unwrap();
|
||||
let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
|
||||
let fingerprint = bitcoin::bip32::Fingerprint::from_str("73c5da0a").unwrap();
|
||||
check(
|
||||
Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
|
@ -84,9 +84,9 @@ pub enum Error {
|
||||
/// Miniscript PSBT error
|
||||
MiniscriptPsbt(MiniscriptPsbtError),
|
||||
/// BIP32 error
|
||||
Bip32(bitcoin::util::bip32::Error),
|
||||
Bip32(bitcoin::bip32::Error),
|
||||
/// Partially signed bitcoin transaction error
|
||||
Psbt(bitcoin::util::psbt::Error),
|
||||
Psbt(bitcoin::psbt::Error),
|
||||
}
|
||||
|
||||
/// 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!(MiniscriptPsbtError, MiniscriptPsbt);
|
||||
impl_error!(bitcoin::util::bip32::Error, Bip32);
|
||||
impl_error!(bitcoin::util::psbt::Error, Psbt);
|
||||
impl_error!(bitcoin::bip32::Error, Bip32);
|
||||
impl_error!(bitcoin::psbt::Error, Psbt);
|
||||
|
@ -15,7 +15,7 @@
|
||||
// something that should be fairly simple to re-implement.
|
||||
|
||||
use alloc::string::String;
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::Network;
|
||||
|
||||
use miniscript::ScriptContext;
|
||||
@ -142,7 +142,7 @@ impl<Ctx: ScriptContext> GeneratableKey<Ctx> for Mnemonic {
|
||||
(word_count, language): Self::Options,
|
||||
entropy: Self::Entropy,
|
||||
) -> 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)?;
|
||||
|
||||
Ok(GeneratedKey::new(mnemonic, any_network()))
|
||||
@ -154,7 +154,7 @@ mod test {
|
||||
use alloc::string::ToString;
|
||||
use core::str::FromStr;
|
||||
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::bip32;
|
||||
|
||||
use bip39::{Language, Mnemonic};
|
||||
|
||||
|
@ -22,8 +22,8 @@ use core::str::FromStr;
|
||||
|
||||
use bitcoin::secp256k1::{self, Secp256k1, Signing};
|
||||
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::{Network, PrivateKey, PublicKey, XOnlyPublicKey};
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::{key::XOnlyPublicKey, Network, PrivateKey, PublicKey};
|
||||
|
||||
use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard};
|
||||
pub use miniscript::descriptor::{
|
||||
@ -388,12 +388,12 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
|
||||
///
|
||||
/// ```
|
||||
/// use bdk::bitcoin;
|
||||
/// use bdk::bitcoin::util::bip32;
|
||||
/// use bdk::bitcoin::bip32;
|
||||
/// use bdk::keys::{DerivableKey, ExtendedKey, KeyError, ScriptContext};
|
||||
///
|
||||
/// struct MyCustomKeyType {
|
||||
/// key_data: bitcoin::PrivateKey,
|
||||
/// chain_code: Vec<u8>,
|
||||
/// chain_code: [u8; 32],
|
||||
/// network: bitcoin::Network,
|
||||
/// }
|
||||
///
|
||||
@ -404,7 +404,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
|
||||
/// depth: 0,
|
||||
/// parent_fingerprint: bip32::Fingerprint::default(),
|
||||
/// 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 },
|
||||
/// };
|
||||
///
|
||||
@ -419,14 +419,14 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
|
||||
///
|
||||
/// ```
|
||||
/// use bdk::bitcoin;
|
||||
/// use bdk::bitcoin::util::bip32;
|
||||
/// use bdk::bitcoin::bip32;
|
||||
/// use bdk::keys::{
|
||||
/// any_network, DerivableKey, DescriptorKey, ExtendedKey, KeyError, ScriptContext,
|
||||
/// };
|
||||
///
|
||||
/// struct MyCustomKeyType {
|
||||
/// key_data: bitcoin::PrivateKey,
|
||||
/// chain_code: Vec<u8>,
|
||||
/// chain_code: [u8; 32],
|
||||
/// }
|
||||
///
|
||||
/// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType {
|
||||
@ -436,7 +436,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
|
||||
/// depth: 0,
|
||||
/// parent_fingerprint: bip32::Fingerprint::default(),
|
||||
/// 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 },
|
||||
/// };
|
||||
///
|
||||
@ -927,13 +927,13 @@ pub enum KeyError {
|
||||
Message(String),
|
||||
|
||||
/// BIP32 error
|
||||
Bip32(bitcoin::util::bip32::Error),
|
||||
Bip32(bitcoin::bip32::Error),
|
||||
/// Miniscript error
|
||||
Miniscript(miniscript::Error),
|
||||
}
|
||||
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
@ -953,7 +953,7 @@ impl std::error::Error for KeyError {}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::bip32;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
use crate::FeeRate;
|
||||
use alloc::vec::Vec;
|
||||
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
|
||||
use bitcoin::psbt::PartiallySignedTransaction as Psbt;
|
||||
use bitcoin::TxOut;
|
||||
|
||||
// TODO upstream the functions here to `rust-bitcoin`?
|
||||
|
@ -15,7 +15,7 @@ use core::ops::Sub;
|
||||
|
||||
use bdk_chain::ConfirmationTime;
|
||||
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};
|
||||
|
||||
@ -99,8 +99,8 @@ impl FeeRate {
|
||||
}
|
||||
|
||||
/// Calculate fee rate from `fee` and weight units (`wu`).
|
||||
pub fn from_wu(fee: u64, wu: usize) -> FeeRate {
|
||||
Self::from_vb(fee, wu.vbytes())
|
||||
pub fn from_wu(fee: u64, wu: Weight) -> FeeRate {
|
||||
Self::from_vb(fee, wu.to_vbytes_ceil() as usize)
|
||||
}
|
||||
|
||||
/// Calculate fee rate from `fee` and `vbytes`.
|
||||
@ -120,8 +120,8 @@ impl FeeRate {
|
||||
}
|
||||
|
||||
/// Calculate absolute fee in Satoshis using size in weight units.
|
||||
pub fn fee_wu(&self, wu: usize) -> u64 {
|
||||
self.fee_vb(wu.vbytes())
|
||||
pub fn fee_wu(&self, wu: Weight) -> u64 {
|
||||
self.fee_vb(wu.to_vbytes_ceil() as usize)
|
||||
}
|
||||
|
||||
/// Calculate absolute fee in Satoshis using size in virtual bytes.
|
||||
|
@ -38,12 +38,12 @@
|
||||
//! &self,
|
||||
//! required_utxos: Vec<WeightedUtxo>,
|
||||
//! optional_utxos: Vec<WeightedUtxo>,
|
||||
//! fee_rate: FeeRate,
|
||||
//! fee_rate: bdk::FeeRate,
|
||||
//! target_amount: u64,
|
||||
//! drain_script: &Script,
|
||||
//! ) -> Result<CoinSelectionResult, bdk::Error> {
|
||||
//! let mut selected_amount = 0;
|
||||
//! let mut additional_weight = 0;
|
||||
//! let mut additional_weight = Weight::ZERO;
|
||||
//! let all_utxos_selected = required_utxos
|
||||
//! .into_iter()
|
||||
//! .chain(optional_utxos)
|
||||
@ -51,7 +51,9 @@
|
||||
//! (&mut selected_amount, &mut additional_weight),
|
||||
//! |(selected_amount, additional_weight), weighted_utxo| {
|
||||
//! **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)
|
||||
//! },
|
||||
//! )
|
||||
@ -80,7 +82,10 @@
|
||||
//! # let mut wallet = doctest_wallet!();
|
||||
//! // 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 mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything);
|
||||
//! builder.add_recipient(to_address.script_pubkey(), 50_000);
|
||||
@ -99,7 +104,7 @@ use crate::{error::Error, Utxo};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use bitcoin::consensus::encode::serialize;
|
||||
use bitcoin::Script;
|
||||
use bitcoin::{Script, Weight};
|
||||
|
||||
use core::convert::TryInto;
|
||||
use rand::seq::SliceRandom;
|
||||
@ -302,8 +307,9 @@ fn select_sorted_utxos(
|
||||
(&mut selected_amount, &mut fee_amount),
|
||||
|(selected_amount, fee_amount), (must_use, weighted_utxo)| {
|
||||
if must_use || **selected_amount < target_amount + **fee_amount {
|
||||
**fee_amount +=
|
||||
fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight);
|
||||
**fee_amount += fee_rate.fee_wu(Weight::from_wu(
|
||||
(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
|
||||
));
|
||||
**selected_amount += weighted_utxo.utxo.txout().value;
|
||||
|
||||
log::debug!(
|
||||
@ -351,7 +357,9 @@ struct OutputGroup {
|
||||
|
||||
impl OutputGroup {
|
||||
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;
|
||||
OutputGroup {
|
||||
weighted_utxo,
|
||||
@ -681,7 +689,7 @@ mod test {
|
||||
use core::str::FromStr;
|
||||
|
||||
use bdk_chain::ConfirmationTime;
|
||||
use bitcoin::{OutPoint, Script, TxOut};
|
||||
use bitcoin::{OutPoint, ScriptBuf, TxOut};
|
||||
|
||||
use super::*;
|
||||
use crate::types::*;
|
||||
@ -710,7 +718,7 @@ mod test {
|
||||
outpoint,
|
||||
txout: TxOut {
|
||||
value,
|
||||
script_pubkey: Script::new(),
|
||||
script_pubkey: ScriptBuf::new(),
|
||||
},
|
||||
keychain: KeychainKind::External,
|
||||
is_spent: false,
|
||||
@ -773,7 +781,7 @@ mod test {
|
||||
.unwrap(),
|
||||
txout: TxOut {
|
||||
value: rng.gen_range(0..200000000),
|
||||
script_pubkey: Script::new(),
|
||||
script_pubkey: ScriptBuf::new(),
|
||||
},
|
||||
keychain: KeychainKind::External,
|
||||
is_spent: false,
|
||||
@ -802,7 +810,7 @@ mod test {
|
||||
.unwrap(),
|
||||
txout: TxOut {
|
||||
value: utxos_value,
|
||||
script_pubkey: Script::new(),
|
||||
script_pubkey: ScriptBuf::new(),
|
||||
},
|
||||
keychain: KeychainKind::External,
|
||||
is_spent: false,
|
||||
@ -825,7 +833,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_largest_first_coin_selection_success() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 250_000 + FEE_AMOUNT;
|
||||
|
||||
let result = LargestFirstCoinSelection::default()
|
||||
@ -846,7 +854,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_largest_first_coin_selection_use_all() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 20_000 + FEE_AMOUNT;
|
||||
|
||||
let result = LargestFirstCoinSelection::default()
|
||||
@ -867,7 +875,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_largest_first_coin_selection_use_only_necessary() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 20_000 + FEE_AMOUNT;
|
||||
|
||||
let result = LargestFirstCoinSelection::default()
|
||||
@ -889,7 +897,7 @@ mod test {
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_largest_first_coin_selection_insufficient_funds() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 500_000 + FEE_AMOUNT;
|
||||
|
||||
LargestFirstCoinSelection::default()
|
||||
@ -907,7 +915,7 @@ mod test {
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_largest_first_coin_selection_insufficient_funds_high_fees() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 250_000 + FEE_AMOUNT;
|
||||
|
||||
LargestFirstCoinSelection::default()
|
||||
@ -924,7 +932,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_oldest_first_coin_selection_success() {
|
||||
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 result = OldestFirstCoinSelection::default()
|
||||
@ -945,7 +953,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_oldest_first_coin_selection_use_all() {
|
||||
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 result = OldestFirstCoinSelection::default()
|
||||
@ -966,7 +974,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_oldest_first_coin_selection_use_only_necessary() {
|
||||
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 result = OldestFirstCoinSelection::default()
|
||||
@ -988,7 +996,7 @@ mod test {
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_oldest_first_coin_selection_insufficient_funds() {
|
||||
let utxos = get_oldest_first_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 600_000 + FEE_AMOUNT;
|
||||
|
||||
OldestFirstCoinSelection::default()
|
||||
@ -1008,7 +1016,7 @@ mod test {
|
||||
let utxos = get_oldest_first_test_utxos();
|
||||
|
||||
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()
|
||||
.coin_select(
|
||||
@ -1027,7 +1035,7 @@ mod test {
|
||||
// select three outputs
|
||||
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;
|
||||
|
||||
@ -1049,7 +1057,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_bnb_coin_selection_required_are_enough() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 20_000 + FEE_AMOUNT;
|
||||
|
||||
let result = BranchAndBoundCoinSelection::default()
|
||||
@ -1070,7 +1078,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_bnb_coin_selection_optional_are_enough() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 299756 + FEE_AMOUNT;
|
||||
|
||||
let result = BranchAndBoundCoinSelection::default()
|
||||
@ -1106,7 +1114,7 @@ mod test {
|
||||
assert_eq!(amount, 100_000);
|
||||
let amount: u64 = optional.iter().map(|u| u.utxo.txout().value).sum();
|
||||
assert!(amount > 150_000);
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
|
||||
let target_amount = 150_000 + FEE_AMOUNT;
|
||||
|
||||
@ -1129,7 +1137,7 @@ mod test {
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_bnb_coin_selection_insufficient_funds() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 500_000 + FEE_AMOUNT;
|
||||
|
||||
BranchAndBoundCoinSelection::default()
|
||||
@ -1147,7 +1155,7 @@ mod test {
|
||||
#[should_panic(expected = "InsufficientFunds")]
|
||||
fn test_bnb_coin_selection_insufficient_funds_high_fees() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
let target_amount = 250_000 + FEE_AMOUNT;
|
||||
|
||||
BranchAndBoundCoinSelection::default()
|
||||
@ -1164,7 +1172,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_bnb_coin_selection_check_fee_rate() {
|
||||
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 result = BranchAndBoundCoinSelection::new(0)
|
||||
@ -1192,7 +1200,7 @@ mod test {
|
||||
for _i in 0..200 {
|
||||
let mut optional_utxos = generate_random_utxos(&mut rng, 16);
|
||||
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)
|
||||
.coin_select(
|
||||
vec![],
|
||||
@ -1220,7 +1228,7 @@ mod test {
|
||||
let size_of_change = 31;
|
||||
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;
|
||||
BranchAndBoundCoinSelection::new(size_of_change)
|
||||
.bnb(
|
||||
@ -1251,7 +1259,7 @@ mod test {
|
||||
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
|
||||
let target_amount = 20_000 + FEE_AMOUNT;
|
||||
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
|
||||
BranchAndBoundCoinSelection::new(size_of_change)
|
||||
.bnb(
|
||||
@ -1287,7 +1295,7 @@ mod test {
|
||||
// cost_of_change + 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)
|
||||
.bnb(
|
||||
@ -1327,7 +1335,7 @@ mod test {
|
||||
let target_amount =
|
||||
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)
|
||||
.bnb(
|
||||
@ -1358,7 +1366,7 @@ mod test {
|
||||
.map(|u| OutputGroup::new(u, fee_rate))
|
||||
.collect();
|
||||
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
|
||||
let result = BranchAndBoundCoinSelection::default().single_random_draw(
|
||||
vec![],
|
||||
@ -1376,7 +1384,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_bnb_exclude_negative_effective_value() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
|
||||
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
||||
vec![],
|
||||
@ -1398,7 +1406,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_bnb_include_negative_effective_value_when_required() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
|
||||
let (required, optional) = utxos
|
||||
.into_iter()
|
||||
@ -1424,7 +1432,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_bnb_sum_of_effective_value_negative() {
|
||||
let utxos = get_test_utxos();
|
||||
let drain_script = Script::default();
|
||||
let drain_script = ScriptBuf::default();
|
||||
|
||||
let selection = BranchAndBoundCoinSelection::default().coin_select(
|
||||
utxos,
|
||||
|
@ -232,7 +232,7 @@ mod test {
|
||||
input: vec![],
|
||||
output: vec![],
|
||||
version: 0,
|
||||
lock_time: bitcoin::PackedLockTime::ZERO,
|
||||
lock_time: bitcoin::absolute::LockTime::ZERO,
|
||||
};
|
||||
wallet
|
||||
.insert_checkpoint(BlockId {
|
||||
|
@ -19,7 +19,7 @@
|
||||
//! # use bdk::wallet::hardwaresigner::HWISigner;
|
||||
//! # use bdk::wallet::AddressIndex::New;
|
||||
//! # use bdk::{FeeRate, KeychainKind, SignOptions, Wallet};
|
||||
//! # use hwi::{types::HWIChain, HWIClient};
|
||||
//! # use hwi::HWIClient;
|
||||
//! # use std::sync::Arc;
|
||||
//! #
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -28,7 +28,7 @@
|
||||
//! panic!("No devices found!");
|
||||
//! }
|
||||
//! 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(
|
||||
//! # "",
|
||||
@ -47,9 +47,9 @@
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use bitcoin::bip32::Fingerprint;
|
||||
use bitcoin::psbt::PartiallySignedTransaction;
|
||||
use bitcoin::secp256k1::{All, Secp256k1};
|
||||
use bitcoin::util::bip32::Fingerprint;
|
||||
|
||||
use hwi::error::Error;
|
||||
use hwi::types::{HWIChain, HWIDevice};
|
||||
|
@ -29,11 +29,12 @@ use bdk_chain::{
|
||||
IndexedTxGraph, Persist, PersistBackend,
|
||||
};
|
||||
use bitcoin::consensus::encode::serialize;
|
||||
use bitcoin::psbt;
|
||||
use bitcoin::secp256k1::Secp256k1;
|
||||
use bitcoin::util::psbt;
|
||||
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
||||
use bitcoin::{
|
||||
Address, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script, Sequence,
|
||||
Transaction, TxOut, Txid, Witness,
|
||||
absolute, Address, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxOut, Txid,
|
||||
Weight, Witness,
|
||||
};
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
@ -322,11 +323,11 @@ impl<D> Wallet<D> {
|
||||
let (index, spk, additions) = match address_index {
|
||||
AddressIndex::New => {
|
||||
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 => {
|
||||
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) => {
|
||||
let (index, spk) = txout_index
|
||||
@ -396,7 +397,7 @@ impl<D> Wallet<D> {
|
||||
/// script pubkeys the wallet is storing internally).
|
||||
pub fn spks_of_all_keychains(
|
||||
&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()
|
||||
}
|
||||
|
||||
@ -408,7 +409,7 @@ impl<D> Wallet<D> {
|
||||
pub fn spks_of_keychain(
|
||||
&self,
|
||||
keychain: KeychainKind,
|
||||
) -> impl Iterator<Item = (u32, Script)> + Clone {
|
||||
) -> impl Iterator<Item = (u32, ScriptBuf)> + Clone {
|
||||
self.indexed_graph.index.spks_of_keychain(&keychain)
|
||||
}
|
||||
|
||||
@ -599,7 +600,7 @@ impl<D> Wallet<D> {
|
||||
/// # use bdk::*;
|
||||
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||
/// # 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 mut builder = wallet.build_tx();
|
||||
/// builder
|
||||
@ -716,7 +717,7 @@ impl<D> Wallet<D> {
|
||||
None => self
|
||||
.chain
|
||||
.tip()
|
||||
.map(|cp| LockTime::from_height(cp.height()).expect("Invalid height")),
|
||||
.map(|cp| absolute::LockTime::from_height(cp.height()).expect("Invalid height")),
|
||||
h => h,
|
||||
};
|
||||
|
||||
@ -726,7 +727,7 @@ impl<D> Wallet<D> {
|
||||
// Fee sniping can be partially prevented by setting the timelock
|
||||
// to current_height. If we don't know the current_height,
|
||||
// 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
|
||||
// height
|
||||
@ -734,7 +735,7 @@ impl<D> Wallet<D> {
|
||||
// No requirement, just use the fee_sniping_height
|
||||
None => 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
|
||||
// than the fee_sniping_height use that value
|
||||
Some(value) => value,
|
||||
@ -750,7 +751,9 @@ impl<D> Wallet<D> {
|
||||
|
||||
let n_sequence = match (params.rbf, requirements.csv) {
|
||||
// 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
|
||||
(None, None) => Sequence::MAX,
|
||||
|
||||
@ -813,7 +816,7 @@ impl<D> Wallet<D> {
|
||||
|
||||
let mut tx = Transaction {
|
||||
version,
|
||||
lock_time: lock_time.into(),
|
||||
lock_time,
|
||||
input: 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.
|
||||
// If, instead, we undershoot, we may end up with a feerate lower than the requested one
|
||||
// - 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
|
||||
&& internal_descriptor.is_none()
|
||||
@ -878,7 +881,7 @@ impl<D> Wallet<D> {
|
||||
params.drain_wallet,
|
||||
params.manually_selected_only,
|
||||
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
|
||||
@ -888,7 +891,7 @@ impl<D> Wallet<D> {
|
||||
let change_keychain = self.map_keychain(KeychainKind::Internal);
|
||||
let ((index, spk), index_additions) =
|
||||
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.persist
|
||||
.stage(ChangeSet::from(IndexedAdditions::from(index_additions)));
|
||||
@ -912,7 +915,7 @@ impl<D> Wallet<D> {
|
||||
.iter()
|
||||
.map(|u| bitcoin::TxIn {
|
||||
previous_output: u.outpoint(),
|
||||
script_sig: Script::default(),
|
||||
script_sig: ScriptBuf::default(),
|
||||
sequence: n_sequence,
|
||||
witness: Witness::new(),
|
||||
})
|
||||
@ -1000,7 +1003,7 @@ impl<D> Wallet<D> {
|
||||
/// # use bdk::*;
|
||||
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||
/// # 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 builder = wallet.build_tx();
|
||||
/// builder
|
||||
@ -1014,7 +1017,7 @@ impl<D> Wallet<D> {
|
||||
/// let (mut psbt, _) = {
|
||||
/// let mut builder = wallet.build_fee_bump(tx.txid())?;
|
||||
/// builder
|
||||
/// .fee_rate(FeeRate::from_sat_per_vb(5.0));
|
||||
/// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0));
|
||||
/// builder.finish()?
|
||||
/// };
|
||||
///
|
||||
@ -1078,6 +1081,7 @@ impl<D> Wallet<D> {
|
||||
|
||||
let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
|
||||
Some(&(keychain, derivation_index)) => {
|
||||
#[allow(deprecated)]
|
||||
let satisfaction_weight = self
|
||||
.get_descriptor_for_keychain(keychain)
|
||||
.max_satisfaction_weight()
|
||||
@ -1170,7 +1174,7 @@ impl<D> Wallet<D> {
|
||||
/// # use bdk::*;
|
||||
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
|
||||
/// # 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 builder = wallet.build_tx();
|
||||
/// builder.add_recipient(to_address.script_pubkey(), 50_000);
|
||||
@ -1207,8 +1211,8 @@ impl<D> Wallet<D> {
|
||||
&& !psbt.inputs.iter().all(|i| {
|
||||
i.sighash_type.is_none()
|
||||
|| i.sighash_type == Some(EcdsaSighashType::All.into())
|
||||
|| i.sighash_type == Some(SchnorrSighashType::All.into())
|
||||
|| i.sighash_type == Some(SchnorrSighashType::Default.into())
|
||||
|| i.sighash_type == Some(TapSighashType::All.into())
|
||||
|| i.sighash_type == Some(TapSighashType::Default.into())
|
||||
})
|
||||
{
|
||||
return Err(Error::Signer(signer::SignerError::NonStandardSighash));
|
||||
@ -1403,13 +1407,14 @@ impl<D> Wallet<D> {
|
||||
.index
|
||||
.index_of_spk(&txout.script_pubkey)?;
|
||||
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)> {
|
||||
self.list_unspent()
|
||||
.map(|utxo| {
|
||||
let keychain = utxo.keychain;
|
||||
#[allow(deprecated)]
|
||||
(
|
||||
utxo,
|
||||
self.get_descriptor_for_keychain(keychain)
|
||||
@ -1620,7 +1625,9 @@ impl<D> Wallet<D> {
|
||||
};
|
||||
|
||||
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
|
||||
.update_with_descriptor_unchecked(&derived_descriptor)
|
||||
@ -1669,7 +1676,9 @@ impl<D> Wallet<D> {
|
||||
);
|
||||
|
||||
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 {
|
||||
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_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::wallet::{AddressIndex, Wallet};
|
||||
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 tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
value: 500_000,
|
||||
|
@ -19,7 +19,7 @@
|
||||
//! # use core::str::FromStr;
|
||||
//! # use bitcoin::secp256k1::{Secp256k1, All};
|
||||
//! # use bitcoin::*;
|
||||
//! # use bitcoin::util::psbt;
|
||||
//! # use bitcoin::psbt;
|
||||
//! # use bdk::signer::*;
|
||||
//! # use bdk::*;
|
||||
//! # #[derive(Debug)]
|
||||
@ -86,18 +86,17 @@ use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::ops::{Bound::Included, Deref};
|
||||
|
||||
use bitcoin::blockdata::opcodes;
|
||||
use bitcoin::blockdata::script::Builder as ScriptBuilder;
|
||||
use bitcoin::hashes::{hash160, Hash};
|
||||
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint};
|
||||
use bitcoin::hashes::hash160;
|
||||
use bitcoin::secp256k1::Message;
|
||||
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint};
|
||||
use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot};
|
||||
use bitcoin::{secp256k1, XOnlyPublicKey};
|
||||
use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script};
|
||||
use bitcoin::sighash::{EcdsaSighashType, TapSighash, TapSighashType};
|
||||
use bitcoin::{ecdsa, psbt, sighash, taproot};
|
||||
use bitcoin::{key::TapTweak, key::XOnlyPublicKey, secp256k1};
|
||||
use bitcoin::{PrivateKey, PublicKey};
|
||||
|
||||
use miniscript::descriptor::{
|
||||
Descriptor, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, KeyMap, SinglePriv,
|
||||
SinglePubKey,
|
||||
Descriptor, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey,
|
||||
InnerXKey, KeyMap, SinglePriv, SinglePubKey,
|
||||
};
|
||||
use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey};
|
||||
|
||||
@ -130,7 +129,7 @@ impl From<Fingerprint> for SignerId {
|
||||
}
|
||||
|
||||
/// Signing error
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum SignerError {
|
||||
/// The private key is missing for the required public key
|
||||
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> {
|
||||
fn id(&self, secp: &SecpCtx) -> SignerId {
|
||||
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 {
|
||||
SignerContext::Segwitv0 => Segwitv0::sighash(psbt, input_index, ())?,
|
||||
SignerContext::Legacy => Legacy::sighash(psbt, input_index, ())?,
|
||||
SignerContext::Segwitv0 => {
|
||||
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
|
||||
};
|
||||
sign_psbt_ecdsa(
|
||||
@ -499,12 +548,12 @@ fn sign_psbt_ecdsa(
|
||||
secret_key: &secp256k1::SecretKey,
|
||||
pubkey: PublicKey,
|
||||
psbt_input: &mut psbt::Input,
|
||||
hash: bitcoin::Sighash,
|
||||
hash: impl bitcoin::hashes::Hash + bitcoin::secp256k1::ThirtyTwoByteHash,
|
||||
hash_ty: EcdsaSighashType,
|
||||
secp: &SecpCtx,
|
||||
allow_grinding: bool,
|
||||
) {
|
||||
let msg = &Message::from_slice(&hash.into_inner()[..]).unwrap();
|
||||
let msg = &Message::from(hash);
|
||||
let sig = if allow_grinding {
|
||||
secp.sign_ecdsa_low_r(msg, secret_key)
|
||||
} else {
|
||||
@ -513,7 +562,7 @@ fn sign_psbt_ecdsa(
|
||||
secp.verify_ecdsa(msg, &sig, &pubkey.inner)
|
||||
.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);
|
||||
}
|
||||
|
||||
@ -523,12 +572,10 @@ fn sign_psbt_schnorr(
|
||||
pubkey: XOnlyPublicKey,
|
||||
leaf_hash: Option<taproot::TapLeafHash>,
|
||||
psbt_input: &mut psbt::Input,
|
||||
hash: taproot::TapSighashHash,
|
||||
hash_ty: SchnorrSighashType,
|
||||
hash: TapSighash,
|
||||
hash_ty: TapSighashType,
|
||||
secp: &SecpCtx,
|
||||
) {
|
||||
use schnorr::TapTweak;
|
||||
|
||||
let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
|
||||
let keypair = match leaf_hash {
|
||||
None => keypair
|
||||
@ -537,12 +584,12 @@ fn sign_psbt_schnorr(
|
||||
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);
|
||||
secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
|
||||
.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 {
|
||||
psbt_input
|
||||
@ -632,6 +679,11 @@ impl SignersContainer {
|
||||
SignerOrdering::default(),
|
||||
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 {
|
||||
type Extra = ();
|
||||
type Sighash = bitcoin::Sighash;
|
||||
type Sighash = sighash::LegacySighash;
|
||||
type SighashType = EcdsaSighashType;
|
||||
|
||||
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 {
|
||||
type Extra = ();
|
||||
type Sighash = bitcoin::Sighash;
|
||||
type Sighash = sighash::SegwitV0Sighash;
|
||||
type SighashType = EcdsaSighashType;
|
||||
|
||||
fn sighash(
|
||||
@ -908,14 +950,21 @@ impl ComputeSighash for Segwitv0 {
|
||||
Some(ref witness_script) => witness_script.clone(),
|
||||
None => {
|
||||
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
|
||||
.redeem_script
|
||||
.as_ref()
|
||||
.map(Script::is_v0_p2wpkh)
|
||||
.map(|s| s.is_v0_p2wpkh())
|
||||
.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 {
|
||||
return Err(SignerError::MissingWitnessScript);
|
||||
}
|
||||
@ -936,14 +985,14 @@ impl ComputeSighash for Segwitv0 {
|
||||
|
||||
impl ComputeSighash for Tap {
|
||||
type Extra = Option<taproot::TapLeafHash>;
|
||||
type Sighash = taproot::TapSighashHash;
|
||||
type SighashType = SchnorrSighashType;
|
||||
type Sighash = TapSighash;
|
||||
type SighashType = TapSighashType;
|
||||
|
||||
fn sighash(
|
||||
psbt: &psbt::PartiallySignedTransaction,
|
||||
input_index: usize,
|
||||
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() {
|
||||
return Err(SignerError::InputIndexOutOfRange);
|
||||
}
|
||||
@ -952,8 +1001,8 @@ impl ComputeSighash for Tap {
|
||||
|
||||
let sighash_type = psbt_input
|
||||
.sighash_type
|
||||
.unwrap_or_else(|| SchnorrSighashType::Default.into())
|
||||
.schnorr_hash_ty()
|
||||
.unwrap_or_else(|| TapSighashType::Default.into())
|
||||
.taproot_hash_ty()
|
||||
.map_err(|_| SignerError::InvalidSighash)?;
|
||||
let witness_utxos = (0..psbt.inputs.len())
|
||||
.map(|i| psbt.get_utxo_for(i))
|
||||
@ -1015,8 +1064,8 @@ mod signers_container_tests {
|
||||
use crate::descriptor::IntoWalletDescriptor;
|
||||
use crate::keys::{DescriptorKey, IntoDescriptorKey};
|
||||
use assert_matches::assert_matches;
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::secp256k1::{All, Secp256k1};
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::Network;
|
||||
use core::str::FromStr;
|
||||
use miniscript::ScriptContext;
|
||||
|
@ -18,7 +18,7 @@
|
||||
//! # use bitcoin::*;
|
||||
//! # use bdk::*;
|
||||
//! # 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!();
|
||||
//! // create a TxBuilder from a wallet
|
||||
//! let mut tx_builder = wallet.build_tx();
|
||||
@ -27,7 +27,7 @@
|
||||
//! // Create a transaction with one output to `to_address` of 50_000 satoshi
|
||||
//! .add_recipient(to_address.script_pubkey(), 50_000)
|
||||
//! // 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
|
||||
//! .do_not_spend_change()
|
||||
//! // Turn on RBF signaling
|
||||
@ -43,8 +43,8 @@ use bdk_chain::PersistBackend;
|
||||
use core::cell::RefCell;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt};
|
||||
use bitcoin::{LockTime, OutPoint, Script, Sequence, Transaction};
|
||||
use bitcoin::psbt::{self, PartiallySignedTransaction as Psbt};
|
||||
use bitcoin::{absolute, script::PushBytes, OutPoint, ScriptBuf, Sequence, Transaction};
|
||||
|
||||
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
|
||||
use super::ChangeSet;
|
||||
@ -82,7 +82,7 @@ impl TxBuilderContext for BumpFee {}
|
||||
/// # use bitcoin::*;
|
||||
/// # use core::str::FromStr;
|
||||
/// # 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();
|
||||
/// // chaining
|
||||
/// let (psbt1, details) = {
|
||||
@ -129,9 +129,9 @@ pub struct TxBuilder<'a, D, Cs, Ctx> {
|
||||
//TODO: TxParams should eventually be exposed publicly.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub(crate) struct TxParams {
|
||||
pub(crate) recipients: Vec<(Script, u64)>,
|
||||
pub(crate) recipients: Vec<(ScriptBuf, u64)>,
|
||||
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) internal_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) sighash: Option<psbt::PsbtSighashType>,
|
||||
pub(crate) ordering: TxOrdering,
|
||||
pub(crate) locktime: Option<LockTime>,
|
||||
pub(crate) locktime: Option<absolute::LockTime>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
pub(crate) version: Option<Version>,
|
||||
pub(crate) change_policy: ChangeSpendPolicy,
|
||||
@ -148,7 +148,7 @@ pub(crate) struct TxParams {
|
||||
pub(crate) add_global_xpubs: bool,
|
||||
pub(crate) include_output_redeem_witness_script: bool,
|
||||
pub(crate) bumping_fee: Option<PreviousFee>,
|
||||
pub(crate) current_height: Option<LockTime>,
|
||||
pub(crate) current_height: Option<absolute::LockTime>,
|
||||
pub(crate) allow_dust: bool,
|
||||
}
|
||||
|
||||
@ -245,7 +245,10 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
|
||||
/// # use std::collections::BTreeMap;
|
||||
/// # use bitcoin::*;
|
||||
/// # 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 path = BTreeMap::new();
|
||||
/// 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 {
|
||||
let descriptor = wallet.get_descriptor_for_keychain(utxo.keychain);
|
||||
#[allow(deprecated)]
|
||||
let satisfaction_weight = descriptor.max_satisfaction_weight().unwrap();
|
||||
self.params.utxos.push(WeightedUtxo {
|
||||
satisfaction_weight,
|
||||
@ -431,7 +435,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
|
||||
/// Use a specific nLockTime while creating the transaction
|
||||
///
|
||||
/// 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
|
||||
}
|
||||
@ -470,7 +474,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// Fill-in the [`psbt::Output::redeem_script`](bitcoin::util::psbt::Output::redeem_script) and
|
||||
/// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields.
|
||||
/// Fill-in the [`psbt::Output::redeem_script`](bitcoin::psbt::Output::redeem_script) and
|
||||
/// [`psbt::Output::witness_script`](bitcoin::psbt::Output::witness_script) fields.
|
||||
///
|
||||
/// This is useful for signers which always require it, like ColdCard hardware wallets.
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
@ -583,20 +588,20 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
|
||||
|
||||
impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// Add data as an output, using OP_RETURN
|
||||
pub fn add_data(&mut self, data: &[u8]) -> &mut Self {
|
||||
let script = Script::new_op_return(data);
|
||||
pub fn add_data<T: AsRef<PushBytes>>(&mut self, data: &T) -> &mut Self {
|
||||
let script = ScriptBuf::new_op_return(data);
|
||||
self.add_recipient(script, 0u64);
|
||||
self
|
||||
}
|
||||
@ -626,7 +631,10 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
|
||||
/// # use bitcoin::*;
|
||||
/// # use bdk::*;
|
||||
/// # 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 tx_builder = wallet.build_tx();
|
||||
///
|
||||
@ -635,7 +643,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
|
||||
/// .drain_wallet()
|
||||
/// // Send the excess (which is all the coins minus the fee) to this address.
|
||||
/// .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();
|
||||
/// let (psbt, tx_details) = tx_builder.finish()?;
|
||||
/// # Ok::<(), bdk::Error>(())
|
||||
@ -645,7 +653,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm> TxBuilder<'a, D, Cs, CreateTx> {
|
||||
/// [`add_recipient`]: Self::add_recipient
|
||||
/// [`add_utxos`]: Self::add_utxos
|
||||
/// [`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
|
||||
}
|
||||
@ -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
|
||||
/// 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
|
||||
.params
|
||||
.recipients
|
||||
@ -868,8 +876,11 @@ mod test {
|
||||
);
|
||||
|
||||
assert_eq!(tx.output[0].value, 800);
|
||||
assert_eq!(tx.output[1].script_pubkey, From::from(vec![0xAA]));
|
||||
assert_eq!(tx.output[2].script_pubkey, From::from(vec![0xAA, 0xEE]));
|
||||
assert_eq!(tx.output[1].script_pubkey, ScriptBuf::from(vec![0xAA]));
|
||||
assert_eq!(
|
||||
tx.output[2].script_pubkey,
|
||||
ScriptBuf::from(vec![0xAA, 0xEE])
|
||||
);
|
||||
}
|
||||
|
||||
fn get_test_utxos() -> Vec<LocalUtxo> {
|
||||
@ -878,7 +889,7 @@ mod test {
|
||||
vec![
|
||||
LocalUtxo {
|
||||
outpoint: OutPoint {
|
||||
txid: bitcoin::Txid::from_inner([0; 32]),
|
||||
txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
|
||||
vout: 0,
|
||||
},
|
||||
txout: Default::default(),
|
||||
@ -889,7 +900,7 @@ mod test {
|
||||
},
|
||||
LocalUtxo {
|
||||
outpoint: OutPoint {
|
||||
txid: bitcoin::Txid::from_inner([0; 32]),
|
||||
txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
|
||||
vout: 1,
|
||||
},
|
||||
txout: Default::default(),
|
||||
|
@ -10,7 +10,7 @@
|
||||
// licenses.
|
||||
|
||||
use bitcoin::secp256k1::{All, Secp256k1};
|
||||
use bitcoin::{LockTime, Script, Sequence};
|
||||
use bitcoin::{absolute, Script, Sequence};
|
||||
|
||||
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 {
|
||||
fn check_after(&self, n: LockTime) -> bool {
|
||||
fn check_after(&self, n: absolute::LockTime) -> bool {
|
||||
if let Some(current_height) = self.current_height {
|
||||
current_height >= n.to_consensus_u32()
|
||||
} else {
|
||||
@ -119,12 +119,14 @@ mod test {
|
||||
pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
|
||||
|
||||
use super::{check_nsequence_rbf, IsDust};
|
||||
use crate::bitcoin::{Address, Sequence};
|
||||
use crate::bitcoin::{Address, Network, Sequence};
|
||||
use core::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_is_dust() {
|
||||
let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe")
|
||||
.unwrap()
|
||||
.require_network(Network::Bitcoin)
|
||||
.unwrap()
|
||||
.script_pubkey();
|
||||
assert!(script_p2pkh.is_p2pkh());
|
||||
@ -132,6 +134,8 @@ mod test {
|
||||
assert!(!546.is_dust(&script_p2pkh));
|
||||
|
||||
let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8")
|
||||
.unwrap()
|
||||
.require_network(Network::Bitcoin)
|
||||
.unwrap()
|
||||
.script_pubkey();
|
||||
assert!(script_p2wpkh.is_v0_p2wpkh());
|
||||
|
@ -14,7 +14,7 @@ pub fn get_funded_wallet_with_change(
|
||||
|
||||
let tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: bitcoin::PackedLockTime(0),
|
||||
lock_time: bitcoin::absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
value: 50_000,
|
||||
|
@ -2,7 +2,7 @@ use bdk::bitcoin::TxIn;
|
||||
use bdk::wallet::AddressIndex;
|
||||
use bdk::wallet::AddressIndex::New;
|
||||
use bdk::{psbt, FeeRate, SignOptions};
|
||||
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
|
||||
use bitcoin::psbt::PartiallySignedTransaction as Psbt;
|
||||
use core::str::FromStr;
|
||||
mod common;
|
||||
use common::*;
|
||||
|
@ -11,13 +11,14 @@ use bdk_chain::BlockId;
|
||||
use bdk_chain::ConfirmationTime;
|
||||
use bdk_chain::COINBASE_MATURITY;
|
||||
use bitcoin::hashes::Hash;
|
||||
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
||||
use bitcoin::BlockHash;
|
||||
use bitcoin::Script;
|
||||
use bitcoin::{util::psbt, Network};
|
||||
use bitcoin::ScriptBuf;
|
||||
use bitcoin::{
|
||||
Address, EcdsaSighashType, LockTime, OutPoint, PackedLockTime, SchnorrSighashType, Sequence,
|
||||
Transaction, TxIn, TxOut,
|
||||
absolute, script::PushBytesBuf, taproot::TapNodeHash, Address, OutPoint, Sequence, Transaction,
|
||||
TxIn, TxOut, Weight,
|
||||
};
|
||||
use bitcoin::{psbt, Network};
|
||||
use core::str::FromStr;
|
||||
|
||||
mod common;
|
||||
@ -26,7 +27,7 @@ use common::*;
|
||||
fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint {
|
||||
let tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
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
|
||||
// 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]
|
||||
@ -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
|
||||
assert_eq!(
|
||||
psbt.unsigned_tx.lock_time.0,
|
||||
psbt.unsigned_tx.lock_time.to_consensus_u32(),
|
||||
wallet.latest_checkpoint().unwrap().height()
|
||||
);
|
||||
}
|
||||
@ -237,7 +238,7 @@ fn test_create_tx_default_locktime_cltv() {
|
||||
builder.add_recipient(addr.script_pubkey(), 25_000);
|
||||
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]
|
||||
@ -248,13 +249,13 @@ fn test_create_tx_custom_locktime() {
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 25_000)
|
||||
.current_height(630_001)
|
||||
.nlocktime(LockTime::from_height(630_000).unwrap());
|
||||
.nlocktime(absolute::LockTime::from_height(630_000).unwrap());
|
||||
let (psbt, _) = builder.finish().unwrap();
|
||||
|
||||
// When we explicitly specify a nlocktime
|
||||
// we don't try any fee sniping prevention trick
|
||||
// (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]
|
||||
@ -264,10 +265,10 @@ fn test_create_tx_custom_locktime_compatible_with_cltv() {
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.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();
|
||||
|
||||
assert_eq!(psbt.unsigned_tx.lock_time.0, 630_000);
|
||||
assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -280,7 +281,7 @@ fn test_create_tx_custom_locktime_incompatible_with_cltv() {
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 25_000)
|
||||
.nlocktime(LockTime::from_height(50000).unwrap());
|
||||
.nlocktime(absolute::LockTime::from_height(50000).unwrap());
|
||||
builder.finish().unwrap();
|
||||
}
|
||||
|
||||
@ -402,7 +403,9 @@ fn test_create_tx_drain_wallet_and_drain_to() {
|
||||
#[test]
|
||||
fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
|
||||
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 mut builder = wallet.build_tx();
|
||||
builder
|
||||
@ -614,18 +617,18 @@ fn test_create_tx_custom_sighash() {
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 30_000)
|
||||
.sighash(bitcoin::EcdsaSighashType::Single.into());
|
||||
.sighash(EcdsaSighashType::Single.into());
|
||||
let (psbt, _) = builder.finish().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
psbt.inputs[0].sighash_type,
|
||||
Some(bitcoin::EcdsaSighashType::Single.into())
|
||||
Some(EcdsaSighashType::Single.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_input_hd_keypaths() {
|
||||
use bitcoin::util::bip32::{DerivationPath, Fingerprint};
|
||||
use bitcoin::bip32::{DerivationPath, Fingerprint};
|
||||
use core::str::FromStr;
|
||||
|
||||
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]
|
||||
fn test_create_tx_output_hd_keypaths() {
|
||||
use bitcoin::util::bip32::{DerivationPath, Fingerprint};
|
||||
use bitcoin::bip32::{DerivationPath, Fingerprint};
|
||||
use core::str::FromStr;
|
||||
|
||||
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!(
|
||||
psbt.inputs[0].redeem_script,
|
||||
Some(Script::from(
|
||||
Some(ScriptBuf::from(
|
||||
Vec::<u8>::from_hex(
|
||||
"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].witness_script,
|
||||
Some(Script::from(
|
||||
Some(ScriptBuf::from(
|
||||
Vec::<u8>::from_hex(
|
||||
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
|
||||
)
|
||||
@ -715,8 +718,6 @@ fn test_create_tx_set_witness_script_p2wsh() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
|
||||
use bitcoin::hashes::hex::FromHex;
|
||||
|
||||
let (mut wallet, _) =
|
||||
get_funded_wallet("sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))");
|
||||
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();
|
||||
let (psbt, _) = builder.finish().unwrap();
|
||||
|
||||
let script = Script::from(
|
||||
Vec::<u8>::from_hex(
|
||||
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
let script = ScriptBuf::from_hex(
|
||||
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(psbt.inputs[0].redeem_script, Some(script.to_v0_p2wsh()));
|
||||
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(),
|
||||
}],
|
||||
version: 0,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
};
|
||||
wallet
|
||||
.insert_tx(
|
||||
@ -808,7 +807,9 @@ fn test_create_tx_add_utxo() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.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(),
|
||||
}],
|
||||
version: 0,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
};
|
||||
|
||||
wallet
|
||||
@ -848,7 +849,9 @@ fn test_create_tx_manually_selected_insufficient() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.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() {
|
||||
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();
|
||||
builder.add_recipient(addr.script_pubkey(), 30_000);
|
||||
builder.finish().unwrap();
|
||||
@ -879,7 +884,7 @@ fn test_create_tx_policy_path_no_csv() {
|
||||
|
||||
let tx = Transaction {
|
||||
version: 0,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
value: 50_000,
|
||||
@ -895,7 +900,9 @@ fn test_create_tx_policy_path_no_csv() {
|
||||
// child #0 is just the key "A"
|
||||
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();
|
||||
builder
|
||||
.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))
|
||||
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();
|
||||
builder
|
||||
.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)
|
||||
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();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 30_000)
|
||||
@ -945,8 +956,8 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_global_xpubs_with_origin() {
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::hashes::hex::FromHex;
|
||||
use bitcoin::util::bip32;
|
||||
|
||||
let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
|
||||
let addr = wallet.get_address(New);
|
||||
@ -970,8 +981,11 @@ fn test_add_foreign_utxo() {
|
||||
let (wallet2, _) =
|
||||
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!");
|
||||
#[allow(deprecated)]
|
||||
let foreign_utxo_satisfaction = wallet2
|
||||
.get_descriptor_for_keychain(KeychainKind::External)
|
||||
.max_satisfaction_weight()
|
||||
@ -1036,6 +1050,7 @@ fn test_add_foreign_utxo() {
|
||||
fn test_add_foreign_utxo_invalid_psbt_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let outpoint = wallet.list_unspent().next().expect("must exist").outpoint;
|
||||
#[allow(deprecated)]
|
||||
let foreign_utxo_satisfaction = wallet
|
||||
.get_descriptor_for_keychain(KeychainKind::External)
|
||||
.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 tx2 = wallet2.get_tx(txid2, true).unwrap().transaction.unwrap();
|
||||
|
||||
#[allow(deprecated)]
|
||||
let satisfaction_weight = wallet2
|
||||
.get_descriptor_for_keychain(KeychainKind::External)
|
||||
.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 (wallet2, txid2) =
|
||||
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();
|
||||
|
||||
#[allow(deprecated)]
|
||||
let satisfaction_weight = wallet2
|
||||
.get_descriptor_for_keychain(KeychainKind::External)
|
||||
.max_satisfaction_weight()
|
||||
@ -1181,8 +1200,8 @@ fn test_create_tx_global_xpubs_origin_missing() {
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_global_xpubs_master_without_origin() {
|
||||
use bitcoin::bip32;
|
||||
use bitcoin::hashes::hex::FromHex;
|
||||
use bitcoin::util::bip32;
|
||||
|
||||
let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
|
||||
let addr = wallet.get_address(New);
|
||||
@ -1315,7 +1334,9 @@ fn test_bump_fee_zero_abs() {
|
||||
#[test]
|
||||
fn test_bump_fee_reduce_change() {
|
||||
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();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 25_000)
|
||||
@ -1401,7 +1422,9 @@ fn test_bump_fee_reduce_change() {
|
||||
#[test]
|
||||
fn test_bump_fee_reduce_single_recipient() {
|
||||
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();
|
||||
builder
|
||||
.drain_to(addr.script_pubkey())
|
||||
@ -1434,7 +1457,9 @@ fn test_bump_fee_reduce_single_recipient() {
|
||||
#[test]
|
||||
fn test_bump_fee_absolute_reduce_single_recipient() {
|
||||
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();
|
||||
builder
|
||||
.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.
|
||||
let tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
value: 25_000,
|
||||
@ -1486,7 +1511,9 @@ fn test_bump_fee_drain_wallet() {
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
|
||||
let mut builder = wallet.build_tx();
|
||||
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.
|
||||
let init_tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
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(),
|
||||
vout: 0,
|
||||
};
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.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 init_tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![],
|
||||
output: vec![TxOut {
|
||||
script_pubkey: wallet.get_address(New).script_pubkey(),
|
||||
@ -1597,7 +1626,9 @@ fn test_bump_fee_add_input() {
|
||||
.into();
|
||||
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);
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 45_000)
|
||||
@ -1643,7 +1674,9 @@ fn test_bump_fee_add_input() {
|
||||
fn test_bump_fee_absolute_add_input() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
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);
|
||||
builder
|
||||
.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);
|
||||
|
||||
// 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();
|
||||
builder
|
||||
.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() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
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);
|
||||
builder
|
||||
.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
|
||||
// + 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
|
||||
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
|
||||
// We use epsilon here to avoid asking for a slightly too high feerate
|
||||
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 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);
|
||||
builder
|
||||
.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 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);
|
||||
builder
|
||||
.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
|
||||
// if that input was included in one of the original transactions.
|
||||
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();
|
||||
builder
|
||||
.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
|
||||
// original transaction)
|
||||
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
|
||||
// in the drain tx.
|
||||
receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));
|
||||
@ -1996,7 +2042,9 @@ fn test_fee_amount_negative_drain_val() {
|
||||
// for a transaction.
|
||||
// See https://github.com/bitcoindevkit/bdk/issues/660
|
||||
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 incoming_op = receive_output_in_latest_block(&mut wallet, 8859);
|
||||
|
||||
@ -2110,7 +2158,9 @@ fn test_sign_single_xprv_no_hd_keypaths() {
|
||||
#[test]
|
||||
fn test_include_output_redeem_witness_script() {
|
||||
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();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 45_000)
|
||||
@ -2127,7 +2177,9 @@ fn test_include_output_redeem_witness_script() {
|
||||
#[test]
|
||||
fn test_signing_only_one_of_multiple_inputs() {
|
||||
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();
|
||||
builder
|
||||
.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();
|
||||
|
||||
// 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 {
|
||||
value: 100_000,
|
||||
script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
|
||||
@ -2383,7 +2435,9 @@ fn test_returns_index_and_address() {
|
||||
wallet.get_address(New),
|
||||
AddressInfo {
|
||||
index: 0,
|
||||
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a").unwrap(),
|
||||
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::External,
|
||||
}
|
||||
);
|
||||
@ -2393,7 +2447,9 @@ fn test_returns_index_and_address() {
|
||||
wallet.get_address(New),
|
||||
AddressInfo {
|
||||
index: 1,
|
||||
address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7").unwrap(),
|
||||
address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::External,
|
||||
}
|
||||
);
|
||||
@ -2403,7 +2459,9 @@ fn test_returns_index_and_address() {
|
||||
wallet.get_address(Peek(25)),
|
||||
AddressInfo {
|
||||
index: 25,
|
||||
address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2").unwrap(),
|
||||
address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::External,
|
||||
}
|
||||
);
|
||||
@ -2413,7 +2471,9 @@ fn test_returns_index_and_address() {
|
||||
wallet.get_address(New),
|
||||
AddressInfo {
|
||||
index: 2,
|
||||
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2").unwrap(),
|
||||
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::External,
|
||||
}
|
||||
);
|
||||
@ -2423,7 +2483,8 @@ fn test_returns_index_and_address() {
|
||||
fn test_sending_to_bip350_bech32m_address() {
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
|
||||
let addr = Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), 45_000);
|
||||
builder.finish().unwrap();
|
||||
@ -2432,7 +2493,7 @@ fn test_sending_to_bip350_bech32m_address() {
|
||||
#[test]
|
||||
fn test_get_address() {
|
||||
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(
|
||||
Bip84(key, KeychainKind::External),
|
||||
Some(Bip84(key, KeychainKind::Internal)),
|
||||
@ -2444,7 +2505,9 @@ fn test_get_address() {
|
||||
wallet.get_address(AddressIndex::New),
|
||||
AddressInfo {
|
||||
index: 0,
|
||||
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(),
|
||||
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::External,
|
||||
}
|
||||
);
|
||||
@ -2453,7 +2516,9 @@ fn test_get_address() {
|
||||
wallet.get_internal_address(AddressIndex::New),
|
||||
AddressInfo {
|
||||
index: 0,
|
||||
address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79").unwrap(),
|
||||
address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::Internal,
|
||||
}
|
||||
);
|
||||
@ -2465,7 +2530,9 @@ fn test_get_address() {
|
||||
wallet.get_internal_address(AddressIndex::New),
|
||||
AddressInfo {
|
||||
index: 0,
|
||||
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(),
|
||||
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
|
||||
.unwrap()
|
||||
.assume_checked(),
|
||||
keychain: KeychainKind::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 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 =
|
||||
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 addr = wallet.get_address(AddressIndex::New);
|
||||
|
||||
let path = vec![("e5mmg3xh".to_string(), vec![0])]
|
||||
let path = vec![("rn4nre9c".to_string(), vec![0])]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
@ -2552,13 +2619,6 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
|
||||
assert_eq!(
|
||||
input_key_origins,
|
||||
vec![
|
||||
(
|
||||
from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
|
||||
(
|
||||
vec![],
|
||||
(FromStr::from_str("871fd295").unwrap(), vec![].into())
|
||||
)
|
||||
),
|
||||
(
|
||||
from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
|
||||
(
|
||||
@ -2572,6 +2632,13 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
|
||||
],
|
||||
(FromStr::from_str("ece52657").unwrap(), vec![].into())
|
||||
)
|
||||
),
|
||||
(
|
||||
from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
|
||||
(
|
||||
vec![],
|
||||
(FromStr::from_str("871fd295").unwrap(), vec![].into())
|
||||
)
|
||||
)
|
||||
],
|
||||
"Wrong input tap_key_origins"
|
||||
@ -2592,10 +2659,8 @@ fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
|
||||
|
||||
#[test]
|
||||
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::util::taproot;
|
||||
use bitcoin::taproot;
|
||||
|
||||
let (mut wallet, _) = get_funded_wallet(get_test_tr_with_taptree());
|
||||
let addr = wallet.get_address(AddressIndex::Peek(0));
|
||||
@ -2607,15 +2672,17 @@ fn test_taproot_psbt_input_tap_tree() {
|
||||
assert_eq!(
|
||||
psbt.inputs[0].tap_merkle_root,
|
||||
Some(
|
||||
FromHex::from_hex("61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986")
|
||||
.unwrap()
|
||||
TapNodeHash::from_str(
|
||||
"61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986"
|
||||
)
|
||||
.unwrap()
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(),
|
||||
vec![
|
||||
(taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (from_str!("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac"), 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("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (ScriptBuf::from_hex("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac").unwrap(), taproot::LeafVersion::TapScript)),
|
||||
(taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (ScriptBuf::from_hex("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac").unwrap(), taproot::LeafVersion::TapScript)),
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -2632,10 +2699,8 @@ fn test_taproot_psbt_input_tap_tree() {
|
||||
psbt.outputs[0].tap_internal_key
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
psbt.outputs[0].tap_tree,
|
||||
Some(TapTree::deserialize(&Vec::<u8>::from_hex("01c022208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac01c0222051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",).unwrap()).unwrap())
|
||||
);
|
||||
let tap_tree: bitcoin::taproot::TapTree = serde_json::from_str(r#"[1,{"Script":["2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",192]},1,{"Script":["208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac",192]}]"#).unwrap();
|
||||
assert_eq!(psbt.outputs[0].tap_tree, Some(tap_tree));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2706,9 +2771,12 @@ fn test_taproot_foreign_utxo() {
|
||||
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
|
||||
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 psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
|
||||
#[allow(deprecated)]
|
||||
let foreign_utxo_satisfaction = wallet2
|
||||
.get_descriptor_for_keychain(KeychainKind::External)
|
||||
.max_satisfaction_weight()
|
||||
@ -2829,7 +2897,7 @@ fn test_taproot_script_spend_sign_all_leaves() {
|
||||
#[test]
|
||||
fn test_taproot_script_spend_sign_include_some_leaves() {
|
||||
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 addr = wallet.get_address(AddressIndex::New);
|
||||
@ -2869,7 +2937,7 @@ fn test_taproot_script_spend_sign_include_some_leaves() {
|
||||
#[test]
|
||||
fn test_taproot_script_spend_sign_exclude_some_leaves() {
|
||||
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 addr = wallet.get_address(AddressIndex::New);
|
||||
@ -2958,7 +3026,7 @@ fn test_taproot_sign_explicit_sighash_all() {
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.drain_to(addr.script_pubkey())
|
||||
.sighash(SchnorrSighashType::All.into())
|
||||
.sighash(TapSighashType::All.into())
|
||||
.drain_wallet();
|
||||
let (mut psbt, _) = builder.finish().unwrap();
|
||||
|
||||
@ -2971,7 +3039,7 @@ fn test_taproot_sign_explicit_sighash_all() {
|
||||
|
||||
#[test]
|
||||
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 addr = wallet.get_address(New);
|
||||
@ -3052,7 +3120,7 @@ fn test_spend_coinbase() {
|
||||
.unwrap();
|
||||
let coinbase_tx = Transaction {
|
||||
version: 1,
|
||||
lock_time: bitcoin::PackedLockTime(0),
|
||||
lock_time: absolute::LockTime::ZERO,
|
||||
input: vec![TxIn {
|
||||
previous_output: OutPoint::null(),
|
||||
..Default::default()
|
||||
@ -3088,7 +3156,9 @@ fn test_spend_coinbase() {
|
||||
|
||||
// We try to create a transaction, only to notice that all
|
||||
// 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();
|
||||
builder
|
||||
.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 fee_rate = FeeRate::from_sat_per_vb(1.0);
|
||||
let mut builder = wallet.build_tx();
|
||||
let mut data = vec![0];
|
||||
let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
|
||||
builder
|
||||
.drain_to(addr.script_pubkey())
|
||||
.drain_wallet()
|
||||
@ -3187,8 +3257,8 @@ fn test_fee_rate_sign_no_grinding_high_r() {
|
||||
while sig_len < 71 {
|
||||
// Changing the OP_RETURN data will make the signature change (but not the fee, until
|
||||
// data[0] is small enough)
|
||||
data[0] += 1;
|
||||
psbt.unsigned_tx.output[op_return_vout].script_pubkey = Script::new_op_return(&data);
|
||||
data.as_mut_bytes()[0] += 1;
|
||||
psbt.unsigned_tx.output[op_return_vout].script_pubkey = ScriptBuf::new_op_return(&data);
|
||||
// Clearing the previous signature
|
||||
psbt.inputs[0].partial_sigs.clear();
|
||||
// Signing
|
||||
@ -3310,7 +3380,9 @@ fn test_taproot_load_descriptor_duplicated_keys() {
|
||||
fn test_tx_cancellation() {
|
||||
macro_rules! new_tx {
|
||||
($wallet:expr) => {{
|
||||
let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
|
||||
let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
|
||||
.unwrap()
|
||||
.assume_checked();
|
||||
let mut builder = $wallet.build_tx();
|
||||
builder.add_recipient(addr.script_pubkey(), 10_000);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user