diff --git a/examples/htlc.rs b/examples/htlc.rs index c669823aa..38c12a14f 100644 --- a/examples/htlc.rs +++ b/examples/htlc.rs @@ -19,7 +19,6 @@ use std::str::FromStr; use bitcoin::Network; use miniscript::descriptor::Wsh; use miniscript::policy::{Concrete, Liftable}; -use miniscript::DescriptorTrait; fn main() { // HTLC policy with 10:1 odds for happy (co-operative) case compared to uncooperative case. @@ -54,7 +53,7 @@ fn main() { // Get the scriptPpubkey for this Wsh descriptor. assert_eq!( - format!("{:x}", htlc_descriptor.spk()), + format!("{:x}", htlc_descriptor.script_pubkey()), "0020d853877af928a8d2a569c9c0ed14bd16f6a80ce9cccaf8a6150fd8f7f8867ae2" ); @@ -66,7 +65,7 @@ fn main() { // Get the address for this Wsh descriptor.v assert_eq!( - format!("{}", htlc_descriptor.address(Network::Bitcoin).unwrap()), + format!("{}", htlc_descriptor.address(Network::Bitcoin)), "bc1qmpfcw7he9z5d9ftfe8qw699azmm2sr8fen903fs4plv007yx0t3qxfmqv5" ); } diff --git a/examples/parse.rs b/examples/parse.rs index 3e1db2172..9bd00ff8c 100644 --- a/examples/parse.rs +++ b/examples/parse.rs @@ -17,7 +17,7 @@ use std::str::FromStr; use miniscript::descriptor::DescriptorType; -use miniscript::{Descriptor, DescriptorTrait}; +use miniscript::Descriptor; fn main() { let desc = miniscript::Descriptor::::from_str( @@ -44,7 +44,7 @@ fn main() { // us to call infallible methods for getting script pubkey. if let Descriptor::Wsh(wsh) = &desc { assert_eq!( - format!("{:x}", wsh.spk()), + format!("{:x}", wsh.script_pubkey()), "0020daef16dd7c946a3e735a6e43310cb2ce33dfd14a04f76bf8241a16654cb2f0f9" ); } diff --git a/examples/sign_multisig.rs b/examples/sign_multisig.rs index 1ad82034e..5c58bf5d2 100644 --- a/examples/sign_multisig.rs +++ b/examples/sign_multisig.rs @@ -19,7 +19,6 @@ use std::str::FromStr; use bitcoin::blockdata::witness::Witness; use bitcoin::secp256k1; -use miniscript::DescriptorTrait; fn main() { let mut tx = spending_transaction(); diff --git a/examples/xpub_descriptors.rs b/examples/xpub_descriptors.rs index ce6c48d02..c66f26307 100644 --- a/examples/xpub_descriptors.rs +++ b/examples/xpub_descriptors.rs @@ -18,7 +18,7 @@ use std::str::FromStr; use miniscript::bitcoin::secp256k1::{Secp256k1, Verification}; use miniscript::bitcoin::{Address, Network}; -use miniscript::{Descriptor, DescriptorPublicKey, DescriptorTrait, TranslatePk2}; +use miniscript::{Descriptor, DescriptorPublicKey, TranslatePk2}; const XPUB_1: &str = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"; const XPUB_2: &str = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"; diff --git a/integration_test/src/test_cpp.rs b/integration_test/src/test_cpp.rs index fec467019..2e6e8ab46 100644 --- a/integration_test/src/test_cpp.rs +++ b/integration_test/src/test_cpp.rs @@ -13,7 +13,7 @@ use miniscript::miniscript::iter; use miniscript::psbt::PsbtExt; use miniscript::MiniscriptKey; use miniscript::Segwitv0; -use miniscript::{Descriptor, DescriptorTrait, Miniscript}; +use miniscript::{Descriptor, Miniscript}; use std::collections::BTreeMap; use std::fs::File; use std::io::{self, BufRead}; diff --git a/integration_test/src/test_desc.rs b/integration_test/src/test_desc.rs index 8a6e1811d..9d4d0e7d6 100644 --- a/integration_test/src/test_desc.rs +++ b/integration_test/src/test_desc.rs @@ -13,8 +13,8 @@ use bitcoin::util::{psbt, sighash}; use bitcoin::{self, Amount, OutPoint, SchnorrSig, Script, Transaction, TxIn, TxOut, Txid}; use bitcoincore_rpc::{json, Client, RpcApi}; use miniscript::miniscript::iter; -use miniscript::psbt::{PsbtInputExt, PsbtExt}; -use miniscript::{Descriptor, DescriptorTrait, Miniscript, ToPublicKey}; +use miniscript::psbt::{PsbtExt, PsbtInputExt}; +use miniscript::{Descriptor, Miniscript, ToPublicKey}; use miniscript::{MiniscriptKey, ScriptContext}; use std::collections::BTreeMap; diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index bb1ee4702..269317d9c 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -22,10 +22,9 @@ use std::fmt; use std::str::FromStr; use bitcoin::blockdata::script; -use bitcoin::{self, Script}; +use bitcoin::{Address, Network, Script}; use super::checksum::{desc_checksum, verify_checksum}; -use super::DescriptorTrait; use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; use crate::policy::{semantic, Liftable}; @@ -60,25 +59,68 @@ impl Bare { pub fn as_inner(&self) -> &Miniscript { &self.ms } + + /// Checks whether the descriptor is safe. + pub fn sanity_check(&self) -> Result<(), Error> { + self.ms.sanity_check()?; + Ok(()) + } + + /// Computes an upper bound on the weight of a satisfying witness to the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the + /// scriptSig and witness stack length. + /// + /// # Errors + /// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)). + pub fn max_satisfaction_weight(&self) -> Result { + let scriptsig_len = self.ms.max_satisfaction_size()?; + Ok(4 * (varint_len(scriptsig_len) + scriptsig_len)) + } } impl Bare { - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::script_pubkey`] for this descriptor - pub fn spk(&self) -> Script { + /// Obtains the corresponding script pubkey for this descriptor. + pub fn script_pubkey(&self) -> Script { self.ms.encode() } - /// Obtain the underlying miniscript for this descriptor - /// Non failing verion of [`DescriptorTrait::explicit_script`] for this descriptor + /// Obtains the underlying miniscript for this descriptor. pub fn inner_script(&self) -> Script { - self.spk() + self.script_pubkey() } - /// Obtain the pre bip-340 signature script code for this descriptor - /// Non failing verion of [`DescriptorTrait::script_code`] for this descriptor + /// Obtains the pre bip-340 signature script code for this descriptor. pub fn ecdsa_sighash_script_code(&self) -> Script { - self.spk() + self.script_pubkey() + } + + /// Returns satisfying non-malleable witness and scriptSig with minimum + /// weight to spend an output controlled by the given descriptor if it is + /// possible to construct one using the `satisfier`. + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + let ms = self.ms.satisfy(satisfier)?; + let script_sig = witness_to_scriptsig(&ms); + let witness = vec![]; + Ok((witness, script_sig)) + } + + /// Returns satisfying, possibly malleable, witness and scriptSig with + /// minimum weight to spend an output controlled by the given descriptor if + /// it is possible to construct one using the `satisfier`. + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + let ms = self.ms.satisfy_malleable(satisfier)?; + let script_sig = witness_to_scriptsig(&ms); + let witness = vec![]; + Ok((witness, script_sig)) } } @@ -132,75 +174,6 @@ where } } -impl DescriptorTrait for Bare { - fn sanity_check(&self) -> Result<(), Error> { - self.ms.sanity_check()?; - Ok(()) - } - - fn address(&self, _network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - Err(Error::BareDescriptorAddr) - } - - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - self.spk() - } - - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { - Script::new() - } - - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.inner_script()) - } - - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - let ms = self.ms.satisfy(satisfier)?; - let script_sig = witness_to_scriptsig(&ms); - let witness = vec![]; - Ok((witness, script_sig)) - } - - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - let ms = self.ms.satisfy_malleable(satisfier)?; - let script_sig = witness_to_scriptsig(&ms); - let witness = vec![]; - Ok((witness, script_sig)) - } - - fn max_satisfaction_weight(&self) -> Result { - let scriptsig_len = self.ms.max_satisfaction_size()?; - Ok(4 * (varint_len(scriptsig_len) + scriptsig_len)) - } - - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.ecdsa_sighash_script_code()) - } -} - impl ForEachKey for Bare { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: F) -> bool where @@ -252,32 +225,70 @@ impl Pkh { pub fn into_inner(self) -> Pk { self.pk } + + /// Computes an upper bound on the weight of a satisfying witness to the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the + /// scriptSig and witness stack length. + pub fn max_satisfaction_weight(&self) -> usize { + 4 * (1 + 73 + BareCtx::pk_len(&self.pk)) + } } impl Pkh { - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::script_pubkey`] for this descriptor - pub fn spk(&self) -> Script { - let addr = bitcoin::Address::p2pkh(&self.pk.to_public_key(), bitcoin::Network::Bitcoin); + /// Obtains the corresponding script pubkey for this descriptor. + pub fn script_pubkey(&self) -> Script { + // Fine to hard code the `Network` here because we immediately call + // `script_pubkey` which does not use the `network` field of `Address`. + let addr = self.address(Network::Bitcoin); addr.script_pubkey() } - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::address`] for this descriptor - pub fn addr(&self, network: bitcoin::Network) -> bitcoin::Address { - bitcoin::Address::p2pkh(&self.pk.to_public_key(), network) + /// Obtains the corresponding script pubkey for this descriptor. + pub fn address(&self, network: Network) -> Address { + Address::p2pkh(&self.pk.to_public_key(), network) } - /// Obtain the underlying miniscript for this descriptor - /// Non failing verion of [`DescriptorTrait::explicit_script`] for this descriptor + /// Obtains the underlying miniscript for this descriptor. pub fn inner_script(&self) -> Script { - self.spk() + self.script_pubkey() } - /// Obtain the pre bip-340 signature script code for this descriptor - /// Non failing verion of [`DescriptorTrait::script_code`] for this descriptor + /// Obtains the pre bip-340 signature script code for this descriptor. pub fn ecdsa_sighash_script_code(&self) -> Script { - self.spk() + self.script_pubkey() + } + + /// Returns satisfying non-malleable witness and scriptSig with minimum + /// weight to spend an output controlled by the given descriptor if it is + /// possible to construct one using the `satisfier`. + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) { + let sig_vec = sig.to_vec(); + let script_sig = script::Builder::new() + .push_slice(&sig_vec[..]) + .push_key(&self.pk.to_public_key()) + .into_script(); + let witness = vec![]; + Ok((witness, script_sig)) + } else { + Err(Error::MissingSig(self.pk.to_public_key())) + } + } + + /// Returns satisfying, possibly malleable, witness and scriptSig with + /// minimum weight to spend an output controlled by the given descriptor if + /// it is possible to construct one using the `satisfier`. + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + self.get_satisfaction(satisfier) } } @@ -339,77 +350,6 @@ where } } -impl DescriptorTrait for Pkh { - fn sanity_check(&self) -> Result<(), Error> { - Ok(()) - } - - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - Ok(self.addr(network)) - } - - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - self.spk() - } - - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { - Script::new() - } - - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.inner_script()) - } - - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) { - let sig_vec = sig.to_vec(); - let script_sig = script::Builder::new() - .push_slice(&sig_vec[..]) - .push_key(&self.pk.to_public_key()) - .into_script(); - let witness = vec![]; - Ok((witness, script_sig)) - } else { - Err(Error::MissingSig(self.pk.to_public_key())) - } - } - - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - self.get_satisfaction(satisfier) - } - - fn max_satisfaction_weight(&self) -> Result { - Ok(4 * (1 + 73 + BareCtx::pk_len(&self.pk))) - } - - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.ecdsa_sighash_script_code()) - } -} - impl ForEachKey for Pkh { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool where diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index fb6a04c65..13a932bdc 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -31,7 +31,7 @@ use std::sync::Arc; use bitcoin::blockdata::witness::Witness; use bitcoin::util::address::WitnessVersion; -use bitcoin::{self, secp256k1, Script}; +use bitcoin::{self, secp256k1, Address, Network, Script, TxIn}; use self::checksum::verify_checksum; use crate::miniscript::{Legacy, Miniscript, Segwitv0}; @@ -40,9 +40,6 @@ use crate::{ ToPublicKey, TranslatePk, TranslatePk2, }; -// Directly export from lib.rs, exporting the trait here causes conflicts in this file -pub(crate) mod pretaproot; - mod bare; mod segwitv0; mod sh; @@ -72,106 +69,6 @@ pub use self::key::{ /// public key from the descriptor. pub type KeyMap = HashMap; -/// A general trait for Bitcoin descriptor. -/// Offers function for witness cost estimation, script pubkey creation -/// satisfaction using the [Satisfier] trait. -// Unfortunately, the translation function cannot be added to trait -// because of traits cannot know underlying generic of Self. -// Thus, we must implement additional trait for translate function -pub trait DescriptorTrait { - /// Whether the descriptor is safe - /// Checks whether all the spend paths in the descriptor are possible - /// on the bitcoin network under the current standardness and consensus rules - /// Also checks whether the descriptor requires signauture on all spend paths - /// And whether the script is malleable. - /// In general, all the guarantees of miniscript hold only for safe scripts. - /// All the analysis guarantees of miniscript only hold safe scripts. - /// The signer may not be able to find satisfactions even if one exists - fn sanity_check(&self) -> Result<(), Error>; - - /// Computes the Bitcoin address of the descriptor, if one exists - /// Some descriptors like pk() don't have any address. - /// Errors: - /// - On raw/bare descriptors that don't have any address - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey; - - /// Computes the scriptpubkey of the descriptor - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey; - - /// Computes the scriptSig that will be in place for an unsigned - /// input spending an output with this descriptor. For pre-segwit - /// descriptors, which use the scriptSig for signatures, this - /// returns the empty script. - /// - /// This is used in Segwit transactions to produce an unsigned - /// transaction whose txid will not change during signing (since - /// only the witness data will change). - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey; - - /// Computes the "witness script" of the descriptor, i.e. the underlying - /// script before any hashing is done. For `Bare`, `Pkh` and `Wpkh` this - /// is the scriptPubkey; for `ShWpkh` and `Sh` this is the redeemScript; - /// for the others it is the witness script. - /// For `Tr` descriptors, this will error as there is no underlying script - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey; - - /// Returns satisfying non-malleable witness and scriptSig with minimum weight to spend an - /// output controlled by the given descriptor if it possible to - /// construct one using the satisfier S. - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier; - - /// Returns satisfying, possibly malleable witness and scriptSig to spend an - /// output controlled by the given descriptor if it possible to - /// construct one using the satisfier S. - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier; - - /// Attempts to produce a non-malleable satisfying witness and scriptSig to spend an - /// output controlled by the given descriptor; add the data to a given - /// `TxIn` output. - fn satisfy(&self, txin: &mut bitcoin::TxIn, satisfier: S) -> Result<(), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - // easy default implementation - let (witness, script_sig) = self.get_satisfaction(satisfier)?; - txin.witness = Witness::from_vec(witness); - txin.script_sig = script_sig; - Ok(()) - } - - /// Computes an upper bound on the weight of a satisfying witness to the - /// transaction. Assumes all ec-signatures are 73 bytes, including push opcode - /// and sighash suffix. Includes the weight of the VarInts encoding the - /// scriptSig and witness stack length. - /// Returns Error when the descriptor is impossible to safisfy (ex: sh(OP_FALSE)) - fn max_satisfaction_weight(&self) -> Result; - - /// Get the `scriptCode` of a transaction output. - /// - /// The `scriptCode` is the Script of the previous transaction output being serialized in the - /// sighash when evaluating a `CHECKSIG` & co. OP code. - /// Errors: - /// - When the descriptor is Tr - fn script_code(&self) -> Result - where - Pk: ToPublicKey; -} - /// Script descriptor #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Descriptor { @@ -399,113 +296,47 @@ impl Descriptor { } } - /// . - /// Convert a Descriptor into [`pretaproot::PreTaprootDescriptor`] - /// # Examples - /// - /// ``` - /// use std::str::FromStr; - /// use miniscript::descriptor::Descriptor; - /// use miniscript::{PreTaprootDescriptor, PreTaprootDescriptorTrait}; - /// use miniscript::bitcoin; - /// - /// // A descriptor with a string generic - /// let desc = Descriptor::::from_str("wpkh(02e18f242c8b0b589bfffeac30e1baa80a60933a649c7fb0f1103e78fbf58aa0ed)") - /// .expect("Valid segwitv0 descriptor"); - /// let pre_tap_desc = desc.into_pre_taproot_desc().expect("Wsh is pre taproot"); - /// - /// // Now the script code and explicit script no longer fail on longer fail - /// // on PreTaprootDescriptor using PreTaprootDescriptorTrait - /// let script_code = pre_tap_desc.script_code(); - /// assert_eq!(script_code.to_string(), - /// "Script(OP_DUP OP_HASH160 OP_PUSHBYTES_20 62107d047e8818b594303fe0657388cc4fc8771f OP_EQUALVERIFY OP_CHECKSIG)" - /// ); - /// ``` - /// - /// # Errors - /// - /// This function will return an error if descriptor is not a pre taproot descriptor. - pub fn into_pre_taproot_desc(self) -> Result, Self> { - match self { - Descriptor::Bare(bare) => Ok(pretaproot::PreTaprootDescriptor::Bare(bare)), - Descriptor::Pkh(pkh) => Ok(pretaproot::PreTaprootDescriptor::Pkh(pkh)), - Descriptor::Wpkh(wpkh) => Ok(pretaproot::PreTaprootDescriptor::Wpkh(wpkh)), - Descriptor::Sh(sh) => Ok(pretaproot::PreTaprootDescriptor::Sh(sh)), - Descriptor::Wsh(wsh) => Ok(pretaproot::PreTaprootDescriptor::Wsh(wsh)), - Descriptor::Tr(tr) => Err(Descriptor::Tr(tr)), - } - } -} - -impl TranslatePk for Descriptor

-where - P: MiniscriptKey, - Q: MiniscriptKey, -{ - type Output = Descriptor; - /// Converts a descriptor using abstract keys to one using specific keys. + /// Checks whether the descriptor is safe. /// - /// # Panics + /// Checks whether all the spend paths in the descriptor are possible on the + /// bitcoin network under the current standardness and consensus rules. Also + /// checks whether the descriptor requires signatures on all spend paths and + /// whether the script is malleable. /// - /// If `fpk` returns an uncompressed key when converting to a Segwit descriptor. - /// To prevent this panic, ensure `fpk` returns an error in this case instead. - fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result, E> - where - Fpk: FnMut(&P) -> Result, - Fpkh: FnMut(&P::Hash) -> Result, - Q: MiniscriptKey, - { - let desc = match *self { - Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(&mut fpk, &mut fpkh)?), - Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(&mut fpk, &mut fpkh)?), - }; - Ok(desc) - } -} - -impl DescriptorTrait for Descriptor { - /// Whether the descriptor is safe - /// Checks whether all the spend paths in the descriptor are possible - /// on the bitcoin network under the current standardness and consensus rules - /// Also checks whether the descriptor requires signauture on all spend paths - /// And whether the script is malleable. /// In general, all the guarantees of miniscript hold only for safe scripts. - /// All the analysis guarantees of miniscript only hold safe scripts. - /// The signer may not be able to find satisfactions even if one exists - fn sanity_check(&self) -> Result<(), Error> { + /// The signer may not be able to find satisfactions even if one exists. + pub fn sanity_check(&self) -> Result<(), Error> { match *self { Descriptor::Bare(ref bare) => bare.sanity_check(), - Descriptor::Pkh(ref pkh) => pkh.sanity_check(), + Descriptor::Pkh(_) => Ok(()), Descriptor::Wpkh(ref wpkh) => wpkh.sanity_check(), Descriptor::Wsh(ref wsh) => wsh.sanity_check(), Descriptor::Sh(ref sh) => sh.sanity_check(), Descriptor::Tr(ref tr) => tr.sanity_check(), } } +} + +impl Descriptor { /// Computes the Bitcoin address of the descriptor, if one exists - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { + /// + /// Some descriptors like pk() don't have any address. + /// + /// # Errors + /// For raw/bare descriptors that don't have any address. + pub fn address(&self, network: Network) -> Result { match *self { - Descriptor::Bare(ref bare) => bare.address(network), - Descriptor::Pkh(ref pkh) => pkh.address(network), - Descriptor::Wpkh(ref wpkh) => wpkh.address(network), - Descriptor::Wsh(ref wsh) => wsh.address(network), - Descriptor::Sh(ref sh) => sh.address(network), - Descriptor::Tr(ref tr) => tr.address(network), + Descriptor::Bare(_) => Err(Error::BareDescriptorAddr), + Descriptor::Pkh(ref pkh) => Ok(pkh.address(network)), + Descriptor::Wpkh(ref wpkh) => Ok(wpkh.address(network)), + Descriptor::Wsh(ref wsh) => Ok(wsh.address(network)), + Descriptor::Sh(ref sh) => Ok(sh.address(network)), + Descriptor::Tr(ref tr) => Ok(tr.address(network)), } } - /// Computes the scriptpubkey of the descriptor - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { + /// Computes the scriptpubkey of the descriptor. + pub fn script_pubkey(&self) -> Script { match *self { Descriptor::Bare(ref bare) => bare.script_pubkey(), Descriptor::Pkh(ref pkh) => pkh.script_pubkey(), @@ -516,54 +347,64 @@ impl DescriptorTrait for Descriptor { } } - /// Computes the scriptSig that will be in place for an unsigned - /// input spending an output with this descriptor. For pre-segwit - /// descriptors, which use the scriptSig for signatures, this - /// returns the empty script. + /// Computes the scriptSig that will be in place for an unsigned input + /// spending an output with this descriptor. For pre-segwit descriptors, + /// which use the scriptSig for signatures, this returns the empty script. /// - /// This is used in Segwit transactions to produce an unsigned - /// transaction whose txid will not change during signing (since - /// only the witness data will change). - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { + /// This is used in Segwit transactions to produce an unsigned transaction + /// whose txid will not change during signing (since only the witness data + /// will change). + pub fn unsigned_script_sig(&self) -> Script { match *self { - Descriptor::Bare(ref bare) => bare.unsigned_script_sig(), - Descriptor::Pkh(ref pkh) => pkh.unsigned_script_sig(), - Descriptor::Wpkh(ref wpkh) => wpkh.unsigned_script_sig(), - Descriptor::Wsh(ref wsh) => wsh.unsigned_script_sig(), + Descriptor::Bare(_) => Script::new(), + Descriptor::Pkh(_) => Script::new(), + Descriptor::Wpkh(_) => Script::new(), + Descriptor::Wsh(_) => Script::new(), Descriptor::Sh(ref sh) => sh.unsigned_script_sig(), - Descriptor::Tr(ref tr) => tr.unsigned_script_sig(), + Descriptor::Tr(_) => Script::new(), } } - /// Computes the "witness script" of the descriptor, i.e. the underlying - /// script before any hashing is done. For `Bare`, `Pkh` and `Wpkh` this - /// is the scriptPubkey; for `ShWpkh` and `Sh` this is the redeemScript; - /// for the others it is the witness script. - /// Errors: - /// - When the descriptor is Tr - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { + /// Computes the the underlying script before any hashing is done. For + /// `Bare`, `Pkh` and `Wpkh` this is the scriptPubkey; for `ShWpkh` and `Sh` + /// this is the redeemScript; for the others it is the witness script. + /// + /// # Errors + /// If the descriptor is a taproot descriptor. + pub fn explicit_script(&self) -> Result { + match *self { + Descriptor::Bare(ref bare) => Ok(bare.script_pubkey()), + Descriptor::Pkh(ref pkh) => Ok(pkh.script_pubkey()), + Descriptor::Wpkh(ref wpkh) => Ok(wpkh.script_pubkey()), + Descriptor::Wsh(ref wsh) => Ok(wsh.inner_script()), + Descriptor::Sh(ref sh) => Ok(sh.inner_script()), + Descriptor::Tr(_) => Err(Error::TrNoScriptCode), + } + } + + /// Computes the `scriptCode` of a transaction output. + /// + /// The `scriptCode` is the Script of the previous transaction output being + /// serialized in the sighash when evaluating a `CHECKSIG` & co. OP code. + /// + /// # Errors + /// If the descriptor is a taproot descriptor. + pub fn script_code(&self) -> Result { match *self { - Descriptor::Bare(ref bare) => bare.explicit_script(), - Descriptor::Pkh(ref pkh) => pkh.explicit_script(), - Descriptor::Wpkh(ref wpkh) => wpkh.explicit_script(), - Descriptor::Wsh(ref wsh) => wsh.explicit_script(), - Descriptor::Sh(ref sh) => sh.explicit_script(), - Descriptor::Tr(ref tr) => tr.explicit_script(), + Descriptor::Bare(ref bare) => Ok(bare.ecdsa_sighash_script_code()), + Descriptor::Pkh(ref pkh) => Ok(pkh.ecdsa_sighash_script_code()), + Descriptor::Wpkh(ref wpkh) => Ok(wpkh.ecdsa_sighash_script_code()), + Descriptor::Wsh(ref wsh) => Ok(wsh.ecdsa_sighash_script_code()), + Descriptor::Sh(ref sh) => Ok(sh.ecdsa_sighash_script_code()), + Descriptor::Tr(_) => Err(Error::TrNoScriptCode), } } /// Returns satisfying non-malleable witness and scriptSig to spend an /// output controlled by the given descriptor if it possible to /// construct one using the satisfier S. - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> where - Pk: ToPublicKey, S: Satisfier, { match *self { @@ -579,9 +420,8 @@ impl DescriptorTrait for Descriptor { /// Returns a possilbly mallable satisfying non-malleable witness and scriptSig to spend an /// output controlled by the given descriptor if it possible to /// construct one using the satisfier S. - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> where - Pk: ToPublicKey, S: Satisfier, { match *self { @@ -594,38 +434,68 @@ impl DescriptorTrait for Descriptor { } } + /// Attempts to produce a non-malleable satisfying witness and scriptSig to spend an + /// output controlled by the given descriptor; add the data to a given + /// `TxIn` output. + pub fn satisfy(&self, txin: &mut TxIn, satisfier: S) -> Result<(), Error> + where + S: Satisfier, + { + let (witness, script_sig) = self.get_satisfaction(satisfier)?; + txin.witness = Witness::from_vec(witness); + txin.script_sig = script_sig; + Ok(()) + } + /// Computes an upper bound on the weight of a satisfying witness to the - /// transaction. Assumes all signatures are 73 bytes, including push opcode - /// and sighash suffix. Includes the weight of the VarInts encoding the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the /// scriptSig and witness stack length. - fn max_satisfaction_weight(&self) -> Result { - match *self { - Descriptor::Bare(ref bare) => bare.max_satisfaction_weight(), + /// + /// # Errors + /// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)). + pub fn max_satisfaction_weight(&self) -> Result { + let weight = match *self { + Descriptor::Bare(ref bare) => bare.max_satisfaction_weight()?, Descriptor::Pkh(ref pkh) => pkh.max_satisfaction_weight(), Descriptor::Wpkh(ref wpkh) => wpkh.max_satisfaction_weight(), - Descriptor::Wsh(ref wsh) => wsh.max_satisfaction_weight(), - Descriptor::Sh(ref sh) => sh.max_satisfaction_weight(), - Descriptor::Tr(ref tr) => tr.max_satisfaction_weight(), - } + Descriptor::Wsh(ref wsh) => wsh.max_satisfaction_weight()?, + Descriptor::Sh(ref sh) => sh.max_satisfaction_weight()?, + Descriptor::Tr(ref tr) => tr.max_satisfaction_weight()?, + }; + Ok(weight) } +} - /// Get the `scriptCode` of a transaction output. +impl TranslatePk for Descriptor

+where + P: MiniscriptKey, + Q: MiniscriptKey, +{ + type Output = Descriptor; + /// Converts a descriptor using abstract keys to one using specific keys. /// - /// The `scriptCode` is the Script of the previous transaction output being serialized in the - /// sighash when evaluating a `CHECKSIG` & co. OP code. - /// Returns Error for Tr descriptors - fn script_code(&self) -> Result + /// # Panics + /// + /// If `fpk` returns an uncompressed key when converting to a Segwit descriptor. + /// To prevent this panic, ensure `fpk` returns an error in this case instead. + fn translate_pk(&self, mut fpk: Fpk, mut fpkh: Fpkh) -> Result, E> where - Pk: ToPublicKey, + Fpk: FnMut(&P) -> Result, + Fpkh: FnMut(&P::Hash) -> Result, + Q: MiniscriptKey, { - match *self { - Descriptor::Bare(ref bare) => bare.script_code(), - Descriptor::Pkh(ref pkh) => pkh.script_code(), - Descriptor::Wpkh(ref wpkh) => wpkh.script_code(), - Descriptor::Wsh(ref wsh) => wsh.script_code(), - Descriptor::Sh(ref sh) => sh.script_code(), - Descriptor::Tr(ref tr) => tr.script_code(), - } + let desc = match *self { + Descriptor::Bare(ref bare) => Descriptor::Bare(bare.translate_pk(&mut fpk, &mut fpkh)?), + Descriptor::Pkh(ref pk) => Descriptor::Pkh(pk.translate_pk(&mut fpk, &mut fpkh)?), + Descriptor::Wpkh(ref pk) => Descriptor::Wpkh(pk.translate_pk(&mut fpk, &mut fpkh)?), + Descriptor::Sh(ref sh) => Descriptor::Sh(sh.translate_pk(&mut fpk, &mut fpkh)?), + Descriptor::Wsh(ref wsh) => Descriptor::Wsh(wsh.translate_pk(&mut fpk, &mut fpkh)?), + Descriptor::Tr(ref tr) => Descriptor::Tr(tr.translate_pk(&mut fpk, &mut fpkh)?), + }; + Ok(desc) } } @@ -971,16 +841,14 @@ mod tests { ) ); assert_eq!( - bare.address(bitcoin::Network::Bitcoin) - .unwrap_err() - .to_string(), + bare.address(Network::Bitcoin).unwrap_err().to_string(), "Bare descriptors don't have address" ); let pk = StdDescriptor::from_str(TEST_PK).unwrap(); assert_eq!( pk.script_pubkey(), - bitcoin::Script::from(vec![ + Script::from(vec![ 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xac, @@ -1007,7 +875,7 @@ mod tests { .into_script() ); assert_eq!( - pkh.address(bitcoin::Network::Bitcoin,).unwrap().to_string(), + pkh.address(Network::Bitcoin,).unwrap().to_string(), "1D7nRvrRgzCg9kYBwhPH3j3Gs6SmsRg3Wq" ); @@ -1028,9 +896,7 @@ mod tests { .into_script() ); assert_eq!( - wpkh.address(bitcoin::Network::Bitcoin,) - .unwrap() - .to_string(), + wpkh.address(Network::Bitcoin,).unwrap().to_string(), "bc1qsn57m9drscflq5nl76z6ny52hck5w4x5wqd9yt" ); @@ -1052,10 +918,7 @@ mod tests { .into_script() ); assert_eq!( - shwpkh - .address(bitcoin::Network::Bitcoin,) - .unwrap() - .to_string(), + shwpkh.address(Network::Bitcoin,).unwrap().to_string(), "3PjMEzoveVbvajcnDDuxcJhsuqPHgydQXq" ); @@ -1077,7 +940,7 @@ mod tests { .into_script() ); assert_eq!( - sh.address(bitcoin::Network::Bitcoin,).unwrap().to_string(), + sh.address(Network::Bitcoin,).unwrap().to_string(), "3HDbdvM9CQ6ASnQFUkWw6Z4t3qNwMesJE9" ); @@ -1103,7 +966,7 @@ mod tests { .into_script() ); assert_eq!( - wsh.address(bitcoin::Network::Bitcoin,).unwrap().to_string(), + wsh.address(Network::Bitcoin,).unwrap().to_string(), "bc1qlymeahyfsv2jm3upw3urqp6m65ufde9seedl7umh0lth6yjt5zzsk33tv6" ); @@ -1125,10 +988,7 @@ mod tests { .into_script() ); assert_eq!( - shwsh - .address(bitcoin::Network::Bitcoin,) - .unwrap() - .to_string(), + shwsh.address(Network::Bitcoin,).unwrap().to_string(), "38cTksiyPT2b1uGRVbVqHdDhW9vKs84N6Z" ); } diff --git a/src/descriptor/pretaproot.rs b/src/descriptor/pretaproot.rs deleted file mode 100644 index e8c67ff5c..000000000 --- a/src/descriptor/pretaproot.rs +++ /dev/null @@ -1,282 +0,0 @@ -use std::fmt; -use std::str::{self, FromStr}; - -use bitcoin::{self, Script}; - -use super::checksum::verify_checksum; -use super::{Bare, Pkh, Sh, Wpkh, Wsh}; -use crate::{expression, DescriptorTrait, Error, MiniscriptKey, Satisfier, ToPublicKey}; - -/// Script descriptor -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum PreTaprootDescriptor { - /// Bare descriptor - Bare(Bare), - /// Pay-to-PubKey-Hash - Pkh(Pkh), - /// Pay-to-Witness-PubKey-Hash - Wpkh(Wpkh), - /// Pay-to-ScriptHash(includes nested wsh/wpkh/sorted multi) - Sh(Sh), - /// Pay-to-Witness-ScriptHash with Segwitv0 context - Wsh(Wsh), -} - -impl DescriptorTrait for PreTaprootDescriptor { - /// Whether the descriptor is safe - /// Checks whether all the spend paths in the descriptor are possible - /// on the bitcoin network under the current standardness and consensus rules - /// Also checks whether the descriptor requires signauture on all spend paths - /// And whether the script is malleable. - /// In general, all the guarantees of miniscript hold only for safe scripts. - /// All the analysis guarantees of miniscript only hold safe scripts. - /// The signer may not be able to find satisfactions even if one exists - fn sanity_check(&self) -> Result<(), Error> { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.sanity_check(), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.sanity_check(), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.sanity_check(), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.sanity_check(), - PreTaprootDescriptor::Sh(ref sh) => sh.sanity_check(), - } - } - /// Computes the Bitcoin address of the descriptor, if one exists - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.address(network), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.address(network), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.address(network), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.address(network), - PreTaprootDescriptor::Sh(ref sh) => sh.address(network), - } - } - - /// Computes the scriptpubkey of the descriptor - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.script_pubkey(), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.script_pubkey(), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.script_pubkey(), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.script_pubkey(), - PreTaprootDescriptor::Sh(ref sh) => sh.script_pubkey(), - } - } - - /// Computes the scriptSig that will be in place for an unsigned - /// input spending an output with this descriptor. For pre-segwit - /// descriptors, which use the scriptSig for signatures, this - /// returns the empty script. - /// - /// This is used in Segwit transactions to produce an unsigned - /// transaction whose txid will not change during signing (since - /// only the witness data will change). - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.unsigned_script_sig(), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.unsigned_script_sig(), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.unsigned_script_sig(), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.unsigned_script_sig(), - PreTaprootDescriptor::Sh(ref sh) => sh.unsigned_script_sig(), - } - } - - /// Computes the "witness script" of the descriptor, i.e. the underlying - /// script before any hashing is done. For `Bare`, `Pkh` and `Wpkh` this - /// is the scriptPubkey; for `ShWpkh` and `Sh` this is the redeemScript; - /// for the others it is the witness script. - /// Errors: - /// - When the descriptor is Tr - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.explicit_script(), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.explicit_script(), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.explicit_script(), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.explicit_script(), - PreTaprootDescriptor::Sh(ref sh) => sh.explicit_script(), - } - } - - /// Returns satisfying non-malleable witness and scriptSig to spend an - /// output controlled by the given descriptor if it possible to - /// construct one using the satisfier S. - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.get_satisfaction(satisfier), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.get_satisfaction(satisfier), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction(satisfier), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.get_satisfaction(satisfier), - PreTaprootDescriptor::Sh(ref sh) => sh.get_satisfaction(satisfier), - } - } - - /// Returns a possilbly mallable satisfying non-malleable witness and scriptSig to spend an - /// output controlled by the given descriptor if it possible to - /// construct one using the satisfier S. - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.get_satisfaction_mall(satisfier), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.get_satisfaction_mall(satisfier), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.get_satisfaction_mall(satisfier), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.get_satisfaction_mall(satisfier), - PreTaprootDescriptor::Sh(ref sh) => sh.get_satisfaction_mall(satisfier), - } - } - - /// Computes an upper bound on the weight of a satisfying witness to the - /// transaction. Assumes all signatures are 73 bytes, including push opcode - /// and sighash suffix. Includes the weight of the VarInts encoding the - /// scriptSig and witness stack length. - fn max_satisfaction_weight(&self) -> Result { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.max_satisfaction_weight(), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.max_satisfaction_weight(), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.max_satisfaction_weight(), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.max_satisfaction_weight(), - PreTaprootDescriptor::Sh(ref sh) => sh.max_satisfaction_weight(), - } - } - - /// Get the `scriptCode` of a transaction output. - /// - /// The `scriptCode` is the Script of the previous transaction output being serialized in the - /// sighash when evaluating a `CHECKSIG` & co. OP code. - /// Returns Error for Tr descriptors - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - match *self { - PreTaprootDescriptor::Bare(ref bare) => bare.script_code(), - PreTaprootDescriptor::Pkh(ref pkh) => pkh.script_code(), - PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.script_code(), - PreTaprootDescriptor::Wsh(ref wsh) => wsh.script_code(), - PreTaprootDescriptor::Sh(ref sh) => sh.script_code(), - } - } -} - -impl expression::FromTree for PreTaprootDescriptor -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - /// Parse an expression tree into a descriptor - fn from_tree(top: &expression::Tree) -> Result, Error> { - Ok(match (top.name, top.args.len() as u32) { - ("pkh", 1) => PreTaprootDescriptor::Pkh(Pkh::from_tree(top)?), - ("wpkh", 1) => PreTaprootDescriptor::Wpkh(Wpkh::from_tree(top)?), - ("sh", 1) => PreTaprootDescriptor::Sh(Sh::from_tree(top)?), - ("wsh", 1) => PreTaprootDescriptor::Wsh(Wsh::from_tree(top)?), - _ => PreTaprootDescriptor::Bare(Bare::from_tree(top)?), - }) - } -} - -impl FromStr for PreTaprootDescriptor -where - Pk: MiniscriptKey + str::FromStr, - Pk::Hash: str::FromStr, - ::Err: ToString, - <::Hash as FromStr>::Err: ToString, -{ - type Err = Error; - - fn from_str(s: &str) -> Result, Error> { - let desc_str = verify_checksum(s)?; - let top = expression::Tree::from_str(desc_str)?; - expression::FromTree::from_tree(&top) - } -} - -impl fmt::Debug for PreTaprootDescriptor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - PreTaprootDescriptor::Bare(ref sub) => write!(f, "{:?}", sub), - PreTaprootDescriptor::Pkh(ref pkh) => write!(f, "{:?}", pkh), - PreTaprootDescriptor::Wpkh(ref wpkh) => write!(f, "{:?}", wpkh), - PreTaprootDescriptor::Sh(ref sub) => write!(f, "{:?}", sub), - PreTaprootDescriptor::Wsh(ref sub) => write!(f, "{:?}", sub), - } - } -} - -impl fmt::Display for PreTaprootDescriptor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - PreTaprootDescriptor::Bare(ref sub) => write!(f, "{}", sub), - PreTaprootDescriptor::Pkh(ref pkh) => write!(f, "{}", pkh), - PreTaprootDescriptor::Wpkh(ref wpkh) => write!(f, "{}", wpkh), - PreTaprootDescriptor::Sh(ref sub) => write!(f, "{}", sub), - PreTaprootDescriptor::Wsh(ref sub) => write!(f, "{}", sub), - } - } -} - -serde_string_impl_pk!(PreTaprootDescriptor, "a pre-taproot script descriptor"); - -// Have the trait in a separate module to avoid conflicts -pub(crate) mod traits { - use bitcoin::Script; - - use super::PreTaprootDescriptor; - use crate::descriptor::{Pkh, Sh, Wpkh, Wsh}; - use crate::{DescriptorTrait, MiniscriptKey, ToPublicKey}; - - /// A general trait for Pre taproot bitcoin descriptor. - /// Similar to [`DescriptorTrait`], but `explicit_script` and `script_code` methods cannot fail - pub trait PreTaprootDescriptorTrait: DescriptorTrait { - /// Same as [`DescriptorTrait::explicit_script`], but a non failing version. - /// All PreTaproot descriptors have a unique explicit script - fn explicit_script(&self) -> Script - where - Pk: ToPublicKey, - { - // This expect can technically be avoided if we implement this for types, but - // having this expect saves lots of LoC because of default implementation - >::explicit_script(self) - .expect("Pre taproot descriptor have explicit script") - } - - /// Same as [`DescriptorTrait::script_code`], but a non failing version. - /// All PreTaproot descriptors have a script code - fn script_code(&self) -> Script - where - Pk: ToPublicKey, - { - >::script_code(self) - .expect("Pre taproot descriptor have non-failing script code") - } - } - - impl PreTaprootDescriptorTrait for Pkh {} - - impl PreTaprootDescriptorTrait for Sh {} - - impl PreTaprootDescriptorTrait for Wpkh {} - - impl PreTaprootDescriptorTrait for Wsh {} - - impl PreTaprootDescriptorTrait for PreTaprootDescriptor {} -} diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 4c8d8d003..bc452e02c 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -19,10 +19,10 @@ use std::fmt; use std::str::FromStr; -use bitcoin::{self, Script}; +use bitcoin::{self, Address, Network, Script}; use super::checksum::{desc_checksum, verify_checksum}; -use super::{DescriptorTrait, SortedMultiVec}; +use super::SortedMultiVec; use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; use crate::policy::{semantic, Liftable}; @@ -74,26 +74,61 @@ impl Wsh { WshInner::Ms(ref ms) => format!("wsh({})", ms), } } + + /// Checks whether the descriptor is safe. + pub fn sanity_check(&self) -> Result<(), Error> { + match self.inner { + WshInner::SortedMulti(ref smv) => smv.sanity_check()?, + WshInner::Ms(ref ms) => ms.sanity_check()?, + } + Ok(()) + } + + /// Computes an upper bound on the weight of a satisfying witness to the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the + /// scriptSig and witness stack length. + /// + /// # Errors + /// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)). + pub fn max_satisfaction_weight(&self) -> Result { + let (script_size, max_sat_elems, max_sat_size) = match self.inner { + WshInner::SortedMulti(ref smv) => ( + smv.script_size(), + smv.max_satisfaction_witness_elements(), + smv.max_satisfaction_size(), + ), + WshInner::Ms(ref ms) => ( + ms.script_size(), + ms.max_satisfaction_witness_elements()?, + ms.max_satisfaction_size()?, + ), + }; + Ok(4 + // scriptSig length byte + varint_len(script_size) + + script_size + + varint_len(max_sat_elems) + + max_sat_size) + } } impl Wsh { - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::script_pubkey`] for this descriptor - pub fn spk(&self) -> Script { + /// Obtains the corresponding script pubkey for this descriptor. + pub fn script_pubkey(&self) -> Script { self.inner_script().to_v0_p2wsh() } - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::address`] for this descriptor - pub fn addr(&self, network: bitcoin::Network) -> bitcoin::Address { + /// Obtains the corresponding script pubkey for this descriptor. + pub fn address(&self, network: Network) -> Address { match self.inner { - WshInner::SortedMulti(ref smv) => bitcoin::Address::p2wsh(&smv.encode(), network), - WshInner::Ms(ref ms) => bitcoin::Address::p2wsh(&ms.encode(), network), + WshInner::SortedMulti(ref smv) => Address::p2wsh(&smv.encode(), network), + WshInner::Ms(ref ms) => Address::p2wsh(&ms.encode(), network), } } - /// Obtain the underlying miniscript for this descriptor - /// Non failing verion of [`DescriptorTrait::explicit_script`] for this descriptor + /// Obtains the underlying miniscript for this descriptor. pub fn inner_script(&self) -> Script { match self.inner { WshInner::SortedMulti(ref smv) => smv.encode(), @@ -101,11 +136,43 @@ impl Wsh { } } - /// Obtain the pre bip-340 signature script code for this descriptor - /// Non failing verion of [`DescriptorTrait::script_code`] for this descriptor + /// Obtains the pre bip-340 signature script code for this descriptor. pub fn ecdsa_sighash_script_code(&self) -> Script { self.inner_script() } + + /// Returns satisfying non-malleable witness and scriptSig with minimum + /// weight to spend an output controlled by the given descriptor if it is + /// possible to construct one using the `satisfier`. + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + let mut witness = match self.inner { + WshInner::SortedMulti(ref smv) => smv.satisfy(satisfier)?, + WshInner::Ms(ref ms) => ms.satisfy(satisfier)?, + }; + let witness_script = self.inner_script(); + witness.push(witness_script.into_bytes()); + let script_sig = Script::new(); + Ok((witness, script_sig)) + } + + /// Returns satisfying, possibly malleable, witness and scriptSig with + /// minimum weight to spend an output controlled by the given descriptor if + /// it is possible to construct one using the `satisfier`. + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + let mut witness = match self.inner { + WshInner::SortedMulti(ref smv) => smv.satisfy(satisfier)?, + WshInner::Ms(ref ms) => ms.satisfy_malleable(satisfier)?, + }; + witness.push(self.inner_script().into_bytes()); + let script_sig = Script::new(); + Ok((witness, script_sig)) + } } /// Wsh Inner @@ -188,100 +255,6 @@ where } } -impl DescriptorTrait for Wsh { - fn sanity_check(&self) -> Result<(), Error> { - match self.inner { - WshInner::SortedMulti(ref smv) => smv.sanity_check()?, - WshInner::Ms(ref ms) => ms.sanity_check()?, - } - Ok(()) - } - - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - Ok(self.addr(network)) - } - - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - self.spk() - } - - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { - Script::new() - } - - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.inner_script()) - } - - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - let mut witness = match self.inner { - WshInner::SortedMulti(ref smv) => smv.satisfy(satisfier)?, - WshInner::Ms(ref ms) => ms.satisfy(satisfier)?, - }; - let witness_script = self.inner_script(); - witness.push(witness_script.into_bytes()); - let script_sig = Script::new(); - Ok((witness, script_sig)) - } - - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - let mut witness = match self.inner { - WshInner::SortedMulti(ref smv) => smv.satisfy(satisfier)?, - WshInner::Ms(ref ms) => ms.satisfy_malleable(satisfier)?, - }; - witness.push(self.inner_script().into_bytes()); - let script_sig = Script::new(); - Ok((witness, script_sig)) - } - - fn max_satisfaction_weight(&self) -> Result { - let (script_size, max_sat_elems, max_sat_size) = match self.inner { - WshInner::SortedMulti(ref smv) => ( - smv.script_size(), - smv.max_satisfaction_witness_elements(), - smv.max_satisfaction_size(), - ), - WshInner::Ms(ref ms) => ( - ms.script_size(), - ms.max_satisfaction_witness_elements()?, - ms.max_satisfaction_size()?, - ), - }; - Ok(4 + // scriptSig length byte - varint_len(script_size) + - script_size + - varint_len(max_sat_elems) + - max_sat_size) - } - - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.ecdsa_sighash_script_code()) - } -} - impl ForEachKey for Wsh { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: F) -> bool where @@ -349,40 +322,84 @@ impl Wpkh { pub fn to_string_no_checksum(&self) -> String { format!("wpkh({})", self.pk) } + + /// Checks whether the descriptor is safe. + pub fn sanity_check(&self) -> Result<(), Error> { + if self.pk.is_uncompressed() { + Err(Error::ContextError(ScriptContextError::CompressedOnly( + self.pk.to_string(), + ))) + } else { + Ok(()) + } + } + + /// Computes an upper bound on the weight of a satisfying witness to the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the + /// scriptSig and witness stack length. + pub fn max_satisfaction_weight(&self) -> usize { + 4 + 1 + 73 + Segwitv0::pk_len(&self.pk) + } } impl Wpkh { - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::script_pubkey`] for this descriptor - pub fn spk(&self) -> Script { - let addr = bitcoin::Address::p2wpkh(&self.pk.to_public_key(), bitcoin::Network::Bitcoin) + /// Obtains the corresponding script pubkey for this descriptor. + pub fn script_pubkey(&self) -> Script { + let addr = Address::p2wpkh(&self.pk.to_public_key(), Network::Bitcoin) .expect("wpkh descriptors have compressed keys"); addr.script_pubkey() } - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::address`] for this descriptor - pub fn addr(&self, network: bitcoin::Network) -> bitcoin::Address { - bitcoin::Address::p2wpkh(&self.pk.to_public_key(), network) + /// Obtains the corresponding script pubkey for this descriptor. + pub fn address(&self, network: Network) -> Address { + Address::p2wpkh(&self.pk.to_public_key(), network) .expect("Rust Miniscript types don't allow uncompressed pks in segwit descriptors") } - /// Obtain the underlying miniscript for this descriptor - /// Non failing verion of [`DescriptorTrait::explicit_script`] for this descriptor + /// Obtains the underlying miniscript for this descriptor. pub fn inner_script(&self) -> Script { - self.spk() + self.script_pubkey() } - /// Obtain the pre bip-340 signature script code for this descriptor - /// Non failing verion of [`DescriptorTrait::script_code`] for this descriptor + /// Obtains the pre bip-340 signature script code for this descriptor. pub fn ecdsa_sighash_script_code(&self) -> Script { // For SegWit outputs, it is defined by bip-0143 (quoted below) and is different from // the previous txo's scriptPubKey. // The item 5: // - For P2WPKH witness program, the scriptCode is `0x1976a914{20-byte-pubkey-hash}88ac`. - let addr = bitcoin::Address::p2pkh(&self.pk.to_public_key(), bitcoin::Network::Bitcoin); + let addr = Address::p2pkh(&self.pk.to_public_key(), Network::Bitcoin); addr.script_pubkey() } + + /// Returns satisfying non-malleable witness and scriptSig with minimum + /// weight to spend an output controlled by the given descriptor if it is + /// possible to construct one using the `satisfier`. + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) { + let sig_vec = sig.to_vec(); + let script_sig = Script::new(); + let witness = vec![sig_vec, self.pk.to_public_key().to_bytes()]; + Ok((witness, script_sig)) + } else { + Err(Error::MissingSig(self.pk.to_public_key())) + } + } + + /// Returns satisfying, possibly malleable, witness and scriptSig with + /// minimum weight to spend an output controlled by the given descriptor if + /// it is possible to construct one using the `satisfier`. + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + self.get_satisfaction(satisfier) + } } impl fmt::Debug for Wpkh { @@ -443,80 +460,6 @@ where } } -impl DescriptorTrait for Wpkh { - fn sanity_check(&self) -> Result<(), Error> { - if self.pk.is_uncompressed() { - Err(Error::ContextError(ScriptContextError::CompressedOnly( - self.pk.to_string(), - ))) - } else { - Ok(()) - } - } - - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - Ok(self.addr(network)) - } - - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - self.spk() - } - - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { - Script::new() - } - - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.inner_script()) - } - - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - if let Some(sig) = satisfier.lookup_ecdsa_sig(&self.pk) { - let sig_vec = sig.to_vec(); - let script_sig = Script::new(); - let witness = vec![sig_vec, self.pk.to_public_key().to_bytes()]; - Ok((witness, script_sig)) - } else { - Err(Error::MissingSig(self.pk.to_public_key())) - } - } - - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - self.get_satisfaction(satisfier) - } - - fn max_satisfaction_weight(&self) -> Result { - Ok(4 + 1 + 73 + Segwitv0::pk_len(&self.pk)) - } - - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.ecdsa_sighash_script_code()) - } -} - impl ForEachKey for Wpkh { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool where diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index 5f834ea92..1c628fde2 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -22,10 +22,10 @@ use std::fmt; use std::str::FromStr; use bitcoin::blockdata::script; -use bitcoin::{self, Script}; +use bitcoin::{Address, Network, Script}; use super::checksum::{desc_checksum, verify_checksum}; -use super::{DescriptorTrait, SortedMultiVec, Wpkh, Wsh}; +use super::{SortedMultiVec, Wpkh, Wsh}; use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; use crate::policy::{semantic, Liftable}; @@ -180,6 +180,17 @@ impl Sh { } } + /// Checks whether the descriptor is safe. + pub fn sanity_check(&self) -> Result<(), Error> { + match self.inner { + ShInner::Wsh(ref wsh) => wsh.sanity_check()?, + ShInner::Wpkh(ref wpkh) => wpkh.sanity_check()?, + ShInner::SortedMulti(ref smv) => smv.sanity_check()?, + ShInner::Ms(ref ms) => ms.sanity_check()?, + } + Ok(()) + } + /// Create a new p2sh wrapped wsh sortedmulti descriptor from threshold /// `k` and Vec of `pks` pub fn new_wsh_sortedmulti(k: usize, pks: Vec) -> Result { @@ -203,52 +214,81 @@ impl Sh { inner: ShInner::Wpkh(wpkh), } } + + /// Computes an upper bound on the weight of a satisfying witness to the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the + /// scriptSig and witness stack length. + /// + /// # Errors + /// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)). + pub fn max_satisfaction_weight(&self) -> Result { + Ok(match self.inner { + // add weighted script sig, len byte stays the same + ShInner::Wsh(ref wsh) => 4 * 35 + wsh.max_satisfaction_weight()?, + ShInner::SortedMulti(ref smv) => { + let ss = smv.script_size(); + let ps = push_opcode_size(ss); + let scriptsig_len = ps + ss + smv.max_satisfaction_size(); + 4 * (varint_len(scriptsig_len) + scriptsig_len) + } + // add weighted script sig, len byte stays the same + ShInner::Wpkh(ref wpkh) => 4 * 23 + wpkh.max_satisfaction_weight(), + ShInner::Ms(ref ms) => { + let ss = ms.script_size(); + let ps = push_opcode_size(ss); + let scriptsig_len = ps + ss + ms.max_satisfaction_size()?; + 4 * (varint_len(scriptsig_len) + scriptsig_len) + } + }) + } } impl Sh { - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::script_pubkey`] for this descriptor - pub fn spk(&self) -> Script { + /// Obtains the corresponding script pubkey for this descriptor. + pub fn script_pubkey(&self) -> Script { match self.inner { - ShInner::Wsh(ref wsh) => wsh.spk().to_p2sh(), - ShInner::Wpkh(ref wpkh) => wpkh.spk().to_p2sh(), + ShInner::Wsh(ref wsh) => wsh.script_pubkey().to_p2sh(), + ShInner::Wpkh(ref wpkh) => wpkh.script_pubkey().to_p2sh(), ShInner::SortedMulti(ref smv) => smv.encode().to_p2sh(), ShInner::Ms(ref ms) => ms.encode().to_p2sh(), } } - /// Obtain the corresponding script pubkey for this descriptor - /// Non failing verion of [`DescriptorTrait::address`] for this descriptor - pub fn addr(&self, network: bitcoin::Network) -> bitcoin::Address { - match self.inner { - ShInner::Wsh(ref wsh) => { - bitcoin::Address::p2sh(&wsh.spk(), network).expect("Size checked in Miniscript") - } - ShInner::Wpkh(ref wpkh) => { - bitcoin::Address::p2sh(&wpkh.spk(), network).expect("Size checked in Miniscript") - } - ShInner::SortedMulti(ref smv) => { - bitcoin::Address::p2sh(&smv.encode(), network).expect("Size checked in Miniscript") - } - ShInner::Ms(ref ms) => { - bitcoin::Address::p2sh(&ms.encode(), network).expect("Size checked in Miniscript") - } - } + /// Obtains the corresponding address for this descriptor. + pub fn address(&self, network: Network) -> Address { + let addr = self.address_fallible(network); + + // Size is checked in `check_global_consensus_validity`. + assert!(addr.is_ok()); + addr.expect("only fails if size > MAX_SCRIPT_ELEMENT_SIZE") + } + + fn address_fallible(&self, network: Network) -> Result { + let script = match self.inner { + ShInner::Wsh(ref wsh) => wsh.script_pubkey(), + ShInner::Wpkh(ref wpkh) => wpkh.script_pubkey(), + ShInner::SortedMulti(ref smv) => smv.encode(), + ShInner::Ms(ref ms) => ms.encode(), + }; + let address = Address::p2sh(&script, network)?; + + Ok(address) } /// Obtain the underlying miniscript for this descriptor - /// Non failing verion of [`DescriptorTrait::explicit_script`] for this descriptor pub fn inner_script(&self) -> Script { match self.inner { ShInner::Wsh(ref wsh) => wsh.inner_script(), - ShInner::Wpkh(ref wpkh) => wpkh.spk(), + ShInner::Wpkh(ref wpkh) => wpkh.script_pubkey(), ShInner::SortedMulti(ref smv) => smv.encode(), ShInner::Ms(ref ms) => ms.encode(), } } - /// Obtain the pre bip-340 signature script code for this descriptor - /// Non failing verion of [`DescriptorTrait::script_code`] for this descriptor + /// Obtains the pre bip-340 signature script code for this descriptor. pub fn ecdsa_sighash_script_code(&self) -> Script { match self.inner { // - For P2WSH witness program, if the witnessScript does not contain any `OP_CODESEPARATOR`, @@ -260,42 +300,15 @@ impl Sh { ShInner::Ms(ref ms) => ms.encode(), } } -} - -impl DescriptorTrait for Sh { - fn sanity_check(&self) -> Result<(), Error> { - match self.inner { - ShInner::Wsh(ref wsh) => wsh.sanity_check()?, - ShInner::Wpkh(ref wpkh) => wpkh.sanity_check()?, - ShInner::SortedMulti(ref smv) => smv.sanity_check()?, - ShInner::Ms(ref ms) => ms.sanity_check()?, - } - Ok(()) - } - - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - match self.inner { - ShInner::Wsh(ref wsh) => Ok(bitcoin::Address::p2sh(&wsh.spk(), network)?), - ShInner::Wpkh(ref wpkh) => Ok(bitcoin::Address::p2sh(&wpkh.spk(), network)?), - ShInner::SortedMulti(ref smv) => Ok(bitcoin::Address::p2sh(&smv.encode(), network)?), - ShInner::Ms(ref ms) => Ok(bitcoin::Address::p2sh(&ms.encode(), network)?), - } - } - - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - self.spk() - } - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { + /// Computes the scriptSig that will be in place for an unsigned input + /// spending an output with this descriptor. For pre-segwit descriptors, + /// which use the scriptSig for signatures, this returns the empty script. + /// + /// This is used in Segwit transactions to produce an unsigned transaction + /// whose txid will not change during signing (since only the witness data + /// will change). + pub fn unsigned_script_sig(&self) -> Script { match self.inner { ShInner::Wsh(ref wsh) => { // wsh explicit must contain exactly 1 element @@ -305,7 +318,7 @@ impl DescriptorTrait for Sh { .into_script() } ShInner::Wpkh(ref wpkh) => { - let redeem_script = wpkh.spk(); + let redeem_script = wpkh.script_pubkey(); script::Builder::new() .push_slice(&redeem_script[..]) .into_script() @@ -314,16 +327,11 @@ impl DescriptorTrait for Sh { } } - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.inner_script()) - } - - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + /// Returns satisfying non-malleable witness and scriptSig with minimum + /// weight to spend an output controlled by the given descriptor if it is + /// possible to construct one using the `satisfier`. + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> where - Pk: ToPublicKey, S: Satisfier, { let script_sig = self.unsigned_script_sig(); @@ -353,9 +361,11 @@ impl DescriptorTrait for Sh { } } - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + /// Returns satisfying, possibly malleable, witness and scriptSig with + /// minimum weight to spend an output controlled by the given descriptor if + /// it is possible to construct one using the `satisfier`. + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> where - Pk: ToPublicKey, S: Satisfier, { let script_sig = self.unsigned_script_sig(); @@ -374,34 +384,6 @@ impl DescriptorTrait for Sh { _ => self.get_satisfaction(satisfier), } } - - fn max_satisfaction_weight(&self) -> Result { - Ok(match self.inner { - // add weighted script sig, len byte stays the same - ShInner::Wsh(ref wsh) => 4 * 35 + wsh.max_satisfaction_weight()?, - ShInner::SortedMulti(ref smv) => { - let ss = smv.script_size(); - let ps = push_opcode_size(ss); - let scriptsig_len = ps + ss + smv.max_satisfaction_size(); - 4 * (varint_len(scriptsig_len) + scriptsig_len) - } - // add weighted script sig, len byte stays the same - ShInner::Wpkh(ref wpkh) => 4 * 23 + wpkh.max_satisfaction_weight()?, - ShInner::Ms(ref ms) => { - let ss = ms.script_size(); - let ps = push_opcode_size(ss); - let scriptsig_len = ps + ss + ms.max_satisfaction_size()?; - 4 * (varint_len(scriptsig_len) + scriptsig_len) - } - }) - } - - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - Ok(self.ecdsa_sighash_script_code()) - } } impl ForEachKey for Sh { diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 4a3e6a166..9b063cff1 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -10,7 +10,7 @@ use bitcoin::util::taproot::{ LeafVersion, TaprootBuilder, TaprootBuilderError, TaprootSpendInfo, TAPROOT_CONTROL_BASE_SIZE, TAPROOT_CONTROL_MAX_NODE_COUNT, TAPROOT_CONTROL_NODE_SIZE, }; -use bitcoin::{self, secp256k1, Script}; +use bitcoin::{secp256k1, Address, Network, Script}; use super::checksum::{desc_checksum, verify_checksum}; use crate::expression::{self, FromTree}; @@ -19,8 +19,7 @@ use crate::policy::semantic::Policy; use crate::policy::Liftable; use crate::util::{varint_len, witness_size}; use crate::{ - errstr, DescriptorTrait, Error, ForEach, ForEachKey, MiniscriptKey, Satisfier, Tap, - ToPublicKey, TranslatePk, + errstr, Error, ForEach, ForEachKey, MiniscriptKey, Satisfier, Tap, ToPublicKey, TranslatePk, }; /// A Taproot Tree representation. @@ -270,12 +269,52 @@ impl Tr { *self.spend_info.lock().expect("Lock poisoned") = Some(Arc::clone(&spend_info)); spend_info } + + /// Checks whether the descriptor is safe. + pub fn sanity_check(&self) -> Result<(), Error> { + for (_depth, ms) in self.iter_scripts() { + ms.sanity_check()?; + } + Ok(()) + } + + /// Computes an upper bound on the weight of a satisfying witness to the + /// transaction. + /// + /// Assumes all ec-signatures are 73 bytes, including push opcode and + /// sighash suffix. Includes the weight of the VarInts encoding the + /// scriptSig and witness stack length. + /// + /// # Errors + /// When the descriptor is impossible to safisfy (ex: sh(OP_FALSE)). + pub fn max_satisfaction_weight(&self) -> Result { + let mut max_wieght = Some(65); + for (depth, ms) in self.iter_scripts() { + let script_size = ms.script_size(); + let max_sat_elems = match ms.max_satisfaction_witness_elements() { + Ok(elem) => elem, + Err(..) => continue, + }; + let max_sat_size = match ms.max_satisfaction_size() { + Ok(sz) => sz, + Err(..) => continue, + }; + let control_block_sz = control_block_len(depth); + let wit_size = 4 + // scriptSig len byte + control_block_sz + // first element control block + varint_len(script_size) + + script_size + // second element script len with prefix + varint_len(max_sat_elems) + + max_sat_size; // witness + max_wieght = cmp::max(max_wieght, Some(wit_size)); + } + max_wieght.ok_or(Error::ImpossibleSatisfaction) + } } impl Tr { - /// Obtain the corresponding script pubkey for this descriptor - /// Same as[`DescriptorTrait::script_pubkey`] for this descriptor - pub fn spk(&self) -> Script { + /// Obtains the corresponding script pubkey for this descriptor. + pub fn script_pubkey(&self) -> Script { let output_key = self.spend_info().output_key(); let builder = bitcoin::blockdata::script::Builder::new(); builder @@ -284,14 +323,30 @@ impl Tr { .into_script() } - /// Obtain the corresponding script pubkey for this descriptor - /// Same as[`DescriptorTrait::address`] for this descriptor - pub fn addr(&self, network: bitcoin::Network) -> Result { + /// Obtains the corresponding address for this descriptor. + pub fn address(&self, network: Network) -> Address { let spend_info = self.spend_info(); - Ok(bitcoin::Address::p2tr_tweaked( - spend_info.output_key(), - network, - )) + Address::p2tr_tweaked(spend_info.output_key(), network) + } + + /// Returns satisfying non-malleable witness and scriptSig with minimum + /// weight to spend an output controlled by the given descriptor if it is + /// possible to construct one using the `satisfier`. + pub fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + best_tap_spend(self, satisfier, false /* allow_mall */) + } + + /// Returns satisfying, possibly malleable, witness and scriptSig with + /// minimum weight to spend an output controlled by the given descriptor if + /// it is possible to construct one using the `satisfier`. + pub fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> + where + S: Satisfier, + { + best_tap_spend(self, satisfier, true /* allow_mall */) } } @@ -550,90 +605,6 @@ impl Liftable for Tr { } } -impl DescriptorTrait for Tr { - fn sanity_check(&self) -> Result<(), Error> { - for (_depth, ms) in self.iter_scripts() { - ms.sanity_check()?; - } - Ok(()) - } - - fn address(&self, network: bitcoin::Network) -> Result - where - Pk: ToPublicKey, - { - self.addr(network) - } - - fn script_pubkey(&self) -> Script - where - Pk: ToPublicKey, - { - self.spk() - } - - fn unsigned_script_sig(&self) -> Script - where - Pk: ToPublicKey, - { - Script::new() - } - - fn explicit_script(&self) -> Result - where - Pk: ToPublicKey, - { - Err(Error::TrNoScriptCode) - } - - fn get_satisfaction(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - best_tap_spend(self, satisfier, false /* allow_mall */) - } - - fn get_satisfaction_mall(&self, satisfier: S) -> Result<(Vec>, Script), Error> - where - Pk: ToPublicKey, - S: Satisfier, - { - best_tap_spend(self, satisfier, true /* allow_mall */) - } - - fn max_satisfaction_weight(&self) -> Result { - let mut max_wieght = Some(65); - for (depth, ms) in self.iter_scripts() { - let script_size = ms.script_size(); - let max_sat_elems = match ms.max_satisfaction_witness_elements() { - Ok(elem) => elem, - Err(..) => continue, - }; - let max_sat_size = match ms.max_satisfaction_size() { - Ok(sz) => sz, - Err(..) => continue, - }; - let control_block_sz = control_block_len(depth); - let wit_size = 4 + // scriptSig len byte - control_block_sz + // first element control block - varint_len(script_size) + - script_size + // second element script len with prefix - varint_len(max_sat_elems) + - max_sat_size; // witness - max_wieght = cmp::max(max_wieght, Some(wit_size)); - } - max_wieght.ok_or(Error::ImpossibleSatisfaction) - } - - fn script_code(&self) -> Result - where - Pk: ToPublicKey, - { - Err(Error::TrNoScriptCode) - } -} - impl ForEachKey for Tr { fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool where diff --git a/src/lib.rs b/src/lib.rs index 6a8019b33..5c061a07a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,7 +54,6 @@ //! //! ```rust //! use std::str::FromStr; -//! use miniscript::DescriptorTrait; //! //! let desc = miniscript::Descriptor::::from_str("\ //! sh(wsh(or_d(\ @@ -114,9 +113,7 @@ use std::{error, fmt, hash, str}; use bitcoin::blockdata::{opcodes, script}; use bitcoin::hashes::{hash160, sha256, Hash}; -pub use crate::descriptor::pretaproot::traits::PreTaprootDescriptorTrait; -pub use crate::descriptor::pretaproot::PreTaprootDescriptor; -pub use crate::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait}; +pub use crate::descriptor::{Descriptor, DescriptorPublicKey}; pub use crate::interpreter::Interpreter; pub use crate::miniscript::context::{BareCtx, Legacy, ScriptContext, Segwitv0, Tap}; pub use crate::miniscript::decode::Terminal; @@ -708,7 +705,7 @@ impl fmt::Display for Error { write!(f, "MultiA too many keys {}", k) } Error::TaprootSpendInfoUnavialable => { - write!(f, "Taproot Spend Info not computed. Hint: Did you call `compute_spend_info` before calling methods from DescriptorTrait") + write!(f, "Taproot Spend Info not computed.") } Error::TrNoScriptCode => { write!(f, "No script code for Tr descriptors") diff --git a/src/psbt/finalizer.rs b/src/psbt/finalizer.rs index bc95bf3be..bb5de4455 100644 --- a/src/psbt/finalizer.rs +++ b/src/psbt/finalizer.rs @@ -29,7 +29,6 @@ use bitcoin::util::taproot::LeafVersion; use bitcoin::{self, PublicKey, Script, TxOut}; use super::{sanity_check, Error, InputError, Psbt, PsbtInputSatisfier}; -use crate::descriptor::DescriptorTrait; use crate::util::witness_size; use crate::{interpreter, BareCtx, Descriptor, Legacy, Miniscript, Satisfier, Segwitv0, Tap}; diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 9c23bdf0e..33df897ad 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -34,8 +34,8 @@ use crate::miniscript::iter::PkPkh; use crate::miniscript::limits::SEQUENCE_LOCKTIME_DISABLE_FLAG; use crate::miniscript::satisfy::{After, Older}; use crate::{ - descriptor, interpreter, Descriptor, DescriptorPublicKey, DescriptorTrait, MiniscriptKey, - Preimage32, Satisfier, ToPublicKey, TranslatePk, TranslatePk2, + descriptor, interpreter, Descriptor, DescriptorPublicKey, MiniscriptKey, Preimage32, Satisfier, + ToPublicKey, TranslatePk, TranslatePk2, }; mod finalizer;