aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/descriptor.cpp295
-rw-r--r--src/script/descriptor.h25
-rw-r--r--src/script/interpreter.cpp8
-rw-r--r--src/script/ismine.cpp193
-rw-r--r--src/script/ismine.h30
-rw-r--r--src/script/keyorigin.h37
-rw-r--r--src/script/script.cpp1
-rw-r--r--src/script/script.h13
-rw-r--r--src/script/sigcache.cpp1
-rw-r--r--src/script/sign.cpp64
-rw-r--r--src/script/sign.h73
-rw-r--r--src/script/signingprovider.cpp199
-rw-r--r--src/script/signingprovider.h92
-rw-r--r--src/script/standard.cpp19
-rw-r--r--src/script/standard.h25
15 files changed, 630 insertions, 445 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 43448d7222..b782ebbd1f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -164,6 +164,9 @@ struct PubkeyProvider
/** Get the descriptor string form including private data (if available in arg). */
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
+
+ /** Derive a private key, if private data is available in arg. */
+ virtual bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const = 0;
};
class OriginPubkeyProvider final : public PubkeyProvider
@@ -195,6 +198,10 @@ public:
ret = "[" + OriginString() + "]" + std::move(sub);
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ return m_provider->GetPrivKey(pos, arg, key);
+ }
};
/** An object representing a parsed constant public key in a descriptor. */
@@ -222,6 +229,10 @@ public:
ret = EncodeSecret(key);
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ return arg.GetKey(m_pubkey.GetID(), key);
+ }
};
enum class DeriveType {
@@ -266,14 +277,9 @@ public:
{
if (key) {
if (IsHardened()) {
- CExtKey extkey;
- if (!GetExtKey(arg, extkey)) return false;
- for (auto entry : m_path) {
- extkey.Derive(extkey, entry);
- }
- if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
- if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
- *key = extkey.Neuter().pubkey;
+ CKey priv_key;
+ if (!GetPrivKey(pos, arg, priv_key)) return false;
+ *key = priv_key.GetPubKey();
} else {
// TODO: optimize by caching
CExtPubKey extkey = m_extkey;
@@ -312,15 +318,29 @@ public:
}
return true;
}
+ bool GetPrivKey(int pos, const SigningProvider& arg, CKey& key) const override
+ {
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
+ for (auto entry : m_path) {
+ extkey.Derive(extkey, entry);
+ }
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ key = extkey.key;
+ return true;
+ }
};
/** Base class for all Descriptor implementations. */
class DescriptorImpl : public Descriptor
{
- //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size of Multisig).
+ //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size for Multisig).
const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
//! The sub-descriptor argument (nullptr for everything but SH and WSH).
- const std::unique_ptr<DescriptorImpl> m_script_arg;
+ //! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
+ //! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
+ const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg;
//! The string name of the descriptor function.
const std::string m_name;
@@ -331,10 +351,10 @@ protected:
/** A helper function to construct the scripts for this descriptor.
*
* This function is invoked once for every CScript produced by evaluating
- * m_script_arg, or just once in case m_script_arg is nullptr.
+ * m_subdescriptor_arg, or just once in case m_subdescriptor_arg is nullptr.
* @param pubkeys The evaluations of the m_pubkey_args field.
- * @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr).
+ * @param script The evaluation of m_subdescriptor_arg (or nullptr when m_subdescriptor_arg is nullptr).
* @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
* The script arguments to this function are automatically added, as is the origin info of the provided pubkeys.
* @return A vector with scriptPubKeys for this descriptor.
@@ -342,12 +362,12 @@ protected:
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
public:
- DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)), m_name(name) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_subdescriptor_arg(std::move(script)), m_name(name) {}
bool IsSolvable() const override
{
- if (m_script_arg) {
- if (!m_script_arg->IsSolvable()) return false;
+ if (m_subdescriptor_arg) {
+ if (!m_subdescriptor_arg->IsSolvable()) return false;
}
return true;
}
@@ -357,8 +377,8 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pubkey->IsRange()) return true;
}
- if (m_script_arg) {
- if (m_script_arg->IsRange()) return true;
+ if (m_subdescriptor_arg) {
+ if (m_subdescriptor_arg->IsRange()) return true;
}
return false;
}
@@ -378,10 +398,10 @@ public:
}
ret += std::move(tmp);
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
if (pos++) ret += ",";
std::string tmp;
- if (!m_script_arg->ToStringHelper(arg, tmp, priv)) return false;
+ if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv)) return false;
ret += std::move(tmp);
}
out = std::move(ret) + ")";
@@ -410,6 +430,8 @@ public:
// Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
for (const auto& p : m_pubkey_args) {
entries.emplace_back();
+ // If we have a cache, we don't need GetPubKey to compute the public key.
+ // Pass in nullptr to signify only origin info is desired.
if (!p->GetPubKey(pos, arg, cache_read ? nullptr : &entries.back().first, entries.back().second)) return false;
if (cache_read) {
// Cached expanded public key exists, use it.
@@ -426,9 +448,9 @@ public:
}
}
std::vector<CScript> subscripts;
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
- if (!m_script_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
+ if (!m_subdescriptor_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
out = Merge(out, subprovider);
}
@@ -436,9 +458,9 @@ public:
pubkeys.reserve(entries.size());
for (auto& entry : entries) {
pubkeys.push_back(entry.first);
- out.origins.emplace(entry.first.GetID(), std::move(entry.second));
+ out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
for (const auto& subscript : subscripts) {
out.scripts.emplace(CScriptID(subscript), subscript);
std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out);
@@ -462,6 +484,20 @@ public:
Span<const unsigned char> span = MakeSpan(cache);
return ExpandHelper(pos, DUMMY_SIGNING_PROVIDER, &span, output_scripts, out, nullptr) && span.size() == 0;
}
+
+ void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const final
+ {
+ for (const auto& p : m_pubkey_args) {
+ CKey key;
+ if (!p->GetPrivKey(pos, provider, key)) continue;
+ out.keys.emplace(key.GetPubKey().GetID(), key);
+ }
+ if (m_subdescriptor_arg) {
+ FlatSigningProvider subprovider;
+ m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider);
+ out = Merge(out, subprovider);
+ }
+ }
};
/** Construct a vector with one element, which is moved into it. */
@@ -514,7 +550,7 @@ protected:
{
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
- return Singleton(GetScriptForDestination(id));
+ return Singleton(GetScriptForDestination(PKHash(id)));
}
public:
PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {}
@@ -544,12 +580,12 @@ protected:
CKeyID id = keys[0].GetID();
out.pubkeys.emplace(id, keys[0]);
ret.emplace_back(GetScriptForRawPubKey(keys[0])); // P2PK
- ret.emplace_back(GetScriptForDestination(id)); // P2PKH
+ ret.emplace_back(GetScriptForDestination(PKHash(id))); // P2PKH
if (keys[0].IsCompressed()) {
CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(id));
out.scripts.emplace(CScriptID(p2wpkh), p2wpkh);
ret.emplace_back(p2wpkh);
- ret.emplace_back(GetScriptForDestination(CScriptID(p2wpkh))); // P2SH-P2WPKH
+ ret.emplace_back(GetScriptForDestination(ScriptHash(p2wpkh))); // P2SH-P2WPKH
}
return ret;
}
@@ -572,7 +608,7 @@ public:
class SHDescriptor final : public DescriptorImpl
{
protected:
- std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(CScriptID(*script))); }
+ std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript* script, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(ScriptHash(*script))); }
public:
SHDescriptor(std::unique_ptr<DescriptorImpl> desc) : DescriptorImpl({}, std::move(desc), "sh") {}
};
@@ -654,7 +690,7 @@ std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
}
/** Parse a key path, being passed a split list of elements (the first element is ignored). */
-NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
+NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out, std::string& error)
{
for (size_t i = 1; i < split.size(); ++i) {
Span<const char> elem = split[i];
@@ -664,33 +700,60 @@ NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath&
hardened = true;
}
uint32_t p;
- if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || p > 0x7FFFFFFFUL) return false;
+ if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p)) {
+ 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);
+ return false;
+ }
out.push_back(p | (((uint32_t)hardened) << 31));
}
return true;
}
/** Parse a public key that excludes origin information. */
-std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
{
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
+ if (str.size() == 0) {
+ error = "No key provided";
+ return nullptr;
+ }
if (split.size() == 1) {
if (IsHex(str)) {
std::vector<unsigned char> data = ParseHex(str);
CPubKey pubkey(data);
- if (pubkey.IsFullyValid() && (permit_uncompressed || pubkey.IsCompressed())) return MakeUnique<ConstPubkeyProvider>(pubkey);
+ if (pubkey.IsFullyValid()) {
+ if (permit_uncompressed || pubkey.IsCompressed()) {
+ return MakeUnique<ConstPubkeyProvider>(pubkey);
+ } else {
+ error = "Uncompressed keys are not allowed";
+ return nullptr;
+ }
+ }
+ error = strprintf("Pubkey '%s' is invalid", str);
+ return nullptr;
}
CKey key = DecodeSecret(str);
- if (key.IsValid() && (permit_uncompressed || key.IsCompressed())) {
- CPubKey pubkey = key.GetPubKey();
- out.keys.emplace(pubkey.GetID(), key);
- return MakeUnique<ConstPubkeyProvider>(pubkey);
+ if (key.IsValid()) {
+ if (permit_uncompressed || key.IsCompressed()) {
+ CPubKey pubkey = key.GetPubKey();
+ out.keys.emplace(pubkey.GetID(), key);
+ return MakeUnique<ConstPubkeyProvider>(pubkey);
+ } else {
+ error = "Uncompressed keys are not allowed";
+ return nullptr;
+ }
}
}
CExtKey extkey = DecodeExtKey(str);
CExtPubKey extpubkey = DecodeExtPubKey(str);
- if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) return nullptr;
+ if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) {
+ error = strprintf("key '%s' is not valid", str);
+ return nullptr;
+ }
KeyPath path;
DeriveType type = DeriveType::NO;
if (split.back() == MakeSpan("*").first(1)) {
@@ -700,7 +763,7 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
split.pop_back();
type = DeriveType::HARDENED;
}
- if (!ParseKeyPath(split, path)) return nullptr;
+ if (!ParseKeyPath(split, path, error)) return nullptr;
if (extkey.key.IsValid()) {
extpubkey = extkey.Neuter();
out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
@@ -709,95 +772,154 @@ std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, boo
}
/** Parse a public key including origin information (if enabled). */
-std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out, std::string& error)
{
auto origin_split = Split(sp, ']');
- if (origin_split.size() > 2) return nullptr;
- if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out);
- if (origin_split[0].size() < 1 || origin_split[0][0] != '[') return nullptr;
+ if (origin_split.size() > 2) {
+ error = "Multiple ']' characters found for a single pubkey";
+ return nullptr;
+ }
+ if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out, error);
+ if (origin_split[0].size() < 1 || origin_split[0][0] != '[') {
+ error = strprintf("Key origin start '[ character expected but not found, got '%c' instead", origin_split[0][0]);
+ return nullptr;
+ }
auto slash_split = Split(origin_split[0].subspan(1), '/');
- if (slash_split[0].size() != 8) return nullptr;
+ if (slash_split[0].size() != 8) {
+ error = strprintf("Fingerprint is not 4 bytes (%u characters instead of 8 characters)", slash_split[0].size());
+ return nullptr;
+ }
std::string fpr_hex = std::string(slash_split[0].begin(), slash_split[0].end());
- if (!IsHex(fpr_hex)) return nullptr;
+ if (!IsHex(fpr_hex)) {
+ error = strprintf("Fingerprint '%s' is not hex", fpr_hex);
+ return nullptr;
+ }
auto fpr_bytes = ParseHex(fpr_hex);
KeyOriginInfo info;
static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes");
assert(fpr_bytes.size() == 4);
std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
- if (!ParseKeyPath(slash_split, info.path)) return nullptr;
- auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out);
+ if (!ParseKeyPath(slash_split, info.path, error)) return nullptr;
+ auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out, error);
if (!provider) return nullptr;
return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(provider));
}
/** Parse a script in a particular context. */
-std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
+std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out, std::string& error)
{
auto expr = Expr(sp);
if (Func("pk", expr)) {
- auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
+ auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
return MakeUnique<PKDescriptor>(std::move(pubkey));
}
if (Func("pkh", expr)) {
- auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out);
+ auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out, error);
if (!pubkey) return nullptr;
return MakeUnique<PKHDescriptor>(std::move(pubkey));
}
if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
- auto pubkey = ParsePubkey(expr, true, out);
+ auto pubkey = ParsePubkey(expr, true, out, error);
if (!pubkey) return nullptr;
return MakeUnique<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)) {
auto threshold = Expr(expr);
uint32_t thres;
std::vector<std::unique_ptr<PubkeyProvider>> providers;
- if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) return nullptr;
+ if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) {
+ error = strprintf("Multi threshold '%s' is not valid", std::string(threshold.begin(), threshold.end()).c_str());
+ return nullptr;
+ }
size_t script_size = 0;
while (expr.size()) {
- if (!Const(",", expr)) return nullptr;
+ if (!Const(",", expr)) {
+ error = strprintf("Multi: expected ',', got '%c'", expr[0]);
+ return nullptr;
+ }
auto arg = Expr(expr);
- auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out);
+ auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out, error);
if (!pk) return nullptr;
script_size += pk->GetSize() + 1;
providers.emplace_back(std::move(pk));
}
- if (providers.size() < 1 || providers.size() > 16 || thres < 1 || thres > providers.size()) return nullptr;
+ if (providers.size() < 1 || providers.size() > 16) {
+ 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;
+ }
if (ctx == ParseScriptContext::TOP) {
- if (providers.size() > 3) return nullptr; // Not more than 3 pubkeys for raw multisig
+ if (providers.size() > 3) {
+ error = strprintf("Cannot have %u pubkeys in bare multisig; only at most 3 pubkeys", providers.size());
+ return nullptr;
+ }
}
if (ctx == ParseScriptContext::P2SH) {
- if (script_size + 3 > 520) return nullptr; // Enforce P2SH script size limit
+ if (script_size + 3 > 520) {
+ error = strprintf("P2SH script is too large, %d bytes is larger than 520 bytes", script_size + 3);
+ return nullptr;
+ }
}
return MakeUnique<MultisigDescriptor>(thres, std::move(providers));
}
if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) {
- auto pubkey = ParsePubkey(expr, false, out);
+ auto pubkey = ParsePubkey(expr, false, out, error);
if (!pubkey) return nullptr;
return MakeUnique<WPKHDescriptor>(std::move(pubkey));
+ } else if (ctx == ParseScriptContext::P2WSH && Func("wpkh", expr)) {
+ error = "Cannot have wpkh within wsh";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
- auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
+ auto desc = ParseScript(expr, ParseScriptContext::P2SH, out, error);
if (!desc || expr.size()) return nullptr;
return MakeUnique<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::P2WSH && Func("wsh", expr)) {
- auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out);
+ auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out, error);
if (!desc || expr.size()) return nullptr;
return MakeUnique<WSHDescriptor>(std::move(desc));
+ } else if (ctx == ParseScriptContext::P2WSH && Func("wsh", expr)) {
+ error = "Cannot have wsh within wsh";
+ return nullptr;
}
if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
- if (!IsValidDestination(dest)) return nullptr;
+ if (!IsValidDestination(dest)) {
+ error = "Address is not valid";
+ return nullptr;
+ }
return MakeUnique<AddressDescriptor>(std::move(dest));
}
if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
std::string str(expr.begin(), expr.end());
- if (!IsHex(str)) return nullptr;
+ if (!IsHex(str)) {
+ error = "Raw script is not hex";
+ return nullptr;
+ }
auto bytes = ParseHex(str);
return MakeUnique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
}
+ if (ctx == ParseScriptContext::P2SH) {
+ error = "A function is needed within P2SH";
+ return nullptr;
+ } else if (ctx == ParseScriptContext::P2WSH) {
+ error = "A function is needed within P2WSH";
+ return nullptr;
+ }
+ error = strprintf("%s is not a valid descriptor function", std::string(expr.begin(), expr.end()));
return nullptr;
}
@@ -878,27 +1000,58 @@ 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& error, 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) {
+ error = "Multiple '#' symbols";
+ return false;
+ }
+ if (check_split.size() == 1 && require_checksum){
+ error = "Missing checksum";
+ return false;
+ }
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 (check_split[1].size() != 8) {
+ error = strprintf("Expected 8 character checksum, not %u characters", check_split[1].size());
+ return false;
+ }
+ }
+ auto checksum = DescriptorChecksum(check_split[0]);
+ if (checksum.empty()) {
+ error = "Invalid characters in payload";
+ return false;
+ }
+ if (check_split.size() == 2) {
+ if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) {
+ error = strprintf("Provided checksum '%s' does not match computed checksum '%s'", std::string(check_split[1].begin(), check_split[1].end()), checksum);
+ return false;
+ }
}
+ if (out_checksum) *out_checksum = std::move(checksum);
sp = check_split[0];
+ return true;
+}
- auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, bool require_checksum)
+{
+ Span<const char> sp(descriptor.data(), descriptor.size());
+ if (!CheckChecksum(sp, require_checksum, error)) return nullptr;
+ auto ret = ParseScript(sp, ParseScriptContext::TOP, out, error);
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;
+ std::string error;
+ Span<const char> sp(descriptor.data(), descriptor.size());
+ if (!CheckChecksum(sp, false, error, &ret)) return "";
+ return ret;
+}
+
std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
{
return InferScript(script, ParseScriptContext::TOP, provider);
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 907a102284..0195ca0939 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -7,6 +7,7 @@
#include <script/script.h>
#include <script/sign.h>
+#include <script/signingprovider.h>
#include <vector>
@@ -46,9 +47,9 @@ struct Descriptor {
*
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
* provider: the provider to query for private keys in case of hardened derivation.
- * output_script: the expanded scriptPubKeys will be put here.
+ * output_scripts: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
- * cache: vector which will be overwritten with cache data necessary to-evaluate the descriptor at this point without access to private keys.
+ * cache: vector which will be overwritten with cache data necessary to evaluate the descriptor at this point without access to private keys.
*/
virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const = 0;
@@ -56,10 +57,18 @@ struct Descriptor {
*
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
* cache: vector from which cached expansion data will be read.
- * output_script: the expanded scriptPubKeys will be put here.
+ * output_scripts: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
*/
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
+
+ /** Expand the private key for a descriptor at a specified position, if possible.
+ *
+ * pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
+ * provider: the provider to query for the private keys.
+ * out: any private keys available for the specified pos will be placed here.
+ */
+ virtual void ExpandPrivate(int pos, const SigningProvider& provider, FlatSigningProvider& out) const = 0;
};
/** Parse a descriptor string. Included private keys are put in out.
@@ -70,7 +79,15 @@ struct Descriptor {
* If a parse error occurs, or the checksum is missing/invalid, or anything
* else is wrong, nullptr is returned.
*/
-std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false);
+std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, std::string& error, 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.
*
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 95b25b4911..20fae2eebf 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -334,7 +334,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
opcode == OP_MOD ||
opcode == OP_LSHIFT ||
opcode == OP_RSHIFT)
- return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
+ return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes (CVE-2010-5137).
// With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch
if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
@@ -926,7 +926,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Drop the signature in pre-segwit scripts but not segwit scripts
if (sigversion == SigVersion::BASE) {
- int found = FindAndDelete(scriptCode, CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript() << vchSig);
if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
@@ -992,7 +992,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
valtype& vchSig = stacktop(-isig-k);
if (sigversion == SigVersion::BASE) {
- int found = FindAndDelete(scriptCode, CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript() << vchSig);
if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
@@ -1483,6 +1483,8 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
}
+ // scriptSig and scriptPubKey must be evaluated sequentially on the same stack
+ // rather than being simply concatenated (see CVE-2010-5141)
std::vector<std::vector<unsigned char> > stack, stackCopy;
if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror))
// serror is set
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
deleted file mode 100644
index 51bd2d6e9f..0000000000
--- a/src/script/ismine.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <script/ismine.h>
-
-#include <key.h>
-#include <keystore.h>
-#include <script/script.h>
-#include <script/sign.h>
-
-
-typedef std::vector<unsigned char> valtype;
-
-namespace {
-
-/**
- * This is an enum that tracks the execution context of a script, similar to
- * SigVersion in script/interpreter. It is separate however because we want to
- * distinguish between top-level scriptPubKey execution and P2SH redeemScript
- * execution (a distinction that has no impact on consensus rules).
- */
-enum class IsMineSigVersion
-{
- TOP = 0, //!< scriptPubKey execution
- P2SH = 1, //!< P2SH redeemScript
- WITNESS_V0 = 2, //!< P2WSH witness script execution
-};
-
-/**
- * This is an internal representation of isminetype + invalidity.
- * Its order is significant, as we return the max of all explored
- * possibilities.
- */
-enum class IsMineResult
-{
- NO = 0, //!< Not ours
- WATCH_ONLY = 1, //!< Included in watch-only balance
- SPENDABLE = 2, //!< Included in all balances
- INVALID = 3, //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
-};
-
-bool PermitsUncompressed(IsMineSigVersion sigversion)
-{
- return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
-}
-
-bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
-{
- for (const valtype& pubkey : pubkeys) {
- CKeyID keyID = CPubKey(pubkey).GetID();
- if (!keystore.HaveKey(keyID)) return false;
- }
- return true;
-}
-
-IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
-{
- IsMineResult ret = IsMineResult::NO;
-
- std::vector<valtype> vSolutions;
- txnouttype whichType = Solver(scriptPubKey, vSolutions);
-
- CKeyID keyID;
- switch (whichType)
- {
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
- break;
- case TX_PUBKEY:
- keyID = CPubKey(vSolutions[0]).GetID();
- if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
- return IsMineResult::INVALID;
- }
- if (keystore.HaveKey(keyID)) {
- ret = std::max(ret, IsMineResult::SPENDABLE);
- }
- break;
- case TX_WITNESS_V0_KEYHASH:
- {
- if (sigversion == IsMineSigVersion::WITNESS_V0) {
- // P2WPKH inside P2WSH is invalid.
- return IsMineResult::INVALID;
- }
- if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
- // We do not support bare witness outputs unless the P2SH version of it would be
- // acceptable as well. This protects against matching before segwit activates.
- // This also applies to the P2WSH case.
- break;
- }
- ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
- break;
- }
- case TX_PUBKEYHASH:
- keyID = CKeyID(uint160(vSolutions[0]));
- if (!PermitsUncompressed(sigversion)) {
- CPubKey pubkey;
- if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
- return IsMineResult::INVALID;
- }
- }
- if (keystore.HaveKey(keyID)) {
- ret = std::max(ret, IsMineResult::SPENDABLE);
- }
- break;
- case TX_SCRIPTHASH:
- {
- if (sigversion != IsMineSigVersion::TOP) {
- // P2SH inside P2WSH or P2SH is invalid.
- return IsMineResult::INVALID;
- }
- CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
- CScript subscript;
- if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
- }
- break;
- }
- case TX_WITNESS_V0_SCRIPTHASH:
- {
- if (sigversion == IsMineSigVersion::WITNESS_V0) {
- // P2WSH inside P2WSH is invalid.
- return IsMineResult::INVALID;
- }
- if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
- break;
- }
- uint160 hash;
- CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
- CScriptID scriptID = CScriptID(hash);
- CScript subscript;
- if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
- }
- break;
- }
-
- case TX_MULTISIG:
- {
- // Never treat bare multisig outputs as ours (they can still be made watchonly-though)
- if (sigversion == IsMineSigVersion::TOP) {
- break;
- }
-
- // Only consider transactions "mine" if we own ALL the
- // keys involved. Multi-signature transactions that are
- // partially owned (somebody else has a key that can spend
- // them) enable spend-out-from-under-you attacks, especially
- // in shared-wallet situations.
- std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
- if (!PermitsUncompressed(sigversion)) {
- for (size_t i = 0; i < keys.size(); i++) {
- if (keys[i].size() != 33) {
- return IsMineResult::INVALID;
- }
- }
- }
- if (HaveKeys(keys, keystore)) {
- ret = std::max(ret, IsMineResult::SPENDABLE);
- }
- break;
- }
- }
-
- if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
- ret = std::max(ret, IsMineResult::WATCH_ONLY);
- }
- return ret;
-}
-
-} // namespace
-
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
-{
- switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
- case IsMineResult::INVALID:
- case IsMineResult::NO:
- return ISMINE_NO;
- case IsMineResult::WATCH_ONLY:
- return ISMINE_WATCH_ONLY;
- case IsMineResult::SPENDABLE:
- return ISMINE_SPENDABLE;
- }
- assert(false);
-}
-
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
-{
- CScript script = GetScriptForDestination(dest);
- return IsMine(keystore, script);
-}
diff --git a/src/script/ismine.h b/src/script/ismine.h
deleted file mode 100644
index 601e70f709..0000000000
--- a/src/script/ismine.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_SCRIPT_ISMINE_H
-#define BITCOIN_SCRIPT_ISMINE_H
-
-#include <script/standard.h>
-
-#include <stdint.h>
-
-class CKeyStore;
-class CScript;
-
-/** IsMine() return codes */
-enum isminetype
-{
- ISMINE_NO = 0,
- ISMINE_WATCH_ONLY = 1,
- ISMINE_SPENDABLE = 2,
- ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
-};
-/** used for bitflags of isminetype */
-typedef uint8_t isminefilter;
-
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
-isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
-
-#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/script/keyorigin.h b/src/script/keyorigin.h
new file mode 100644
index 0000000000..610f233500
--- /dev/null
+++ b/src/script/keyorigin.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_SCRIPT_KEYORIGIN_H
+#define BITCOIN_SCRIPT_KEYORIGIN_H
+
+#include <serialize.h>
+#include <streams.h>
+#include <vector>
+
+struct KeyOriginInfo
+{
+ unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
+ std::vector<uint32_t> path;
+
+ friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
+ {
+ return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
+ }
+
+ ADD_SERIALIZE_METHODS;
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action)
+ {
+ READWRITE(fingerprint);
+ READWRITE(path);
+ }
+
+ void clear()
+ {
+ memset(fingerprint, 0, 4);
+ path.clear();
+ }
+};
+
+#endif // BITCOIN_SCRIPT_KEYORIGIN_H
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 982aa241e7..0666a385d1 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -5,7 +5,6 @@
#include <script/script.h>
-#include <tinyformat.h>
#include <util/strencodings.h>
const char* GetOpName(opcodetype opcode)
diff --git a/src/script/script.h b/src/script/script.h
index 1d8ddba2f2..6355b8a704 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -437,7 +437,9 @@ public:
explicit CScript(opcodetype b) { operator<<(b); }
explicit CScript(const CScriptNum& b) { operator<<(b); }
- explicit CScript(const std::vector<unsigned char>& b) { operator<<(b); }
+ // delete non-existent constructor to defend against future introduction
+ // e.g. via prevector
+ explicit CScript(const std::vector<unsigned char>& b) = delete;
CScript& operator<<(int64_t b) { return push_int64(b); }
@@ -581,13 +583,4 @@ struct CScriptWitness
std::string ToString() const;
};
-class CReserveScript
-{
-public:
- CScript reserveScript;
- virtual void KeepScript() {}
- CReserveScript() {}
- virtual ~CReserveScript() {}
-};
-
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 94005cf6f3..eaf5363bd7 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -5,7 +5,6 @@
#include <script/sigcache.h>
-#include <memusage.h>
#include <pubkey.h>
#include <random.h>
#include <uint256.h>
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 4af66008d6..0ed92e8d5b 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -8,6 +8,7 @@
#include <key.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
@@ -420,22 +421,10 @@ public:
}
};
-template<typename M, typename K, typename V>
-bool LookupHelper(const M& map, const K& key, V& value)
-{
- auto it = map.find(key);
- if (it != map.end()) {
- value = it->second;
- return true;
- }
- return false;
-}
-
}
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(32, 32);
const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR = DummySignatureCreator(33, 32);
-const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
bool IsSolvable(const SigningProvider& provider, const CScript& script)
{
@@ -456,43 +445,18 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
return false;
}
-bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
-{
- return m_provider->GetCScript(scriptid, script);
-}
-
-bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
-{
- return m_provider->GetPubKey(keyid, pubkey);
-}
-
-bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
+bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
{
- if (m_hide_secret) return false;
- return m_provider->GetKey(keyid, key);
-}
-
-bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
-{
- if (m_hide_origin) return false;
- return m_provider->GetKeyOrigin(keyid, info);
-}
-
-bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
-bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
-bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); }
-bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
-
-FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
-{
- FlatSigningProvider ret;
- ret.scripts = a.scripts;
- ret.scripts.insert(b.scripts.begin(), b.scripts.end());
- ret.pubkeys = a.pubkeys;
- ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
- ret.keys = a.keys;
- ret.keys.insert(b.keys.begin(), b.keys.end());
- ret.origins = a.origins;
- ret.origins.insert(b.origins.begin(), b.origins.end());
- return ret;
+ std::vector<valtype> solutions;
+ auto whichtype = Solver(script, solutions);
+ if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
+ if (whichtype == TX_SCRIPTHASH) {
+ auto h160 = uint160(solutions[0]);
+ CScript subscript;
+ if (provider.GetCScript(h160, subscript)) {
+ whichtype = Solver(subscript, solutions);
+ if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
+ }
+ }
+ return false;
}
diff --git a/src/script/sign.h b/src/script/sign.h
index 491fb54c45..0e751afd3b 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -10,6 +10,7 @@
#include <hash.h>
#include <pubkey.h>
#include <script/interpreter.h>
+#include <script/keyorigin.h>
#include <streams.h>
class CKey;
@@ -17,77 +18,10 @@ class CKeyID;
class CScript;
class CScriptID;
class CTransaction;
+class SigningProvider;
struct CMutableTransaction;
-struct KeyOriginInfo
-{
- unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path
- std::vector<uint32_t> path;
-
- friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
- {
- return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
- }
-
- ADD_SERIALIZE_METHODS;
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action)
- {
- READWRITE(fingerprint);
- READWRITE(path);
- }
-
- void clear()
- {
- memset(fingerprint, 0, 4);
- path.clear();
- }
-};
-
-/** An interface to be implemented by keystores that support signing. */
-class SigningProvider
-{
-public:
- virtual ~SigningProvider() {}
- virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
- virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
- virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
- virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
-};
-
-extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
-
-class HidingSigningProvider : public SigningProvider
-{
-private:
- const bool m_hide_secret;
- const bool m_hide_origin;
- const SigningProvider* m_provider;
-
-public:
- HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {}
- bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
- bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
- bool GetKey(const CKeyID& keyid, CKey& key) const override;
- bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
-};
-
-struct FlatSigningProvider final : public SigningProvider
-{
- std::map<CScriptID, CScript> scripts;
- std::map<CKeyID, CPubKey> pubkeys;
- std::map<CKeyID, KeyOriginInfo> origins;
- std::map<CKeyID, CKey> keys;
-
- bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
- bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
- bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
- bool GetKey(const CKeyID& keyid, CKey& key) const override;
-};
-
-FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
-
/** Interface for signature creators. */
class BaseSignatureCreator {
public:
@@ -232,4 +166,7 @@ void UpdateInput(CTxIn& input, const SignatureData& data);
* Solvability is unrelated to whether we consider this output to be ours. */
bool IsSolvable(const SigningProvider& provider, const CScript& script);
+/** Check whether a scriptPubKey is known to be segwit. */
+bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
+
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
new file mode 100644
index 0000000000..01757e2f65
--- /dev/null
+++ b/src/script/signingprovider.cpp
@@ -0,0 +1,199 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <script/keyorigin.h>
+#include <script/signingprovider.h>
+#include <script/standard.h>
+
+#include <util/system.h>
+
+const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
+
+template<typename M, typename K, typename V>
+bool LookupHelper(const M& map, const K& key, V& value)
+{
+ auto it = map.find(key);
+ if (it != map.end()) {
+ value = it->second;
+ return true;
+ }
+ return false;
+}
+
+bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
+{
+ return m_provider->GetCScript(scriptid, script);
+}
+
+bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
+{
+ return m_provider->GetPubKey(keyid, pubkey);
+}
+
+bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
+{
+ if (m_hide_secret) return false;
+ return m_provider->GetKey(keyid, key);
+}
+
+bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ if (m_hide_origin) return false;
+ return m_provider->GetKeyOrigin(keyid, info);
+}
+
+bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
+bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
+bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ std::pair<CPubKey, KeyOriginInfo> out;
+ bool ret = LookupHelper(origins, keyid, out);
+ if (ret) info = std::move(out.second);
+ return ret;
+}
+bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
+
+FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
+{
+ FlatSigningProvider ret;
+ ret.scripts = a.scripts;
+ ret.scripts.insert(b.scripts.begin(), b.scripts.end());
+ ret.pubkeys = a.pubkeys;
+ ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
+ ret.keys = a.keys;
+ ret.keys.insert(b.keys.begin(), b.keys.end());
+ ret.origins = a.origins;
+ ret.origins.insert(b.origins.begin(), b.origins.end());
+ return ret;
+}
+
+void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
+{
+ AssertLockHeld(cs_KeyStore);
+ CKeyID key_id = pubkey.GetID();
+ // This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
+ // outputs. Technically P2WPKH outputs don't have a redeemscript to be
+ // spent. However, our current IsMine logic requires the corresponding
+ // P2SH-P2WPKH redeemscript to be present in the wallet in order to accept
+ // payment even to P2WPKH outputs.
+ // Also note that having superfluous scripts in the keystore never hurts.
+ // They're only used to guide recursion in signing and IsMine logic - if
+ // a script is present but we can't do anything with it, it has no effect.
+ // "Implicitly" refers to fact that scripts are derived automatically from
+ // existing keys, and are present in memory, even without being explicitly
+ // loaded (e.g. from a file).
+ if (pubkey.IsCompressed()) {
+ CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id));
+ // This does not use AddCScript, as it may be overridden.
+ CScriptID id(script);
+ mapScripts[id] = std::move(script);
+ }
+}
+
+bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
+{
+ CKey key;
+ if (!GetKey(address, key)) {
+ return false;
+ }
+ vchPubKeyOut = key.GetPubKey();
+ return true;
+}
+
+bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
+{
+ LOCK(cs_KeyStore);
+ mapKeys[pubkey.GetID()] = key;
+ ImplicitlyLearnRelatedKeyScripts(pubkey);
+ return true;
+}
+
+bool FillableSigningProvider::HaveKey(const CKeyID &address) const
+{
+ LOCK(cs_KeyStore);
+ return mapKeys.count(address) > 0;
+}
+
+std::set<CKeyID> FillableSigningProvider::GetKeys() const
+{
+ LOCK(cs_KeyStore);
+ std::set<CKeyID> set_address;
+ for (const auto& mi : mapKeys) {
+ set_address.insert(mi.first);
+ }
+ return set_address;
+}
+
+bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const
+{
+ LOCK(cs_KeyStore);
+ KeyMap::const_iterator mi = mapKeys.find(address);
+ if (mi != mapKeys.end()) {
+ keyOut = mi->second;
+ return true;
+ }
+ return false;
+}
+
+bool FillableSigningProvider::AddCScript(const CScript& redeemScript)
+{
+ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
+ return error("FillableSigningProvider::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);
+
+ LOCK(cs_KeyStore);
+ mapScripts[CScriptID(redeemScript)] = redeemScript;
+ return true;
+}
+
+bool FillableSigningProvider::HaveCScript(const CScriptID& hash) const
+{
+ LOCK(cs_KeyStore);
+ return mapScripts.count(hash) > 0;
+}
+
+std::set<CScriptID> FillableSigningProvider::GetCScripts() const
+{
+ LOCK(cs_KeyStore);
+ std::set<CScriptID> set_script;
+ for (const auto& mi : mapScripts) {
+ set_script.insert(mi.first);
+ }
+ return set_script;
+}
+
+bool FillableSigningProvider::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
+{
+ LOCK(cs_KeyStore);
+ ScriptMap::const_iterator mi = mapScripts.find(hash);
+ if (mi != mapScripts.end())
+ {
+ redeemScriptOut = (*mi).second;
+ return true;
+ }
+ return false;
+}
+
+CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest)
+{
+ // Only supports destinations which map to single public keys, i.e. P2PKH,
+ // P2WPKH, and P2SH-P2WPKH.
+ if (auto id = boost::get<PKHash>(&dest)) {
+ return CKeyID(*id);
+ }
+ if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
+ return CKeyID(*witness_id);
+ }
+ if (auto script_hash = boost::get<ScriptHash>(&dest)) {
+ CScript script;
+ CScriptID script_id(*script_hash);
+ CTxDestination inner_dest;
+ if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) {
+ if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
+ return CKeyID(*inner_witness_id);
+ }
+ }
+ }
+ return CKeyID();
+}
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
new file mode 100644
index 0000000000..4eec2311d4
--- /dev/null
+++ b/src/script/signingprovider.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_SCRIPT_SIGNINGPROVIDER_H
+#define BITCOIN_SCRIPT_SIGNINGPROVIDER_H
+
+#include <key.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <script/standard.h>
+#include <sync.h>
+
+struct KeyOriginInfo;
+
+/** An interface to be implemented by keystores that support signing. */
+class SigningProvider
+{
+public:
+ virtual ~SigningProvider() {}
+ virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
+ virtual bool HaveCScript(const CScriptID &scriptid) const { return false; }
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
+ virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
+ virtual bool HaveKey(const CKeyID &address) const { return false; }
+ virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
+};
+
+extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
+
+class HidingSigningProvider : public SigningProvider
+{
+private:
+ const bool m_hide_secret;
+ const bool m_hide_origin;
+ const SigningProvider* m_provider;
+
+public:
+ HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {}
+ bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
+ bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
+ bool GetKey(const CKeyID& keyid, CKey& key) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+};
+
+struct FlatSigningProvider final : public SigningProvider
+{
+ std::map<CScriptID, CScript> scripts;
+ std::map<CKeyID, CPubKey> pubkeys;
+ std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
+ std::map<CKeyID, CKey> keys;
+
+ bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
+ bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
+ bool GetKey(const CKeyID& keyid, CKey& key) const override;
+};
+
+FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
+
+/** Fillable signing provider that keeps keys in an address->secret map */
+class FillableSigningProvider : public SigningProvider
+{
+protected:
+ mutable CCriticalSection cs_KeyStore;
+
+ using KeyMap = std::map<CKeyID, CKey>;
+ using ScriptMap = std::map<CScriptID, CScript>;
+
+ KeyMap mapKeys GUARDED_BY(cs_KeyStore);
+ ScriptMap mapScripts GUARDED_BY(cs_KeyStore);
+
+ void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
+
+public:
+ virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
+ virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
+ virtual bool HaveKey(const CKeyID &address) const override;
+ virtual std::set<CKeyID> GetKeys() const;
+ virtual bool GetKey(const CKeyID &address, CKey &keyOut) const override;
+ virtual bool AddCScript(const CScript& redeemScript);
+ virtual bool HaveCScript(const CScriptID &hash) const override;
+ virtual std::set<CScriptID> GetCScripts() const;
+ virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override;
+};
+
+/** Return the CKeyID of the key involved in a script (if there is a unique one). */
+CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest);
+
+#endif // BITCOIN_SCRIPT_SIGNINGPROVIDER_H
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 31bfd04b0f..fc6898f444 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -8,9 +8,6 @@
#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
-#include <util/system.h>
-#include <util/strencodings.h>
-
typedef std::vector<unsigned char> valtype;
@@ -19,6 +16,10 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+
+PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
+
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
{
CSHA256().Write(in.data(), in.size()).Finalize(begin());
@@ -162,17 +163,17 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
if (!pubKey.IsValid())
return false;
- addressRet = pubKey.GetID();
+ addressRet = PKHash(pubKey);
return true;
}
else if (whichType == TX_PUBKEYHASH)
{
- addressRet = CKeyID(uint160(vSolutions[0]));
+ addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
- addressRet = CScriptID(uint160(vSolutions[0]));
+ addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
WitnessV0KeyHash hash;
@@ -217,7 +218,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
if (!pubKey.IsValid())
continue;
- CTxDestination address = pubKey.GetID();
+ CTxDestination address = PKHash(pubKey);
addressRet.push_back(address);
}
@@ -250,13 +251,13 @@ public:
return false;
}
- bool operator()(const CKeyID &keyID) const {
+ bool operator()(const PKHash &keyID) const {
script->clear();
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}
- bool operator()(const CScriptID &scriptID) const {
+ bool operator()(const ScriptHash &scriptID) const {
script->clear();
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return true;
diff --git a/src/script/standard.h b/src/script/standard.h
index fc20fb6a08..e45e2d92cc 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -73,6 +73,22 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
+struct PKHash : public uint160
+{
+ PKHash() : uint160() {}
+ explicit PKHash(const uint160& hash) : uint160(hash) {}
+ explicit PKHash(const CPubKey& pubkey);
+ using uint160::uint160;
+};
+
+struct ScriptHash : public uint160
+{
+ ScriptHash() : uint160() {}
+ explicit ScriptHash(const uint160& hash) : uint160(hash) {}
+ explicit ScriptHash(const CScript& script);
+ using uint160::uint160;
+};
+
struct WitnessV0ScriptHash : public uint256
{
WitnessV0ScriptHash() : uint256() {}
@@ -113,14 +129,14 @@ struct WitnessUnknown
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
- * * CKeyID: TX_PUBKEYHASH destination (P2PKH)
- * * CScriptID: TX_SCRIPTHASH destination (P2SH)
+ * * PKHash: TX_PUBKEYHASH destination (P2PKH)
+ * * ScriptHash: TX_SCRIPTHASH destination (P2SH)
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
-typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
+typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
@@ -153,8 +169,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* multisig scripts, this populates the addressRet vector with the pubkey IDs
* and nRequiredRet with the n required to spend. For other destinations,
* addressRet is populated with a single value and nRequiredRet is set to 1.
- * Returns true if successful. Currently does not extract address from
- * pay-to-witness scripts.
+ * Returns true if successful.
*
* Note: this function confuses destinations (a subset of CScripts that are
* encodable as an address) with key identifiers (of keys involved in a