Changeset View
Changeset View
Standalone View
Standalone View
src/script/descriptor.cpp
// Copyright (c) 2018 The Bitcoin Core developers | // Copyright (c) 2018 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <script/descriptor.h> | #include <script/descriptor.h> | ||||
#include <chainparams.h> // For Params() | #include <chainparams.h> // For Params() | ||||
#include <config.h> | #include <config.h> | ||||
#include <key_io.h> | #include <key_io.h> | ||||
#include <pubkey.h> | #include <pubkey.h> | ||||
#include <script/standard.h> | #include <script/standard.h> | ||||
#include <span.h> | #include <span.h> | ||||
#include <util/bip32.h> | #include <util/bip32.h> | ||||
#include <util/spanparsing.h> | |||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <memory> | #include <memory> | ||||
#include <string> | #include <string> | ||||
namespace { | namespace { | ||||
▲ Show 20 Lines • Show All 707 Lines • ▼ Show 20 Lines | |||||
//////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////// | ||||
enum class ParseScriptContext { | enum class ParseScriptContext { | ||||
TOP, | TOP, | ||||
P2SH, | P2SH, | ||||
}; | }; | ||||
/** | /** | ||||
* Parse a constant. If successful, sp is updated to skip the constant and | |||||
* return true. | |||||
*/ | |||||
bool Const(const std::string &str, Span<const char> &sp) { | |||||
if ((size_t)sp.size() >= str.size() && | |||||
std::equal(str.begin(), str.end(), sp.begin())) { | |||||
sp = sp.subspan(str.size()); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** | |||||
* Parse a function call. If successful, sp is updated to be the function's | |||||
* argument(s). | |||||
*/ | |||||
bool Func(const std::string &str, Span<const char> &sp) { | |||||
if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && | |||||
sp[sp.size() - 1] == ')' && | |||||
std::equal(str.begin(), str.end(), sp.begin())) { | |||||
sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
/** Return the expression that sp begins with, and update sp to skip it. */ | |||||
Span<const char> Expr(Span<const char> &sp) { | |||||
int level = 0; | |||||
auto it = sp.begin(); | |||||
while (it != sp.end()) { | |||||
if (*it == '(') { | |||||
++level; | |||||
} else if (level && *it == ')') { | |||||
--level; | |||||
} else if (level == 0 && (*it == ')' || *it == ',')) { | |||||
break; | |||||
} | |||||
++it; | |||||
} | |||||
Span<const char> ret = sp.first(it - sp.begin()); | |||||
sp = sp.subspan(it - sp.begin()); | |||||
return ret; | |||||
} | |||||
/** Split a string on every instance of sep, returning a vector. */ | |||||
std::vector<Span<const char>> Split(const Span<const char> &sp, char sep) { | |||||
std::vector<Span<const char>> ret; | |||||
auto it = sp.begin(); | |||||
auto start = it; | |||||
while (it != sp.end()) { | |||||
if (*it == sep) { | |||||
ret.emplace_back(start, it); | |||||
start = it + 1; | |||||
} | |||||
++it; | |||||
} | |||||
ret.emplace_back(start, it); | |||||
return ret; | |||||
} | |||||
/** | |||||
* Parse a key path, being passed a split list of elements (the first element is | * Parse a key path, being passed a split list of elements (the first element is | ||||
* ignored). | * ignored). | ||||
*/ | */ | ||||
NODISCARD bool ParseKeyPath(const std::vector<Span<const char>> &split, | NODISCARD bool ParseKeyPath(const std::vector<Span<const char>> &split, | ||||
KeyPath &out, std::string &error) { | KeyPath &out, std::string &error) { | ||||
for (size_t i = 1; i < split.size(); ++i) { | for (size_t i = 1; i < split.size(); ++i) { | ||||
Span<const char> elem = split[i]; | Span<const char> elem = split[i]; | ||||
bool hardened = false; | bool hardened = false; | ||||
Show All 15 Lines | NODISCARD bool ParseKeyPath(const std::vector<Span<const char>> &split, | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** Parse a public key that excludes origin information. */ | /** Parse a public key that excludes origin information. */ | ||||
std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char> &sp, | std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char> &sp, | ||||
FlatSigningProvider &out, | FlatSigningProvider &out, | ||||
std::string &error) { | std::string &error) { | ||||
using namespace spanparsing; | |||||
auto split = Split(sp, '/'); | auto split = Split(sp, '/'); | ||||
std::string str(split[0].begin(), split[0].end()); | std::string str(split[0].begin(), split[0].end()); | ||||
if (str.size() == 0) { | if (str.size() == 0) { | ||||
error = "No key provided"; | error = "No key provided"; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (split.size() == 1) { | if (split.size() == 1) { | ||||
if (IsHex(str)) { | if (IsHex(str)) { | ||||
Show All 38 Lines | std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char> &sp, | ||||
return std::make_unique<BIP32PubkeyProvider>(extpubkey, std::move(path), | return std::make_unique<BIP32PubkeyProvider>(extpubkey, std::move(path), | ||||
type); | type); | ||||
} | } | ||||
/** Parse a public key including origin information (if enabled). */ | /** Parse a public key including origin information (if enabled). */ | ||||
std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char> &sp, | std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char> &sp, | ||||
FlatSigningProvider &out, | FlatSigningProvider &out, | ||||
std::string &error) { | std::string &error) { | ||||
using namespace spanparsing; | |||||
auto origin_split = Split(sp, ']'); | auto origin_split = Split(sp, ']'); | ||||
if (origin_split.size() > 2) { | if (origin_split.size() > 2) { | ||||
error = "Multiple ']' characters found for a single pubkey"; | error = "Multiple ']' characters found for a single pubkey"; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (origin_split.size() == 1) { | if (origin_split.size() == 1) { | ||||
return ParsePubkeyInner(origin_split[0], out, error); | return ParsePubkeyInner(origin_split[0], out, error); | ||||
} | } | ||||
Show All 32 Lines | return std::make_unique<OriginPubkeyProvider>(std::move(info), | ||||
std::move(provider)); | std::move(provider)); | ||||
} | } | ||||
/** Parse a script in a particular context. */ | /** Parse a script in a particular context. */ | ||||
std::unique_ptr<DescriptorImpl> ParseScript(Span<const char> &sp, | std::unique_ptr<DescriptorImpl> ParseScript(Span<const char> &sp, | ||||
ParseScriptContext ctx, | ParseScriptContext ctx, | ||||
FlatSigningProvider &out, | FlatSigningProvider &out, | ||||
std::string &error) { | std::string &error) { | ||||
using namespace spanparsing; | |||||
auto expr = Expr(sp); | auto expr = Expr(sp); | ||||
if (Func("pk", expr)) { | if (Func("pk", expr)) { | ||||
auto pubkey = ParsePubkey(expr, out, error); | auto pubkey = ParsePubkey(expr, out, error); | ||||
if (!pubkey) { | if (!pubkey) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return std::make_unique<PKDescriptor>(std::move(pubkey)); | return std::make_unique<PKDescriptor>(std::move(pubkey)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | |||||
} // namespace | } // namespace | ||||
/** | /** | ||||
* Check a descriptor checksum, and update desc to be the checksum-less part. | * Check a descriptor checksum, and update desc to be the checksum-less part. | ||||
*/ | */ | ||||
bool CheckChecksum(Span<const char> &sp, bool require_checksum, | bool CheckChecksum(Span<const char> &sp, bool require_checksum, | ||||
std::string &error, std::string *out_checksum = nullptr) { | std::string &error, std::string *out_checksum = nullptr) { | ||||
using namespace spanparsing; | |||||
auto check_split = Split(sp, '#'); | auto check_split = Split(sp, '#'); | ||||
if (check_split.size() > 2) { | if (check_split.size() > 2) { | ||||
error = "Multiple '#' symbols"; | error = "Multiple '#' symbols"; | ||||
return false; | return false; | ||||
} | } | ||||
if (check_split.size() == 1 && require_checksum) { | if (check_split.size() == 1 && require_checksum) { | ||||
error = "Missing checksum"; | error = "Missing checksum"; | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 59 Lines • Show Last 20 Lines |