diff --git a/Cargo-latest.lock b/Cargo-latest.lock index c78d9bc8..854484e6 100644 --- a/Cargo-latest.lock +++ b/Cargo-latest.lock @@ -240,12 +240,6 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -307,7 +301,6 @@ dependencies = [ "serde", "serde_json", "serde_test", - "simplicity-lang", ] [[package]] @@ -342,12 +335,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ghost-cell" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" - [[package]] name = "hex-conservative" version = "0.1.2" @@ -467,12 +454,13 @@ dependencies = [ [[package]] name = "miniscript" -version = "12.3.5" +version = "13.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c" +checksum = "cd35e2c377504e50159561884b03610711db6c2fec6c89f2b98d94016684726b" dependencies = [ "bech32 0.11.0", "bitcoin 0.32.102", + "hex-conservative 1.2.0", ] [[package]] @@ -719,33 +707,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "simplicity-lang" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff7b10251528d46d9cf60238267911984fedbb0948cccea98dd74fc6bf9f729" -dependencies = [ - "bitcoin 0.32.102", - "bitcoin_hashes 1.2.0", - "byteorder", - "elements", - "getrandom", - "ghost-cell", - "hex-conservative 1.2.0", - "miniscript", - "simplicity-sys", -] - -[[package]] -name = "simplicity-sys" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955935733b2735665b4735d9b1361eade6be4c7aea6bca2640345e4c81756f58" -dependencies = [ - "bitcoin_hashes 1.2.0", - "cc", -] - [[package]] name = "syn" version = "2.0.106" diff --git a/Cargo.toml b/Cargo.toml index f7918393..7149567a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,7 @@ base64 = ["bitcoin/base64", "elements/base64"] [dependencies] bitcoin = "0.32.0" elements = "0.27.0" -bitcoin-miniscript = { package = "miniscript", version = "12.0" } -simplicity = { package = "simplicity-lang", version = "0.9.0", optional = true } +bitcoin-miniscript = { package = "miniscript", version = "13.1" } # Do NOT use this as a feature! Use the `serde` feature instead. actual-serde = { package = "serde", version = "1.0", optional = true } diff --git a/contrib/test_vars.sh b/contrib/test_vars.sh index 227b292c..307179e3 100644 --- a/contrib/test_vars.sh +++ b/contrib/test_vars.sh @@ -8,7 +8,7 @@ FEATURES_WITH_STD="" # Test all these features without "std" enabled. -FEATURES_WITHOUT_STD="compiler trace serde rand base64 simplicity" +FEATURES_WITHOUT_STD="compiler trace serde rand base64" # Run these examples. EXAMPLES="htlc:compiler parse: sign_multisig: verify_tx: xpub_descriptors: taproot:compiler psbt_sign_finalize:base64" diff --git a/src/confidential/elip151.rs b/src/confidential/elip151.rs index 14622781..0a4fa7c7 100644 --- a/src/confidential/elip151.rs +++ b/src/confidential/elip151.rs @@ -207,7 +207,7 @@ mod test { (&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1;2>/*))"), Error::MultipathDescLenMismatch), ] { let err = confidential_descriptor(invalid_desc).unwrap_err(); - assert_eq!(err, expected_err); + assert_eq!(format!("{:?}", err), format!("{:?}", expected_err)); /* _i = _i + 1; println!("* Invalid Test vector {}", _i); diff --git a/src/confidential/mod.rs b/src/confidential/mod.rs index 828600a3..a6f49939 100644 --- a/src/confidential/mod.rs +++ b/src/confidential/mod.rs @@ -586,10 +586,10 @@ mod tests { .unwrap(), }; - assert_eq!( + assert!(matches!( desc.address(&secp, params).unwrap_err(), Error::Conversion(ConversionError::HardenedChild) - ); + )); } // Same case, reached through `at_derivation_index` on a hardened wildcard blinding @@ -605,10 +605,10 @@ mod tests { let desc = Descriptor::::from_str(&desc_str).unwrap(); let definite_desc = desc.at_derivation_index(1).unwrap(); - assert_eq!( + assert!(matches!( definite_desc.address(&secp, params).unwrap_err(), Error::Conversion(ConversionError::HardenedChild) - ); + )); } #[test] @@ -638,10 +638,10 @@ mod tests { // but we can't enforce this with the Descriptor generic. let desc_view_str = format!("ct({}/*,elwpkh({}))#ls6mx2ac", xprv, xpub); let desc_view = Descriptor::::from_str(&desc_view_str).unwrap(); - assert_eq!(desc_view.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into())); + assert!(matches!(desc_view.address(&secp, params).unwrap_err(), Error::Unexpected(e) if e == "wildcard blinding key")); let desc_bare_str = format!("ct({}/*,elwpkh({}))#czkz0hwn", xpub, xpub); let desc_bare = Descriptor::::from_str(&desc_bare_str).unwrap(); - assert_eq!(desc_bare.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into())); + assert!(matches!(desc_bare.address(&secp, params).unwrap_err(), Error::Unexpected(e) if e == "wildcard blinding key")); } } diff --git a/src/descriptor/checksum.rs b/src/descriptor/checksum.rs index 85ee4ec4..f0768955 100644 --- a/src/descriptor/checksum.rs +++ b/src/descriptor/checksum.rs @@ -8,8 +8,7 @@ use core::fmt; use core::iter::FromIterator; -use bitcoin_miniscript::expression::check_valid_chars; - +use crate::expression::check_valid_chars; use crate::Error; const INPUT_CHARSET: &str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "; diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 233d5311..2640cfaf 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -114,7 +114,7 @@ impl_from_tree!( // TODO: Confirm with Andrew about the descriptor type for dynafed // Assuming sh(wsh) for now. - let fed_desc = BtcDescriptor::::from_tree(&ms_expr)?; + let fed_desc = BtcDescriptor::::from_tree(ms_expr.root())?; let elem_desc = Descriptor::>::from_tree(&top.args[1])?; Ok(Pegin::new(fed_desc, elem_desc)) } else { @@ -252,7 +252,9 @@ impl Pegin { .into_bytes(); let mut t = TranslateTweak(&claim_script[..], secp); - let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(&self.fed_desc, &mut t) + let tweaked_desc = self + .fed_desc + .translate_pk(&mut t) .expect("Tweaking must succeed"); let res = tweaked_desc.get_satisfaction(satisfier)?; @@ -303,7 +305,8 @@ fn bitcoin_witness_script( ) -> Result { let mut t = TranslateTweak(claim_script, secp); - let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t) + let tweaked_desc = fed_desc + .translate_pk(&mut t) .expect("Tweaking must succeed"); Ok(tweaked_desc.explicit_script()?) } @@ -313,12 +316,14 @@ struct TranslateTweak<'a, 'b, C: secp256k1_zkp::Verification>( &'b secp256k1_zkp::Secp256k1, ); -impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator - for TranslateTweak<'a, 'b, C> +impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator for TranslateTweak<'a, 'b, C> where Pk: MiniscriptKey + ToPublicKey, C: secp256k1_zkp::Verification, { + type TargetPk = bitcoin::PublicKey; + type Error = (); + fn pk(&mut self, pk: &Pk) -> Result { Ok(tweak_key(&pk.to_public_key(), self.1, self.0)) } @@ -342,7 +347,7 @@ mod tests { type Segwitv0Script = bitcoin_miniscript::Miniscript; - let m = Segwitv0Script::parse(&s).unwrap(); + let m = Segwitv0Script::decode(&s).unwrap(); assert_eq!(m.encode(), s); BtcDescriptor::<_>::new_wsh(m).unwrap() } diff --git a/src/descriptor/pegin/legacy_pegin.rs b/src/descriptor/pegin/legacy_pegin.rs index 64fb9d5d..f11bab7d 100644 --- a/src/descriptor/pegin/legacy_pegin.rs +++ b/src/descriptor/pegin/legacy_pegin.rs @@ -32,7 +32,6 @@ use bitcoin::blockdata::{opcodes, script}; use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; use bitcoin::{self, hashes, ScriptBuf as BtcScript}; use bitcoin_miniscript::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; -use bitcoin_miniscript::TranslatePk as BtcTranslatePk; use elements::secp256k1_zkp; use crate::descriptor::checksum::{self, verify_checksum}; @@ -237,7 +236,10 @@ impl LegacyPegin { }; struct TranslateUnTweak; - impl bitcoin_miniscript::Translator for TranslateUnTweak { + impl bitcoin_miniscript::Translator for TranslateUnTweak { + type TargetPk = bitcoin::PublicKey; + type Error = (); + fn pk(&mut self, pk: &LegacyPeginKey) -> Result { Ok(*pk.as_untweaked()) } @@ -349,7 +351,7 @@ impl_from_tree!( let ms_str = top.args[0].to_string(); let ms_expr = BtcTree::from_str(&ms_str)?; // - let ms = BtcMiniscript::::from_tree(&ms_expr); + let ms = BtcMiniscript::::from_tree(ms_expr.root()); let desc = Descriptor::>::from_tree(&top.args[1]); Ok(LegacyPegin::from_ms_and_desc(desc?, ms?)) } else { diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 02c16295..9952b521 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -4,7 +4,6 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::{fmt, hash}; -use bitcoin_miniscript::expression::check_valid_chars; use elements::taproot::{ LeafVersion, TaprootBuilder, TaprootSpendInfo, TAPROOT_CONTROL_BASE_SIZE, TAPROOT_CONTROL_MAX_NODE_COUNT, TAPROOT_CONTROL_NODE_SIZE, @@ -14,7 +13,7 @@ use elements::{self, opcodes, secp256k1_zkp, Script}; use super::checksum::verify_checksum; use super::ELMTS_STR; use crate::descriptor::checksum; -use crate::expression::{self, FromTree}; +use crate::expression::{self, check_valid_chars, FromTree}; use crate::extensions::ParseableExt; use crate::miniscript::Miniscript; use crate::policy::semantic::Policy; @@ -38,9 +37,6 @@ pub enum TapTree { // in adding a LeafVersion with Leaf type here. All Miniscripts right now // are of Leafversion::default Leaf(Arc>), - /// A taproot leaf denoting a spending condition in terms of Simplicity - #[cfg(feature = "simplicity")] - SimplicityLeaf(Arc>), } /// A taproot descriptor @@ -119,8 +115,6 @@ impl TapTree { 1 + max(left_tree.taptree_height(), right_tree.taptree_height()) } TapTree::Leaf(..) => 0, - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(..) => 0, } } @@ -139,33 +133,12 @@ impl TapTree { Q: MiniscriptKey, Ext: Extension, { - #[cfg(feature = "simplicity")] - struct SimTranslator<'a, T>(&'a mut T); - - #[cfg(feature = "simplicity")] - impl<'a, Pk, T, Q, Error> simplicity::Translator for SimTranslator<'a, T> - where - Pk: MiniscriptKey, - T: Translator, - Q: MiniscriptKey, - { - fn pk(&mut self, pk: &Pk) -> Result { - self.0.pk(pk) - } - - fn sha256(&mut self, sha256: &Pk::Sha256) -> Result { - self.0.sha256(sha256) - } - } - let frag = match self { TapTree::Tree(l, r) => TapTree::Tree( Arc::new(l.translate_helper(t)?), Arc::new(r.translate_helper(t)?), ), TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)), - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(sim) => TapTree::SimplicityLeaf(Arc::new(sim.translate(&mut SimTranslator(t))?)) }; Ok(frag) } @@ -183,8 +156,6 @@ impl TapTree { Arc::new(r.translate_ext_helper(t)?), ), TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_ext(t)?)), - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(sim) => TapTree::SimplicityLeaf(Arc::clone(sim)), }; Ok(frag) } @@ -195,8 +166,6 @@ impl fmt::Display for TapTree { match self { TapTree::Tree(ref left, ref right) => write!(f, "{{{},{}}}", *left, *right), TapTree::Leaf(ref script) => write!(f, "{}", *script), - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(ref policy) => write!(f, "sim{{{}}}", policy), } } } @@ -206,8 +175,6 @@ impl fmt::Debug for TapTree { match self { TapTree::Tree(ref left, ref right) => write!(f, "{{{:?},{:?}}}", *left, *right), TapTree::Leaf(ref script) => write!(f, "{:?}", *script), - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(ref policy) => write!(f, "{:?}", policy), } } } @@ -294,9 +261,6 @@ impl Tr { for (_depth, script) in self.iter_scripts() { match script { TapLeafScript::Miniscript(ms) => ms.sanity_check()?, - // TODO: Add sanity check for Simplicity policies - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => {}, } } Ok(()) @@ -444,9 +408,6 @@ impl Tr { pub enum TapLeafScript<'a, Pk: MiniscriptKey, Ext: Extension> { /// Miniscript leaf Miniscript(&'a Miniscript), - /// Simplicity leaf - #[cfg(feature = "simplicity")] - Simplicity(&'a simplicity::Policy) } impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { @@ -454,17 +415,6 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { pub fn as_miniscript(&self) -> Option<&'a Miniscript> { match self { TapLeafScript::Miniscript(ms) => Some(ms), - #[cfg(feature = "simplicity")] - _ => None, - } - } - - /// Get the Simplicity policy at the leaf, if it exists. - #[cfg(feature = "simplicity")] - pub fn as_simplicity(&self) -> Option<&'a simplicity::Policy> { - match self { - TapLeafScript::Simplicity(sim) => Some(sim), - _ => None, } } @@ -472,8 +422,6 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { pub fn version(&self) -> LeafVersion { match self { TapLeafScript::Miniscript(..) => LeafVersion::default(), - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => simplicity::leaf_version(), } } @@ -481,9 +429,6 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { pub fn script_size(&self) -> usize { match self { TapLeafScript::Miniscript(ms) => ms.script_size(), - // Simplicity's witness script is always a 32-byte CMR - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => 32, } } @@ -492,12 +437,6 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { pub fn max_satisfaction_witness_elements(&self) -> Result { match self { TapLeafScript::Miniscript(ms) => ms.max_satisfaction_witness_elements(), - // Simplicity always has one witness element plus leaf script: - // (1) Encoded program+witness - // (2) CMR program - // The third element is the control block, which is not counted by this method. - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => Ok(2), } } @@ -505,11 +444,6 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { pub fn max_satisfaction_size(&self) -> Result { match self { TapLeafScript::Miniscript(ms) => ms.max_satisfaction_size(), - // There is currently no way to bound the Simplicity witness size without producing one - // We mark the witness size as malleable since it depends on the chosen spending path - // TODO: Add method to simplicity::Policy and use it here - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => Err(Error::AnalysisError(crate::AnalysisError::Malleable)) } } @@ -517,8 +451,6 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> { pub fn iter_pk(&self) -> Box + 'a> { match self { TapLeafScript::Miniscript(ms) => Box::new(ms.iter_pk()), - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(sim) => Box::new(sim.iter_pk()), } } } @@ -528,39 +460,20 @@ impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> { pub fn encode(&self) -> Script { match self { TapLeafScript::Miniscript(ms) => ms.encode(), - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(sim) => { - Script::from(sim.cmr().as_ref().to_vec()) - } } } /// Attempt to produce a malleable satisfying witness for the leaf script. - /// - /// Returns [`Error::CouldNotSatisfy`] for Simplicity leaves until their descriptor - /// integration is rewritten. pub fn satisfy_malleable>(&self, satisfier: S) -> Result>, Error> { match self { TapLeafScript::Miniscript(ms) => ms.satisfy_malleable(satisfier), - // There doesn't (yet?) exist a malleable satisfaction of Simplicity policy - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => self.satisfy(satisfier), } } /// Attempt to produce a non-malleable satisfying witness for the leaf script. - /// - /// Returns [`Error::CouldNotSatisfy`] for Simplicity leaves until their descriptor - /// integration is rewritten. pub fn satisfy>(&self, satisfier: S) -> Result>, Error> { match self { TapLeafScript::Miniscript(ms) => ms.satisfy(satisfier), - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(..) => { - // TODO: The descriptor rewrite must supply a real ElementsEnv and - // branded inference context for Simplicity 0.9 satisfaction. - Err(Error::CouldNotSatisfy) - } } } } @@ -602,10 +515,6 @@ where TapTree::Leaf(ref ms) => { return Some((depth, TapLeafScript::Miniscript(ms))) }, - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(ref sim) => { - return Some((depth, TapLeafScript::Simplicity(sim))) - } } } None @@ -619,11 +528,6 @@ impl_block_str!( // Helper function to parse taproot script path fn parse_tr_script_spend(tree: &expression::Tree,) -> Result, Error> { match tree { - #[cfg(feature = "simplicity")] - expression::Tree { name, args } if *name == "sim" && args.len() == 1 => { - let policy = crate::simplicity::PolicyWrapper::::from_str(args[0].name)?; - Ok(TapTree::SimplicityLeaf(Arc::new(policy.0))) - } expression::Tree { name, args } if !name.is_empty() && args.is_empty() => { let script = Miniscript::::from_str(name)?; Ok(TapTree::Leaf(Arc::new(script))) @@ -794,8 +698,6 @@ impl Liftable for TapTree { Ok(Policy::Threshold(1, vec![lift_helper(l)?, lift_helper(r)?])) } TapTree::Leaf(ref leaf) => leaf.lift(), - #[cfg(feature = "simplicity")] - TapTree::SimplicityLeaf(..) => panic!("FIXME: Cannot lift Simplicity policy to Miniscript semantic policy"), } } @@ -826,8 +728,6 @@ impl ForEachKey for Tr { .all(|(_d, script)| { match script { TapLeafScript::Miniscript(ms) => ms.for_each_key(&mut pred), - #[cfg(feature = "simplicity")] - TapLeafScript::Simplicity(sim) => crate::simplicity::for_each_key(sim, &mut pred), } }); script_keys_res && pred(&self.internal_key) @@ -955,52 +855,6 @@ mod tests { use super::*; use crate::{ForEachKey, NoExt}; - #[cfg(feature = "simplicity")] - #[test] - fn mixed_tree_satisfaction_skips_simplicity() { - let key = bitcoin::XOnlyPublicKey::from_str( - "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - ) - .unwrap(); - let ms = - Miniscript::::from_str(&format!("pk({})", key)).unwrap(); - let script = ms.encode(); - let leaf_hash = - elements::taproot::TapLeafHash::from_script(&script, LeafVersion::default()); - let sig = elements::SchnorrSig { - sig: secp256k1_zkp::schnorr::Signature::from_slice(&[1; 64]).unwrap(), - hash_ty: elements::SchnorrSighashType::Default, - }; - let mut satisfier = std::collections::HashMap::new(); - satisfier.insert((key, leaf_hash), sig); - - for tree in [ - format!("{{pk({0}),sim{{pk({0})}}}}", key), - format!("{{sim{{pk({0})}},pk({0})}}", key), - ] { - let desc = Tr::::from_str(&format!("eltr({},{})", key, tree)) - .unwrap(); - let control_block = desc - .spend_info() - .control_block(&(script.clone(), LeafVersion::default())) - .unwrap(); - let expected = ( - vec![sig.to_vec(), script.to_bytes(), control_block.serialize()], - Script::new(), - ); - assert_eq!(desc.get_satisfaction(&satisfier).unwrap(), expected); - assert_eq!(desc.get_satisfaction_mall(&satisfier).unwrap(), expected); - assert!(matches!( - desc.get_satisfaction(()), - Err(Error::CouldNotSatisfy) - )); - assert!(matches!( - desc.get_satisfaction_mall(()), - Err(Error::CouldNotSatisfy) - )); - } - } - #[test] fn test_for_each() { let desc = "eltr(acc0, { @@ -1046,21 +900,5 @@ mod tests { "eltr(internal,pk(a))#vadmk9gd", "internal", &[TapLeafScript::Miniscript(&ms)] ); - - #[cfg(feature = "simplicity")] - { - // Simplicity key spend - let sim = simplicity::Policy::Key("a".to_string()); - verify_from_str( - "eltr(internal,sim{pk(a)})#duhmnzmm", "internal", - &[TapLeafScript::Simplicity(&sim)] - ); - - // Mixed Miniscript and Simplicity - verify_from_str( - "eltr(internal,{pk(a),sim{pk(a)}})#7vmfhpaj", "internal", - &[TapLeafScript::Miniscript(&ms), TapLeafScript::Simplicity(&sim)] - ); - } } } diff --git a/src/expression.rs b/src/expression.rs index a884c78f..b0a3c9ba 100644 --- a/src/expression.rs +++ b/src/expression.rs @@ -7,7 +7,8 @@ use std::fmt; use std::str::FromStr; -use bitcoin_miniscript::expression::check_valid_chars; +use bitcoin_miniscript::descriptor::checksum::Error as ChecksumError; +use bitcoin_miniscript::{ParseError, ParseTreeError}; use crate::{errstr, Error, MAX_RECURSION_DEPTH}; @@ -215,6 +216,22 @@ impl<'a> Tree<'a> { } } +// TODO: remove this when we replace our expression.rs module with calls to the rust-miniscript expression module +/// Check that a string contains only characters from the descriptor `INPUT_CHARSET` +/// +/// `INPUT_CHARSET` is exactly the printable ASCII characters. rust-miniscript 13 +/// no longer exports its `check_valid_chars`, so this replaces it. +pub(crate) fn check_valid_chars(s: &str) -> Result<(), Error> { + for (pos, ch) in s.char_indices() { + if !(32..127).contains(&u32::from(ch)) { + let e = ChecksumError::InvalidCharacter { ch, pos }; + let e = ParseError::Tree(ParseTreeError::Checksum(e)); + return Err(Error::BtcError(bitcoin_miniscript::Error::Parse(e))); + } + } + Ok(()) +} + /// Parse a string as a u32, for timelocks or thresholds pub fn parse_num(s: &str) -> Result { if s.len() > 1 { diff --git a/src/lib.rs b/src/lib.rs index 489f6495..3f748c2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,8 +134,6 @@ pub mod miniscript; pub mod policy; pub mod psbt; -#[cfg(feature = "simplicity")] -mod simplicity; #[cfg(test)] mod test_utils; mod util; @@ -279,7 +277,7 @@ where } /// Miniscript Error -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum Error { /// Opcode appeared which is not part of the script subset InvalidOpcode(opcodes::All), diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index f5fd378d..5209e2b7 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -1077,7 +1077,7 @@ mod tests { assert!(Segwitv0Script::from_str_insane("🌏") .unwrap_err() .to_string() - .contains("unprintable character")); + .contains("invalid character")); } #[test] diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 949e8e87..ea3a1030 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -9,7 +9,6 @@ use std::collections::HashSet; use std::{error, fmt, str}; -use bitcoin_miniscript::expression::check_valid_chars; use elements::{LockTime, Sequence}; #[cfg(feature = "compiler")] use { @@ -28,7 +27,7 @@ use { }; use super::ENTAILMENT_MAX_TERMINALS; -use crate::expression::{self, FromTree}; +use crate::expression::{self, check_valid_chars, FromTree}; use crate::miniscript::types::extra_props::TimelockInfo; #[cfg(all(doc, not(feature = "compiler")))] use crate::Descriptor; diff --git a/src/policy/semantic.rs b/src/policy/semantic.rs index 77193a5f..8c6b003e 100644 --- a/src/policy/semantic.rs +++ b/src/policy/semantic.rs @@ -6,11 +6,11 @@ use std::str::FromStr; use std::{fmt, str}; -use bitcoin_miniscript::expression::check_valid_chars; use elements::{LockTime, Sequence}; use super::concrete::PolicyError; use super::ENTAILMENT_MAX_TERMINALS; +use crate::expression::check_valid_chars; use crate::{errstr, expression, AbsLockTime, Error, MiniscriptKey, Translator}; /// Abstract policy which corresponds to the semantics of a Miniscript diff --git a/src/simplicity.rs b/src/simplicity.rs deleted file mode 100644 index 2871a1a1..00000000 --- a/src/simplicity.rs +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 -use std::fmt; -use std::str::FromStr; -use std::sync::Arc; - -use simplicity::{FailEntropy, Policy}; - -use crate::policy::concrete::PolicyError; -use crate::{expression, Error, MiniscriptKey}; - -impl_from_tree!( - Policy, - fn from_tree(top: &expression::Tree) -> Result { - match (top.name, top.args.len() as u32) { - ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable(FailEntropy::ZERO)), - ("TRIVIAL", 0) => Ok(Policy::Trivial), - ("pk", 1) => expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Policy::Key)), - ("after", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num(x).map(Policy::After) - }), - ("older", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num(x).map(Policy::Older) - }), - ("sha256", 1) => expression::terminal(&top.args[0], |x| { - Pk::Sha256::from_str(x).map(Policy::Sha256) - }), - ("and", _) => { - if top.args.len() != 2 { - return Err(Error::PolicyError(PolicyError::NonBinaryArgAnd)); - } - let left = Arc::new(Policy::from_tree(&top.args[0])?); - let right = Arc::new(Policy::from_tree(&top.args[0])?); - Ok(Policy::And { left, right }) - } - ("or", _) => { - if top.args.len() != 2 { - return Err(Error::PolicyError(PolicyError::NonBinaryArgOr)); - } - let left = Arc::new(Policy::from_tree(&top.args[0])?); - let right = Arc::new(Policy::from_tree(&top.args[0])?); - Ok(Policy::Or { left, right }) - } - ("thresh", nsubs) => { - if nsubs == 0 { - return Err(Error::Unexpected("thresh without args".to_owned())); - } - if nsubs < 3 { - return Err(Error::Unexpected( - "thresh must have a threshold value and at least 2 children".to_owned(), - )); - } - if !top.args[0].args.is_empty() { - return Err(Error::Unexpected(top.args[0].args[0].name.to_owned())); - } - - let thresh: u32 = expression::parse_num(top.args[0].name)?; - if thresh >= nsubs { - return Err(Error::Unexpected(top.args[0].name.to_owned())); - } - - let mut subs = Vec::with_capacity(top.args.len() - 1); - for arg in &top.args[1..] { - subs.push(Policy::from_tree(arg)?); - } - Ok(Policy::Threshold(thresh as usize, subs)) - } - _ => Err(Error::Unexpected(top.name.to_owned())), - } - } -); - -// We cannot implement FromStr for Policy because neither is defined in this crate -// Use a crate-local wrapper type to avoid code repetition -// Users use `Tr` / `Descriptor` and never encounter this wrapper -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)] -pub(crate) struct PolicyWrapper(pub Policy); - -impl fmt::Debug for PolicyWrapper { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl_from_str!( - PolicyWrapper, - type Err = Error;, - fn from_str(s: &str) -> Result { - let tree = expression::Tree::from_str(s)?; - as expression::FromTree>::from_tree(&tree).map(PolicyWrapper) - } -); - -// We cannot implement ForEachKey for Policy because it is not defined in this crate -// We cannot use our wrapper because we don't own the Policy (we have a reference) -// Implementing a wrapper of Cow<'a, Policy> leads to lifetime issues -// when implementing ForEachKey, because for_each_key() has its own lifetime 'a -pub fn for_each_key<'a, Pk: MiniscriptKey + 'a, F: FnMut(&'a Pk) -> bool>(policy: &'a Policy, mut pred: F) -> bool -{ - let mut stack = vec![policy]; - - while let Some(top) = stack.pop() { - match top { - Policy::Key(key) => { - if !pred(key) { - return false; - } - } - Policy::And { left, right } | Policy::Or { left, right } => { - stack.push(right); - stack.push(left); - } - Policy::Threshold(_, sub_policies) => { - stack.extend(sub_policies.iter()); - } - _ => {} - } - } - - true -} - -#[cfg(test)] -mod tests { - use secp256k1::XOnlyPublicKey; - use crate::DescriptorPublicKey; - use super::*; - - #[test] - fn parse_bad_thresh() { - assert_eq!( - PolicyWrapper::::from_str("thresh()"), - Err(Error::Unexpected( - "thresh must have a threshold value and at least 2 children".to_string() - )), - ); - - assert_eq!( - PolicyWrapper::::from_str("thresh"), - Err(Error::Unexpected("thresh without args".to_string())), - ); - - assert_eq!( - PolicyWrapper::::from_str("thresh(0)"), - Err(Error::Unexpected( - "thresh must have a threshold value and at least 2 children".to_string() - )), - ); - - assert_eq!( - PolicyWrapper::::from_str("thresh(0,TRIVIAL)"), - Err(Error::Unexpected( - "thresh must have a threshold value and at least 2 children".to_string() - )), - ); - - assert!(PolicyWrapper::::from_str("thresh(0,TRIVIAL,TRIVIAL)").is_ok()); - assert!(PolicyWrapper::::from_str("thresh(2,TRIVIAL,TRIVIAL)").is_ok()); - - assert_eq!( - PolicyWrapper::::from_str("thresh(3,TRIVIAL,TRIVIAL)"), - Err(Error::Unexpected("3".to_string())), - ); - } - - #[test] - fn decode_xpub() { - let s = "[78412e3a/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*"; - let decoded_key = DescriptorPublicKey::from_str(s).expect("constant key"); - let s = format!("pk({})", s); - let decoded_policy = PolicyWrapper::::from_str(&s).expect("decode policy").0; - - if let Policy::Key(key) = decoded_policy { - assert_eq!(decoded_key, key); - } else { - panic!("Decoded policy should be public key") - } - } -}