@@ -6,10 +6,13 @@ use warnings;
66use Mooish::Base -standard;
77use Types::Common -sigs;
88use List::Util qw( first) ;
9+ use Scalar::Util qw( blessed) ;
10+ use Carp qw( carp) ;
911
1012use Bitcoin::Crypto::Types -types;
1113use Bitcoin::Crypto::Exception;
1214use Bitcoin::Crypto::Util::Internal qw( tagged_hash pack_compactsize has_even_y) ;
15+ use Bitcoin::Crypto::Script::Tree::Leaf;
1316use Bitcoin::Crypto::Transaction::ControlBlock;
1417
1518# recursive structure - a binary tree
@@ -26,7 +29,7 @@ has field '_tree_cache' => (
2629# script trees
2730sub _traverse
2831{
29- my ($self , $join_action , $leaf_action ) = @_ ;
32+ my ($self , $paths_ref , $leaves_ref ) = @_ ;
3033
3134 my @stack = ({nodes => [@{$self -> tree}], results => []});
3235 my $result ;
@@ -40,30 +43,14 @@ sub _traverse
4043 push @stack , {nodes => [@$item ], results => []};
4144 }
4245 else {
43- state $precomputed_type = Dict [hash => ByteStr];
44- state $leaf_type = Dict [
45- leaf_version => IntMaxBits [8],
46- script => BitcoinScript,
47- id => Optional [ByteStr],
48- hash => Optional [ByteStr],
49- ];
5046
5147 # this value is a leaf which may need calculating
52- my $value = $precomputed_type -> coerce($item );
53- if (!$precomputed_type -> check($value )) {
54- $value = $leaf_type -> assert_coerce($item );
55- if (!defined $value -> {hash }) {
56- my $script = $value -> {script }-> to_serialized;
57- my $script_len = pack_compactsize(length $script );
58-
59- $value -> {hash } =
60- tagged_hash(' TapLeaf' , join ' ' , pack (' C' , $value -> {leaf_version }), $script_len , $script );
61- }
62-
63- $value -> {id } //= $value -> {hash };
64- }
48+ my $value = Bitcoin::Crypto::Script::Tree::Leaf-> new(
49+ %{$item },
50+ depth => scalar @stack ,
51+ );
6552
66- $leaf_action -> ( $value , scalar @stack ) if defined $leaf_action ;
53+ push @{ $leaves_ref }, $value ;
6754 push @{$stack [-1]{results }}, $value ;
6855 }
6956 }
@@ -75,9 +62,21 @@ sub _traverse
7562 @results = reverse @results
7663 if $results [0]{hash } gt $results [1]{hash };
7764
78- my %data = defined $join_action ? $join_action -> (@results , scalar @stack ) : ();
65+ my @all_ids ;
66+ foreach my $key (keys @results ) {
67+ my ($this_one , $other_one ) = @results [$key , ($key + 1) % 2];
68+
69+ my $is_leaf = blessed $this_one ;
70+ my @ids = $is_leaf ? ($this_one -> id) : @{$this_one -> {ids }};
71+ push @all_ids , @ids ;
72+
73+ foreach my $id (@ids ) {
74+ push @{$paths_ref -> {$id }}, $other_one -> {hash };
75+ }
76+ }
77+
7978 $result = {
80- %data ,
79+ ids => \ @all_ids ,
8180 hash => tagged_hash(' TapBranch' , join ' ' , map { $_ -> {hash } } @results ),
8281 };
8382 }
@@ -98,52 +97,17 @@ sub _traverse
9897 return $result ;
9998}
10099
101- sub _tree_paths_action
102- {
103- my ($self ) = @_ ;
104-
105- my %paths ;
106- my $action = sub {
107- my ($node1 , $node2 ) = @_ ;
108- my @all_ids ;
109-
110- foreach my $info ([$node1 , $node2 ], [$node2 , $node1 ]) {
111- my ($this_one , $other_one ) = @{$info };
112- next unless defined $this_one -> {id };
113- my @ids = ref $this_one -> {id } ? @{$this_one -> {id }} : $this_one -> {id };
114- push @all_ids , @ids ;
115-
116- foreach my $id (@ids ) {
117- push @{$paths {$id }}, $other_one -> {hash };
118- }
119- }
120-
121- return (
122- id => \@all_ids ,
123- );
124- };
125-
126- return (\%paths , $action );
127- }
128-
129100sub _build_tree_cache
130101{
131102 my ($self ) = @_ ;
132103
133104 my @leaves ;
134- my ($paths , $paths_action ) = $self -> _tree_paths_action;
135-
136- my $root = $self -> _traverse(
137- $paths_action ,
138- sub {
139- my $leaf = shift ;
140- push @leaves , $leaf ;
141- }
142- );
105+ my %paths ;
106+ my $root = $self -> _traverse(\%paths , \@leaves );
143107
144108 return {
145109 leaves => \@leaves ,
146- paths => $ paths ,
110+ paths => \ % paths ,
147111 root => $root ,
148112 };
149113}
@@ -155,12 +119,12 @@ sub get_merkle_root
155119 return $self -> _tree_cache-> {root }{hash };
156120}
157121
158- signature_for _get_tapleaf => (
122+ signature_for get_leaf => (
159123 method => !!1,
160124 positional => [ByteStr],
161125);
162126
163- sub _get_tapleaf
127+ sub get_leaf
164128{
165129 my ($self , $leaf_id ) = @_ ;
166130
@@ -176,21 +140,27 @@ sub get_tapleaf_script
176140{
177141 my ($self , $leaf_id ) = @_ ;
178142
179- return $self -> _get_tapleaf($leaf_id )-> {script };
143+ carp ' get_tapleaf_script is deprecated - use get_leaf(...)->script instead' ;
144+
145+ return $self -> get_leaf($leaf_id )-> script;
180146}
181147
182148sub get_tapleaf_hash
183149{
184150 my ($self , $leaf_id ) = @_ ;
185151
186- return $self -> _get_tapleaf($leaf_id )-> {hash };
152+ carp ' get_tapleaf_hash is deprecated - use get_leaf(...)->hash instead' ;
153+
154+ return $self -> get_leaf($leaf_id )-> hash;
187155}
188156
189157sub get_tapleaf_version
190158{
191159 my ($self , $leaf_id ) = @_ ;
192160
193- return $self -> _get_tapleaf($leaf_id )-> {leaf_version };
161+ carp ' get_tapleaf_version is deprecated - use get_leaf(...)->leaf_version instead' ;
162+
163+ return $self -> get_leaf($leaf_id )-> leaf_version;
194164}
195165
196166sub get_tree_paths
@@ -200,6 +170,13 @@ sub get_tree_paths
200170 return $self -> _tree_cache-> {paths };
201171}
202172
173+ sub get_leaves
174+ {
175+ my ($self ) = @_ ;
176+
177+ return $self -> _tree_cache-> {leaves };
178+ }
179+
203180signature_for from_path => (
204181 method => !!1,
205182 positional => [HashRef, ArrayRef [ByteStr]],
@@ -233,7 +210,7 @@ sub get_control_block
233210 my ($self , $leaf_id , $pubkey ) = @_ ;
234211 my $cache = $self -> _tree_cache;
235212
236- my $leaf_version = $self -> get_tapleaf_version ($leaf_id );
213+ my $leaf_version = $self -> get_leaf ($leaf_id )-> leaf_version ;
237214 my $tapkey = $pubkey -> get_taproot_output_key($cache -> {root }{hash });
238215 my $parity = has_even_y($tapkey );
239216
@@ -285,7 +262,8 @@ trees are used by taproot and are necessary to build custom taproot scripts.
285262
286263=head2 Tree leaves
287264
288- Each leaf in the tree is represented with this Perl structure:
265+ Each leaf in the tree is represented with this Perl structure, which is turned
266+ into an instance of L<Bitcoin::Crypto::Script::Tree::Leaf> :
289267
290268 {
291269 id => bytestring (optional),
@@ -367,6 +345,10 @@ disclosing information about a script:
367345 ]
368346 ]
369347
348+ The structure in C<tree > is considered immutable and will not be changed. All
349+ data coercions will be done in separate structures, which can be cleared using
350+ L</clear_tree_cache> to be updated if the source C<tree > has changed.
351+
370352=head2 Methods
371353
372354=head3 new
@@ -398,24 +380,37 @@ could look like this:
398380Calculates a merkle root of the script tree. Returns a bytestring which is the
399381root hash of the tree.
400382
383+ =head3 get_leaf
384+
385+ $leaf = $tree->get_leaf($id_or_hash)
386+
387+ Returns a tree leaf - instance of L<Bitcoin::Crypto::Script::Tree::Leaf> . If
388+ the leaf does not exist, an exception is raised.
389+
401390=head3 get_tapleaf_script
402391
403392 $script = $tree->get_tapleaf_script($leaf_id)
404393
394+ Deprecated - use C<< $tree->get_leaf($leaf_id)->script >> instead.
395+
405396Returns a tapleaf script of a leaf with given C<$leaf_id > . If such leaf does
406397not exist, an exception is thrown. Returns a script instance.
407398
408399=head3 get_tapleaf_version
409400
410401 $int = $tree->get_tapleaf_version($leaf_id)
411402
403+ Deprecated - use C<< $tree->get_leaf($leaf_id)->leaf_version >> instead.
404+
412405Returns a tapleaf version of a leaf with given C<$leaf_id > . If such leaf does
413406not exist, an exception is thrown. Returns an integer.
414407
415408=head3 get_tapleaf_hash
416409
417410 $hash = $tree->get_tapleaf_hash($leaf_id)
418411
412+ Deprecated - use C<< $tree->get_leaf($leaf_id)->hash >> instead.
413+
419414Calculates a tapleaf hash of a leaf with given C<$leaf_id > . If such leaf does
420415not exist, an exception is thrown. Returns a bytestring.
421416
@@ -435,6 +430,14 @@ of L<Bitcoin::Crypto::Transaction::ControlBlock>.
435430Returns a hash reference of paths for each of leaves in the tree which have an
436431id. Each path is an array reference - same as what L</from_path> takes as input.
437432
433+ =head3 get_leaves
434+
435+ $leaves = $tree->get_leaves()
436+
437+ Returns an array reference of leaves in the hash - each leaf is an instance of
438+ L<Bitcoin::Crypto::Script::Tree::Leaf> . The order of leaves in the array is
439+ strictly determined by the order of leaves in the tree.
440+
438441=head3 clear_tree_cache
439442
440443 $tree->clear_tree_cache()
0 commit comments