aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/bitcoinconsensus.cpp2
-rw-r--r--src/script/descriptor.cpp566
-rw-r--r--src/script/descriptor.h102
-rw-r--r--src/script/interpreter.cpp12
-rw-r--r--src/script/sign.cpp241
-rw-r--r--src/script/sign.h599
-rw-r--r--src/script/standard.cpp2
7 files changed, 1502 insertions, 22 deletions
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 8cc44b675f..e2370c5e50 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -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/descriptor.cpp b/src/script/descriptor.cpp
new file mode 100644
index 0000000000..f366b99ec3
--- /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 0000000000..e079c72e92
--- /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 4b982d647d..be2138d502 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -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/sign.cpp b/src/script/sign.cpp
index 60a8a2655d..fa09adbaf8 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -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,9 +48,10 @@ 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)) {
+ sigdata.misc_pubkeys.emplace(pubkey.GetID(), pubkey);
return true;
}
// Look for pubkey in all partial sigs
@@ -60,6 +60,12 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd
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;
+ return true;
+ }
return false;
}
@@ -70,9 +76,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;
@@ -200,6 +206,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 +217,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 +233,32 @@ 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)
+{
+ // 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
+ input.FillSignatureData(sigdata);
+
+ // Get UTXO
+ CTxOut utxo;
+ if (input.non_witness_utxo) {
+ utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
+ } else if (!input.witness_utxo.IsNull()) {
+ utxo = input.witness_utxo;
+ } else {
+ return false;
+ }
+
+ MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
+ bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
+ input.FromSignatureData(sigdata);
+ return sig_complete;
+}
+
class SignatureExtractorChecker final : public BaseSignatureChecker
{
private:
@@ -251,18 +287,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;
- }
};
}
@@ -407,6 +436,18 @@ public:
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();
@@ -429,3 +470,177 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
}
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.first);
+ }
+}
+
+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;
+ }
+}
+
+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.first);
+ }
+}
+
+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;
+ }
+}
+
+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 PublicOnlySigningProvider::GetCScript(const CScriptID &scriptid, CScript& script) const
+{
+ return m_provider->GetCScript(scriptid, script);
+}
+
+bool PublicOnlySigningProvider::GetPubKey(const CKeyID &address, CPubKey& pubkey) const
+{
+ return m_provider->GetPubKey(address, pubkey);
+}
+
+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 3666859641..96ef59fbe8 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -6,7 +6,11 @@
#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;
@@ -28,6 +32,30 @@ public:
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
+class PublicOnlySigningProvider : public SigningProvider
+{
+private:
+ 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;
+};
+
+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:
@@ -62,17 +90,585 @@ 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, CPubKey> 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, std::vector<uint32_t>>& 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);
+ std::vector<uint32_t> keypath;
+ for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) {
+ uint32_t index;
+ s >> index;
+ keypath.push_back(index);
+ }
+
+ // Add to map
+ hd_keypaths.emplace(pubkey, 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)
+{
+ 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) {
+ 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, std::vector<uint32_t>> 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);
+ SerializeToVector(s, 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");
+ }
+ UnserializeFromVector(s, 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, std::vector<uint32_t>> 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
+ SerializeToVector(s, *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;
+ UnserializeFromVector(s, 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 +676,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 */
+bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1);
+
/** 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 f0b2c62a91..d7b1724790 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -324,8 +324,6 @@ 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)) {