@@ -55,7 +55,7 @@ mod key;
55
55
pub use self :: key:: {
56
56
DefiniteDescriptorKey , DerivPaths , DescriptorKeyParseError , DescriptorMultiXKey ,
57
57
DescriptorPublicKey , DescriptorSecretKey , DescriptorXKey , InnerXKey , MalformedKeyDataKind ,
58
- NonDefiniteKeyError , SinglePriv , SinglePub , SinglePubKey , Wildcard ,
58
+ NonDefiniteKeyError , SinglePriv , SinglePub , SinglePubKey , Wildcard , XKeyNetwork ,
59
59
} ;
60
60
61
61
/// Alias type for a map of public key to secret key
@@ -927,6 +927,33 @@ impl Descriptor<DescriptorPublicKey> {
927
927
928
928
Ok ( descriptors)
929
929
}
930
+
931
+ /// Check the network consistency of all extended keys in this descriptor.
932
+ ///
933
+ /// Returns `XKeyNetwork::NoXKeys` if no extended keys are present,
934
+ /// `XKeyNetwork::Mixed` if extended keys have conflicting network prefixes,
935
+ /// or `XKeyNetwork::Single(network)` if all extended keys have the same network.
936
+ ///
937
+ /// This can be used to prevent accidentally using testnet keys on mainnet
938
+ /// and vice versa, which could lead to funds being sent to unspendable addresses.
939
+ pub fn xkey_network ( & self ) -> XKeyNetwork {
940
+ let mut first_network = None ;
941
+
942
+ for key in self . iter_pk ( ) {
943
+ if let Some ( network) = key. xkey_network ( ) {
944
+ match first_network {
945
+ None => first_network = Some ( network) ,
946
+ Some ( ref n) if * n != network => return XKeyNetwork :: Mixed ,
947
+ _ => continue ,
948
+ }
949
+ }
950
+ }
951
+
952
+ match first_network {
953
+ Some ( network) => XKeyNetwork :: Single ( network) ,
954
+ None => XKeyNetwork :: NoXKeys ,
955
+ }
956
+ }
930
957
}
931
958
932
959
impl Descriptor < DefiniteDescriptorKey > {
@@ -977,6 +1004,33 @@ impl Descriptor<DefiniteDescriptorKey> {
977
1004
Err ( e) => panic ! ( "Context errors when deriving keys: {}" , e. into_outer_err( ) ) ,
978
1005
}
979
1006
}
1007
+
1008
+ /// Check the network consistency of all extended keys in this descriptor.
1009
+ ///
1010
+ /// Returns `XKeyNetwork::NoXKeys` if no extended keys are present,
1011
+ /// `XKeyNetwork::Mixed` if extended keys have conflicting network prefixes,
1012
+ /// or `XKeyNetwork::Single(network)` if all extended keys have the same network.
1013
+ ///
1014
+ /// This can be used to prevent accidentally using testnet keys on mainnet
1015
+ /// and vice versa, which could lead to funds being sent to unspendable addresses.
1016
+ pub fn xkey_network ( & self ) -> XKeyNetwork {
1017
+ let mut first_network = None ;
1018
+
1019
+ for key in self . iter_pk ( ) {
1020
+ if let Some ( network) = key. as_descriptor_public_key ( ) . xkey_network ( ) {
1021
+ match first_network {
1022
+ None => first_network = Some ( network) ,
1023
+ Some ( ref n) if * n != network => return XKeyNetwork :: Mixed ,
1024
+ _ => continue ,
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ match first_network {
1030
+ Some ( network) => XKeyNetwork :: Single ( network) ,
1031
+ None => XKeyNetwork :: NoXKeys ,
1032
+ }
1033
+ }
980
1034
}
981
1035
982
1036
impl < Pk : FromStrKey > crate :: expression:: FromTree for Descriptor < Pk > {
@@ -2421,4 +2475,229 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
2421
2475
assert ! ( keys[ 1 ..] . iter( ) . any( |k| k. to_string( )
2422
2476
== "0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352" ) ) ;
2423
2477
}
2478
+
2479
+ // Helper function to provide unique test keys for multi-key scenarios
2480
+ fn test_keys ( ) -> ( Vec < & ' static str > , Vec < & ' static str > , Vec < & ' static str > ) {
2481
+ // Unique mainnet xpubs
2482
+ let mainnet_xpubs = vec ! [
2483
+ "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" ,
2484
+ "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ" ,
2485
+ "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" ,
2486
+ "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH" ,
2487
+ ] ;
2488
+
2489
+ // Unique testnet tpubs
2490
+ let testnet_tpubs = vec ! [
2491
+ "tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi" ,
2492
+ "tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr" ,
2493
+ "tpubD6NzVbkrYhZ4YqYr3amYH15zjxHvBkUUeadieW8AxTZC7aY2L8aPSk3tpW6yW1QnWzXAB7zoiaNMfwXPPz9S68ZCV4yWvkVXjdeksLskCed" ,
2494
+ "tpubDCvNhURocXGZsLNqWcqD3syHTqPXrMSTwi8feKVwAcpi29oYKsDD3Vex7x2TDneKMVN23RbLprfxB69v94iYqdaYHsVz3kPR37NQXeqouVz" ,
2495
+ ] ;
2496
+
2497
+ // Unique single public keys
2498
+ let single_keys = vec ! [
2499
+ "021d4ea7132d4e1a362ee5efd8d0b59dd4d1fe8906eefa7dd812b05a46b73d829b" ,
2500
+ "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357" ,
2501
+ "022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01" ,
2502
+ "023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb" ,
2503
+ ] ;
2504
+
2505
+ ( mainnet_xpubs, testnet_tpubs, single_keys)
2506
+ }
2507
+
2508
+ #[ test]
2509
+ fn test_descriptor_pubkey_xkey_network ( ) {
2510
+ use core:: str:: FromStr ;
2511
+
2512
+ use bitcoin:: NetworkKind ;
2513
+
2514
+ use crate :: descriptor:: { DescriptorPublicKey , XKeyNetwork } ;
2515
+
2516
+ let ( mainnet_xpubs, testnet_tpubs, single_keys) = test_keys ( ) ;
2517
+
2518
+ // Basic single key scenarios
2519
+ let basic_tests = vec ! [
2520
+ // Single mainnet xpub
2521
+ ( format!( "wpkh({})" , mainnet_xpubs[ 0 ] ) , XKeyNetwork :: Single ( NetworkKind :: Main ) ) ,
2522
+ // Single testnet tpub
2523
+ ( format!( "wpkh({})" , testnet_tpubs[ 0 ] ) , XKeyNetwork :: Single ( NetworkKind :: Test ) ) ,
2524
+ // Single public key (no extended keys)
2525
+ ( format!( "wpkh({})" , single_keys[ 0 ] ) , XKeyNetwork :: NoXKeys ) ,
2526
+ ] ;
2527
+
2528
+ for ( desc_str, expected) in basic_tests {
2529
+ let desc = Descriptor :: < DescriptorPublicKey > :: from_str ( & desc_str) . unwrap ( ) ;
2530
+ assert_eq ! ( desc. xkey_network( ) , expected, "Failed for basic descriptor: {}" , desc_str) ;
2531
+ }
2532
+
2533
+ // Multi-key descriptor combinations with unique keys
2534
+ let multi_key_tests = vec ! [
2535
+ // Mixed networks: mainnet + testnet
2536
+ (
2537
+ format!( "wsh(multi(2,{},{}))" , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] ) ,
2538
+ XKeyNetwork :: Mixed ,
2539
+ ) ,
2540
+ // Consistent mainnet keys
2541
+ (
2542
+ format!( "wsh(multi(2,{},{}))" , mainnet_xpubs[ 0 ] , mainnet_xpubs[ 1 ] ) ,
2543
+ XKeyNetwork :: Single ( NetworkKind :: Main ) ,
2544
+ ) ,
2545
+ // Consistent testnet multisig
2546
+ (
2547
+ format!(
2548
+ "wsh(multi(2,{},{},{}))" ,
2549
+ testnet_tpubs[ 0 ] , testnet_tpubs[ 1 ] , testnet_tpubs[ 2 ]
2550
+ ) ,
2551
+ XKeyNetwork :: Single ( NetworkKind :: Test ) ,
2552
+ ) ,
2553
+ // Sorted multisig with mixed key types
2554
+ (
2555
+ format!(
2556
+ "wsh(sortedmulti(2,{},{},{}))" ,
2557
+ mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] , single_keys[ 0 ]
2558
+ ) ,
2559
+ XKeyNetwork :: Mixed ,
2560
+ ) ,
2561
+ // 3-of-4 multisig with all mainnet keys
2562
+ (
2563
+ format!(
2564
+ "wsh(multi(3,{},{},{},{}))" ,
2565
+ mainnet_xpubs[ 0 ] , mainnet_xpubs[ 1 ] , mainnet_xpubs[ 2 ] , mainnet_xpubs[ 3 ]
2566
+ ) ,
2567
+ XKeyNetwork :: Single ( NetworkKind :: Main ) ,
2568
+ ) ,
2569
+ ] ;
2570
+
2571
+ for ( desc_str, expected) in multi_key_tests {
2572
+ let desc = Descriptor :: < DescriptorPublicKey > :: from_str ( & desc_str) . unwrap ( ) ;
2573
+ assert_eq ! (
2574
+ desc. xkey_network( ) ,
2575
+ expected,
2576
+ "Failed for multi-key descriptor: {}" ,
2577
+ desc_str
2578
+ ) ;
2579
+ }
2580
+
2581
+ // Threshold and logical operator tests
2582
+ let threshold_tests = vec ! [
2583
+ // Threshold with mixed key types
2584
+ (
2585
+ format!(
2586
+ "wsh(thresh(2,c:pk_k({}),sc:pk_k({}),sc:pk_k({})))" ,
2587
+ single_keys[ 0 ] , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ]
2588
+ ) ,
2589
+ XKeyNetwork :: Mixed ,
2590
+ ) ,
2591
+ // OR with mixed networks
2592
+ (
2593
+ format!( "wsh(or_d(pk({}),pk({})))" , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] ) ,
2594
+ XKeyNetwork :: Mixed ,
2595
+ ) ,
2596
+ // AND with consistent mainnet keys
2597
+ (
2598
+ format!( "wsh(and_v(v:pk({}),pk({})))" , mainnet_xpubs[ 0 ] , mainnet_xpubs[ 1 ] ) ,
2599
+ XKeyNetwork :: Single ( NetworkKind :: Main ) ,
2600
+ ) ,
2601
+ // Complex threshold with all testnet keys
2602
+ (
2603
+ format!(
2604
+ "wsh(thresh(3,c:pk_k({}),sc:pk_k({}),sc:pk_k({}),sc:pk_k({})))" ,
2605
+ testnet_tpubs[ 0 ] , testnet_tpubs[ 1 ] , testnet_tpubs[ 2 ] , testnet_tpubs[ 3 ]
2606
+ ) ,
2607
+ XKeyNetwork :: Single ( NetworkKind :: Test ) ,
2608
+ ) ,
2609
+ ] ;
2610
+
2611
+ for ( desc_str, expected) in threshold_tests {
2612
+ let desc = Descriptor :: < DescriptorPublicKey > :: from_str ( & desc_str) . unwrap ( ) ;
2613
+ assert_eq ! (
2614
+ desc. xkey_network( ) ,
2615
+ expected,
2616
+ "Failed for threshold descriptor: {}" ,
2617
+ desc_str
2618
+ ) ;
2619
+ }
2620
+
2621
+ // Taproot and complex miniscript tests
2622
+ let complex_tests = vec ! [
2623
+ // Taproot with mixed networks
2624
+ ( format!( "tr({},pk({}))" , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] ) , XKeyNetwork :: Mixed ) ,
2625
+ // Taproot with consistent mainnet keys
2626
+ ( format!( "tr({},pk({}))" , mainnet_xpubs[ 0 ] , mainnet_xpubs[ 1 ] ) , XKeyNetwork :: Single ( NetworkKind :: Main ) ) ,
2627
+ // HTLC-like pattern with mixed networks
2628
+ ( format!( "wsh(andor(pk({}),sha256(1111111111111111111111111111111111111111111111111111111111111111),and_v(v:pkh({}),older(144))))" , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] ) , XKeyNetwork :: Mixed ) ,
2629
+ // Multi-path spending with testnet keys
2630
+ ( format!( "wsh(or_d(multi(2,{},{}),and_v(v:pk({}),older(1000))))" , testnet_tpubs[ 0 ] , testnet_tpubs[ 1 ] , testnet_tpubs[ 2 ] ) , XKeyNetwork :: Single ( NetworkKind :: Test ) ) ,
2631
+ // Nested conditions with only single keys
2632
+ ( format!( "wsh(thresh(3,c:pk_k({}),sc:pk_k({}),sc:pk_k({}),sc:pk_k({})))" , single_keys[ 0 ] , single_keys[ 1 ] , single_keys[ 2 ] , single_keys[ 3 ] ) , XKeyNetwork :: NoXKeys ) ,
2633
+ // Complex pattern with mainnet keys
2634
+ ( format!( "wsh(or_d(multi(2,{},{}),and_v(v:pk({}),older(1000))))" , mainnet_xpubs[ 0 ] , mainnet_xpubs[ 1 ] , mainnet_xpubs[ 2 ] ) , XKeyNetwork :: Single ( NetworkKind :: Main ) ) ,
2635
+ ] ;
2636
+
2637
+ for ( desc_str, expected) in complex_tests {
2638
+ let desc = Descriptor :: < DescriptorPublicKey > :: from_str ( & desc_str) . unwrap ( ) ;
2639
+ assert_eq ! (
2640
+ desc. xkey_network( ) ,
2641
+ expected,
2642
+ "Failed for complex descriptor: {}" ,
2643
+ desc_str
2644
+ ) ;
2645
+ }
2646
+ }
2647
+
2648
+ #[ test]
2649
+ fn test_definite_descriptor_key_xkey_network ( ) {
2650
+ use core:: str:: FromStr ;
2651
+
2652
+ use bitcoin:: NetworkKind ;
2653
+
2654
+ use crate :: descriptor:: { DefiniteDescriptorKey , XKeyNetwork } ;
2655
+
2656
+ let ( mainnet_xpubs, testnet_tpubs, single_keys) = test_keys ( ) ;
2657
+
2658
+ // DefiniteDescriptorKey tests (no wildcards, specific derivation paths)
2659
+ let definite_key_tests = vec ! [
2660
+ // Basic single key scenarios
2661
+ ( format!( "wpkh({})" , mainnet_xpubs[ 0 ] ) , XKeyNetwork :: Single ( NetworkKind :: Main ) ) ,
2662
+ ( format!( "wpkh({})" , testnet_tpubs[ 0 ] ) , XKeyNetwork :: Single ( NetworkKind :: Test ) ) ,
2663
+ ( format!( "wpkh({})" , single_keys[ 0 ] ) , XKeyNetwork :: NoXKeys ) ,
2664
+ // Multi-key scenarios with specific derivation paths
2665
+ (
2666
+ format!( "wsh(multi(2,{},{}))" , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] ) ,
2667
+ XKeyNetwork :: Mixed ,
2668
+ ) ,
2669
+ (
2670
+ format!( "wsh(multi(2,{}/0,{}/1))" , testnet_tpubs[ 0 ] , testnet_tpubs[ 1 ] ) ,
2671
+ XKeyNetwork :: Single ( NetworkKind :: Test ) ,
2672
+ ) ,
2673
+ (
2674
+ format!( "wsh(multi(2,{}/0,{}/1))" , mainnet_xpubs[ 0 ] , mainnet_xpubs[ 1 ] ) ,
2675
+ XKeyNetwork :: Single ( NetworkKind :: Main ) ,
2676
+ ) ,
2677
+ // Sorted multisig with specific paths
2678
+ (
2679
+ format!(
2680
+ "wsh(sortedmulti(2,{}/0,{}/1,{}))" ,
2681
+ mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] , single_keys[ 0 ]
2682
+ ) ,
2683
+ XKeyNetwork :: Mixed ,
2684
+ ) ,
2685
+ // Taproot scenarios
2686
+ ( format!( "tr({})" , mainnet_xpubs[ 0 ] ) , XKeyNetwork :: Single ( NetworkKind :: Main ) ) ,
2687
+ (
2688
+ format!( "tr({},pk({}/0))" , mainnet_xpubs[ 0 ] , testnet_tpubs[ 0 ] ) ,
2689
+ XKeyNetwork :: Mixed ,
2690
+ ) ,
2691
+ ] ;
2692
+
2693
+ for ( desc_str, expected) in definite_key_tests {
2694
+ let desc = Descriptor :: < DefiniteDescriptorKey > :: from_str ( & desc_str) . unwrap ( ) ;
2695
+ assert_eq ! (
2696
+ desc. xkey_network( ) ,
2697
+ expected,
2698
+ "Failed for DefiniteDescriptorKey: {}" ,
2699
+ desc_str
2700
+ ) ;
2701
+ }
2702
+ }
2424
2703
}
0 commit comments