diff options
Diffstat (limited to 'src/script')
| -rw-r--r-- | src/script/bitcoinconsensus.cpp | 4 | ||||
| -rw-r--r-- | src/script/bitcoinconsensus.h | 2 | ||||
| -rw-r--r-- | src/script/descriptor.cpp | 566 | ||||
| -rw-r--r-- | src/script/descriptor.h | 55 | ||||
| -rw-r--r-- | src/script/interpreter.cpp | 14 | ||||
| -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 | 333 | ||||
| -rw-r--r-- | src/script/sign.h | 630 | ||||
| -rw-r--r-- | src/script/standard.cpp | 61 | ||||
| -rw-r--r-- | src/script/standard.h | 7 |
18 files changed, 1594 insertions, 99 deletions
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 8cc44b675..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. @@ -81,7 +81,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { if (!verify_flags(flags)) { - return bitcoinconsensus_ERR_INVALID_FLAGS; + return set_error(err, bitcoinconsensus_ERR_INVALID_FLAGS); } try { TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); 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..45b097dde --- /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 successful, sp is updated to skip the constant and return true. */ +bool Const(const std::string& str, Span<const char>& sp) +{ + if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) { + sp = sp.subspan(str.size()); + return true; + } + return false; +} + +/** Parse a function call. If successful, sp is updated to be the function's argument(s). */ +bool Func(const std::string& str, Span<const char>& sp) +{ + if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) { + sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2); + return true; + } + return false; +} + +/** Return the expression that sp begins with, and update sp to skip it. */ +Span<const char> Expr(Span<const char>& sp) +{ + int level = 0; + auto it = sp.begin(); + while (it != sp.end()) { + if (*it == '(') { + ++level; + } else if (level && *it == ')') { + --level; + } else if (level == 0 && (*it == ')' || *it == ',')) { + break; + } + ++it; + } + Span<const char> ret = sp.first(it - sp.begin()); + sp = sp.subspan(it - sp.begin()); + return ret; +} + +/** Split a string on every instance of sep, returning a vector. */ +std::vector<Span<const char>> Split(const Span<const char>& sp, char sep) +{ + std::vector<Span<const char>> ret; + auto it = sp.begin(); + auto start = it; + while (it != sp.end()) { + if (*it == sep) { + ret.emplace_back(start, it); + start = it + 1; + } + ++it; + } + ret.emplace_back(start, it); + return ret; +} + +/** Parse a key path, being passed a split list of elements (the first element is 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..87e07369c --- /dev/null +++ b/src/script/descriptor.h @@ -0,0 +1,55 @@ +// 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. +// +// Reference documentation about the descriptor language can be found in +// doc/descriptors.md. + +/** 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 4b982d647..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. @@ -99,7 +99,7 @@ bool static IsCompressedPubKey(const valtype &vchPubKey) { * Where R and S are not negative (their first byte has its highest bit not set), and not * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, * in which case a single 0 byte is necessary and even required). - * + * * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 * * This function is consensus-critical since BIP66. @@ -139,7 +139,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) { // Verify that the length of the signature matches the sum of the length // of the elements. if ((size_t)(lenR + lenS + 7) != sig.size()) return false; - + // Check whether the R element is an integer. if (sig[2] != 0x02) return false; @@ -899,7 +899,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& popstack(stack); stack.push_back(vchHash); } - break; + break; case OP_CODESEPARATOR: { @@ -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 60a8a2655..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) {} @@ -49,17 +48,28 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig return false; } -static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) +static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) { - if (provider.GetPubKey(address, pubkey)) { - return true; - } // Look for pubkey in all partial sigs const auto it = sigdata.signatures.find(address); if (it != sigdata.signatures.end()) { pubkey = it->second.first; return true; } + // 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.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; } @@ -70,9 +80,9 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat sig_out = it->second.second; return true; } + CPubKey pubkey; + GetPubKey(provider, sigdata, keyid, pubkey); if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) { - CPubKey pubkey; - GetPubKey(provider, sigdata, keyid, pubkey); auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out)); assert(i.second); return true; @@ -95,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) { @@ -200,6 +209,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato txnouttype subType; solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata); sigdata.scriptWitness.stack = result; + sigdata.witness = true; result.clear(); } else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) @@ -210,7 +220,10 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; + sigdata.witness = true; result.clear(); + } else if (solved && whichType == TX_WITNESS_UNKNOWN) { + sigdata.witness = true; } if (P2SH) { @@ -223,6 +236,59 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato return sigdata.complete; } +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()) { + return true; + } + + // 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; +} + class SignatureExtractorChecker final : public BaseSignatureChecker { private: @@ -251,18 +317,11 @@ struct Stacks std::vector<valtype> script; std::vector<valtype> witness; - Stacks() {} - explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {} + Stacks() = delete; + Stacks(const Stacks&) = delete; explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) { EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE); } - - SignatureData Output() const { - SignatureData result; - result.scriptSig = PushAll(script); - result.scriptWitness.stack = witness; - return result; - } }; } @@ -284,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; @@ -297,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()) { @@ -307,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(); @@ -388,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) @@ -424,8 +498,201 @@ 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(); +} + +void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt) +{ + for (unsigned int i = 0; i < inputs.size(); ++i) { + inputs[i].Merge(psbt.inputs[i]); + } + for (unsigned int i = 0; i < outputs.size(); ++i) { + outputs[i].Merge(psbt.outputs[i]); + } + unknown.insert(psbt.unknown.begin(), psbt.unknown.end()); +} + +bool PartiallySignedTransaction::IsSane() const +{ + for (PSBTInput input : inputs) { + if (!input.IsSane()) return false; + } + return true; +} + +bool PSBTInput::IsNull() const +{ + return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty(); +} + +void PSBTInput::FillSignatureData(SignatureData& sigdata) const +{ + if (!final_script_sig.empty()) { + sigdata.scriptSig = final_script_sig; + sigdata.complete = true; + } + if (!final_script_witness.IsNull()) { + sigdata.scriptWitness = final_script_witness; + sigdata.complete = true; + } + if (sigdata.complete) { + return; + } + + sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end()); + if (!redeem_script.empty()) { + sigdata.redeem_script = redeem_script; + } + if (!witness_script.empty()) { + sigdata.witness_script = witness_script; + } + for (const auto& key_pair : hd_keypaths) { + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); + } +} + +void PSBTInput::FromSignatureData(const SignatureData& sigdata) +{ + if (sigdata.complete) { + partial_sigs.clear(); + hd_keypaths.clear(); + redeem_script.clear(); + witness_script.clear(); + + if (!sigdata.scriptSig.empty()) { + final_script_sig = sigdata.scriptSig; + } + if (!sigdata.scriptWitness.IsNull()) { + final_script_witness = sigdata.scriptWitness; + } + return; + } + + partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end()); + if (redeem_script.empty() && !sigdata.redeem_script.empty()) { + redeem_script = sigdata.redeem_script; + } + 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) +{ + if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo; + if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) { + witness_utxo = input.witness_utxo; + non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one. + } + + partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end()); + hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end()); + unknown.insert(input.unknown.begin(), input.unknown.end()); + + if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script; + if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script; + if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig; + if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness; +} + +bool PSBTInput::IsSane() const +{ + // Cannot have both witness and non-witness utxos + if (!witness_utxo.IsNull() && non_witness_utxo) return false; + + // If we have a witness_script or a scriptWitness, we must also have a witness utxo + if (!witness_script.empty() && witness_utxo.IsNull()) return false; + if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false; + + return true; +} + +void PSBTOutput::FillSignatureData(SignatureData& sigdata) const +{ + if (!redeem_script.empty()) { + sigdata.redeem_script = redeem_script; + } + if (!witness_script.empty()) { + sigdata.witness_script = witness_script; + } + for (const auto& key_pair : hd_keypaths) { + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); + } +} + +void PSBTOutput::FromSignatureData(const SignatureData& sigdata) +{ + if (redeem_script.empty() && !sigdata.redeem_script.empty()) { + redeem_script = sigdata.redeem_script; + } + 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 +{ + return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty(); +} + +void PSBTOutput::Merge(const PSBTOutput& output) +{ + hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end()); + unknown.insert(output.unknown.begin(), output.unknown.end()); + + if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script; + if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script; +} + +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::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 366685964..18b732099 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -1,12 +1,16 @@ // 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. #ifndef BITCOIN_SCRIPT_SIGN_H #define BITCOIN_SCRIPT_SIGN_H +#include <boost/optional.hpp> +#include <hash.h> +#include <pubkey.h> #include <script/interpreter.h> +#include <streams.h> class CKey; class CKeyID; @@ -16,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 { @@ -24,10 +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 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, 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: @@ -52,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; @@ -62,17 +103,599 @@ typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair; // in order to construct final scriptSigs and scriptWitnesses. struct SignatureData { bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete + bool witness = false; ///< Stores whether the input this SigData corresponds to is a witness input CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format CScript redeem_script; ///< The redeemScript (if any) for the input 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, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys; SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} void MergeSignatureData(SignatureData sigdata); }; +// Magic bytes +static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff}; + +// Global types +static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00; + +// Input types +static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00; +static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01; +static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02; +static constexpr uint8_t PSBT_IN_SIGHASH = 0x03; +static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04; +static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05; +static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06; +static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07; +static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08; + +// Output types +static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00; +static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01; +static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02; + +// The separator is 0x00. Reading this in means that the unserializer can interpret it +// as a 0 length key which indicates that this is the separator. The separator has no value. +static constexpr uint8_t PSBT_SEPARATOR = 0x00; + +// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream +// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other. +template<typename Stream, typename... X> +void SerializeToVector(Stream& s, const X&... args) +{ + WriteCompactSize(s, GetSerializeSizeMany(s, args...)); + SerializeMany(s, args...); +} + +// Takes a stream and multiple arguments and unserializes them first as a vector then each object individually in the order provided in the arguments +template<typename Stream, typename... X> +void UnserializeFromVector(Stream& s, X&... args) +{ + size_t expected_size = ReadCompactSize(s); + size_t remaining_before = s.size(); + UnserializeMany(s, args...); + size_t remaining_after = s.size(); + if (remaining_after + expected_size != remaining_before) { + throw std::ios_base::failure("Size of value was not the stated size"); + } +} + +// Deserialize HD keypaths into a map +template<typename Stream> +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) { + throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath"); + } + // Read in the pubkey from key + CPubKey pubkey(key.begin() + 1, key.end()); + if (!pubkey.IsFullyValid()) { + throw std::ios_base::failure("Invalid pubkey"); + } + if (hd_keypaths.count(pubkey) > 0) { + throw std::ios_base::failure("Duplicate Key, pubkey derivation path already provided"); + } + + // Read in key path + uint64_t value_len = ReadCompactSize(s); + 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.path.push_back(index); + } + + // Add to map + 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, KeyOriginInfo>& hd_keypaths, uint8_t type) +{ + for (auto keypath_pair : hd_keypaths) { + SerializeToVector(s, type, MakeSpan(keypath_pair.first)); + 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; + } + } +} + +/** A structure for PSBTs which contain per-input information */ +struct PSBTInput +{ + CTransactionRef non_witness_utxo; + CTxOut witness_utxo; + CScript redeem_script; + CScript witness_script; + CScript final_script_sig; + CScriptWitness final_script_witness; + 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; + + bool IsNull() const; + void FillSignatureData(SignatureData& sigdata) const; + void FromSignatureData(const SignatureData& sigdata); + void Merge(const PSBTInput& input); + bool IsSane() const; + PSBTInput() {} + + template <typename Stream> + inline void Serialize(Stream& s) const { + // Write the utxo + // 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); + 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); + } + + if (final_script_sig.empty() && final_script_witness.IsNull()) { + // Write any partial signatures + for (auto sig_pair : partial_sigs) { + SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first)); + s << sig_pair.second.second; + } + + // Write the sighash type + if (sighash_type > 0) { + SerializeToVector(s, PSBT_IN_SIGHASH); + SerializeToVector(s, sighash_type); + } + + // Write the redeem script + if (!redeem_script.empty()) { + SerializeToVector(s, PSBT_IN_REDEEMSCRIPT); + s << redeem_script; + } + + // Write the witness script + if (!witness_script.empty()) { + SerializeToVector(s, PSBT_IN_WITNESSSCRIPT); + s << witness_script; + } + + // Write any hd keypaths + SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION); + } + + // Write script sig + if (!final_script_sig.empty()) { + SerializeToVector(s, PSBT_IN_SCRIPTSIG); + s << final_script_sig; + } + // write script witness + if (!final_script_witness.IsNull()) { + SerializeToVector(s, PSBT_IN_SCRIPTWITNESS); + SerializeToVector(s, final_script_witness.stack); + } + + // Write unknown things + for (auto& entry : unknown) { + s << entry.first; + s << entry.second; + } + + s << PSBT_SEPARATOR; + } + + + template <typename Stream> + inline void Unserialize(Stream& s) { + // Read loop + while(!s.empty()) { + // Read + std::vector<unsigned char> key; + s >> key; + + // the key is empty if that was actually a separator byte + // This is a special case for key lengths 0 as those are not allowed (except for separator) + if (key.empty()) return; + + // First byte of key is the type + unsigned char type = key[0]; + + // 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"); + } + // 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"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Witness utxo key is more than one byte type"); + } + UnserializeFromVector(s, witness_utxo); + break; + case PSBT_IN_PARTIAL_SIG: + { + // 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) { + throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey"); + } + // Read in the pubkey from key + CPubKey pubkey(key.begin() + 1, key.end()); + if (!pubkey.IsFullyValid()) { + throw std::ios_base::failure("Invalid pubkey"); + } + if (partial_sigs.count(pubkey.GetID()) > 0) { + throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided"); + } + + // Read in the signature from value + std::vector<unsigned char> sig; + s >> sig; + + // Add to list + partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig))); + break; + } + case PSBT_IN_SIGHASH: + if (sighash_type > 0) { + throw std::ios_base::failure("Duplicate Key, input sighash type already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Sighash type key is more than one byte type"); + } + UnserializeFromVector(s, sighash_type); + break; + case PSBT_IN_REDEEMSCRIPT: + { + if (!redeem_script.empty()) { + throw std::ios_base::failure("Duplicate Key, input redeemScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input redeemScript key is more than one byte type"); + } + s >> redeem_script; + break; + } + case PSBT_IN_WITNESSSCRIPT: + { + if (!witness_script.empty()) { + throw std::ios_base::failure("Duplicate Key, input witnessScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input witnessScript key is more than one byte type"); + } + s >> witness_script; + break; + } + case PSBT_IN_BIP32_DERIVATION: + { + DeserializeHDKeypaths(s, key, hd_keypaths); + break; + } + case PSBT_IN_SCRIPTSIG: + { + if (!final_script_sig.empty()) { + throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Final scriptSig key is more than one byte type"); + } + s >> final_script_sig; + break; + } + case PSBT_IN_SCRIPTWITNESS: + { + if (!final_script_witness.IsNull()) { + throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Final scriptWitness key is more than one byte type"); + } + UnserializeFromVector(s, final_script_witness.stack); + break; + } + // Unknown stuff + default: + if (unknown.count(key) > 0) { + throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); + } + // Read in the value + std::vector<unsigned char> val_bytes; + s >> val_bytes; + unknown.emplace(std::move(key), std::move(val_bytes)); + break; + } + } + } + + template <typename Stream> + PSBTInput(deserialize_type, Stream& s) { + Unserialize(s); + } +}; + +/** A structure for PSBTs which contains per output information */ +struct PSBTOutput +{ + CScript redeem_script; + CScript witness_script; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; + std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; + + bool IsNull() const; + void FillSignatureData(SignatureData& sigdata) const; + void FromSignatureData(const SignatureData& sigdata); + void Merge(const PSBTOutput& output); + bool IsSane() const; + PSBTOutput() {} + + template <typename Stream> + inline void Serialize(Stream& s) const { + // Write the redeem script + if (!redeem_script.empty()) { + SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT); + s << redeem_script; + } + + // Write the witness script + if (!witness_script.empty()) { + SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT); + s << witness_script; + } + + // Write any hd keypaths + SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION); + + // Write unknown things + for (auto& entry : unknown) { + s << entry.first; + s << entry.second; + } + + s << PSBT_SEPARATOR; + } + + + template <typename Stream> + inline void Unserialize(Stream& s) { + // Read loop + while(!s.empty()) { + // Read + std::vector<unsigned char> key; + s >> key; + + // the key is empty if that was actually a separator byte + // This is a special case for key lengths 0 as those are not allowed (except for separator) + if (key.empty()) return; + + // First byte of key is the type + unsigned char type = key[0]; + + // Do stuff based on type + switch(type) { + case PSBT_OUT_REDEEMSCRIPT: + { + if (!redeem_script.empty()) { + throw std::ios_base::failure("Duplicate Key, output redeemScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Output redeemScript key is more than one byte type"); + } + s >> redeem_script; + break; + } + case PSBT_OUT_WITNESSSCRIPT: + { + if (!witness_script.empty()) { + throw std::ios_base::failure("Duplicate Key, output witnessScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Output witnessScript key is more than one byte type"); + } + s >> witness_script; + break; + } + case PSBT_OUT_BIP32_DERIVATION: + { + DeserializeHDKeypaths(s, key, hd_keypaths); + break; + } + // Unknown stuff + default: { + if (unknown.count(key) > 0) { + throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); + } + // Read in the value + std::vector<unsigned char> val_bytes; + s >> val_bytes; + unknown.emplace(std::move(key), std::move(val_bytes)); + break; + } + } + } + } + + template <typename Stream> + PSBTOutput(deserialize_type, Stream& s) { + Unserialize(s); + } +}; + +/** A version of CTransaction with the PSBT format*/ +struct PartiallySignedTransaction +{ + boost::optional<CMutableTransaction> tx; + std::vector<PSBTInput> inputs; + std::vector<PSBTOutput> outputs; + std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; + + bool IsNull() const; + void Merge(const PartiallySignedTransaction& psbt); + bool IsSane() const; + PartiallySignedTransaction() {} + PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {} + + // Only checks if they refer to the same transaction + friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b) + { + return a.tx->GetHash() == b.tx->GetHash(); + } + friend bool operator!=(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b) + { + return !(a == b); + } + + template <typename Stream> + inline void Serialize(Stream& s) const { + + // magic bytes + s << PSBT_MAGIC_BYTES; + + // unsigned tx flag + SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX); + + // Write serialized tx to a stream + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + SerializeToVector(os, *tx); + + // Write the unknown things + for (auto& entry : unknown) { + s << entry.first; + s << entry.second; + } + + // Separator + s << PSBT_SEPARATOR; + + // Write inputs + for (const PSBTInput& input : inputs) { + s << input; + } + // Write outputs + for (const PSBTOutput& output : outputs) { + s << output; + } + } + + + template <typename Stream> + inline void Unserialize(Stream& s) { + // Read the magic bytes + uint8_t magic[5]; + s >> magic; + if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) { + throw std::ios_base::failure("Invalid PSBT magic bytes"); + } + + // Read global data + while(!s.empty()) { + // Read + std::vector<unsigned char> key; + s >> key; + + // the key is empty if that was actually a separator byte + // This is a special case for key lengths 0 as those are not allowed (except for separator) + if (key.empty()) break; + + // First byte of key is the type + unsigned char type = key[0]; + + // Do stuff based on type + switch(type) { + case PSBT_GLOBAL_UNSIGNED_TX: + { + if (tx) { + throw std::ios_base::failure("Duplicate Key, unsigned tx already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Global unsigned tx key is more than one byte type"); + } + CMutableTransaction 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) { + if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) { + throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses."); + } + } + break; + } + // Unknown stuff + default: { + if (unknown.count(key) > 0) { + throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); + } + // Read in the value + std::vector<unsigned char> val_bytes; + s >> val_bytes; + unknown.emplace(std::move(key), std::move(val_bytes)); + } + } + } + + // Make sure that we got an unsigned tx + if (!tx) { + throw std::ios_base::failure("No unsigned transcation was provided"); + } + + // Read input data + unsigned int i = 0; + while (!s.empty() && i < tx->vin.size()) { + PSBTInput input; + s >> input; + inputs.push_back(input); + + // Make sure the non-witness utxo matches the outpoint + if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) { + throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash"); + } + ++i; + } + // Make sure that the number of inputs matches the number of inputs in the transaction + if (inputs.size() != tx->vin.size()) { + throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction."); + } + + // Read output data + i = 0; + while (!s.empty() && i < tx->vout.size()) { + PSBTOutput output; + s >> output; + outputs.push_back(output); + ++i; + } + // Make sure that the number of outputs matches the number of outputs in the transaction + if (outputs.size() != tx->vout.size()) { + throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction."); + } + // Sanity check + if (!IsSane()) { + throw std::ios_base::failure("PSBT is not sane."); + } + } + + template <typename Stream> + PartiallySignedTransaction(deserialize_type, Stream& s) { + Unserialize(s); + } +}; + /** Produce a script signature using a generic signature creator. */ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); @@ -80,6 +703,9 @@ 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, 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); void UpdateInput(CTxIn& input, const SignatureData& data); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index f0b2c62a9..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,16 +311,12 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) CScript GetScriptForWitness(const CScript& redeemscript) { - CScript ret; - - 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 |