|
| 1 | +use miniscript::descriptor::TapTree; |
| 2 | +use miniscript::{Miniscript, MiniscriptKey}; |
| 3 | + |
| 4 | +use crate::error::WasmUtxoError; |
| 5 | + |
| 6 | +/// The recursive tap tree was removed from rust-miniscript in https://github.com/rust-bitcoin/rust-miniscript/pull/808 |
| 7 | +/// Our API is somewhat dependent on it and providing backwards compatibility is easier than rewriting everything. |
| 8 | +pub enum RecursiveTapTree<Pk: MiniscriptKey> { |
| 9 | + Tree { |
| 10 | + left: Box<RecursiveTapTree<Pk>>, |
| 11 | + right: Box<RecursiveTapTree<Pk>>, |
| 12 | + }, |
| 13 | + Leaf(Miniscript<Pk, miniscript::Tap>), |
| 14 | +} |
| 15 | + |
| 16 | +impl<Pk: MiniscriptKey + Clone> TryFrom<&TapTree<Pk>> for RecursiveTapTree<Pk> { |
| 17 | + type Error = WasmUtxoError; |
| 18 | + |
| 19 | + fn try_from(tree: &TapTree<Pk>) -> Result<Self, Self::Error> { |
| 20 | + use std::sync::Arc; |
| 21 | + |
| 22 | + // Collect leaves with depths (miniscript() returns Arc<Miniscript>) |
| 23 | + let leaves: Vec<(u8, Arc<Miniscript<Pk, miniscript::Tap>>)> = tree |
| 24 | + .leaves() |
| 25 | + .map(|item| (item.depth(), item.miniscript().clone())) |
| 26 | + .collect(); |
| 27 | + |
| 28 | + if leaves.is_empty() { |
| 29 | + return Err(WasmUtxoError::new("Empty tap tree")); |
| 30 | + } |
| 31 | + |
| 32 | + // Stack-based reconstruction: process leaves left-to-right, |
| 33 | + // combining siblings at the same depth into Tree nodes |
| 34 | + let mut stack: Vec<(u8, RecursiveTapTree<Pk>)> = Vec::new(); |
| 35 | + |
| 36 | + for (depth, ms) in leaves { |
| 37 | + // Clone the Miniscript from the Arc |
| 38 | + stack.push((depth, RecursiveTapTree::Leaf((*ms).clone()))); |
| 39 | + |
| 40 | + // Combine nodes at the same depth |
| 41 | + while stack.len() >= 2 { |
| 42 | + let len = stack.len(); |
| 43 | + if stack[len - 2].0 != stack[len - 1].0 { |
| 44 | + break; |
| 45 | + } |
| 46 | + |
| 47 | + let (_, right) = stack.pop().unwrap(); |
| 48 | + let (d, left) = stack.pop().unwrap(); |
| 49 | + |
| 50 | + stack.push(( |
| 51 | + d - 1, |
| 52 | + RecursiveTapTree::Tree { |
| 53 | + left: Box::new(left), |
| 54 | + right: Box::new(right), |
| 55 | + }, |
| 56 | + )); |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + if stack.len() != 1 { |
| 61 | + return Err(WasmUtxoError::new("Invalid tap tree structure")); |
| 62 | + } |
| 63 | + |
| 64 | + Ok(stack.pop().unwrap().1) |
| 65 | + } |
| 66 | +} |
0 commit comments