Changeset View
Changeset View
Standalone View
Standalone View
src/script/descriptor.cpp
Show First 20 Lines • Show All 804 Lines • ▼ Show 20 Lines | 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; | ||||
if (elem.size() > 0 && | if (elem.size() > 0 && | ||||
(elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) { | (elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) { | ||||
elem = elem.first(elem.size() - 1); | elem = elem.first(elem.size() - 1); | ||||
hardened = true; | hardened = true; | ||||
} | } | ||||
uint32_t p; | uint32_t p; | ||||
if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || | if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) { | ||||
p > 0x7FFFFFFFUL) { | error = strprintf("Key path value '%s' is not a valid uint32", | ||||
std::string(elem.begin(), elem.end()).c_str()); | |||||
return false; | |||||
} else if (p > 0x7FFFFFFFUL) { | |||||
error = strprintf("Key path value %u is out of range", p); | error = strprintf("Key path value %u is out of range", p); | ||||
return false; | return false; | ||||
} | } | ||||
out.push_back(p | (uint32_t(hardened) << 31)); | out.push_back(p | (uint32_t(hardened) << 31)); | ||||
} | } | ||||
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) { | ||||
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) { | |||||
error = "No key provided"; | |||||
return nullptr; | |||||
} | |||||
if (split.size() == 1) { | if (split.size() == 1) { | ||||
if (IsHex(str)) { | if (IsHex(str)) { | ||||
std::vector<uint8_t> data = ParseHex(str); | std::vector<uint8_t> data = ParseHex(str); | ||||
CPubKey pubkey(data); | CPubKey pubkey(data); | ||||
if (pubkey.IsFullyValid()) { | if (pubkey.IsFullyValid()) { | ||||
return std::make_unique<ConstPubkeyProvider>(pubkey); | return std::make_unique<ConstPubkeyProvider>(pubkey); | ||||
} | } | ||||
error = strprintf("Pubkey '%s' is invalid", str); | |||||
return nullptr; | |||||
} | } | ||||
CKey key = DecodeSecret(str); | CKey key = DecodeSecret(str); | ||||
if (key.IsValid()) { | if (key.IsValid()) { | ||||
CPubKey pubkey = key.GetPubKey(); | CPubKey pubkey = key.GetPubKey(); | ||||
out.keys.emplace(pubkey.GetID(), key); | out.keys.emplace(pubkey.GetID(), key); | ||||
return std::make_unique<ConstPubkeyProvider>(pubkey); | return std::make_unique<ConstPubkeyProvider>(pubkey); | ||||
} | } | ||||
} | } | ||||
Show All 32 Lines | std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char> &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); | ||||
} | } | ||||
if (origin_split[0].size() < 1 || origin_split[0][0] != '[') { | if (origin_split[0].size() < 1 || origin_split[0][0] != '[') { | ||||
error = strprintf( | error = strprintf("Key origin start '[ character expected but not " | ||||
"Key origin expected but not found, got '%s' instead", | "found, got '%c' instead", | ||||
std::string(origin_split[0].begin(), origin_split[0].end())); | origin_split[0][0]); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
auto slash_split = Split(origin_split[0].subspan(1), '/'); | auto slash_split = Split(origin_split[0].subspan(1), '/'); | ||||
if (slash_split[0].size() != 8) { | if (slash_split[0].size() != 8) { | ||||
error = strprintf("Fingerprint is not 4 bytes (%u characters instead " | error = strprintf("Fingerprint is not 4 bytes (%u characters instead " | ||||
"of 8 characters)", | "of 8 characters)", | ||||
slash_split[0].size()); | slash_split[0].size()); | ||||
return nullptr; | return nullptr; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (Func("pkh", expr)) { | ||||
return std::make_unique<PKHDescriptor>(std::move(pubkey)); | return std::make_unique<PKHDescriptor>(std::move(pubkey)); | ||||
} | } | ||||
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { | if (ctx == ParseScriptContext::TOP && Func("combo", 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<ComboDescriptor>(std::move(pubkey)); | return std::make_unique<ComboDescriptor>(std::move(pubkey)); | ||||
} else if (ctx != ParseScriptContext::TOP && Func("combo", expr)) { | |||||
error = "Cannot have combo in non-top level"; | |||||
return nullptr; | |||||
} | } | ||||
if (Func("multi", expr)) { | if (Func("multi", expr)) { | ||||
auto threshold = Expr(expr); | auto threshold = Expr(expr); | ||||
uint32_t thres; | uint32_t thres; | ||||
std::vector<std::unique_ptr<PubkeyProvider>> providers; | std::vector<std::unique_ptr<PubkeyProvider>> providers; | ||||
if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), | if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), | ||||
&thres)) { | &thres)) { | ||||
error = strprintf("multi threshold %u out of range", thres); | error = strprintf( | ||||
"Multi threshold '%s' is not valid", | |||||
std::string(threshold.begin(), threshold.end()).c_str()); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
size_t script_size = 0; | size_t script_size = 0; | ||||
while (expr.size()) { | while (expr.size()) { | ||||
if (!Const(",", expr)) { | if (!Const(",", expr)) { | ||||
error = strprintf("multi: expected ',', got '%c'", expr[0]); | error = strprintf("Multi: expected ',', got '%c'", expr[0]); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
auto arg = Expr(expr); | auto arg = Expr(expr); | ||||
auto pk = ParsePubkey(arg, out, error); | auto pk = ParsePubkey(arg, out, error); | ||||
if (!pk) { | if (!pk) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
script_size += pk->GetSize() + 1; | script_size += pk->GetSize() + 1; | ||||
providers.emplace_back(std::move(pk)); | providers.emplace_back(std::move(pk)); | ||||
} | } | ||||
if (providers.size() < 1 || providers.size() > 16 || thres < 1 || | if (providers.size() < 1 || providers.size() > 16) { | ||||
thres > providers.size()) { | error = strprintf("Cannot have %u keys in multisig; must have " | ||||
"between 1 and 16 keys, inclusive", | |||||
providers.size()); | |||||
return nullptr; | |||||
} else if (thres < 1) { | |||||
error = strprintf( | |||||
"Multisig threshold cannot be %d, must be at least 1", thres); | |||||
return nullptr; | |||||
} else if (thres > providers.size()) { | |||||
error = | |||||
strprintf("Multisig threshold cannot be larger than the number " | |||||
"of keys; threshold is %d but only %u keys specified", | |||||
thres, providers.size()); | |||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (ctx == ParseScriptContext::TOP) { | if (ctx == ParseScriptContext::TOP) { | ||||
if (providers.size() > 3) { | if (providers.size() > 3) { | ||||
error = strprintf("Cannot %u pubkeys in bare multisig; only at " | error = strprintf("Cannot have %u pubkeys in bare multisig; " | ||||
"most 3 pubkeys", | "only at most 3 pubkeys", | ||||
providers.size()); | providers.size()); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
if (ctx == ParseScriptContext::P2SH) { | if (ctx == ParseScriptContext::P2SH) { | ||||
if (script_size + 3 > 520) { | if (script_size + 3 > 520) { | ||||
error = strprintf("P2SH script is too large, %d bytes is " | error = strprintf("P2SH script is too large, %d bytes is " | ||||
"larger than 520 bytes", | "larger than 520 bytes", | ||||
script_size + 3); | script_size + 3); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
return std::make_unique<MultisigDescriptor>(thres, | return std::make_unique<MultisigDescriptor>(thres, | ||||
std::move(providers)); | std::move(providers)); | ||||
} | } | ||||
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) { | if (ctx == ParseScriptContext::TOP && Func("sh", expr)) { | ||||
auto desc = ParseScript(expr, ParseScriptContext::P2SH, out, error); | auto desc = ParseScript(expr, ParseScriptContext::P2SH, out, error); | ||||
if (!desc || expr.size()) { | if (!desc || expr.size()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return std::make_unique<SHDescriptor>(std::move(desc)); | return std::make_unique<SHDescriptor>(std::move(desc)); | ||||
} else if (ctx != ParseScriptContext::TOP && Func("sh", expr)) { | |||||
error = "Cannot have sh in non-top level"; | |||||
return nullptr; | |||||
} | } | ||||
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) { | if (ctx == ParseScriptContext::TOP && Func("addr", expr)) { | ||||
CTxDestination dest = | CTxDestination dest = | ||||
DecodeDestination(std::string(expr.begin(), expr.end()), Params()); | DecodeDestination(std::string(expr.begin(), expr.end()), Params()); | ||||
if (!IsValidDestination(dest)) { | if (!IsValidDestination(dest)) { | ||||
error = "Address is not valid"; | error = "Address is not valid"; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return std::make_unique<AddressDescriptor>(std::move(dest)); | return std::make_unique<AddressDescriptor>(std::move(dest)); | ||||
} | } | ||||
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) { | if (ctx == ParseScriptContext::TOP && Func("raw", expr)) { | ||||
std::string str(expr.begin(), expr.end()); | std::string str(expr.begin(), expr.end()); | ||||
if (!IsHex(str)) { | if (!IsHex(str)) { | ||||
error = "Raw script is not hex"; | error = "Raw script is not hex"; | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
auto bytes = ParseHex(str); | auto bytes = ParseHex(str); | ||||
return std::make_unique<RawDescriptor>( | return std::make_unique<RawDescriptor>( | ||||
CScript(bytes.begin(), bytes.end())); | CScript(bytes.begin(), bytes.end())); | ||||
} | } | ||||
if (ctx == ParseScriptContext::P2SH) { | |||||
error = "A function is needed within P2SH"; | |||||
return nullptr; | |||||
} | |||||
error = strprintf("%s is not a valid descriptor function", | error = strprintf("%s is not a valid descriptor function", | ||||
std::string(expr.begin(), expr.end())); | std::string(expr.begin(), expr.end())); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
static std::unique_ptr<PubkeyProvider> | static std::unique_ptr<PubkeyProvider> | ||||
InferPubkey(const CPubKey &pubkey, ParseScriptContext, | InferPubkey(const CPubKey &pubkey, ParseScriptContext, | ||||
const SigningProvider &provider) { | const SigningProvider &provider) { | ||||
▲ Show 20 Lines • Show All 137 Lines • Show Last 20 Lines |