Skip to content

Commit efa12a7

Browse files
taproot descriptor parsing done
1 parent 7d33643 commit efa12a7

File tree

5 files changed

+383
-6
lines changed

5 files changed

+383
-6
lines changed

src/descriptor/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod bare;
4545
mod segwitv0;
4646
mod sh;
4747
mod sortedmulti;
48+
mod tr;
4849
// Descriptor Exports
4950
pub use self::bare::{Bare, Pkh};
5051
pub use self::segwitv0::{Wpkh, Wsh, WshInner};
@@ -53,6 +54,7 @@ pub use self::sortedmulti::SortedMultiVec;
5354

5455
mod checksum;
5556
mod key;
57+
5658
pub use self::key::{
5759
ConversionError, DescriptorKeyParseError, DescriptorPublicKey, DescriptorSecretKey,
5860
DescriptorSinglePriv, DescriptorSinglePub, DescriptorXKey, InnerXKey, Wildcard,

src/descriptor/tr.rs

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
// Tapscript
2+
3+
use super::checksum::{desc_checksum, verify_checksum};
4+
use bitcoin::hashes::_export::_core::fmt::Formatter;
5+
use errstr;
6+
use expression::{self, FromTree, Tree};
7+
use miniscript::{limits::TAPROOT_MAX_NODE_COUNT, Miniscript};
8+
use std::cmp::max;
9+
use std::sync::Arc;
10+
use std::{fmt, str::FromStr};
11+
use Segwitv0;
12+
use {Error, MiniscriptKey};
13+
14+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
15+
pub enum TapTree<Pk: MiniscriptKey> {
16+
Tree(Arc<TapTree<Pk>>, Arc<TapTree<Pk>>),
17+
Leaf(Arc<Miniscript<Pk, Segwitv0>>),
18+
}
19+
20+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
21+
pub struct Tr<Pk: MiniscriptKey> {
22+
internal_key: Pk,
23+
tree: Option<TapTree<Pk>>,
24+
}
25+
26+
impl<Pk: MiniscriptKey> TapTree<Pk> {
27+
pub fn to_string_no_checksum(&self) -> String {
28+
match self {
29+
TapTree::Tree(ref left, ref right) => {
30+
format!("{{{},{}}}", *left, *right)
31+
}
32+
TapTree::Leaf(ref script) => format!("{}", *script),
33+
}
34+
}
35+
}
36+
37+
impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk> {
38+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
39+
let desc = self.to_string_no_checksum();
40+
write!(f, "{}", &desc)
41+
}
42+
}
43+
44+
impl<Pk: MiniscriptKey> Tr<Pk> {
45+
fn taptree_height(tree: &TapTree<Pk>) -> usize {
46+
match tree {
47+
TapTree::Tree(ref left_tree, ref right_tree) => {
48+
1 + max(
49+
Self::taptree_height(&**left_tree),
50+
Self::taptree_height(&**right_tree),
51+
)
52+
}
53+
TapTree::Leaf(_) => 1,
54+
}
55+
}
56+
57+
pub fn new(internal_key: Pk, tree: Option<TapTree<Pk>>) -> Result<Self, Error> {
58+
let nodes = match tree {
59+
Some(ref t) => Self::taptree_height(t),
60+
None => 0,
61+
};
62+
63+
if nodes > TAPROOT_MAX_NODE_COUNT {
64+
Ok(Self { internal_key, tree })
65+
} else {
66+
Err(Error::MaxRecursiveDepthExceeded)
67+
}
68+
}
69+
70+
fn to_string_no_checksum(&self) -> String {
71+
let key = &self.internal_key;
72+
match self.tree {
73+
Some(ref s) => format!("tr({},{})", key, s),
74+
None => format!("tr({})", key),
75+
}
76+
}
77+
78+
pub fn get_internal_key(&self) -> Result<Pk, Error> {
79+
Ok(self.internal_key.clone())
80+
}
81+
82+
pub fn get_taptree(&self) -> Result<TapTree<Pk>, Error> {
83+
match &self.tree {
84+
Some(t) => Ok(t.clone()),
85+
None => Err(Error::Unexpected("TapTree empty".to_string())),
86+
}
87+
}
88+
}
89+
90+
impl<Pk> Tr<Pk>
91+
where
92+
Pk: MiniscriptKey + FromStr,
93+
Pk::Hash: FromStr,
94+
<Pk as FromStr>::Err: ToString,
95+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
96+
{
97+
fn parse_miniscript(script: &str) -> Result<Miniscript<Pk, Segwitv0>, Error> {
98+
let (ref script_tree, rest) = Tree::from_slice(script)?;
99+
if rest.is_empty() {
100+
Miniscript::<Pk, Segwitv0>::from_tree(script_tree)
101+
} else {
102+
return Err(Error::Unexpected(format!(
103+
"error parsing miniscript from tapscript"
104+
)));
105+
}
106+
}
107+
108+
// helper function for semantic parsing of script paths
109+
pub fn tr_script_path(tree: &Tree) -> Result<TapTree<Pk>, Error> {
110+
match tree {
111+
Tree { name, args } if name.len() > 0 && args.len() == 0 => {
112+
let script = name;
113+
let script = Self::parse_miniscript(script)?;
114+
let script = Arc::new(script);
115+
Ok(TapTree::Leaf(script))
116+
}
117+
Tree { name, args } if name.len() == 0 && args.len() == 2 => {
118+
let left_branch = &args[0];
119+
let right_branch = &args[1];
120+
let left_tree = Self::tr_script_path(&left_branch)?;
121+
let right_tree = Self::tr_script_path(&right_branch)?;
122+
let left_ref = Arc::new(left_tree);
123+
let right_ref = Arc::new(right_tree);
124+
Ok(TapTree::Tree(left_ref, right_ref))
125+
}
126+
_ => {
127+
return Err(Error::Unexpected(
128+
"unknown format for script spending paths while parsing taproot descriptor"
129+
.to_string(),
130+
));
131+
}
132+
}
133+
}
134+
}
135+
136+
impl<Pk: MiniscriptKey> FromTree for Tr<Pk>
137+
where
138+
Pk: MiniscriptKey + FromStr,
139+
Pk::Hash: FromStr,
140+
<Pk as FromStr>::Err: ToString,
141+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
142+
{
143+
fn from_tree(top: &Tree) -> Result<Self, Error> {
144+
if top.name == "tr" {
145+
match top.args.len() {
146+
1 => {
147+
let key = &top.args[0];
148+
if key.args.len() > 0 {
149+
return Err(Error::Unexpected(format!(
150+
"#{} script associated with `key-path` while parsing taproot descriptor",
151+
key.args.len()
152+
)));
153+
}
154+
Ok(Tr {
155+
internal_key: expression::terminal(key, Pk::from_str)?,
156+
tree: None,
157+
})
158+
}
159+
2 => {
160+
let ref key = top.args[0];
161+
if key.args.len() > 0 {
162+
return Err(Error::Unexpected(format!(
163+
"#{} script associated with `key-path` while parsing taproot descriptor",
164+
key.args.len()
165+
)));
166+
}
167+
let ref tree = top.args[1];
168+
let ret = Tr::tr_script_path(tree)?;
169+
Ok(Tr {
170+
internal_key: expression::terminal(key, Pk::from_str)?,
171+
tree: Some(ret),
172+
})
173+
}
174+
_ => {
175+
return Err(Error::Unexpected(format!(
176+
"{}[#{} args] while parsing taproot descriptor",
177+
top.name,
178+
top.args.len()
179+
)));
180+
}
181+
}
182+
} else {
183+
return Err(Error::Unexpected(format!(
184+
"{}[#{} args] while parsing taproot descriptor",
185+
top.name,
186+
top.args.len()
187+
)));
188+
}
189+
}
190+
}
191+
192+
impl<Pk: MiniscriptKey> FromStr for Tr<Pk>
193+
where
194+
Pk: MiniscriptKey + FromStr,
195+
Pk::Hash: FromStr,
196+
<Pk as FromStr>::Err: ToString,
197+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
198+
{
199+
type Err = Error;
200+
201+
fn from_str(s: &str) -> Result<Self, Self::Err> {
202+
let desc_str = verify_checksum(s)?;
203+
let top = parse_tr(desc_str)?;
204+
Self::from_tree(&top)
205+
}
206+
}
207+
208+
impl<Pk: MiniscriptKey> fmt::Display for Tr<Pk> {
209+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210+
let desc = self.to_string_no_checksum();
211+
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
212+
write!(f, "{}#{}", &desc, &checksum)
213+
}
214+
}
215+
216+
fn parse_tr(s: &str) -> Result<Tree, Error> {
217+
for ch in s.bytes() {
218+
if ch > 0x7f {
219+
return Err(Error::Unprintable(ch));
220+
}
221+
}
222+
223+
return if s.len() > 3 && &s[..3] == "tr(" && s.as_bytes()[s.len() - 1] == b')' {
224+
let rest = &s[3..s.len() - 1];
225+
if !rest.contains(',') {
226+
let internal_key = Tree {
227+
name: rest,
228+
args: vec![],
229+
};
230+
return Ok(Tree {
231+
name: "tr",
232+
args: vec![internal_key],
233+
});
234+
}
235+
// use str::split_once() method to refactor this when compiler version bumps up
236+
let (key, script) = split_once(rest, ',')
237+
.ok_or_else(|| Error::BadDescriptor("invalid taproot descriptor".to_string()))?;
238+
let internal_key = Tree {
239+
name: key,
240+
args: vec![],
241+
};
242+
if script.is_empty() {
243+
return Ok(Tree {
244+
name: "tr",
245+
args: vec![internal_key],
246+
});
247+
}
248+
let (tree, rest) = expression::Tree::from_slice_helper_curly(script, 1)?;
249+
if rest.is_empty() {
250+
Ok(Tree {
251+
name: "tr",
252+
args: vec![internal_key, tree],
253+
})
254+
} else {
255+
Err(errstr(rest))
256+
}
257+
} else {
258+
Err(Error::Unexpected("invalid taproot descriptor".to_string()))
259+
};
260+
}
261+
262+
fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> {
263+
return if inp.len() == 0 {
264+
None
265+
} else {
266+
let mut found = 0;
267+
for (idx, ch) in inp.chars().enumerate() {
268+
if ch == delim {
269+
found = idx;
270+
break;
271+
}
272+
}
273+
if found == inp.len() - 1 {
274+
Some((&inp[..], &""))
275+
} else {
276+
Some((&inp[..found], &inp[found + 1..]))
277+
}
278+
};
279+
}

0 commit comments

Comments
 (0)