@@ -791,6 +791,42 @@ impl Descriptor<DescriptorPublicKey> {
791
791
}
792
792
}
793
793
794
+ impl < Pk : MiniscriptKey > Descriptor < Pk > {
795
+ /// Whether this descriptor is a multipath descriptor that contains any 2 multipath keys
796
+ /// with a different number of derivation paths.
797
+ /// Such a descriptor is invalid according to BIP389.
798
+ pub fn multipath_length_mismatch ( & self ) -> bool {
799
+ // (Ab)use `for_each_key` to record the number of derivation paths a multipath key has.
800
+ #[ derive( PartialEq ) ]
801
+ enum MultipathLenChecker {
802
+ SinglePath ,
803
+ MultipathLen ( usize ) ,
804
+ LenMismatch ,
805
+ }
806
+
807
+ let mut checker = MultipathLenChecker :: SinglePath ;
808
+ self . for_each_key ( |key| {
809
+ match key. num_der_paths ( ) {
810
+ 0 | 1 => { }
811
+ n => match checker {
812
+ MultipathLenChecker :: SinglePath => {
813
+ checker = MultipathLenChecker :: MultipathLen ( n) ;
814
+ }
815
+ MultipathLenChecker :: MultipathLen ( len) => {
816
+ if len != n {
817
+ checker = MultipathLenChecker :: LenMismatch ;
818
+ }
819
+ }
820
+ MultipathLenChecker :: LenMismatch => { }
821
+ } ,
822
+ }
823
+ true
824
+ } ) ;
825
+
826
+ checker == MultipathLenChecker :: LenMismatch
827
+ }
828
+ }
829
+
794
830
impl Descriptor < DefiniteDescriptorKey > {
795
831
/// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or
796
832
/// otherwise converting them. All [`bitcoin::XOnlyPublicKey`]s are converted to by adding a
@@ -861,13 +897,19 @@ impl_from_str!(
861
897
// tr tree parsing has special code
862
898
// Tr::from_str will check the checksum
863
899
// match "tr(" to handle more extensibly
864
- if s. starts_with( "tr(" ) {
900
+ let desc = if s. starts_with( "tr(" ) {
865
901
Ok ( Descriptor :: Tr ( Tr :: from_str( s) ?) )
866
902
} else {
867
903
let desc_str = verify_checksum( s) ?;
868
904
let top = expression:: Tree :: from_str( desc_str) ?;
869
905
expression:: FromTree :: from_tree( & top)
906
+ } ?;
907
+
908
+ if desc. multipath_length_mismatch( ) {
909
+ return Err ( Error :: MultipathDescLenMismatch ) ;
870
910
}
911
+
912
+ Ok ( desc)
871
913
}
872
914
) ;
873
915
@@ -1831,6 +1873,7 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
1831
1873
// We can parse a multipath descriptors, and make it into separate single-path descriptors.
1832
1874
let desc = Descriptor :: from_str ( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<7';8h;20>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/<0;1;987>/*)))" ) . unwrap ( ) ;
1833
1875
assert ! ( desc. is_multipath( ) ) ;
1876
+ assert ! ( !desc. multipath_length_mismatch( ) ) ;
1834
1877
assert_eq ! ( desc. into_single_descriptors( ) . unwrap( ) , vec![
1835
1878
Descriptor :: from_str( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/7'/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/0/*)))" ) . unwrap( ) ,
1836
1879
Descriptor :: from_str( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/8h/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/1/*)))" ) . unwrap( ) ,
@@ -1840,6 +1883,7 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
1840
1883
// Even if only one of the keys is multipath.
1841
1884
let desc = Descriptor :: from_str ( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<0;1>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))" ) . unwrap ( ) ;
1842
1885
assert ! ( desc. is_multipath( ) ) ;
1886
+ assert ! ( !desc. multipath_length_mismatch( ) ) ;
1843
1887
assert_eq ! ( desc. into_single_descriptors( ) . unwrap( ) , vec![
1844
1888
Descriptor :: from_str( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/0/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))" ) . unwrap( ) ,
1845
1889
Descriptor :: from_str( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/1/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))" ) . unwrap( ) ,
@@ -1848,9 +1892,14 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
1848
1892
// We can detect regular single-path descriptors.
1849
1893
let notmulti_desc = Descriptor :: from_str ( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/4567/*)))" ) . unwrap ( ) ;
1850
1894
assert ! ( !notmulti_desc. is_multipath( ) ) ;
1895
+ assert ! ( !notmulti_desc. multipath_length_mismatch( ) ) ;
1851
1896
assert_eq ! (
1852
1897
notmulti_desc. clone( ) . into_single_descriptors( ) . unwrap( ) ,
1853
1898
vec![ notmulti_desc]
1854
1899
) ;
1900
+
1901
+ // We refuse to parse multipath descriptors with a mismatch in the number of derivation paths between keys.
1902
+ Descriptor :: < DescriptorPublicKey > :: from_str ( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<0;1>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/<0;1;2;3;4>/*)))" ) . unwrap_err ( ) ;
1903
+ Descriptor :: < DescriptorPublicKey > :: from_str ( "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/0'/<0;1;2;3>/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/8/<0;1;2>/*)))" ) . unwrap_err ( ) ;
1855
1904
}
1856
1905
}
0 commit comments