@@ -345,3 +345,324 @@ impl<'sp, Pk: MiniscriptKey> TrSpendInfoIterItem<'sp, Pk> {
345
345
#[ inline]
346
346
pub fn control_block ( & self ) -> & ControlBlock { & self . control_block }
347
347
}
348
+
349
+ #[ cfg( test) ]
350
+ mod tests {
351
+ use super :: * ;
352
+
353
+ #[ derive( PartialEq , Eq , Debug ) ]
354
+ struct ExpectedTree {
355
+ internal_key : UntweakedPublicKey ,
356
+ output_key : TweakedPublicKey ,
357
+ output_key_parity : Parity ,
358
+ merkle_root : Option < TapNodeHash > ,
359
+ }
360
+
361
+ #[ derive( PartialEq , Eq , Debug ) ]
362
+ struct ExpectedLeaf {
363
+ leaf_hash : TapLeafHash ,
364
+ branch : TaprootMerkleBranch ,
365
+ }
366
+
367
+ fn test_cases ( ) -> Vec < ( String , ExpectedTree , Vec < ExpectedLeaf > ) > {
368
+ let secp = Secp256k1 :: verification_only ( ) ;
369
+ let pk = "03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115"
370
+ . parse :: < bitcoin:: PublicKey > ( )
371
+ . unwrap ( ) ;
372
+
373
+ // Hash of the FALSE script
374
+ let zero_hash = "e7e4d593fcb72926eedbe0d1e311f41acd6f6ef161dcba081a75168ec4dcd379"
375
+ . parse :: < TapLeafHash > ( )
376
+ . unwrap ( ) ;
377
+ // Hash of the TRUE script
378
+ let one_hash = "a85b2107f791b26a84e7586c28cec7cb61202ed3d01944d832500f363782d675"
379
+ . parse :: < TapLeafHash > ( )
380
+ . unwrap ( ) ;
381
+
382
+ let mut ret = vec ! [ ] ;
383
+
384
+ // Empty tree
385
+ let merkle_root = None ;
386
+ let internal_key = pk. to_x_only_pubkey ( ) ;
387
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
388
+ ret. push ( (
389
+ format ! ( "tr({pk})" ) ,
390
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
391
+ vec ! [ ] ,
392
+ ) ) ;
393
+
394
+ // Single-leaf tree
395
+ let merkle_root = Some ( TapNodeHash :: from ( zero_hash) ) ;
396
+ let internal_key = pk. to_x_only_pubkey ( ) ;
397
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
398
+ ret. push ( (
399
+ format ! ( "tr({pk},0)" ) ,
400
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
401
+ vec ! [ ExpectedLeaf {
402
+ leaf_hash: zero_hash,
403
+ branch: TaprootMerkleBranch :: try_from( vec![ ] ) . unwrap( ) ,
404
+ } ] ,
405
+ ) ) ;
406
+
407
+ // Two-leaf tree, repeated leaf
408
+ let merkle_root = Some (
409
+ "e3208df58f4fae78044357451c8830698300cd7da47cf41957d82ac4ce1dd170"
410
+ . parse ( )
411
+ . unwrap ( ) ,
412
+ ) ;
413
+ let internal_key = pk. to_x_only_pubkey ( ) ;
414
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
415
+ ret. push ( (
416
+ format ! ( "tr({pk},{{0,0}})" ) ,
417
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
418
+ vec ! [
419
+ ExpectedLeaf {
420
+ leaf_hash: zero_hash,
421
+ branch: TaprootMerkleBranch :: try_from( vec![ TapNodeHash :: from( zero_hash) ] )
422
+ . unwrap( ) ,
423
+ } ,
424
+ ExpectedLeaf {
425
+ leaf_hash: zero_hash,
426
+ branch: TaprootMerkleBranch :: try_from( vec![ TapNodeHash :: from( zero_hash) ] )
427
+ . unwrap( ) ,
428
+ } ,
429
+ ] ,
430
+ ) ) ;
431
+
432
+ // Two-leaf tree, non-repeated leaf
433
+ let merkle_root = Some (
434
+ "15526cd6108b4765640abe555e75f4bd11d9b1453b9db4cd36cf4189577a6f63"
435
+ . parse ( )
436
+ . unwrap ( ) ,
437
+ ) ;
438
+ let internal_key = pk. to_x_only_pubkey ( ) ;
439
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
440
+ ret. push ( (
441
+ format ! ( "tr({pk},{{0,1}})" ) ,
442
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
443
+ vec ! [
444
+ ExpectedLeaf {
445
+ leaf_hash: zero_hash,
446
+ branch: TaprootMerkleBranch :: try_from( vec![ TapNodeHash :: from( one_hash) ] )
447
+ . unwrap( ) ,
448
+ } ,
449
+ ExpectedLeaf {
450
+ leaf_hash: one_hash,
451
+ branch: TaprootMerkleBranch :: try_from( vec![ TapNodeHash :: from( zero_hash) ] )
452
+ . unwrap( ) ,
453
+ } ,
454
+ ] ,
455
+ ) ) ;
456
+
457
+ // Fuzz test vector 1
458
+ let merkle_root = Some (
459
+ "d281962c67932b82e19b0da5ea437af316213e24509be0ef1bd7c5ee2b460d79"
460
+ . parse ( )
461
+ . unwrap ( ) ,
462
+ ) ;
463
+ let internal_key = pk. to_x_only_pubkey ( ) ;
464
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
465
+
466
+ ret. push ( (
467
+ format ! ( "tr({pk},{{0,{{0,tv:0}}}})" ) ,
468
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
469
+ vec ! [
470
+ ExpectedLeaf {
471
+ leaf_hash: zero_hash,
472
+ branch: TaprootMerkleBranch :: try_from( vec![
473
+ "573d619569d58a36b52187e56f168650ac17f66a9a3afaf054900a04001019b3"
474
+ . parse:: <TapNodeHash >( )
475
+ . unwrap( ) ,
476
+ ] )
477
+ . unwrap( ) ,
478
+ } ,
479
+ ExpectedLeaf {
480
+ leaf_hash: zero_hash,
481
+ branch: TaprootMerkleBranch :: try_from( vec![
482
+ "64ac241466a5e7032586718ff7465716f77a88d89946ce472daa4c3d0b81148f"
483
+ . parse:: <TapNodeHash >( )
484
+ . unwrap( ) ,
485
+ TapNodeHash :: from( zero_hash) ,
486
+ ] )
487
+ . unwrap( ) ,
488
+ } ,
489
+ ExpectedLeaf {
490
+ leaf_hash: "64ac241466a5e7032586718ff7465716f77a88d89946ce472daa4c3d0b81148f"
491
+ . parse( )
492
+ . unwrap( ) ,
493
+ branch: TaprootMerkleBranch :: try_from( vec![
494
+ TapNodeHash :: from( zero_hash) ,
495
+ TapNodeHash :: from( zero_hash) ,
496
+ ] )
497
+ . unwrap( ) ,
498
+ } ,
499
+ ] ,
500
+ ) ) ;
501
+
502
+ // Fuzz test vector 2
503
+ let merkle_root = Some (
504
+ "2534e94c6ad06281b61fff86bad38a3911fb13436fb27fed6f5c057e4a71a911"
505
+ . parse ( )
506
+ . unwrap ( ) ,
507
+ ) ;
508
+ let internal_key = pk. to_x_only_pubkey ( ) ;
509
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
510
+
511
+ ret. push ( (
512
+ format ! ( "tr({pk},{{uuu:0,{{0,uu:0}}}})" ) ,
513
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
514
+ vec ! [
515
+ ExpectedLeaf {
516
+ leaf_hash: "6498e1d56640a272493d1d87549f3347dc448ca674556a2110cdfe100e3c238b"
517
+ . parse( )
518
+ . unwrap( ) ,
519
+ branch: TaprootMerkleBranch :: try_from( vec![
520
+ "7e3e98bab404812c8eebd21c5d825527676b8e9f261f7ad479f3a08a83a43fb4"
521
+ . parse:: <TapNodeHash >( )
522
+ . unwrap( ) ,
523
+ ] )
524
+ . unwrap( ) ,
525
+ } ,
526
+ ExpectedLeaf {
527
+ leaf_hash: zero_hash,
528
+ branch: TaprootMerkleBranch :: try_from( vec![
529
+ "19417c32bc6ca7e0f6e65b006ac305107c6add73c8bef31181037e6faaa55e7f"
530
+ . parse:: <TapNodeHash >( )
531
+ . unwrap( ) ,
532
+ "6498e1d56640a272493d1d87549f3347dc448ca674556a2110cdfe100e3c238b"
533
+ . parse:: <TapNodeHash >( )
534
+ . unwrap( ) ,
535
+ ] )
536
+ . unwrap( ) ,
537
+ } ,
538
+ ExpectedLeaf {
539
+ leaf_hash: "19417c32bc6ca7e0f6e65b006ac305107c6add73c8bef31181037e6faaa55e7f"
540
+ . parse( )
541
+ . unwrap( ) ,
542
+ branch: TaprootMerkleBranch :: try_from( vec![
543
+ TapNodeHash :: from( zero_hash) ,
544
+ "6498e1d56640a272493d1d87549f3347dc448ca674556a2110cdfe100e3c238b"
545
+ . parse:: <TapNodeHash >( )
546
+ . unwrap( ) ,
547
+ ] )
548
+ . unwrap( ) ,
549
+ } ,
550
+ ] ,
551
+ ) ) ;
552
+
553
+ // Fuzz test vector 3
554
+ let merkle_root = Some (
555
+ "9f4bc03c65a88ffbbb3a8d4fe5e01be608109d9f875f35685d8865e181def26e"
556
+ . parse ( )
557
+ . unwrap ( ) ,
558
+ ) ;
559
+ let internal_key = pk. to_x_only_pubkey ( ) ;
560
+ let ( output_key, output_key_parity) = internal_key. tap_tweak ( & secp, merkle_root) ;
561
+
562
+ ret. push ( (
563
+ format ! ( "tr({pk},{{{{0,{{uuu:0,0}}}},{{0,uu:0}}}})" ) ,
564
+ ExpectedTree { internal_key, output_key, output_key_parity, merkle_root } ,
565
+ vec ! [
566
+ ExpectedLeaf {
567
+ leaf_hash: zero_hash,
568
+ branch: TaprootMerkleBranch :: try_from( vec![
569
+ "57e3b7d414075ff4864deec9efa99db4462c038706306e02c58e02e957c8a51e"
570
+ . parse:: <TapNodeHash >( )
571
+ . unwrap( ) ,
572
+ "7e3e98bab404812c8eebd21c5d825527676b8e9f261f7ad479f3a08a83a43fb4"
573
+ . parse:: <TapNodeHash >( )
574
+ . unwrap( ) ,
575
+ ] )
576
+ . unwrap( ) ,
577
+ } ,
578
+ ExpectedLeaf {
579
+ leaf_hash: "6498e1d56640a272493d1d87549f3347dc448ca674556a2110cdfe100e3c238b"
580
+ . parse( )
581
+ . unwrap( ) ,
582
+ branch: TaprootMerkleBranch :: try_from( vec![
583
+ TapNodeHash :: from( zero_hash) ,
584
+ TapNodeHash :: from( zero_hash) ,
585
+ "7e3e98bab404812c8eebd21c5d825527676b8e9f261f7ad479f3a08a83a43fb4"
586
+ . parse:: <TapNodeHash >( )
587
+ . unwrap( ) ,
588
+ ] )
589
+ . unwrap( ) ,
590
+ } ,
591
+ ExpectedLeaf {
592
+ leaf_hash: zero_hash,
593
+ branch: TaprootMerkleBranch :: try_from( vec![
594
+ "6498e1d56640a272493d1d87549f3347dc448ca674556a2110cdfe100e3c238b"
595
+ . parse:: <TapNodeHash >( )
596
+ . unwrap( ) ,
597
+ TapNodeHash :: from( zero_hash) ,
598
+ "7e3e98bab404812c8eebd21c5d825527676b8e9f261f7ad479f3a08a83a43fb4"
599
+ . parse:: <TapNodeHash >( )
600
+ . unwrap( ) ,
601
+ ] )
602
+ . unwrap( ) ,
603
+ } ,
604
+ ExpectedLeaf {
605
+ leaf_hash: zero_hash,
606
+ branch: TaprootMerkleBranch :: try_from( vec![
607
+ "19417c32bc6ca7e0f6e65b006ac305107c6add73c8bef31181037e6faaa55e7f"
608
+ . parse:: <TapNodeHash >( )
609
+ . unwrap( ) ,
610
+ "e034d7d8b221034861bf3893c63cb0ff60d28a7a00090d0dc57c26fec91983cb"
611
+ . parse:: <TapNodeHash >( )
612
+ . unwrap( ) ,
613
+ ] )
614
+ . unwrap( ) ,
615
+ } ,
616
+ ExpectedLeaf {
617
+ leaf_hash: "19417c32bc6ca7e0f6e65b006ac305107c6add73c8bef31181037e6faaa55e7f"
618
+ . parse( )
619
+ . unwrap( ) ,
620
+ branch: TaprootMerkleBranch :: try_from( vec![
621
+ TapNodeHash :: from( zero_hash) ,
622
+ "e034d7d8b221034861bf3893c63cb0ff60d28a7a00090d0dc57c26fec91983cb"
623
+ . parse:: <TapNodeHash >( )
624
+ . unwrap( ) ,
625
+ ] )
626
+ . unwrap( ) ,
627
+ } ,
628
+ ] ,
629
+ ) ) ;
630
+
631
+ ret
632
+ }
633
+
634
+ #[ test]
635
+ fn spend_info_fixed_vectors ( ) {
636
+ for ( s, tree, leaves) in test_cases ( ) {
637
+ let tr = s
638
+ . parse :: < crate :: descriptor:: Tr < bitcoin:: PublicKey > > ( )
639
+ . unwrap ( ) ;
640
+ let spend_info = tr. spend_info ( ) ;
641
+
642
+ assert_eq ! (
643
+ spend_info. internal_key( ) ,
644
+ tree. internal_key,
645
+ "internal key mismatch (left: computed, right: expected)" ,
646
+ ) ;
647
+ assert_eq ! (
648
+ spend_info. merkle_root( ) ,
649
+ tree. merkle_root,
650
+ "merkle root mismatch (left: computed, right: expected)" ,
651
+ ) ;
652
+ assert_eq ! (
653
+ spend_info. output_key( ) ,
654
+ tree. output_key,
655
+ "output key mismatch (left: computed, right: expected)" ,
656
+ ) ;
657
+
658
+ let got_leaves: Vec < _ > = spend_info
659
+ . leaves ( )
660
+ . map ( |leaf| ExpectedLeaf {
661
+ leaf_hash : leaf. leaf_hash ( ) ,
662
+ branch : leaf. control_block ( ) . merkle_branch . clone ( ) ,
663
+ } )
664
+ . collect ( ) ;
665
+ assert_eq ! ( got_leaves, leaves, "leaves mismatch (left: computed, right: expected)" , ) ;
666
+ }
667
+ }
668
+ }
0 commit comments