Switch to "mainline" rust-miniscript
This commit is contained in:
parent
35579cb216
commit
7a42c5e095
@ -8,7 +8,7 @@ authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccard
|
|||||||
bdk-macros = { version = "0.1.0-beta.1", path = "./macros" }
|
bdk-macros = { version = "0.1.0-beta.1", path = "./macros" }
|
||||||
log = "^0.4"
|
log = "^0.4"
|
||||||
bitcoin = { version = "0.25", features = ["use-serde"] }
|
bitcoin = { version = "0.25", features = ["use-serde"] }
|
||||||
miniscript = { git = "https://github.com/MagicalBitcoin/rust-miniscript", rev = "dfc53201aa5796b6bf3b5a40581d192abaa84133" }
|
miniscript = { git = "https://github.com/rust-bitcoin/rust-miniscript.git", rev = "ca60da2" }
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
serde_json = { version = "^1.0" }
|
serde_json = { version = "^1.0" }
|
||||||
rand = "^0.7"
|
rand = "^0.7"
|
||||||
|
@ -27,11 +27,15 @@ extern crate serde_json;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||||
use bdk::bitcoin::util::bip32::ChildNumber;
|
use bdk::bitcoin::util::bip32::ChildNumber;
|
||||||
use bdk::bitcoin::*;
|
use bdk::bitcoin::*;
|
||||||
use bdk::descriptor::*;
|
use bdk::descriptor::*;
|
||||||
|
use bdk::miniscript::DescriptorPublicKeyCtx;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
|
||||||
let desc = "wsh(or_d(\
|
let desc = "wsh(or_d(\
|
||||||
multi(\
|
multi(\
|
||||||
2,[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*,tprv8ZgxMBicQKsPduL5QnGihpprdHyypMGi4DhimjtzYemu7se5YQNcZfAPLqXRuGHb5ZX2eTQj62oNqMnyxJ7B7wz54Uzswqw8fFqMVdcmVF7/1/*\
|
2,[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*,tprv8ZgxMBicQKsPduL5QnGihpprdHyypMGi4DhimjtzYemu7se5YQNcZfAPLqXRuGHb5ZX2eTQj62oNqMnyxJ7B7wz54Uzswqw8fFqMVdcmVF7/1/*\
|
||||||
@ -39,19 +43,18 @@ fn main() {
|
|||||||
and_v(vc:pk_h(cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy),older(1000))\
|
and_v(vc:pk_h(cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy),older(1000))\
|
||||||
))";
|
))";
|
||||||
|
|
||||||
let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc).unwrap();
|
let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(desc).unwrap();
|
||||||
println!("{:?}", extended_desc);
|
println!("{:?}", extended_desc);
|
||||||
|
|
||||||
|
let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, ChildNumber::from_normal_idx(42).unwrap());
|
||||||
|
|
||||||
let signers = Arc::new(key_map.into());
|
let signers = Arc::new(key_map.into());
|
||||||
let policy = extended_desc.extract_policy(signers).unwrap();
|
let policy = extended_desc.extract_policy(signers, &secp).unwrap();
|
||||||
println!("policy: {}", serde_json::to_string(&policy).unwrap());
|
println!("policy: {}", serde_json::to_string(&policy).unwrap());
|
||||||
|
|
||||||
let derived_desc = extended_desc.derive(ChildNumber::from_normal_idx(42).unwrap());
|
let addr = extended_desc.address(Network::Testnet, deriv_ctx).unwrap();
|
||||||
println!("{:?}", derived_desc);
|
|
||||||
|
|
||||||
let addr = derived_desc.address(Network::Testnet).unwrap();
|
|
||||||
println!("{}", addr);
|
println!("{}", addr);
|
||||||
|
|
||||||
let script = derived_desc.witness_script();
|
let script = extended_desc.witness_script(deriv_ctx);
|
||||||
println!("{:?}", script);
|
println!("{:?}", script);
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,10 @@ macro_rules! impl_top_level_pk {
|
|||||||
( $descriptor_variant:ident, $ctx:ty, $key:expr ) => {{
|
( $descriptor_variant:ident, $ctx:ty, $key:expr ) => {{
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use $crate::keys::{DescriptorKey, ToDescriptorKey};
|
use $crate::keys::{DescriptorKey, ToDescriptorKey};
|
||||||
|
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
|
||||||
|
|
||||||
$key.to_descriptor_key()
|
$key.to_descriptor_key()
|
||||||
.and_then(|key: DescriptorKey<$ctx>| key.extract())
|
.and_then(|key: DescriptorKey<$ctx>| key.extract(&secp))
|
||||||
.map(|(pk, key_map, valid_networks)| {
|
.map(|(pk, key_map, valid_networks)| {
|
||||||
(
|
(
|
||||||
$crate::miniscript::Descriptor::<
|
$crate::miniscript::Descriptor::<
|
||||||
@ -314,7 +315,8 @@ macro_rules! fragment {
|
|||||||
$crate::impl_leaf_opcode!(False)
|
$crate::impl_leaf_opcode!(False)
|
||||||
});
|
});
|
||||||
( pk_k $key:expr ) => ({
|
( pk_k $key:expr ) => ({
|
||||||
$crate::keys::make_pk($key)
|
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
|
||||||
|
$crate::keys::make_pk($key, &secp)
|
||||||
});
|
});
|
||||||
( pk $key:expr ) => ({
|
( pk $key:expr ) => ({
|
||||||
$crate::fragment!(+c pk_k $key)
|
$crate::fragment!(+c pk_k $key)
|
||||||
@ -391,6 +393,7 @@ macro_rules! fragment {
|
|||||||
});
|
});
|
||||||
( multi $thresh:expr $(, $key:expr )+ ) => ({
|
( multi $thresh:expr $(, $key:expr )+ ) => ({
|
||||||
use $crate::keys::ToDescriptorKey;
|
use $crate::keys::ToDescriptorKey;
|
||||||
|
let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
|
||||||
|
|
||||||
let mut keys = vec![];
|
let mut keys = vec![];
|
||||||
$(
|
$(
|
||||||
@ -398,7 +401,7 @@ macro_rules! fragment {
|
|||||||
)*
|
)*
|
||||||
|
|
||||||
keys.into_iter().collect::<Result<Vec<_>, _>>()
|
keys.into_iter().collect::<Result<Vec<_>, _>>()
|
||||||
.and_then(|keys| $crate::keys::make_multi($thresh, keys))
|
.and_then(|keys| $crate::keys::make_multi($thresh, keys, &secp))
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -406,7 +409,8 @@ macro_rules! fragment {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use bitcoin::hashes::hex::ToHex;
|
use bitcoin::hashes::hex::ToHex;
|
||||||
use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
|
use miniscript::descriptor::{DescriptorPublicKey, DescriptorPublicKeyCtx, KeyMap};
|
||||||
use miniscript::{Descriptor, Legacy, Segwitv0};
|
use miniscript::{Descriptor, Legacy, Segwitv0};
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -426,6 +430,9 @@ mod test {
|
|||||||
is_fixed: bool,
|
is_fixed: bool,
|
||||||
expected: &[&str],
|
expected: &[&str],
|
||||||
) {
|
) {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, ChildNumber::Normal { index: 0 });
|
||||||
|
|
||||||
let (desc, _key_map, _networks) = desc.unwrap();
|
let (desc, _key_map, _networks) = desc.unwrap();
|
||||||
assert_eq!(desc.is_witness(), is_witness);
|
assert_eq!(desc.is_witness(), is_witness);
|
||||||
assert_eq!(desc.is_fixed(), is_fixed);
|
assert_eq!(desc.is_fixed(), is_fixed);
|
||||||
@ -436,11 +443,11 @@ mod test {
|
|||||||
} else {
|
} else {
|
||||||
desc.derive(ChildNumber::from_normal_idx(index).unwrap())
|
desc.derive(ChildNumber::from_normal_idx(index).unwrap())
|
||||||
};
|
};
|
||||||
let address = child_desc.address(Regtest);
|
let address = child_desc.address(Regtest, deriv_ctx);
|
||||||
if address.is_some() {
|
if address.is_some() {
|
||||||
assert_eq!(address.unwrap().to_string(), *expected.get(i).unwrap());
|
assert_eq!(address.unwrap().to_string(), *expected.get(i).unwrap());
|
||||||
} else {
|
} else {
|
||||||
let script = child_desc.script_pubkey();
|
let script = child_desc.script_pubkey(deriv_ctx);
|
||||||
assert_eq!(script.to_hex().as_str(), *expected.get(i).unwrap());
|
assert_eq!(script.to_hex().as_str(), *expected.get(i).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -649,6 +656,8 @@ mod test {
|
|||||||
// - verify the key_maps are correctly merged together
|
// - verify the key_maps are correctly merged together
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_maps_merged() {
|
fn test_key_maps_merged() {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
|
||||||
let xprv1 = bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
let xprv1 = bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||||
let path1 = bip32::DerivationPath::from_str("m/0").unwrap();
|
let path1 = bip32::DerivationPath::from_str("m/0").unwrap();
|
||||||
let desc_key1 = (xprv1, path1.clone()).to_descriptor_key().unwrap();
|
let desc_key1 = (xprv1, path1.clone()).to_descriptor_key().unwrap();
|
||||||
@ -672,9 +681,9 @@ mod test {
|
|||||||
let desc_key3: DescriptorKey<Segwitv0> =
|
let desc_key3: DescriptorKey<Segwitv0> =
|
||||||
(xprv3, path3.clone()).to_descriptor_key().unwrap();
|
(xprv3, path3.clone()).to_descriptor_key().unwrap();
|
||||||
|
|
||||||
let (key1, _key_map, _valid_networks) = desc_key1.extract().unwrap();
|
let (key1, _key_map, _valid_networks) = desc_key1.extract(&secp).unwrap();
|
||||||
let (key2, _key_map, _valid_networks) = desc_key2.extract().unwrap();
|
let (key2, _key_map, _valid_networks) = desc_key2.extract(&secp).unwrap();
|
||||||
let (key3, _key_map, _valid_networks) = desc_key3.extract().unwrap();
|
let (key3, _key_map, _valid_networks) = desc_key3.extract(&secp).unwrap();
|
||||||
assert_eq!(key_map.get(&key1).unwrap().to_string(), "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/0/*");
|
assert_eq!(key_map.get(&key1).unwrap().to_string(), "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/0/*");
|
||||||
assert_eq!(key_map.get(&key2).unwrap().to_string(), "tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF/2147483647'/0/*");
|
assert_eq!(key_map.get(&key2).unwrap().to_string(), "tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF/2147483647'/0/*");
|
||||||
assert_eq!(key_map.get(&key3).unwrap().to_string(), "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf/10/20/30/40/*");
|
assert_eq!(key_map.get(&key3).unwrap().to_string(), "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf/10/20/30/40/*");
|
||||||
|
@ -53,6 +53,7 @@ use self::error::Error;
|
|||||||
pub use self::policy::Policy;
|
pub use self::policy::Policy;
|
||||||
use crate::keys::{KeyError, ToDescriptorKey, ValidNetworks};
|
use crate::keys::{KeyError, ToDescriptorKey, ValidNetworks};
|
||||||
use crate::wallet::signer::SignersContainer;
|
use crate::wallet::signer::SignersContainer;
|
||||||
|
use crate::wallet::utils::{descriptor_to_pk_ctx, SecpCtx};
|
||||||
|
|
||||||
/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
|
/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
|
||||||
pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
|
pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
|
||||||
@ -92,7 +93,7 @@ impl ToWalletDescriptor for &str {
|
|||||||
self
|
self
|
||||||
};
|
};
|
||||||
|
|
||||||
ExtendedDescriptor::parse_secret(descriptor)?.to_wallet_descriptor(network)
|
ExtendedDescriptor::parse_descriptor(descriptor)?.to_wallet_descriptor(network)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,15 +122,17 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap) {
|
|||||||
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
|
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
|
||||||
use crate::keys::DescriptorKey;
|
use crate::keys::DescriptorKey;
|
||||||
|
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
|
||||||
let check_key = |pk: &DescriptorPublicKey| {
|
let check_key = |pk: &DescriptorPublicKey| {
|
||||||
let (pk, _, networks) = if self.0.is_witness() {
|
let (pk, _, networks) = if self.0.is_witness() {
|
||||||
let desciptor_key: DescriptorKey<miniscript::Segwitv0> =
|
let desciptor_key: DescriptorKey<miniscript::Segwitv0> =
|
||||||
pk.clone().to_descriptor_key()?;
|
pk.clone().to_descriptor_key()?;
|
||||||
desciptor_key.extract()?
|
desciptor_key.extract(&secp)?
|
||||||
} else {
|
} else {
|
||||||
let desciptor_key: DescriptorKey<miniscript::Legacy> =
|
let desciptor_key: DescriptorKey<miniscript::Legacy> =
|
||||||
pk.clone().to_descriptor_key()?;
|
pk.clone().to_descriptor_key()?;
|
||||||
desciptor_key.extract()?
|
desciptor_key.extract(&secp)?
|
||||||
};
|
};
|
||||||
|
|
||||||
if networks.contains(&network) {
|
if networks.contains(&network) {
|
||||||
@ -185,12 +188,16 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap, ValidNetworks) {
|
|||||||
|
|
||||||
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
|
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
|
||||||
pub trait ExtractPolicy {
|
pub trait ExtractPolicy {
|
||||||
fn extract_policy(&self, signers: Arc<SignersContainer>) -> Result<Option<Policy>, Error>;
|
fn extract_policy(
|
||||||
|
&self,
|
||||||
|
signers: Arc<SignersContainer>,
|
||||||
|
secp: &SecpCtx,
|
||||||
|
) -> Result<Option<Policy>, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait XKeyUtils {
|
pub(crate) trait XKeyUtils {
|
||||||
fn full_path(&self, append: &[ChildNumber]) -> DerivationPath;
|
fn full_path(&self, append: &[ChildNumber]) -> DerivationPath;
|
||||||
fn root_fingerprint(&self) -> Fingerprint;
|
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
|
impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
|
||||||
@ -215,46 +222,55 @@ impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_fingerprint(&self) -> Fingerprint {
|
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
|
||||||
match self.origin {
|
match self.origin {
|
||||||
Some((fingerprint, _)) => fingerprint,
|
Some((fingerprint, _)) => fingerprint,
|
||||||
None => self.xkey.xkey_fingerprint(),
|
None => self.xkey.xkey_fingerprint(secp),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait DescriptorMeta: Sized {
|
pub(crate) trait DescriptorMeta: Sized {
|
||||||
fn is_witness(&self) -> bool;
|
fn is_witness(&self) -> bool;
|
||||||
fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error>;
|
fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error>;
|
||||||
fn is_fixed(&self) -> bool;
|
fn is_fixed(&self) -> bool;
|
||||||
fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option<Self>;
|
fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self>;
|
||||||
fn derive_from_psbt_input(&self, psbt_input: &psbt::Input, utxo: Option<TxOut>)
|
fn derive_from_psbt_input(
|
||||||
-> Option<Self>;
|
&self,
|
||||||
|
psbt_input: &psbt::Input,
|
||||||
|
utxo: Option<TxOut>,
|
||||||
|
secp: &SecpCtx,
|
||||||
|
) -> Option<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait DescriptorScripts {
|
pub(crate) trait DescriptorScripts {
|
||||||
fn psbt_redeem_script(&self) -> Option<Script>;
|
fn psbt_redeem_script(&self, secp: &SecpCtx) -> Option<Script>;
|
||||||
fn psbt_witness_script(&self) -> Option<Script>;
|
fn psbt_witness_script(&self, secp: &SecpCtx) -> Option<Script>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DescriptorScripts for Descriptor<T>
|
impl DescriptorScripts for Descriptor<DescriptorPublicKey> {
|
||||||
where
|
fn psbt_redeem_script(&self, secp: &SecpCtx) -> Option<Script> {
|
||||||
T: miniscript::MiniscriptKey + miniscript::ToPublicKey,
|
let deriv_ctx = descriptor_to_pk_ctx(secp);
|
||||||
{
|
|
||||||
fn psbt_redeem_script(&self) -> Option<Script> {
|
|
||||||
match self {
|
match self {
|
||||||
Descriptor::ShWpkh(_) => Some(self.witness_script()),
|
Descriptor::ShWpkh(_) => Some(self.witness_script(deriv_ctx)),
|
||||||
Descriptor::ShWsh(ref script) => Some(script.encode().to_v0_p2wsh()),
|
Descriptor::ShWsh(ref script) => Some(script.encode(deriv_ctx).to_v0_p2wsh()),
|
||||||
Descriptor::Sh(ref script) => Some(script.encode()),
|
Descriptor::Sh(ref script) => Some(script.encode(deriv_ctx)),
|
||||||
Descriptor::Bare(ref script) => Some(script.encode()),
|
Descriptor::Bare(ref script) => Some(script.encode(deriv_ctx)),
|
||||||
|
Descriptor::ShSortedMulti(ref keys) => Some(keys.encode(deriv_ctx)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn psbt_witness_script(&self) -> Option<Script> {
|
fn psbt_witness_script(&self, secp: &SecpCtx) -> Option<Script> {
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(secp);
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Descriptor::Wsh(ref script) => Some(script.encode()),
|
Descriptor::Wsh(ref script) => Some(script.encode(deriv_ctx)),
|
||||||
Descriptor::ShWsh(ref script) => Some(script.encode()),
|
Descriptor::ShWsh(ref script) => Some(script.encode(deriv_ctx)),
|
||||||
|
Descriptor::WshSortedMulti(ref keys) | Descriptor::ShWshSortedMulti(ref keys) => {
|
||||||
|
Some(keys.encode(deriv_ctx))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,22 +279,25 @@ where
|
|||||||
impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
||||||
fn is_witness(&self) -> bool {
|
fn is_witness(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Descriptor::Bare(_) | Descriptor::Pk(_) | Descriptor::Pkh(_) | Descriptor::Sh(_) => {
|
Descriptor::Bare(_)
|
||||||
false
|
| Descriptor::Pk(_)
|
||||||
}
|
| Descriptor::Pkh(_)
|
||||||
|
| Descriptor::Sh(_)
|
||||||
|
| Descriptor::ShSortedMulti(_) => false,
|
||||||
Descriptor::Wpkh(_)
|
Descriptor::Wpkh(_)
|
||||||
| Descriptor::ShWpkh(_)
|
| Descriptor::ShWpkh(_)
|
||||||
| Descriptor::Wsh(_)
|
| Descriptor::Wsh(_)
|
||||||
| Descriptor::ShWsh(_) => true,
|
| Descriptor::ShWsh(_)
|
||||||
|
| Descriptor::ShWshSortedMulti(_)
|
||||||
|
| Descriptor::WshSortedMulti(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error> {
|
fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error> {
|
||||||
fn translate_key(
|
let translate_key = |key: &DescriptorPublicKey,
|
||||||
key: &DescriptorPublicKey,
|
|
||||||
index: u32,
|
index: u32,
|
||||||
paths: &mut HDKeyPaths,
|
paths: &mut HDKeyPaths|
|
||||||
) -> Result<DummyKey, Error> {
|
-> Result<DummyKey, Error> {
|
||||||
match key {
|
match key {
|
||||||
DescriptorPublicKey::SinglePub(_) => {}
|
DescriptorPublicKey::SinglePub(_) => {}
|
||||||
DescriptorPublicKey::XPub(xpub) => {
|
DescriptorPublicKey::XPub(xpub) => {
|
||||||
@ -298,7 +317,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
|||||||
paths.insert(
|
paths.insert(
|
||||||
derived_pubkey.public_key,
|
derived_pubkey.public_key,
|
||||||
(
|
(
|
||||||
xpub.root_fingerprint(),
|
xpub.root_fingerprint(secp),
|
||||||
xpub.full_path(&[ChildNumber::from_normal_idx(index)?]),
|
xpub.full_path(&[ChildNumber::from_normal_idx(index)?]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -306,7 +325,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(DummyKey::default())
|
Ok(DummyKey::default())
|
||||||
}
|
};
|
||||||
|
|
||||||
let mut answer_pk = BTreeMap::new();
|
let mut answer_pk = BTreeMap::new();
|
||||||
let mut answer_pkh = BTreeMap::new();
|
let mut answer_pkh = BTreeMap::new();
|
||||||
@ -347,12 +366,11 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
|||||||
!found_wildcard_pk && !found_wildcard_pkh
|
!found_wildcard_pk && !found_wildcard_pkh
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option<Self> {
|
fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self> {
|
||||||
fn try_key(
|
let try_key = |key: &DescriptorPublicKey,
|
||||||
key: &DescriptorPublicKey,
|
|
||||||
index: &HashMap<Fingerprint, DerivationPath>,
|
index: &HashMap<Fingerprint, DerivationPath>,
|
||||||
found_path: &mut Option<ChildNumber>,
|
found_path: &mut Option<ChildNumber>|
|
||||||
) -> Result<DummyKey, Error> {
|
-> Result<DummyKey, Error> {
|
||||||
if found_path.is_some() {
|
if found_path.is_some() {
|
||||||
// already found a matching path, we are done
|
// already found a matching path, we are done
|
||||||
return Ok(DummyKey::default());
|
return Ok(DummyKey::default());
|
||||||
@ -363,13 +381,15 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
|||||||
// return the "prefix" that matched, so we remove that prefix from the full path
|
// return the "prefix" that matched, so we remove that prefix from the full path
|
||||||
// found in `index` and save it in `derive_path`. We expect this to be a derivation
|
// found in `index` and save it in `derive_path`. We expect this to be a derivation
|
||||||
// path of length 1 if the key `is_wildcard` and an empty path otherwise.
|
// path of length 1 if the key `is_wildcard` and an empty path otherwise.
|
||||||
let root_fingerprint = xpub.root_fingerprint();
|
let root_fingerprint = xpub.root_fingerprint(secp);
|
||||||
let derivation_path: Option<Vec<ChildNumber>> = index
|
let derivation_path: Option<Vec<ChildNumber>> = index
|
||||||
.get_key_value(&root_fingerprint)
|
.get_key_value(&root_fingerprint)
|
||||||
.and_then(|(fingerprint, path)| xpub.matches(*fingerprint, path))
|
.and_then(|(fingerprint, path)| {
|
||||||
|
xpub.matches(&(*fingerprint, path.clone()), secp)
|
||||||
|
})
|
||||||
.map(|prefix| {
|
.map(|prefix| {
|
||||||
index
|
index
|
||||||
.get(&xpub.root_fingerprint())
|
.get(&xpub.root_fingerprint(secp))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(prefix.into_iter().count())
|
.skip(prefix.into_iter().count())
|
||||||
@ -422,8 +442,9 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
|||||||
&self,
|
&self,
|
||||||
psbt_input: &psbt::Input,
|
psbt_input: &psbt::Input,
|
||||||
utxo: Option<TxOut>,
|
utxo: Option<TxOut>,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.hd_keypaths) {
|
if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.hd_keypaths, secp) {
|
||||||
return Some(derived);
|
return Some(derived);
|
||||||
} else if !self.is_fixed() {
|
} else if !self.is_fixed() {
|
||||||
// If the descriptor is not fixed we can't brute-force the derivation address, so just
|
// If the descriptor is not fixed we can't brute-force the derivation address, so just
|
||||||
@ -431,25 +452,38 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(secp);
|
||||||
match self {
|
match self {
|
||||||
Descriptor::Pk(_)
|
Descriptor::Pk(_)
|
||||||
| Descriptor::Pkh(_)
|
| Descriptor::Pkh(_)
|
||||||
| Descriptor::Wpkh(_)
|
| Descriptor::Wpkh(_)
|
||||||
| Descriptor::ShWpkh(_)
|
| Descriptor::ShWpkh(_)
|
||||||
if utxo.is_some()
|
if utxo.is_some()
|
||||||
&& self.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
|
&& self.script_pubkey(deriv_ctx) == utxo.as_ref().unwrap().script_pubkey =>
|
||||||
{
|
{
|
||||||
Some(self.clone())
|
Some(self.clone())
|
||||||
}
|
}
|
||||||
Descriptor::Bare(ms) | Descriptor::Sh(ms)
|
Descriptor::Bare(ms) | Descriptor::Sh(ms)
|
||||||
if psbt_input.redeem_script.is_some()
|
if psbt_input.redeem_script.is_some()
|
||||||
&& &ms.encode() == psbt_input.redeem_script.as_ref().unwrap() =>
|
&& &ms.encode(deriv_ctx) == psbt_input.redeem_script.as_ref().unwrap() =>
|
||||||
{
|
{
|
||||||
Some(self.clone())
|
Some(self.clone())
|
||||||
}
|
}
|
||||||
Descriptor::Wsh(ms) | Descriptor::ShWsh(ms)
|
Descriptor::Wsh(ms) | Descriptor::ShWsh(ms)
|
||||||
if psbt_input.witness_script.is_some()
|
if psbt_input.witness_script.is_some()
|
||||||
&& &ms.encode() == psbt_input.witness_script.as_ref().unwrap() =>
|
&& &ms.encode(deriv_ctx) == psbt_input.witness_script.as_ref().unwrap() =>
|
||||||
|
{
|
||||||
|
Some(self.clone())
|
||||||
|
}
|
||||||
|
Descriptor::ShSortedMulti(keys)
|
||||||
|
if psbt_input.redeem_script.is_some()
|
||||||
|
&& &keys.encode(deriv_ctx) == psbt_input.redeem_script.as_ref().unwrap() =>
|
||||||
|
{
|
||||||
|
Some(self.clone())
|
||||||
|
}
|
||||||
|
Descriptor::WshSortedMulti(keys) | Descriptor::ShWshSortedMulti(keys)
|
||||||
|
if psbt_input.witness_script.is_some()
|
||||||
|
&& &keys.encode(deriv_ctx) == psbt_input.witness_script.as_ref().unwrap() =>
|
||||||
{
|
{
|
||||||
Some(self.clone())
|
Some(self.clone())
|
||||||
}
|
}
|
||||||
@ -489,6 +523,7 @@ mod test {
|
|||||||
|
|
||||||
use bitcoin::consensus::encode::deserialize;
|
use bitcoin::consensus::encode::deserialize;
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::util::{bip32, psbt};
|
use bitcoin::util::{bip32, psbt};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -513,7 +548,7 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(descriptor
|
assert!(descriptor
|
||||||
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
||||||
.is_some());
|
.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,7 +579,7 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(descriptor
|
assert!(descriptor
|
||||||
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
||||||
.is_some());
|
.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +603,7 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(descriptor
|
assert!(descriptor
|
||||||
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
||||||
.is_some());
|
.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,7 +633,7 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(descriptor
|
assert!(descriptor
|
||||||
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
||||||
.is_some());
|
.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,13 +32,15 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use std::sync::Arc;
|
//! # use std::sync::Arc;
|
||||||
//! # use bdk::descriptor::*;
|
//! # use bdk::descriptor::*;
|
||||||
|
//! # use bdk::bitcoin::secp256k1::Secp256k1;
|
||||||
|
//! let secp = Secp256k1::new();
|
||||||
//! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
|
//! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
|
||||||
//!
|
//!
|
||||||
//! let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc)?;
|
//! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(desc)?;
|
||||||
//! println!("{:?}", extended_desc);
|
//! println!("{:?}", extended_desc);
|
||||||
//!
|
//!
|
||||||
//! let signers = Arc::new(key_map.into());
|
//! let signers = Arc::new(key_map.into());
|
||||||
//! let policy = extended_desc.extract_policy(signers)?;
|
//! let policy = extended_desc.extract_policy(signers, &secp)?;
|
||||||
//! println!("policy: {}", serde_json::to_string(&policy)?);
|
//! println!("policy: {}", serde_json::to_string(&policy)?);
|
||||||
//! # Ok::<(), bdk::Error>(())
|
//! # Ok::<(), bdk::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
@ -55,7 +57,7 @@ use bitcoin::hashes::*;
|
|||||||
use bitcoin::util::bip32::Fingerprint;
|
use bitcoin::util::bip32::Fingerprint;
|
||||||
use bitcoin::PublicKey;
|
use bitcoin::PublicKey;
|
||||||
|
|
||||||
use miniscript::descriptor::DescriptorPublicKey;
|
use miniscript::descriptor::{DescriptorPublicKey, SortedMultiVec};
|
||||||
use miniscript::{Descriptor, Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey};
|
use miniscript::{Descriptor, Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey};
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
@ -63,6 +65,7 @@ use log::{debug, error, info, trace};
|
|||||||
|
|
||||||
use crate::descriptor::ExtractPolicy;
|
use crate::descriptor::ExtractPolicy;
|
||||||
use crate::wallet::signer::{SignerId, SignersContainer};
|
use crate::wallet::signer::{SignerId, SignersContainer};
|
||||||
|
use crate::wallet::utils::{descriptor_to_pk_ctx, SecpCtx};
|
||||||
|
|
||||||
use super::checksum::get_checksum;
|
use super::checksum::get_checksum;
|
||||||
use super::error::Error;
|
use super::error::Error;
|
||||||
@ -80,14 +83,14 @@ pub struct PKOrF {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PKOrF {
|
impl PKOrF {
|
||||||
fn from_key(k: &DescriptorPublicKey) -> Self {
|
fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
|
||||||
match k {
|
match k {
|
||||||
DescriptorPublicKey::SinglePub(pubkey) => PKOrF {
|
DescriptorPublicKey::SinglePub(pubkey) => PKOrF {
|
||||||
pubkey: Some(pubkey.key),
|
pubkey: Some(pubkey.key),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
DescriptorPublicKey::XPub(xpub) => PKOrF {
|
DescriptorPublicKey::XPub(xpub) => PKOrF {
|
||||||
fingerprint: Some(xpub.root_fingerprint()),
|
fingerprint: Some(xpub.root_fingerprint(secp)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -241,6 +244,9 @@ pub enum Satisfaction {
|
|||||||
m: usize,
|
m: usize,
|
||||||
/// The items that can be satisfied by the descriptor
|
/// The items that can be satisfied by the descriptor
|
||||||
items: Vec<usize>,
|
items: Vec<usize>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
|
||||||
|
sorted: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
||||||
/// Extra conditions that also need to be satisfied
|
/// Extra conditions that also need to be satisfied
|
||||||
conditions: ConditionMap,
|
conditions: ConditionMap,
|
||||||
@ -253,6 +259,9 @@ pub enum Satisfaction {
|
|||||||
m: usize,
|
m: usize,
|
||||||
/// The items that can be satisfied by the descriptor
|
/// The items that can be satisfied by the descriptor
|
||||||
items: Vec<usize>,
|
items: Vec<usize>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
|
||||||
|
sorted: Option<bool>,
|
||||||
#[serde(
|
#[serde(
|
||||||
serialize_with = "serialize_folded_cond_map",
|
serialize_with = "serialize_folded_cond_map",
|
||||||
skip_serializing_if = "BTreeMap::is_empty"
|
skip_serializing_if = "BTreeMap::is_empty"
|
||||||
@ -324,6 +333,7 @@ impl Satisfaction {
|
|||||||
m,
|
m,
|
||||||
items,
|
items,
|
||||||
conditions,
|
conditions,
|
||||||
|
sorted,
|
||||||
} = self
|
} = self
|
||||||
{
|
{
|
||||||
if items.len() >= *m {
|
if items.len() >= *m {
|
||||||
@ -378,6 +388,7 @@ impl Satisfaction {
|
|||||||
m: *m,
|
m: *m,
|
||||||
items: items.clone(),
|
items: items.clone(),
|
||||||
conditions: map,
|
conditions: map,
|
||||||
|
sorted: *sorted,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,6 +521,7 @@ impl Policy {
|
|||||||
m: threshold,
|
m: threshold,
|
||||||
items: vec![],
|
items: vec![],
|
||||||
conditions: Default::default(),
|
conditions: Default::default(),
|
||||||
|
sorted: None,
|
||||||
};
|
};
|
||||||
for (index, item) in items.iter().enumerate() {
|
for (index, item) in items.iter().enumerate() {
|
||||||
contribution.add(&item.contribution, index)?;
|
contribution.add(&item.contribution, index)?;
|
||||||
@ -526,21 +538,24 @@ impl Policy {
|
|||||||
keys: &[DescriptorPublicKey],
|
keys: &[DescriptorPublicKey],
|
||||||
signers: Arc<SignersContainer>,
|
signers: Arc<SignersContainer>,
|
||||||
threshold: usize,
|
threshold: usize,
|
||||||
|
sorted: bool,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Result<Option<Policy>, PolicyError> {
|
) -> Result<Option<Policy>, PolicyError> {
|
||||||
if threshold == 0 {
|
if threshold == 0 {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed_keys = keys.iter().map(|k| PKOrF::from_key(k)).collect();
|
let parsed_keys = keys.iter().map(|k| PKOrF::from_key(k, secp)).collect();
|
||||||
|
|
||||||
let mut contribution = Satisfaction::Partial {
|
let mut contribution = Satisfaction::Partial {
|
||||||
n: keys.len(),
|
n: keys.len(),
|
||||||
m: threshold,
|
m: threshold,
|
||||||
items: vec![],
|
items: vec![],
|
||||||
conditions: Default::default(),
|
conditions: Default::default(),
|
||||||
|
sorted: Some(sorted),
|
||||||
};
|
};
|
||||||
for (index, key) in keys.iter().enumerate() {
|
for (index, key) in keys.iter().enumerate() {
|
||||||
if signers.find(signer_id(key)).is_some() {
|
if signers.find(signer_id(key, secp)).is_some() {
|
||||||
contribution.add(
|
contribution.add(
|
||||||
&Satisfaction::Complete {
|
&Satisfaction::Complete {
|
||||||
condition: Default::default(),
|
condition: Default::default(),
|
||||||
@ -648,17 +663,17 @@ impl From<SatisfiableItem> for Policy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signer_id(key: &DescriptorPublicKey) -> SignerId {
|
fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
|
||||||
match key {
|
match key {
|
||||||
DescriptorPublicKey::SinglePub(pubkey) => pubkey.key.to_pubkeyhash().into(),
|
DescriptorPublicKey::SinglePub(pubkey) => pubkey.key.to_pubkeyhash().into(),
|
||||||
DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint().into(),
|
DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>) -> Policy {
|
fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>, secp: &SecpCtx) -> Policy {
|
||||||
let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key(key)).into();
|
let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key(key, secp)).into();
|
||||||
|
|
||||||
policy.contribution = if signers.find(signer_id(key)).is_some() {
|
policy.contribution = if signers.find(signer_id(key, secp)).is_some() {
|
||||||
Satisfaction::Complete {
|
Satisfaction::Complete {
|
||||||
condition: Default::default(),
|
condition: Default::default(),
|
||||||
}
|
}
|
||||||
@ -672,8 +687,10 @@ fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>) -> Polic
|
|||||||
fn signature_key(
|
fn signature_key(
|
||||||
key: &<DescriptorPublicKey as MiniscriptKey>::Hash,
|
key: &<DescriptorPublicKey as MiniscriptKey>::Hash,
|
||||||
signers: Arc<SignersContainer>,
|
signers: Arc<SignersContainer>,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Policy {
|
) -> Policy {
|
||||||
let key_hash = key.to_public_key().to_pubkeyhash();
|
let deriv_ctx = descriptor_to_pk_ctx(secp);
|
||||||
|
let key_hash = key.to_public_key(deriv_ctx).to_pubkeyhash();
|
||||||
let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key_hash(key_hash)).into();
|
let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key_hash(key_hash)).into();
|
||||||
|
|
||||||
if signers.find(SignerId::PkHash(key_hash)).is_some() {
|
if signers.find(SignerId::PkHash(key_hash)).is_some() {
|
||||||
@ -686,12 +703,18 @@ fn signature_key(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
|
impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
|
||||||
fn extract_policy(&self, signers: Arc<SignersContainer>) -> Result<Option<Policy>, Error> {
|
fn extract_policy(
|
||||||
|
&self,
|
||||||
|
signers: Arc<SignersContainer>,
|
||||||
|
secp: &SecpCtx,
|
||||||
|
) -> Result<Option<Policy>, Error> {
|
||||||
Ok(match &self.node {
|
Ok(match &self.node {
|
||||||
// Leaves
|
// Leaves
|
||||||
Terminal::True | Terminal::False => None,
|
Terminal::True | Terminal::False => None,
|
||||||
Terminal::PkK(pubkey) => Some(signature(pubkey, Arc::clone(&signers))),
|
Terminal::PkK(pubkey) => Some(signature(pubkey, Arc::clone(&signers), secp)),
|
||||||
Terminal::PkH(pubkey_hash) => Some(signature_key(pubkey_hash, Arc::clone(&signers))),
|
Terminal::PkH(pubkey_hash) => {
|
||||||
|
Some(signature_key(pubkey_hash, Arc::clone(&signers), secp))
|
||||||
|
}
|
||||||
Terminal::After(value) => {
|
Terminal::After(value) => {
|
||||||
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into();
|
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into();
|
||||||
policy.contribution = Satisfaction::Complete {
|
policy.contribution = Satisfaction::Complete {
|
||||||
@ -724,7 +747,9 @@ impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx>
|
|||||||
Terminal::Hash160(hash) => {
|
Terminal::Hash160(hash) => {
|
||||||
Some(SatisfiableItem::HASH160Preimage { hash: *hash }.into())
|
Some(SatisfiableItem::HASH160Preimage { hash: *hash }.into())
|
||||||
}
|
}
|
||||||
Terminal::Multi(k, pks) => Policy::make_multisig(pks, Arc::clone(&signers), *k)?,
|
Terminal::Multi(k, pks) => {
|
||||||
|
Policy::make_multisig(pks, Arc::clone(&signers), *k, false, secp)?
|
||||||
|
}
|
||||||
// Identities
|
// Identities
|
||||||
Terminal::Alt(inner)
|
Terminal::Alt(inner)
|
||||||
| Terminal::Swap(inner)
|
| Terminal::Swap(inner)
|
||||||
@ -732,31 +757,31 @@ impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx>
|
|||||||
| Terminal::DupIf(inner)
|
| Terminal::DupIf(inner)
|
||||||
| Terminal::Verify(inner)
|
| Terminal::Verify(inner)
|
||||||
| Terminal::NonZero(inner)
|
| Terminal::NonZero(inner)
|
||||||
| Terminal::ZeroNotEqual(inner) => inner.extract_policy(Arc::clone(&signers))?,
|
| Terminal::ZeroNotEqual(inner) => inner.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
// Complex policies
|
// Complex policies
|
||||||
Terminal::AndV(a, b) | Terminal::AndB(a, b) => Policy::make_and(
|
Terminal::AndV(a, b) | Terminal::AndB(a, b) => Policy::make_and(
|
||||||
a.extract_policy(Arc::clone(&signers))?,
|
a.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
b.extract_policy(Arc::clone(&signers))?,
|
b.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
)?,
|
)?,
|
||||||
Terminal::AndOr(x, y, z) => Policy::make_or(
|
Terminal::AndOr(x, y, z) => Policy::make_or(
|
||||||
Policy::make_and(
|
Policy::make_and(
|
||||||
x.extract_policy(Arc::clone(&signers))?,
|
x.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
y.extract_policy(Arc::clone(&signers))?,
|
y.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
)?,
|
)?,
|
||||||
z.extract_policy(Arc::clone(&signers))?,
|
z.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
)?,
|
)?,
|
||||||
Terminal::OrB(a, b)
|
Terminal::OrB(a, b)
|
||||||
| Terminal::OrD(a, b)
|
| Terminal::OrD(a, b)
|
||||||
| Terminal::OrC(a, b)
|
| Terminal::OrC(a, b)
|
||||||
| Terminal::OrI(a, b) => Policy::make_or(
|
| Terminal::OrI(a, b) => Policy::make_or(
|
||||||
a.extract_policy(Arc::clone(&signers))?,
|
a.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
b.extract_policy(Arc::clone(&signers))?,
|
b.extract_policy(Arc::clone(&signers), secp)?,
|
||||||
)?,
|
)?,
|
||||||
Terminal::Thresh(k, nodes) => {
|
Terminal::Thresh(k, nodes) => {
|
||||||
let mut threshold = *k;
|
let mut threshold = *k;
|
||||||
let mapped: Vec<_> = nodes
|
let mapped: Vec<_> = nodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|n| n.extract_policy(Arc::clone(&signers)))
|
.map(|n| n.extract_policy(Arc::clone(&signers), secp))
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|x| x)
|
.filter_map(|x| x)
|
||||||
@ -776,14 +801,42 @@ impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
|
impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
|
||||||
fn extract_policy(&self, signers: Arc<SignersContainer>) -> Result<Option<Policy>, Error> {
|
fn extract_policy(
|
||||||
|
&self,
|
||||||
|
signers: Arc<SignersContainer>,
|
||||||
|
secp: &SecpCtx,
|
||||||
|
) -> Result<Option<Policy>, Error> {
|
||||||
|
fn make_sortedmulti<Ctx: ScriptContext>(
|
||||||
|
keys: &SortedMultiVec<DescriptorPublicKey, Ctx>,
|
||||||
|
signers: Arc<SignersContainer>,
|
||||||
|
secp: &SecpCtx,
|
||||||
|
) -> Result<Option<Policy>, Error> {
|
||||||
|
Ok(Policy::make_multisig(
|
||||||
|
keys.pks.as_ref(),
|
||||||
|
signers,
|
||||||
|
keys.k,
|
||||||
|
true,
|
||||||
|
secp,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Descriptor::Pk(pubkey)
|
Descriptor::Pk(pubkey)
|
||||||
| Descriptor::Pkh(pubkey)
|
| Descriptor::Pkh(pubkey)
|
||||||
| Descriptor::Wpkh(pubkey)
|
| Descriptor::Wpkh(pubkey)
|
||||||
| Descriptor::ShWpkh(pubkey) => Ok(Some(signature(pubkey, signers))),
|
| Descriptor::ShWpkh(pubkey) => Ok(Some(signature(pubkey, signers, secp))),
|
||||||
Descriptor::Bare(inner) | Descriptor::Sh(inner) => Ok(inner.extract_policy(signers)?),
|
Descriptor::Bare(inner) | Descriptor::Sh(inner) => {
|
||||||
Descriptor::Wsh(inner) | Descriptor::ShWsh(inner) => Ok(inner.extract_policy(signers)?),
|
Ok(inner.extract_policy(signers, secp)?)
|
||||||
|
}
|
||||||
|
Descriptor::Wsh(inner) | Descriptor::ShWsh(inner) => {
|
||||||
|
Ok(inner.extract_policy(signers, secp)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `sortedmulti()` is handled separately
|
||||||
|
Descriptor::ShSortedMulti(keys) => make_sortedmulti(&keys, signers, secp),
|
||||||
|
Descriptor::ShWshSortedMulti(keys) | Descriptor::WshSortedMulti(keys) => {
|
||||||
|
make_sortedmulti(&keys, signers, secp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -833,7 +886,7 @@ mod test {
|
|||||||
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = wallet_desc
|
let policy = wallet_desc
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -846,7 +899,7 @@ mod test {
|
|||||||
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = wallet_desc
|
let policy = wallet_desc
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -926,7 +979,7 @@ mod test {
|
|||||||
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = wallet_desc
|
let policy = wallet_desc
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -936,7 +989,7 @@ mod test {
|
|||||||
&& &keys[1].fingerprint.unwrap() == &fingerprint1)
|
&& &keys[1].fingerprint.unwrap() == &fingerprint1)
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions} if n == &2
|
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
|
||||||
&& m == &1
|
&& m == &1
|
||||||
&& items.len() == 2
|
&& items.len() == 2
|
||||||
&& conditions.contains_key(&vec![0])
|
&& conditions.contains_key(&vec![0])
|
||||||
@ -954,7 +1007,7 @@ mod test {
|
|||||||
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = wallet_desc
|
let policy = wallet_desc
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -965,7 +1018,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions} if n == &2
|
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
|
||||||
&& m == &2
|
&& m == &2
|
||||||
&& items.len() == 2
|
&& items.len() == 2
|
||||||
&& conditions.contains_key(&vec![0,1])
|
&& conditions.contains_key(&vec![0,1])
|
||||||
@ -983,7 +1036,7 @@ mod test {
|
|||||||
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
|
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = single_key
|
let policy = single_key
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -997,7 +1050,7 @@ mod test {
|
|||||||
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
|
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = single_key
|
let policy = single_key
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1019,7 +1072,7 @@ mod test {
|
|||||||
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
|
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = single_key
|
let policy = single_key
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1029,7 +1082,7 @@ mod test {
|
|||||||
&& &keys[1].fingerprint.unwrap() == &fingerprint1)
|
&& &keys[1].fingerprint.unwrap() == &fingerprint1)
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions } if n == &2
|
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
|
||||||
&& m == &1
|
&& m == &1
|
||||||
&& items.len() == 2
|
&& items.len() == 2
|
||||||
&& conditions.contains_key(&vec![0])
|
&& conditions.contains_key(&vec![0])
|
||||||
@ -1053,7 +1106,7 @@ mod test {
|
|||||||
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
|
||||||
let signers_container = Arc::new(SignersContainer::from(keymap));
|
let signers_container = Arc::new(SignersContainer::from(keymap));
|
||||||
let policy = wallet_desc
|
let policy = wallet_desc
|
||||||
.extract_policy(signers_container)
|
.extract_policy(signers_container, &Secp256k1::new())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -1062,7 +1115,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions } if n == &3
|
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3
|
||||||
&& m == &2
|
&& m == &2
|
||||||
&& items.len() == 3
|
&& items.len() == 3
|
||||||
&& conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
|
&& conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
|
||||||
|
@ -432,8 +432,9 @@ mod test {
|
|||||||
use crate::keys::{KeyError, ValidNetworks};
|
use crate::keys::{KeyError, ValidNetworks};
|
||||||
use bitcoin::hashes::core::str::FromStr;
|
use bitcoin::hashes::core::str::FromStr;
|
||||||
use bitcoin::network::constants::Network::Regtest;
|
use bitcoin::network::constants::Network::Regtest;
|
||||||
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::util::bip32::ChildNumber;
|
use bitcoin::util::bip32::ChildNumber;
|
||||||
use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
|
use miniscript::descriptor::{DescriptorPublicKey, DescriptorPublicKeyCtx, KeyMap};
|
||||||
use miniscript::Descriptor;
|
use miniscript::Descriptor;
|
||||||
|
|
||||||
// verify template descriptor generates expected address(es)
|
// verify template descriptor generates expected address(es)
|
||||||
@ -443,6 +444,10 @@ mod test {
|
|||||||
is_fixed: bool,
|
is_fixed: bool,
|
||||||
expected: &[&str],
|
expected: &[&str],
|
||||||
) {
|
) {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
let deriv_ctx =
|
||||||
|
DescriptorPublicKeyCtx::new(&secp, ChildNumber::from_normal_idx(0).unwrap());
|
||||||
|
|
||||||
let (desc, _key_map, _networks) = desc.unwrap();
|
let (desc, _key_map, _networks) = desc.unwrap();
|
||||||
assert_eq!(desc.is_witness(), is_witness);
|
assert_eq!(desc.is_witness(), is_witness);
|
||||||
assert_eq!(desc.is_fixed(), is_fixed);
|
assert_eq!(desc.is_fixed(), is_fixed);
|
||||||
@ -453,7 +458,7 @@ mod test {
|
|||||||
} else {
|
} else {
|
||||||
desc.derive(ChildNumber::from_normal_idx(index).unwrap())
|
desc.derive(ChildNumber::from_normal_idx(index).unwrap())
|
||||||
};
|
};
|
||||||
let address = child_desc.address(Regtest).unwrap();
|
let address = child_desc.address(Regtest, deriv_ctx).unwrap();
|
||||||
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,8 @@ use miniscript::descriptor::{DescriptorXKey, KeyMap};
|
|||||||
pub use miniscript::ScriptContext;
|
pub use miniscript::ScriptContext;
|
||||||
use miniscript::{Miniscript, Terminal};
|
use miniscript::{Miniscript, Terminal};
|
||||||
|
|
||||||
|
use crate::wallet::utils::SecpCtx;
|
||||||
|
|
||||||
#[cfg(feature = "keys-bip39")]
|
#[cfg(feature = "keys-bip39")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
|
||||||
pub mod bip39;
|
pub mod bip39;
|
||||||
@ -101,7 +103,10 @@ impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
|
|||||||
// public because it is effectively called by external crates, once the macros are expanded,
|
// public because it is effectively called by external crates, once the macros are expanded,
|
||||||
// but since it is not meant to be part of the public api we hide it from the docs.
|
// but since it is not meant to be part of the public api we hide it from the docs.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn extract(self) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
|
pub fn extract(
|
||||||
|
self,
|
||||||
|
secp: &SecpCtx,
|
||||||
|
) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
|
||||||
match self {
|
match self {
|
||||||
DescriptorKey::Public(public, valid_networks, _) => {
|
DescriptorKey::Public(public, valid_networks, _) => {
|
||||||
Ok((public, KeyMap::default(), valid_networks))
|
Ok((public, KeyMap::default(), valid_networks))
|
||||||
@ -110,7 +115,7 @@ impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
|
|||||||
let mut key_map = KeyMap::with_capacity(1);
|
let mut key_map = KeyMap::with_capacity(1);
|
||||||
|
|
||||||
let public = secret
|
let public = secret
|
||||||
.as_public()
|
.as_public(secp)
|
||||||
.map_err(|e| miniscript::Error::Unexpected(e.to_string()))?;
|
.map_err(|e| miniscript::Error::Unexpected(e.to_string()))?;
|
||||||
key_map.insert(public.clone(), secret);
|
key_map.insert(public.clone(), secret);
|
||||||
|
|
||||||
@ -529,8 +534,9 @@ impl<Ctx: ScriptContext, T: DerivableKey<Ctx>> ToDescriptorKey<Ctx>
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
|
pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
|
||||||
descriptor_key: Pk,
|
descriptor_key: Pk,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
|
) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
|
||||||
let (key, key_map, valid_networks) = descriptor_key.to_descriptor_key()?.extract()?;
|
let (key, key_map, valid_networks) = descriptor_key.to_descriptor_key()?.extract(secp)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Miniscript::from_ast(Terminal::PkK(key))?,
|
Miniscript::from_ast(Terminal::PkK(key))?,
|
||||||
@ -544,10 +550,11 @@ pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
|
|||||||
pub fn make_multi<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
|
pub fn make_multi<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
|
||||||
thresh: usize,
|
thresh: usize,
|
||||||
pks: Vec<Pk>,
|
pks: Vec<Pk>,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
|
) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
|
||||||
let (pks, key_maps_networks): (Vec<_>, Vec<_>) = pks
|
let (pks, key_maps_networks): (Vec<_>, Vec<_>) = pks
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|key| Ok::<_, KeyError>(key.to_descriptor_key()?.extract()?))
|
.map(|key| Ok::<_, KeyError>(key.to_descriptor_key()?.extract(secp)?))
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(a, b, c)| (a, (b, c)))
|
.map(|(a, b, c)| (a, (b, c)))
|
||||||
|
@ -71,7 +71,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use miniscript::{Descriptor, ScriptContext, Terminal};
|
use miniscript::{Descriptor, DescriptorPublicKey, ScriptContext, Terminal};
|
||||||
|
|
||||||
use crate::blockchain::BlockchainMarker;
|
use crate::blockchain::BlockchainMarker;
|
||||||
use crate::database::BatchDatabase;
|
use crate::database::BatchDatabase;
|
||||||
@ -122,7 +122,7 @@ impl WalletExport {
|
|||||||
) -> Result<Self, &'static str> {
|
) -> Result<Self, &'static str> {
|
||||||
let descriptor = wallet
|
let descriptor = wallet
|
||||||
.descriptor
|
.descriptor
|
||||||
.to_string_with_secret(&wallet.signers.as_key_map());
|
.to_string_with_secret(&wallet.signers.as_key_map(wallet.secp_ctx()));
|
||||||
Self::is_compatible_with_core(&descriptor)?;
|
Self::is_compatible_with_core(&descriptor)?;
|
||||||
|
|
||||||
let blockheight = match wallet.database.borrow().iter_txs(false) {
|
let blockheight = match wallet.database.borrow().iter_txs(false) {
|
||||||
@ -145,12 +145,10 @@ impl WalletExport {
|
|||||||
blockheight,
|
blockheight,
|
||||||
};
|
};
|
||||||
|
|
||||||
if export.change_descriptor()
|
let desc_to_string = |d: &Descriptor<DescriptorPublicKey>| {
|
||||||
!= wallet
|
d.to_string_with_secret(&wallet.change_signers.as_key_map(wallet.secp_ctx()))
|
||||||
.change_descriptor
|
};
|
||||||
.as_ref()
|
if export.change_descriptor() != wallet.change_descriptor.as_ref().map(desc_to_string) {
|
||||||
.map(|d| d.to_string_with_secret(&wallet.change_signers.as_key_map()))
|
|
||||||
{
|
|
||||||
return Err("Incompatible change descriptor");
|
return Err("Incompatible change descriptor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ use std::collections::{BTreeMap, HashSet};
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
|
|
||||||
use bitcoin::consensus::encode::serialize;
|
use bitcoin::consensus::encode::serialize;
|
||||||
use bitcoin::util::bip32::ChildNumber;
|
use bitcoin::util::bip32::ChildNumber;
|
||||||
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
|
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
|
||||||
@ -55,7 +57,7 @@ pub use utils::IsDust;
|
|||||||
use address_validator::AddressValidator;
|
use address_validator::AddressValidator;
|
||||||
use signer::{Signer, SignerId, SignerOrdering, SignersContainer};
|
use signer::{Signer, SignerId, SignerOrdering, SignersContainer};
|
||||||
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxBuilderContext};
|
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxBuilderContext};
|
||||||
use utils::{After, Older};
|
use utils::{descriptor_to_pk_ctx, After, Older, SecpCtx};
|
||||||
|
|
||||||
use crate::blockchain::{Blockchain, BlockchainMarker, OfflineBlockchain, Progress};
|
use crate::blockchain::{Blockchain, BlockchainMarker, OfflineBlockchain, Progress};
|
||||||
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
|
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
|
||||||
@ -97,6 +99,8 @@ pub struct Wallet<B: BlockchainMarker, D: BatchDatabase> {
|
|||||||
|
|
||||||
client: Option<B>,
|
client: Option<B>,
|
||||||
database: RefCell<D>,
|
database: RefCell<D>,
|
||||||
|
|
||||||
|
secp: SecpCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
// offline actions, always available
|
// offline actions, always available
|
||||||
@ -149,16 +153,19 @@ where
|
|||||||
|
|
||||||
client: None,
|
client: None,
|
||||||
database: RefCell::new(database),
|
database: RefCell::new(database),
|
||||||
|
|
||||||
|
secp: Secp256k1::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a newly generated address using the external descriptor
|
/// Return a newly generated address using the external descriptor
|
||||||
pub fn get_new_address(&self) -> Result<Address, Error> {
|
pub fn get_new_address(&self) -> Result<Address, Error> {
|
||||||
let index = self.fetch_and_increment_index(ScriptType::External)?;
|
let index = self.fetch_and_increment_index(ScriptType::External)?;
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
|
|
||||||
self.descriptor
|
self.descriptor
|
||||||
.derive(ChildNumber::from_normal_idx(index)?)
|
.derive(ChildNumber::from_normal_idx(index)?)
|
||||||
.address(self.network)
|
.address(self.network, deriv_ctx)
|
||||||
.ok_or(Error::ScriptDoesntHaveAddressForm)
|
.ok_or(Error::ScriptDoesntHaveAddressForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,14 +253,14 @@ where
|
|||||||
) -> Result<(PSBT, TransactionDetails), Error> {
|
) -> Result<(PSBT, TransactionDetails), Error> {
|
||||||
let external_policy = self
|
let external_policy = self
|
||||||
.descriptor
|
.descriptor
|
||||||
.extract_policy(Arc::clone(&self.signers))?
|
.extract_policy(Arc::clone(&self.signers), &self.secp)?
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let internal_policy = self
|
let internal_policy = self
|
||||||
.change_descriptor
|
.change_descriptor
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|desc| {
|
.map(|desc| {
|
||||||
Ok::<_, Error>(
|
Ok::<_, Error>(
|
||||||
desc.extract_policy(Arc::clone(&self.change_signers))?
|
desc.extract_policy(Arc::clone(&self.change_signers), &self.secp)?
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -601,14 +608,18 @@ where
|
|||||||
details.received -= removed_updatable_output.value;
|
details.received -= removed_updatable_output.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
|
|
||||||
let external_weight = self
|
let external_weight = self
|
||||||
.get_descriptor_for_script_type(ScriptType::External)
|
.get_descriptor_for_script_type(ScriptType::External)
|
||||||
.0
|
.0
|
||||||
.max_satisfaction_weight();
|
.max_satisfaction_weight(deriv_ctx)
|
||||||
|
.unwrap();
|
||||||
let internal_weight = self
|
let internal_weight = self
|
||||||
.get_descriptor_for_script_type(ScriptType::Internal)
|
.get_descriptor_for_script_type(ScriptType::Internal)
|
||||||
.0
|
.0
|
||||||
.max_satisfaction_weight();
|
.max_satisfaction_weight(deriv_ctx)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let original_sequence = tx.input[0].sequence;
|
let original_sequence = tx.input[0].sequence;
|
||||||
|
|
||||||
@ -801,10 +812,10 @@ where
|
|||||||
.chain(self.change_signers.signers().iter())
|
.chain(self.change_signers.signers().iter())
|
||||||
{
|
{
|
||||||
if signer.sign_whole_tx() {
|
if signer.sign_whole_tx() {
|
||||||
signer.sign(&mut psbt, None)?;
|
signer.sign(&mut psbt, None, &self.secp)?;
|
||||||
} else {
|
} else {
|
||||||
for index in 0..psbt.inputs.len() {
|
for index in 0..psbt.inputs.len() {
|
||||||
signer.sign(&mut psbt, Some(index))?;
|
signer.sign(&mut psbt, Some(index), &self.secp)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -816,12 +827,12 @@ where
|
|||||||
/// Return the spending policies for the wallet's descriptor
|
/// Return the spending policies for the wallet's descriptor
|
||||||
pub fn policies(&self, script_type: ScriptType) -> Result<Option<Policy>, Error> {
|
pub fn policies(&self, script_type: ScriptType) -> Result<Option<Policy>, Error> {
|
||||||
match (script_type, self.change_descriptor.as_ref()) {
|
match (script_type, self.change_descriptor.as_ref()) {
|
||||||
(ScriptType::External, _) => {
|
(ScriptType::External, _) => Ok(self
|
||||||
Ok(self.descriptor.extract_policy(Arc::clone(&self.signers))?)
|
.descriptor
|
||||||
}
|
.extract_policy(Arc::clone(&self.signers), &self.secp)?),
|
||||||
(ScriptType::Internal, None) => Ok(None),
|
(ScriptType::Internal, None) => Ok(None),
|
||||||
(ScriptType::Internal, Some(desc)) => {
|
(ScriptType::Internal, Some(desc)) => {
|
||||||
Ok(desc.extract_policy(Arc::clone(&self.change_signers))?)
|
Ok(desc.extract_policy(Arc::clone(&self.change_signers), &self.secp)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -877,22 +888,21 @@ where
|
|||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
desc
|
desc
|
||||||
} else if let Some(desc) = self
|
} else if let Some(desc) =
|
||||||
.descriptor
|
self.descriptor
|
||||||
.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n))
|
.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
|
||||||
{
|
{
|
||||||
desc
|
desc
|
||||||
} else if let Some(desc) = self
|
} else if let Some(desc) = self.change_descriptor.as_ref().and_then(|desc| {
|
||||||
.change_descriptor
|
desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
|
||||||
.as_ref()
|
}) {
|
||||||
.and_then(|desc| desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n)))
|
|
||||||
{
|
|
||||||
desc
|
desc
|
||||||
} else {
|
} else {
|
||||||
debug!("Couldn't find the right derived descriptor for input {}", n);
|
debug!("Couldn't find the right derived descriptor for input {}", n);
|
||||||
return Ok((psbt, false));
|
return Ok((psbt, false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
match desc.satisfy(
|
match desc.satisfy(
|
||||||
input,
|
input,
|
||||||
(
|
(
|
||||||
@ -900,6 +910,7 @@ where
|
|||||||
After::new(current_height, false),
|
After::new(current_height, false),
|
||||||
Older::new(current_height, create_height, false),
|
Older::new(current_height, create_height, false),
|
||||||
),
|
),
|
||||||
|
deriv_ctx,
|
||||||
) {
|
) {
|
||||||
Ok(_) => continue,
|
Ok(_) => continue,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -918,6 +929,10 @@ where
|
|||||||
Ok((psbt, true))
|
Ok((psbt, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn secp_ctx(&self) -> &SecpCtx {
|
||||||
|
&self.secp
|
||||||
|
}
|
||||||
|
|
||||||
// Internals
|
// Internals
|
||||||
|
|
||||||
fn get_descriptor_for_script_type(
|
fn get_descriptor_for_script_type(
|
||||||
@ -943,12 +958,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_change_address(&self) -> Result<Script, Error> {
|
fn get_change_address(&self) -> Result<Script, Error> {
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
|
|
||||||
let (desc, script_type) = self.get_descriptor_for_script_type(ScriptType::Internal);
|
let (desc, script_type) = self.get_descriptor_for_script_type(ScriptType::Internal);
|
||||||
let index = self.fetch_and_increment_index(script_type)?;
|
let index = self.fetch_and_increment_index(script_type)?;
|
||||||
|
|
||||||
Ok(desc
|
Ok(desc
|
||||||
.derive(ChildNumber::from_normal_idx(index)?)
|
.derive(ChildNumber::from_normal_idx(index)?)
|
||||||
.script_pubkey())
|
.script_pubkey(deriv_ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_and_increment_index(&self, script_type: ScriptType) -> Result<u32, Error> {
|
fn fetch_and_increment_index(&self, script_type: ScriptType) -> Result<u32, Error> {
|
||||||
@ -970,10 +987,12 @@ where
|
|||||||
self.cache_addresses(script_type, index, CACHE_ADDR_BATCH_SIZE)?;
|
self.cache_addresses(script_type, index, CACHE_ADDR_BATCH_SIZE)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hd_keypaths = descriptor.get_hd_keypaths(index)?;
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
|
|
||||||
|
let hd_keypaths = descriptor.get_hd_keypaths(index, &self.secp)?;
|
||||||
let script = descriptor
|
let script = descriptor
|
||||||
.derive(ChildNumber::from_normal_idx(index)?)
|
.derive(ChildNumber::from_normal_idx(index)?)
|
||||||
.script_pubkey();
|
.script_pubkey(deriv_ctx);
|
||||||
for validator in &self.address_validators {
|
for validator in &self.address_validators {
|
||||||
validator.validate(script_type, &hd_keypaths, &script)?;
|
validator.validate(script_type, &hd_keypaths, &script)?;
|
||||||
}
|
}
|
||||||
@ -996,6 +1015,8 @@ where
|
|||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
|
|
||||||
let mut address_batch = self.database.borrow().begin_batch();
|
let mut address_batch = self.database.borrow().begin_batch();
|
||||||
|
|
||||||
let start_time = time::Instant::new();
|
let start_time = time::Instant::new();
|
||||||
@ -1003,7 +1024,7 @@ where
|
|||||||
address_batch.set_script_pubkey(
|
address_batch.set_script_pubkey(
|
||||||
&descriptor
|
&descriptor
|
||||||
.derive(ChildNumber::from_normal_idx(i)?)
|
.derive(ChildNumber::from_normal_idx(i)?)
|
||||||
.script_pubkey(),
|
.script_pubkey(deriv_ctx),
|
||||||
script_type,
|
script_type,
|
||||||
i,
|
i,
|
||||||
)?;
|
)?;
|
||||||
@ -1022,14 +1043,18 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_available_utxos(&self) -> Result<Vec<(UTXO, usize)>, Error> {
|
fn get_available_utxos(&self) -> Result<Vec<(UTXO, usize)>, Error> {
|
||||||
|
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
|
||||||
|
|
||||||
let external_weight = self
|
let external_weight = self
|
||||||
.get_descriptor_for_script_type(ScriptType::External)
|
.get_descriptor_for_script_type(ScriptType::External)
|
||||||
.0
|
.0
|
||||||
.max_satisfaction_weight();
|
.max_satisfaction_weight(deriv_ctx)
|
||||||
|
.unwrap();
|
||||||
let internal_weight = self
|
let internal_weight = self
|
||||||
.get_descriptor_for_script_type(ScriptType::Internal)
|
.get_descriptor_for_script_type(ScriptType::Internal)
|
||||||
.0
|
.0
|
||||||
.max_satisfaction_weight();
|
.max_satisfaction_weight(deriv_ctx)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let add_weight = |utxo: UTXO| {
|
let add_weight = |utxo: UTXO| {
|
||||||
let weight = match utxo.is_internal {
|
let weight = match utxo.is_internal {
|
||||||
@ -1161,11 +1186,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (desc, _) = self.get_descriptor_for_script_type(script_type);
|
let (desc, _) = self.get_descriptor_for_script_type(script_type);
|
||||||
psbt_input.hd_keypaths = desc.get_hd_keypaths(child)?;
|
psbt_input.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
|
||||||
let derived_descriptor = desc.derive(ChildNumber::from_normal_idx(child)?);
|
let derived_descriptor = desc.derive(ChildNumber::from_normal_idx(child)?);
|
||||||
|
|
||||||
psbt_input.redeem_script = derived_descriptor.psbt_redeem_script();
|
psbt_input.redeem_script = derived_descriptor.psbt_redeem_script(&self.secp);
|
||||||
psbt_input.witness_script = derived_descriptor.psbt_witness_script();
|
psbt_input.witness_script = derived_descriptor.psbt_witness_script(&self.secp);
|
||||||
|
|
||||||
let prev_output = input.previous_output;
|
let prev_output = input.previous_output;
|
||||||
if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
|
if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
|
||||||
@ -1194,7 +1219,7 @@ where
|
|||||||
.get_path_from_script_pubkey(&tx_output.script_pubkey)?
|
.get_path_from_script_pubkey(&tx_output.script_pubkey)?
|
||||||
{
|
{
|
||||||
let (desc, _) = self.get_descriptor_for_script_type(script_type);
|
let (desc, _) = self.get_descriptor_for_script_type(script_type);
|
||||||
psbt_output.hd_keypaths = desc.get_hd_keypaths(child)?;
|
psbt_output.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1219,7 +1244,7 @@ where
|
|||||||
|
|
||||||
// merge hd_keypaths
|
// merge hd_keypaths
|
||||||
let (desc, _) = self.get_descriptor_for_script_type(script_type);
|
let (desc, _) = self.get_descriptor_for_script_type(script_type);
|
||||||
let mut hd_keypaths = desc.get_hd_keypaths(child)?;
|
let mut hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
|
||||||
psbt_input.hd_keypaths.append(&mut hd_keypaths);
|
psbt_input.hd_keypaths.append(&mut hd_keypaths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use std::sync::Arc;
|
//! # use std::sync::Arc;
|
||||||
//! # use std::str::FromStr;
|
//! # use std::str::FromStr;
|
||||||
|
//! # use bitcoin::secp256k1::{Secp256k1, All};
|
||||||
//! # use bitcoin::*;
|
//! # use bitcoin::*;
|
||||||
//! # use bitcoin::util::psbt;
|
//! # use bitcoin::util::psbt;
|
||||||
//! # use bitcoin::util::bip32::Fingerprint;
|
//! # use bitcoin::util::bip32::Fingerprint;
|
||||||
@ -62,6 +63,7 @@
|
|||||||
//! &self,
|
//! &self,
|
||||||
//! psbt: &mut psbt::PartiallySignedTransaction,
|
//! psbt: &mut psbt::PartiallySignedTransaction,
|
||||||
//! input_index: Option<usize>,
|
//! input_index: Option<usize>,
|
||||||
|
//! _secp: &Secp256k1<All>,
|
||||||
//! ) -> Result<(), SignerError> {
|
//! ) -> Result<(), SignerError> {
|
||||||
//! let input_index = input_index.ok_or(SignerError::InputIndexOutOfRange)?;
|
//! let input_index = input_index.ok_or(SignerError::InputIndexOutOfRange)?;
|
||||||
//! self.device.sign_input(psbt, input_index)?;
|
//! self.device.sign_input(psbt, input_index)?;
|
||||||
@ -105,6 +107,7 @@ use bitcoin::{PrivateKey, Script, SigHash, SigHashType};
|
|||||||
use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap};
|
use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap};
|
||||||
use miniscript::{Legacy, MiniscriptKey, Segwitv0};
|
use miniscript::{Legacy, MiniscriptKey, Segwitv0};
|
||||||
|
|
||||||
|
use super::utils::SecpCtx;
|
||||||
use crate::descriptor::XKeyUtils;
|
use crate::descriptor::XKeyUtils;
|
||||||
|
|
||||||
/// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among
|
/// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among
|
||||||
@ -172,6 +175,7 @@ pub trait Signer: fmt::Debug + Send + Sync {
|
|||||||
&self,
|
&self,
|
||||||
psbt: &mut psbt::PartiallySignedTransaction,
|
psbt: &mut psbt::PartiallySignedTransaction,
|
||||||
input_index: Option<usize>,
|
input_index: Option<usize>,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Result<(), SignerError>;
|
) -> Result<(), SignerError>;
|
||||||
|
|
||||||
/// Return whether or not the signer signs the whole transaction in one go instead of every
|
/// Return whether or not the signer signs the whole transaction in one go instead of every
|
||||||
@ -193,6 +197,7 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
|
|||||||
&self,
|
&self,
|
||||||
psbt: &mut psbt::PartiallySignedTransaction,
|
psbt: &mut psbt::PartiallySignedTransaction,
|
||||||
input_index: Option<usize>,
|
input_index: Option<usize>,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Result<(), SignerError> {
|
) -> Result<(), SignerError> {
|
||||||
let input_index = input_index.unwrap();
|
let input_index = input_index.unwrap();
|
||||||
if input_index >= psbt.inputs.len() {
|
if input_index >= psbt.inputs.len() {
|
||||||
@ -203,7 +208,7 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
|
|||||||
.hd_keypaths
|
.hd_keypaths
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(pk, &(fingerprint, ref path))| {
|
.filter_map(|(pk, &(fingerprint, ref path))| {
|
||||||
if self.matches(fingerprint, &path).is_some() {
|
if self.matches(&(fingerprint, path.clone()), &secp).is_some() {
|
||||||
Some((pk, path))
|
Some((pk, path))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -215,13 +220,11 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
|
|||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ctx = Secp256k1::signing_only();
|
let derived_key = self.xkey.derive_priv(&secp, &deriv_path).unwrap();
|
||||||
|
if &derived_key.private_key.public_key(&secp) != public_key {
|
||||||
let derived_key = self.xkey.derive_priv(&ctx, &deriv_path).unwrap();
|
|
||||||
if &derived_key.private_key.public_key(&ctx) != public_key {
|
|
||||||
Err(SignerError::InvalidKey)
|
Err(SignerError::InvalidKey)
|
||||||
} else {
|
} else {
|
||||||
derived_key.private_key.sign(psbt, Some(input_index))
|
derived_key.private_key.sign(psbt, Some(input_index), secp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,15 +242,14 @@ impl Signer for PrivateKey {
|
|||||||
&self,
|
&self,
|
||||||
psbt: &mut psbt::PartiallySignedTransaction,
|
psbt: &mut psbt::PartiallySignedTransaction,
|
||||||
input_index: Option<usize>,
|
input_index: Option<usize>,
|
||||||
|
secp: &SecpCtx,
|
||||||
) -> Result<(), SignerError> {
|
) -> Result<(), SignerError> {
|
||||||
let input_index = input_index.unwrap();
|
let input_index = input_index.unwrap();
|
||||||
if input_index >= psbt.inputs.len() {
|
if input_index >= psbt.inputs.len() {
|
||||||
return Err(SignerError::InputIndexOutOfRange);
|
return Err(SignerError::InputIndexOutOfRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctx = Secp256k1::signing_only();
|
let pubkey = self.public_key(&secp);
|
||||||
|
|
||||||
let pubkey = self.public_key(&ctx);
|
|
||||||
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
|
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -261,7 +263,7 @@ impl Signer for PrivateKey {
|
|||||||
None => Legacy::sighash(psbt, input_index)?,
|
None => Legacy::sighash(psbt, input_index)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let signature = ctx.sign(
|
let signature = secp.sign(
|
||||||
&Message::from_slice(&hash.into_inner()[..]).unwrap(),
|
&Message::from_slice(&hash.into_inner()[..]).unwrap(),
|
||||||
&self.key,
|
&self.key,
|
||||||
);
|
);
|
||||||
@ -323,17 +325,18 @@ impl From<(SignerId, SignerOrdering)> for SignersContainerKey {
|
|||||||
pub struct SignersContainer(BTreeMap<SignersContainerKey, Arc<dyn Signer>>);
|
pub struct SignersContainer(BTreeMap<SignersContainerKey, Arc<dyn Signer>>);
|
||||||
|
|
||||||
impl SignersContainer {
|
impl SignersContainer {
|
||||||
pub fn as_key_map(&self) -> KeyMap {
|
pub fn as_key_map(&self, secp: &SecpCtx) -> KeyMap {
|
||||||
self.0
|
self.0
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|signer| signer.descriptor_secret_key())
|
.filter_map(|signer| signer.descriptor_secret_key())
|
||||||
.filter_map(|secret| secret.as_public().ok().map(|public| (public, secret)))
|
.filter_map(|secret| secret.as_public(secp).ok().map(|public| (public, secret)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<KeyMap> for SignersContainer {
|
impl From<KeyMap> for SignersContainer {
|
||||||
fn from(keymap: KeyMap) -> SignersContainer {
|
fn from(keymap: KeyMap) -> SignersContainer {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
let mut container = SignersContainer::new();
|
let mut container = SignersContainer::new();
|
||||||
|
|
||||||
for (_, secret) in keymap {
|
for (_, secret) in keymap {
|
||||||
@ -349,7 +352,7 @@ impl From<KeyMap> for SignersContainer {
|
|||||||
Arc::new(private_key.key),
|
Arc::new(private_key.key),
|
||||||
),
|
),
|
||||||
DescriptorSecretKey::XPrv(xprv) => container.add_external(
|
DescriptorSecretKey::XPrv(xprv) => container.add_external(
|
||||||
SignerId::from(xprv.root_fingerprint()),
|
SignerId::from(xprv.root_fingerprint(&secp)),
|
||||||
SignerOrdering::default(),
|
SignerOrdering::default(),
|
||||||
Arc::new(xprv),
|
Arc::new(xprv),
|
||||||
),
|
),
|
||||||
|
@ -22,7 +22,11 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
// SOFTWARE.
|
// SOFTWARE.
|
||||||
|
|
||||||
use miniscript::{MiniscriptKey, Satisfier};
|
use bitcoin::secp256k1::{All, Secp256k1};
|
||||||
|
use bitcoin::util::bip32;
|
||||||
|
|
||||||
|
use miniscript::descriptor::DescriptorPublicKeyCtx;
|
||||||
|
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
|
||||||
|
|
||||||
// De-facto standard "dust limit" (even though it should change based on the output type)
|
// De-facto standard "dust limit" (even though it should change based on the output type)
|
||||||
const DUST_LIMIT_SATOSHI: u64 = 546;
|
const DUST_LIMIT_SATOSHI: u64 = 546;
|
||||||
@ -56,7 +60,7 @@ impl After {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Pk: MiniscriptKey> Satisfier<Pk> for After {
|
impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx, Pk> for After {
|
||||||
fn check_after(&self, n: u32) -> bool {
|
fn check_after(&self, n: u32) -> bool {
|
||||||
if let Some(current_height) = self.current_height {
|
if let Some(current_height) = self.current_height {
|
||||||
current_height >= n
|
current_height >= n
|
||||||
@ -86,7 +90,7 @@ impl Older {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Pk: MiniscriptKey> Satisfier<Pk> for Older {
|
impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx, Pk> for Older {
|
||||||
fn check_older(&self, n: u32) -> bool {
|
fn check_older(&self, n: u32) -> bool {
|
||||||
if let Some(current_height) = self.current_height {
|
if let Some(current_height) = self.current_height {
|
||||||
// TODO: test >= / >
|
// TODO: test >= / >
|
||||||
@ -97,6 +101,14 @@ impl<Pk: MiniscriptKey> Satisfier<Pk> for Older {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type SecpCtx = Secp256k1<All>;
|
||||||
|
pub(crate) fn descriptor_to_pk_ctx(secp: &SecpCtx) -> DescriptorPublicKeyCtx<'_, All> {
|
||||||
|
// Create a `to_pk_ctx` with a dummy derivation index, since we always use this on descriptor
|
||||||
|
// that have already been derived with `Descriptor::derive()`, so the child number added here
|
||||||
|
// is ignored.
|
||||||
|
DescriptorPublicKeyCtx::new(secp, bip32::ChildNumber::Normal { index: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ChunksIterator<I: Iterator> {
|
pub struct ChunksIterator<I: Iterator> {
|
||||||
iter: I,
|
iter: I,
|
||||||
size: usize,
|
size: usize,
|
||||||
|
@ -116,15 +116,19 @@ impl TestIncomingTx {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! testutils {
|
macro_rules! testutils {
|
||||||
( @external $descriptors:expr, $child:expr ) => ({
|
( @external $descriptors:expr, $child:expr ) => ({
|
||||||
use miniscript::descriptor::{Descriptor, DescriptorPublicKey};
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
|
use miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorPublicKeyCtx};
|
||||||
|
|
||||||
let parsed = Descriptor::<DescriptorPublicKey>::parse_secret(&$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
|
let secp = Secp256k1::new();
|
||||||
parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest).expect("No address form")
|
let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, bitcoin::util::bip32::ChildNumber::from_normal_idx(0).unwrap());
|
||||||
|
|
||||||
|
let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
|
||||||
|
parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest, deriv_ctx).expect("No address form")
|
||||||
});
|
});
|
||||||
( @internal $descriptors:expr, $child:expr ) => ({
|
( @internal $descriptors:expr, $child:expr ) => ({
|
||||||
use miniscript::descriptor::{Descriptor, DescriptorPublicKey};
|
use miniscript::descriptor::{Descriptor, DescriptorPublicKey};
|
||||||
|
|
||||||
let parsed = Descriptor::<DescriptorPublicKey>::parse_secret(&$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
|
let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
|
||||||
parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest).expect("No address form")
|
parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest).expect("No address form")
|
||||||
});
|
});
|
||||||
( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
|
( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user