From ce911204e42b8653cad791d1727aa625de9d0079 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 12 Jul 2021 15:31:12 -0400 Subject: [PATCH 01/18] Move individual KeyOriginInfo de/ser to separate function To make it easier to de/serialize individual KeyOriginInfo for PSBTs, separate the actual de/serialization of KeyOriginInfo to its own function. This is an additional separation where any length prefix is processed by the caller. --- src/psbt.h | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/psbt.h b/src/psbt.h index 4a6d41076f..b8f3e70eee 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -97,22 +97,30 @@ void UnserializeFromVector(Stream& s, X&... args) } } -// Deserialize an individual HD keypath to a stream +// Deserialize bytes of given length from the stream as a KeyOriginInfo template -void DeserializeHDKeypath(Stream& s, KeyOriginInfo& hd_keypath) +KeyOriginInfo DeserializeKeyOrigin(Stream& s, uint64_t length) { // Read in key path - uint64_t value_len = ReadCompactSize(s); - if (value_len % 4 || value_len == 0) { + if (length % 4 || length == 0) { throw std::ios_base::failure("Invalid length for HD key path"); } + KeyOriginInfo hd_keypath; s >> hd_keypath.fingerprint; - for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) { + for (unsigned int i = 4; i < length; i += sizeof(uint32_t)) { uint32_t index; s >> index; hd_keypath.path.push_back(index); } + return hd_keypath; +} + +// Deserialize a length prefixed KeyOriginInfo from a stream +template +void DeserializeHDKeypath(Stream& s, KeyOriginInfo& hd_keypath) +{ + hd_keypath = DeserializeKeyOrigin(s, ReadCompactSize(s)); } // Deserialize HD keypaths into a map @@ -139,17 +147,24 @@ void DeserializeHDKeypaths(Stream& s, const std::vector& key, std hd_keypaths.emplace(pubkey, std::move(keypath)); } -// Serialize an individual HD keypath to a stream +// Serialize a KeyOriginInfo to a stream template -void SerializeHDKeypath(Stream& s, KeyOriginInfo hd_keypath) +void SerializeKeyOrigin(Stream& s, KeyOriginInfo hd_keypath) { - WriteCompactSize(s, (hd_keypath.path.size() + 1) * sizeof(uint32_t)); s << hd_keypath.fingerprint; for (const auto& path : hd_keypath.path) { s << path; } } +// Serialize a length prefixed KeyOriginInfo to a stream +template +void SerializeHDKeypath(Stream& s, KeyOriginInfo hd_keypath) +{ + WriteCompactSize(s, (hd_keypath.path.size() + 1) * sizeof(uint32_t)); + SerializeKeyOrigin(s, hd_keypath); +} + // Serialize HD keypaths to a stream from a map template void SerializeHDKeypaths(Stream& s, const std::map& hd_keypaths, CompactSizeWriter type) From d43923c38155fdadad3837d79c19a84c9d2d7f50 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 12 Jul 2021 17:04:46 -0400 Subject: [PATCH 02/18] Add TaprootBuilder::GetTreeTuples GetTreeTuples returns the leaves in DFS order as tuples of depth, leaf version, and script. This is a representation of the tree that can be serialized. --- src/script/standard.cpp | 16 ++++++++++++++++ src/script/standard.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index e25155d3dd..063e149d36 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -642,3 +642,19 @@ std::optional>> InferTaprootTree(const return ret; } + +std::vector> TaprootBuilder::GetTreeTuples() const +{ + assert(IsComplete()); + std::vector> tuples; + if (m_branch.size()) { + const auto& leaves = m_branch[0]->leaves; + for (const auto& leaf : leaves) { + assert(leaf.merkle_branch.size() <= TAPROOT_CONTROL_MAX_NODE_COUNT); + uint8_t depth = (uint8_t)leaf.merkle_branch.size(); + uint8_t leaf_ver = (uint8_t)leaf.leaf_version; + tuples.push_back(std::make_tuple(depth, leaf_ver, leaf.script)); + } + } + return tuples; +} diff --git a/src/script/standard.h b/src/script/standard.h index 6a15ba4e3d..448fdff010 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -322,6 +322,8 @@ public: static bool ValidDepths(const std::vector& depths); /** Compute spending data (after Finalize()). */ TaprootSpendData GetSpendData() const; + /** Returns a vector of tuples representing the depth, leaf version, and script */ + std::vector> GetTreeTuples() const; }; /** Given a TaprootSpendData and the output key, reconstruct its script tree. From d557eff2add151781537978e27d6f1aff1b83ef7 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 12 Jul 2021 17:06:20 -0400 Subject: [PATCH 03/18] Add serialization methods to XOnlyPubKey It is useful to have serialzation methods for XOnlyPubKey. These will serialize the internal uint256, so it is not prefixed with the length as CPubKey does. --- src/pubkey.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pubkey.h b/src/pubkey.h index dfe06f834c..463efe1b00 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -286,6 +286,9 @@ public: bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; } bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; } bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; } + + //! Implement serialization without length prefixes since it is a fixed length + SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); } }; struct CExtPubKey { From 05e2cc9a302ba7f14fc65ba255594c047cb44559 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 12 Jul 2021 17:07:08 -0400 Subject: [PATCH 04/18] Implement de/ser of PSBT's Taproot fields --- src/psbt.h | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) diff --git a/src/psbt.h b/src/psbt.h index b8f3e70eee..a143a99988 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -40,12 +40,21 @@ static constexpr uint8_t PSBT_IN_RIPEMD160 = 0x0A; static constexpr uint8_t PSBT_IN_SHA256 = 0x0B; static constexpr uint8_t PSBT_IN_HASH160 = 0x0C; static constexpr uint8_t PSBT_IN_HASH256 = 0x0D; +static constexpr uint8_t PSBT_IN_TAP_KEY_SIG = 0x13; +static constexpr uint8_t PSBT_IN_TAP_SCRIPT_SIG = 0x14; +static constexpr uint8_t PSBT_IN_TAP_LEAF_SCRIPT = 0x15; +static constexpr uint8_t PSBT_IN_TAP_BIP32_DERIVATION = 0x16; +static constexpr uint8_t PSBT_IN_TAP_INTERNAL_KEY = 0x17; +static constexpr uint8_t PSBT_IN_TAP_MERKLE_ROOT = 0x18; static constexpr uint8_t PSBT_IN_PROPRIETARY = 0xFC; // 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; +static constexpr uint8_t PSBT_OUT_TAP_INTERNAL_KEY = 0x05; +static constexpr uint8_t PSBT_OUT_TAP_TREE = 0x06; +static constexpr uint8_t PSBT_OUT_TAP_BIP32_DERIVATION = 0x07; static constexpr uint8_t PSBT_OUT_PROPRIETARY = 0xFC; // The separator is 0x00. Reading this in means that the unserializer can interpret it @@ -193,6 +202,15 @@ struct PSBTInput std::map> sha256_preimages; std::map> hash160_preimages; std::map> hash256_preimages; + + // Taproot fields + std::vector m_tap_key_sig; + std::map, std::vector> m_tap_script_sigs; + std::map, std::set, ShortestVectorFirstComparator>> m_tap_scripts; + std::map, KeyOriginInfo>> m_tap_bip32_paths; + XOnlyPubKey m_tap_internal_key; + uint256 m_tap_merkle_root; + std::map, std::vector> unknown; std::set m_proprietary; std::optional sighash_type; @@ -267,6 +285,53 @@ struct PSBTInput SerializeToVector(s, CompactSizeWriter(PSBT_IN_HASH256), Span{hash}); s << preimage; } + + // Write taproot key sig + if (!m_tap_key_sig.empty()) { + SerializeToVector(s, PSBT_IN_TAP_KEY_SIG); + s << m_tap_key_sig; + } + + // Write taproot script sigs + for (const auto& [pubkey_leaf, sig] : m_tap_script_sigs) { + const auto& [xonly, leaf_hash] = pubkey_leaf; + SerializeToVector(s, PSBT_IN_TAP_SCRIPT_SIG, xonly, leaf_hash); + s << sig; + } + + // Write taproot leaf scripts + for (const auto& [leaf, control_blocks] : m_tap_scripts) { + const auto& [script, leaf_ver] = leaf; + for (const auto& control_block : control_blocks) { + SerializeToVector(s, PSBT_IN_TAP_LEAF_SCRIPT, Span{control_block}); + std::vector value_v(script.begin(), script.end()); + value_v.push_back((uint8_t)leaf_ver); + s << value_v; + } + } + + // Write taproot bip32 keypaths + for (const auto& [xonly, leaf_origin] : m_tap_bip32_paths) { + const auto& [leaf_hashes, origin] = leaf_origin; + SerializeToVector(s, PSBT_IN_TAP_BIP32_DERIVATION, xonly); + std::vector value; + CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0); + s_value << leaf_hashes; + SerializeKeyOrigin(s_value, origin); + s << value; + } + + // Write taproot internal key + if (!m_tap_internal_key.IsNull()) { + SerializeToVector(s, PSBT_IN_TAP_INTERNAL_KEY); + s << ToByteVector(m_tap_internal_key); + } + + // Write taproot merkle root + if (!m_tap_merkle_root.IsNull()) { + SerializeToVector(s, PSBT_IN_TAP_MERKLE_ROOT); + SerializeToVector(s, m_tap_merkle_root); + } } // Write script sig @@ -503,6 +568,103 @@ struct PSBTInput hash256_preimages.emplace(hash, std::move(preimage)); break; } + case PSBT_IN_TAP_KEY_SIG: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, input Taproot key signature already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input Taproot key signature key is more than one byte type"); + } + s >> m_tap_key_sig; + if (m_tap_key_sig.size() < 64) { + throw std::ios_base::failure("Input Taproot key path signature is shorter than 64 bytes"); + } else if (m_tap_key_sig.size() > 65) { + throw std::ios_base::failure("Input Taproot key path signature is longer than 65 bytes"); + } + break; + } + case PSBT_IN_TAP_SCRIPT_SIG: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, input Taproot script signature already provided"); + } else if (key.size() != 65) { + throw std::ios_base::failure("Input Taproot script signature key is not 65 bytes"); + } + SpanReader s_key(s.GetType(), s.GetVersion(), Span{key}.subspan(1)); + XOnlyPubKey xonly; + uint256 hash; + s_key >> xonly; + s_key >> hash; + std::vector sig; + s >> sig; + if (sig.size() < 64) { + throw std::ios_base::failure("Input Taproot script path signature is shorter than 64 bytes"); + } else if (sig.size() > 65) { + throw std::ios_base::failure("Input Taproot script path signature is longer than 65 bytes"); + } + m_tap_script_sigs.emplace(std::make_pair(xonly, hash), sig); + break; + } + case PSBT_IN_TAP_LEAF_SCRIPT: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, input Taproot leaf script already provided"); + } else if (key.size() < 34) { + throw std::ios_base::failure("Taproot leaf script key is not at least 34 bytes"); + } else if ((key.size() - 2) % 32 != 0) { + throw std::ios_base::failure("Input Taproot leaf script key's control block size is not valid"); + } + std::vector script_v; + s >> script_v; + if (script_v.empty()) { + throw std::ios_base::failure("Input Taproot leaf script must be at least 1 byte"); + } + uint8_t leaf_ver = script_v.back(); + script_v.pop_back(); + const auto leaf_script = std::make_pair(CScript(script_v.begin(), script_v.end()), (int)leaf_ver); + m_tap_scripts[leaf_script].insert(std::vector(key.begin() + 1, key.end())); + break; + } + case PSBT_IN_TAP_BIP32_DERIVATION: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, input Taproot BIP32 keypath already provided"); + } else if (key.size() != 33) { + throw std::ios_base::failure("Input Taproot BIP32 keypath key is not at 33 bytes"); + } + SpanReader s_key(s.GetType(), s.GetVersion(), Span{key}.subspan(1)); + XOnlyPubKey xonly; + s_key >> xonly; + std::set leaf_hashes; + uint64_t value_len = ReadCompactSize(s); + size_t before_hashes = s.size(); + s >> leaf_hashes; + size_t after_hashes = s.size(); + size_t hashes_len = before_hashes - after_hashes; + size_t origin_len = value_len - hashes_len; + m_tap_bip32_paths.emplace(xonly, std::make_pair(leaf_hashes, DeserializeKeyOrigin(s, origin_len))); + break; + } + case PSBT_IN_TAP_INTERNAL_KEY: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, input Taproot internal key already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input Taproot internal key key is more than one byte type"); + } + UnserializeFromVector(s, m_tap_internal_key); + break; + } + case PSBT_IN_TAP_MERKLE_ROOT: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, input Taproot merkle root already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input Taproot merkle root key is more than one byte type"); + } + UnserializeFromVector(s, m_tap_merkle_root); + break; + } case PSBT_IN_PROPRIETARY: { PSBTProprietary this_prop; @@ -547,6 +709,9 @@ struct PSBTOutput CScript redeem_script; CScript witness_script; std::map hd_keypaths; + XOnlyPubKey m_tap_internal_key; + std::optional m_tap_tree; + std::map, KeyOriginInfo>> m_tap_bip32_paths; std::map, std::vector> unknown; std::set m_proprietary; @@ -579,6 +744,40 @@ struct PSBTOutput s << entry.value; } + // Write taproot internal key + if (!m_tap_internal_key.IsNull()) { + SerializeToVector(s, PSBT_OUT_TAP_INTERNAL_KEY); + s << ToByteVector(m_tap_internal_key); + } + + // Write taproot tree + if (m_tap_tree.has_value()) { + SerializeToVector(s, PSBT_OUT_TAP_TREE); + std::vector value; + CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0); + const auto& tuples = m_tap_tree->GetTreeTuples(); + for (const auto& tuple : tuples) { + uint8_t depth = std::get<0>(tuple); + uint8_t leaf_ver = std::get<1>(tuple); + CScript script = std::get<2>(tuple); + s_value << depth; + s_value << leaf_ver; + s_value << script; + } + s << value; + } + + // Write taproot bip32 keypaths + for (const auto& [xonly, leaf] : m_tap_bip32_paths) { + const auto& [leaf_hashes, origin] = leaf; + SerializeToVector(s, PSBT_OUT_TAP_BIP32_DERIVATION, xonly); + std::vector value; + CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0); + s_value << leaf_hashes; + SerializeKeyOrigin(s_value, origin); + s << value; + } + // Write unknown things for (auto& entry : unknown) { s << entry.first; @@ -639,6 +838,59 @@ struct PSBTOutput DeserializeHDKeypaths(s, key, hd_keypaths); break; } + case PSBT_OUT_TAP_INTERNAL_KEY: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, output Taproot internal key already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Output Taproot internal key key is more than one byte type"); + } + UnserializeFromVector(s, m_tap_internal_key); + break; + } + case PSBT_OUT_TAP_TREE: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, output Taproot tree already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Output Taproot tree key is more than one byte type"); + } + m_tap_tree.emplace(); + std::vector tree_v; + s >> tree_v; + SpanReader s_tree(s.GetType(), s.GetVersion(), tree_v); + while (!s_tree.empty()) { + uint8_t depth; + uint8_t leaf_ver; + CScript script; + s_tree >> depth; + s_tree >> leaf_ver; + s_tree >> script; + m_tap_tree->Add((int)depth, script, (int)leaf_ver, true /* track */); + } + if (!m_tap_tree->IsComplete()) { + throw std::ios_base::failure("Output Taproot tree is malformed"); + } + break; + } + case PSBT_OUT_TAP_BIP32_DERIVATION: + { + if (!key_lookup.emplace(key).second) { + throw std::ios_base::failure("Duplicate Key, output Taproot BIP32 keypath already provided"); + } else if (key.size() != 33) { + throw std::ios_base::failure("Output Taproot BIP32 keypath key is not at 33 bytes"); + } + XOnlyPubKey xonly(uint256({key.begin() + 1, key.begin() + 33})); + std::set leaf_hashes; + uint64_t value_len = ReadCompactSize(s); + size_t before_hashes = s.size(); + s >> leaf_hashes; + size_t after_hashes = s.size(); + size_t hashes_len = before_hashes - after_hashes; + size_t origin_len = value_len - hashes_len; + m_tap_bip32_paths.emplace(xonly, std::make_pair(leaf_hashes, DeserializeKeyOrigin(s, origin_len))); + break; + } case PSBT_OUT_PROPRIETARY: { PSBTProprietary this_prop; @@ -667,6 +919,11 @@ struct PSBTOutput } } + // Finalize m_tap_tree so that all of the computed things are computed + if (m_tap_tree.has_value() && m_tap_tree->IsComplete() && m_tap_internal_key.IsFullyValid()) { + m_tap_tree->Finalize(m_tap_internal_key); + } + if (!found_sep) { throw std::ios_base::failure("Separator is missing at the end of an output map"); } From 52e3f2f88ef1ac7062e905bf2d745b70463ee3e9 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 19 Jul 2021 15:29:29 -0400 Subject: [PATCH 05/18] Fill PSBT Taproot input data to/from SignatureData --- src/psbt.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/script/sign.h | 1 + 2 files changed, 37 insertions(+) diff --git a/src/psbt.cpp b/src/psbt.cpp index c1c8a385cc..ba32d7acae 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -113,6 +113,24 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const for (const auto& key_pair : hd_keypaths) { sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } + if (!m_tap_key_sig.empty()) { + sigdata.taproot_key_path_sig = m_tap_key_sig; + } + for (const auto& [pubkey_leaf, sig] : m_tap_script_sigs) { + sigdata.taproot_script_sigs.emplace(pubkey_leaf, sig); + } + if (!m_tap_internal_key.IsNull()) { + sigdata.tr_spenddata.internal_key = m_tap_internal_key; + } + if (!m_tap_merkle_root.IsNull()) { + sigdata.tr_spenddata.merkle_root = m_tap_merkle_root; + } + for (const auto& [leaf_script, control_block] : m_tap_scripts) { + sigdata.tr_spenddata.scripts.emplace(leaf_script, control_block); + } + for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) { + sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin); + } } void PSBTInput::FromSignatureData(const SignatureData& sigdata) @@ -142,6 +160,24 @@ void PSBTInput::FromSignatureData(const SignatureData& sigdata) for (const auto& entry : sigdata.misc_pubkeys) { hd_keypaths.emplace(entry.second); } + if (!sigdata.taproot_key_path_sig.empty()) { + m_tap_key_sig = sigdata.taproot_key_path_sig; + } + for (const auto& [pubkey_leaf, sig] : sigdata.taproot_script_sigs) { + m_tap_script_sigs.emplace(pubkey_leaf, sig); + } + if (!sigdata.tr_spenddata.internal_key.IsNull()) { + m_tap_internal_key = sigdata.tr_spenddata.internal_key; + } + if (!sigdata.tr_spenddata.merkle_root.IsNull()) { + m_tap_merkle_root = sigdata.tr_spenddata.merkle_root; + } + for (const auto& [leaf_script, control_block] : sigdata.tr_spenddata.scripts) { + m_tap_scripts.emplace(leaf_script, control_block); + } + for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) { + m_tap_bip32_paths.emplace(pubkey, leaf_origin); + } } void PSBTInput::Merge(const PSBTInput& input) diff --git a/src/script/sign.h b/src/script/sign.h index 71203d08ec..cb3c229298 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -74,6 +74,7 @@ struct SignatureData { std::map> misc_pubkeys; std::vector taproot_key_path_sig; /// Schnorr signature for key path spending std::map, std::vector> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash. + std::map, KeyOriginInfo>> taproot_misc_pubkeys; ///< Miscellaneous Taproot pubkeys involved in this input along with their leaf script hashes and key origin data. Also includes the Taproot internal key (may have no leaf script hashes). std::vector missing_pubkeys; ///< KeyIDs of pubkeys which could not be found std::vector missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any) From 4d1223e5123e60be93b5ad42ba0aee72d0612ea7 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 19 Jul 2021 15:29:55 -0400 Subject: [PATCH 06/18] Fetch key origins for Taproot keys --- src/script/sign.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 2d569d674a..e46d9be7d4 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -169,6 +169,17 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu // OP_CHECKSIG if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) { XOnlyPubKey pubkey{Span{script}.subspan(1, 32)}; + + KeyOriginInfo info; + if (provider.GetKeyOriginByXOnly(pubkey, info)) { + auto it = sigdata.taproot_misc_pubkeys.find(pubkey); + if (it == sigdata.taproot_misc_pubkeys.end()) { + sigdata.taproot_misc_pubkeys.emplace(pubkey, std::make_pair(std::set({leaf_hash}), info)); + } else { + it->second.first.insert(leaf_hash); + } + } + std::vector sig; if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) { result = Vector(std::move(sig)); @@ -213,6 +224,14 @@ static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCrea // Try key path spending. { + KeyOriginInfo info; + if (provider.GetKeyOriginByXOnly(spenddata.internal_key, info)) { + auto it = sigdata.taproot_misc_pubkeys.find(spenddata.internal_key); + if (it == sigdata.taproot_misc_pubkeys.end()) { + sigdata.taproot_misc_pubkeys.emplace(spenddata.internal_key, std::make_pair(std::set(), info)); + } + } + std::vector sig; if (sigdata.taproot_key_path_sig.size() == 0) { if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) { From 3ae5b6af21cf45b3da5e341e84f50e0717eaf589 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 19 Jul 2021 16:01:12 -0400 Subject: [PATCH 07/18] Store TaprootBuilder in SigningProviders instead of TaprootSpendData TaprootSpendData can be gotten from TaprootBuilder, however for PSBT, we also need TaprootBuilders directly (for the outputs). So we store the TaprootBuilder in the FlatSigningProvider and when the TaprootSpendData is needed, we generate it on the fly using the stored builder. --- src/script/descriptor.cpp | 2 +- src/script/signingprovider.cpp | 21 ++++++++++++++++----- src/script/signingprovider.h | 5 ++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index cece0b60ce..ca0170c84b 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -882,7 +882,7 @@ protected: if (!xpk.IsFullyValid()) return {}; builder.Finalize(xpk); WitnessV1Taproot output = builder.GetOutput(); - out.tr_spenddata[output].Merge(builder.GetSpendData()); + out.tr_trees[output] = builder; out.pubkeys.emplace(keys[0].GetID(), keys[0]); return Vector(GetScriptForDestination(output)); } diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index 552934e0eb..c624a17628 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -48,6 +48,10 @@ bool HidingSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, T { return m_provider->GetTaprootSpendData(output_key, spenddata); } +bool HidingSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const +{ + return m_provider->GetTaprootBuilder(output_key, builder); +} 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); } @@ -61,7 +65,16 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); } bool FlatSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { - return LookupHelper(tr_spenddata, output_key, spenddata); + TaprootBuilder builder; + if (LookupHelper(tr_trees, output_key, builder)) { + spenddata = builder.GetSpendData(); + return true; + } + return false; +} +bool FlatSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const +{ + return LookupHelper(tr_trees, output_key, builder); } FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b) @@ -75,10 +88,8 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide ret.keys.insert(b.keys.begin(), b.keys.end()); ret.origins = a.origins; ret.origins.insert(b.origins.begin(), b.origins.end()); - ret.tr_spenddata = a.tr_spenddata; - for (const auto& [output_key, spenddata] : b.tr_spenddata) { - ret.tr_spenddata[output_key].Merge(spenddata); - } + ret.tr_trees = a.tr_trees; + ret.tr_trees.insert(b.tr_trees.begin(), b.tr_trees.end()); return ret; } diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index f1bded1a8c..792cc903f2 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -25,6 +25,7 @@ public: virtual bool HaveKey(const CKeyID &address) const { return false; } virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; } virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; } + virtual bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const { return false; } bool GetKeyByXOnly(const XOnlyPubKey& pubkey, CKey& key) const { @@ -67,6 +68,7 @@ public: bool GetKey(const CKeyID& keyid, CKey& key) const override; bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override; + bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override; }; struct FlatSigningProvider final : public SigningProvider @@ -75,13 +77,14 @@ struct FlatSigningProvider final : public SigningProvider std::map pubkeys; std::map> origins; std::map keys; - std::map tr_spenddata; /** Map from output key to spend data. */ + std::map tr_trees; /** Map from output key to Taproot tree (which can then make the TaprootSpendData */ bool GetCScript(const CScriptID& scriptid, CScript& script) const override; bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; bool GetKey(const CKeyID& keyid, CKey& key) const override; bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override; + bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override; }; FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b); From 25b6ae46e7249a1b363ef4fb12375f368903c58e Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 24 Jun 2022 12:43:06 -0400 Subject: [PATCH 08/18] Assert that TaprootBuilder is Finalized during GetSpendData GetSpendData needs to be finalized in order to be used. To avoid future bugs, assert `!m_output_key.IsNull()` as m_output_key is only set during Finalize. --- src/script/standard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 063e149d36..5d80891485 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -485,6 +485,7 @@ WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_ TaprootSpendData TaprootBuilder::GetSpendData() const { assert(IsComplete()); + assert(m_output_key.IsFullyValid()); TaprootSpendData spd; spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash; spd.internal_key = m_internal_key; From ac7747585fb0629be502a089c9c9be876bd7107d Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 19 Jul 2021 16:02:36 -0400 Subject: [PATCH 09/18] Fill PSBT Taproot output data to/from SignatureData --- src/psbt.cpp | 18 ++++++++++++++++++ src/script/sign.cpp | 4 ++++ src/script/sign.h | 1 + 3 files changed, 23 insertions(+) diff --git a/src/psbt.cpp b/src/psbt.cpp index ba32d7acae..e4bac4aa63 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -213,6 +213,15 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const for (const auto& key_pair : hd_keypaths) { sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } + if (m_tap_tree.has_value() && m_tap_internal_key.IsFullyValid()) { + TaprootSpendData spenddata = m_tap_tree->GetSpendData(); + + sigdata.tr_spenddata.internal_key = m_tap_internal_key; + sigdata.tr_spenddata.Merge(spenddata); + } + for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) { + sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin); + } } void PSBTOutput::FromSignatureData(const SignatureData& sigdata) @@ -226,6 +235,15 @@ void PSBTOutput::FromSignatureData(const SignatureData& sigdata) for (const auto& entry : sigdata.misc_pubkeys) { hd_keypaths.emplace(entry.second); } + if (!sigdata.tr_spenddata.internal_key.IsNull()) { + m_tap_internal_key = sigdata.tr_spenddata.internal_key; + } + if (sigdata.tr_builder.has_value()) { + m_tap_tree = sigdata.tr_builder; + } + for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) { + m_tap_bip32_paths.emplace(pubkey, leaf_origin); + } } bool PSBTOutput::IsNull() const diff --git a/src/script/sign.cpp b/src/script/sign.cpp index e46d9be7d4..5cd773f6ee 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -216,11 +216,15 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector& result) { TaprootSpendData spenddata; + TaprootBuilder builder; // Gather information about this output. if (provider.GetTaprootSpendData(output, spenddata)) { sigdata.tr_spenddata.Merge(spenddata); } + if (provider.GetTaprootBuilder(output, builder)) { + sigdata.tr_builder = builder; + } // Try key path spending. { diff --git a/src/script/sign.h b/src/script/sign.h index cb3c229298..5e58272154 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -70,6 +70,7 @@ struct SignatureData { CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. TaprootSpendData tr_spenddata; ///< Taproot spending data. + std::optional tr_builder; ///< Taproot tree used to build tr_spenddata. std::map signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. std::map> misc_pubkeys; std::vector taproot_key_path_sig; /// Schnorr signature for key path spending From 7dccdd3157a87f55f5398316b98f909d6d6f1feb Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 19 Jul 2021 16:54:16 -0400 Subject: [PATCH 10/18] Implement decodepsbt for Taproot fields --- src/rpc/rawtransaction.cpp | 169 ++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b9b8c36bb3..792a1e13b0 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -790,6 +790,43 @@ static RPCHelpMan decodepsbt() { {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."}, }}, + {RPCResult::Type::STR_HEX, "taproot_key_path_sig", /*optional=*/ true, "hex-encoded signature for the Taproot key path spend"}, + {RPCResult::Type::ARR, "taproot_script_path_sigs", /*optional=*/ true, "", + { + {RPCResult::Type::OBJ, "signature", /*optional=*/ true, "The signature for the pubkey and leaf hash combination", + { + {RPCResult::Type::STR, "pubkey", "The x-only pubkey for this signature"}, + {RPCResult::Type::STR, "leaf_hash", "The leaf hash for this signature"}, + {RPCResult::Type::STR, "sig", "The signature itself"}, + }}, + }}, + {RPCResult::Type::ARR, "taproot_scripts", /*optional=*/ true, "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "script", "A leaf script"}, + {RPCResult::Type::NUM, "leaf_ver", "The version number for the leaf script"}, + {RPCResult::Type::ARR, "control_blocks", "The control blocks for this script", + { + {RPCResult::Type::STR_HEX, "control_block", "A hex-encoded control block for this script"}, + }}, + }}, + }}, + {RPCResult::Type::ARR, "taproot_bip32_derivs", /*optional=*/ true, "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "pubkey", "The x-only public key this path corresponds to"}, + {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + {RPCResult::Type::ARR, "leaf_hashes", "The hashes of the leaves this pubkey appears in", + { + {RPCResult::Type::STR_HEX, "hash", "The hash of a leaf this pubkey appears in"}, + }}, + }}, + }}, + {RPCResult::Type::STR_HEX, "taproot_internal_key", /*optional=*/ true, "The hex-encoded Taproot x-only internal key"}, + {RPCResult::Type::STR_HEX, "taproot_merkle_root", /*optional=*/ true, "The hex-encoded Taproot merkle root"}, {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, @@ -831,7 +868,30 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR, "path", "The path"}, }}, }}, - {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown global fields", + {RPCResult::Type::STR_HEX, "taproot_internal_key", /*optional=*/ true, "The hex-encoded Taproot x-only internal key"}, + {RPCResult::Type::ARR, "taproot_tree", /*optional=*/ true, "The tuples that make up the Taproot tree, in depth first search order", + { + {RPCResult::Type::OBJ, "tuple", /*optional=*/ true, "A single leaf script in the taproot tree", + { + {RPCResult::Type::NUM, "depth", "The depth of this element in the tree"}, + {RPCResult::Type::NUM, "leaf_ver", "The version of this leaf"}, + {RPCResult::Type::STR, "script", "The hex-encoded script itself"}, + }}, + }}, + {RPCResult::Type::ARR, "taproot_bip32_derivs", /*optional=*/ true, "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "pubkey", "The x-only public key this path corresponds to"}, + {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + {RPCResult::Type::ARR, "leaf_hashes", "The hashes of the leaves this pubkey appears in", + { + {RPCResult::Type::STR_HEX, "hash", "The hash of a leaf this pubkey appears in"}, + }}, + }}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown output fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, }}, @@ -1045,6 +1105,72 @@ static RPCHelpMan decodepsbt() in.pushKV("hash256_preimages", hash256_preimages); } + // Taproot key path signature + if (!input.m_tap_key_sig.empty()) { + in.pushKV("taproot_key_path_sig", HexStr(input.m_tap_key_sig)); + } + + // Taproot script path signatures + if (!input.m_tap_script_sigs.empty()) { + UniValue script_sigs(UniValue::VARR); + for (const auto& [pubkey_leaf, sig] : input.m_tap_script_sigs) { + const auto& [xonly, leaf_hash] = pubkey_leaf; + UniValue sigobj(UniValue::VOBJ); + sigobj.pushKV("pubkey", HexStr(xonly)); + sigobj.pushKV("leaf_hash", HexStr(leaf_hash)); + sigobj.pushKV("sig", HexStr(sig)); + script_sigs.push_back(sigobj); + } + in.pushKV("taproot_script_path_sigs", script_sigs); + } + + // Taproot leaf scripts + if (!input.m_tap_scripts.empty()) { + UniValue tap_scripts(UniValue::VARR); + for (const auto& [leaf, control_blocks] : input.m_tap_scripts) { + const auto& [script, leaf_ver] = leaf; + UniValue script_info(UniValue::VOBJ); + script_info.pushKV("script", HexStr(script)); + script_info.pushKV("leaf_ver", leaf_ver); + UniValue control_blocks_univ(UniValue::VARR); + for (const auto& control_block : control_blocks) { + control_blocks_univ.push_back(HexStr(control_block)); + } + script_info.pushKV("control_blocks", control_blocks_univ); + tap_scripts.push_back(script_info); + } + in.pushKV("taproot_scripts", tap_scripts); + } + + // Taproot bip32 keypaths + if (!input.m_tap_bip32_paths.empty()) { + UniValue keypaths(UniValue::VARR); + for (const auto& [xonly, leaf_origin] : input.m_tap_bip32_paths) { + const auto& [leaf_hashes, origin] = leaf_origin; + UniValue path_obj(UniValue::VOBJ); + path_obj.pushKV("pubkey", HexStr(xonly)); + path_obj.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(origin.fingerprint))); + path_obj.pushKV("path", WriteHDKeypath(origin.path)); + UniValue leaf_hashes_arr(UniValue::VARR); + for (const auto& leaf_hash : leaf_hashes) { + leaf_hashes_arr.push_back(HexStr(leaf_hash)); + } + path_obj.pushKV("leaf_hashes", leaf_hashes_arr); + keypaths.push_back(path_obj); + } + in.pushKV("taproot_bip32_derivs", keypaths); + } + + // Taproot internal key + if (!input.m_tap_internal_key.IsNull()) { + in.pushKV("taproot_internal_key", HexStr(input.m_tap_internal_key)); + } + + // Write taproot merkle root + if (!input.m_tap_merkle_root.IsNull()) { + in.pushKV("taproot_merkle_root", HexStr(input.m_tap_merkle_root)); + } + // Proprietary if (!input.m_proprietary.empty()) { UniValue proprietary(UniValue::VARR); @@ -1103,6 +1229,47 @@ static RPCHelpMan decodepsbt() out.pushKV("bip32_derivs", keypaths); } + // Taproot internal key + if (!output.m_tap_internal_key.IsNull()) { + out.pushKV("taproot_internal_key", HexStr(output.m_tap_internal_key)); + } + + // Taproot tree + if (output.m_tap_tree.has_value()) { + UniValue tree(UniValue::VARR); + const auto& tuples = output.m_tap_tree->GetTreeTuples(); + for (const auto& tuple : tuples) { + uint8_t depth = std::get<0>(tuple); + uint8_t leaf_ver = std::get<1>(tuple); + CScript script = std::get<2>(tuple); + UniValue elem(UniValue::VOBJ); + elem.pushKV("depth", (int)depth); + elem.pushKV("leaf_ver", (int)leaf_ver); + elem.pushKV("script", HexStr(script)); + tree.push_back(elem); + } + out.pushKV("taproot_tree", tree); + } + + // Taproot bip32 keypaths + if (!output.m_tap_bip32_paths.empty()) { + UniValue keypaths(UniValue::VARR); + for (const auto& [xonly, leaf_origin] : output.m_tap_bip32_paths) { + const auto& [leaf_hashes, origin] = leaf_origin; + UniValue path_obj(UniValue::VOBJ); + path_obj.pushKV("pubkey", HexStr(xonly)); + path_obj.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(origin.fingerprint))); + path_obj.pushKV("path", WriteHDKeypath(origin.path)); + UniValue leaf_hashes_arr(UniValue::VARR); + for (const auto& leaf_hash : leaf_hashes) { + leaf_hashes_arr.push_back(HexStr(leaf_hash)); + } + path_obj.pushKV("leaf_hashes", leaf_hashes_arr); + keypaths.push_back(path_obj); + } + out.pushKV("taproot_bip32_derivs", keypaths); + } + // Proprietary if (!output.m_proprietary.empty()) { UniValue proprietary(UniValue::VARR); From 103c6fd2791f7e73eeab7f3900fbedd5b550211d Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 20 Jul 2021 20:04:33 -0400 Subject: [PATCH 11/18] psbt: Remove non_witness_utxo for segwit v1+ If all inputs are segwit v1+, the non_witness_utxos can be removed. --- src/psbt.cpp | 6 +++--- src/wallet/wallet.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/psbt.cpp b/src/psbt.cpp index e4bac4aa63..98fde4cb04 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -184,7 +184,6 @@ 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()) { - // TODO: For segwit v1, we will want to clear out the non-witness utxo when setting a witness one. For v0 and non-segwit, this is not safe witness_utxo = input.witness_utxo; } @@ -367,10 +366,11 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& input.FromSignatureData(sigdata); // If we have a witness signature, put a witness UTXO. - // TODO: For segwit v1, we should remove the non_witness_utxo if (sigdata.witness) { input.witness_utxo = utxo; - // input.non_witness_utxo = nullptr; + // We can remove the non_witness_utxo if and only if there are no non-segwit or segwit v0 + // inputs in this transaction. Since this requires inspecting the entire transaction, this + // is something for the caller to deal with (i.e. FillPSBT). } // Fill in the missing info diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d0b093bbb7..041481559b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2006,6 +2006,35 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp } } + // Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY + if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) { + // Figure out if any non_witness_utxos should be dropped + std::vector to_drop; + for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { + const auto& input = psbtx.inputs.at(i); + int wit_ver; + std::vector wit_prog; + if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) { + // There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos + to_drop.clear(); + break; + } + if (wit_ver == 0) { + // Segwit v0, so we cannot drop any non_witness_utxos + to_drop.clear(); + break; + } + if (input.non_witness_utxo) { + to_drop.push_back(i); + } + } + + // Drop the non_witness_utxos that we can drop + for (unsigned int i : to_drop) { + psbtx.inputs.at(i).non_witness_utxo = nullptr; + } + } + // Complete if every input is now signed complete = true; for (const auto& input : psbtx.inputs) { From 0ad21e7c558da47f50d6b39974d0d2713e829d25 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 23 Jul 2021 18:50:54 -0400 Subject: [PATCH 12/18] tests: Test taproot fields for PSBT --- test/functional/data/rpc_psbt.json | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/functional/data/rpc_psbt.json b/test/functional/data/rpc_psbt.json index 8672400a92..430a1802a8 100644 --- a/test/functional/data/rpc_psbt.json +++ b/test/functional/data/rpc_psbt.json @@ -27,7 +27,18 @@ "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA=", "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA=", "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA=", - "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA=" + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA=", + "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARchAv40kGTJjW4qhT+jybEr2LMEoZwZXGDvp+4jkwRtP6IyAAAA", + "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARM/Fzuz02wHSvtxb+xjB6BpouRQuZXzyCeFlFq43w4kJg3NcDsMvzTeOZGEqUgawrNYbbZgHwJqd/fkk4SBvDR1AAAA", + "cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARNCFzuz02wHSvtxb+xjB6BpouRQuZXzyCeFlFq43w4kJg3NcDsMvzTeOZGEqUgawrNYbbZgHwJqd/fkk4SBvDR1FwGqAAAA", + "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" ], "valid" : [ "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA", @@ -43,7 +54,13 @@ "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" ], "creator" : [ { From 496a1bbe5e442ffc0948f626cca1b85a46ef58db Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 26 Jul 2021 16:23:50 -0400 Subject: [PATCH 13/18] taproot: Use pre-existing signatures if available Actually use pre-existing signatures in CreateTaprootScriptSig if a signature is found for the given key and leaf hash. --- src/script/sign.cpp | 1 + test/functional/wallet_taproot.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 5cd773f6ee..12f2ec4d3a 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -150,6 +150,7 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur auto it = sigdata.taproot_script_sigs.find(lookup_key); if (it != sigdata.taproot_script_sigs.end()) { sig_out = it->second; + return true; } if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) { sigdata.taproot_script_sigs[lookup_key] = sig_out; diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index d238c50bca..845953926b 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -442,8 +442,7 @@ class WalletTaprootTest(BitcoinTestFramework): assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0) psbt = self.psbt_online.sendall(recipients=[self.boring.getnewaddress()], options={"psbt": True})["psbt"] - res = self.psbt_offline.walletprocesspsbt(psbt) - assert(res['complete']) + res = self.psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False) rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] txid = self.nodes[0].sendrawtransaction(rawtx) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) From 1ece9a371510d887ed9612f2d219f8dfae278658 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 26 Jul 2021 16:25:42 -0400 Subject: [PATCH 14/18] psbt, test: Check for taproot fields in taproot psbt test --- test/functional/wallet_taproot.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index 845953926b..6af9c9d2ce 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -301,9 +301,21 @@ class WalletTaprootTest(BitcoinTestFramework): test_balance = int(self.psbt_online.getbalance() * 100000000) ret_amnt = random.randrange(100000, test_balance) # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. - psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200})['psbt'] - res = self.psbt_offline.walletprocesspsbt(psbt) - assert(res['complete']) + psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200, "change_type": "bech32m"})['psbt'] + res = self.psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False) + + decoded = self.psbt_offline.decodepsbt(res["psbt"]) + if pattern.startswith("tr("): + for psbtin in decoded["inputs"]: + assert "non_witness_utxo" not in psbtin + assert "witness_utxo" in psbtin + assert "taproot_internal_key" in psbtin + assert "taproot_bip32_derivs" in psbtin + assert "taproot_key_path_sig" in psbtin or "taproot_script_path_sigs" in psbtin + if "taproot_script_path_sigs" in psbtin: + assert "taproot_merkle_root" in psbtin + assert "taproot_scripts" in psbtin + rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] txid = self.nodes[0].sendrawtransaction(rawtx) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) From 5f12fe3f36bc8a9ad2733986d9493354265a525c Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Wed, 24 Nov 2021 22:50:10 -0500 Subject: [PATCH 15/18] psbt: Implement merge for Taproot fields --- src/psbt.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/psbt.cpp b/src/psbt.cpp index 98fde4cb04..36fec74bc9 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -194,11 +194,17 @@ void PSBTInput::Merge(const PSBTInput& input) hash256_preimages.insert(input.hash256_preimages.begin(), input.hash256_preimages.end()); hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end()); unknown.insert(input.unknown.begin(), input.unknown.end()); + m_tap_script_sigs.insert(input.m_tap_script_sigs.begin(), input.m_tap_script_sigs.end()); + m_tap_scripts.insert(input.m_tap_scripts.begin(), input.m_tap_scripts.end()); + m_tap_bip32_paths.insert(input.m_tap_bip32_paths.begin(), input.m_tap_bip32_paths.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; + if (m_tap_key_sig.empty() && !input.m_tap_key_sig.empty()) m_tap_key_sig = input.m_tap_key_sig; + if (m_tap_internal_key.IsNull() && !input.m_tap_internal_key.IsNull()) m_tap_internal_key = input.m_tap_internal_key; + if (m_tap_merkle_root.IsNull() && !input.m_tap_merkle_root.IsNull()) m_tap_merkle_root = input.m_tap_merkle_root; } void PSBTOutput::FillSignatureData(SignatureData& sigdata) const @@ -254,9 +260,12 @@ 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()); + m_tap_bip32_paths.insert(output.m_tap_bip32_paths.begin(), output.m_tap_bip32_paths.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; + if (m_tap_internal_key.IsNull() && !output.m_tap_internal_key.IsNull()) m_tap_internal_key = output.m_tap_internal_key; + if (m_tap_tree.has_value() && !output.m_tap_tree.has_value()) m_tap_tree = output.m_tap_tree; } bool PSBTInputSigned(const PSBTInput& input) { From 6cff82722f47b589a6a2cb264bfce20f4d45426a Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 2 May 2022 11:28:49 -0400 Subject: [PATCH 16/18] sign: Use sigdata taproot spenddata when signing The taproot spenddata stored in a sigdata is the combination of data existing previously (e.g. in a PSBT) and the data stored in a SigningProvider. In order to use the external data when signing, we need to be using the sigdata's spenddata. --- src/script/sign.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 12f2ec4d3a..a3681d26cc 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -230,16 +230,16 @@ static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCrea // Try key path spending. { KeyOriginInfo info; - if (provider.GetKeyOriginByXOnly(spenddata.internal_key, info)) { - auto it = sigdata.taproot_misc_pubkeys.find(spenddata.internal_key); + if (provider.GetKeyOriginByXOnly(sigdata.tr_spenddata.internal_key, info)) { + auto it = sigdata.taproot_misc_pubkeys.find(sigdata.tr_spenddata.internal_key); if (it == sigdata.taproot_misc_pubkeys.end()) { - sigdata.taproot_misc_pubkeys.emplace(spenddata.internal_key, std::make_pair(std::set(), info)); + sigdata.taproot_misc_pubkeys.emplace(sigdata.tr_spenddata.internal_key, std::make_pair(std::set(), info)); } } std::vector sig; if (sigdata.taproot_key_path_sig.size() == 0) { - if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) { + if (creator.CreateSchnorrSig(provider, sig, sigdata.tr_spenddata.internal_key, nullptr, &sigdata.tr_spenddata.merkle_root, SigVersion::TAPROOT)) { sigdata.taproot_key_path_sig = sig; } } From a73b56888a1562d9fe46b7b1d2eea08802d98dfe Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 2 May 2022 11:30:03 -0400 Subject: [PATCH 17/18] wallet: also search taproot pubkeys in FillPSBT When filling a PSBT, we search the listed pubkeys in order to determine whether the current DescriptorScriptPubKeyMan could sign the transaction even if it is not watching the scripts. With Taproot, the taproot pubkeys need to be searched as well. --- src/wallet/scriptpubkeyman.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 8633e7c62c..1fec82a485 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -2180,6 +2180,19 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& *keys = Merge(*keys, *pk_keys); } } + for (const auto& pk_pair : input.m_tap_bip32_paths) { + const XOnlyPubKey& pubkey = pk_pair.first; + for (unsigned char prefix : {0x02, 0x03}) { + unsigned char b[33] = {prefix}; + std::copy(pubkey.begin(), pubkey.end(), b + 1); + CPubKey fullpubkey; + fullpubkey.Set(b, b + 33); + std::unique_ptr pk_keys = GetSigningProvider(fullpubkey); + if (pk_keys) { + *keys = Merge(*keys, *pk_keys); + } + } + } } SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize); From b80de4c505bf6377f2e476133dce6f2a803f1fa1 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 23 Jun 2022 13:46:06 -0400 Subject: [PATCH 18/18] test: Test signing psbts without explicitly having scripts --- test/functional/rpc_psbt.py | 43 ++++++++++++++++++++++++++- test/functional/test_framework/key.py | 4 +++ test/functional/wallet_taproot.py | 4 +-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 444e56610e..d2a888fd31 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -9,7 +9,7 @@ from decimal import Decimal from itertools import product from test_framework.descriptors import descsum_create -from test_framework.key import ECKey +from test_framework.key import ECKey, H_POINT from test_framework.messages import ( ser_compact_size, WITNESS_SCALE_FACTOR, @@ -723,5 +723,46 @@ class PSBTTest(BitcoinTestFramework): ) assert_equal(psbt2["fee"], psbt3["fee"]) + self.log.info("Test signing inputs that the wallet has keys for but is not watching the scripts") + self.nodes[1].createwallet(wallet_name="scriptwatchonly", disable_private_keys=True) + watchonly = self.nodes[1].get_wallet_rpc("scriptwatchonly") + + eckey = ECKey() + eckey.generate() + privkey = bytes_to_wif(eckey.get_bytes()) + + desc = descsum_create("wsh(pkh({}))".format(eckey.get_pubkey().get_bytes().hex())) + if self.options.descriptors: + res = watchonly.importdescriptors([{"desc": desc, "timestamp": "now"}]) + else: + res = watchonly.importmulti([{"desc": desc, "timestamp": "now"}]) + assert res[0]["success"] + addr = self.nodes[0].deriveaddresses(desc)[0] + self.nodes[0].sendtoaddress(addr, 10) + self.generate(self.nodes[0], 1) + self.nodes[0].importprivkey(privkey) + + psbt = watchonly.sendall([wallet.getnewaddress()])["psbt"] + psbt = self.nodes[0].walletprocesspsbt(psbt)["psbt"] + self.nodes[0].sendrawtransaction(self.nodes[0].finalizepsbt(psbt)["hex"]) + + # Same test but for taproot + if self.options.descriptors: + eckey = ECKey() + eckey.generate() + privkey = bytes_to_wif(eckey.get_bytes()) + + desc = descsum_create("tr({},pk({}))".format(H_POINT, eckey.get_pubkey().get_bytes().hex())) + res = watchonly.importdescriptors([{"desc": desc, "timestamp": "now"}]) + assert res[0]["success"] + addr = self.nodes[0].deriveaddresses(desc)[0] + self.nodes[0].sendtoaddress(addr, 10) + self.generate(self.nodes[0], 1) + self.nodes[0].importdescriptors([{"desc": descsum_create("tr({})".format(privkey)), "timestamp":"now"}]) + + psbt = watchonly.sendall([wallet.getnewaddress()])["psbt"] + psbt = self.nodes[0].walletprocesspsbt(psbt)["psbt"] + self.nodes[0].sendrawtransaction(self.nodes[0].finalizepsbt(psbt)["hex"]) + if __name__ == '__main__': PSBTTest().main() diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index e5dea66963..68afc1383d 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -15,6 +15,10 @@ import unittest from .util import modinv +# Point with no known discrete log. +H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" + + def TaggedHash(tag, data): ss = hashlib.sha256(tag.encode('utf-8')).digest() ss += ss diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index 6af9c9d2ce..c8d4a1da45 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -8,6 +8,7 @@ import random from decimal import Decimal from test_framework.address import output_key_to_p2tr +from test_framework.key import H_POINT from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal from test_framework.descriptors import descsum_create @@ -157,9 +158,6 @@ KEYS = [ CHANGE_XPRV = "tprv8ZgxMBicQKsPcyDrWwiecVnTtFmfRwbfFqEfR4ZGWvq5aTTwLBWmAm5zrbMcYtb9gQNFfhRfqhhrBG37U3nhmXxEgeEPBJGHAPrHCrAd1WX" CHANGE_XPUB = "tpubD6NzVbkrYhZ4WSFeQbPF1uSaTHHbbGnZq8qShabZwCdUQwihxaLMMFhs2kidGF2qrRKiQVqw8VoyuTHj1bZqmMXMeciaU1gBjWA1sim2zUB" -# Point with no known discrete log. -H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" - def key(hex_key): """Construct an x-only pubkey from its hex representation."""