@@ -690,7 +690,7 @@ std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
690
690
}
691
691
692
692
/* * Parse a key path, being passed a split list of elements (the first element is ignored). */
693
- NODISCARD bool ParseKeyPath (const std::vector<Span<const char >>& split, KeyPath& out)
693
+ NODISCARD bool ParseKeyPath (const std::vector<Span<const char >>& split, KeyPath& out, std::string& error )
694
694
{
695
695
for (size_t i = 1 ; i < split.size (); ++i) {
696
696
Span<const char > elem = split[i];
@@ -700,14 +700,17 @@ NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath&
700
700
hardened = true ;
701
701
}
702
702
uint32_t p;
703
- if (!ParseUInt32 (std::string (elem.begin (), elem.end ()), &p) || p > 0x7FFFFFFFUL ) return false ;
703
+ if (!ParseUInt32 (std::string (elem.begin (), elem.end ()), &p) || p > 0x7FFFFFFFUL ) {
704
+ error = strprintf (" Key path value %u is out of range" , p);
705
+ return false ;
706
+ }
704
707
out.push_back (p | (((uint32_t )hardened) << 31 ));
705
708
}
706
709
return true ;
707
710
}
708
711
709
712
/* * Parse a public key that excludes origin information. */
710
- std::unique_ptr<PubkeyProvider> ParsePubkeyInner (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out)
713
+ std::unique_ptr<PubkeyProvider> ParsePubkeyInner (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error )
711
714
{
712
715
auto split = Split (sp, ' /' );
713
716
std::string str (split[0 ].begin (), split[0 ].end ());
@@ -726,7 +729,10 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
726
729
}
727
730
CExtKey extkey = DecodeExtKey (str);
728
731
CExtPubKey extpubkey = DecodeExtPubKey (str);
729
- if (!extkey.key .IsValid () && !extpubkey.pubkey .IsValid ()) return nullptr ;
732
+ if (!extkey.key .IsValid () && !extpubkey.pubkey .IsValid ()) {
733
+ error = strprintf (" key '%s' is not valid" , str);
734
+ return nullptr ;
735
+ }
730
736
KeyPath path;
731
737
DeriveType type = DeriveType::NO;
732
738
if (split.back () == MakeSpan (" *" ).first (1 )) {
@@ -736,7 +742,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
736
742
split.pop_back ();
737
743
type = DeriveType::HARDENED;
738
744
}
739
- if (!ParseKeyPath (split, path)) return nullptr ;
745
+ if (!ParseKeyPath (split, path, error )) return nullptr ;
740
746
if (extkey.key .IsValid ()) {
741
747
extpubkey = extkey.Neuter ();
742
748
out.keys .emplace (extpubkey.pubkey .GetID (), extkey.key );
@@ -745,95 +751,126 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
745
751
}
746
752
747
753
/* * Parse a public key including origin information (if enabled). */
748
- std::unique_ptr<PubkeyProvider> ParsePubkey (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out)
754
+ std::unique_ptr<PubkeyProvider> ParsePubkey (const Span<const char >& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error )
749
755
{
750
756
auto origin_split = Split (sp, ' ]' );
751
- if (origin_split.size () > 2 ) return nullptr ;
752
- if (origin_split.size () == 1 ) return ParsePubkeyInner (origin_split[0 ], permit_uncompressed, out);
753
- if (origin_split[0 ].size () < 1 || origin_split[0 ][0 ] != ' [' ) return nullptr ;
757
+ if (origin_split.size () > 2 ) {
758
+ error = " Multiple ']' characters found for a single pubkey" ;
759
+ return nullptr ;
760
+ }
761
+ if (origin_split.size () == 1 ) return ParsePubkeyInner (origin_split[0 ], permit_uncompressed, out, error);
762
+ if (origin_split[0 ].size () < 1 || origin_split[0 ][0 ] != ' [' ) {
763
+ error = strprintf (" Key origin expected but not found, got '%s' instead" , std::string (origin_split[0 ].begin (), origin_split[0 ].end ()));
764
+ return nullptr ;
765
+ }
754
766
auto slash_split = Split (origin_split[0 ].subspan (1 ), ' /' );
755
- if (slash_split[0 ].size () != 8 ) return nullptr ;
767
+ if (slash_split[0 ].size () != 8 ) {
768
+ error = strprintf (" Fingerprint is not 4 bytes (%u characters instead of 8 characters)" , slash_split[0 ].size ());
769
+ return nullptr ;
770
+ }
756
771
std::string fpr_hex = std::string (slash_split[0 ].begin (), slash_split[0 ].end ());
757
- if (!IsHex (fpr_hex)) return nullptr ;
772
+ if (!IsHex (fpr_hex)) {
773
+ error = strprintf (" Fingerprint '%s' is not hex" , fpr_hex);
774
+ return nullptr ;
775
+ }
758
776
auto fpr_bytes = ParseHex (fpr_hex);
759
777
KeyOriginInfo info;
760
778
static_assert (sizeof (info.fingerprint ) == 4 , " Fingerprint must be 4 bytes" );
761
779
assert (fpr_bytes.size () == 4 );
762
780
std::copy (fpr_bytes.begin (), fpr_bytes.end (), info.fingerprint );
763
- if (!ParseKeyPath (slash_split, info.path )) return nullptr ;
764
- auto provider = ParsePubkeyInner (origin_split[1 ], permit_uncompressed, out);
781
+ if (!ParseKeyPath (slash_split, info.path , error )) return nullptr ;
782
+ auto provider = ParsePubkeyInner (origin_split[1 ], permit_uncompressed, out, error );
765
783
if (!provider) return nullptr ;
766
784
return MakeUnique<OriginPubkeyProvider>(std::move (info), std::move (provider));
767
785
}
768
786
769
787
/* * Parse a script in a particular context. */
770
- std::unique_ptr<DescriptorImpl> ParseScript (Span<const char >& sp, ParseScriptContext ctx, FlatSigningProvider& out)
788
+ std::unique_ptr<DescriptorImpl> ParseScript (Span<const char >& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error )
771
789
{
772
790
auto expr = Expr (sp);
773
791
if (Func (" pk" , expr)) {
774
- auto pubkey = ParsePubkey (expr, ctx != ParseScriptContext::P2WSH, out);
792
+ auto pubkey = ParsePubkey (expr, ctx != ParseScriptContext::P2WSH, out, error );
775
793
if (!pubkey) return nullptr ;
776
794
return MakeUnique<PKDescriptor>(std::move (pubkey));
777
795
}
778
796
if (Func (" pkh" , expr)) {
779
- auto pubkey = ParsePubkey (expr, ctx != ParseScriptContext::P2WSH, out);
797
+ auto pubkey = ParsePubkey (expr, ctx != ParseScriptContext::P2WSH, out, error );
780
798
if (!pubkey) return nullptr ;
781
799
return MakeUnique<PKHDescriptor>(std::move (pubkey));
782
800
}
783
801
if (ctx == ParseScriptContext::TOP && Func (" combo" , expr)) {
784
- auto pubkey = ParsePubkey (expr, true , out);
802
+ auto pubkey = ParsePubkey (expr, true , out, error );
785
803
if (!pubkey) return nullptr ;
786
804
return MakeUnique<ComboDescriptor>(std::move (pubkey));
787
805
}
788
806
if (Func (" multi" , expr)) {
789
807
auto threshold = Expr (expr);
790
808
uint32_t thres;
791
809
std::vector<std::unique_ptr<PubkeyProvider>> providers;
792
- if (!ParseUInt32 (std::string (threshold.begin (), threshold.end ()), &thres)) return nullptr ;
810
+ if (!ParseUInt32 (std::string (threshold.begin (), threshold.end ()), &thres)) {
811
+ error = strprintf (" multi threshold %u out of range" , thres);
812
+ return nullptr ;
813
+ }
793
814
size_t script_size = 0 ;
794
815
while (expr.size ()) {
795
- if (!Const (" ," , expr)) return nullptr ;
816
+ if (!Const (" ," , expr)) {
817
+ error = strprintf (" multi: expected ',', got '%c'" , expr[0 ]);
818
+ return nullptr ;
819
+ }
796
820
auto arg = Expr (expr);
797
- auto pk = ParsePubkey (arg, ctx != ParseScriptContext::P2WSH, out);
821
+ auto pk = ParsePubkey (arg, ctx != ParseScriptContext::P2WSH, out, error );
798
822
if (!pk) return nullptr ;
799
823
script_size += pk->GetSize () + 1 ;
800
824
providers.emplace_back (std::move (pk));
801
825
}
802
826
if (providers.size () < 1 || providers.size () > 16 || thres < 1 || thres > providers.size ()) return nullptr ;
803
827
if (ctx == ParseScriptContext::TOP) {
804
- if (providers.size () > 3 ) return nullptr ; // Not more than 3 pubkeys for raw multisig
828
+ if (providers.size () > 3 ) {
829
+ error = strprintf (" Cannot %u pubkeys in bare multisig; only at most 3 pubkeys" , providers.size ());
830
+ return nullptr ;
831
+ }
805
832
}
806
833
if (ctx == ParseScriptContext::P2SH) {
807
- if (script_size + 3 > 520 ) return nullptr ; // Enforce P2SH script size limit
834
+ if (script_size + 3 > 520 ) {
835
+ error = strprintf (" P2SH script is too large, %d bytes is larger than 520 bytes" , script_size + 3 );
836
+ return nullptr ;
837
+ }
808
838
}
809
839
return MakeUnique<MultisigDescriptor>(thres, std::move (providers));
810
840
}
811
841
if (ctx != ParseScriptContext::P2WSH && Func (" wpkh" , expr)) {
812
- auto pubkey = ParsePubkey (expr, false , out);
842
+ auto pubkey = ParsePubkey (expr, false , out, error );
813
843
if (!pubkey) return nullptr ;
814
844
return MakeUnique<WPKHDescriptor>(std::move (pubkey));
815
845
}
816
846
if (ctx == ParseScriptContext::TOP && Func (" sh" , expr)) {
817
- auto desc = ParseScript (expr, ParseScriptContext::P2SH, out);
847
+ auto desc = ParseScript (expr, ParseScriptContext::P2SH, out, error );
818
848
if (!desc || expr.size ()) return nullptr ;
819
849
return MakeUnique<SHDescriptor>(std::move (desc));
820
850
}
821
851
if (ctx != ParseScriptContext::P2WSH && Func (" wsh" , expr)) {
822
- auto desc = ParseScript (expr, ParseScriptContext::P2WSH, out);
852
+ auto desc = ParseScript (expr, ParseScriptContext::P2WSH, out, error );
823
853
if (!desc || expr.size ()) return nullptr ;
824
854
return MakeUnique<WSHDescriptor>(std::move (desc));
825
855
}
826
856
if (ctx == ParseScriptContext::TOP && Func (" addr" , expr)) {
827
857
CTxDestination dest = DecodeDestination (std::string (expr.begin (), expr.end ()));
828
- if (!IsValidDestination (dest)) return nullptr ;
858
+ if (!IsValidDestination (dest)) {
859
+ error = " Address is not valid" ;
860
+ return nullptr ;
861
+ }
829
862
return MakeUnique<AddressDescriptor>(std::move (dest));
830
863
}
831
864
if (ctx == ParseScriptContext::TOP && Func (" raw" , expr)) {
832
865
std::string str (expr.begin (), expr.end ());
833
- if (!IsHex (str)) return nullptr ;
866
+ if (!IsHex (str)) {
867
+ error = " Raw script is not hex" ;
868
+ return nullptr ;
869
+ }
834
870
auto bytes = ParseHex (str);
835
871
return MakeUnique<RawDescriptor>(CScript (bytes.begin (), bytes.end ()));
836
872
}
873
+ error = strprintf (" %s is not a valid descriptor function" , std::string (expr.begin (), expr.end ()));
837
874
return nullptr ;
838
875
}
839
876
@@ -915,38 +952,54 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
915
952
} // namespace
916
953
917
954
/* * Check a descriptor checksum, and update desc to be the checksum-less part. */
918
- bool CheckChecksum (Span<const char >& sp, bool require_checksum, std::string* out_checksum = nullptr )
955
+ bool CheckChecksum (Span<const char >& sp, bool require_checksum, std::string& error, std::string * out_checksum = nullptr )
919
956
{
920
957
auto check_split = Split (sp, ' #' );
921
- if (check_split.size () > 2 ) return false ; // Multiple '#' symbols
922
- if (check_split.size () == 1 && require_checksum) return false ; // Missing checksum
958
+ if (check_split.size () > 2 ) {
959
+ error = " Multiple '#' symbols" ;
960
+ return false ;
961
+ }
962
+ if (check_split.size () == 1 && require_checksum){
963
+ error = " Missing checksum" ;
964
+ return false ;
965
+ }
923
966
if (check_split.size () == 2 ) {
924
- if (check_split[1 ].size () != 8 ) return false ; // Unexpected length for checksum
967
+ if (check_split[1 ].size () != 8 ) {
968
+ error = strprintf (" Expected 8 character checksum, not %u characters" , check_split[1 ].size ());
969
+ return false ;
970
+ }
925
971
}
926
972
auto checksum = DescriptorChecksum (check_split[0 ]);
927
- if (checksum.empty ()) return false ; // Invalid characters in payload
973
+ if (checksum.empty ()) {
974
+ error = " Invalid characters in payload" ;
975
+ return false ;
976
+ }
928
977
if (check_split.size () == 2 ) {
929
- if (!std::equal (checksum.begin (), checksum.end (), check_split[1 ].begin ())) return false ; // Checksum mismatch
978
+ if (!std::equal (checksum.begin (), checksum.end (), check_split[1 ].begin ())) {
979
+ error = strprintf (" Provided checksum '%s' does not match computed checksum '%s'" , std::string (check_split[1 ].begin (), check_split[1 ].end ()), checksum);
980
+ return false ;
981
+ }
930
982
}
931
983
if (out_checksum) *out_checksum = std::move (checksum);
932
984
sp = check_split[0 ];
933
985
return true ;
934
986
}
935
987
936
- std::unique_ptr<Descriptor> Parse (const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
988
+ std::unique_ptr<Descriptor> Parse (const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum)
937
989
{
938
990
Span<const char > sp (descriptor.data (), descriptor.size ());
939
- if (!CheckChecksum (sp, require_checksum)) return nullptr ;
940
- auto ret = ParseScript (sp, ParseScriptContext::TOP, out);
991
+ if (!CheckChecksum (sp, require_checksum, error )) return nullptr ;
992
+ auto ret = ParseScript (sp, ParseScriptContext::TOP, out, error );
941
993
if (sp.size () == 0 && ret) return std::unique_ptr<Descriptor>(std::move (ret));
942
994
return nullptr ;
943
995
}
944
996
945
997
std::string GetDescriptorChecksum (const std::string& descriptor)
946
998
{
947
999
std::string ret;
1000
+ std::string error;
948
1001
Span<const char > sp (descriptor.data (), descriptor.size ());
949
- if (!CheckChecksum (sp, false , &ret)) return " " ;
1002
+ if (!CheckChecksum (sp, false , error, &ret)) return " " ;
950
1003
return ret;
951
1004
}
952
1005
0 commit comments