@@ -29,10 +29,11 @@ use miniscript::types::extra_props::TimeLockInfo;
29
29
use {
30
30
descriptor:: TapTree ,
31
31
miniscript:: ScriptContext ,
32
- policy:: compiler:: CompilerError ,
32
+ policy:: compiler:: { CompilerError , OrdF64 } ,
33
33
policy:: Concrete ,
34
34
policy:: { compiler, Liftable , Semantic } ,
35
- std:: collections:: HashMap ,
35
+ std:: cmp:: Reverse ,
36
+ std:: collections:: { BinaryHeap , HashMap } ,
36
37
std:: sync:: Arc ,
37
38
Descriptor , Miniscript , Tap ,
38
39
} ;
@@ -156,15 +157,23 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
156
157
}
157
158
}
158
159
160
+ /// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
159
161
#[ cfg( feature = "compiler" ) ]
160
- fn compile_leaf_taptree ( & self ) -> Result < TapTree < Pk > , Error > {
161
- let compilation = self . compile :: < Tap > ( ) . unwrap ( ) ;
162
- Ok ( TapTree :: Leaf ( Arc :: new ( compilation) ) )
162
+ fn compile_tr_policy ( & self ) -> Result < TapTree < Pk > , Error > {
163
+ let leaf_compilations: Vec < _ > = self
164
+ . to_tapleaf_prob_vec ( 1.0 )
165
+ . into_iter ( )
166
+ . filter ( |x| x. 1 != Policy :: Unsatisfiable )
167
+ . map ( |( prob, ref policy) | ( OrdF64 ( prob) , compiler:: best_compilation ( policy) . unwrap ( ) ) )
168
+ . collect ( ) ;
169
+ let taptree = with_huffman_tree :: < Pk > ( leaf_compilations) . unwrap ( ) ;
170
+ Ok ( taptree)
163
171
}
164
172
165
- /// Extract the Taproot internal_key from policy tree.
173
+ /// Extract the internal_key from policy tree.
166
174
#[ cfg( feature = "compiler" ) ]
167
175
fn extract_key ( self , unspendable_key : Option < Pk > ) -> Result < ( Pk , Policy < Pk > ) , Error > {
176
+ // Making sure the borrow ends before you move the value.
168
177
let mut internal_key: Option < Pk > = None ;
169
178
{
170
179
let mut prob = 0. ;
@@ -205,11 +214,28 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
205
214
}
206
215
}
207
216
208
- /// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
217
+ /// Compile the [`Policy`] into a [`Tr`][`Descriptor::Tr`] Descriptor.
218
+ ///
219
+ /// ### TapTree compilation
220
+ ///
221
+ /// The policy tree constructed by root-level disjunctions over [`Or`][`Policy::Or`] and
222
+ /// [`Thresh`][`Policy::Threshold`](1, ..) which is flattened into a vector (with respective
223
+ /// probabilities derived from odds) of policies.
224
+ /// For example, the policy `thresh(1,or(pk(A),pk(B)),and(or(pk(C),pk(D)),pk(E)))` gives the vector
225
+ /// `[pk(A),pk(B),and(or(pk(C),pk(D)),pk(E)))]`. Each policy in the vector is compiled into
226
+ /// the respective miniscripts. A Huffman Tree is created from this vector which optimizes over
227
+ /// the probabilitity of satisfaction for the respective branch in the TapTree.
228
+ // TODO: We might require other compile errors for Taproot.
209
229
#[ cfg( feature = "compiler" ) ]
210
230
pub fn compile_tr ( & self , unspendable_key : Option < Pk > ) -> Result < Descriptor < Pk > , Error > {
211
231
let ( internal_key, policy) = self . clone ( ) . extract_key ( unspendable_key) ?;
212
- let tree = Descriptor :: new_tr ( internal_key, Some ( policy. compile_leaf_taptree ( ) ?) ) ?;
232
+ let tree = Descriptor :: new_tr (
233
+ internal_key,
234
+ match policy {
235
+ Policy :: Trivial => None ,
236
+ policy => Some ( policy. compile_tr_policy ( ) ?) ,
237
+ } ,
238
+ ) ?;
213
239
Ok ( tree)
214
240
}
215
241
@@ -763,3 +789,34 @@ where
763
789
Policy :: from_tree_prob ( top, false ) . map ( |( _, result) | result)
764
790
}
765
791
}
792
+
793
+ /// Create a Huffman Tree from compiled [Miniscript] nodes
794
+ #[ cfg( feature = "compiler" ) ]
795
+ fn with_huffman_tree < Pk : MiniscriptKey > (
796
+ ms : Vec < ( OrdF64 , Miniscript < Pk , Tap > ) > ,
797
+ ) -> Result < TapTree < Pk > , Error > {
798
+ let mut node_weights = BinaryHeap :: < ( Reverse < OrdF64 > , TapTree < Pk > ) > :: new ( ) ;
799
+ for ( prob, script) in ms {
800
+ node_weights. push ( ( Reverse ( prob) , TapTree :: Leaf ( Arc :: new ( script) ) ) ) ;
801
+ }
802
+ if node_weights. is_empty ( ) {
803
+ return Err ( errstr ( "Empty Miniscript compilation" ) ) ;
804
+ }
805
+ while node_weights. len ( ) > 1 {
806
+ let ( p1, s1) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
807
+ let ( p2, s2) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
808
+
809
+ let p = ( p1. 0 ) . 0 + ( p2. 0 ) . 0 ;
810
+ node_weights. push ( (
811
+ Reverse ( OrdF64 ( p) ) ,
812
+ TapTree :: Tree ( Arc :: from ( s1) , Arc :: from ( s2) ) ,
813
+ ) ) ;
814
+ }
815
+
816
+ debug_assert ! ( node_weights. len( ) == 1 ) ;
817
+ let node = node_weights
818
+ . pop ( )
819
+ . expect ( "huffman tree algorithm is broken" )
820
+ . 1 ;
821
+ Ok ( node)
822
+ }
0 commit comments