1
1
// SPDX-License-Identifier: CC0-1.0
2
2
3
- use core:: str:: FromStr ;
4
3
use core:: { cmp, fmt, hash} ;
5
4
6
5
#[ cfg( not( test) ) ] // https://github.com/rust-lang/rust/issues/121684
@@ -12,9 +11,10 @@ use bitcoin::taproot::{
12
11
use bitcoin:: { opcodes, Address , Network , ScriptBuf , Weight } ;
13
12
use sync:: Arc ;
14
13
15
- use super :: checksum:: { self , verify_checksum } ;
14
+ use super :: checksum;
16
15
use crate :: descriptor:: DefiniteDescriptorKey ;
17
16
use crate :: expression:: { self , FromTree } ;
17
+ use crate :: iter:: TreeLike as _;
18
18
use crate :: miniscript:: satisfy:: { Placeholder , Satisfaction , SchnorrSigType , Witness } ;
19
19
use crate :: miniscript:: Miniscript ;
20
20
use crate :: plan:: AssetProvider ;
@@ -23,8 +23,8 @@ use crate::policy::Liftable;
23
23
use crate :: prelude:: * ;
24
24
use crate :: util:: { varint_len, witness_size} ;
25
25
use crate :: {
26
- Error , ForEachKey , FromStrKey , MiniscriptKey , Satisfier , ScriptContext , Tap , Threshold ,
27
- ToPublicKey , TranslateErr , Translator ,
26
+ Error , ForEachKey , FromStrKey , MiniscriptKey , ParseError , Satisfier , ScriptContext , Tap ,
27
+ Threshold , ToPublicKey , TranslateErr , Translator ,
28
28
} ;
29
29
30
30
/// A Taproot Tree representation.
@@ -490,79 +490,114 @@ where
490
490
}
491
491
}
492
492
493
- #[ rustfmt:: skip]
494
- impl < Pk : FromStrKey > Tr < Pk > {
495
- // Helper function to parse taproot script path
496
- fn parse_tr_script_spend ( tree : & expression:: Tree , ) -> Result < TapTree < Pk > , Error > {
497
- match tree {
498
- expression:: Tree { name, args, .. } if !name. is_empty ( ) && args. is_empty ( ) => {
499
- let script = Miniscript :: < Pk , Tap > :: from_str ( name) ?;
500
- Ok ( TapTree :: Leaf ( Arc :: new ( script) ) )
501
- }
502
- expression:: Tree { name, args, .. } if name. is_empty ( ) && args. len ( ) == 2 => {
503
- let left = Self :: parse_tr_script_spend ( & args[ 0 ] ) ?;
504
- let right = Self :: parse_tr_script_spend ( & args[ 1 ] ) ?;
505
- Ok ( TapTree :: combine ( left, right) )
506
- }
507
- _ => {
508
- Err ( Error :: Unexpected (
509
- "unknown format for script spending paths while parsing taproot descriptor"
510
- . to_string ( ) ,
511
- ) ) } ,
512
- }
493
+ impl < Pk : FromStrKey > core:: str:: FromStr for Tr < Pk > {
494
+ type Err = Error ;
495
+
496
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
497
+ let expr_tree = expression:: Tree :: from_str ( s) ?;
498
+ Self :: from_tree ( & expr_tree)
513
499
}
514
500
}
515
501
516
502
impl < Pk : FromStrKey > crate :: expression:: FromTree for Tr < Pk > {
517
- fn from_tree ( top : & expression:: Tree ) -> Result < Self , Error > {
518
- if top. name == "tr" {
519
- match top. args . len ( ) {
520
- 1 => {
521
- let key = & top. args [ 0 ] ;
522
- if !key. args . is_empty ( ) {
523
- return Err ( Error :: Unexpected ( format ! (
524
- "#{} script associated with `key-path` while parsing taproot descriptor" ,
525
- key. args. len( )
526
- ) ) ) ;
503
+ fn from_tree ( expr_tree : & expression:: Tree ) -> Result < Self , Error > {
504
+ use crate :: expression:: { Parens , ParseTreeError } ;
505
+
506
+ expr_tree
507
+ . verify_toplevel ( "tr" , 1 ..=2 )
508
+ . map_err ( From :: from)
509
+ . map_err ( Error :: Parse ) ?;
510
+
511
+ let mut round_paren_depth = 0 ;
512
+
513
+ let mut internal_key = None ;
514
+ let mut tree_stack = vec ! [ ] ;
515
+
516
+ for item in expr_tree. verbose_pre_order_iter ( ) {
517
+ // Top-level "tr" node.
518
+ if item. index == 0 {
519
+ if item. is_complete {
520
+ debug_assert ! (
521
+ internal_key. is_some( ) ,
522
+ "checked above that top-level 'tr' has children"
523
+ ) ;
524
+ }
525
+ } else if item. index == 1 {
526
+ // First child of tr, which must be the internal key
527
+ if !item. node . args . is_empty ( ) {
528
+ return Err ( Error :: Parse ( ParseError :: Tree (
529
+ ParseTreeError :: IncorrectNumberOfChildren {
530
+ description : "internal key" ,
531
+ n_children : item. node . args . len ( ) ,
532
+ minimum : Some ( 0 ) ,
533
+ maximum : Some ( 0 ) ,
534
+ } ,
535
+ ) ) ) ;
536
+ }
537
+ internal_key = Some (
538
+ Pk :: from_str ( item. node . name )
539
+ . map_err ( ParseError :: box_from_str)
540
+ . map_err ( Error :: Parse ) ?,
541
+ ) ;
542
+ } else {
543
+ // From here on we are into the taptree.
544
+ if item. n_children_yielded == 0 {
545
+ match item. node . parens {
546
+ Parens :: Curly => {
547
+ if !item. node . name . is_empty ( ) {
548
+ return Err ( Error :: Parse ( ParseError :: Tree (
549
+ ParseTreeError :: IncorrectName {
550
+ actual : item. node . name . to_owned ( ) ,
551
+ expected : "" ,
552
+ } ,
553
+ ) ) ) ;
554
+ }
555
+ if round_paren_depth > 0 {
556
+ return Err ( Error :: Parse ( ParseError :: Tree (
557
+ ParseTreeError :: IllegalCurlyBrace {
558
+ pos : item. node . children_pos ,
559
+ } ,
560
+ ) ) ) ;
561
+ }
562
+ }
563
+ Parens :: Round => round_paren_depth += 1 ,
564
+ _ => { }
527
565
}
528
- Tr :: new ( expression:: terminal ( key, Pk :: from_str) ?, None )
529
566
}
530
- 2 => {
531
- let key = & top. args [ 0 ] ;
532
- if !key. args . is_empty ( ) {
533
- return Err ( Error :: Unexpected ( format ! (
534
- "#{} script associated with `key-path` while parsing taproot descriptor" ,
535
- key. args. len( )
536
- ) ) ) ;
567
+ if item. is_complete {
568
+ if item. node . parens == Parens :: Curly {
569
+ if item. n_children_yielded == 2 {
570
+ let rchild = tree_stack. pop ( ) . unwrap ( ) ;
571
+ let lchild = tree_stack. pop ( ) . unwrap ( ) ;
572
+ tree_stack. push ( TapTree :: combine ( lchild, rchild) ) ;
573
+ } else {
574
+ return Err ( Error :: Parse ( ParseError :: Tree (
575
+ ParseTreeError :: IncorrectNumberOfChildren {
576
+ description : "Taptree node" ,
577
+ n_children : item. n_children_yielded ,
578
+ minimum : Some ( 2 ) ,
579
+ maximum : Some ( 2 ) ,
580
+ } ,
581
+ ) ) ) ;
582
+ }
583
+ } else {
584
+ if item. node . parens == Parens :: Round {
585
+ round_paren_depth -= 1 ;
586
+ }
587
+ if round_paren_depth == 0 {
588
+ let script = Miniscript :: from_tree ( item. node ) ?;
589
+ // FIXME hack for https://github.com/rust-bitcoin/rust-miniscript/issues/734
590
+ if script. ty . corr . base != crate :: miniscript:: types:: Base :: B {
591
+ return Err ( Error :: NonTopLevel ( format ! ( "{:?}" , script) ) ) ;
592
+ } ;
593
+ tree_stack. push ( TapTree :: Leaf ( Arc :: new ( script) ) ) ;
594
+ }
537
595
}
538
- let tree = & top. args [ 1 ] ;
539
- let ret = Self :: parse_tr_script_spend ( tree) ?;
540
- Tr :: new ( expression:: terminal ( key, Pk :: from_str) ?, Some ( ret) )
541
596
}
542
- _ => Err ( Error :: Unexpected ( format ! (
543
- "{}[#{} args] while parsing taproot descriptor" ,
544
- top. name,
545
- top. args. len( )
546
- ) ) ) ,
547
597
}
548
- } else {
549
- Err ( Error :: Unexpected ( format ! (
550
- "{}[#{} args] while parsing taproot descriptor" ,
551
- top. name,
552
- top. args. len( )
553
- ) ) )
554
598
}
555
- }
556
- }
557
-
558
- impl < Pk : FromStrKey > core:: str:: FromStr for Tr < Pk > {
559
- type Err = Error ;
560
- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
561
- let desc_str = verify_checksum ( s)
562
- . map_err ( From :: from)
563
- . map_err ( Error :: ParseTree ) ?;
564
- let top = parse_tr_tree ( desc_str) ?;
565
- Self :: from_tree ( & top)
599
+ assert ! ( tree_stack. len( ) <= 1 ) ;
600
+ Tr :: new ( internal_key. unwrap ( ) , tree_stack. pop ( ) )
566
601
}
567
602
}
568
603
@@ -588,69 +623,6 @@ impl<Pk: MiniscriptKey> fmt::Display for Tr<Pk> {
588
623
}
589
624
}
590
625
591
- // Helper function to parse string into miniscript tree form
592
- fn parse_tr_tree ( s : & str ) -> Result < expression:: Tree , Error > {
593
- if s. len ( ) > 3 && & s[ ..3 ] == "tr(" && s. as_bytes ( ) [ s. len ( ) - 1 ] == b')' {
594
- let rest = & s[ 3 ..s. len ( ) - 1 ] ;
595
- if !rest. contains ( ',' ) {
596
- let key = expression:: Tree :: from_str ( rest) ?;
597
- if !key. args . is_empty ( ) {
598
- return Err ( Error :: Unexpected ( "invalid taproot internal key" . to_string ( ) ) ) ;
599
- }
600
- let internal_key = expression:: Tree {
601
- name : key. name ,
602
- parens : expression:: Parens :: None ,
603
- children_pos : 0 ,
604
- args : vec ! [ ] ,
605
- } ;
606
- return Ok ( expression:: Tree {
607
- name : "tr" ,
608
- parens : expression:: Parens :: Round ,
609
- children_pos : 0 ,
610
- args : vec ! [ internal_key] ,
611
- } ) ;
612
- }
613
- // use str::split_once() method to refactor this when compiler version bumps up
614
- let ( key, script) = split_once ( rest, ',' )
615
- . ok_or_else ( || Error :: BadDescriptor ( "invalid taproot descriptor" . to_string ( ) ) ) ?;
616
-
617
- let key = expression:: Tree :: from_str ( key) ?;
618
- if !key. args . is_empty ( ) {
619
- return Err ( Error :: Unexpected ( "invalid taproot internal key" . to_string ( ) ) ) ;
620
- }
621
- let internal_key = expression:: Tree {
622
- name : key. name ,
623
- parens : expression:: Parens :: None ,
624
- children_pos : 0 ,
625
- args : vec ! [ ] ,
626
- } ;
627
- let tree = expression:: Tree :: from_slice_delim ( script, expression:: Deliminator :: Taproot )
628
- . map_err ( Error :: ParseTree ) ?;
629
- Ok ( expression:: Tree {
630
- name : "tr" ,
631
- parens : expression:: Parens :: Round ,
632
- children_pos : 0 ,
633
- args : vec ! [ internal_key, tree] ,
634
- } )
635
- } else {
636
- Err ( Error :: Unexpected ( "invalid taproot descriptor" . to_string ( ) ) )
637
- }
638
- }
639
-
640
- fn split_once ( inp : & str , delim : char ) -> Option < ( & str , & str ) > {
641
- if inp. is_empty ( ) {
642
- None
643
- } else {
644
- // find the first character that matches delim
645
- let res = inp
646
- . chars ( )
647
- . position ( |ch| ch == delim)
648
- . map ( |idx| ( & inp[ ..idx] , & inp[ idx + 1 ..] ) )
649
- . unwrap_or ( ( inp, "" ) ) ;
650
- Some ( res)
651
- }
652
- }
653
-
654
626
impl < Pk : MiniscriptKey > Liftable < Pk > for TapTree < Pk > {
655
627
fn lift ( & self ) -> Result < Policy < Pk > , Error > {
656
628
fn lift_helper < Pk : MiniscriptKey > ( s : & TapTree < Pk > ) -> Result < Policy < Pk > , Error > {
@@ -767,6 +739,8 @@ where
767
739
768
740
#[ cfg( test) ]
769
741
mod tests {
742
+ use core:: str:: FromStr ;
743
+
770
744
use super :: * ;
771
745
772
746
fn descriptor ( ) -> String {
0 commit comments