diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index 9d5dbcc85..1b12245fa 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -26,6 +26,7 @@ use bitcoin::{Address, Network, Script}; use super::checksum::{desc_checksum, verify_checksum}; use crate::expression::{self, FromTree}; use crate::miniscript::context::ScriptContext; +use crate::miniscript::musig_key::KeyExpr; use crate::policy::{semantic, Liftable}; use crate::prelude::*; use crate::util::{varint_len, witness_to_scriptsig}; @@ -219,7 +220,7 @@ impl Pkh { /// 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)) + 4 * (1 + 73 + BareCtx::pk_len(&KeyExpr::SingleKey(self.pk.clone()))) } } diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 2b02dc165..0ad79ae4e 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -34,6 +34,7 @@ use bitcoin::{self, secp256k1, Address, Network, Script, TxIn}; use sync::Arc; use self::checksum::verify_checksum; +use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::{Legacy, Miniscript, Segwitv0}; use crate::prelude::*; use crate::{ @@ -180,7 +181,7 @@ impl Descriptor { // roundabout way to constuct `c:pk_k(pk)` let ms: Miniscript = Miniscript::from_ast(miniscript::decode::Terminal::Check(Arc::new( - Miniscript::from_ast(miniscript::decode::Terminal::PkK(pk)) + Miniscript::from_ast(miniscript::decode::Terminal::PkK(KeyExpr::SingleKey(pk))) .expect("Type check cannot fail"), ))) .expect("Type check cannot fail"); diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 9f306ccbc..78c022242 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -24,6 +24,7 @@ use super::checksum::{desc_checksum, verify_checksum}; use super::SortedMultiVec; use crate::expression::{self, FromTree}; use crate::miniscript::context::{ScriptContext, ScriptContextError}; +use crate::miniscript::musig_key::KeyExpr; use crate::policy::{semantic, Liftable}; use crate::prelude::*; use crate::util::varint_len; @@ -330,7 +331,7 @@ impl Wpkh { /// 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) + 4 + 1 + 73 + Segwitv0::pk_len(&KeyExpr::SingleKey(self.pk.clone())) } } diff --git a/src/descriptor/sortedmulti.rs b/src/descriptor/sortedmulti.rs index a79e84507..9646d9fb1 100644 --- a/src/descriptor/sortedmulti.rs +++ b/src/descriptor/sortedmulti.rs @@ -25,6 +25,7 @@ use bitcoin::blockdata::script; use crate::miniscript::context::ScriptContext; use crate::miniscript::decode::Terminal; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; +use crate::miniscript::musig_key::KeyExpr; use crate::prelude::*; use crate::{ errstr, expression, miniscript, policy, script_num_size, Error, ForEachKey, Miniscript, @@ -183,7 +184,11 @@ impl SortedMultiVec { script_num_size(self.k) + 1 + script_num_size(self.pks.len()) - + self.pks.iter().map(|pk| Ctx::pk_len(pk)).sum::() + + self + .pks + .iter() + .map(|pk| Ctx::pk_len(&KeyExpr::SingleKey(pk.clone()))) + .sum::() } /// Maximum number of witness elements used to satisfy the Miniscript diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 2c2c4b917..1bcb0105f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -593,7 +593,10 @@ where Terminal::PkK(ref pk) => { debug_assert_eq!(node_state.n_evaluated, 0); debug_assert_eq!(node_state.n_satisfied, 0); - let res = self.stack.evaluate_pk(&mut self.verify_sig, *pk); + let res = match pk.single_key() { + Some(pk) => self.stack.evaluate_pk(&mut self.verify_sig, *pk), + None => None, + }; if res.is_some() { return res; } @@ -868,10 +871,11 @@ where // evaluate each key with as a pk // note that evaluate_pk will error on non-empty incorrect sigs // push 1 on satisfied sigs and push 0 on empty sigs - match self - .stack - .evaluate_pk(&mut self.verify_sig, subs[node_state.n_evaluated]) - { + let temp = match subs[node_state.n_evaluated].single_key() { + Some(pkk) => self.stack.evaluate_pk(&mut self.verify_sig, *pkk), + None => None, + }; + match temp { Some(Ok(x)) => { self.push_evaluation_state( node_state.node, diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs index b21756430..ae9fee515 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -26,6 +26,7 @@ use bitcoin::blockdata::{opcodes, script}; use sync::Arc; use crate::miniscript::context::SigType; +use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::types::{self, Property}; use crate::miniscript::ScriptContext; use crate::prelude::*; @@ -80,7 +81,7 @@ impl Terminal { Pk::RawPkHash: 'a, { match *self { - Terminal::PkK(ref p) => pred(p), + Terminal::PkK(ref p) => p.for_each_key(pred), Terminal::PkH(ref p) => pred(p), Terminal::RawPkH(..) | Terminal::After(..) @@ -112,9 +113,8 @@ impl Terminal { && c.real_for_each_key(pred) } Terminal::Thresh(_, ref subs) => subs.iter().all(|sub| sub.real_for_each_key(pred)), - Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => { - keys.iter().all(|key| pred(key)) - } + Terminal::Multi(_, ref keys) => keys.iter().all(|key| pred(key)), + Terminal::MultiA(_, ref keys) => keys.iter().all(|key| key.for_each_key(&mut *pred)), } } @@ -125,7 +125,7 @@ impl Terminal { T: Translator, { let frag: Terminal = match *self { - Terminal::PkK(ref p) => Terminal::PkK(t.pk(p)?), + Terminal::PkK(ref p) => Terminal::PkK(p.translate_pk(t)?), Terminal::PkH(ref p) => Terminal::PkH(t.pk(p)?), Terminal::RawPkH(ref p) => Terminal::RawPkH(t.pkh(p)?), Terminal::After(n) => Terminal::After(n), @@ -186,7 +186,8 @@ impl Terminal { Terminal::Multi(k, keys?) } Terminal::MultiA(k, ref keys) => { - let keys: Result, _> = keys.iter().map(|k| t.pk(k)).collect(); + let keys: Result>, _> = + keys.iter().map(|k| k.translate_pk(t)).collect(); Terminal::MultiA(k, keys?) } }; @@ -455,7 +456,7 @@ impl_from_tree!( } let mut unwrapped = match (frag_name, top.args.len()) { ("pk_k", 1) => { - expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkK)) + expression::unary(top, Terminal::PkK) } ("pk_h", 1) => expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkH)), ("after", 1) => expression::terminal(&top.args[0], |x| { @@ -522,15 +523,18 @@ impl_from_tree!( return Err(errstr("higher threshold than there were keys in multi")); } - let pks: Result, _> = top.args[1..] - .iter() - .map(|sub| expression::terminal(sub, Pk::from_str)) - .collect(); - if frag_name == "multi" { + let pks: Result, _> = top.args[1..] + .iter() + .map(|sub| expression::terminal(sub, Pk::from_str)) + .collect(); pks.map(|pks| Terminal::Multi(k, pks)) } else { // must be multi_a + let pks: Result>, _> = top.args[1..] + .iter() + .map(|sub| expression::terminal(sub, KeyExpr::::from_str)) + .collect(); pks.map(|pks| Terminal::MultiA(k, pks)) } } @@ -734,7 +738,7 @@ impl Terminal { builder = builder.push_ms_key::<_, Ctx>(&keys[0]); builder = builder.push_opcode(opcodes::all::OP_CHECKSIG); for pk in keys.iter().skip(1) { - builder = builder.push_ms_key::<_, Ctx>(pk); + builder = builder.push_ms_key::<_, Ctx>(&pk); builder = builder.push_opcode(opcodes::all::OP_CHECKSIGADD); } builder @@ -793,7 +797,10 @@ impl Terminal { script_num_size(k) + 1 + script_num_size(pks.len()) - + pks.iter().map(|pk| Ctx::pk_len(pk)).sum::() + + pks + .iter() + .map(|pk| Ctx::pk_len(&KeyExpr::SingleKey(pk.clone()))) + .sum::() } Terminal::MultiA(k, ref pks) => { script_num_size(k) diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs index d838ee4ba..bd0a278f6 100644 --- a/src/miniscript/context.rs +++ b/src/miniscript/context.rs @@ -26,6 +26,7 @@ use crate::miniscript::limits::{ MAX_SCRIPT_SIZE, MAX_STACK_SIZE, MAX_STANDARD_P2WSH_SCRIPT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEMS, }; +use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::types; use crate::prelude::*; use crate::util::witness_to_scriptsig; @@ -77,6 +78,8 @@ pub enum ScriptContextError { CheckMultiSigLimitExceeded, /// MultiA is only allowed in post tapscript MultiANotAllowed, + /// Musig is only allowed in tapscript and taproot descriptors + MusigNotAllowed, } #[cfg(feature = "std")] @@ -100,6 +103,7 @@ impl error::Error for ScriptContextError { | TaprootMultiDisabled | StackSizeLimitExceeded { .. } | CheckMultiSigLimitExceeded + | MusigNotAllowed | MultiANotAllowed => None, } } @@ -180,6 +184,9 @@ impl fmt::Display for ScriptContextError { ScriptContextError::MultiANotAllowed => { write!(f, "Multi a(CHECKSIGADD) only allowed post tapscript") } + ScriptContextError::MusigNotAllowed => { + write!(f, "Musig is only allowed in tapscript ") + } } } } @@ -332,7 +339,7 @@ where /// Note that this includes the serialization prefix. Returns /// 34/66 for Bare/Legacy based on key compressedness /// 34 for Segwitv0, 33 for Tap - fn pk_len(pk: &Pk) -> usize; + fn pk_len(pk: &KeyExpr) -> usize; /// Local helper function to display error messages with context fn name_str() -> &'static str; @@ -384,12 +391,17 @@ impl ScriptContext for Legacy { } match ms.node { - Terminal::PkK(ref key) if key.is_x_only_key() => { - return Err(ScriptContextError::XOnlyKeysNotAllowed( - key.to_string(), - Self::name_str(), - )) - } + Terminal::PkK(ref key) => match key { + KeyExpr::::SingleKey(pk) => { + if pk.is_x_only_key() { + return Err(ScriptContextError::XOnlyKeysNotAllowed( + pk.to_string(), + Self::name_str(), + )); + } + } + KeyExpr::::MuSig(_) => return Err(ScriptContextError::MusigNotAllowed), + }, Terminal::Multi(_k, ref pks) => { if pks.len() > MAX_PUBKEYS_PER_MULTISIG { return Err(ScriptContextError::CheckMultiSigLimitExceeded); @@ -443,11 +455,16 @@ impl ScriptContext for Legacy { ms.ext.max_sat_size.map(|x| x.1) } - fn pk_len(pk: &Pk) -> usize { - if pk.is_uncompressed() { - 66 - } else { - 34 + fn pk_len(pk: &KeyExpr) -> usize { + match pk { + KeyExpr::::SingleKey(pkk) => { + if pkk.is_uncompressed() { + 66 + } else { + 34 + } + } + KeyExpr::::MuSig(_) => 32, } } @@ -490,17 +507,22 @@ impl ScriptContext for Segwitv0 { } match ms.node { - Terminal::PkK(ref pk) => { - if pk.is_uncompressed() { - return Err(ScriptContextError::CompressedOnly(pk.to_string())); - } else if pk.is_x_only_key() { - return Err(ScriptContextError::XOnlyKeysNotAllowed( - pk.to_string(), - Self::name_str(), - )); + Terminal::PkK(ref key) => match key { + KeyExpr::::SingleKey(pk) => { + if pk.is_uncompressed() { + return Err(ScriptContextError::CompressedOnly(pk.to_string())); + } else if pk.is_x_only_key() { + return Err(ScriptContextError::XOnlyKeysNotAllowed( + pk.to_string(), + Self::name_str(), + )); + } + Ok(()) } - Ok(()) - } + KeyExpr::::MuSig(_) => { + return Err(ScriptContextError::MusigNotAllowed); + } + }, Terminal::Multi(_k, ref pks) => { if pks.len() > MAX_PUBKEYS_PER_MULTISIG { return Err(ScriptContextError::CheckMultiSigLimitExceeded); @@ -567,8 +589,11 @@ impl ScriptContext for Segwitv0 { ms.ext.max_sat_size.map(|x| x.0) } - fn pk_len(_pk: &Pk) -> usize { - 34 + fn pk_len(pk: &KeyExpr) -> usize { + match pk { + KeyExpr::::SingleKey(_) => 34, + KeyExpr::::MuSig(_) => 32, + } } fn name_str() -> &'static str { @@ -618,13 +643,24 @@ impl ScriptContext for Tap { } match ms.node { - Terminal::PkK(ref pk) => { - if pk.is_uncompressed() { + Terminal::PkK(ref key) => { + if key.iter().all(|pk| !pk.is_uncompressed()) { + Ok(()) + } else { return Err(ScriptContextError::UncompressedKeysNotAllowed); } - Ok(()) } Terminal::Multi(..) => Err(ScriptContextError::TaprootMultiDisabled), + Terminal::MultiA(_, ref keys) => { + if keys + .iter() + .all(|keyexpr| keyexpr.iter().all(|pk| !pk.is_uncompressed())) + { + Ok(()) + } else { + return Err(ScriptContextError::UncompressedKeysNotAllowed); + } + } _ => Ok(()), } } @@ -677,8 +713,11 @@ impl ScriptContext for Tap { SigType::Schnorr } - fn pk_len(_pk: &Pk) -> usize { - 33 + fn pk_len(pk: &KeyExpr) -> usize { + match pk { + KeyExpr::::SingleKey(_) => 33, + KeyExpr::::MuSig(_) => 32, + } } fn name_str() -> &'static str { @@ -712,11 +751,19 @@ impl ScriptContext for BareCtx { return Err(ScriptContextError::MaxWitnessScriptSizeExceeded); } match ms.node { - Terminal::PkK(ref key) if key.is_x_only_key() => { - return Err(ScriptContextError::XOnlyKeysNotAllowed( - key.to_string(), - Self::name_str(), - )) + Terminal::PkK(ref key) => { + match key { + KeyExpr::::SingleKey(pk) => { + if pk.is_x_only_key() { + return Err(ScriptContextError::XOnlyKeysNotAllowed( + key.to_string(), + Self::name_str(), + )); + } + } + KeyExpr::::MuSig(_) => return Err(ScriptContextError::MusigNotAllowed), + } + Ok(()) } Terminal::Multi(_k, ref pks) => { if pks.len() > MAX_PUBKEYS_PER_MULTISIG { @@ -753,7 +800,13 @@ impl ScriptContext for BareCtx { match &ms.node { Terminal::Check(ref ms) => match &ms.node { Terminal::RawPkH(_pkh) => Ok(()), - Terminal::PkK(_pk) | Terminal::PkH(_pk) => Ok(()), + Terminal::PkH(_pk) => Ok(()), + Terminal::PkK(key) => match key { + KeyExpr::::SingleKey(_pk) => Ok(()), + KeyExpr::::MuSig(_) => { + return Err(Error::NonStandardBareScript); + } + }, _ => Err(Error::NonStandardBareScript), }, Terminal::Multi(_k, subs) if subs.len() <= 3 => Ok(()), @@ -766,11 +819,16 @@ impl ScriptContext for BareCtx { ms.ext.max_sat_size.map(|x| x.1) } - fn pk_len(pk: &Pk) -> usize { - if pk.is_uncompressed() { - 65 - } else { - 33 + fn pk_len(pk: &KeyExpr) -> usize { + match pk { + KeyExpr::::SingleKey(pk) => { + if pk.is_uncompressed() { + 65 + } else { + 33 + } + } + KeyExpr::::MuSig(_pk) => 32, } } @@ -827,7 +885,7 @@ impl ScriptContext for NoChecks { panic!("Tried to compute a satisfaction size bound on a no-checks ecdsa miniscript") } - fn pk_len(_pk: &Pk) -> usize { + fn pk_len(_pk: &KeyExpr) -> usize { panic!("Tried to compute a pk len bound on a no-checks ecdsa miniscript") } diff --git a/src/miniscript/decode.rs b/src/miniscript/decode.rs index d7f72c1d8..f09f176f8 100644 --- a/src/miniscript/decode.rs +++ b/src/miniscript/decode.rs @@ -28,6 +28,7 @@ use sync::Arc; use crate::miniscript::lex::{Token as Tk, TokenIter}; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; +use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::types::extra_props::ExtData; use crate::miniscript::types::{Property, Type}; use crate::miniscript::ScriptContext; @@ -132,7 +133,7 @@ pub enum Terminal { False, // pubkey checks /// `` - PkK(Pk), + PkK(KeyExpr), /// `DUP HASH160 EQUALVERIFY` PkH(Pk), /// Only for parsing PkH for Script @@ -192,7 +193,7 @@ pub enum Terminal { /// k ()* n CHECKMULTISIG Multi(usize, Vec), /// CHECKSIG ( CHECKSIGADD)*(n-1) k NUMEQUAL - MultiA(usize, Vec), + MultiA(usize, Vec>), } macro_rules! match_token { @@ -300,12 +301,12 @@ pub fn parse( Tk::Bytes33(pk) => { let ret = Ctx::Key::from_slice(pk) .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?; - term.reduce0(Terminal::PkK(ret))? + term.reduce0(Terminal::PkK(KeyExpr::SingleKey(ret)))? }, Tk::Bytes65(pk) => { let ret = Ctx::Key::from_slice(pk) .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?; - term.reduce0(Terminal::PkK(ret))? + term.reduce0(Terminal::PkK(KeyExpr::SingleKey(ret)))? }, // Note this does not collide with hash32 because they always followed by equal // and would be parsed in different branch. If we get a naked Bytes32, it must be @@ -321,7 +322,7 @@ pub fn parse( // Finally for the first case, K being parsed as a solo expression is a Pk type Tk::Bytes32(pk) => { let ret = Ctx::Key::from_slice(pk).map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?; - term.reduce0(Terminal::PkK(ret))? + term.reduce0(Terminal::PkK(KeyExpr::SingleKey(ret)))? }, // checksig Tk::CheckSig => { @@ -494,15 +495,15 @@ pub fn parse( while tokens.peek() == Some(&Tk::CheckSigAdd) { match_token!( tokens, - Tk::CheckSigAdd, Tk::Bytes32(pk) => keys.push(::from_slice(pk) - .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?), + Tk::CheckSigAdd, Tk::Bytes32(pk) => keys.push(KeyExpr::SingleKey(::from_slice(pk) + .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?)), ); } // Last key must be with a CheckSig match_token!( tokens, - Tk::CheckSig, Tk::Bytes32(pk) => keys.push(::from_slice(pk) - .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?), + Tk::CheckSig, Tk::Bytes32(pk) => keys.push(KeyExpr::SingleKey(::from_slice(pk) + .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?)), ); keys.reverse(); term.reduce0(Terminal::MultiA(k as usize, keys))?; diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index 1df400902..a9825f48d 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -22,6 +22,7 @@ use sync::Arc; use super::decode::Terminal; use super::{Miniscript, MiniscriptKey, ScriptContext}; +use crate::miniscript::musig_key::KeyExprIter; use crate::prelude::*; /// Iterator-related extensions for [Miniscript] @@ -125,8 +126,17 @@ impl Miniscript { /// `miniscript.iter_pubkeys().collect()`. pub fn get_leapk(&self) -> Vec { match self.node { - Terminal::PkK(ref key) | Terminal::PkH(ref key) => vec![key.clone()], - Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => keys.clone(), + Terminal::PkK(ref key) => key.iter().map(|pk| pk.clone()).collect(), + Terminal::PkH(ref key) => vec![key.clone()], + Terminal::Multi(_, ref keys) => keys.clone(), + Terminal::MultiA(_, ref keys) => { + let mut res: Vec = Vec::::new(); + for key in keys { + let temp: Vec = key.iter().map(|pk| pk.clone()).collect(); + res.extend(temp); + } + res + } _ => vec![], } } @@ -143,9 +153,17 @@ impl Miniscript { pub fn get_leapkh(&self) -> Vec { match self.node { Terminal::RawPkH(ref hash) => vec![hash.clone()], - Terminal::PkK(ref key) | Terminal::PkH(ref key) => vec![key.to_pubkeyhash()], - Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => { - keys.iter().map(Pk::to_pubkeyhash).collect() + Terminal::PkH(ref key) => vec![key.to_pubkeyhash()], + Terminal::PkK(ref key) => key.iter().map(|pk| pk.to_pubkeyhash()).collect(), + Terminal::Multi(_, ref keys) => keys.iter().map(Pk::to_pubkeyhash).collect(), + Terminal::MultiA(_, ref keys) => { + let mut res: Vec = Vec::::new(); + for key in keys { + let temp: Vec = + key.iter().map(|pk| pk.to_pubkeyhash()).collect(); + res.extend(temp) + } + res } _ => vec![], } @@ -161,13 +179,28 @@ impl Miniscript { pub fn get_leapk_pkh(&self) -> Vec> { match self.node { Terminal::RawPkH(ref hash) => vec![PkPkh::HashedPubkey(hash.clone())], - Terminal::PkH(ref key) | Terminal::PkK(ref key) => { + Terminal::PkH(ref key) => { vec![PkPkh::PlainPubkey(key.clone())] } - Terminal::Multi(_, ref keys) | Terminal::MultiA(_, ref keys) => keys + Terminal::PkK(ref key) => key + .iter() + .map(|pk| PkPkh::PlainPubkey(pk.clone())) + .collect(), + Terminal::Multi(_, ref keys) => keys .iter() .map(|key| PkPkh::PlainPubkey(key.clone())) .collect(), + Terminal::MultiA(_, ref keys) => { + let mut res: Vec> = Vec::>::new(); + for key in keys { + let temp: Vec> = key + .iter() + .map(|pk| PkPkh::PlainPubkey(pk.clone())) + .collect(); + res.extend(temp) + } + res + } _ => vec![], } } @@ -176,16 +209,20 @@ impl Miniscript { /// if any. Otherwise returns `Option::None`. /// /// NB: The function analyzes only single miniscript item and not any of its descendants in AST. - pub fn get_nth_pk(&self, n: usize) -> Option { + + pub fn my_get_nth_pk(&self, n: usize) -> Option { match (&self.node, n) { - (&Terminal::PkK(ref key), 0) | (&Terminal::PkH(ref key), 0) => Some(key.clone()), - (&Terminal::Multi(_, ref keys), _) | (&Terminal::MultiA(_, ref keys), _) => { - keys.get(n).cloned() + (&Terminal::PkH(ref key), 0) => Some(key.clone()), + (&Terminal::Multi(_, ref keys), _) => { + if n < keys.len() { + Some(keys[n].clone()) + } else { + None + } } _ => None, } } - /// Returns `Option::Some` with hash of n'th public key from the current miniscript item, /// if any. Otherwise returns `Option::None`. /// @@ -193,14 +230,17 @@ impl Miniscript { /// returns it cloned copy. /// /// NB: The function analyzes only single miniscript item and not any of its descendants in AST. - pub fn get_nth_pkh(&self, n: usize) -> Option { + + pub fn my_get_nth_pkh(&self, n: usize) -> Option { match (&self.node, n) { (&Terminal::RawPkH(ref hash), 0) => Some(hash.clone()), - (&Terminal::PkK(ref key), 0) | (&Terminal::PkH(ref key), 0) => { - Some(key.to_pubkeyhash()) - } - (&Terminal::Multi(_, ref keys), _) | (&Terminal::MultiA(_, ref keys), _) => { - keys.get(n).map(Pk::to_pubkeyhash) + (&Terminal::PkH(ref key), 0) => Some(key.to_pubkeyhash()), + (&Terminal::Multi(_, ref keys), _) => { + if n < keys.len() { + Some(keys[n].to_pubkeyhash()) + } else { + None + } } _ => None, } @@ -210,20 +250,53 @@ impl Miniscript { /// if any. Otherwise returns `Option::None`. /// /// NB: The function analyzes only single miniscript item and not any of its descendants in AST. - pub fn get_nth_pk_pkh(&self, n: usize) -> Option> { + + pub fn my_get_nth_pk_pkh(&self, n: usize) -> Option> { match (&self.node, n) { (&Terminal::RawPkH(ref hash), 0) => Some(PkPkh::HashedPubkey(hash.clone())), - (&Terminal::PkH(ref key), 0) | (&Terminal::PkK(ref key), 0) => { - Some(PkPkh::PlainPubkey(key.clone())) - } - (&Terminal::Multi(_, ref keys), _) | (&Terminal::MultiA(_, ref keys), _) => { - keys.get(n).map(|key| PkPkh::PlainPubkey(key.clone())) + (&Terminal::PkH(ref key), 0) => Some(PkPkh::PlainPubkey(key.clone())), + (&Terminal::Multi(_, ref keys), _) => { + if n < keys.len() { + Some(PkPkh::PlainPubkey(keys[n].clone())) + } else { + None + } } _ => None, } } } +/// Parent iter for all the below iters +pub struct BaseIter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> { + node_iter: Iter<'a, Pk, Ctx>, + curr_node: Option<&'a Miniscript>, + musig_iter: Option>, // If current fragment is PkK or MultiA, then this is the iterator over public keys + multi_a_len: Option, // Helps in checking whether current fragment is MultiA or not + key_index: usize, +} +impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> BaseIter<'a, Pk, Ctx> { + fn goto_next_node(&mut self) -> () { + self.curr_node = self.node_iter.next(); + self.key_index = 0; + let mut multi_a_len = None; + self.musig_iter = match self.curr_node { + Some(script) => match script.node { + Terminal::PkK(ref pk) => { + multi_a_len = Some(1 as u32); + Some(pk.iter()) + } + Terminal::MultiA(_, ref keys) => { + multi_a_len = Some(keys.len() as u32); + Some(keys[0].iter()) + } + _ => None, + }, + None => None, + }; + self.multi_a_len = multi_a_len; + } +} /// Iterator for traversing all [Miniscript] miniscript AST references starting from some specific /// node which constructs the iterator via [Miniscript::iter] method. pub struct Iter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> { @@ -285,43 +358,95 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for Iter<'a, Pk, Ctx> { curr } } - /// Iterator for traversing all [MiniscriptKey]'s in AST starting from some specific node which /// constructs the iterator via [Miniscript::iter_pk] method. pub struct PkIter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> { - node_iter: Iter<'a, Pk, Ctx>, - curr_node: Option<&'a Miniscript>, - key_index: usize, + base_iter: BaseIter<'a, Pk, Ctx>, } impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkIter<'a, Pk, Ctx> { fn new(miniscript: &'a Miniscript) -> Self { let mut iter = Iter::new(miniscript); - PkIter { - curr_node: iter.next(), + let curr_node = iter.next(); + let mut multi_a_len = None; + let musig_iter = match curr_node { + Some(script) => match script.node { + Terminal::PkK(ref pk) => { + multi_a_len = Some(1 as u32); + Some(pk.iter()) + } + Terminal::MultiA(_, ref keys) => { + multi_a_len = Some(keys.len() as u32); + Some(keys[0].iter()) + } + _ => None, + }, + None => None, + }; + let bs_iter = BaseIter { + curr_node: curr_node, node_iter: iter, + musig_iter: musig_iter, key_index: 0, - } + multi_a_len: multi_a_len, + }; + PkIter { base_iter: bs_iter } } } impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> { type Item = Pk; - fn next(&mut self) -> Option { loop { - match self.curr_node { + match self.base_iter.curr_node { None => break None, - Some(node) => match node.get_nth_pk(self.key_index) { - None => { - self.curr_node = self.node_iter.next(); - self.key_index = 0; - continue; + Some(script) => match &script.node { + Terminal::PkK(_) => { + // check if musig_iter has something + match self.base_iter.musig_iter.as_mut().unwrap().next() { + Some(pk) => break Some(pk.clone()), + None => { + self.base_iter.goto_next_node(); + continue; + } + } } - Some(pk) => { - self.key_index += 1; - break Some(pk); + Terminal::MultiA(_, keys) => { + match self.base_iter.musig_iter.as_mut().unwrap().next() { + Some(pk) => break Some(pk.clone()), + None => { + // When the current iterator has yielded all the keys + let vec_size = self.base_iter.multi_a_len.unwrap(); + self.base_iter.key_index += 1; + if (self.base_iter.key_index as u32) < vec_size { + // goto the next KeyExpr in the vector + self.base_iter.musig_iter = + Some(keys[self.base_iter.key_index].iter()); + match self.base_iter.musig_iter.as_mut().unwrap().next() { + None => { + self.base_iter.key_index += 1; + continue; + } + Some(pk) => break Some(pk.clone()), + } + } else { + // if we have exhausted all the KeyExpr + self.base_iter.goto_next_node(); + continue; + } + } + } } + _ => match script.my_get_nth_pk(self.base_iter.key_index) { + Some(pk) => { + self.base_iter.key_index += 1; + break Some(pk); + } + None => { + self.base_iter.goto_next_node(); + continue; + } + }, }, } } @@ -331,39 +456,92 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkIter<'a, Pk, Ctx> /// Iterator for traversing all [MiniscriptKey] hashes in AST starting from some specific node which /// constructs the iterator via [Miniscript::iter_pkh] method. pub struct PkhIter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> { - node_iter: Iter<'a, Pk, Ctx>, - curr_node: Option<&'a Miniscript>, - key_index: usize, + base_iter: BaseIter<'a, Pk, Ctx>, } impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkhIter<'a, Pk, Ctx> { fn new(miniscript: &'a Miniscript) -> Self { let mut iter = Iter::new(miniscript); - PkhIter { - curr_node: iter.next(), + let curr_node = iter.next(); + let mut multi_a_len = None; + let musig_iter = match curr_node { + Some(script) => match script.node { + Terminal::PkK(ref pk) => { + multi_a_len = Some(1 as u32); + Some(pk.iter()) + } + Terminal::MultiA(_, ref keys) => { + multi_a_len = Some(keys.len() as u32); + Some(keys[0].iter()) + } + _ => None, + }, + None => None, + }; + let bs_iter = BaseIter { + curr_node: curr_node, node_iter: iter, + musig_iter: musig_iter, + multi_a_len: multi_a_len, key_index: 0, - } + }; + PkhIter { base_iter: bs_iter } } } impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkhIter<'a, Pk, Ctx> { type Item = Pk::RawPkHash; - fn next(&mut self) -> Option { loop { - match self.curr_node { + match self.base_iter.curr_node { None => break None, - Some(node) => match node.get_nth_pkh(self.key_index) { - None => { - self.curr_node = self.node_iter.next(); - self.key_index = 0; - continue; + Some(script) => match &script.node { + Terminal::PkK(_) => { + // check if musig_iter has something + match self.base_iter.musig_iter.as_mut().unwrap().next() { + Some(pk) => break Some(pk.to_pubkeyhash()), + None => { + self.base_iter.goto_next_node(); + continue; + } + } } - Some(pk) => { - self.key_index += 1; - break Some(pk); + Terminal::MultiA(_, keys) => { + match self.base_iter.musig_iter.as_mut().unwrap().next() { + Some(pk) => break Some(pk.to_pubkeyhash()), + None => { + // When the current iterator has yielded all the keys + let vec_size = self.base_iter.multi_a_len.unwrap(); + self.base_iter.key_index += 1; + if (self.base_iter.key_index as u32) < vec_size { + // goto the next KeyExpr in the vector + self.base_iter.musig_iter = + Some(keys[self.base_iter.key_index].iter()); + match self.base_iter.musig_iter.as_mut().unwrap().next() { + None => { + self.base_iter.key_index += 1; + continue; + } + Some(pk) => break Some(pk.to_pubkeyhash()), + } + } else { + // if we have exhausted all the KeyExpr + self.base_iter.goto_next_node(); + continue; + } + } + } } + _ => match script.my_get_nth_pk(self.base_iter.key_index) { + Some(pk) => { + self.base_iter.key_index += 1; + break Some(pk.to_pubkeyhash()); + } + None => { + self.base_iter.goto_next_node(); + continue; + } + }, }, } } @@ -393,19 +571,36 @@ impl> PkPkh { /// starting from some specific node which constructs the iterator via /// [Miniscript::iter_pk_pkh] method. pub struct PkPkhIter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> { - node_iter: Iter<'a, Pk, Ctx>, - curr_node: Option<&'a Miniscript>, - key_index: usize, + base_iter: BaseIter<'a, Pk, Ctx>, } impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkPkhIter<'a, Pk, Ctx> { fn new(miniscript: &'a Miniscript) -> Self { let mut iter = Iter::new(miniscript); - PkPkhIter { - curr_node: iter.next(), + let curr_node = iter.next(); + let mut multi_a_len = None; + let musig_iter = match curr_node { + Some(script) => match script.node { + Terminal::PkK(ref pk) => { + multi_a_len = Some(1 as u32); + Some(pk.iter()) + } + Terminal::MultiA(_, ref keys) => { + multi_a_len = Some(keys.len() as u32); + Some(keys[0].iter()) + } + _ => None, + }, + None => None, + }; + let bs_iter = BaseIter { + curr_node: curr_node, node_iter: iter, key_index: 0, - } + musig_iter: musig_iter, + multi_a_len: multi_a_len, + }; + PkPkhIter { base_iter: bs_iter } } /// Returns a `Option`, listing all public keys found in AST starting from this @@ -438,18 +633,55 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkPkhIter<'a, Pk, C fn next(&mut self) -> Option { loop { - match self.curr_node { + match self.base_iter.curr_node { None => break None, - Some(node) => match node.get_nth_pk_pkh(self.key_index) { - None => { - self.curr_node = self.node_iter.next(); - self.key_index = 0; - continue; + Some(script) => match &script.node { + Terminal::PkK(_) => { + // check if musig_iter has something + match self.base_iter.musig_iter.as_mut().unwrap().next() { + Some(pk) => break Some(PkPkh::PlainPubkey(pk.clone())), + None => { + self.base_iter.goto_next_node(); + continue; + } + } } - Some(pk) => { - self.key_index += 1; - break Some(pk); + Terminal::MultiA(_, keys) => { + match self.base_iter.musig_iter.as_mut().unwrap().next() { + Some(pk) => break Some(PkPkh::PlainPubkey(pk.clone())), + None => { + // When the current iterator has yielded all the keys + let vec_size = self.base_iter.multi_a_len.unwrap(); + self.base_iter.key_index += 1; + if (self.base_iter.key_index as u32) < vec_size { + // goto the next KeyExpr in the vector + self.base_iter.musig_iter = + Some(keys[self.base_iter.key_index].iter()); + match self.base_iter.musig_iter.as_mut().unwrap().next() { + None => { + self.base_iter.key_index += 1; + continue; + } + Some(pk) => break Some(PkPkh::PlainPubkey(pk.clone())), + } + } else { + // if we have exhausted all the KeyExpr + self.base_iter.goto_next_node(); + continue; + } + } + } } + _ => match script.my_get_nth_pk(self.base_iter.key_index) { + Some(pk) => { + self.base_iter.key_index += 1; + break Some(PkPkh::PlainPubkey(pk.clone())); + } + None => { + self.base_iter.goto_next_node(); + continue; + } + }, }, } } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index f835364ce..e347d5af1 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -458,16 +458,61 @@ mod tests { use sync::Arc; use super::{Miniscript, ScriptContext, Segwitv0, Tap}; + use crate::miniscript::context::ScriptContextError; + use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::types::{self, ExtData, Property, Type}; use crate::miniscript::Terminal; use crate::policy::Liftable; use crate::prelude::*; use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator}; - use crate::{hex_script, DummyKey, DummyKeyHash, Satisfier, ToPublicKey, TranslatePk}; + use crate::{ + hex_script, Descriptor, DummyKey, DummyKeyHash, Error, Satisfier, ToPublicKey, TranslatePk, + }; type Segwitv0Script = Miniscript; + type Segwitv0String = Miniscript; + type TapscriptString = Miniscript; type Tapscript = Miniscript; + #[test] + fn musig_validity() { + // create a miniscript with segwit context and try to add a musig key + // expect to receive error + let ms = Segwitv0String::from_str("or_b(pk(A),s:pk(musig(B,C)))"); + assert_eq!( + Error::ContextError(ScriptContextError::MusigNotAllowed), + ms.unwrap_err() + ); + let ms = Segwitv0String::from_str("or_b(pk(A),s:pk(musig(A,B,musig(C,musig(D,E)))))"); + assert_eq!( + Error::ContextError(ScriptContextError::MusigNotAllowed), + ms.unwrap_err() + ); + // create a miniscript with tapscript context and have musig key inside it + // expect to get parsed correctly. + let ms = TapscriptString::from_str("or_b(pk(A),s:pk(musig(B,C)))"); + assert_eq!(ms.is_ok(), true); + + // create a P2SH descriptor with musig key inside and expect to receive an error + let desc = Descriptor::::from_str("sh(pk(musig(K2,K3)))"); + assert_eq!( + Error::ContextError(ScriptContextError::MusigNotAllowed), + desc.unwrap_err() + ); + + let desc = Descriptor::::from_str("sh(and_v(v:pk(A),pk(musig(B))))"); + assert_eq!( + Error::ContextError(ScriptContextError::MusigNotAllowed), + desc.unwrap_err() + ); + // create a Tr descriptor with musig key inside and expect to get parsed correctly + let desc = Descriptor::::from_str("tr(X,{pk(musig(X1)),multi_a(1,X2,X3)})"); + assert_eq!(desc.is_ok(), true); + + let desc = Descriptor::::from_str("tr(X,pk(musig(A,B,musig(C))))"); + assert_eq!(desc.is_ok(), true); + } + fn pubkeys(n: usize) -> Vec { let mut ret = Vec::with_capacity(n); let secp = secp256k1::Secp256k1::new(); @@ -645,7 +690,7 @@ mod tests { let pkk_ms: Miniscript = Miniscript { node: Terminal::Check(Arc::new(Miniscript { - node: Terminal::PkK(DummyKey), + node: Terminal::PkK(KeyExpr::SingleKey(DummyKey)), ty: Type::from_pk_k::(), ext: types::extra_props::ExtData::from_pk_k::(), phantom: PhantomData, @@ -683,7 +728,7 @@ mod tests { let pkk_ms: Segwitv0Script = Miniscript { node: Terminal::Check(Arc::new(Miniscript { - node: Terminal::PkK(pk), + node: Terminal::PkK(KeyExpr::SingleKey(pk)), ty: Type::from_pk_k::(), ext: types::extra_props::ExtData::from_pk_k::(), phantom: PhantomData, diff --git a/src/miniscript/musig_key.rs b/src/miniscript/musig_key.rs index 8b2290270..b0c8320de 100644 --- a/src/miniscript/musig_key.rs +++ b/src/miniscript/musig_key.rs @@ -4,7 +4,7 @@ use core::str::FromStr; use crate::expression::{FromTree, Tree}; use crate::prelude::*; -use crate::{Error, MiniscriptKey}; +use crate::{Error, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator}; #[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] /// Enum for representing musig keys in miniscript @@ -16,7 +16,10 @@ pub enum KeyExpr { MuSig(Vec>), } -impl FromTree for KeyExpr { +impl FromTree for KeyExpr +where + ::Err: ToString, +{ fn from_tree(tree: &Tree) -> Result, Error> { if tree.name == "musig" { let mut key_expr_vec = vec![]; @@ -26,13 +29,17 @@ impl FromTree for KeyExpr { } Ok(KeyExpr::MuSig(key_expr_vec)) } else { - let single_key = Pk::from_str(tree.name).map_err(|_| Error::SingleKeyParseError)?; + let single_key = + Pk::from_str(tree.name).map_err(|err| Error::Unexpected(err.to_string()))?; Ok(KeyExpr::SingleKey(single_key)) } } } -impl FromStr for KeyExpr { +impl FromStr for KeyExpr +where + ::Err: ToString, +{ type Err = Error; fn from_str(s: &str) -> Result { let (key_tree, _) = Tree::from_slice(s).unwrap(); @@ -116,6 +123,70 @@ impl KeyExpr { } } +impl KeyExpr { + /// Returns an XOnlyPublicKey from a KeyExpr + pub fn key_agg(&self) -> bitcoin::XOnlyPublicKey { + match self { + KeyExpr::::SingleKey(pk) => pk.to_x_only_pubkey(), + KeyExpr::::MuSig(_keys) => todo!(), + } + } +} + +impl ForEachKey for KeyExpr { + fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool + where + Pk: 'a, + Pk::RawPkHash: 'a, + { + let keys_res = self.iter().all(|my_key| pred(my_key)); + keys_res + } +} + +impl TranslatePk for KeyExpr

+where + P: MiniscriptKey, + Q: MiniscriptKey, +{ + type Output = KeyExpr; + fn translate_pk(&self, t: &mut T) -> Result + where + T: Translator, + { + match self { + KeyExpr::

::SingleKey(pk) => Ok(KeyExpr::SingleKey(t.pk(pk)?)), + KeyExpr::

::MuSig(vec) => { + let mut new_vec: Vec> = vec![]; + for x in vec { + new_vec.push(x.translate_pk(t)?) + } + Ok(KeyExpr::MuSig(new_vec)) + } + } + } +} + +impl KeyExpr { + /// Returns the Pk if KeyExpr is SingleKey, otherwise None + pub fn single_key(&self) -> Option<&Pk> { + match self { + KeyExpr::::SingleKey(ref pk) => Some(pk), + KeyExpr::::MuSig(_) => None, + } + } + + /// Return vector of bytes + pub fn to_vec(&self) -> Vec + where + Pk: ToPublicKey, + { + match self { + KeyExpr::::SingleKey(ref pk) => pk.to_public_key().to_bytes(), + KeyExpr::::MuSig(_) => todo!(), + } + } +} #[cfg(test)] mod tests { use super::*; diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs index 37723117c..81fd778d9 100644 --- a/src/miniscript/satisfy.rs +++ b/src/miniscript/satisfy.rs @@ -28,6 +28,7 @@ use sync::Arc; use crate::miniscript::limits::{ LOCKTIME_THRESHOLD, SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_LOCKTIME_TYPE_FLAG, }; +use crate::miniscript::musig_key::KeyExpr; use crate::prelude::*; use crate::util::witness_size; use crate::{Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey}; @@ -546,21 +547,27 @@ impl Witness { /// Turn a signature into (part of) a satisfaction fn signature, Ctx: ScriptContext>( sat: S, - pk: &Pk, + pk: &KeyExpr, leaf_hash: &TapLeafHash, ) -> Self { - match Ctx::sig_type() { - super::context::SigType::Ecdsa => match sat.lookup_ecdsa_sig(pk) { - Some(sig) => Witness::Stack(vec![sig.to_vec()]), - // Signatures cannot be forged - None => Witness::Impossible, - }, - super::context::SigType::Schnorr => match sat.lookup_tap_leaf_script_sig(pk, leaf_hash) - { - Some(sig) => Witness::Stack(vec![sig.to_vec()]), - // Signatures cannot be forged - None => Witness::Impossible, - }, + match pk { + KeyExpr::::SingleKey(pkk) => { + match Ctx::sig_type() { + super::context::SigType::Ecdsa => match sat.lookup_ecdsa_sig(pkk) { + Some(sig) => Witness::Stack(vec![sig.to_vec()]), + // Signatures cannot be forged + None => Witness::Impossible, + }, + super::context::SigType::Schnorr => { + match sat.lookup_tap_leaf_script_sig(pkk, leaf_hash) { + Some(sig) => Witness::Stack(vec![sig.to_vec()]), + // Signatures cannot be forged + None => Witness::Impossible, + } + } + } + } + KeyExpr::::MuSig(_) => Witness::Impossible, // Musig satisfaction is not implemented } } @@ -1144,7 +1151,11 @@ impl Satisfaction { let mut sig_count = 0; let mut sigs = Vec::with_capacity(k); for pk in keys { - match Witness::signature::<_, _, Ctx>(stfr, pk, leaf_hash) { + match Witness::signature::<_, _, Ctx>( + stfr, + &KeyExpr::SingleKey(pk.clone()), + leaf_hash, + ) { Witness::Stack(sig) => { sigs.push(sig); sig_count += 1; diff --git a/src/miniscript/types/extra_props.rs b/src/miniscript/types/extra_props.rs index 0cd97f78b..a9a149b2d 100644 --- a/src/miniscript/types/extra_props.rs +++ b/src/miniscript/types/extra_props.rs @@ -911,7 +911,26 @@ impl Property for ExtData { Terminal::False => Ok(Self::from_false()), Terminal::PkK(..) => Ok(Self::from_pk_k::()), Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::()), - Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => { + Terminal::Multi(k, ref pks) => { + if k == 0 { + return Err(Error { + fragment: fragment.clone(), + error: ErrorKind::ZeroThreshold, + }); + } + if k > pks.len() { + return Err(Error { + fragment: fragment.clone(), + error: ErrorKind::OverThreshold(k, pks.len()), + }); + } + match *fragment { + Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())), + Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())), + _ => unreachable!(), + } + } + Terminal::MultiA(k, ref pks) => { if k == 0 { return Err(Error { fragment: fragment.clone(), diff --git a/src/miniscript/types/mod.rs b/src/miniscript/types/mod.rs index 161185c4f..2d13b9e58 100644 --- a/src/miniscript/types/mod.rs +++ b/src/miniscript/types/mod.rs @@ -414,7 +414,26 @@ pub trait Property: Sized { Terminal::False => Ok(Self::from_false()), Terminal::PkK(..) => Ok(Self::from_pk_k::()), Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::()), - Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => { + Terminal::Multi(k, ref pks) => { + if k == 0 { + return Err(Error { + fragment: fragment.clone(), + error: ErrorKind::ZeroThreshold, + }); + } + if k > pks.len() { + return Err(Error { + fragment: fragment.clone(), + error: ErrorKind::OverThreshold(k, pks.len()), + }); + } + match *fragment { + Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())), + Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())), + _ => unreachable!(), + } + } + Terminal::MultiA(k, ref pks) => { if k == 0 { return Err(Error { fragment: fragment.clone(), @@ -797,7 +816,26 @@ impl Property for Type { Terminal::False => Ok(Self::from_false()), Terminal::PkK(..) => Ok(Self::from_pk_k::()), Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::()), - Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => { + Terminal::Multi(k, ref pks) => { + if k == 0 { + return Err(Error { + fragment: fragment.clone(), + error: ErrorKind::ZeroThreshold, + }); + } + if k > pks.len() { + return Err(Error { + fragment: fragment.clone(), + error: ErrorKind::OverThreshold(k, pks.len()), + }); + } + match *fragment { + Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())), + Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())), + _ => unreachable!(), + } + } + Terminal::MultiA(k, ref pks) => { if k == 0 { return Err(Error { fragment: fragment.clone(), diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index c5cb7cdea..4a5ccbc76 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -27,6 +27,7 @@ use sync::Arc; use crate::miniscript::context::SigType; use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG; +use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::types::{self, ErrorKind, ExtData, Property, Type}; use crate::miniscript::ScriptContext; use crate::policy::Concrete; @@ -841,7 +842,9 @@ where } Concrete::Key(ref pk) => { insert_wrap!(AstElemExt::terminal(Terminal::PkH(pk.clone()))); - insert_wrap!(AstElemExt::terminal(Terminal::PkK(pk.clone()))); + insert_wrap!(AstElemExt::terminal(Terminal::PkK(KeyExpr::SingleKey( + pk.clone() + )))); } Concrete::After(n) => insert_wrap!(AstElemExt::terminal(Terminal::After(n))), Concrete::Older(n) => insert_wrap!(AstElemExt::terminal(Terminal::Older(n))), @@ -1032,7 +1035,11 @@ where match Ctx::sig_type() { SigType::Schnorr if key_vec.len() == subs.len() => { - insert_wrap!(AstElemExt::terminal(Terminal::MultiA(k, key_vec))) + let mut k_vec: Vec> = vec![]; + for key in key_vec { + k_vec.push(KeyExpr::SingleKey(key)) + } + insert_wrap!(AstElemExt::terminal(Terminal::MultiA(k, k_vec))) } SigType::Ecdsa if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG => diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 9e91dc7fe..429ad50b8 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -35,6 +35,7 @@ pub use self::concrete::Policy as Concrete; /// avoid this word because it is a reserved keyword in Rust pub use self::semantic::Policy as Semantic; use crate::descriptor::Descriptor; +use crate::miniscript::musig_key::KeyExpr; use crate::miniscript::{Miniscript, ScriptContext}; use crate::{Error, MiniscriptKey, Terminal}; @@ -124,7 +125,17 @@ impl Liftable for Miniscript impl Liftable for Terminal { fn lift(&self) -> Result, Error> { let ret = match *self { - Terminal::PkK(ref pk) | Terminal::PkH(ref pk) => Semantic::KeyHash(pk.to_pubkeyhash()), + Terminal::PkK(ref pk) => match pk { + KeyExpr::SingleKey(pkk) => Semantic::KeyHash(pkk.to_pubkeyhash()), + KeyExpr::MuSig(pkk) => { + let mut policy_vec: Vec> = vec![]; + for key in pkk { + policy_vec.push((Terminal::::PkK(key.clone())).lift()?) + } + Semantic::Threshold(pkk.len(), policy_vec) + } + }, + Terminal::PkH(ref pk) => Semantic::KeyHash(pk.to_pubkeyhash()), Terminal::RawPkH(ref pkh) => Semantic::KeyHash(pkh.clone()), Terminal::After(t) => Semantic::After(t), Terminal::Older(t) => Semantic::Older(t), @@ -161,12 +172,19 @@ impl Liftable for Terminal { let semantic_subs: Result<_, Error> = subs.iter().map(|s| s.node.lift()).collect(); Semantic::Threshold(k, semantic_subs?) } - Terminal::Multi(k, ref keys) | Terminal::MultiA(k, ref keys) => Semantic::Threshold( + Terminal::Multi(k, ref keys) => Semantic::Threshold( k, keys.iter() .map(|k| Semantic::KeyHash(k.to_pubkeyhash())) .collect(), ), + Terminal::MultiA(_, ref keys) => { + let mut policy_vec: Vec> = vec![]; + for key in keys { + policy_vec.push((Terminal::::PkK(key.clone())).lift()?) + } + Semantic::Threshold(keys.len(), policy_vec) + } } .normalized(); Ok(ret) diff --git a/src/util.rs b/src/util.rs index b59c2fde4..583cd39a5 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,6 +2,7 @@ use bitcoin::blockdata::script; use bitcoin::Script; use crate::miniscript::context; +use crate::miniscript::musig_key::KeyExpr; use crate::prelude::*; use crate::{MiniscriptKey, ScriptContext, ToPublicKey}; pub(crate) fn varint_len(n: usize) -> usize { @@ -28,7 +29,7 @@ pub(crate) fn witness_to_scriptsig(witness: &[Vec]) -> Script { // trait for pushing key that depend on context pub(crate) trait MsKeyBuilder { /// Serialize the key as bytes based on script context. Used when encoding miniscript into bitcoin script - fn push_ms_key(self, key: &Pk) -> Self + fn push_ms_key(self, key: &KeyExpr) -> Self where Pk: ToPublicKey, Ctx: ScriptContext; @@ -41,14 +42,18 @@ pub(crate) trait MsKeyBuilder { } impl MsKeyBuilder for script::Builder { - fn push_ms_key(self, key: &Pk) -> Self + fn push_ms_key(self, key: &KeyExpr) -> Self where Pk: ToPublicKey, Ctx: ScriptContext, { match Ctx::sig_type() { - context::SigType::Ecdsa => self.push_key(&key.to_public_key()), - context::SigType::Schnorr => self.push_slice(&key.to_x_only_pubkey().serialize()), + context::SigType::Ecdsa => self.push_key( + &key.single_key() + .expect("Unreachable, Found musig in Ecsdsa context") + .to_public_key(), + ), + context::SigType::Schnorr => self.push_slice(key.key_agg().serialize().as_ref()), } }