@@ -345,3 +345,324 @@ impl<'sp, Pk: MiniscriptKey> TrSpendInfoIterItem<'sp, Pk> {
345345 #[ inline]
346346 pub fn control_block ( & self ) -> & ControlBlock { & self . control_block }
347347}
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