Skip to content

Commit

Permalink
Merge #68: Add feature simplicity
Browse files Browse the repository at this point in the history
b80cb46 Add feature simplicity (Leonardo Comandini)

Pull request description:

  `rust-simplicity` is still evolving and it might need fixes or interface changes. Make it optional.

ACKs for top commit:
  apoelstra:
    ACK b80cb46

Tree-SHA512: ab891015b890969525ddf0fe515523b90efb4ac3927bd9290026813a75bf5f3dd34d7f5e506b4ee72336cf40c7ac3a5a290c2f64d5d2c244bbe4349838a76451
  • Loading branch information
apoelstra committed Jan 16, 2024
2 parents 259f7c4 + b80cb46 commit 2f58314
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ base64 = ["bitcoin/base64"]
bitcoin = "0.30.0"
elements = "0.23.0"
bitcoin-miniscript = { package = "miniscript", version = "10.0" }
simplicity = { git = "https://github.com/BlockstreamResearch/rust-simplicity", rev = "d5c0d65320816bfdf36411feed4bdff0708b5b12" }
simplicity = { git = "https://github.com/BlockstreamResearch/rust-simplicity", rev = "d5c0d65320816bfdf36411feed4bdff0708b5b12", optional = true }

# Do NOT use this as a feature! Use the `serde` feature instead.
actual-serde = { package = "serde", version = "1.0", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion contrib/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -ex

FEATURES="compiler serde rand base64"
FEATURES="compiler serde rand base64 simplicity"

cargo --version
rustc --version
Expand Down
56 changes: 43 additions & 13 deletions src/descriptor/tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::{
// Hidden leaves are not yet supported in descriptor spec. Conceptually, it should
// be simple to integrate those here, but it is best to wait on core for the exact syntax.
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum TapTree<Pk: MiniscriptKey, Ext: Extension = NoExt> {
/// A taproot tree structure
Tree(Arc<TapTree<Pk, Ext>>, Arc<TapTree<Pk, Ext>>),
Expand All @@ -37,6 +38,7 @@ pub enum TapTree<Pk: MiniscriptKey, Ext: Extension = NoExt> {
// are of Leafversion::default
Leaf(Arc<Miniscript<Pk, Tap, Ext>>),
/// A taproot leaf denoting a spending condition in terms of Simplicity
#[cfg(feature = "simplicity")]
SimplicityLeaf(Arc<simplicity::Policy<Pk>>),
}

Expand Down Expand Up @@ -119,7 +121,9 @@ impl<Pk: MiniscriptKey, Ext: Extension> TapTree<Pk, Ext> {
TapTree::Tree(ref left_tree, ref right_tree) => {
1 + max(left_tree.taptree_height(), right_tree.taptree_height())
}
TapTree::Leaf(..) | TapTree::SimplicityLeaf(..) => 0,
TapTree::Leaf(..) => 0,
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(..) => 0,
}
}

Expand All @@ -138,8 +142,10 @@ impl<Pk: MiniscriptKey, Ext: Extension> TapTree<Pk, Ext> {
Q: MiniscriptKey,
Ext: Extension,
{
#[cfg(feature = "simplicity")]
struct SimTranslator<'a, T>(&'a mut T);

#[cfg(feature = "simplicity")]
impl<'a, Pk, T, Q, Error> simplicity::Translator<Pk, Q, Error> for SimTranslator<'a, T>
where
Pk: MiniscriptKey,
Expand All @@ -161,6 +167,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> TapTree<Pk, Ext> {
Arc::new(r.translate_helper(t)?),
),
TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_pk(t)?)),
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(sim) => TapTree::SimplicityLeaf(Arc::new(sim.translate(&mut SimTranslator(t))?))
};
Ok(frag)
Expand All @@ -179,6 +186,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> TapTree<Pk, Ext> {
Arc::new(r.translate_ext_helper(t)?),
),
TapTree::Leaf(ms) => TapTree::Leaf(Arc::new(ms.translate_ext(t)?)),
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(sim) => TapTree::SimplicityLeaf(Arc::clone(sim)),
};
Ok(frag)
Expand All @@ -190,6 +198,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> fmt::Display for TapTree<Pk, Ext> {
match self {
TapTree::Tree(ref left, ref right) => write!(f, "{{{},{}}}", *left, *right),
TapTree::Leaf(ref script) => write!(f, "{}", *script),
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(ref policy) => write!(f, "sim{{{}}}", policy),
}
}
Expand All @@ -200,6 +209,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> fmt::Debug for TapTree<Pk, Ext> {
match self {
TapTree::Tree(ref left, ref right) => write!(f, "{{{:?},{:?}}}", *left, *right),
TapTree::Leaf(ref script) => write!(f, "{:?}", *script),
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(ref policy) => write!(f, "{:?}", policy),
}
}
Expand Down Expand Up @@ -288,6 +298,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> Tr<Pk, Ext> {
match script {
TapLeafScript::Miniscript(ms) => ms.sanity_check()?,
// TODO: Add sanity check for Simplicity policies
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(..) => {},
}
}
Expand Down Expand Up @@ -432,10 +443,12 @@ impl<Pk: MiniscriptKey + ToPublicKey, Ext: ParseableExt> Tr<Pk, Ext> {

/// Script at a tap leaf.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum TapLeafScript<'a, Pk: MiniscriptKey, Ext: Extension> {
/// Miniscript leaf
Miniscript(&'a Miniscript<Pk, Tap, Ext>),
/// Simplicity leaf
#[cfg(feature = "simplicity")]
Simplicity(&'a simplicity::Policy<Pk>)
}

Expand All @@ -444,11 +457,13 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
pub fn as_miniscript(&self) -> Option<&'a Miniscript<Pk, Tap, Ext>> {
match self {
TapLeafScript::Miniscript(ms) => Some(ms),
#[cfg(feature = "simplicity")]
_ => None,
}
}

/// Get the Simplicity policy at the leaf, if it exists.
#[cfg(feature = "simplicity")]
pub fn as_simplicity(&self) -> Option<&'a simplicity::Policy<Pk>> {
match self {
TapLeafScript::Simplicity(sim) => Some(sim),
Expand All @@ -460,6 +475,7 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
pub fn version(&self) -> LeafVersion {
match self {
TapLeafScript::Miniscript(..) => LeafVersion::default(),
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(..) => simplicity::leaf_version(),
}
}
Expand All @@ -469,6 +485,7 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
match self {
TapLeafScript::Miniscript(ms) => ms.script_size(),
// Simplicity's witness script is always a 32-byte CMR
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(..) => 32,
}
}
Expand All @@ -482,6 +499,7 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
// (1) Encoded program+witness
// (2) CMR program
// The third element is the control block, which is not counted by this method.
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(..) => Ok(2),
}
}
Expand All @@ -493,6 +511,7 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
// There is currently no way to bound the Simplicity witness size without producing one
// We mark the witness size as malleable since it depends on the chosen spending path
// TODO: Add method to simplicity::Policy and use it here
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(..) => Err(Error::AnalysisError(crate::AnalysisError::Malleable))
}
}
Expand All @@ -501,6 +520,7 @@ impl<'a, Pk: MiniscriptKey, Ext: Extension> TapLeafScript<'a, Pk, Ext> {
pub fn iter_pk(&self) -> Box<dyn Iterator<Item=Pk> + 'a> {
match self {
TapLeafScript::Miniscript(ms) => Box::new(ms.iter_pk()),
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(sim) => Box::new(sim.iter_pk()),
}
}
Expand All @@ -511,6 +531,7 @@ impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
pub fn encode(&self) -> Script {
match self {
TapLeafScript::Miniscript(ms) => ms.encode(),
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(sim) => {
Script::from(sim.cmr().as_ref().to_vec())
}
Expand All @@ -522,6 +543,7 @@ impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
match self {
TapLeafScript::Miniscript(ms) => ms.satisfy_malleable(satisfier),
// There doesn't (yet?) exist a malleable satisfaction of Simplicity policy
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(..) => self.satisfy(satisfier),
}
}
Expand All @@ -530,6 +552,7 @@ impl<'a, Pk: ToPublicKey, Ext: ParseableExt> TapLeafScript<'a, Pk, Ext> {
pub fn satisfy<S: Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error> {
match self {
TapLeafScript::Miniscript(ms) => ms.satisfy(satisfier),
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(sim) => {
let satisfier = crate::simplicity::SatisfierWrapper::new(satisfier);
let program = sim.satisfy(&satisfier).map_err(|_| Error::CouldNotSatisfy)?;
Expand Down Expand Up @@ -577,6 +600,7 @@ where
TapTree::Leaf(ref ms) => {
return Some((depth, TapLeafScript::Miniscript(ms)))
},
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(ref sim) => {
return Some((depth, TapLeafScript::Simplicity(sim)))
}
Expand All @@ -593,6 +617,7 @@ impl_block_str!(
// Helper function to parse taproot script path
fn parse_tr_script_spend(tree: &expression::Tree,) -> Result<TapTree<Pk, Ext>, Error> {
match tree {
#[cfg(feature = "simplicity")]
expression::Tree { name, args } if *name == "sim" && args.len() == 1 => {
let policy = crate::simplicity::PolicyWrapper::<Pk>::from_str(args[0].name)?;
Ok(TapTree::SimplicityLeaf(Arc::new(policy.0)))
Expand Down Expand Up @@ -771,6 +796,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> Liftable<Pk> for TapTree<Pk, Ext> {
Ok(Policy::Threshold(1, vec![lift_helper(l)?, lift_helper(r)?]))
}
TapTree::Leaf(ref leaf) => leaf.lift(),
#[cfg(feature = "simplicity")]
TapTree::SimplicityLeaf(..) => panic!("FIXME: Cannot lift Simplicity policy to Miniscript semantic policy"),
}
}
Expand Down Expand Up @@ -802,6 +828,7 @@ impl<Pk: MiniscriptKey, Ext: Extension> ForEachKey<Pk> for Tr<Pk, Ext> {
.all(|(_d, script)| {
match script {
TapLeafScript::Miniscript(ms) => ms.for_each_key(&mut pred),
#[cfg(feature = "simplicity")]
TapLeafScript::Simplicity(sim) => crate::simplicity::for_each_key(sim, &mut pred),
}
});
Expand Down Expand Up @@ -976,17 +1003,20 @@ mod tests {
&[TapLeafScript::Miniscript(&ms)]
);

// Simplicity key spend
let sim = simplicity::Policy::Key("a".to_string());
verify_from_str(
"eltr(internal,sim{pk(a)})#duhmnzmm", "internal",
&[TapLeafScript::Simplicity(&sim)]
);

// Mixed Miniscript and Simplicity
verify_from_str(
"eltr(internal,{pk(a),sim{pk(a)}})#7vmfhpaj", "internal",
&[TapLeafScript::Miniscript(&ms), TapLeafScript::Simplicity(&sim)]
);
#[cfg(feature = "simplicity")]
{
// Simplicity key spend
let sim = simplicity::Policy::Key("a".to_string());
verify_from_str(
"eltr(internal,sim{pk(a)})#duhmnzmm", "internal",
&[TapLeafScript::Simplicity(&sim)]
);

// Mixed Miniscript and Simplicity
verify_from_str(
"eltr(internal,{pk(a),sim{pk(a)}})#7vmfhpaj", "internal",
&[TapLeafScript::Miniscript(&ms), TapLeafScript::Simplicity(&sim)]
);
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub mod psbt;
#[cfg(test)]
mod test_utils;
mod util;
#[cfg(feature = "simplicity")]
mod simplicity;

use std::{cmp, error, fmt, str};
Expand Down

0 comments on commit 2f58314

Please sign in to comment.