diff options
Diffstat (limited to 'src/script')
| -rw-r--r-- | src/script/descriptor.cpp | 175 | ||||
| -rw-r--r-- | src/script/descriptor.h | 11 | ||||
| -rw-r--r-- | src/script/script.h | 9 | ||||
| -rw-r--r-- | src/script/sign.cpp | 244 | ||||
| -rw-r--r-- | src/script/sign.h | 562 |
5 files changed, 206 insertions, 795 deletions
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index a702be5b7..a333d4d4a 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -10,6 +10,7 @@ #include <script/standard.h> #include <span.h> +#include <util/bip32.h> #include <util/system.h> #include <util/strencodings.h> @@ -20,21 +21,130 @@ namespace { //////////////////////////////////////////////////////////////////////////// -// Internal representation // +// Checksum // //////////////////////////////////////////////////////////////////////////// -typedef std::vector<uint32_t> KeyPath; +// This section implements a checksum algorithm for descriptors with the +// following properties: +// * Mistakes in a descriptor string are measured in "symbol errors". The higher +// the number of symbol errors, the harder it is to detect: +// * An error substituting a character from 0123456789()[],'/*abcdefgh@:$%{} for +// another in that set always counts as 1 symbol error. +// * Note that hex encoded keys are covered by these characters. Xprvs and +// xpubs use other characters too, but already have their own checksum +// mechanism. +// * Function names like "multi()" use other characters, but mistakes in +// these would generally result in an unparseable descriptor. +// * A case error always counts as 1 symbol error. +// * Any other 1 character substitution error counts as 1 or 2 symbol errors. +// * Any 1 symbol error is always detected. +// * Any 2 or 3 symbol error in a descriptor of up to 49154 characters is always detected. +// * Any 4 symbol error in a descriptor of up to 507 characters is always detected. +// * Any 5 symbol error in a descriptor of up to 77 characters is always detected. +// * Is optimized to minimize the chance a 5 symbol error in a descriptor up to 387 characters is undetected +// * Random errors have a chance of 1 in 2**40 of being undetected. +// +// These properties are achieved by expanding every group of 3 (non checksum) characters into +// 4 GF(32) symbols, over which a cyclic code is defined. + +/* + * Interprets c as 8 groups of 5 bits which are the coefficients of a degree 8 polynomial over GF(32), + * multiplies that polynomial by x, computes its remainder modulo a generator, and adds the constant term val. + * + * This generator is G(x) = x^8 + {30}x^7 + {23}x^6 + {15}x^5 + {14}x^4 + {10}x^3 + {6}x^2 + {12}x + {9}. + * It is chosen to define an cyclic error detecting code which is selected by: + * - Starting from all BCH codes over GF(32) of degree 8 and below, which by construction guarantee detecting + * 3 errors in windows up to 19000 symbols. + * - Taking all those generators, and for degree 7 ones, extend them to degree 8 by adding all degree-1 factors. + * - Selecting just the set of generators that guarantee detecting 4 errors in a window of length 512. + * - Selecting one of those with best worst-case behavior for 5 errors in windows of length up to 512. + * + * The generator and the constants to implement it can be verified using this Sage code: + * B = GF(2) # Binary field + * BP.<b> = B[] # Polynomials over the binary field + * F_mod = b**5 + b**3 + 1 + * F.<f> = GF(32, modulus=F_mod, repr='int') # GF(32) definition + * FP.<x> = F[] # Polynomials over GF(32) + * E_mod = x**3 + x + F.fetch_int(8) + * E.<e> = F.extension(E_mod) # Extension field definition + * alpha = e**2743 # Choice of an element in extension field + * for p in divisors(E.order() - 1): # Verify alpha has order 32767. + * assert((alpha**p == 1) == (p % 32767 == 0)) + * G = lcm([(alpha**i).minpoly() for i in [1056,1057,1058]] + [x + 1]) + * print(G) # Print out the generator + * for i in [1,2,4,8,16]: # Print out {1,2,4,8,16}*(G mod x^8), packed in hex integers. + * v = 0 + * for coef in reversed((F.fetch_int(i)*(G % x**8)).coefficients(sparse=True)): + * v = v*32 + coef.integer_representation() + * print("0x%x" % v) + */ +uint64_t PolyMod(uint64_t c, int val) +{ + uint8_t c0 = c >> 35; + c = ((c & 0x7ffffffff) << 5) ^ val; + if (c0 & 1) c ^= 0xf5dee51989; + if (c0 & 2) c ^= 0xa9fdca3312; + if (c0 & 4) c ^= 0x1bab10e32d; + if (c0 & 8) c ^= 0x3706b1677a; + if (c0 & 16) c ^= 0x644d626ffd; + return c; +} -std::string FormatKeyPath(const KeyPath& path) +std::string DescriptorChecksum(const Span<const char>& span) { - std::string ret; - for (auto i : path) { - ret += strprintf("/%i", (i << 1) >> 1); - if (i >> 31) ret += '\''; + /** A character set designed such that: + * - The most common 'unprotected' descriptor characters (hex, keypaths) are in the first group of 32. + * - Case errors cause an offset that's a multiple of 32. + * - As many alphabetic characters are in the same group (while following the above restrictions). + * + * If p(x) gives the position of a character c in this character set, every group of 3 characters + * (a,b,c) is encoded as the 4 symbols (p(a) & 31, p(b) & 31, p(c) & 31, (p(a) / 32) + 3 * (p(b) / 32) + 9 * (p(c) / 32). + * This means that changes that only affect the lower 5 bits of the position, or only the higher 2 bits, will just + * affect a single symbol. + * + * As a result, within-group-of-32 errors count as 1 symbol, as do cross-group errors that don't affect + * the position within the groups. + */ + static std::string INPUT_CHARSET = + "0123456789()[],'/*abcdefgh@:$%{}" + "IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~" + "ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "; + + /** The character set for the checksum itself (same as bech32). */ + static std::string CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + + uint64_t c = 1; + int cls = 0; + int clscount = 0; + for (auto ch : span) { + auto pos = INPUT_CHARSET.find(ch); + if (pos == std::string::npos) return ""; + c = PolyMod(c, pos & 31); // Emit a symbol for the position inside the group, for every character. + cls = cls * 3 + (pos >> 5); // Accumulate the group numbers + if (++clscount == 3) { + // Emit an extra symbol representing the group numbers, for every 3 characters. + c = PolyMod(c, cls); + cls = 0; + clscount = 0; + } } + if (clscount > 0) c = PolyMod(c, cls); + for (int j = 0; j < 8; ++j) c = PolyMod(c, 0); // Shift further to determine the checksum. + c ^= 1; // Prevent appending zeroes from not affecting the checksum. + + std::string ret(8, ' '); + for (int j = 0; j < 8; ++j) ret[j] = CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31]; return ret; } +std::string AddChecksum(const std::string& str) { return str + "#" + DescriptorChecksum(MakeSpan(str)); } + +//////////////////////////////////////////////////////////////////////////// +// Internal representation // +//////////////////////////////////////////////////////////////////////////// + +typedef std::vector<uint32_t> KeyPath; + /** Interface for public key objects in descriptors. */ struct PubkeyProvider { @@ -63,7 +173,7 @@ class OriginPubkeyProvider final : public PubkeyProvider std::string OriginString() const { - return HexStr(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint)) + FormatKeyPath(m_origin.path); + return HexStr(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint)) + FormatHDKeypath(m_origin.path); } public: @@ -184,7 +294,7 @@ public: } std::string ToString() const override { - std::string ret = EncodeExtPubKey(m_extkey) + FormatKeyPath(m_path); + std::string ret = EncodeExtPubKey(m_extkey) + FormatHDKeypath(m_path); if (IsRange()) { ret += "/*"; if (m_derive == DeriveType::HARDENED) ret += '\''; @@ -195,7 +305,7 @@ public: { CExtKey key; if (!GetExtKey(arg, key)) return false; - out = EncodeExtKey(key) + FormatKeyPath(m_path); + out = EncodeExtKey(key) + FormatHDKeypath(m_path); if (IsRange()) { out += "/*"; if (m_derive == DeriveType::HARDENED) out += '\''; @@ -226,7 +336,7 @@ protected: * @param pubkeys The evaluations of the m_pubkey_args field. * @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr). * @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver. - * The script and pubkeys argument to this function are automatically added. + * The script arguments to this function are automatically added, as is the origin info of the provided pubkeys. * @return A vector with scriptPubKeys for this descriptor. */ virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0; @@ -282,10 +392,15 @@ public: { std::string ret; ToStringHelper(nullptr, ret, false); - return ret; + return AddChecksum(ret); } - bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final { return ToStringHelper(&arg, out, true); } + bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final + { + bool ret = ToStringHelper(&arg, out, true); + out = AddChecksum(out); + return ret; + } bool ExpandHelper(int pos, const SigningProvider& arg, Span<const unsigned char>* cache_read, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache_write) const { @@ -321,8 +436,7 @@ public: pubkeys.reserve(entries.size()); for (auto& entry : entries) { pubkeys.push_back(entry.first); - out.origins.emplace(entry.first.GetID(), std::move(entry.second)); - out.pubkeys.emplace(entry.first.GetID(), entry.first); + out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second))); } if (m_script_arg) { for (const auto& subscript : subscripts) { @@ -396,7 +510,12 @@ public: class PKHDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(keys[0].GetID())); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override + { + CKeyID id = keys[0].GetID(); + out.pubkeys.emplace(id, keys[0]); + return Singleton(GetScriptForDestination(id)); + } public: PKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "pkh") {} }; @@ -405,7 +524,12 @@ public: class WPKHDescriptor final : public DescriptorImpl { protected: - std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider&) const override { return Singleton(GetScriptForDestination(WitnessV0KeyHash(keys[0].GetID()))); } + std::vector<CScript> MakeScripts(const std::vector<CPubKey>& keys, const CScript*, FlatSigningProvider& out) const override + { + CKeyID id = keys[0].GetID(); + out.pubkeys.emplace(id, keys[0]); + return Singleton(GetScriptForDestination(WitnessV0KeyHash(id))); + } public: WPKHDescriptor(std::unique_ptr<PubkeyProvider> prov) : DescriptorImpl(Singleton(std::move(prov)), {}, "wpkh") {} }; @@ -418,6 +542,7 @@ protected: { std::vector<CScript> ret; CKeyID id = keys[0].GetID(); + out.pubkeys.emplace(id, keys[0]); ret.emplace_back(GetScriptForRawPubKey(keys[0])); // P2PK ret.emplace_back(GetScriptForDestination(id)); // P2PKH if (keys[0].IsCompressed()) { @@ -750,11 +875,25 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo return MakeUnique<RawDescriptor>(script); } + } // namespace -std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out) +std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum) { Span<const char> sp(descriptor.data(), descriptor.size()); + + // Checksum checks + auto check_split = Split(sp, '#'); + if (check_split.size() > 2) return nullptr; // Multiple '#' symbols + if (check_split.size() == 1 && require_checksum) return nullptr; // Missing checksum + if (check_split.size() == 2) { + if (check_split[1].size() != 8) return nullptr; // Unexpected length for checksum + auto checksum = DescriptorChecksum(check_split[0]); + if (checksum.empty()) return nullptr; // Invalid characters in payload + if (!std::equal(checksum.begin(), checksum.end(), check_split[1].begin())) return nullptr; // Checksum mismatch + } + sp = check_split[0]; + auto ret = ParseScript(sp, ParseScriptContext::TOP, out); if (sp.size() == 0 && ret) return std::unique_ptr<Descriptor>(std::move(ret)); return nullptr; diff --git a/src/script/descriptor.h b/src/script/descriptor.h index 44f0efca0..907a10228 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -62,8 +62,15 @@ struct Descriptor { virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, 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); +/** Parse a descriptor string. Included private keys are put in out. + * + * If the descriptor has a checksum, it must be valid. If require_checksum + * is set, the checksum is mandatory - otherwise it is optional. + * + * If a parse error occurs, or the checksum is missing/invalid, or anything + * else is wrong, nullptr is returned. + */ +std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out, bool require_checksum = false); /** Find a descriptor for the specified script, using information from provider where possible. * diff --git a/src/script/script.h b/src/script/script.h index 1d8ddba2f..11e8661a5 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -581,13 +581,4 @@ struct CScriptWitness std::string ToString() const; }; -class CReserveScript -{ -public: - CScript reserveScript; - virtual void KeepScript() {} - CReserveScript() {} - virtual ~CReserveScript() {} -}; - #endif // BITCOIN_SCRIPT_SCRIPT_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 635e4fa3d..36dd68a3d 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -83,6 +83,8 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat assert(i.second); return true; } + // Could not make signature or signature not found, add keyid to missing + sigdata.missing_sigs.push_back(keyid); return false; } @@ -116,17 +118,24 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TX_PUBKEYHASH: { CKeyID keyID = CKeyID(uint160(vSolutions[0])); CPubKey pubkey; - if (!GetPubKey(provider, sigdata, keyID, pubkey)) return false; + if (!GetPubKey(provider, sigdata, keyID, pubkey)) { + // Pubkey could not be found, add to missing + sigdata.missing_pubkeys.push_back(keyID); + return false; + } if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false; ret.push_back(std::move(sig)); ret.push_back(ToByteVector(pubkey)); return true; } case TX_SCRIPTHASH: - if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) { + h160 = uint160(vSolutions[0]); + if (GetCScript(provider, sigdata, h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } + // Could not find redeemScript, add to missing + sigdata.missing_redeem_script = h160; return false; case TX_MULTISIG: { @@ -154,6 +163,8 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } + // Could not find witnessScript, add to missing + sigdata.missing_witness_script = uint256(vSolutions[0]); return false; default: @@ -232,67 +243,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato return sigdata.complete; } -bool PSBTInputSigned(PSBTInput& input) -{ - return !input.final_script_sig.empty() || !input.final_script_witness.IsNull(); -} - -bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash) -{ - PSBTInput& input = psbt.inputs.at(index); - const CMutableTransaction& tx = *psbt.tx; - - if (PSBTInputSigned(input)) { - return true; - } - - // Fill SignatureData with input info - SignatureData sigdata; - input.FillSignatureData(sigdata); - - // Get UTXO - bool require_witness_sig = false; - CTxOut utxo; - - // Verify input sanity, which checks that at most one of witness or non-witness utxos is provided. - if (!input.IsSane()) { - return false; - } - - if (input.non_witness_utxo) { - // If we're taking our information from a non-witness UTXO, verify that it matches the prevout. - COutPoint prevout = tx.vin[index].prevout; - if (input.non_witness_utxo->GetHash() != prevout.hash) { - return false; - } - utxo = input.non_witness_utxo->vout[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 we have a witness signature, use the smaller witness UTXO. - if (sigdata.witness) { - input.witness_utxo = utxo; - input.non_witness_utxo = nullptr; - } - - return sig_complete; -} - class SignatureExtractorChecker final : public BaseSignatureChecker { private: @@ -509,166 +459,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script) return false; } -PartiallySignedTransaction::PartiallySignedTransaction(const CTransaction& tx) : tx(tx) -{ - inputs.resize(tx.vin.size()); - outputs.resize(tx.vout.size()); -} - -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); @@ -693,7 +483,13 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); } bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); } -bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); } +bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const +{ + std::pair<CPubKey, KeyOriginInfo> out; + bool ret = LookupHelper(origins, keyid, out); + if (ret) info = std::move(out.second); + return ret; +} bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); } FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b) diff --git a/src/script/sign.h b/src/script/sign.h index 20c7203b2..f746325b9 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -22,13 +22,27 @@ struct CMutableTransaction; struct KeyOriginInfo { - unsigned char fingerprint[4]; + unsigned char fingerprint[4]; //!< First 32 bits of the Hash160 of the public key at the root of the path std::vector<uint32_t> path; friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b) { return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path; } + + ADD_SERIALIZE_METHODS; + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(fingerprint); + READWRITE(path); + } + + void clear() + { + memset(fingerprint, 0, 4); + path.clear(); + } }; /** An interface to be implemented by keystores that support signing. */ @@ -63,7 +77,7 @@ struct FlatSigningProvider final : public SigningProvider { std::map<CScriptID, CScript> scripts; std::map<CKeyID, CPubKey> pubkeys; - std::map<CKeyID, KeyOriginInfo> origins; + std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins; std::map<CKeyID, CKey> keys; bool GetCScript(const CScriptID& scriptid, CScript& script) const override; @@ -117,38 +131,16 @@ struct SignatureData { 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; + std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found + std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found + uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any) + uint256 missing_witness_script; ///< SHA256 of the missing witnessScript (if any) 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> @@ -223,514 +215,6 @@ void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_k } } -/** 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 - bool found_sep = false; - 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()) { - found_sep = true; - break; - } - - // 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; - } - } - - if (!found_sep) { - throw std::ios_base::failure("Separator is missing at the end of an input map"); - } - } - - 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 - bool found_sep = false; - 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()) { - found_sep = true; - break; - } - - // 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; - } - } - } - - if (!found_sep) { - throw std::ios_base::failure("Separator is missing at the end of an output map"); - } - } - - 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) {} - explicit PartiallySignedTransaction(const CTransaction& tx); - - // 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 - bool found_sep = false; - 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()) { - found_sep = true; - 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)); - } - } - } - - if (!found_sep) { - throw std::ios_base::failure("Separator is missing at the end of the global map"); - } - - // 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); @@ -738,12 +222,6 @@ 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); -/** Checks whether a PSBTInput is already signed. */ -bool PSBTInputSigned(PSBTInput& input); - -/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */ -bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, 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); |