diff options
Diffstat (limited to 'src/script')
| -rw-r--r-- | src/script/bitcoinconsensus.cpp | 2 | ||||
| -rw-r--r-- | src/script/bitcoinconsensus.h | 2 | ||||
| -rw-r--r-- | src/script/descriptor.cpp | 566 | ||||
| -rw-r--r-- | src/script/descriptor.h | 102 | ||||
| -rw-r--r-- | src/script/interpreter.cpp | 8 | ||||
| -rw-r--r-- | src/script/interpreter.h | 2 | ||||
| -rw-r--r-- | src/script/ismine.cpp | 5 | ||||
| -rw-r--r-- | src/script/ismine.h | 2 | ||||
| -rw-r--r-- | src/script/script.cpp | 2 | ||||
| -rw-r--r-- | src/script/script.h | 2 | ||||
| -rw-r--r-- | src/script/script_error.cpp | 2 | ||||
| -rw-r--r-- | src/script/script_error.h | 2 | ||||
| -rw-r--r-- | src/script/sigcache.cpp | 2 | ||||
| -rw-r--r-- | src/script/sigcache.h | 2 | ||||
| -rw-r--r-- | src/script/sign.cpp | 138 | ||||
| -rw-r--r-- | src/script/sign.h | 86 | ||||
| -rw-r--r-- | src/script/standard.cpp | 59 | ||||
| -rw-r--r-- | src/script/standard.h | 7 |
18 files changed, 880 insertions, 111 deletions
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index e2370c5e5..01cfeb23f 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 5973808fa..c5dceac84 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp new file mode 100644 index 000000000..f366b99ec --- /dev/null +++ b/src/script/descriptor.cpp @@ -0,0 +1,566 @@ +// Copyright (c) 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/descriptor.h> + +#include <key_io.h> +#include <pubkey.h> +#include <script/script.h> +#include <script/standard.h> + +#include <span.h> +#include <util.h> +#include <utilstrencodings.h> + +#include <memory> +#include <string> +#include <vector> + +namespace { + +//////////////////////////////////////////////////////////////////////////// +// Internal representation // +//////////////////////////////////////////////////////////////////////////// + +typedef std::vector<uint32_t> KeyPath; + +std::string FormatKeyPath(const KeyPath& path) +{ + std::string ret; + for (auto i : path) { + ret += strprintf("/%i", (i << 1) >> 1); + if (i >> 31) ret += '\''; + } + return ret; +} + +/** Interface for public key objects in descriptors. */ +struct PubkeyProvider +{ + virtual ~PubkeyProvider() = default; + + /** Derive a public key. */ + virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const = 0; + + /** Whether this represent multiple public keys at different positions. */ + virtual bool IsRange() const = 0; + + /** Get the size of the generated public key(s) in bytes (33 or 65). */ + virtual size_t GetSize() const = 0; + + /** Get the descriptor string form. */ + virtual std::string ToString() const = 0; + + /** Get the descriptor string form including private data (if available in arg). */ + virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0; +}; + +/** An object representing a parsed constant public key in a descriptor. */ +class ConstPubkeyProvider final : public PubkeyProvider +{ + CPubKey m_pubkey; + +public: + ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {} + bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override + { + out = m_pubkey; + return true; + } + bool IsRange() const override { return false; } + size_t GetSize() const override { return m_pubkey.size(); } + std::string ToString() const override { return HexStr(m_pubkey.begin(), m_pubkey.end()); } + bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override + { + CKey key; + if (!arg.GetKey(m_pubkey.GetID(), key)) return false; + ret = EncodeSecret(key); + return true; + } +}; + +enum class DeriveType { + NO, + UNHARDENED, + HARDENED, +}; + +/** An object representing a parsed extended public key in a descriptor. */ +class BIP32PubkeyProvider final : public PubkeyProvider +{ + CExtPubKey m_extkey; + KeyPath m_path; + DeriveType m_derive; + + bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const + { + CKey key; + if (!arg.GetKey(m_extkey.pubkey.GetID(), key)) return false; + ret.nDepth = m_extkey.nDepth; + std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + 4, ret.vchFingerprint); + ret.nChild = m_extkey.nChild; + ret.chaincode = m_extkey.chaincode; + ret.key = key; + return true; + } + + bool IsHardened() const + { + if (m_derive == DeriveType::HARDENED) return true; + for (auto entry : m_path) { + if (entry >> 31) return true; + } + return false; + } + +public: + BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {} + bool IsRange() const override { return m_derive != DeriveType::NO; } + size_t GetSize() const override { return 33; } + bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override + { + if (IsHardened()) { + CExtKey key; + if (!GetExtKey(arg, key)) return false; + for (auto entry : m_path) { + key.Derive(key, entry); + } + if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos); + if (m_derive == DeriveType::HARDENED) key.Derive(key, pos | 0x80000000UL); + out = key.Neuter().pubkey; + } else { + // TODO: optimize by caching + CExtPubKey key = m_extkey; + for (auto entry : m_path) { + key.Derive(key, entry); + } + if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos); + assert(m_derive != DeriveType::HARDENED); + out = key.pubkey; + } + return true; + } + std::string ToString() const override + { + std::string ret = EncodeExtPubKey(m_extkey) + FormatKeyPath(m_path); + if (IsRange()) { + ret += "/*"; + if (m_derive == DeriveType::HARDENED) ret += '\''; + } + return ret; + } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override + { + CExtKey key; + if (!GetExtKey(arg, key)) return false; + out = EncodeExtKey(key) + FormatKeyPath(m_path); + if (IsRange()) { + out += "/*"; + if (m_derive == DeriveType::HARDENED) out += '\''; + } + return true; + } +}; + +/** A parsed addr(A) descriptor. */ +class AddressDescriptor final : public Descriptor +{ + CTxDestination m_destination; + +public: + AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {} + + bool IsRange() const override { return false; } + std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; } + bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override + { + output_scripts = std::vector<CScript>{GetScriptForDestination(m_destination)}; + return true; + } +}; + +/** A parsed raw(H) descriptor. */ +class RawDescriptor final : public Descriptor +{ + CScript m_script; + +public: + RawDescriptor(CScript script) : m_script(std::move(script)) {} + + bool IsRange() const override { return false; } + std::string ToString() const override { return "raw(" + HexStr(m_script.begin(), m_script.end()) + ")"; } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; } + bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override + { + output_scripts = std::vector<CScript>{m_script}; + return true; + } +}; + +/** A parsed pk(P), pkh(P), or wpkh(P) descriptor. */ +class SingleKeyDescriptor final : public Descriptor +{ + const std::function<CScript(const CPubKey&)> m_script_fn; + const std::string m_fn_name; + std::unique_ptr<PubkeyProvider> m_provider; + +public: + SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {} + + bool IsRange() const override { return m_provider->IsRange(); } + std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override + { + std::string ret; + if (!m_provider->ToPrivateString(arg, ret)) return false; + out = m_fn_name + "(" + std::move(ret) + ")"; + return true; + } + bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override + { + CPubKey key; + if (!m_provider->GetPubKey(pos, arg, key)) return false; + output_scripts = std::vector<CScript>{m_script_fn(key)}; + out.pubkeys.emplace(key.GetID(), std::move(key)); + return true; + } +}; + +CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); } +CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); } +CScript P2WPKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(WitnessV0KeyHash(pubkey.GetID())); } + +/** A parsed multi(...) descriptor. */ +class MultisigDescriptor : public Descriptor +{ + int m_threshold; + std::vector<std::unique_ptr<PubkeyProvider>> m_providers; + +public: + MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : m_threshold(threshold), m_providers(std::move(providers)) {} + + bool IsRange() const override + { + for (const auto& p : m_providers) { + if (p->IsRange()) return true; + } + return false; + } + + std::string ToString() const override + { + std::string ret = strprintf("multi(%i", m_threshold); + for (const auto& p : m_providers) { + ret += "," + p->ToString(); + } + return std::move(ret) + ")"; + } + + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override + { + std::string ret = strprintf("multi(%i", m_threshold); + for (const auto& p : m_providers) { + std::string sub; + if (!p->ToPrivateString(arg, sub)) return false; + ret += "," + std::move(sub); + } + out = std::move(ret) + ")"; + return true; + } + + bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override + { + std::vector<CPubKey> pubkeys; + pubkeys.reserve(m_providers.size()); + for (const auto& p : m_providers) { + CPubKey key; + if (!p->GetPubKey(pos, arg, key)) return false; + pubkeys.push_back(key); + } + for (const CPubKey& key : pubkeys) { + out.pubkeys.emplace(key.GetID(), std::move(key)); + } + output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)}; + return true; + } +}; + +/** A parsed sh(S) or wsh(S) descriptor. */ +class ConvertorDescriptor : public Descriptor +{ + const std::function<CScript(const CScript&)> m_convert_fn; + const std::string m_fn_name; + std::unique_ptr<Descriptor> m_descriptor; + +public: + ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {} + + bool IsRange() const override { return m_descriptor->IsRange(); } + std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override + { + std::string ret; + if (!m_descriptor->ToPrivateString(arg, ret)) return false; + out = m_fn_name + "(" + std::move(ret) + ")"; + return true; + } + bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override + { + std::vector<CScript> sub; + if (!m_descriptor->Expand(pos, arg, sub, out)) return false; + output_scripts.clear(); + for (const auto& script : sub) { + CScriptID id(script); + out.scripts.emplace(CScriptID(script), script); + output_scripts.push_back(m_convert_fn(script)); + } + return true; + } +}; + +CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); } +CScript ConvertP2WSH(const CScript& script) { return GetScriptForDestination(WitnessV0ScriptHash(script)); } + +/** A parsed combo(P) descriptor. */ +class ComboDescriptor final : public Descriptor +{ + std::unique_ptr<PubkeyProvider> m_provider; + +public: + ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {} + + bool IsRange() const override { return m_provider->IsRange(); } + std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override + { + std::string ret; + if (!m_provider->ToPrivateString(arg, ret)) return false; + out = "combo(" + std::move(ret) + ")"; + return true; + } + bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override + { + CPubKey key; + if (!m_provider->GetPubKey(pos, arg, key)) return false; + CKeyID keyid = key.GetID(); + { + CScript p2pk = GetScriptForRawPubKey(key); + CScript p2pkh = GetScriptForDestination(keyid); + output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)}; + out.pubkeys.emplace(keyid, key); + } + if (key.IsCompressed()) { + CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid)); + CScriptID p2wpkh_id(p2wpkh); + CScript p2sh_p2wpkh = GetScriptForDestination(p2wpkh_id); + out.scripts.emplace(p2wpkh_id, p2wpkh); + output_scripts.push_back(std::move(p2wpkh)); + output_scripts.push_back(std::move(p2sh_p2wpkh)); + } + return true; + } +}; + +//////////////////////////////////////////////////////////////////////////// +// Parser // +//////////////////////////////////////////////////////////////////////////// + +enum class ParseScriptContext { + TOP, + P2SH, + P2WSH, +}; + +/** Parse a constant. If succesful, 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 succesful, 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 ignored). */ +bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out) +{ + for (size_t i = 1; i < split.size(); ++i) { + Span<const char> elem = split[i]; + bool hardened = false; + if (elem.size() > 0 && (elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) { + elem = elem.first(elem.size() - 1); + hardened = true; + } + uint32_t p; + if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || p > 0x7FFFFFFFUL) return false; + out.push_back(p | (((uint32_t)hardened) << 31)); + } + return true; +} + +std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out) +{ + auto split = Split(sp, '/'); + std::string str(split[0].begin(), split[0].end()); + 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); + } + 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); + } + } + CExtKey extkey = DecodeExtKey(str); + CExtPubKey extpubkey = DecodeExtPubKey(str); + if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) return nullptr; + KeyPath path; + DeriveType type = DeriveType::NO; + if (split.back() == MakeSpan("*").first(1)) { + split.pop_back(); + type = DeriveType::UNHARDENED; + } else if (split.back() == MakeSpan("*'").first(2) || split.back() == MakeSpan("*h").first(2)) { + split.pop_back(); + type = DeriveType::HARDENED; + } + if (!ParseKeyPath(split, path)) return nullptr; + if (extkey.key.IsValid()) { + extpubkey = extkey.Neuter(); + out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key); + } + return MakeUnique<BIP32PubkeyProvider>(extpubkey, std::move(path), type); +} + +/** Parse a script in a particular context. */ +std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out) +{ + auto expr = Expr(sp); + if (Func("pk", expr)) { + auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out); + if (!pubkey) return nullptr; + return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKGetScript, "pk"); + } + if (Func("pkh", expr)) { + auto pubkey = ParsePubkey(expr, ctx != ParseScriptContext::P2WSH, out); + if (!pubkey) return nullptr; + return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2PKHGetScript, "pkh"); + } + if (ctx == ParseScriptContext::TOP && Func("combo", expr)) { + auto pubkey = ParsePubkey(expr, true, out); + if (!pubkey) return nullptr; + return MakeUnique<ComboDescriptor>(std::move(pubkey)); + } + 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; + size_t script_size = 0; + while (expr.size()) { + if (!Const(",", expr)) return nullptr; + auto arg = Expr(expr); + auto pk = ParsePubkey(arg, ctx != ParseScriptContext::P2WSH, out); + 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 (ctx == ParseScriptContext::TOP) { + if (providers.size() > 3) return nullptr; // Not more than 3 pubkeys for raw multisig + } + if (ctx == ParseScriptContext::P2SH) { + if (script_size + 3 > 520) return nullptr; // Enforce P2SH script size limit + } + return MakeUnique<MultisigDescriptor>(thres, std::move(providers)); + } + if (ctx != ParseScriptContext::P2WSH && Func("wpkh", expr)) { + auto pubkey = ParsePubkey(expr, false, out); + if (!pubkey) return nullptr; + return MakeUnique<SingleKeyDescriptor>(std::move(pubkey), P2WPKHGetScript, "wpkh"); + } + if (ctx == ParseScriptContext::TOP && Func("sh", expr)) { + auto desc = ParseScript(expr, ParseScriptContext::P2SH, out); + if (!desc || expr.size()) return nullptr; + return MakeUnique<ConvertorDescriptor>(std::move(desc), ConvertP2SH, "sh"); + } + if (ctx != ParseScriptContext::P2WSH && Func("wsh", expr)) { + auto desc = ParseScript(expr, ParseScriptContext::P2WSH, out); + if (!desc || expr.size()) return nullptr; + return MakeUnique<ConvertorDescriptor>(std::move(desc), ConvertP2WSH, "wsh"); + } + if (ctx == ParseScriptContext::TOP && Func("addr", expr)) { + CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end())); + if (!IsValidDestination(dest)) 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; + auto bytes = ParseHex(str); + return MakeUnique<RawDescriptor>(CScript(bytes.begin(), bytes.end())); + } + return nullptr; +} + +} // namespace + +std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out) +{ + Span<const char> sp(descriptor.data(), descriptor.size()); + auto ret = ParseScript(sp, ParseScriptContext::TOP, out); + if (sp.size() == 0 && ret) return ret; + return nullptr; +} diff --git a/src/script/descriptor.h b/src/script/descriptor.h new file mode 100644 index 000000000..e079c72e9 --- /dev/null +++ b/src/script/descriptor.h @@ -0,0 +1,102 @@ +// Copyright (c) 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_DESCRIPTOR_H +#define BITCOIN_SCRIPT_DESCRIPTOR_H + +#include <script/script.h> +#include <script/sign.h> + +#include <vector> + +// Descriptors are strings that describe a set of scriptPubKeys, together with +// all information necessary to solve them. By combining all information into +// one, they avoid the need to separately import keys and scripts. +// +// Descriptors may be ranged, which occurs when the public keys inside are +// specified in the form of HD chains (xpubs). +// +// Descriptors always represent public information - public keys and scripts - +// but in cases where private keys need to be conveyed along with a descriptor, +// they can be included inside by changing public keys to private keys (WIF +// format), and changing xpubs by xprvs. +// +// 1. Examples +// +// A P2PK descriptor with a fixed public key: +// - pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798) +// +// A P2SH-P2WSH-P2PKH descriptor with a fixed public key: +// - sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13))) +// +// A bare 1-of-2 multisig descriptor: +// - multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc) +// +// A chain of P2PKH outputs (this needs the corresponding private key to derive): +// - pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2/*) +// +// 2. Grammar description: +// +// X: xpub or xprv encoded extended key +// I: decimal encoded integer +// H: Hex encoded byte array +// A: Address in P2PKH, P2SH, or Bech32 encoding +// +// S (Scripts): +// * pk(P): Pay-to-pubkey (P2PK) output for public key P. +// * pkh(P): Pay-to-pubkey-hash (P2PKH) output for public key P. +// * wpkh(P): Pay-to-witness-pubkey-hash (P2WPKH) output for public key P. +// * sh(S): Pay-to-script-hash (P2SH) output for script S +// * wsh(S): Pay-to-witness-script-hash (P2WSH) output for script S +// * combo(P): combination of P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH for public key P. +// * multi(I,L): k-of-n multisig for given public keys +// * addr(A): Output to address +// * raw(H): scriptPubKey with raw bytes +// +// P (Public keys): +// * H: fixed public key (or WIF-encoded private key) +// * E: extended public key +// * E/*: (ranged) all unhardened direct children of an extended public key +// * E/*': (ranged) all hardened direct children of an extended public key +// +// L (Comma-separated lists of public keys): +// * P +// * L,P +// +// E (Extended public keys): +// * X +// * E/I: unhardened child +// * E/I': hardened child +// * E/Ih: hardened child (alternative notation) +// +// The top level is S. + +/** Interface for parsed descriptor objects. */ +struct Descriptor { + virtual ~Descriptor() = default; + + /** Whether the expansion of this descriptor depends on the position. */ + virtual bool IsRange() const = 0; + + /** Convert the descriptor back to a string, undoing parsing. */ + virtual std::string ToString() const = 0; + + /** Convert the descriptor to a private string. This fails if the provided provider does not have the relevant private keys. */ + virtual bool ToPrivateString(const SigningProvider& provider, std::string& out) const = 0; + + /** Expand a descriptor at a specified position. + * + * 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. + * out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider). + */ + virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0; +}; + +/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */ +std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out); + +#endif // BITCOIN_SCRIPT_DESCRIPTOR_H + diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 1936d44cd..95b25b491 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. @@ -1588,7 +1588,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_success(serror); } -size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness, int flags) +size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness) { if (witversion == 0) { if (witprogram.size() == WITNESS_V0_KEYHASH_SIZE) @@ -1616,7 +1616,7 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, int witnessversion; std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty); } if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) { @@ -1628,7 +1628,7 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, } CScript subscript(data.begin(), data.end()); if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) { - return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty); } } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 2d21aa81d..276ff9a58 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 8c2686648..1433ebf42 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. @@ -60,8 +60,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineResult ret = IsMineResult::NO; std::vector<valtype> vSolutions; - txnouttype whichType; - Solver(scriptPubKey, whichType, vSolutions); + txnouttype whichType = Solver(scriptPubKey, vSolutions); CKeyID keyID; switch (whichType) diff --git a/src/script/ismine.h b/src/script/ismine.h index 4246da49f..601e70f70 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/script.cpp b/src/script/script.cpp index c84c7b8ec..9bdf3ed80 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/script.h b/src/script/script.h index a4f377dd9..00065a24b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index ceda74058..9d7deffc7 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/script_error.h b/src/script/script_error.h index 6982a087f..400f63ff0 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2016 The Bitcoin Core developers +// 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. diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 9638b12f9..68f054229 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 1309d57cc..807b61b54 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. diff --git a/src/script/sign.cpp b/src/script/sign.cpp index d10b1c4fd..d77991042 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. @@ -11,7 +11,6 @@ #include <script/standard.h> #include <uint256.h> - typedef std::vector<unsigned char> valtype; MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} @@ -51,10 +50,6 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) { - if (provider.GetPubKey(address, pubkey)) { - sigdata.misc_pubkeys.emplace(pubkey.GetID(), pubkey); - return true; - } // Look for pubkey in all partial sigs const auto it = sigdata.signatures.find(address); if (it != sigdata.signatures.end()) { @@ -64,7 +59,15 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c // Look for pubkey in pubkey list const auto& pk_it = sigdata.misc_pubkeys.find(address); if (pk_it != sigdata.misc_pubkeys.end()) { - pubkey = pk_it->second; + pubkey = pk_it->second.first; + return true; + } + // Query the underlying provider + if (provider.GetPubKey(address, pubkey)) { + KeyOriginInfo info; + if (provider.GetKeyOrigin(address, info)) { + sigdata.misc_pubkeys.emplace(address, std::make_pair(pubkey, std::move(info))); + } return true; } return false; @@ -102,8 +105,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator std::vector<unsigned char> sig; std::vector<valtype> vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) - return false; + whichTypeRet = Solver(scriptPubKey, vSolutions); switch (whichTypeRet) { @@ -234,7 +236,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato return sigdata.complete; } -bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash) +bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash) { // if this input has a final scriptsig or scriptwitness, don't do anything with it if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) { @@ -242,21 +244,48 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t } // Fill SignatureData with input info + SignatureData sigdata; input.FillSignatureData(sigdata); // Get UTXO + bool require_witness_sig = false; CTxOut utxo; if (input.non_witness_utxo) { + // If we're taking our information from a non-witness UTXO, verify that it matches the prevout. + if (input.non_witness_utxo->GetHash() != tx.vin[index].prevout.hash) return false; + // If both witness and non-witness UTXO are provided, verify that they match. This check shouldn't + // matter, as the PSBT deserializer enforces only one of both is provided, and the only way both + // can be present is when they're added simultaneously by FillPSBT (in which case they always match). + // Still, check in order to not rely on callers to enforce this. + if (!input.witness_utxo.IsNull() && input.non_witness_utxo->vout[tx.vin[index].prevout.n] != input.witness_utxo) return false; utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n]; } else if (!input.witness_utxo.IsNull()) { utxo = input.witness_utxo; + // When we're taking our information from a witness UTXO, we can't verify it is actually data from + // the output being spent. This is safe in case a witness signature is produced (which includes this + // information directly in the hash), but not for non-witness signatures. Remember that we require + // a witness signature in this situation. + require_witness_sig = true; } else { return false; } MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash); + sigdata.witness = false; bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata); + // Verify that a witness signature was produced in case one was required. + if (require_witness_sig && !sigdata.witness) return false; input.FromSignatureData(sigdata); + + // If both UTXO types are present, drop the unnecessary one. + if (input.non_witness_utxo && !input.witness_utxo.IsNull()) { + if (sigdata.witness) { + input.non_witness_utxo = nullptr; + } else { + input.witness_utxo.SetNull(); + } + } + return sig_complete; } @@ -314,9 +343,8 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI } // Get scripts - txnouttype script_type; std::vector<std::vector<unsigned char>> solutions; - Solver(txout.scriptPubKey, script_type, solutions); + txnouttype script_type = Solver(txout.scriptPubKey, solutions); SigVersion sigversion = SigVersion::BASE; CScript next_script = txout.scriptPubKey; @@ -327,7 +355,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI next_script = std::move(redeem_script); // Get redeemScript type - Solver(next_script, script_type, solutions); + script_type = Solver(next_script, solutions); stack.script.pop_back(); } if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) { @@ -337,7 +365,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI next_script = std::move(witness_script); // Get witnessScript type - Solver(next_script, script_type, solutions); + script_type = Solver(next_script, solutions); stack.witness.pop_back(); stack.script = std::move(stack.witness); stack.witness.clear(); @@ -418,28 +446,44 @@ public: const DummySignatureChecker DUMMY_CHECKER; class DummySignatureCreator final : public BaseSignatureCreator { +private: + char m_r_len = 32; + char m_s_len = 32; public: - DummySignatureCreator() {} + DummySignatureCreator(char r_len, char s_len) : m_r_len(r_len), m_s_len(s_len) {} const BaseSignatureChecker& Checker() const override { return DUMMY_CHECKER; } bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override { // Create a dummy signature that is a valid DER-encoding - vchSig.assign(72, '\000'); + vchSig.assign(m_r_len + m_s_len + 7, '\000'); vchSig[0] = 0x30; - vchSig[1] = 69; + vchSig[1] = m_r_len + m_s_len + 4; vchSig[2] = 0x02; - vchSig[3] = 33; + vchSig[3] = m_r_len; vchSig[4] = 0x01; - vchSig[4 + 33] = 0x02; - vchSig[5 + 33] = 32; - vchSig[6 + 33] = 0x01; - vchSig[6 + 33 + 32] = SIGHASH_ALL; + vchSig[4 + m_r_len] = 0x02; + vchSig[5 + m_r_len] = m_s_len; + vchSig[6 + m_r_len] = 0x01; + vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL; return true; } }; + +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(); +} + +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) @@ -454,13 +498,13 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script) static_assert(STANDARD_SCRIPT_VERIFY_FLAGS & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, "IsSolvable requires standard script flags to include WITNESS_PUBKEYTYPE"); if (ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, script, sigs)) { // VerifyScript check is just defensive, and should never fail. - assert(VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, DUMMY_CHECKER)); + bool verified = VerifyScript(sigs.scriptSig, script, &sigs.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, DUMMY_CHECKER); + assert(verified); return true; } return false; } - bool PartiallySignedTransaction::IsNull() const { return !tx && inputs.empty() && outputs.empty() && unknown.empty(); @@ -512,7 +556,7 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const sigdata.witness_script = witness_script; } for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first); + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } } @@ -540,6 +584,9 @@ void PSBTInput::FromSignatureData(const SignatureData& sigdata) if (witness_script.empty() && !sigdata.witness_script.empty()) { witness_script = sigdata.witness_script; } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } } void PSBTInput::Merge(const PSBTInput& input) @@ -581,7 +628,7 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const sigdata.witness_script = witness_script; } for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first); + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } } @@ -593,6 +640,9 @@ void PSBTOutput::FromSignatureData(const SignatureData& sigdata) if (witness_script.empty() && !sigdata.witness_script.empty()) { witness_script = sigdata.witness_script; } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } } bool PSBTOutput::IsNull() const @@ -609,12 +659,40 @@ void PSBTOutput::Merge(const PSBTOutput& output) if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script; } -bool PublicOnlySigningProvider::GetCScript(const CScriptID &scriptid, CScript& script) const +bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return m_provider->GetCScript(scriptid, script); } -bool PublicOnlySigningProvider::GetPubKey(const CKeyID &address, CPubKey& pubkey) const +bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const +{ + return m_provider->GetPubKey(keyid, pubkey); +} + +bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { - return m_provider->GetPubKey(address, pubkey); + 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::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()); + return ret; } diff --git a/src/script/sign.h b/src/script/sign.h index d12d0b587..18b732099 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. @@ -20,6 +20,12 @@ class CTransaction; struct CMutableTransaction; +struct KeyOriginInfo +{ + unsigned char fingerprint[4]; + std::vector<uint32_t> path; +}; + /** An interface to be implemented by keystores that support signing. */ class SigningProvider { @@ -28,21 +34,39 @@ public: 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& id, KeyOriginInfo& info) const { return false; } }; extern const SigningProvider& DUMMY_SIGNING_PROVIDER; -class PublicOnlySigningProvider : public SigningProvider +class HidingSigningProvider : public SigningProvider { private: + const bool m_hide_secret; + const bool m_hide_origin; const SigningProvider* m_provider; public: - PublicOnlySigningProvider(const SigningProvider* provider) : m_provider(provider) {} - bool GetCScript(const CScriptID &scriptid, CScript& script) const; - bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const; + 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, CKey> keys; + + 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; }; +FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b); + /** Interface for signature creators. */ class BaseSignatureCreator { public: @@ -67,8 +91,10 @@ public: bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; -/** A signature creator that just produces 72-byte empty signatures. */ +/** A signature creator that just produces 71-byte empty signatures. */ extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR; +/** A signature creator that just produces 72-byte empty signatures. */ +extern const BaseSignatureCreator& DUMMY_MAXIMUM_SIGNATURE_CREATOR; typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair; @@ -83,7 +109,7 @@ struct SignatureData { CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. - std::map<CKeyID, CPubKey> misc_pubkeys; + std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys; SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} @@ -140,7 +166,7 @@ void UnserializeFromVector(Stream& s, X&... args) // Deserialize HD keypaths into a map template<typename Stream> -void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths) +void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths) { // Make sure that the key is the size of pubkey + 1 if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) { @@ -157,25 +183,31 @@ void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std // Read in key path uint64_t value_len = ReadCompactSize(s); - std::vector<uint32_t> keypath; - for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) { + if (value_len % 4 || value_len == 0) { + throw std::ios_base::failure("Invalid length for HD key path"); + } + + KeyOriginInfo keypath; + s >> keypath.fingerprint; + for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) { uint32_t index; s >> index; - keypath.push_back(index); + keypath.path.push_back(index); } // Add to map - hd_keypaths.emplace(pubkey, keypath); + hd_keypaths.emplace(pubkey, std::move(keypath)); } // Serialize HD keypaths to a stream from a map template<typename Stream> -void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths, uint8_t type) +void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type) { for (auto keypath_pair : hd_keypaths) { SerializeToVector(s, type, MakeSpan(keypath_pair.first)); - WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t)); - for (auto& path : keypath_pair.second) { + WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t)); + s << keypath_pair.second.fingerprint; + for (const auto& path : keypath_pair.second.path) { s << path; } } @@ -190,7 +222,7 @@ struct PSBTInput CScript witness_script; CScript final_script_sig; CScriptWitness final_script_witness; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<CKeyID, SigPair> partial_sigs; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; int sighash_type = 0; @@ -208,7 +240,8 @@ struct PSBTInput // If there is a non-witness utxo, then don't add the witness one. if (non_witness_utxo) { SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO); - SerializeToVector(s, non_witness_utxo); + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + SerializeToVector(os, non_witness_utxo); } else if (!witness_utxo.IsNull()) { SerializeToVector(s, PSBT_IN_WITNESS_UTXO); SerializeToVector(s, witness_utxo); @@ -282,13 +315,17 @@ struct PSBTInput // Do stuff based on type switch(type) { case PSBT_IN_NON_WITNESS_UTXO: + { if (non_witness_utxo) { throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided"); } else if (key.size() != 1) { throw std::ios_base::failure("Non-witness utxo key is more than one byte type"); } - UnserializeFromVector(s, non_witness_utxo); + // Set the stream to unserialize with witness since this is always a valid network transaction + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS); + UnserializeFromVector(os, non_witness_utxo); break; + } case PSBT_IN_WITNESS_UTXO: if (!witness_utxo.IsNull()) { throw std::ios_base::failure("Duplicate Key, input witness utxo already provided"); @@ -398,7 +435,7 @@ struct PSBTOutput { CScript redeem_script; CScript witness_script; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; bool IsNull() const; @@ -532,7 +569,8 @@ struct PartiallySignedTransaction SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX); // Write serialized tx to a stream - SerializeToVector(s, *tx); + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + SerializeToVector(os, *tx); // Write the unknown things for (auto& entry : unknown) { @@ -586,7 +624,9 @@ struct PartiallySignedTransaction throw std::ios_base::failure("Global unsigned tx key is more than one byte type"); } CMutableTransaction mtx; - UnserializeFromVector(s, mtx); + // Set the stream to serialize with non-witness since this should always be non-witness + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + UnserializeFromVector(os, mtx); tx = std::move(mtx); // Make sure that all scriptSigs and scriptWitnesses are empty for (const CTxIn& txin : tx->vin) { @@ -663,8 +703,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); -/** Signs a PSBTInput */ -bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1); +/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */ +bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash = SIGHASH_ALL); /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index d7b172479..08ba1b1e0 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. @@ -87,7 +87,7 @@ static bool MatchMultisig(const CScript& script, unsigned int& required, std::ve return (it + 1 == script.end()); } -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet) +txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet) { vSolutionsRet.clear(); @@ -95,33 +95,28 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL if (scriptPubKey.IsPayToScriptHash()) { - typeRet = TX_SCRIPTHASH; std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); vSolutionsRet.push_back(hashBytes); - return true; + return TX_SCRIPTHASH; } int witnessversion; std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { - typeRet = TX_WITNESS_V0_KEYHASH; vSolutionsRet.push_back(witnessprogram); - return true; + return TX_WITNESS_V0_KEYHASH; } if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { - typeRet = TX_WITNESS_V0_SCRIPTHASH; vSolutionsRet.push_back(witnessprogram); - return true; + return TX_WITNESS_V0_SCRIPTHASH; } if (witnessversion != 0) { - typeRet = TX_WITNESS_UNKNOWN; vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); vSolutionsRet.push_back(std::move(witnessprogram)); - return true; + return TX_WITNESS_UNKNOWN; } - typeRet = TX_NONSTANDARD; - return false; + return TX_NONSTANDARD; } // Provably prunable, data-carrying output @@ -130,47 +125,39 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v // byte passes the IsPushOnly() test we don't care what exactly is in the // script. if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { - typeRet = TX_NULL_DATA; - return true; + return TX_NULL_DATA; } std::vector<unsigned char> data; if (MatchPayToPubkey(scriptPubKey, data)) { - typeRet = TX_PUBKEY; vSolutionsRet.push_back(std::move(data)); - return true; + return TX_PUBKEY; } if (MatchPayToPubkeyHash(scriptPubKey, data)) { - typeRet = TX_PUBKEYHASH; vSolutionsRet.push_back(std::move(data)); - return true; + return TX_PUBKEYHASH; } unsigned int required; std::vector<std::vector<unsigned char>> keys; if (MatchMultisig(scriptPubKey, required, keys)) { - typeRet = TX_MULTISIG; vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16 vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16 - return true; + return TX_MULTISIG; } vSolutionsRet.clear(); - typeRet = TX_NONSTANDARD; - return false; + return TX_NONSTANDARD; } bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { std::vector<valtype> vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; + txnouttype whichType = Solver(scriptPubKey, vSolutions); - if (whichType == TX_PUBKEY) - { + if (whichType == TX_PUBKEY) { CPubKey pubKey(vSolutions[0]); if (!pubKey.IsValid()) return false; @@ -212,11 +199,11 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet) { addressRet.clear(); - typeRet = TX_NONSTANDARD; std::vector<valtype> vSolutions; - if (!Solver(scriptPubKey, typeRet, vSolutions)) + typeRet = Solver(scriptPubKey, vSolutions); + if (typeRet == TX_NONSTANDARD) { return false; - if (typeRet == TX_NULL_DATA){ + } else if (typeRet == TX_NULL_DATA) { // This is data, not addresses return false; } @@ -324,14 +311,12 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) CScript GetScriptForWitness(const CScript& redeemscript) { - txnouttype typ; std::vector<std::vector<unsigned char> > vSolutions; - if (Solver(redeemscript, typ, vSolutions)) { - if (typ == TX_PUBKEY) { - return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end()))); - } else if (typ == TX_PUBKEYHASH) { - return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); - } + txnouttype typ = Solver(redeemscript, vSolutions); + if (typ == TX_PUBKEY) { + return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end()))); + } else if (typ == TX_PUBKEYHASH) { + return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); } return GetScriptForDestination(WitnessV0ScriptHash(redeemscript)); } diff --git a/src/script/standard.h b/src/script/standard.h index 138003087..fc20fb6a0 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// 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. @@ -135,11 +135,10 @@ const char* GetTxnOutputType(txnouttype t); * script hash, for P2PKH it will contain the key hash, etc. * * @param[in] scriptPubKey Script to parse - * @param[out] typeRet The script type * @param[out] vSolutionsRet Vector of parsed pubkeys and hashes - * @return True if script matches standard template + * @return The script type. TX_NONSTANDARD represents a failed solve. */ -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); +txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet); /** * Parse a standard scriptPubKey for the destination address. Assigns result to |