Skip to content

Commit

Permalink
merge bitcoin#15986: Add checksum to getdescriptorinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Oct 28, 2021
1 parent 40f38df commit 60e2e63
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 11 deletions.
4 changes: 3 additions & 1 deletion src/rpc/misc.cpp
Expand Up @@ -303,7 +303,8 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
}}.ToString() +
"\nResult:\n"
"{\n"
" \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
" \"descriptor\" : \"desc\", (string) The descriptor in canonical form, without private keys\n"
" \"checksum\" : \"chksum\", (string) The checksum for the input descriptor\n"
" \"isrange\" : true|false, (boolean) Whether the descriptor is ranged\n"
" \"issolvable\" : true|false, (boolean) Whether the descriptor is solvable\n"
" \"hasprivatekeys\" : true|false, (boolean) Whether the input descriptor contained at least one private key\n"
Expand All @@ -324,6 +325,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)

UniValue result(UniValue::VOBJ);
result.pushKV("descriptor", desc->ToString());
result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
result.pushKV("isrange", desc->IsRange());
result.pushKV("issolvable", desc->IsSolvable());
result.pushKV("hasprivatekeys", provider.keys.size() > 0);
Expand Down
35 changes: 25 additions & 10 deletions src/script/descriptor.cpp
Expand Up @@ -832,27 +832,42 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo

} // namespace

std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum)
/** Check a descriptor checksum, and update desc to be the checksum-less part. */
bool CheckChecksum(Span<const char>& sp, bool require_checksum, std::string* out_checksum = nullptr)
{
Span<const char> sp(descriptor.data(), descriptor.size());

// Checksum checks
auto check_split = Split(sp, '#');
if (check_split.size() > 2) return nullptr; // Multiple '#' symbols
if (check_split.size() == 1 && require_checksum) return nullptr; // Missing checksum
if (check_split.size() > 2) return false; // Multiple '#' symbols
if (check_split.size() == 1 && require_checksum) return false; // Missing checksum
if (check_split.size() == 2) {
if (check_split[1].size() != 8) return false; // Unexpected length for checksum
}
auto checksum = DescriptorChecksum(check_split[0]);
if (checksum.empty()) return false; // Invalid characters in payload
if (check_split.size() == 2) {
if (check_split[1].size() != 8) return nullptr; // Unexpected length for checksum
auto checksum = DescriptorChecksum(check_split[0]);
if (checksum.empty()) return nullptr; // Invalid characters in payload
if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return nullptr; // Checksum mismatch
if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return false; // Checksum mismatch
}
if (out_checksum) *out_checksum = std::move(checksum);
sp = check_split[0];
return true;
}

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

std::string GetDescriptorChecksum(const std::string& descriptor)
{
std::string ret;
Span<const char> sp(descriptor.data(), descriptor.size());
if (!CheckChecksum(sp, false, &ret)) return "";
return ret;
}

std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
{
return InferScript(script, ParseScriptContext::TOP, provider);
Expand Down
8 changes: 8 additions & 0 deletions src/script/descriptor.h
Expand Up @@ -72,6 +72,14 @@ struct Descriptor {
*/
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false);

/** Get the checksum for a descriptor.
*
* If it already has one, and it is correct, return the checksum in the input.
* If it already has one that is wrong, return "".
* If it does not already have one, return the checksum that would need to be added.
*/
std::string GetDescriptorChecksum(const std::string& descriptor);

/** Find a descriptor for the specified script, using information from provider where possible.
*
* A non-ranged descriptor which only generates the specified script will be returned in all
Expand Down

0 comments on commit 60e2e63

Please sign in to comment.