Skip to content

Commit 73dd23e

Browse files
tr.rs: tentative taproot tree structure and parsing utilities
1 parent 7d33643 commit 73dd23e

File tree

4 files changed

+386
-7
lines changed

4 files changed

+386
-7
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: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Tapscript
2+
3+
// use super::{
4+
// checksum::{desc_checksum, verify_checksum},
5+
// DescriptorTrait,
6+
// };
7+
use bitcoin::hashes::_export::_core::fmt::Formatter;
8+
use expression::{self, FromTree, Tree};
9+
use std::sync::Arc;
10+
use std::{fmt, str::FromStr};
11+
use Segwitv0;
12+
use {miniscript::Miniscript, Error, MiniscriptKey};
13+
14+
// TODO: Update this to infer version from descriptor.
15+
const VER: u8 = 0xc0;
16+
17+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
18+
pub enum TapTree<Pk: MiniscriptKey> {
19+
Tree(Arc<TapTree<Pk>>, Arc<TapTree<Pk>>),
20+
Miniscript_(u8, Arc<Miniscript<Pk, Segwitv0>>),
21+
}
22+
23+
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
24+
pub struct Tr<Pk: MiniscriptKey> {
25+
key_path: Pk,
26+
script_path: Option<TapTree<Pk>>,
27+
}
28+
29+
impl<Pk: MiniscriptKey> TapTree<Pk>
30+
where
31+
Pk: MiniscriptKey + FromStr,
32+
Pk::Hash: FromStr,
33+
<Pk as FromStr>::Err: ToString,
34+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
35+
{
36+
pub fn to_string_no_checksum(&self) -> String {
37+
match self {
38+
TapTree::Tree(ref left, ref right) => format!("{{{},{}}}", *left, *right),
39+
TapTree::Miniscript_(_, ref miniscript) => format!("{}", *miniscript),
40+
}
41+
}
42+
}
43+
44+
impl<Pk: MiniscriptKey> fmt::Display for TapTree<Pk>
45+
where
46+
Pk: MiniscriptKey + FromStr,
47+
Pk::Hash: FromStr,
48+
<Pk as FromStr>::Err: ToString,
49+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
50+
{
51+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52+
let desc = self.to_string_no_checksum();
53+
// let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
54+
// write!(f, "{}", &desc)
55+
write!(f, "{}", &desc)
56+
}
57+
}
58+
59+
impl<Pk: MiniscriptKey> Tr<Pk>
60+
where
61+
Pk: MiniscriptKey + FromStr,
62+
Pk::Hash: FromStr,
63+
<Pk as FromStr>::Err: ToString,
64+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
65+
{
66+
pub fn new(key_path: Pk, script_path: Option<TapTree<Pk>>) -> Result<Self, Error> {
67+
Ok(Tr {
68+
key_path,
69+
script_path,
70+
})
71+
}
72+
73+
fn parse_miniscript(script: &str) -> Result<Miniscript<Pk, Segwitv0>, Error> {
74+
let (ref script_tree, rest) = Tree::from_slice(script)?;
75+
if rest.is_empty() {
76+
Miniscript::<Pk, Segwitv0>::from_tree(script_tree)
77+
} else {
78+
return Err(Error::Unexpected(format!(
79+
"error parsing miniscript from tapscript"
80+
)));
81+
}
82+
}
83+
84+
// helper function for semantic parsing of script paths
85+
pub fn tr_script_path(tree: &Tree) -> Result<TapTree<Pk>, Error> {
86+
match tree {
87+
Tree { name, args } if name.len() > 0 && args.len() == 0 => {
88+
// children nodes
89+
let script = name;
90+
// Sanity checks
91+
let script = Self::parse_miniscript(script)?;
92+
let script = Arc::new(script);
93+
Ok(TapTree::Miniscript_(VER, script))
94+
}
95+
Tree { name, args } if name.len() == 0 && args.len() == 2 => {
96+
// visit children
97+
let left_branch = &args[0];
98+
let right_branch = &args[1];
99+
let left_tree = Self::tr_script_path(&left_branch)?;
100+
let right_tree = Self::tr_script_path(&right_branch)?;
101+
let left_ref = Arc::new(left_tree);
102+
let right_ref = Arc::new(right_tree);
103+
Ok(TapTree::Tree(left_ref, right_ref))
104+
}
105+
_ => {
106+
return Err(Error::Unexpected(
107+
"unknown format for script spending paths while parsing taproot descriptor"
108+
.to_string(),
109+
));
110+
}
111+
}
112+
}
113+
}
114+
115+
impl<Pk> FromTree for Tr<Pk>
116+
where
117+
Pk: MiniscriptKey + FromStr,
118+
Pk::Hash: FromStr,
119+
<Pk as FromStr>::Err: ToString,
120+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
121+
{
122+
fn from_tree(top: &Tree) -> Result<Self, Error> {
123+
if top.name == "tr" {
124+
match top.args.len() {
125+
1 => {
126+
let key = &top.args[0];
127+
if key.args.len() > 0 {
128+
return Err(Error::Unexpected(format!(
129+
"#{} script associated with `key-path` while parsing taproot descriptor",
130+
key.args.len()
131+
)));
132+
}
133+
Ok(Tr {
134+
key_path: expression::terminal(key, Pk::from_str)?,
135+
script_path: None,
136+
})
137+
}
138+
2 => {
139+
let ref key = top.args[0];
140+
if key.args.len() > 0 {
141+
return Err(Error::Unexpected(format!(
142+
"#{} script associated with `key-path` while parsing taproot descriptor",
143+
key.args.len()
144+
)));
145+
}
146+
let ref tree = top.args[1]; // Tree name should be a valid miniscript except the base case
147+
let ret = Tr::tr_script_path(&tree)?;
148+
Ok(Tr {
149+
key_path: expression::terminal(key, Pk::from_str)?,
150+
script_path: Some(ret),
151+
})
152+
}
153+
_ => {
154+
return Err(Error::Unexpected(format!(
155+
"{}({} args) while parsing taproot descriptor",
156+
top.name,
157+
top.args.len()
158+
)));
159+
}
160+
}
161+
} else {
162+
return Err(Error::Unexpected(format!(
163+
"{}({} args) while parsing taproot descriptor",
164+
top.name,
165+
top.args.len()
166+
)));
167+
}
168+
}
169+
}
170+
171+
impl<Pk> FromStr for Tr<Pk>
172+
where
173+
Pk: MiniscriptKey + FromStr,
174+
Pk::Hash: FromStr,
175+
<Pk as FromStr>::Err: ToString,
176+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
177+
{
178+
type Err = Error;
179+
180+
fn from_str(s: &str) -> Result<Self, Self::Err> {
181+
// let desc_str = verify_checksum(s)?;
182+
// let top = expression::Tree::from_str(desc_str)?;
183+
let top = expression::Tree::from_str(s)?;
184+
Self::from_tree(&top)
185+
}
186+
}
187+
188+
impl<Pk: MiniscriptKey> fmt::Display for Tr<Pk>
189+
where
190+
Pk: MiniscriptKey + FromStr,
191+
Pk::Hash: FromStr,
192+
<Pk as FromStr>::Err: ToString,
193+
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
194+
{
195+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196+
match self.script_path {
197+
Some(ref s) => write!(f, "tr({},{})", self.key_path, s),
198+
None => write!(f, "tr({})", self.key_path),
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)