Skip to content

Commit 6b7fabd

Browse files
taproot descriptor: strucuture, parsing and testing done
1 parent 73dd23e commit 6b7fabd

File tree

4 files changed

+185
-116
lines changed

4 files changed

+185
-116
lines changed

src/descriptor/mod.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub use self::bare::{Bare, Pkh};
5151
pub use self::segwitv0::{Wpkh, Wsh, WshInner};
5252
pub use self::sh::{Sh, ShInner};
5353
pub use self::sortedmulti::SortedMultiVec;
54+
pub use self::tr::Tr;
5455

5556
mod checksum;
5657
mod key;
@@ -168,6 +169,9 @@ pub enum Descriptor<Pk: MiniscriptKey> {
168169
Sh(Sh<Pk>),
169170
/// Pay-to-Witness-ScriptHash with Segwitv0 context
170171
Wsh(Wsh<Pk>),
172+
// /// Pay-to-Taproot with Segwitv0 context
173+
// /// TODO: Update context to Segwitv1
174+
// Tr(Tr<Pk>)
171175
}
172176

173177
/// Descriptor Type of the descriptor
@@ -193,6 +197,8 @@ pub enum DescriptorType {
193197
WshSortedMulti,
194198
/// Sh Wsh Sorted Multi
195199
ShWshSortedMulti,
200+
// /// Tr Descriptor
201+
// Tr
196202
}
197203

198204
impl<Pk: MiniscriptKey> Descriptor<Pk> {
@@ -279,6 +285,12 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
279285
Ok(Descriptor::Wsh(Wsh::new_sortedmulti(k, pks)?))
280286
}
281287

288+
// /// Create new tr descriptor
289+
// /// Errors when miniscript exceeds resource limits under Segwitv0 context
290+
// pub fn new_tr(key: Pk, script: Option<tr::TapTree<Pk>>) -> Result<Self, Error> {
291+
// Ok(Descriptor::Tr(Tr::new(key, script)?))
292+
// }
293+
282294
/// Get the [DescriptorType] of [Descriptor]
283295
pub fn desc_type(&self) -> DescriptorType {
284296
match *self {
@@ -298,6 +310,7 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
298310
WshInner::SortedMulti(ref _smv) => DescriptorType::WshSortedMulti,
299311
WshInner::Ms(ref _ms) => DescriptorType::Wsh,
300312
},
313+
// Descriptor::Tr(_) => DescriptorType::Tr,
301314
}
302315
}
303316
}
@@ -625,6 +638,7 @@ serde_string_impl_pk!(Descriptor, "a script descriptor");
625638
mod tests {
626639
use super::checksum::desc_checksum;
627640
use super::DescriptorTrait;
641+
use super::Tr;
628642
use bitcoin::blockdata::opcodes::all::{OP_CLTV, OP_CSV};
629643
use bitcoin::blockdata::script::Instruction;
630644
use bitcoin::blockdata::{opcodes, script};
@@ -1088,6 +1102,34 @@ mod tests {
10881102
assert_eq!(check, &Ok(Instruction::Op(OP_CSV)))
10891103
}
10901104

1105+
#[test]
1106+
fn tr_roundtrip_key() {
1107+
let script = Tr::<DummyKey>::from_str("tr()").unwrap().to_string();
1108+
assert_eq!(script, format!("tr()"))
1109+
}
1110+
1111+
#[test]
1112+
fn tr_roundtrip_script() {
1113+
let descriptor = Tr::<DummyKey>::from_str(&format!("tr(,{{pk(),pk()}})"))
1114+
.unwrap()
1115+
.to_string();
1116+
1117+
assert_eq!(descriptor, format!("tr(,{{pk(),pk()}})"))
1118+
}
1119+
1120+
#[test]
1121+
fn tr_roundtrip_tree() {
1122+
let descriptor =
1123+
Tr::<DummyKey>::from_str(&format!("tr(,{{pk(),{{pk(),or_d(pk(),pkh())}}}})"))
1124+
.unwrap()
1125+
.to_string();
1126+
1127+
assert_eq!(
1128+
descriptor,
1129+
format!("tr(,{{pk(),{{pk(),or_d(pk(),pkh())}}}})")
1130+
)
1131+
}
1132+
10911133
#[test]
10921134
fn roundtrip_tests() {
10931135
let descriptor = Descriptor::<bitcoin::PublicKey>::from_str("multi");

src/descriptor/tr.rs

Lines changed: 125 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@
55
// DescriptorTrait,
66
// };
77
use bitcoin::hashes::_export::_core::fmt::Formatter;
8+
use errstr;
89
use expression::{self, FromTree, Tree};
910
use std::sync::Arc;
1011
use std::{fmt, str::FromStr};
1112
use Segwitv0;
13+
use MAX_RECURSION_DEPTH;
1214
use {miniscript::Miniscript, Error, MiniscriptKey};
1315

14-
// TODO: Update this to infer version from descriptor.
15-
const VER: u8 = 0xc0;
16-
1716
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
1817
pub enum TapTree<Pk: MiniscriptKey> {
1918
Tree(Arc<TapTree<Pk>>, Arc<TapTree<Pk>>),
20-
Miniscript_(u8, Arc<Miniscript<Pk, Segwitv0>>),
19+
Miniscript_(Arc<Miniscript<Pk, Segwitv0>>),
2120
}
2221

2322
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
@@ -35,8 +34,10 @@ where
3534
{
3635
pub fn to_string_no_checksum(&self) -> String {
3736
match self {
38-
TapTree::Tree(ref left, ref right) => format!("{{{},{}}}", *left, *right),
39-
TapTree::Miniscript_(_, ref miniscript) => format!("{}", *miniscript),
37+
TapTree::Tree(ref left, ref right) => {
38+
format!("{{{},{}}}", *left.clone(), *right.clone())
39+
}
40+
TapTree::Miniscript_(ref script) => format!("{}", *script.clone()),
4041
}
4142
}
4243
}
@@ -51,7 +52,7 @@ where
5152
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
5253
let desc = self.to_string_no_checksum();
5354
// let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
54-
// write!(f, "{}", &desc)
55+
// write!(f, "{}#{}", &desc, &checksum)
5556
write!(f, "{}", &desc)
5657
}
5758
}
@@ -90,7 +91,7 @@ where
9091
// Sanity checks
9192
let script = Self::parse_miniscript(script)?;
9293
let script = Arc::new(script);
93-
Ok(TapTree::Miniscript_(VER, script))
94+
Ok(TapTree::Miniscript_(script))
9495
}
9596
Tree { name, args } if name.len() == 0 && args.len() == 2 => {
9697
// visit children
@@ -112,7 +113,7 @@ where
112113
}
113114
}
114115

115-
impl<Pk> FromTree for Tr<Pk>
116+
impl<Pk: MiniscriptKey> FromTree for Tr<Pk>
116117
where
117118
Pk: MiniscriptKey + FromStr,
118119
Pk::Hash: FromStr,
@@ -143,32 +144,32 @@ where
143144
key.args.len()
144145
)));
145146
}
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)?;
147+
let ref tree = top.args[1];
148+
let ret = Tr::tr_script_path(tree)?;
148149
Ok(Tr {
149150
key_path: expression::terminal(key, Pk::from_str)?,
150151
script_path: Some(ret),
151152
})
152153
}
153154
_ => {
154155
return Err(Error::Unexpected(format!(
155-
"{}({} args) while parsing taproot descriptor",
156+
"{}[#{} args] while parsing taproot descriptor",
156157
top.name,
157158
top.args.len()
158159
)));
159160
}
160161
}
161162
} else {
162163
return Err(Error::Unexpected(format!(
163-
"{}({} args) while parsing taproot descriptor",
164+
"{}[#{} args] while parsing taproot descriptor",
164165
top.name,
165166
top.args.len()
166167
)));
167168
}
168169
}
169170
}
170171

171-
impl<Pk> FromStr for Tr<Pk>
172+
impl<Pk: MiniscriptKey> FromStr for Tr<Pk>
172173
where
173174
Pk: MiniscriptKey + FromStr,
174175
Pk::Hash: FromStr,
@@ -180,8 +181,10 @@ where
180181
fn from_str(s: &str) -> Result<Self, Self::Err> {
181182
// let desc_str = verify_checksum(s)?;
182183
// let top = expression::Tree::from_str(desc_str)?;
183-
let top = expression::Tree::from_str(s)?;
184-
Self::from_tree(&top)
184+
185+
// Pass the TapTree then
186+
let top = parse_tr(s)?;
187+
Self::from_tree(&top) // parse taptree and tapscript differently
185188
}
186189
}
187190

@@ -193,9 +196,113 @@ where
193196
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
194197
{
195198
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199+
let key = &self.key_path;
196200
match self.script_path {
197-
Some(ref s) => write!(f, "tr({},{})", self.key_path, s),
198-
None => write!(f, "tr({})", self.key_path),
201+
Some(ref s) => write!(f, "tr({},{})", key, s),
202+
None => write!(f, "tr({})", key),
203+
}
204+
}
205+
}
206+
207+
/// TODO: Refactor to make from_str more generic in `expression.rs`?
208+
fn parse_tr<'a>(s: &'a str) -> Result<Tree<'a>, Error> {
209+
for ch in s.bytes() {
210+
if ch > 0x7f {
211+
return Err(Error::Unprintable(ch));
212+
}
213+
}
214+
215+
let (top, rem) = parse_tr_helper(s, 0)?;
216+
if rem.is_empty() {
217+
Ok(top)
218+
} else {
219+
Err(errstr(rem))
220+
}
221+
}
222+
223+
/// Helper function to parse Taproot Descriptor into key_path and TapTree
224+
fn parse_tr_helper<'a>(mut sl: &'a str, depth: u32) -> Result<(Tree<'a>, &'a str), Error> {
225+
if depth >= MAX_RECURSION_DEPTH {
226+
return Err(Error::MaxRecursiveDepthExceeded);
227+
}
228+
229+
enum Found {
230+
Nothing,
231+
Lparen(usize),
232+
Comma(usize),
233+
Rparen(usize),
234+
}
235+
236+
let mut found = Found::Nothing;
237+
for (n, ch) in sl.char_indices() {
238+
match ch {
239+
'(' => {
240+
found = Found::Lparen(n);
241+
break;
242+
}
243+
',' => {
244+
found = Found::Comma(n);
245+
break;
246+
}
247+
')' => {
248+
found = Found::Rparen(n);
249+
break;
250+
}
251+
_ => {}
252+
}
253+
}
254+
255+
match found {
256+
// String-ending terminal
257+
Found::Nothing => Ok((
258+
Tree {
259+
name: &sl[..],
260+
args: vec![],
261+
},
262+
"",
263+
)),
264+
// Terminal
265+
Found::Comma(n) | Found::Rparen(n) => Ok((
266+
Tree {
267+
name: &sl[..n],
268+
args: vec![],
269+
},
270+
&sl[n..],
271+
)),
272+
// Function call
273+
Found::Lparen(n) => {
274+
let mut ret = Tree {
275+
name: &sl[..n],
276+
args: vec![],
277+
};
278+
279+
sl = &sl[n + 1..];
280+
let mut prev_sl: &str = "";
281+
let mut prev_arg: Tree;
282+
loop {
283+
if prev_sl.contains("{") {
284+
let (arg, new_sl) = expression::Tree::from_slice_helper_curly(sl, depth + 1)?;
285+
prev_arg = arg;
286+
prev_sl = new_sl;
287+
} else {
288+
let (arg, new_sl) = parse_tr_helper(sl, depth + 1)?;
289+
prev_arg = arg;
290+
prev_sl = new_sl;
291+
}
292+
ret.args.push(prev_arg);
293+
294+
if prev_sl.is_empty() {
295+
return Err(Error::ExpectedChar(')'));
296+
}
297+
298+
sl = &prev_sl[1..];
299+
match prev_sl.as_bytes()[0] {
300+
b',' => {}
301+
b')' => break,
302+
_ => return Err(Error::ExpectedChar(')')),
303+
}
304+
}
305+
Ok((ret, sl))
199306
}
200307
}
201308
}

0 commit comments

Comments
 (0)