@@ -33,12 +33,17 @@ use {
33
33
policy:: Concrete ,
34
34
policy:: { compiler, Liftable , Semantic } ,
35
35
std:: cmp:: Reverse ,
36
+ std:: collections:: BTreeMap ,
36
37
std:: collections:: { BinaryHeap , HashMap } ,
37
38
std:: sync:: Arc ,
38
39
Descriptor , Miniscript , Tap ,
39
40
} ;
40
41
use { Error , ForEach , ForEachKey , MiniscriptKey } ;
41
42
43
+ /// [`TapTree`] -> ([`Policy`], satisfaction cost for TapTree) cache
44
+ #[ cfg( feature = "compiler" ) ]
45
+ type PolicyTapCache < Pk > = BTreeMap < TapTree < Pk > , ( Policy < Pk > , f64 ) > ;
46
+
42
47
/// Concrete policy which corresponds directly to a Miniscript structure,
43
48
/// and whose disjunctions are annotated with satisfaction probabilities
44
49
/// to assist the compiler
@@ -169,9 +174,93 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
169
174
Ok ( node)
170
175
}
171
176
177
+ /// [`TapTree::Leaf`] average satisfaction cost + script size
178
+ #[ cfg( feature = "compiler" ) ]
179
+ fn tr_node_cost ( ms : & Arc < Miniscript < Pk , Tap > > , prob : f64 , cost : & f64 ) -> OrdF64 {
180
+ OrdF64 ( prob * ( ms. script_size ( ) as f64 + cost) )
181
+ }
182
+
183
+ /// Create a [`TapTree`] from the miniscript as leaf nodes
184
+ #[ cfg( feature = "compiler" ) ]
185
+ fn with_huffman_tree_eff (
186
+ ms : Vec < ( OrdF64 , ( Arc < Miniscript < Pk , Tap > > , f64 ) ) > ,
187
+ policy_cache : & mut PolicyTapCache < Pk > ,
188
+ ) -> Result < TapTree < Pk > , Error > {
189
+ let mut node_weights = BinaryHeap :: < ( Reverse < OrdF64 > , OrdF64 , TapTree < Pk > ) > :: new ( ) ; // (cost, branch_prob, tree)
190
+ for ( prob, script) in ms {
191
+ let wt = Self :: tr_node_cost ( & script. 0 , prob. 0 , & script. 1 ) ;
192
+ node_weights. push ( ( Reverse ( wt) , prob, TapTree :: Leaf ( Arc :: clone ( & script. 0 ) ) ) ) ;
193
+ }
194
+ if node_weights. is_empty ( ) {
195
+ return Err ( errstr ( "Empty Miniscript compilation" ) ) ;
196
+ }
197
+ while node_weights. len ( ) > 1 {
198
+ let ( prev_cost1, p1, s1) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
199
+ let ( prev_cost2, p2, s2) = node_weights. pop ( ) . expect ( "len must atleast be two" ) ;
200
+
201
+ match ( s1, s2) {
202
+ ( TapTree :: Leaf ( ms1) , TapTree :: Leaf ( ms2) ) => {
203
+ // Retrieve the respective policies
204
+ let ( left_pol, _c1) = policy_cache
205
+ . get ( & TapTree :: Leaf ( Arc :: clone ( & ms1) ) )
206
+ . ok_or_else ( || errstr ( "No corresponding policy found" ) ) ?
207
+ . clone ( ) ;
208
+
209
+ let ( right_pol, _c2) = policy_cache
210
+ . get ( & TapTree :: Leaf ( Arc :: clone ( & ms2) ) )
211
+ . ok_or_else ( || errstr ( "No corresponding policy found" ) ) ?
212
+ . clone ( ) ;
213
+
214
+ let parent_policy = Policy :: Or ( vec ! [
215
+ ( ( p1. 0 * 1e4 ) . round( ) as usize , left_pol) ,
216
+ ( ( p2. 0 * 1e4 ) . round( ) as usize , right_pol) ,
217
+ ] ) ;
218
+
219
+ let ( parent_compilation, cost) =
220
+ compiler:: best_compilation_sat :: < Pk , Tap > ( & parent_policy) ?;
221
+
222
+ let parent_cost = Self :: tr_node_cost ( & parent_compilation, p1. 0 + p2. 0 , & cost) ;
223
+ let children_cost =
224
+ OrdF64 ( ( prev_cost1. 0 ) . 0 + ( prev_cost2. 0 ) . 0 + 32. * ( p1. 0 + p2. 0 ) ) ;
225
+
226
+ policy_cache. remove ( & TapTree :: Leaf ( Arc :: clone ( & ms1) ) ) ;
227
+ policy_cache. remove ( & TapTree :: Leaf ( Arc :: clone ( & ms2) ) ) ;
228
+ let p = p1. 0 + p2. 0 ;
229
+ node_weights. push ( if parent_cost > children_cost {
230
+ (
231
+ Reverse ( children_cost) ,
232
+ OrdF64 ( p) ,
233
+ TapTree :: Tree (
234
+ Arc :: from ( TapTree :: Leaf ( ms1) ) ,
235
+ Arc :: from ( TapTree :: Leaf ( ms2) ) ,
236
+ ) ,
237
+ )
238
+ } else {
239
+ let node = TapTree :: Leaf ( Arc :: from ( parent_compilation) ) ;
240
+ policy_cache. insert ( node. clone ( ) , ( parent_policy, parent_cost. 0 ) ) ;
241
+ ( Reverse ( parent_cost) , OrdF64 ( p) , node)
242
+ } ) ;
243
+ }
244
+ ( ms1, ms2) => {
245
+ let p = p1. 0 + p2. 0 ;
246
+ let cost = OrdF64 ( ( prev_cost1. 0 ) . 0 + ( prev_cost2. 0 ) . 0 + 32.0 ) ;
247
+ node_weights. push ( (
248
+ Reverse ( cost) ,
249
+ OrdF64 ( p) ,
250
+ TapTree :: Tree ( Arc :: from ( ms1) , Arc :: from ( ms2) ) ,
251
+ ) ) ;
252
+ }
253
+ }
254
+ }
255
+ debug_assert ! ( node_weights. len( ) == 1 ) ;
256
+ let node = node_weights
257
+ . pop ( )
258
+ . expect ( "huffman tree algorithm is broken" )
259
+ . 2 ;
260
+ Ok ( node)
261
+ }
262
+
172
263
/// Flatten the [`Policy`] tree structure into a Vector with corresponding leaf probability
173
- // TODO: 1. Can try to push the maximum of scaling factors and accordingly update later for
174
- // TODO: 1. integer metric. (Accordingly change metrics everywhere)
175
264
#[ cfg( feature = "compiler" ) ]
176
265
fn to_tapleaf_prob_vec ( & self , prob : f64 ) -> Vec < ( f64 , Policy < Pk > ) > {
177
266
match * self {
@@ -197,7 +286,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
197
286
198
287
/// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
199
288
#[ cfg( feature = "compiler" ) ]
200
- fn compile_tr_policy ( & self ) -> Result < TapTree < Pk > , Error > {
289
+ fn compile_tr_private ( & self ) -> Result < TapTree < Pk > , Error > {
201
290
let leaf_compilations: Vec < _ > = self
202
291
. to_tapleaf_prob_vec ( 1.0 )
203
292
. into_iter ( )
@@ -208,6 +297,27 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
208
297
Ok ( taptree)
209
298
}
210
299
300
+ /// Compile [`Policy`] into a [`TapTree`]
301
+ #[ cfg( feature = "compiler" ) ]
302
+ fn compile_tr_efficient ( & self ) -> Result < TapTree < Pk > , Error > {
303
+ let mut policy_cache = PolicyTapCache :: < Pk > :: new ( ) ;
304
+ let leaf_compilations: Vec < _ > = self
305
+ . to_tapleaf_prob_vec ( 1.0 )
306
+ . into_iter ( )
307
+ . filter ( |x| x. 1 != Policy :: Unsatisfiable )
308
+ . map ( |( prob, ref policy) | {
309
+ let compilation = compiler:: best_compilation_sat :: < Pk , Tap > ( policy) . unwrap ( ) ;
310
+ policy_cache. insert (
311
+ TapTree :: Leaf ( Arc :: clone ( & compilation. 0 ) ) ,
312
+ ( policy. clone ( ) , compilation. 1 ) , // (policy, sat_cost)
313
+ ) ;
314
+ ( OrdF64 ( prob) , compilation) // (branch_prob, comp=(ms, sat_cost))
315
+ } )
316
+ . collect ( ) ;
317
+ let taptree = Self :: with_huffman_tree_eff ( leaf_compilations, & mut policy_cache) . unwrap ( ) ;
318
+ Ok ( taptree)
319
+ }
320
+
211
321
/// Extract the internal_key from policy tree.
212
322
#[ cfg( feature = "compiler" ) ]
213
323
fn extract_key ( self , unspendable_key : Option < Pk > ) -> Result < ( Pk , Policy < Pk > ) , Error > {
@@ -255,7 +365,11 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
255
365
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
256
366
// TODO: We might require other compile errors for Taproot. Will discuss and update.
257
367
#[ cfg( feature = "compiler" ) ]
258
- pub fn compile_tr ( & self , unspendable_key : Option < Pk > ) -> Result < Descriptor < Pk > , Error > {
368
+ pub fn compile_tr (
369
+ & self ,
370
+ unspendable_key : Option < Pk > ,
371
+ eff : bool ,
372
+ ) -> Result < Descriptor < Pk > , Error > {
259
373
self . is_valid ( ) ?; // Check for validity
260
374
match self . is_safe_nonmalleable ( ) {
261
375
( false , _) => Err ( Error :: from ( CompilerError :: TopLevelNonSafe ) ) ,
@@ -268,7 +382,13 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
268
382
internal_key,
269
383
match policy {
270
384
Policy :: Trivial => None ,
271
- policy => Some ( policy. compile_tr_policy ( ) ?) ,
385
+ policy => {
386
+ if eff {
387
+ Some ( policy. compile_tr_efficient ( ) ?)
388
+ } else {
389
+ Some ( policy. compile_tr_private ( ) ?)
390
+ }
391
+ }
272
392
} ,
273
393
) ?;
274
394
Ok ( tree)
0 commit comments