Skip to content
Open
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
5 changes: 5 additions & 0 deletions fuzz/fuzz_targets/roundtrip_descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,9 @@ mod tests {
let v = hex::decode_to_vec("abcd").unwrap();
super::do_test(&v);
}

#[test]
fn taproot_tree_format_regression() {
super::do_test(b"tr(acc0,{{multi_a(3,acc10,acc11,acc12),and_v(v:multi_a(2,acc10,acc11,acc12),after(10))},and_v(v:multi_a(1,acc10,acc11,ac12),after(100))})");
}
}
31 changes: 24 additions & 7 deletions src/descriptor/tr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,20 +519,30 @@ mod tests {

use super::*;

fn strip_ws(desc: &str) -> String { desc.replace(&[' ', '\n'][..], "") }

fn descriptor() -> String {
let desc = "tr(acc0, {
multi_a(3, acc10, acc11, acc12), {
{
multi_a(3, acc10, acc11, acc12),
and_v(
v:multi_a(2, acc10, acc11, acc12),
after(10)
),
and_v(
v:multi_a(1, acc10, acc11, ac12),
after(100)
)
}
},
and_v(
v:multi_a(1, acc10, acc11, ac12),
after(100)
)
})";
desc.replace(&[' ', '\n'][..], "")
strip_ws(desc)
}

fn assert_display_roundtrip(desc: &str) {
let desc = strip_ws(desc);
let tr = Tr::<String>::from_str(&desc).unwrap();
assert_eq!(format!("{:#}", tr), desc);
Tr::<String>::from_str(&tr.to_string()).unwrap();
}

#[test]
Expand All @@ -543,6 +553,13 @@ mod tests {
assert!(!tr.for_each_key(|k| k.starts_with("acc")));
}

#[test]
fn display_roundtrips_unbalanced_taptree() {
assert_display_roundtrip(&descriptor());
assert_display_roundtrip("tr(acc0,{pk(acc1),{pk(acc2),pk(acc3)}})");
assert_display_roundtrip("tr(acc0,{{pk(acc1),pk(acc2)},{pk(acc3),pk(acc4)}})");
}

#[test]
fn tr_maximum_depth() {
// Copied from integration tests
Expand Down
52 changes: 42 additions & 10 deletions src/descriptor/tr/taptree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,27 +89,34 @@ fn fmt_helper<Pk: MiniscriptKey>(
f: &mut fmt::Formatter,
mut fmt_ms: impl FnMut(&mut fmt::Formatter, &Miniscript<Pk, Tap>) -> fmt::Result,
) -> fmt::Result {
let mut last_depth = 0;
let mut child_counts = Vec::<u8>::with_capacity(TAPROOT_CONTROL_MAX_NODE_COUNT);

for item in view.leaves() {
if last_depth > 0 {
let depth = usize::from(item.depth());

if !child_counts.is_empty() {
f.write_str(",")?;
}

while last_depth < item.depth() {
while child_counts.len() < depth {
f.write_str("{")?;
last_depth += 1;
child_counts.push(0);
}

fmt_ms(f, item.miniscript())?;
while last_depth > item.depth() {

if let Some(child_count) = child_counts.last_mut() {
*child_count += 1;
}
while let Some(2) = child_counts.last() {
f.write_str("}")?;
last_depth -= 1;
child_counts.pop();
if let Some(child_count) = child_counts.last_mut() {
*child_count += 1;
}
}
}

while last_depth > 0 {
f.write_str("}")?;
last_depth -= 1;
}
Ok(())
}

Expand Down Expand Up @@ -284,3 +291,28 @@ impl<Pk: MiniscriptKey> TapTreeBuilder<Pk> {
#[inline]
pub(super) fn finalize(self) -> TapTree<Pk> { TapTree { depths_leaves: self.depths_leaves } }
}

#[cfg(test)]
mod tests {
use core::str::FromStr;

use super::*;

fn leaf(ms: &str) -> TapTree<String> {
TapTree::leaf(Arc::new(Miniscript::<String, Tap>::from_str(ms).unwrap()))
}

#[test]
fn display_single_leaf_taptree() {
let tree = leaf("pk(A)");

assert_eq!(format!("{}", tree), "pk(A)");
}

#[test]
fn debug_binary_taptree() {
let tree = TapTree::combine(leaf("pk(A)"), leaf("pk(B)")).unwrap();

assert_eq!(format!("{:?}", tree), "{[B/onduesm]pk(\"A\"),[B/onduesm]pk(\"B\")}");
}
}