diff --git a/Cargo.toml b/Cargo.toml
index b3340376b..edcef2219 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ edition = "2018"
 
 [features]
 default = ["std"]
-std = ["bitcoin/std", "bitcoin/secp-recovery"]
+std = ["bitcoin/std", "bitcoin/secp-recovery", "secp256k1-zkp"]
 no-std = ["hashbrown", "bitcoin/no-std"]
 compiler = []
 trace = []
@@ -24,6 +24,7 @@ rand = ["bitcoin/rand"]
 bitcoin = { version = "0.28.1", default-features = false }
 serde = { version = "1.0", optional = true }
 hashbrown = { version = "0.11", optional = true }
+secp256k1-zkp = { git = "https://github.com/sanket1729/rust-secp256k1-zkp", branch = "pr29", optional = true}
 
 [dev-dependencies]
 bitcoind = {version = "0.26.1", features=["22_0"]}
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<Pk: MiniscriptKey> Descriptor<Pk> {
         // roundabout way to constuct `c:pk_k(pk)`
         let ms: Miniscript<Pk, BareCtx> =
             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/interpreter/mod.rs b/src/interpreter/mod.rs
index 2c2c4b917..23392a412 100644
--- a/src/interpreter/mod.rs
+++ b/src/interpreter/mod.rs
@@ -593,6 +593,9 @@ where
                 Terminal::PkK(ref pk) => {
                     debug_assert_eq!(node_state.n_evaluated, 0);
                     debug_assert_eq!(node_state.n_satisfied, 0);
+                    let pk = pk
+                        .single_key()
+                        .expect("Musig keys cannot be parsed from Script");
                     let res = self.stack.evaluate_pk(&mut self.verify_sig, *pk);
                     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 pkk = subs[node_state.n_evaluated]
+                            .single_key()
+                            .expect("Musig keys cannot be parsed from Script");
+                        let res = self.stack.evaluate_pk(&mut self.verify_sig, *pkk);
+                        match res {
                             Some(Ok(x)) => {
                                 self.push_evaluation_state(
                                     node_state.node,
diff --git a/src/lib.rs b/src/lib.rs
index dd0888624..52847d4fb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -99,6 +99,9 @@ extern crate alloc;
 #[cfg(not(feature = "std"))]
 extern crate hashbrown;
 
+#[cfg(feature = "std")]
+extern crate secp256k1_zkp;
+
 #[cfg(any(feature = "std", test))]
 extern crate core;
 
@@ -775,6 +778,8 @@ pub enum Error {
     TrNoScriptCode,
     /// No explicit script for Tr descriptors
     TrNoExplicitScript,
+    /// Parsing error for single key
+    SingleKeyParseError,
 }
 
 // https://github.com/sipa/miniscript/pull/5 for discussion on this number
@@ -848,6 +853,7 @@ impl fmt::Display for Error {
             Error::TaprootSpendInfoUnavialable => write!(f, "Taproot Spend Info not computed."),
             Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"),
             Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"),
+            Error::SingleKeyParseError => f.write_str("not able to parse the single key"),
         }
     }
 }
@@ -888,6 +894,7 @@ impl error::Error for Error {
             | BareDescriptorAddr
             | TaprootSpendInfoUnavialable
             | TrNoScriptCode
+            | SingleKeyParseError
             | TrNoExplicitScript => None,
             Script(e) => Some(e),
             AddrError(e) => Some(e),
diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs
index b21756430..daabb5801 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<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
         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<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
                     && 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<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
         T: Translator<Pk, Q, E>,
     {
         let frag: Terminal<Q, CtxQ> = 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<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
                 Terminal::Multi(k, keys?)
             }
             Terminal::MultiA(k, ref keys) => {
-                let keys: Result<Vec<Q>, _> = keys.iter().map(|k| t.pk(k)).collect();
+                let keys: Result<Vec<KeyExpr<Q>>, _> =
+                    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<Vec<Pk>, _> = top.args[1..]
-                    .iter()
-                    .map(|sub| expression::terminal(sub, Pk::from_str))
-                    .collect();
-
                 if frag_name == "multi" {
+                    let pks: Result<Vec<Pk>, _> = 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<Vec<KeyExpr<Pk>>, _> = top.args[1..]
+                        .iter()
+                        .map(|sub| KeyExpr::<Pk>::from_tree(sub))
+                        .collect();
                     pks.map(|pks| Terminal::MultiA(k, pks))
                 }
             }
@@ -734,7 +738,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
                 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
@@ -753,7 +757,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
     /// will handle the segwit/non-segwit technicalities for you.
     pub fn script_size(&self) -> usize {
         match *self {
-            Terminal::PkK(ref pk) => Ctx::pk_len(pk),
+            Terminal::PkK(ref pk) => Ctx::key_expr_len(pk),
             Terminal::PkH(..) | Terminal::RawPkH(..) => 24,
             Terminal::After(n) => script_num_size(n as usize) + 1,
             Terminal::Older(n) => script_num_size(n as usize) + 1,
@@ -798,7 +802,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
             Terminal::MultiA(k, ref pks) => {
                 script_num_size(k)
                     + 1 // NUMEQUAL
-                    + pks.iter().map(|pk| Ctx::pk_len(pk)).sum::<usize>() // n keys
+                    + pks.iter().map(|pk| Ctx::key_expr_len(pk)).sum::<usize>() // n keys
                     + pks.len() // n times CHECKSIGADD
             }
         }
diff --git a/src/miniscript/context.rs b/src/miniscript/context.rs
index d838ee4ba..37bc750dd 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(String),
 }
 
 #[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(ref err) => {
+                write!(f, "Musig is only allowed in tapscript : err {}", err)
+            }
         }
     }
 }
@@ -334,6 +341,13 @@ where
     /// 34 for Segwitv0, 33 for Tap
     fn pk_len<Pk: MiniscriptKey>(pk: &Pk) -> usize;
 
+    /// Get the len of the keyexpr
+    fn key_expr_len<Pk: MiniscriptKey>(pk: &KeyExpr<Pk>) -> usize {
+        match pk {
+            KeyExpr::SingleKey(pk) => Self::pk_len(pk),
+            KeyExpr::MuSig(_) => 33,
+        }
+    }
     /// Local helper function to display error messages with context
     fn name_str() -> &'static str;
 }
@@ -384,12 +398,21 @@ 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::<Pk>::SingleKey(pk) => {
+                    if pk.is_x_only_key() {
+                        return Err(ScriptContextError::XOnlyKeysNotAllowed(
+                            pk.to_string(),
+                            Self::name_str(),
+                        ));
+                    }
+                }
+                KeyExpr::<Pk>::MuSig(_) => {
+                    return Err(ScriptContextError::MusigNotAllowed(String::from(
+                        Self::name_str(),
+                    )))
+                }
+            },
             Terminal::Multi(_k, ref pks) => {
                 if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
                     return Err(ScriptContextError::CheckMultiSigLimitExceeded);
@@ -490,17 +513,24 @@ 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(),
+            Terminal::PkK(ref key) => match key {
+                KeyExpr::<Pk>::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(())
+                }
+                KeyExpr::<Pk>::MuSig(_) => {
+                    return Err(ScriptContextError::MusigNotAllowed(String::from(
                         Self::name_str(),
-                    ));
+                    )));
                 }
-                Ok(())
-            }
+            },
             Terminal::Multi(_k, ref pks) => {
                 if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
                     return Err(ScriptContextError::CheckMultiSigLimitExceeded);
@@ -618,13 +648,24 @@ impl ScriptContext for Tap {
         }
 
         match ms.node {
-            Terminal::PkK(ref pk) => {
-                if pk.is_uncompressed() {
-                    return Err(ScriptContextError::UncompressedKeysNotAllowed);
+            Terminal::PkK(ref key) => {
+                if key.iter().any(|pk| pk.is_uncompressed()) {
+                    Err(ScriptContextError::UncompressedKeysNotAllowed)
+                } else {
+                    Ok(())
                 }
-                Ok(())
             }
             Terminal::Multi(..) => Err(ScriptContextError::TaprootMultiDisabled),
+            Terminal::MultiA(_, ref keys) => {
+                if keys
+                    .iter()
+                    .all(|keyexpr| keyexpr.iter().any(|pk| pk.is_uncompressed()))
+                {
+                    Err(ScriptContextError::UncompressedKeysNotAllowed)
+                } else {
+                    Ok(())
+                }
+            }
             _ => Ok(()),
         }
     }
@@ -712,11 +753,23 @@ 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::<Pk>::SingleKey(pk) => {
+                        if pk.is_x_only_key() {
+                            return Err(ScriptContextError::XOnlyKeysNotAllowed(
+                                key.to_string(),
+                                Self::name_str(),
+                            ));
+                        }
+                    }
+                    KeyExpr::<Pk>::MuSig(_) => {
+                        return Err(ScriptContextError::MusigNotAllowed(String::from(
+                            Self::name_str(),
+                        )))
+                    }
+                }
+                Ok(())
             }
             Terminal::Multi(_k, ref pks) => {
                 if pks.len() > MAX_PUBKEYS_PER_MULTISIG {
@@ -753,7 +806,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::<Pk>::SingleKey(_pk) => Ok(()),
+                    KeyExpr::<Pk>::MuSig(_) => Err(Error::ContextError(
+                        ScriptContextError::MusigNotAllowed(String::from(Self::name_str())),
+                    )),
+                },
                 _ => Err(Error::NonStandardBareScript),
             },
             Terminal::Multi(_k, subs) if subs.len() <= 3 => Ok(()),
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<Pk: MiniscriptKey, Ctx: ScriptContext> {
     False,
     // pubkey checks
     /// `<key>`
-    PkK(Pk),
+    PkK(KeyExpr<Pk>),
     /// `DUP HASH160 <keyhash> EQUALVERIFY`
     PkH(Pk),
     /// Only for parsing PkH for Script
@@ -192,7 +193,7 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
     /// k (<key>)* n CHECKMULTISIG
     Multi(usize, Vec<Pk>),
     /// <key> CHECKSIG (<key> CHECKSIGADD)*(n-1) k NUMEQUAL
-    MultiA(usize, Vec<Pk>),
+    MultiA(usize, Vec<KeyExpr<Pk>>),
 }
 
 macro_rules! match_token {
@@ -300,12 +301,12 @@ pub fn parse<Ctx: ScriptContext>(
                     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<Ctx: ScriptContext>(
                     // 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<Ctx: ScriptContext>(
                         while tokens.peek() == Some(&Tk::CheckSigAdd) {
                             match_token!(
                                 tokens,
-                                Tk::CheckSigAdd, Tk::Bytes32(pk) => keys.push(<Ctx::Key>::from_slice(pk)
-                                    .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?),
+                                Tk::CheckSigAdd, Tk::Bytes32(pk) => keys.push(KeyExpr::SingleKey(<Ctx::Key>::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(<Ctx::Key>::from_slice(pk)
-                                .map_err(|e| Error::PubKeyCtxError(e, Ctx::name_str()))?),
+                            Tk::CheckSig, Tk::Bytes32(pk) => keys.push(KeyExpr::SingleKey(<Ctx::Key>::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..ad43e5a71 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,16 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     /// `miniscript.iter_pubkeys().collect()`.
     pub fn get_leapk(&self) -> Vec<Pk> {
         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<Pk> = Vec::<Pk>::new();
+                for key in keys {
+                    res.extend(key.iter().cloned());
+                }
+                res
+            }
             _ => vec![],
         }
     }
@@ -143,9 +152,15 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     pub fn get_leapkh(&self) -> Vec<Pk::RawPkHash> {
         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<Pk::RawPkHash> = Vec::<Pk::RawPkHash>::new();
+                for key in keys {
+                    res.extend(key.iter().map(|pk| pk.to_pubkeyhash()));
+                }
+                res
             }
             _ => vec![],
         }
@@ -161,13 +176,24 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     pub fn get_leapk_pkh(&self) -> Vec<PkPkh<Pk>> {
         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<PkPkh<Pk>> = Vec::<PkPkh<Pk>>::new();
+                for key in keys {
+                    res.extend(key.iter().map(|pk| PkPkh::PlainPubkey(pk.clone())));
+                }
+                res
+            }
             _ => vec![],
         }
     }
@@ -176,16 +202,20 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     /// 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<Pk> {
         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 +223,17 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     /// 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<Pk::RawPkHash> {
         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 +243,56 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     /// 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<PkPkh<Pk>> {
         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
+struct BaseIter<'a, Pk: MiniscriptKey, Ctx: ScriptContext> {
+    node_iter: Iter<'a, Pk, Ctx>,
+    curr_node: Option<&'a Miniscript<Pk, Ctx>>,
+    // If current fragment is PkK or MultiA, then this is the iterator over public keys
+    musig_iter: Option<KeyExprIter<'a, Pk>>,
+    // Helps in checking whether current fragment is MultiA or not,
+    // additionally provides the length of vec<keyexpr>
+    multi_a_len: Option<u32>,
+    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 +354,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<Pk, Ctx>>,
-    key_index: usize,
+    base_iter: BaseIter<'a, Pk, Ctx>,
 }
 
 impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkIter<'a, Pk, Ctx> {
     fn new(miniscript: &'a Miniscript<Pk, Ctx>) -> 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<Self::Item> {
         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.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 +452,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<Pk, Ctx>>,
-    key_index: usize,
+    base_iter: BaseIter<'a, Pk, Ctx>,
 }
 
 impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkhIter<'a, Pk, Ctx> {
     fn new(miniscript: &'a Miniscript<Pk, Ctx>) -> 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<Self::Item> {
         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.get_nth_pkh(self.base_iter.key_index) {
+                        Some(pk) => {
+                            self.base_iter.key_index += 1;
+                            break Some(pk);
+                        }
+                        None => {
+                            self.base_iter.goto_next_node();
+                            continue;
+                        }
+                    },
                 },
             }
         }
@@ -393,19 +567,36 @@ impl<Pk: MiniscriptKey<RawPkHash = Pk>> PkPkh<Pk> {
 /// 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<Pk, Ctx>>,
-    key_index: usize,
+    base_iter: BaseIter<'a, Pk, Ctx>,
 }
 
 impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> PkPkhIter<'a, Pk, Ctx> {
     fn new(miniscript: &'a Miniscript<Pk, Ctx>) -> 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 +629,55 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkPkhIter<'a, Pk, C
 
     fn next(&mut self) -> Option<Self::Item> {
         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.get_nth_pk_pkh(self.base_iter.key_index) {
+                        Some(pk) => {
+                            self.base_iter.key_index += 1;
+                            break Some(pk.clone());
+                        }
+                        None => {
+                            self.base_iter.goto_next_node();
+                            continue;
+                        }
+                    },
                 },
             }
         }
@@ -460,12 +688,16 @@ impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> Iterator for PkPkhIter<'a, Pk, C
 // dependent libraries for their own tasts based on Miniscript AST
 #[cfg(test)]
 pub mod test {
+    use core::str::FromStr;
+
     use bitcoin;
     use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
     use bitcoin::secp256k1;
 
-    use super::{Miniscript, PkPkh};
-    use crate::miniscript::context::Segwitv0;
+    use super::{Miniscript, PkIter, PkPkh};
+    use crate::miniscript::context::{Segwitv0, Tap};
+    type Segwitv0String = Miniscript<String, Segwitv0>;
+    type TapscriptString = Miniscript<String, Tap>;
 
     pub type TestData = (
         Miniscript<bitcoin::PublicKey, Segwitv0>,
@@ -604,6 +836,51 @@ pub mod test {
         ]
     }
 
+    #[test]
+    fn test_musig_iter() {
+        // TEST: musig inside Pk in Tap context
+        let ms = TapscriptString::from_str("or_b(pk(A),s:pk(musig(B,C)))").unwrap();
+        let mut pk_iter = PkIter::new(&ms);
+        let keys = vec!["A", "B", "C"];
+        for key in keys {
+            assert_eq!(String::from(key), pk_iter.next().unwrap());
+        }
+
+        // TEST: complex musig in Tap context
+        let ms =
+            TapscriptString::from_str("or_b(pk(A),s:pk(musig(F,B,musig(C,musig(D,E)))))").unwrap();
+        let mut pk_iter = PkIter::new(&ms);
+        let keys = vec!["A", "F", "B", "C", "D", "E"];
+        for key in keys {
+            assert_eq!(String::from(key), pk_iter.next().unwrap());
+        }
+
+        // TEST: without musig in segwit context
+        let ms = Segwitv0String::from_str("or_b(pk(A),s:pk(B))").unwrap();
+        let mut pk_iter = PkIter::new(&ms);
+        let keys = vec!["A", "B"];
+        for key in keys {
+            assert_eq!(String::from(key), pk_iter.next().unwrap());
+        }
+
+        // TEST: musig inside multi_a in Tap context
+        let ms = TapscriptString::from_str("or_b(pk(A),a:multi_a(1,B,musig(C,D)))").unwrap();
+        let mut pk_iter = PkIter::new(&ms);
+        let keys = vec!["A", "B", "C", "D"];
+        for key in keys {
+            assert_eq!(String::from(key), pk_iter.next().unwrap());
+        }
+
+        // TEST: musig and normal key in Tap context
+        let ms =
+            TapscriptString::from_str("or_b(pk(musig(A1,A2)),a:multi_a(1,B,musig(C,musig(D,E))))")
+                .unwrap();
+        let mut pk_iter = PkIter::new(&ms);
+        let keys = vec!["A1", "A2", "B", "C", "D", "E"];
+        for key in keys {
+            assert_eq!(String::from(key), pk_iter.next().unwrap());
+        }
+    }
     #[test]
     fn get_keys() {
         gen_testcases()
diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs
index 9d50d63d1..c67a69eb9 100644
--- a/src/miniscript/mod.rs
+++ b/src/miniscript/mod.rs
@@ -41,6 +41,7 @@ pub mod hash256;
 pub mod iter;
 pub mod lex;
 pub mod limits;
+pub mod musig_key;
 pub mod satisfy;
 pub mod types;
 
@@ -454,19 +455,134 @@ mod tests {
     use bitcoin::secp256k1::XOnlyPublicKey;
     use bitcoin::util::taproot::TapLeafHash;
     use bitcoin::{self, secp256k1};
+    use secp256k1::{rand, KeyPair, Secp256k1};
     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, ForEachKey, Legacy, Satisfier,
+        ToPublicKey, TranslatePk,
+    };
 
     type Segwitv0Script = Miniscript<bitcoin::PublicKey, Segwitv0>;
+    type Segwitv0String = Miniscript<String, Segwitv0>;
+    type TapscriptString = Miniscript<String, Tap>;
     type Tapscript = Miniscript<bitcoin::secp256k1::XOnlyPublicKey, Tap>;
 
+    #[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(String::from(
+                "Segwitv0"
+            ))),
+            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(String::from(
+                "Segwitv0"
+            ))),
+            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::<String>::from_str("sh(pk(musig(K2,K3)))");
+        assert_eq!(
+            Error::ContextError(ScriptContextError::MusigNotAllowed(String::from(
+                "Legacy/p2sh"
+            ))),
+            desc.unwrap_err()
+        );
+
+        let desc = Descriptor::<String>::from_str("sh(and_v(v:pk(A),pk(musig(B))))");
+        assert_eq!(
+            Error::ContextError(ScriptContextError::MusigNotAllowed(String::from(
+                "Legacy/p2sh"
+            ))),
+            desc.unwrap_err()
+        );
+        // create a Tr descriptor with musig key inside and expect to get parsed correctly
+        let desc = Descriptor::<String>::from_str("tr(X,{pk(musig(X1)),multi_a(1,X2,X3)})");
+        assert_eq!(desc.is_ok(), true);
+
+        let desc =
+            Descriptor::<String>::from_str("tr(pk(musig(E)),{pk(A),multi_a(1,B,musig(C,D))})");
+        assert_eq!(desc.is_ok(), true);
+
+        let desc = Descriptor::<String>::from_str("tr(pk(musig(D)),pk(musig(A,B,musig(C))))");
+        assert_eq!(desc.is_ok(), true);
+    }
+
+    #[test]
+    fn check_script_size() {
+        type Segwitv0MS = Miniscript<bitcoin::PublicKey, Segwitv0>;
+        type TapMS = Miniscript<bitcoin::XOnlyPublicKey, Tap>;
+        type LegacyMS = Miniscript<bitcoin::PublicKey, Legacy>;
+
+        let secp = Secp256k1::new();
+        let key_pair = KeyPair::new(&secp, &mut rand::thread_rng());
+        let comp_key = secp256k1::PublicKey::from_keypair(&key_pair);
+        let xonly = secp256k1::XOnlyPublicKey::from_keypair(&key_pair);
+
+        // pk(compressed) in Segwitv0 context
+        let ms: Segwitv0MS = ms_str!("pk({})", &comp_key.to_string());
+        assert_eq!(1 + 33 + 1, ms.script_size()); // PUSH_LEN key_size OP_CHECKSIG
+
+        // pk(xonly) in Tap context
+        let ms: TapMS = ms_str!("pk({})", &xonly.to_string());
+        assert_eq!(1 + 32 + 1, ms.script_size()); // PUSH_LEN key_size OP_CHECKSIG
+
+        // pk(musig) in Tap context
+        let ms: TapMS = ms_str!("pk(musig({}))", &xonly.to_string());
+        assert_eq!(1 + 32 + 1, ms.script_size()); // PUSH_LEN key_size OP_CHECKSIG
+
+        // pk(uncompressed) in Legacy context
+        let pk = bitcoin::PublicKey::from_str(
+            "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"
+        ).unwrap();
+        let ms: LegacyMS = ms_str!("pk({})", &pk.to_string());
+        assert_eq!(1 + 65 + 1, ms.script_size()); // PUSH_LEN key_size OP_CHECKSIG
+    }
+
+    #[test]
+    fn test_for_each_key() {
+        // TEST: regular miniscript without musig
+        let normal_ms: Miniscript<String, Tap> =
+            Miniscript::<String, Tap>::from_str("and_v(v:pk(A),multi_a(2,B,C,D))").unwrap();
+        assert_eq!(normal_ms.for_each_key(|pk| pk.len() == 1), true);
+        assert_eq!(normal_ms.for_each_key(|pk| pk.len() > 0), true);
+
+        // TEST: musig inside pk
+        let musig_ms: Miniscript<String, Tap> =
+            Miniscript::<String, Tap>::from_str("or_b(pk(A),s:pk(musig(B,CC)))").unwrap();
+        assert_eq!(musig_ms.for_each_key(|pk| pk.len() == 1), false);
+        assert_eq!(musig_ms.for_each_key(|pk| pk.len() > 0), true);
+
+        // TEST: complex script containing musig inside inside pk
+        let musig_ms: Miniscript<String, Tap> = Miniscript::<String, Tap>::from_str(
+            "or_b(and_b(pk(musig(A,B)),s:pk(F)),a:multi_a(1,C,musig(musig(D,E))))",
+        )
+        .unwrap();
+
+        assert_eq!(musig_ms.for_each_key(|pk| pk.len() == 1), true);
+        assert_eq!(musig_ms.for_each_key(|pk| pk.len() > 0), true);
+        assert_eq!(musig_ms.for_each_key(|pk| pk.starts_with("A")), false);
+    }
+
     fn pubkeys(n: usize) -> Vec<bitcoin::PublicKey> {
         let mut ret = Vec::with_capacity(n);
         let secp = secp256k1::Secp256k1::new();
@@ -488,6 +604,18 @@ mod tests {
         ret
     }
 
+    #[cfg(feature = "std")]
+    fn xonly_pubkeys(n: usize) -> Vec<bitcoin::XOnlyPublicKey> {
+        let mut ret = Vec::with_capacity(n);
+        let secp = secp256k1::Secp256k1::new();
+        for _ in 0..n {
+            let key_pair = KeyPair::new(&secp, &mut rand::thread_rng());
+            let xonly = XOnlyPublicKey::from_keypair(&key_pair);
+            ret.push(xonly)
+        }
+        ret
+    }
+
     fn string_rtt<Ctx: ScriptContext>(
         script: Miniscript<bitcoin::PublicKey, Ctx>,
         expected_debug: &str,
@@ -644,7 +772,7 @@ mod tests {
 
         let pkk_ms: Miniscript<DummyKey, Segwitv0> = Miniscript {
             node: Terminal::Check(Arc::new(Miniscript {
-                node: Terminal::PkK(DummyKey),
+                node: Terminal::PkK(KeyExpr::SingleKey(DummyKey)),
                 ty: Type::from_pk_k::<Segwitv0>(),
                 ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
                 phantom: PhantomData,
@@ -682,7 +810,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::<Segwitv0>(),
                 ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
                 phantom: PhantomData,
@@ -1033,6 +1161,186 @@ mod tests {
         .unwrap();
     }
 
+    #[test]
+    fn musig_translate() {
+        type TapscriptString = Miniscript<String, Tap>;
+        let ms = TapscriptString::from_str("or_b(pk(A),s:pk(musig(B,C)))").unwrap();
+
+        // TEST: musig inside pk
+        let tap_ms = ms.translate_pk(&mut StrXOnlyKeyTranslator::new()).unwrap();
+        assert_eq!(
+            "or_b(pk(8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa),s:pk(musig(ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2,9729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40)))",
+            tap_ms.to_string(),
+        );
+
+        // TEST: regular miniscript without musig
+        let ms = TapscriptString::from_str("and_v(v:pk(A),multi_a(2,B,C,D))").unwrap();
+        let tap_ms = ms.translate_pk(&mut StrXOnlyKeyTranslator::new()).unwrap();
+        assert_eq!(
+            "and_v(v:pk(8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa),multi_a(2,ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2,9729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40,2564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa))",
+            tap_ms.to_string(),
+        );
+
+        // TEST: nested musig
+        let ms =
+            TapscriptString::from_str("or_b(pk(A),s:pk(musig(B,musig(C,musig(D,E)))))").unwrap();
+        let tap_ms = ms.translate_pk(&mut StrXOnlyKeyTranslator::new()).unwrap();
+        assert_eq!(
+            "or_b(pk(8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa),s:pk(musig(ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2,musig(9729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40,musig(2564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa,89637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff)))))",
+            tap_ms.to_string(),
+        );
+
+        // TEST: musig inside multi_a
+        let ms = TapscriptString::from_str("and_v(v:pk(A),multi_a(1,musig(B,C),D))").unwrap();
+        let tap_ms = ms.translate_pk(&mut StrXOnlyKeyTranslator::new()).unwrap();
+        assert_eq!(
+            "and_v(v:pk(8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa),multi_a(1,musig(ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2,9729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40),2564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa))",
+            tap_ms.to_string(),
+        );
+
+        // TEST: complex script containing musig inside inside pk
+        let ms = TapscriptString::from_str(
+            "or_b(and_b(pk(musig(A,B)),s:pk(F)),a:multi_a(1,C,musig(musig(D,E))))",
+        )
+        .unwrap();
+        let tap_ms = ms.translate_pk(&mut StrXOnlyKeyTranslator::new()).unwrap();
+        assert_eq!(
+            "or_b(and_b(pk(musig(8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa,ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2)),s:pk(71efa4e26a4179e112860b88fc98658a4bdbc59c7ab6d4f8057c35330c7a89ee)),a:multi_a(1,9729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40,musig(musig(2564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa,89637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff))))",
+            tap_ms.to_string(),
+        );
+    }
+
+    #[test]
+    #[cfg(feature = "std")]
+    fn musig_encode_decode_tests() {
+        // TEST: regular miniscript without musig
+        let ms = Miniscript::<String, Tap>::from_str("and_v(v:pk(A),multi_a(2,B,C,D))").unwrap();
+        assert_eq!(ms.to_string(), "and_v(v:pk(A),multi_a(2,B,C,D))");
+        let ms_with_keys = ms.translate_pk(&mut StrXOnlyKeyTranslator::new()).unwrap();
+        assert_eq!(
+            Miniscript::<XOnlyPublicKey, Tap>::parse(&ms_with_keys.encode()).unwrap(),
+            ms_with_keys
+        );
+        assert_eq!(ms_with_keys.encode().len(), ms_with_keys.script_size());
+
+        // TEST: musig inside pk
+        let ms = Miniscript::<String, Tap>::from_str("or_b(pk(A),s:pk(musig(B,C)))").unwrap();
+        assert_eq!(ms.to_string(), "or_b(pk(A),s:pk(musig(B,C)))");
+        let keys = xonly_pubkeys(3);
+        let ms_with_keys: Miniscript<XOnlyPublicKey, Tap> =
+            ms_str!("or_b(pk({}),s:pk(musig({},{})))", keys[0], keys[1], keys[2]);
+        let musig_key = KeyExpr::MuSig(vec![
+            KeyExpr::SingleKey(keys[1]),
+            KeyExpr::SingleKey(keys[2]),
+        ]);
+        let ms_post_encoding: Miniscript<XOnlyPublicKey, Tap> =
+            ms_str!("or_b(pk({}),s:pk({}))", keys[0], musig_key.key_agg(),);
+        assert_eq!(
+            Miniscript::<XOnlyPublicKey, Tap>::parse_insane(&ms_with_keys.encode()).unwrap(),
+            ms_post_encoding
+        );
+
+        // TEST: nested musig
+        let ms =
+            Miniscript::<String, Tap>::from_str("or_b(pk(A),s:pk(musig(B,musig(C,musig(D,E)))))")
+                .unwrap();
+        assert_eq!(
+            ms.to_string(),
+            "or_b(pk(A),s:pk(musig(B,musig(C,musig(D,E)))))"
+        );
+        let keys = xonly_pubkeys(5);
+        let ms_with_keys: Miniscript<XOnlyPublicKey, Tap> = ms_str!(
+            "or_b(pk({}),s:pk(musig({},musig({},musig({},{})))))",
+            keys[0],
+            keys[1],
+            keys[2],
+            keys[3],
+            keys[4],
+        );
+        let musig_key = KeyExpr::MuSig(vec![
+            KeyExpr::SingleKey(keys[1]),
+            KeyExpr::MuSig(vec![
+                KeyExpr::SingleKey(keys[2]),
+                KeyExpr::MuSig(vec![
+                    KeyExpr::SingleKey(keys[3]),
+                    KeyExpr::SingleKey(keys[4]),
+                ]),
+            ]),
+        ]);
+        let ms_post_encoding: Miniscript<XOnlyPublicKey, Tap> =
+            ms_str!("or_b(pk({}),s:pk({}))", keys[0], musig_key.key_agg(),);
+        assert_eq!(
+            Miniscript::<XOnlyPublicKey, Tap>::parse_insane(&ms_with_keys.encode()).unwrap(),
+            ms_post_encoding
+        );
+
+        // TEST: musig inside multi_a
+        let ms =
+            Miniscript::<String, Tap>::from_str("and_v(v:pk(A),multi_a(1,musig(B,C),D))").unwrap();
+        assert_eq!(ms.to_string(), "and_v(v:pk(A),multi_a(1,musig(B,C),D))");
+        let keys = xonly_pubkeys(4);
+        let ms_with_keys: Miniscript<XOnlyPublicKey, Tap> = ms_str!(
+            "and_v(v:pk({}),multi_a(1,musig({},{}),{}))",
+            keys[0],
+            keys[1],
+            keys[2],
+            keys[3],
+        );
+        let musig_key = KeyExpr::MuSig(vec![
+            KeyExpr::SingleKey(keys[1]),
+            KeyExpr::SingleKey(keys[2]),
+        ]);
+        let ms_post_encoding: Miniscript<XOnlyPublicKey, Tap> = ms_str!(
+            "and_v(v:pk({}),multi_a(1,{},{}))",
+            keys[0],
+            musig_key.key_agg(),
+            keys[3],
+        );
+        assert_eq!(
+            Miniscript::<XOnlyPublicKey, Tap>::parse_insane(&ms_with_keys.encode()).unwrap(),
+            ms_post_encoding
+        );
+
+        // TEST: complex script containing musig inside inside pk
+        let ms = Miniscript::<String, Tap>::from_str(
+            "or_b(and_b(pk(musig(A,B)),s:pk(C)),a:multi_a(1,D,musig(musig(E,F))))",
+        )
+        .unwrap();
+        assert_eq!(
+            ms.to_string(),
+            "or_b(and_b(pk(musig(A,B)),s:pk(C)),a:multi_a(1,D,musig(musig(E,F))))"
+        );
+        let keys = xonly_pubkeys(6);
+        let ms_with_keys: Miniscript<XOnlyPublicKey, Tap> = ms_str!(
+            "or_b(and_b(pk(musig({},{})),s:pk({})),a:multi_a(1,{},musig(musig({},{}))))",
+            keys[0],
+            keys[1],
+            keys[2],
+            keys[3],
+            keys[4],
+            keys[5],
+        );
+        let musig_key_1 = KeyExpr::MuSig(vec![
+            KeyExpr::SingleKey(keys[0]),
+            KeyExpr::SingleKey(keys[1]),
+        ]);
+        let musig_key_2 = KeyExpr::MuSig(vec![KeyExpr::MuSig(vec![
+            KeyExpr::SingleKey(keys[4]),
+            KeyExpr::SingleKey(keys[5]),
+        ])]);
+        let ms_post_encoding: Miniscript<XOnlyPublicKey, Tap> = ms_str!(
+            "or_b(and_b(pk({}),s:pk({})),a:multi_a(1,{},{}))",
+            musig_key_1.key_agg(),
+            keys[2],
+            keys[3],
+            musig_key_2.key_agg(),
+        );
+        assert_eq!(
+            Miniscript::<XOnlyPublicKey, Tap>::parse_insane(&ms_with_keys.encode()).unwrap(),
+            ms_post_encoding
+        );
+    }
+
     #[test]
     fn multi_a_tests() {
         // Test from string tests
diff --git a/src/miniscript/musig_key.rs b/src/miniscript/musig_key.rs
new file mode 100644
index 000000000..719f962a5
--- /dev/null
+++ b/src/miniscript/musig_key.rs
@@ -0,0 +1,269 @@
+//! Support for multi-signature keys
+use core::fmt;
+use core::str::FromStr;
+
+use bitcoin::secp256k1::{Secp256k1, VerifyOnly};
+#[cfg(feature = "std")]
+use secp256k1_zkp::MusigKeyAggCache;
+
+use crate::expression::{FromTree, Tree};
+use crate::prelude::*;
+use crate::{expression, Error, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator};
+
+#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
+/// Enum for representing musig keys in miniscript
+pub enum KeyExpr<Pk: MiniscriptKey> {
+    /// Single-key (e.g pk(a), here 'a' is a single key)
+    SingleKey(Pk),
+
+    /// Collection of keys in used for musig-signature
+    MuSig(Vec<KeyExpr<Pk>>),
+}
+
+impl<Pk: MiniscriptKey + FromStr> FromTree for KeyExpr<Pk>
+where
+    <Pk as FromStr>::Err: ToString,
+{
+    fn from_tree(tree: &Tree) -> Result<KeyExpr<Pk>, Error> {
+        if tree.name == "musig" {
+            let key_expr_vec = tree
+                .args
+                .iter()
+                .map(|subtree| KeyExpr::<Pk>::from_tree(subtree))
+                .collect::<Result<Vec<KeyExpr<Pk>>, Error>>()?;
+            Ok(KeyExpr::MuSig(key_expr_vec))
+        } else {
+            let single_key = expression::terminal(tree, Pk::from_str)?;
+            Ok(KeyExpr::SingleKey(single_key))
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey + FromStr> FromStr for KeyExpr<Pk>
+where
+    <Pk as FromStr>::Err: ToString,
+{
+    type Err = Error;
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let (key_tree, _) = Tree::from_slice(s)?;
+        FromTree::from_tree(&key_tree)
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Iterator for [`KeyExpr`]
+pub struct KeyExprIter<'a, Pk: MiniscriptKey> {
+    stack: Vec<&'a KeyExpr<Pk>>,
+}
+
+impl<'a, Pk> Iterator for KeyExprIter<'a, Pk>
+where
+    Pk: MiniscriptKey + 'a,
+{
+    type Item = &'a Pk;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        while !self.stack.is_empty() {
+            let last = self.stack.pop().expect("Size checked above");
+            match last {
+                KeyExpr::MuSig(key_vec) => {
+                    // push the elements in reverse order
+                    key_vec.iter().rev().for_each(|key| self.stack.push(key));
+                }
+                KeyExpr::SingleKey(ref pk) => return Some(pk),
+            }
+        }
+        None
+    }
+}
+
+impl<Pk: MiniscriptKey> fmt::Debug for KeyExpr<Pk> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            KeyExpr::SingleKey(ref pk) => write!(f, "{:?}", pk),
+            KeyExpr::MuSig(ref my_vec) => {
+                write!(f, "musig(")?;
+                let len = my_vec.len();
+                for (index, k) in my_vec.iter().enumerate() {
+                    if index == len - 1 {
+                        write!(f, "{:?}", k)?;
+                    } else {
+                        write!(f, "{:?}", k)?;
+                    }
+                }
+                f.write_str(")")
+            }
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey> fmt::Display for KeyExpr<Pk> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            KeyExpr::SingleKey(ref pk) => write!(f, "{}", pk),
+            KeyExpr::MuSig(ref my_vec) => {
+                write!(f, "musig(")?;
+                let len = my_vec.len();
+                for (index, k) in my_vec.iter().enumerate() {
+                    if index == len - 1 {
+                        write!(f, "{}", k)?;
+                    } else {
+                        write!(f, "{},", k)?;
+                    }
+                }
+                f.write_str(")")
+            }
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey> KeyExpr<Pk> {
+    /// Iterate over all keys
+    pub fn iter(&self) -> KeyExprIter<Pk> {
+        KeyExprIter { stack: vec![self] }
+    }
+}
+
+impl<Pk: ToPublicKey> KeyExpr<Pk> {
+    /// Returns an XOnlyPublicKey from a KeyExpr
+    pub fn key_agg(&self) -> bitcoin::XOnlyPublicKey {
+        match self {
+            KeyExpr::<Pk>::SingleKey(pk) => pk.to_x_only_pubkey(),
+            KeyExpr::<Pk>::MuSig(_keys) => {
+                let secp = Secp256k1::verification_only();
+                self.key_agg_helper(&secp)
+            }
+        }
+    }
+
+    #[cfg(feature = "std")]
+    fn key_agg_helper(&self, secp: &Secp256k1<VerifyOnly>) -> bitcoin::XOnlyPublicKey {
+        match self {
+            KeyExpr::<Pk>::SingleKey(pk) => pk.to_x_only_pubkey(),
+            KeyExpr::<Pk>::MuSig(keys) => {
+                let xonly = keys
+                    .iter()
+                    .map(|key| key.key_agg_helper(secp))
+                    .collect::<Vec<_>>();
+                let key_agg_cache = MusigKeyAggCache::new(secp, &xonly);
+                key_agg_cache.agg_pk()
+            }
+        }
+    }
+
+    #[cfg(not(feature = "std"))]
+    fn key_agg_helper(&self, _secp: &Secp256k1<VerifyOnly>) -> bitcoin::XOnlyPublicKey {
+        match self {
+            KeyExpr::<Pk>::SingleKey(pk) => pk.to_x_only_pubkey(),
+            KeyExpr::<Pk>::MuSig(_keys) => {
+                unimplemented!("Musig not supported for no-std");
+            }
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey> ForEachKey<Pk> for KeyExpr<Pk> {
+    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(|key| pred(key));
+        keys_res
+    }
+}
+
+impl<P, Q> TranslatePk<P, Q> for KeyExpr<P>
+where
+    P: MiniscriptKey,
+    Q: MiniscriptKey,
+{
+    type Output = KeyExpr<Q>;
+    fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
+    where
+        T: Translator<P, Q, E>,
+    {
+        match self {
+            KeyExpr::<P>::SingleKey(pk) => Ok(KeyExpr::SingleKey(t.pk(pk)?)),
+            KeyExpr::<P>::MuSig(vec) => {
+                let mut new_vec: Vec<KeyExpr<Q>> = vec![];
+                for x in vec {
+                    new_vec.push(x.translate_pk(t)?)
+                }
+                Ok(KeyExpr::MuSig(new_vec))
+            }
+        }
+    }
+}
+
+impl<Pk: MiniscriptKey> KeyExpr<Pk> {
+    /// Returns the Pk if KeyExpr is SingleKey, otherwise None
+    pub fn single_key(&self) -> Option<&Pk> {
+        match self {
+            KeyExpr::<Pk>::SingleKey(ref pk) => Some(pk),
+            KeyExpr::<Pk>::MuSig(_) => None,
+        }
+    }
+
+    /// Return vector of bytes, in case of musig, it is first aggregated
+    /// and then converted to bytes
+    pub fn to_vec(&self) -> Vec<u8>
+    where
+        Pk: ToPublicKey,
+    {
+        match self {
+            KeyExpr::<Pk>::SingleKey(ref pk) => pk.to_public_key().to_bytes(),
+            KeyExpr::<Pk>::MuSig(_) => {
+                let agg_key = self.key_agg();
+                agg_key.to_public_key().to_bytes()
+            }
+        }
+    }
+}
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn test_one(musig_key: &str) {
+        let pk = KeyExpr::<String>::from_str(musig_key).unwrap();
+        println!("{}", pk);
+        assert_eq!(musig_key, format!("{}", pk))
+    }
+
+    #[test]
+    fn test_from_str_and_fmt() {
+        test_one("musig(A,B,musig(C,musig(D,E)))");
+        test_one("musig(A)");
+        test_one("A");
+        test_one("musig(,,)");
+    }
+
+    #[test]
+    fn test_iterator() {
+        let pk = KeyExpr::<String>::from_str("musig(A,B,musig(C,musig(D,E)))").unwrap();
+        let mut my_iter = pk.iter();
+        assert_eq!(my_iter.next(), Some(&String::from("A")));
+        assert_eq!(my_iter.next(), Some(&String::from("B")));
+        assert_eq!(my_iter.next(), Some(&String::from("C")));
+        assert_eq!(my_iter.next(), Some(&String::from("D")));
+        assert_eq!(my_iter.next(), Some(&String::from("E")));
+        assert_eq!(my_iter.next(), None);
+    }
+
+    fn test_helper(musig_key: &str, comma_separated_key: &str) {
+        let pk = KeyExpr::<String>::from_str(musig_key).unwrap();
+        let var: Vec<&str> = comma_separated_key.split(",").collect();
+        let key_names: Vec<&String> = pk.iter().collect();
+        for (key1, key2) in key_names.iter().zip(var.iter()) {
+            assert_eq!(key1, key2);
+        }
+    }
+
+    #[test]
+    fn test_iterator_multi() {
+        test_helper("musig(A)", "A");
+        test_helper("A", "A");
+        test_helper("musig(,,)", "");
+        test_helper("musig(musig(A,B),musig(musig(C)))", "A,B,C");
+    }
+}
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<Pk: ToPublicKey, S: Satisfier<Pk>, Ctx: ScriptContext>(
         sat: S,
-        pk: &Pk,
+        pk: &KeyExpr<Pk>,
         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::<Pk>::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::<Pk>::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..f3db7009e 100644
--- a/src/miniscript/types/extra_props.rs
+++ b/src/miniscript/types/extra_props.rs
@@ -911,7 +911,7 @@ impl Property for ExtData {
             Terminal::False => Ok(Self::from_false()),
             Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
             Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
-            Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
+            Terminal::Multi(k, ref pks) => {
                 if k == 0 {
                     return Err(Error {
                         fragment: fragment.clone(),
@@ -924,11 +924,22 @@ impl Property for ExtData {
                         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!(),
+                Ok(Self::from_multi(k, pks.len()))
+            }
+            Terminal::MultiA(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()),
+                    });
                 }
+                Ok(Self::from_multi_a(k, pks.len()))
             }
             Terminal::After(t) => {
                 // Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
diff --git a/src/miniscript/types/mod.rs b/src/miniscript/types/mod.rs
index 161185c4f..07d014a15 100644
--- a/src/miniscript/types/mod.rs
+++ b/src/miniscript/types/mod.rs
@@ -414,7 +414,7 @@ pub trait Property: Sized {
             Terminal::False => Ok(Self::from_false()),
             Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
             Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
-            Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
+            Terminal::Multi(k, ref pks) => {
                 if k == 0 {
                     return Err(Error {
                         fragment: fragment.clone(),
@@ -427,11 +427,22 @@ pub trait Property: Sized {
                         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!(),
+                Ok(Self::from_multi(k, pks.len()))
+            }
+            Terminal::MultiA(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()),
+                    });
                 }
+                Ok(Self::from_multi_a(k, pks.len()))
             }
             Terminal::After(t) => {
                 // Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
@@ -797,7 +808,7 @@ impl Property for Type {
             Terminal::False => Ok(Self::from_false()),
             Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
             Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
-            Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
+            Terminal::Multi(k, ref pks) => {
                 if k == 0 {
                     return Err(Error {
                         fragment: fragment.clone(),
@@ -810,11 +821,22 @@ impl Property for Type {
                         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!(),
+                Ok(Self::from_multi(k, pks.len()))
+            }
+            Terminal::MultiA(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()),
+                    });
                 }
+                Ok(Self::from_multi_a(k, pks.len()))
             }
             Terminal::After(t) => {
                 // Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
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<KeyExpr<Pk>> = 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..3e0bb9b5f 100644
--- a/src/policy/mod.rs
+++ b/src/policy/mod.rs
@@ -25,6 +25,8 @@ use core::fmt;
 #[cfg(feature = "std")]
 use std::error;
 
+use crate::Vec;
+
 #[cfg(feature = "compiler")]
 pub mod compiler;
 pub mod concrete;
@@ -35,6 +37,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 +127,17 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Miniscript<Pk, Ctx>
 impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> {
     fn lift(&self) -> Result<Semantic<Pk>, 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<Semantic<Pk>> = vec![];
+                    for key in pkk {
+                        policy_vec.push((Terminal::<Pk, Ctx>::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 +174,19 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Liftable<Pk> for Terminal<Pk, Ctx> {
                 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(k, ref keys) => {
+                let mut policy_vec: Vec<Semantic<Pk>> = vec![];
+                for key in keys {
+                    policy_vec.push((Terminal::<Pk, Ctx>::PkK(key.clone())).lift()?)
+                }
+                Semantic::Threshold(k, policy_vec)
+            }
         }
         .normalized();
         Ok(ret)
@@ -234,13 +254,13 @@ mod tests {
     #[cfg(feature = "compiler")]
     use sync::Arc;
 
-    use super::super::miniscript::context::Segwitv0;
+    use super::super::miniscript::context::{Segwitv0, Tap};
     use super::super::miniscript::Miniscript;
     use super::{Concrete, Liftable, Semantic};
     use crate::prelude::*;
     use crate::DummyKey;
     #[cfg(feature = "compiler")]
-    use crate::{descriptor::TapTree, Descriptor, Tap};
+    use crate::{descriptor::TapTree, Descriptor};
 
     type ConcretePol = Concrete<DummyKey>;
     type SemanticPol = Semantic<DummyKey>;
@@ -336,6 +356,51 @@ mod tests {
         ConcretePol::from_str(&policy_string).unwrap_err();
     }
 
+    #[test]
+    fn lift_keyexpr() {
+        // TEST: regular miniscript without musig
+        let normal_ms: Miniscript<String, Tap> =
+            Miniscript::<String, Tap>::from_str("and_v(v:pk(A),multi_a(2,B,C,D))").unwrap();
+        assert_eq!(
+            Semantic::from_str("and(pkh(A),thresh(2,pkh(B),pkh(C),pkh(D)))").unwrap(),
+            normal_ms.lift().unwrap()
+        );
+
+        // TEST: musig inside pk
+        let musig_ms: Miniscript<String, Tap> =
+            Miniscript::<String, Tap>::from_str("or_b(pk(A),s:pk(musig(B,C)))").unwrap();
+        assert_eq!(
+            Semantic::from_str("or(pkh(A),and(pkh(B),pkh(C)))").unwrap(),
+            musig_ms.lift().unwrap()
+        );
+
+        // TEST: nested musig
+        let musig_ms: Miniscript<String, Tap> =
+            Miniscript::<String, Tap>::from_str("or_b(pk(A),s:pk(musig(B,musig(C,musig(D,E)))))")
+                .unwrap();
+        assert_eq!(
+            Semantic::from_str("or(pkh(A),and(pkh(B),pkh(C),pkh(D),pkh(E)))").unwrap(),
+            musig_ms.lift().unwrap()
+        );
+
+        // TEST: musig inside multi_a
+        let musig_ms: Miniscript<String, Tap> =
+            Miniscript::<String, Tap>::from_str("and_v(v:pk(A),multi_a(1,musig(B,C),D))").unwrap();
+        assert_eq!(
+            Semantic::from_str("and(pkh(A),or(and(pkh(B),pkh(C)),pkh(D)))").unwrap(),
+            musig_ms.lift().unwrap()
+        );
+
+        // TEST: complex script containing musig inside inside pk
+        let musig_ms: Miniscript<String, Tap> = Miniscript::<String, Tap>::from_str(
+            "or_b(and_b(pk(musig(A,B)),s:pk(F)),a:multi_a(1,C,musig(musig(D,E))))",
+        )
+        .unwrap();
+        assert_eq!(
+            Semantic::from_str("or(and(pkh(A),pkh(B),pkh(F)),pkh(C),and(pkh(D),pkh(E)))").unwrap(),
+            musig_ms.lift().unwrap()
+        );
+    }
     #[test]
     fn lift_andor() {
         let key_a: bitcoin::PublicKey =
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<u8>]) -> 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<Pk, Ctx>(self, key: &Pk) -> Self
+    fn push_ms_key<Pk, Ctx>(self, key: &KeyExpr<Pk>) -> Self
     where
         Pk: ToPublicKey,
         Ctx: ScriptContext;
@@ -41,14 +42,18 @@ pub(crate) trait MsKeyBuilder {
 }
 
 impl MsKeyBuilder for script::Builder {
-    fn push_ms_key<Pk, Ctx>(self, key: &Pk) -> Self
+    fn push_ms_key<Pk, Ctx>(self, key: &KeyExpr<Pk>) -> 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()),
         }
     }