diff options
author | Johnson Lau <jl2012@xbt.hk> | 2020-09-11 14:33:10 -0700 |
---|---|---|
committer | Pieter Wuille <pieter@wuille.net> | 2020-10-12 17:06:38 -0700 |
commit | 5de246ca8159dcffaa4c136a60c8bfed2028e2ee (patch) | |
tree | 6360222a98a2f75e01882ad8c01f771b4f4c0b8b | |
parent | 9eb590894f15ff40806039bfd32972fbc260e30d (diff) |
Implement Taproot signature hashing (BIP 341)
This implements the new sighashing scheme from BIP341, with all relevant
whole-transaction values precomputed once and cached.
Includes changes to PrecomputedTransactionData by Pieter Wuille.
-rw-r--r-- | src/script/interpreter.cpp | 141 | ||||
-rw-r--r-- | src/script/interpreter.h | 27 | ||||
-rw-r--r-- | src/script/script.h | 5 | ||||
-rw-r--r-- | src/validation.cpp | 2 |
4 files changed, 163 insertions, 12 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index c322ecb1a0..af8fd4912e 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1291,23 +1291,79 @@ uint256 GetOutputsSHA256(const T& txTo) return ss.GetSHA256(); } +/** Compute the (single) SHA256 of the concatenation of all amounts spent by a tx. */ +uint256 GetSpentAmountsSHA256(const std::vector<CTxOut>& outputs_spent) +{ + CHashWriter ss(SER_GETHASH, 0); + for (const auto& txout : outputs_spent) { + ss << txout.nValue; + } + return ss.GetSHA256(); +} + +/** Compute the (single) SHA256 of the concatenation of all scriptPubKeys spent by a tx. */ +uint256 GetSpentScriptsSHA256(const std::vector<CTxOut>& outputs_spent) +{ + CHashWriter ss(SER_GETHASH, 0); + for (const auto& txout : outputs_spent) { + ss << txout.scriptPubKey; + } + return ss.GetSHA256(); +} + + } // namespace template <class T> void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs) { - assert(!m_ready); + assert(!m_spent_outputs_ready); m_spent_outputs = std::move(spent_outputs); + if (!m_spent_outputs.empty()) { + assert(m_spent_outputs.size() == txTo.vin.size()); + m_spent_outputs_ready = true; + } - // Cache is calculated only for transactions with witness - if (txTo.HasWitness()) { - hashPrevouts = SHA256Uint256(GetPrevoutsSHA256(txTo)); - hashSequence = SHA256Uint256(GetSequencesSHA256(txTo)); - hashOutputs = SHA256Uint256(GetOutputsSHA256(txTo)); + // Determine which precomputation-impacting features this transaction uses. + bool uses_bip143_segwit = false; + bool uses_bip341_taproot = false; + for (size_t inpos = 0; inpos < txTo.vin.size(); ++inpos) { + if (!txTo.vin[inpos].scriptWitness.IsNull()) { + if (m_spent_outputs_ready && m_spent_outputs[inpos].scriptPubKey.size() == 2 + WITNESS_V1_TAPROOT_SIZE && + m_spent_outputs[inpos].scriptPubKey[0] == OP_1) { + // Treat every witness-bearing spend with 34-byte scriptPubKey that starts with OP_1 as a Taproot + // spend. This only works if spent_outputs was provided as well, but if it wasn't, actual validation + // will fail anyway. Note that this branch may trigger for scriptPubKeys that aren't actually segwit + // but in that case validation will fail as SCRIPT_ERR_WITNESS_UNEXPECTED anyway. + uses_bip341_taproot = true; + } else { + // Treat every spend that's not known to native witness v1 as a Witness v0 spend. This branch may + // also be taken for unknown witness versions, but it is harmless, and being precise would require + // P2SH evaluation to find the redeemScript. + uses_bip143_segwit = true; + } + } + if (uses_bip341_taproot && uses_bip143_segwit) break; // No need to scan further if we already need all. } - m_ready = true; + if (uses_bip143_segwit || uses_bip341_taproot) { + // Computations shared between both sighash schemes. + m_prevouts_single_hash = GetPrevoutsSHA256(txTo); + m_sequences_single_hash = GetSequencesSHA256(txTo); + m_outputs_single_hash = GetOutputsSHA256(txTo); + } + if (uses_bip143_segwit) { + hashPrevouts = SHA256Uint256(m_prevouts_single_hash); + hashSequence = SHA256Uint256(m_sequences_single_hash); + hashOutputs = SHA256Uint256(m_outputs_single_hash); + m_bip143_segwit_ready = true; + } + if (uses_bip341_taproot) { + m_spent_amounts_single_hash = GetSpentAmountsSHA256(m_spent_outputs); + m_spent_scripts_single_hash = GetSpentScriptsSHA256(m_spent_outputs); + m_bip341_taproot_ready = true; + } } template <class T> @@ -1322,6 +1378,75 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); +static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash"); + +template<typename T> +bool SignatureHashSchnorr(uint256& hash_out, const T& tx_to, uint32_t in_pos, uint8_t hash_type, SigVersion sigversion, const PrecomputedTransactionData& cache) +{ + uint8_t ext_flag; + switch (sigversion) { + case SigVersion::TAPROOT: + ext_flag = 0; + break; + default: + assert(false); + } + assert(in_pos < tx_to.vin.size()); + assert(cache.m_bip341_taproot_ready && cache.m_spent_outputs_ready); + + CHashWriter ss = HASHER_TAPSIGHASH; + + // Epoch + static constexpr uint8_t EPOCH = 0; + ss << EPOCH; + + // Hash type + const uint8_t output_type = (hash_type == SIGHASH_DEFAULT) ? SIGHASH_ALL : (hash_type & SIGHASH_OUTPUT_MASK); // Default (no sighash byte) is equivalent to SIGHASH_ALL + const uint8_t input_type = hash_type & SIGHASH_INPUT_MASK; + if (!(hash_type <= 0x03 || (hash_type >= 0x81 && hash_type <= 0x83))) return false; + ss << hash_type; + + // Transaction level data + ss << tx_to.nVersion; + ss << tx_to.nLockTime; + if (input_type != SIGHASH_ANYONECANPAY) { + ss << cache.m_prevouts_single_hash; + ss << cache.m_spent_amounts_single_hash; + ss << cache.m_spent_scripts_single_hash; + ss << cache.m_sequences_single_hash; + } + if (output_type == SIGHASH_ALL) { + ss << cache.m_outputs_single_hash; + } + + // Data about the input/prevout being spent + const auto& witstack = tx_to.vin[in_pos].scriptWitness.stack; + bool have_annex = witstack.size() > 1 && witstack.back().size() > 0 && witstack.back()[0] == ANNEX_TAG; + const uint8_t spend_type = (ext_flag << 1) + (have_annex ? 1 : 0); // The low bit indicates whether an annex is present. + ss << spend_type; + if (input_type == SIGHASH_ANYONECANPAY) { + ss << tx_to.vin[in_pos].prevout; + ss << cache.m_spent_outputs[in_pos]; + ss << tx_to.vin[in_pos].nSequence; + } else { + ss << in_pos; + } + if (have_annex) { + ss << (CHashWriter(SER_GETHASH, 0) << witstack.back()).GetSHA256(); + } + + // Data about the output (if only one). + if (output_type == SIGHASH_SINGLE) { + if (in_pos >= tx_to.vout.size()) return false; + CHashWriter sha_single_output(SER_GETHASH, 0); + sha_single_output << tx_to.vout[in_pos]; + ss << sha_single_output.GetSHA256(); + } + + hash_out = ss.GetSHA256(); + return true; +} + template <class T> uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { @@ -1331,7 +1456,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; - const bool cacheready = cache && cache->m_ready; + const bool cacheready = cache && cache->m_bip143_segwit_ready; if (!(nHashType & SIGHASH_ANYONECANPAY)) { hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo)); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 64cefc0d6c..b739528f0f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -25,6 +25,10 @@ enum SIGHASH_NONE = 2, SIGHASH_SINGLE = 3, SIGHASH_ANYONECANPAY = 0x80, + + SIGHASH_DEFAULT = 0, //!< Taproot only; implied when sighash byte is missing, and equivalent to SIGHASH_ALL + SIGHASH_OUTPUT_MASK = 3, + SIGHASH_INPUT_MASK = 0x80, }; /** Script verification flags. @@ -121,9 +125,24 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i struct PrecomputedTransactionData { + // BIP341 precomputed data. + // These are single-SHA256, see https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-15. + uint256 m_prevouts_single_hash; + uint256 m_sequences_single_hash; + uint256 m_outputs_single_hash; + uint256 m_spent_amounts_single_hash; + uint256 m_spent_scripts_single_hash; + //! Whether the 5 fields above are initialized. + bool m_bip341_taproot_ready = false; + + // BIP143 precomputed data (double-SHA256). uint256 hashPrevouts, hashSequence, hashOutputs; - bool m_ready = false; + //! Whether the 3 fields above are initialized. + bool m_bip143_segwit_ready = false; + std::vector<CTxOut> m_spent_outputs; + //! Whether m_spent_outputs is initialized. + bool m_spent_outputs_ready = false; PrecomputedTransactionData() = default; @@ -136,13 +155,15 @@ struct PrecomputedTransactionData enum class SigVersion { - BASE = 0, - WITNESS_V0 = 1, + BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts + WITNESS_V0 = 1, //!< Witness v0 (P2WPKH and P2WSH); see BIP 141 + TAPROOT = 2, //!< Witness v1 with 32-byte program, not BIP16 P2SH-wrapped, key path spending; see BIP 341 }; /** Signature hash sizes */ static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32; static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20; +static constexpr size_t WITNESS_V1_TAPROOT_SIZE = 32; template <class T> uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); diff --git a/src/script/script.h b/src/script/script.h index c1f2b66921..fcf3e29362 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -44,6 +44,11 @@ static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 // SEQUENCE_FINAL). static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU; +// Tag for input annex. If there are at least two witness elements for a transaction input, +// and the first byte of the last element is 0x50, this last element is called annex, and +// has meanings independent of the script +static constexpr unsigned int ANNEX_TAG = 0x50; + template <typename T> std::vector<unsigned char> ToByteVector(const T& in) { diff --git a/src/validation.cpp b/src/validation.cpp index 2090d9477f..65958967ee 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1538,7 +1538,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C return true; } - if (!txdata.m_ready) { + if (!txdata.m_spent_outputs_ready) { std::vector<CTxOut> spent_outputs; spent_outputs.reserve(tx.vin.size()); |