@@ -197,6 +197,9 @@ pub enum Descriptor<T: miniscript::MiniscriptKey> {
197
197
#[ derive( Debug ) ]
198
198
pub struct ParsedPolicy < ' a > {
199
199
policy : & ' a Policy ,
200
+ // Cached flags for which keys in `policy.keys` are ours.
201
+ // is_our_key[i] is true if policy.keys[i] is our key.
202
+ is_our_key : Vec < bool > ,
200
203
// String for pubkeys so we can parse and process the placeholder wallet policy keys like
201
204
// `@0/**` etc.
202
205
pub descriptor : Descriptor < String > ,
@@ -256,17 +259,8 @@ impl<'a> ParsedPolicy<'a> {
256
259
257
260
self . validate_keys ( ) ?;
258
261
259
- let our_root_fingerprint = crate :: keystore:: root_fingerprint ( ) ?;
260
-
261
262
// Check that at least one key is ours.
262
- let has_our_key = ' block: {
263
- for key in policy. keys . iter ( ) {
264
- if is_our_key ( key, & our_root_fingerprint) ? {
265
- break ' block true ;
266
- }
267
- }
268
- false
269
- } ;
263
+ let has_our_key = self . is_our_key . iter ( ) . any ( |& b| b) ;
270
264
if !has_our_key {
271
265
return Err ( Error :: InvalidInput ) ;
272
266
}
@@ -290,6 +284,103 @@ impl<'a> ParsedPolicy<'a> {
290
284
Ok ( ( ) )
291
285
}
292
286
287
+ /// Confirm the policy. In advanced mode, all details are shown. In basic mode, the advanced
288
+ /// details are optional. Used to verify the policy during account registration (advanced mode),
289
+ /// creating a receive address (basic mode) and signing a transaction (basic mode).
290
+ pub async fn confirm (
291
+ & self ,
292
+ title : & str ,
293
+ params : & Params ,
294
+ name : & str ,
295
+ mode : Mode ,
296
+ ) -> Result < ( ) , Error > {
297
+ let policy = self . policy ;
298
+ confirm:: confirm ( & confirm:: Params {
299
+ title,
300
+ body : & format ! ( "{}\n policy with\n {} keys" , params. name, policy. keys. len( ) , ) ,
301
+ accept_is_nextarrow : true ,
302
+ ..Default :: default ( )
303
+ } )
304
+ . await ?;
305
+
306
+ confirm:: confirm ( & confirm:: Params {
307
+ title : "Name" ,
308
+ body : name,
309
+ scrollable : true ,
310
+ accept_is_nextarrow : true ,
311
+ ..Default :: default ( )
312
+ } )
313
+ . await ?;
314
+
315
+ if matches ! ( mode, Mode :: Basic ) {
316
+ if let Err ( confirm:: UserAbort ) = confirm:: confirm ( & confirm:: Params {
317
+ body : "Show policy\n details?" ,
318
+ accept_is_nextarrow : true ,
319
+ ..Default :: default ( )
320
+ } )
321
+ . await
322
+ {
323
+ return Ok ( ( ) ) ;
324
+ }
325
+ }
326
+
327
+ confirm:: confirm ( & confirm:: Params {
328
+ title : "Policy" ,
329
+ body : & policy. policy ,
330
+ scrollable : true ,
331
+ accept_is_nextarrow : true ,
332
+ ..Default :: default ( )
333
+ } )
334
+ . await ?;
335
+
336
+ let output_xpub_type = match params. coin {
337
+ BtcCoin :: Btc | BtcCoin :: Ltc => bip32:: XPubType :: Xpub ,
338
+ BtcCoin :: Tbtc | BtcCoin :: Tltc => bip32:: XPubType :: Tpub ,
339
+ } ;
340
+ let num_keys = policy. keys . len ( ) ;
341
+ for ( i, key) in policy. keys . iter ( ) . enumerate ( ) {
342
+ let key_str = match key {
343
+ pb:: KeyOriginInfo {
344
+ root_fingerprint,
345
+ keypath,
346
+ xpub : Some ( xpub) ,
347
+ } => {
348
+ let xpub_str = bip32:: Xpub :: from ( xpub)
349
+ . serialize_str ( output_xpub_type)
350
+ . or ( Err ( Error :: InvalidInput ) ) ?;
351
+ if root_fingerprint. is_empty ( ) {
352
+ xpub_str
353
+ } else if root_fingerprint. len ( ) != 4 {
354
+ return Err ( Error :: InvalidInput ) ;
355
+ } else {
356
+ format ! (
357
+ "[{}/{}]{}" ,
358
+ hex:: encode( root_fingerprint) ,
359
+ util:: bip32:: to_string_no_prefix( keypath) ,
360
+ xpub_str
361
+ )
362
+ }
363
+ }
364
+ _ => return Err ( Error :: InvalidInput ) ,
365
+ } ;
366
+ confirm:: confirm ( & confirm:: Params {
367
+ title : & format ! ( "Key {}/{}" , i + 1 , num_keys) ,
368
+ body : ( if self . is_our_key [ i] {
369
+ format ! ( "This device: {}" , key_str)
370
+ } else {
371
+ key_str
372
+ } )
373
+ . as_str ( ) ,
374
+ scrollable : true ,
375
+ longtouch : i == num_keys - 1 && matches ! ( mode, Mode :: Advanced ) ,
376
+ accept_is_nextarrow : true ,
377
+ ..Default :: default ( )
378
+ } )
379
+ . await ?;
380
+ }
381
+ Ok ( ( ) )
382
+ }
383
+
293
384
/// Derive the descriptor of the policy at a receive or change path.
294
385
/// This turns key placeholders into actual pubkeys.
295
386
/// If is_change is false, the descriptor for the receive address is derived.
@@ -368,6 +459,14 @@ pub fn parse(policy: &Policy, coin: BtcCoin) -> Result<ParsedPolicy, Error> {
368
459
}
369
460
370
461
let desc = policy. policy . as_str ( ) ;
462
+ let our_root_fingerprint = crate :: keystore:: root_fingerprint ( ) ?;
463
+
464
+ let is_our_key: Vec < bool > = policy
465
+ . keys
466
+ . iter ( )
467
+ . map ( |key| is_our_key ( key, & our_root_fingerprint) )
468
+ . collect :: < Result < Vec < bool > , ( ) > > ( ) ?;
469
+
371
470
let parsed = match desc. as_bytes ( ) {
372
471
// Match wsh(...).
373
472
[ b'w' , b's' , b'h' , b'(' , .., b')' ] => {
@@ -377,6 +476,7 @@ pub fn parse(policy: &Policy, coin: BtcCoin) -> Result<ParsedPolicy, Error> {
377
476
378
477
ParsedPolicy {
379
478
policy,
479
+ is_our_key,
380
480
descriptor : Descriptor :: Wsh ( Wsh { miniscript_expr } ) ,
381
481
}
382
482
}
@@ -394,104 +494,6 @@ pub enum Mode {
394
494
Advanced ,
395
495
}
396
496
397
- /// Confirm the policy. In advanced mode, all details are shown. In basic mode, the advanced details
398
- /// are optional. Used to verify the policy during account registration (advanced mode), creating a
399
- /// receive address (basic mode) and signing a transaction (basic mode).
400
- pub async fn confirm (
401
- title : & str ,
402
- params : & Params ,
403
- name : & str ,
404
- policy : & Policy ,
405
- mode : Mode ,
406
- ) -> Result < ( ) , Error > {
407
- confirm:: confirm ( & confirm:: Params {
408
- title,
409
- body : & format ! ( "{}\n policy with\n {} keys" , params. name, policy. keys. len( ) , ) ,
410
- accept_is_nextarrow : true ,
411
- ..Default :: default ( )
412
- } )
413
- . await ?;
414
-
415
- confirm:: confirm ( & confirm:: Params {
416
- title : "Name" ,
417
- body : name,
418
- scrollable : true ,
419
- accept_is_nextarrow : true ,
420
- ..Default :: default ( )
421
- } )
422
- . await ?;
423
-
424
- if matches ! ( mode, Mode :: Basic ) {
425
- if let Err ( confirm:: UserAbort ) = confirm:: confirm ( & confirm:: Params {
426
- body : "Show policy\n details?" ,
427
- accept_is_nextarrow : true ,
428
- ..Default :: default ( )
429
- } )
430
- . await
431
- {
432
- return Ok ( ( ) ) ;
433
- }
434
- }
435
-
436
- confirm:: confirm ( & confirm:: Params {
437
- title : "Policy" ,
438
- body : & policy. policy ,
439
- scrollable : true ,
440
- accept_is_nextarrow : true ,
441
- ..Default :: default ( )
442
- } )
443
- . await ?;
444
-
445
- let our_root_fingerprint = crate :: keystore:: root_fingerprint ( ) ?;
446
-
447
- let output_xpub_type = match params. coin {
448
- BtcCoin :: Btc | BtcCoin :: Ltc => bip32:: XPubType :: Xpub ,
449
- BtcCoin :: Tbtc | BtcCoin :: Tltc => bip32:: XPubType :: Tpub ,
450
- } ;
451
- let num_keys = policy. keys . len ( ) ;
452
- for ( i, key) in policy. keys . iter ( ) . enumerate ( ) {
453
- let key_str = match key {
454
- pb:: KeyOriginInfo {
455
- root_fingerprint,
456
- keypath,
457
- xpub : Some ( xpub) ,
458
- } => {
459
- let xpub_str = bip32:: Xpub :: from ( xpub)
460
- . serialize_str ( output_xpub_type)
461
- . or ( Err ( Error :: InvalidInput ) ) ?;
462
- if root_fingerprint. is_empty ( ) {
463
- xpub_str
464
- } else if root_fingerprint. len ( ) != 4 {
465
- return Err ( Error :: InvalidInput ) ;
466
- } else {
467
- format ! (
468
- "[{}/{}]{}" ,
469
- hex:: encode( root_fingerprint) ,
470
- util:: bip32:: to_string_no_prefix( keypath) ,
471
- xpub_str
472
- )
473
- }
474
- }
475
- _ => return Err ( Error :: InvalidInput ) ,
476
- } ;
477
- confirm:: confirm ( & confirm:: Params {
478
- title : & format ! ( "Key {}/{}" , i + 1 , num_keys) ,
479
- body : ( if is_our_key ( key, & our_root_fingerprint) ? {
480
- format ! ( "This device: {}" , key_str)
481
- } else {
482
- key_str
483
- } )
484
- . as_str ( ) ,
485
- scrollable : true ,
486
- longtouch : i == num_keys - 1 && matches ! ( mode, Mode :: Advanced ) ,
487
- accept_is_nextarrow : true ,
488
- ..Default :: default ( )
489
- } )
490
- . await ?;
491
- }
492
- Ok ( ( ) )
493
- }
494
-
495
497
/// Creates a hash of this policy config, useful for registration and identification.
496
498
pub fn get_hash ( coin : BtcCoin , policy : & Policy ) -> Result < Vec < u8 > , ( ) > {
497
499
let mut hasher = Sha256 :: new ( ) ;
0 commit comments