Skip to content

miniscript: non-recursive Clone #724

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

Merged
merged 1 commit into from
Aug 19, 2024
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
61 changes: 60 additions & 1 deletion src/miniscript/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ enum NonTerm {
///
/// The average user should always use the [`Descriptor`] APIs. Advanced users who want deal
/// with Miniscript ASTs should use the [`Miniscript`] APIs.
#[derive(Clone)]
pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
/// `1`
True,
Expand Down Expand Up @@ -186,6 +185,66 @@ pub enum Terminal<Pk: MiniscriptKey, Ctx: ScriptContext> {
MultiA(Threshold<Pk, MAX_PUBKEYS_IN_CHECKSIGADD>),
}

impl<Pk: MiniscriptKey, Ctx: ScriptContext> Clone for Terminal<Pk, Ctx> {
/// We implement clone as a "deep clone" which reconstructs the entire tree.
///
/// If users just want to clone Arcs they can use Arc::clone themselves.
fn clone(&self) -> Self {
match self {
Terminal::PkK(ref p) => Terminal::PkK(p.clone()),
Terminal::PkH(ref p) => Terminal::PkH(p.clone()),
Terminal::RawPkH(ref p) => Terminal::RawPkH(*p),
Terminal::After(ref n) => Terminal::After(*n),
Terminal::Older(ref n) => Terminal::Older(*n),
Terminal::Sha256(ref x) => Terminal::Sha256(x.clone()),
Terminal::Hash256(ref x) => Terminal::Hash256(x.clone()),
Terminal::Ripemd160(ref x) => Terminal::Ripemd160(x.clone()),
Terminal::Hash160(ref x) => Terminal::Hash160(x.clone()),
Terminal::True => Terminal::True,
Terminal::False => Terminal::False,
Terminal::Alt(ref sub) => Terminal::Alt(Arc::new(Miniscript::clone(sub))),
Terminal::Swap(ref sub) => Terminal::Swap(Arc::new(Miniscript::clone(sub))),
Terminal::Check(ref sub) => Terminal::Check(Arc::new(Miniscript::clone(sub))),
Terminal::DupIf(ref sub) => Terminal::DupIf(Arc::new(Miniscript::clone(sub))),
Terminal::Verify(ref sub) => Terminal::Verify(Arc::new(Miniscript::clone(sub))),
Terminal::NonZero(ref sub) => Terminal::NonZero(Arc::new(Miniscript::clone(sub))),
Terminal::ZeroNotEqual(ref sub) => {
Terminal::ZeroNotEqual(Arc::new(Miniscript::clone(sub)))
}
Terminal::AndV(ref left, ref right) => Terminal::AndV(
Arc::new(Miniscript::clone(left)),
Arc::new(Miniscript::clone(right)),
),
Terminal::AndB(ref left, ref right) => Terminal::AndB(
Arc::new(Miniscript::clone(left)),
Arc::new(Miniscript::clone(right)),
),
Terminal::AndOr(ref a, ref b, ref c) => Terminal::AndOr(
Arc::new(Miniscript::clone(a)),
Arc::new(Miniscript::clone(b)),
Arc::new(Miniscript::clone(c)),
),
Terminal::OrB(ref left, ref right) => {
Terminal::OrB(Arc::new(Miniscript::clone(left)), Arc::new(Miniscript::clone(right)))
}
Terminal::OrD(ref left, ref right) => {
Terminal::OrD(Arc::new(Miniscript::clone(left)), Arc::new(Miniscript::clone(right)))
}
Terminal::OrC(ref left, ref right) => {
Terminal::OrC(Arc::new(Miniscript::clone(left)), Arc::new(Miniscript::clone(right)))
}
Terminal::OrI(ref left, ref right) => {
Terminal::OrI(Arc::new(Miniscript::clone(left)), Arc::new(Miniscript::clone(right)))
}
Terminal::Thresh(ref thresh) => {
Terminal::Thresh(thresh.map_ref(|child| Arc::new(Miniscript::clone(child))))
}
Terminal::Multi(ref thresh) => Terminal::Multi(thresh.clone()),
Terminal::MultiA(ref thresh) => Terminal::MultiA(thresh.clone()),
}
}
}

impl<Pk: MiniscriptKey, Ctx: ScriptContext> PartialEq for Terminal<Pk, Ctx> {
fn eq(&self, other: &Self) -> bool {
for (me, you) in self.pre_order_iter().zip(other.pre_order_iter()) {
Expand Down
67 changes: 66 additions & 1 deletion src/miniscript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ mod private {
use core::marker::PhantomData;

use super::types::{ExtData, Type};
use crate::iter::TreeLike as _;
pub use crate::miniscript::context::ScriptContext;
use crate::miniscript::types;
use crate::prelude::sync::Arc;
use crate::{Error, MiniscriptKey, Terminal, MAX_RECURSION_DEPTH};

/// The top-level miniscript abstract syntax tree (AST).
#[derive(Clone)]
pub struct Miniscript<Pk: MiniscriptKey, Ctx: ScriptContext> {
/// A node in the AST.
pub node: Terminal<Pk, Ctx>,
Expand All @@ -69,6 +70,70 @@ mod private {
/// Context PhantomData. Only accessible inside this crate
phantom: PhantomData<Ctx>,
}

impl<Pk: MiniscriptKey, Ctx: ScriptContext> Clone for Miniscript<Pk, Ctx> {
/// We implement clone as a "deep clone" which reconstructs the entire tree.
///
/// If users just want to clone Arcs they can use Arc::clone themselves.
/// Note that if a Miniscript was constructed using shared Arcs, the result
/// of calling `clone` will no longer have shared Arcs. So there is no
/// pleasing everyone. But for the two common cases:
///
/// * Users don't care about sharing at all, and they can call `Arc::clone`
/// on an `Arc<Miniscript>`.
/// * Users want a deep copy which does not share any nodes with the original
/// (for example, because they have keys that have interior mutability),
/// and they can call `Miniscript::clone`.
fn clone(&self) -> Self {
let mut stack = vec![];
for item in self.post_order_iter() {
let child_n = |n| Arc::clone(&stack[item.child_indices[n]]);

let new_term = match item.node.node {
Terminal::PkK(ref p) => Terminal::PkK(p.clone()),
Terminal::PkH(ref p) => Terminal::PkH(p.clone()),
Terminal::RawPkH(ref p) => Terminal::RawPkH(*p),
Terminal::After(ref n) => Terminal::After(*n),
Terminal::Older(ref n) => Terminal::Older(*n),
Terminal::Sha256(ref x) => Terminal::Sha256(x.clone()),
Terminal::Hash256(ref x) => Terminal::Hash256(x.clone()),
Terminal::Ripemd160(ref x) => Terminal::Ripemd160(x.clone()),
Terminal::Hash160(ref x) => Terminal::Hash160(x.clone()),
Terminal::True => Terminal::True,
Terminal::False => Terminal::False,
Terminal::Alt(..) => Terminal::Alt(child_n(0)),
Terminal::Swap(..) => Terminal::Swap(child_n(0)),
Terminal::Check(..) => Terminal::Check(child_n(0)),
Terminal::DupIf(..) => Terminal::DupIf(child_n(0)),
Terminal::Verify(..) => Terminal::Verify(child_n(0)),
Terminal::NonZero(..) => Terminal::NonZero(child_n(0)),
Terminal::ZeroNotEqual(..) => Terminal::ZeroNotEqual(child_n(0)),
Terminal::AndV(..) => Terminal::AndV(child_n(0), child_n(1)),
Terminal::AndB(..) => Terminal::AndB(child_n(0), child_n(1)),
Terminal::AndOr(..) => Terminal::AndOr(child_n(0), child_n(1), child_n(2)),
Terminal::OrB(..) => Terminal::OrB(child_n(0), child_n(1)),
Terminal::OrD(..) => Terminal::OrD(child_n(0), child_n(1)),
Terminal::OrC(..) => Terminal::OrC(child_n(0), child_n(1)),
Terminal::OrI(..) => Terminal::OrI(child_n(0), child_n(1)),
Terminal::Thresh(ref thresh) => Terminal::Thresh(
thresh.map_from_post_order_iter(&item.child_indices, &stack),
),
Terminal::Multi(ref thresh) => Terminal::Multi(thresh.clone()),
Terminal::MultiA(ref thresh) => Terminal::MultiA(thresh.clone()),
};

stack.push(Arc::new(Miniscript {
node: new_term,
ty: item.node.ty,
ext: item.node.ext,
phantom: PhantomData,
}));
}

Arc::try_unwrap(stack.pop().unwrap()).unwrap()
}
}

impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
/// The `1` combinator.
pub const TRUE: Self = Miniscript {
Expand Down
Loading