Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature simplicity #68

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In d5c0d65320816bfdf36411feed4bdff0708b5b12:

We can't make an enum variant dependent on a feature-gate like this because it will make adding the feature a breaking change for anyone trying to match on the enum.

Our choices are either to mark the enum #[non_exhaustive] (probably the easiest thing for now) or to create a dummy simplicity module when the simplicity feature is disabled, that will include a dummy simplicity::Policy so that this can still compile with the variant present but no Simplicity library.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In d9b3caf, sorry. Not sure what that other hash is.

Copy link
Contributor Author

@LeoComandini LeoComandini Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

marked the enums #[non_exhaustive]

}

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
Loading