bdk-macros = { version = "0.1.0-beta.1", path = "./macros" }
log = "^0.4"
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_json = { version = "^1.0" }
rand = "^0.7"
use std::sync::Arc;
+use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::ChildNumber;
use bdk::bitcoin::*;
use bdk::descriptor::*;
+use bdk::miniscript::DescriptorPublicKeyCtx;
fn main() {
+ let secp = Secp256k1::new();
+
let desc = "wsh(or_d(\
multi(\
2,[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*,tprv8ZgxMBicQKsPduL5QnGihpprdHyypMGi4DhimjtzYemu7se5YQNcZfAPLqXRuGHb5ZX2eTQj62oNqMnyxJ7B7wz54Uzswqw8fFqMVdcmVF7/1/*\
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);
+ let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, ChildNumber::from_normal_idx(42).unwrap());
+
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());
- let derived_desc = extended_desc.derive(ChildNumber::from_normal_idx(42).unwrap());
- println!("{:?}", derived_desc);
-
- let addr = derived_desc.address(Network::Testnet).unwrap();
+ let addr = extended_desc.address(Network::Testnet, deriv_ctx).unwrap();
println!("{}", addr);
- let script = derived_desc.witness_script();
+ let script = extended_desc.witness_script(deriv_ctx);
println!("{:?}", script);
}
( $descriptor_variant:ident, $ctx:ty, $key:expr ) => {{
#[allow(unused_imports)]
use $crate::keys::{DescriptorKey, ToDescriptorKey};
+ let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
$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)| {
(
$crate::miniscript::Descriptor::<
$crate::impl_leaf_opcode!(False)
});
( 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 ) => ({
$crate::fragment!(+c pk_k $key)
});
( multi $thresh:expr $(, $key:expr )+ ) => ({
use $crate::keys::ToDescriptorKey;
+ let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
let mut keys = 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))
});
}
#[cfg(test)]
mod test {
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 std::str::FromStr;
is_fixed: bool,
expected: &[&str],
) {
+ let secp = Secp256k1::new();
+ let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, ChildNumber::Normal { index: 0 });
+
let (desc, _key_map, _networks) = desc.unwrap();
assert_eq!(desc.is_witness(), is_witness);
assert_eq!(desc.is_fixed(), is_fixed);
} else {
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() {
assert_eq!(address.unwrap().to_string(), *expected.get(i).unwrap());
} 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());
}
}
// - verify the key_maps are correctly merged together
#[test]
fn test_key_maps_merged() {
+ let secp = Secp256k1::new();
+
let xprv1 = bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
let path1 = bip32::DerivationPath::from_str("m/0").unwrap();
let desc_key1 = (xprv1, path1.clone()).to_descriptor_key().unwrap();
let desc_key3: DescriptorKey<Segwitv0> =
(xprv3, path3.clone()).to_descriptor_key().unwrap();
- let (key1, _key_map, _valid_networks) = desc_key1.extract().unwrap();
- let (key2, _key_map, _valid_networks) = desc_key2.extract().unwrap();
- let (key3, _key_map, _valid_networks) = desc_key3.extract().unwrap();
+ let (key1, _key_map, _valid_networks) = desc_key1.extract(&secp).unwrap();
+ let (key2, _key_map, _valid_networks) = desc_key2.extract(&secp).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(&key2).unwrap().to_string(), "tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF/2147483647'/0/*");
assert_eq!(key_map.get(&key3).unwrap().to_string(), "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf/10/20/30/40/*");
pub use self::policy::Policy;
use crate::keys::{KeyError, ToDescriptorKey, ValidNetworks};
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`]
pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
self
};
- ExtendedDescriptor::parse_secret(descriptor)?.to_wallet_descriptor(network)
+ ExtendedDescriptor::parse_descriptor(descriptor)?.to_wallet_descriptor(network)
}
}
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
use crate::keys::DescriptorKey;
+ let secp = Secp256k1::new();
+
let check_key = |pk: &DescriptorPublicKey| {
let (pk, _, networks) = if self.0.is_witness() {
let desciptor_key: DescriptorKey<miniscript::Segwitv0> =
pk.clone().to_descriptor_key()?;
- desciptor_key.extract()?
+ desciptor_key.extract(&secp)?
} else {
let desciptor_key: DescriptorKey<miniscript::Legacy> =
pk.clone().to_descriptor_key()?;
- desciptor_key.extract()?
+ desciptor_key.extract(&secp)?
};
if networks.contains(&network) {
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
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 {
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> {
}
}
- fn root_fingerprint(&self) -> Fingerprint {
+ fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
match self.origin {
Some((fingerprint, _)) => fingerprint,
- None => self.xkey.xkey_fingerprint(),
+ None => self.xkey.xkey_fingerprint(secp),
}
}
}
pub(crate) trait DescriptorMeta: Sized {
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 derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option<Self>;
- fn derive_from_psbt_input(&self, psbt_input: &psbt::Input, utxo: Option<TxOut>)
- -> 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>,
+ secp: &SecpCtx,
+ ) -> Option<Self>;
}
pub(crate) trait DescriptorScripts {
- fn psbt_redeem_script(&self) -> Option<Script>;
- fn psbt_witness_script(&self) -> Option<Script>;
+ fn psbt_redeem_script(&self, secp: &SecpCtx) -> Option<Script>;
+ fn psbt_witness_script(&self, secp: &SecpCtx) -> Option<Script>;
}
-impl<T> DescriptorScripts for Descriptor<T>
-where
- T: miniscript::MiniscriptKey + miniscript::ToPublicKey,
-{
- fn psbt_redeem_script(&self) -> Option<Script> {
+impl DescriptorScripts for Descriptor<DescriptorPublicKey> {
+ fn psbt_redeem_script(&self, secp: &SecpCtx) -> Option<Script> {
+ let deriv_ctx = descriptor_to_pk_ctx(secp);
+
match self {
- Descriptor::ShWpkh(_) => Some(self.witness_script()),
- Descriptor::ShWsh(ref script) => Some(script.encode().to_v0_p2wsh()),
- Descriptor::Sh(ref script) => Some(script.encode()),
- Descriptor::Bare(ref script) => Some(script.encode()),
+ Descriptor::ShWpkh(_) => Some(self.witness_script(deriv_ctx)),
+ Descriptor::ShWsh(ref script) => Some(script.encode(deriv_ctx).to_v0_p2wsh()),
+ Descriptor::Sh(ref script) => Some(script.encode(deriv_ctx)),
+ Descriptor::Bare(ref script) => Some(script.encode(deriv_ctx)),
+ Descriptor::ShSortedMulti(ref keys) => Some(keys.encode(deriv_ctx)),
_ => 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 {
- Descriptor::Wsh(ref script) => Some(script.encode()),
- Descriptor::ShWsh(ref script) => Some(script.encode()),
+ Descriptor::Wsh(ref script) => Some(script.encode(deriv_ctx)),
+ Descriptor::ShWsh(ref script) => Some(script.encode(deriv_ctx)),
+ Descriptor::WshSortedMulti(ref keys) | Descriptor::ShWshSortedMulti(ref keys) => {
+ Some(keys.encode(deriv_ctx))
+ }
_ => None,
}
}
impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
fn is_witness(&self) -> bool {
match self {
- Descriptor::Bare(_) | Descriptor::Pk(_) | Descriptor::Pkh(_) | Descriptor::Sh(_) => {
- false
- }
+ Descriptor::Bare(_)
+ | Descriptor::Pk(_)
+ | Descriptor::Pkh(_)
+ | Descriptor::Sh(_)
+ | Descriptor::ShSortedMulti(_) => false,
Descriptor::Wpkh(_)
| Descriptor::ShWpkh(_)
| Descriptor::Wsh(_)
- | Descriptor::ShWsh(_) => true,
+ | Descriptor::ShWsh(_)
+ | Descriptor::ShWshSortedMulti(_)
+ | Descriptor::WshSortedMulti(_) => true,
}
}
- fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error> {
- fn translate_key(
- key: &DescriptorPublicKey,
- index: u32,
- paths: &mut HDKeyPaths,
- ) -> Result<DummyKey, Error> {
+ fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error> {
+ let translate_key = |key: &DescriptorPublicKey,
+ index: u32,
+ paths: &mut HDKeyPaths|
+ -> Result<DummyKey, Error> {
match key {
DescriptorPublicKey::SinglePub(_) => {}
DescriptorPublicKey::XPub(xpub) => {
paths.insert(
derived_pubkey.public_key,
(
- xpub.root_fingerprint(),
+ xpub.root_fingerprint(secp),
xpub.full_path(&[ChildNumber::from_normal_idx(index)?]),
),
);
}
Ok(DummyKey::default())
- }
+ };
let mut answer_pk = BTreeMap::new();
let mut answer_pkh = BTreeMap::new();
!found_wildcard_pk && !found_wildcard_pkh
}
- fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option<Self> {
- fn try_key(
- key: &DescriptorPublicKey,
- index: &HashMap<Fingerprint, DerivationPath>,
- found_path: &mut Option<ChildNumber>,
- ) -> Result<DummyKey, Error> {
+ fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self> {
+ let try_key = |key: &DescriptorPublicKey,
+ index: &HashMap<Fingerprint, DerivationPath>,
+ found_path: &mut Option<ChildNumber>|
+ -> Result<DummyKey, Error> {
if found_path.is_some() {
// already found a matching path, we are done
return Ok(DummyKey::default());
// 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
// 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
.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| {
index
- .get(&xpub.root_fingerprint())
+ .get(&xpub.root_fingerprint(secp))
.unwrap()
.into_iter()
.skip(prefix.into_iter().count())
&self,
psbt_input: &psbt::Input,
utxo: Option<TxOut>,
+ secp: &SecpCtx,
) -> 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);
} else if !self.is_fixed() {
// If the descriptor is not fixed we can't brute-force the derivation address, so just
return None;
}
+ let deriv_ctx = descriptor_to_pk_ctx(secp);
match self {
Descriptor::Pk(_)
| Descriptor::Pkh(_)
| Descriptor::Wpkh(_)
| Descriptor::ShWpkh(_)
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())
}
Descriptor::Bare(ms) | Descriptor::Sh(ms)
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())
}
Descriptor::Wsh(ms) | Descriptor::ShWsh(ms)
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())
}
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex;
+ use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::{bip32, psbt};
use super::*;
.unwrap();
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());
}
.unwrap();
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());
}
.unwrap();
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());
}
.unwrap();
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());
}
//! ```
//! # use std::sync::Arc;
//! # 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 (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc)?;
+//! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(desc)?;
//! println!("{:?}", extended_desc);
//!
//! 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)?);
//! # Ok::<(), bdk::Error>(())
//! ```
use bitcoin::util::bip32::Fingerprint;
use bitcoin::PublicKey;
-use miniscript::descriptor::DescriptorPublicKey;
+use miniscript::descriptor::{DescriptorPublicKey, SortedMultiVec};
use miniscript::{Descriptor, Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey};
#[allow(unused_imports)]
use crate::descriptor::ExtractPolicy;
use crate::wallet::signer::{SignerId, SignersContainer};
+use crate::wallet::utils::{descriptor_to_pk_ctx, SecpCtx};
use super::checksum::get_checksum;
use super::error::Error;
}
impl PKOrF {
- fn from_key(k: &DescriptorPublicKey) -> Self {
+ fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
match k {
DescriptorPublicKey::SinglePub(pubkey) => PKOrF {
pubkey: Some(pubkey.key),
..Default::default()
},
DescriptorPublicKey::XPub(xpub) => PKOrF {
- fingerprint: Some(xpub.root_fingerprint()),
+ fingerprint: Some(xpub.root_fingerprint(secp)),
..Default::default()
},
}
m: usize,
/// The items that can be satisfied by the descriptor
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")]
/// Extra conditions that also need to be satisfied
conditions: ConditionMap,
m: usize,
/// The items that can be satisfied by the descriptor
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(
serialize_with = "serialize_folded_cond_map",
skip_serializing_if = "BTreeMap::is_empty"
m,
items,
conditions,
+ sorted,
} = self
{
if items.len() >= *m {
m: *m,
items: items.clone(),
conditions: map,
+ sorted: *sorted,
};
}
}
m: threshold,
items: vec![],
conditions: Default::default(),
+ sorted: None,
};
for (index, item) in items.iter().enumerate() {
contribution.add(&item.contribution, index)?;
keys: &[DescriptorPublicKey],
signers: Arc<SignersContainer>,
threshold: usize,
+ sorted: bool,
+ secp: &SecpCtx,
) -> Result<Option<Policy>, PolicyError> {
if threshold == 0 {
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 {
n: keys.len(),
m: threshold,
items: vec![],
conditions: Default::default(),
+ sorted: Some(sorted),
};
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(
&Satisfaction::Complete {
condition: Default::default(),
}
}
-fn signer_id(key: &DescriptorPublicKey) -> SignerId {
+fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
match key {
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 {
- let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key(key)).into();
+fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>, secp: &SecpCtx) -> Policy {
+ 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 {
condition: Default::default(),
}
fn signature_key(
key: &<DescriptorPublicKey as MiniscriptKey>::Hash,
signers: Arc<SignersContainer>,
+ secp: &SecpCtx,
) -> 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();
if signers.find(SignerId::PkHash(key_hash)).is_some() {
}
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 {
// Leaves
Terminal::True | Terminal::False => None,
- Terminal::PkK(pubkey) => Some(signature(pubkey, Arc::clone(&signers))),
- Terminal::PkH(pubkey_hash) => Some(signature_key(pubkey_hash, 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), secp))
+ }
Terminal::After(value) => {
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into();
policy.contribution = Satisfaction::Complete {
Terminal::Hash160(hash) => {
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
Terminal::Alt(inner)
| Terminal::Swap(inner)
| Terminal::DupIf(inner)
| Terminal::Verify(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
Terminal::AndV(a, b) | Terminal::AndB(a, b) => Policy::make_and(
- a.extract_policy(Arc::clone(&signers))?,
- b.extract_policy(Arc::clone(&signers))?,
+ a.extract_policy(Arc::clone(&signers), secp)?,
+ b.extract_policy(Arc::clone(&signers), secp)?,
)?,
Terminal::AndOr(x, y, z) => Policy::make_or(
Policy::make_and(
- x.extract_policy(Arc::clone(&signers))?,
- y.extract_policy(Arc::clone(&signers))?,
+ x.extract_policy(Arc::clone(&signers), secp)?,
+ 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::OrD(a, b)
| Terminal::OrC(a, b)
| Terminal::OrI(a, b) => Policy::make_or(
- a.extract_policy(Arc::clone(&signers))?,
- b.extract_policy(Arc::clone(&signers))?,
+ a.extract_policy(Arc::clone(&signers), secp)?,
+ b.extract_policy(Arc::clone(&signers), secp)?,
)?,
Terminal::Thresh(k, nodes) => {
let mut threshold = *k;
let mapped: Vec<_> = nodes
.iter()
- .map(|n| n.extract_policy(Arc::clone(&signers)))
+ .map(|n| n.extract_policy(Arc::clone(&signers), secp))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.filter_map(|x| x)
}
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 {
Descriptor::Pk(pubkey)
| Descriptor::Pkh(pubkey)
| Descriptor::Wpkh(pubkey)
- | Descriptor::ShWpkh(pubkey) => Ok(Some(signature(pubkey, signers))),
- Descriptor::Bare(inner) | Descriptor::Sh(inner) => Ok(inner.extract_policy(signers)?),
- Descriptor::Wsh(inner) | Descriptor::ShWsh(inner) => Ok(inner.extract_policy(signers)?),
+ | Descriptor::ShWpkh(pubkey) => Ok(Some(signature(pubkey, signers, secp))),
+ Descriptor::Bare(inner) | Descriptor::Sh(inner) => {
+ 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)
+ }
}
}
}
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = wallet_desc
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = wallet_desc
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = wallet_desc
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
&& &keys[1].fingerprint.unwrap() == &fingerprint1)
);
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
&& items.len() == 2
&& conditions.contains_key(&vec![0])
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = wallet_desc
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
);
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
&& items.len() == 2
&& conditions.contains_key(&vec![0,1])
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = single_key
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = single_key
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = single_key
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
&& &keys[1].fingerprint.unwrap() == &fingerprint1)
);
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
&& items.len() == 2
&& conditions.contains_key(&vec![0])
let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
let signers_container = Arc::new(SignersContainer::from(keymap));
let policy = wallet_desc
- .extract_policy(signers_container)
+ .extract_policy(signers_container, &Secp256k1::new())
.unwrap()
.unwrap();
);
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
&& items.len() == 3
&& conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
use crate::keys::{KeyError, ValidNetworks};
use bitcoin::hashes::core::str::FromStr;
use bitcoin::network::constants::Network::Regtest;
+ use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::bip32::ChildNumber;
- use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
+ use miniscript::descriptor::{DescriptorPublicKey, DescriptorPublicKeyCtx, KeyMap};
use miniscript::Descriptor;
// verify template descriptor generates expected address(es)
is_fixed: bool,
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();
assert_eq!(desc.is_witness(), is_witness);
assert_eq!(desc.is_fixed(), is_fixed);
} else {
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());
}
}
pub use miniscript::ScriptContext;
use miniscript::{Miniscript, Terminal};
+use crate::wallet::utils::SecpCtx;
+
#[cfg(feature = "keys-bip39")]
#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
pub mod bip39;
// 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.
#[doc(hidden)]
- pub fn extract(self) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
+ pub fn extract(
+ self,
+ secp: &SecpCtx,
+ ) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
match self {
DescriptorKey::Public(public, valid_networks, _) => {
Ok((public, KeyMap::default(), valid_networks))
let mut key_map = KeyMap::with_capacity(1);
let public = secret
- .as_public()
+ .as_public(secp)
.map_err(|e| miniscript::Error::Unexpected(e.to_string()))?;
key_map.insert(public.clone(), secret);
#[doc(hidden)]
pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
descriptor_key: Pk,
+ secp: &SecpCtx,
) -> 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((
Miniscript::from_ast(Terminal::PkK(key))?,
pub fn make_multi<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
thresh: usize,
pks: Vec<Pk>,
+ secp: &SecpCtx,
) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
let (pks, key_maps_networks): (Vec<_>, Vec<_>) = pks
.into_iter()
- .map(|key| Ok::<_, KeyError>(key.to_descriptor_key()?.extract()?))
+ .map(|key| Ok::<_, KeyError>(key.to_descriptor_key()?.extract(secp)?))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(a, b, c)| (a, (b, c)))
use serde::{Deserialize, Serialize};
-use miniscript::{Descriptor, ScriptContext, Terminal};
+use miniscript::{Descriptor, DescriptorPublicKey, ScriptContext, Terminal};
use crate::blockchain::BlockchainMarker;
use crate::database::BatchDatabase;
) -> Result<Self, &'static str> {
let descriptor = wallet
.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)?;
let blockheight = match wallet.database.borrow().iter_txs(false) {
blockheight,
};
- if export.change_descriptor()
- != wallet
- .change_descriptor
- .as_ref()
- .map(|d| d.to_string_with_secret(&wallet.change_signers.as_key_map()))
- {
+ let desc_to_string = |d: &Descriptor<DescriptorPublicKey>| {
+ d.to_string_with_secret(&wallet.change_signers.as_key_map(wallet.secp_ctx()))
+ };
+ if export.change_descriptor() != wallet.change_descriptor.as_ref().map(desc_to_string) {
return Err("Incompatible change descriptor");
}
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
+use bitcoin::secp256k1::Secp256k1;
+
use bitcoin::consensus::encode::serialize;
use bitcoin::util::bip32::ChildNumber;
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
use address_validator::AddressValidator;
use signer::{Signer, SignerId, SignerOrdering, SignersContainer};
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::database::{BatchDatabase, BatchOperations, DatabaseUtils};
client: Option<B>,
database: RefCell<D>,
+
+ secp: SecpCtx,
}
// offline actions, always available
client: None,
database: RefCell::new(database),
+
+ secp: Secp256k1::new(),
})
}
/// Return a newly generated address using the external descriptor
pub fn get_new_address(&self) -> Result<Address, Error> {
let index = self.fetch_and_increment_index(ScriptType::External)?;
+ let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
self.descriptor
.derive(ChildNumber::from_normal_idx(index)?)
- .address(self.network)
+ .address(self.network, deriv_ctx)
.ok_or(Error::ScriptDoesntHaveAddressForm)
}
) -> Result<(PSBT, TransactionDetails), Error> {
let external_policy = self
.descriptor
- .extract_policy(Arc::clone(&self.signers))?
+ .extract_policy(Arc::clone(&self.signers), &self.secp)?
.unwrap();
let internal_policy = self
.change_descriptor
.as_ref()
.map(|desc| {
Ok::<_, Error>(
- desc.extract_policy(Arc::clone(&self.change_signers))?
+ desc.extract_policy(Arc::clone(&self.change_signers), &self.secp)?
.unwrap(),
)
})
details.received -= removed_updatable_output.value;
}
+ let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
let external_weight = self
.get_descriptor_for_script_type(ScriptType::External)
.0
- .max_satisfaction_weight();
+ .max_satisfaction_weight(deriv_ctx)
+ .unwrap();
let internal_weight = self
.get_descriptor_for_script_type(ScriptType::Internal)
.0
- .max_satisfaction_weight();
+ .max_satisfaction_weight(deriv_ctx)
+ .unwrap();
let original_sequence = tx.input[0].sequence;
.chain(self.change_signers.signers().iter())
{
if signer.sign_whole_tx() {
- signer.sign(&mut psbt, None)?;
+ signer.sign(&mut psbt, None, &self.secp)?;
} else {
for index in 0..psbt.inputs.len() {
- signer.sign(&mut psbt, Some(index))?;
+ signer.sign(&mut psbt, Some(index), &self.secp)?;
}
}
}
/// Return the spending policies for the wallet's descriptor
pub fn policies(&self, script_type: ScriptType) -> Result<Option<Policy>, Error> {
match (script_type, self.change_descriptor.as_ref()) {
- (ScriptType::External, _) => {
- Ok(self.descriptor.extract_policy(Arc::clone(&self.signers))?)
- }
+ (ScriptType::External, _) => Ok(self
+ .descriptor
+ .extract_policy(Arc::clone(&self.signers), &self.secp)?),
(ScriptType::Internal, None) => Ok(None),
(ScriptType::Internal, Some(desc)) => {
- Ok(desc.extract_policy(Arc::clone(&self.change_signers))?)
+ Ok(desc.extract_policy(Arc::clone(&self.change_signers), &self.secp)?)
}
}
}
.flatten()
{
desc
- } else if let Some(desc) = self
- .descriptor
- .derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n))
+ } else if let Some(desc) =
+ self.descriptor
+ .derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
{
desc
- } else if let Some(desc) = self
- .change_descriptor
- .as_ref()
- .and_then(|desc| desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n)))
- {
+ } else if let Some(desc) = self.change_descriptor.as_ref().and_then(|desc| {
+ desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
+ }) {
desc
} else {
debug!("Couldn't find the right derived descriptor for input {}", n);
return Ok((psbt, false));
};
+ let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
match desc.satisfy(
input,
(
After::new(current_height, false),
Older::new(current_height, create_height, false),
),
+ deriv_ctx,
) {
Ok(_) => continue,
Err(e) => {
Ok((psbt, true))
}
+ pub fn secp_ctx(&self) -> &SecpCtx {
+ &self.secp
+ }
+
// Internals
fn get_descriptor_for_script_type(
}
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 index = self.fetch_and_increment_index(script_type)?;
Ok(desc
.derive(ChildNumber::from_normal_idx(index)?)
- .script_pubkey())
+ .script_pubkey(deriv_ctx))
}
fn fetch_and_increment_index(&self, script_type: ScriptType) -> Result<u32, Error> {
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
.derive(ChildNumber::from_normal_idx(index)?)
- .script_pubkey();
+ .script_pubkey(deriv_ctx);
for validator in &self.address_validators {
validator.validate(script_type, &hd_keypaths, &script)?;
}
count = 1;
}
+ let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
let mut address_batch = self.database.borrow().begin_batch();
let start_time = time::Instant::new();
address_batch.set_script_pubkey(
&descriptor
.derive(ChildNumber::from_normal_idx(i)?)
- .script_pubkey(),
+ .script_pubkey(deriv_ctx),
script_type,
i,
)?;
}
fn get_available_utxos(&self) -> Result<Vec<(UTXO, usize)>, Error> {
+ let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
let external_weight = self
.get_descriptor_for_script_type(ScriptType::External)
.0
- .max_satisfaction_weight();
+ .max_satisfaction_weight(deriv_ctx)
+ .unwrap();
let internal_weight = self
.get_descriptor_for_script_type(ScriptType::Internal)
.0
- .max_satisfaction_weight();
+ .max_satisfaction_weight(deriv_ctx)
+ .unwrap();
let add_weight = |utxo: UTXO| {
let weight = match utxo.is_internal {
};
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)?);
- psbt_input.redeem_script = derived_descriptor.psbt_redeem_script();
- psbt_input.witness_script = derived_descriptor.psbt_witness_script();
+ psbt_input.redeem_script = derived_descriptor.psbt_redeem_script(&self.secp);
+ psbt_input.witness_script = derived_descriptor.psbt_witness_script(&self.secp);
let prev_output = input.previous_output;
if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
.get_path_from_script_pubkey(&tx_output.script_pubkey)?
{
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)?;
}
}
// merge hd_keypaths
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);
}
}
//! ```
//! # use std::sync::Arc;
//! # use std::str::FromStr;
+//! # use bitcoin::secp256k1::{Secp256k1, All};
//! # use bitcoin::*;
//! # use bitcoin::util::psbt;
//! # use bitcoin::util::bip32::Fingerprint;
//! &self,
//! psbt: &mut psbt::PartiallySignedTransaction,
//! input_index: Option<usize>,
+//! _secp: &Secp256k1<All>,
//! ) -> Result<(), SignerError> {
//! let input_index = input_index.ok_or(SignerError::InputIndexOutOfRange)?;
//! self.device.sign_input(psbt, input_index)?;
use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap};
use miniscript::{Legacy, MiniscriptKey, Segwitv0};
+use super::utils::SecpCtx;
use crate::descriptor::XKeyUtils;
/// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among
&self,
psbt: &mut psbt::PartiallySignedTransaction,
input_index: Option<usize>,
+ secp: &SecpCtx,
) -> Result<(), SignerError>;
/// Return whether or not the signer signs the whole transaction in one go instead of every
&self,
psbt: &mut psbt::PartiallySignedTransaction,
input_index: Option<usize>,
+ secp: &SecpCtx,
) -> Result<(), SignerError> {
let input_index = input_index.unwrap();
if input_index >= psbt.inputs.len() {
.hd_keypaths
.iter()
.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))
} else {
None
None => return Ok(()),
};
- let ctx = Secp256k1::signing_only();
-
- let derived_key = self.xkey.derive_priv(&ctx, &deriv_path).unwrap();
- if &derived_key.private_key.public_key(&ctx) != public_key {
+ let derived_key = self.xkey.derive_priv(&secp, &deriv_path).unwrap();
+ if &derived_key.private_key.public_key(&secp) != public_key {
Err(SignerError::InvalidKey)
} else {
- derived_key.private_key.sign(psbt, Some(input_index))
+ derived_key.private_key.sign(psbt, Some(input_index), secp)
}
}
&self,
psbt: &mut psbt::PartiallySignedTransaction,
input_index: Option<usize>,
+ secp: &SecpCtx,
) -> Result<(), SignerError> {
let input_index = input_index.unwrap();
if input_index >= psbt.inputs.len() {
return Err(SignerError::InputIndexOutOfRange);
}
- let ctx = Secp256k1::signing_only();
-
- let pubkey = self.public_key(&ctx);
+ let pubkey = self.public_key(&secp);
if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
return Ok(());
}
None => Legacy::sighash(psbt, input_index)?,
};
- let signature = ctx.sign(
+ let signature = secp.sign(
&Message::from_slice(&hash.into_inner()[..]).unwrap(),
&self.key,
);
pub struct SignersContainer(BTreeMap<SignersContainerKey, Arc<dyn Signer>>);
impl SignersContainer {
- pub fn as_key_map(&self) -> KeyMap {
+ pub fn as_key_map(&self, secp: &SecpCtx) -> KeyMap {
self.0
.values()
.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()
}
}
impl From<KeyMap> for SignersContainer {
fn from(keymap: KeyMap) -> SignersContainer {
+ let secp = Secp256k1::new();
let mut container = SignersContainer::new();
for (_, secret) in keymap {
Arc::new(private_key.key),
),
DescriptorSecretKey::XPrv(xprv) => container.add_external(
- SignerId::from(xprv.root_fingerprint()),
+ SignerId::from(xprv.root_fingerprint(&secp)),
SignerOrdering::default(),
Arc::new(xprv),
),
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// 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)
const DUST_LIMIT_SATOSHI: u64 = 546;
}
}
-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 {
if let Some(current_height) = self.current_height {
current_height >= n
}
}
-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 {
if let Some(current_height) = self.current_height {
// TODO: test >= / >
}
}
+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> {
iter: I,
size: usize,
#[macro_export]
macro_rules! testutils {
( @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;
- parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest).expect("No address form")
+ let secp = Secp256k1::new();
+ 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 ) => ({
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")
});
( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });