Skip to content

Commit 7a960ba

Browse files
committed
Merge #15986: Add checksum to getdescriptorinfo
26d3fad Add unmodified-but-with-checksum to getdescriptorinfo (Pieter Wuille) 104b3a5 Factor out checksum checking from descriptor parsing (Pieter Wuille) Pull request description: ACKs for top commit: achow101: Code Review ACK 26d3fad meshcollider: re-Code Review ACK 26d3fad Sjors: ACK 26d3fad Tree-SHA512: b7a7f89b64a184927d6f9a0c183a087609983f0c5d5593f78e12db4714e930a4af655db9da4b0c407ea2e24d3b926cef6e1f2a15de502d0d1290a6e046826b99
2 parents b80cdfe + 26d3fad commit 7a960ba

File tree

4 files changed

+39
-10
lines changed

4 files changed

+39
-10
lines changed

src/rpc/misc.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
136136
RPCResult{
137137
"{\n"
138138
" \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
139+
" \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n"
139140
" \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
140141
" \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
141142
" \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
@@ -156,6 +157,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
156157

157158
UniValue result(UniValue::VOBJ);
158159
result.pushKV("descriptor", desc->ToString());
160+
result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
159161
result.pushKV("isrange", desc->IsRange());
160162
result.pushKV("issolvable", desc->IsSolvable());
161163
result.pushKV("hasprivatekeys", provider.keys.size() > 0);

src/script/descriptor.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -914,27 +914,42 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
914914

915915
} // namespace
916916

917-
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
917+
/** 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)
918919
{
919-
Span<const char> sp(descriptor.data(), descriptor.size());
920-
921-
// Checksum checks
922920
auto check_split = Split(sp, '#');
923-
if (check_split.size() > 2) return nullptr; // Multiple '#' symbols
924-
if (check_split.size() == 1 && require_checksum) return nullptr; // Missing checksum
921+
if (check_split.size() > 2) return false; // Multiple '#' symbols
922+
if (check_split.size() == 1 && require_checksum) return false; // Missing checksum
923+
if (check_split.size() == 2) {
924+
if (check_split[1].size() != 8) return false; // Unexpected length for checksum
925+
}
926+
auto checksum = DescriptorChecksum(check_split[0]);
927+
if (checksum.empty()) return false; // Invalid characters in payload
925928
if (check_split.size() == 2) {
926-
if (check_split[1].size() != 8) return nullptr; // Unexpected length for checksum
927-
auto checksum = DescriptorChecksum(check_split[0]);
928-
if (checksum.empty()) return nullptr; // Invalid characters in payload
929-
if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return nullptr; // Checksum mismatch
929+
if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return false; // Checksum mismatch
930930
}
931+
if (out_checksum) *out_checksum = std::move(checksum);
931932
sp = check_split[0];
933+
return true;
934+
}
932935

936+
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
937+
{
938+
Span<const char> sp(descriptor.data(), descriptor.size());
939+
if (!CheckChecksum(sp, require_checksum)) return nullptr;
933940
auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
934941
if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret));
935942
return nullptr;
936943
}
937944

945+
std::string GetDescriptorChecksum(const std::string& descriptor)
946+
{
947+
std::string ret;
948+
Span<const char> sp(descriptor.data(), descriptor.size());
949+
if (!CheckChecksum(sp, false, &ret)) return "";
950+
return ret;
951+
}
952+
938953
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
939954
{
940955
return InferScript(script, ParseScriptContext::TOP, provider);

src/script/descriptor.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ struct Descriptor {
8181
*/
8282
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false);
8383

84+
/** Get the checksum for a descriptor.
85+
*
86+
* If it already has one, and it is correct, return the checksum in the input.
87+
* If it already has one that is wrong, return "".
88+
* If it does not already have one, return the checksum that would need to be added.
89+
*/
90+
std::string GetDescriptorChecksum(const std::string& descriptor);
91+
8492
/** Find a descriptor for the specified script, using information from provider where possible.
8593
*
8694
* A non-ranged descriptor which only generates the specified script will be returned in all

test/functional/wallet_address_types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ def test_desc(self, node, address, multisig, typ, utxo):
175175
assert info['desc'] == descsum_create(info['desc'][:-9])
176176
# Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips
177177
assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor']
178+
assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['checksum'])
179+
# Verify that keeping the checksum and feeding it to getdescriptorinfo roundtrips
180+
assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'])['descriptor']
181+
assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'])['checksum'])
178182

179183
if not multisig and typ == 'legacy':
180184
# P2PKH

0 commit comments

Comments
 (0)