diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 91e329985b..4a6e4cd36f 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -45,7 +45,7 @@ static void VerifyScriptBench(benchmark::Bench& bench) txSpend.witness.vtxinwit.resize(1); CScriptWitness& witness = txSpend.witness.vtxinwit[0].scriptWitness; witness.stack.emplace_back(); - key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back()); + key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0, 0), witness.stack.back()); witness.stack.back().push_back(static_cast(SIGHASH_ALL)); witness.stack.push_back(ToByteVector(pubkey)); diff --git a/src/qt/qrimagewidget.cpp b/src/qt/qrimagewidget.cpp index f35eb05b00..851a7850a1 100644 --- a/src/qt/qrimagewidget.cpp +++ b/src/qt/qrimagewidget.cpp @@ -67,7 +67,7 @@ bool QRImageWidget::setQR(const QString& data, const QString& text) // Elements: Hack to get QR address to print right const size_t MORE_WIDTH = 80; - + const int qr_image_size = QR_IMAGE_SIZE + MORE_WIDTH + (text.isEmpty() ? 0 : 2 * QR_IMAGE_MARGIN); QImage qrAddrImage(qr_image_size, qr_image_size, QImage::Format_RGB32); qrAddrImage.fill(0xffffff); diff --git a/src/script/generic.hpp b/src/script/generic.hpp index 8545b01e9f..b26ad01a3b 100644 --- a/src/script/generic.hpp +++ b/src/script/generic.hpp @@ -19,7 +19,7 @@ public: bool sighash_byte; SimpleSignatureChecker(const uint256& hashIn, bool sighash_byte_in) : hash(hashIn), sighash_byte(sighash_byte_in) {}; - bool CheckECDSASignature(const std::vector& vchSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override + bool CheckECDSASignature(const std::vector& vchSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override { std::vector vchSigCopy(vchSig); CPubKey pubkey(vchPubKey); @@ -48,7 +48,7 @@ class SimpleSignatureCreator : public BaseSignatureCreator public: SimpleSignatureCreator(const uint256& hashIn, bool sighash_byte_in) : checker(hashIn, sighash_byte_in), sighash_byte(sighash_byte_in) {}; const BaseSignatureChecker& Checker() const override { return checker; } - bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override + bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override { CKey key; if (!provider.GetKey(keyid, key)) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a27eb3c33e..5ecd3ab410 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -186,11 +186,17 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { return true; } -bool static IsDefinedHashtypeSignature(const valtype &vchSig) { +bool static IsDefinedHashtypeSignature(const valtype &vchSig, unsigned int flags) { if (vchSig.size() == 0) { return false; } unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); + + // ELEMENTS: Only allow SIGHASH_RANGEPROOF if the flag is set (after dynafed activation). + if ((flags & SCRIPT_SIGHASH_RANGEPROOF) == SCRIPT_SIGHASH_RANGEPROOF) { + nHashType = nHashType & (~(SIGHASH_RANGEPROOF)); + } + if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) return false; @@ -216,7 +222,7 @@ bool CheckSignatureEncoding(const std::vector &vchSig, unsigned i } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSigCopy, serror)) { // serror is set return false; - } else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSigCopy)) { + } else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSigCopy, flags)) { return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE); } return true; @@ -368,7 +374,7 @@ static bool EvalChecksigPreTapscript(const valtype& vchSig, const valtype& vchPu //serror is set return false; } - fSuccess = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion); + fSuccess = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion, flags); if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); @@ -1466,7 +1472,7 @@ bool EvalScript(std::vector >& stack, const CScript& } // Check signature - bool fOk = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion); + bool fOk = checker.CheckECDSASignature(vchSig, vchPubKey, scriptCode, sigversion, flags); if (fOk) { isig++; @@ -1647,13 +1653,15 @@ private: const CScript& scriptCode; //!< output script being consumed const unsigned int nIn; //!< input index of txTo being signed const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set + const bool fRangeproof; //!< whether the hashtype has the SIGHASH_RANGEPROOF flag set const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: - CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : + CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn, unsigned int flags) : txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), + fRangeproof(!!(flags & SCRIPT_SIGHASH_RANGEPROOF) && !!(nHashTypeIn & SIGHASH_RANGEPROOF)), fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE), fHashNone((nHashTypeIn & 0x1f) == SIGHASH_NONE) {} @@ -1710,11 +1718,23 @@ public: /** Serialize an output of txTo */ template void SerializeOutput(S &s, unsigned int nOutput) const { - if (fHashSingle && nOutput != nIn) + if (fHashSingle && nOutput != nIn) { // Do not lock-in the txout payee at other indices as txin ::Serialize(s, CTxOut()); - else + } else { ::Serialize(s, txTo.vout[nOutput]); + + // Serialize rangeproof + if (fRangeproof) { + if (nOutput < txTo.witness.vtxoutwit.size()) { + ::Serialize(s, txTo.witness.vtxoutwit[nOutput].vchRangeproof); + ::Serialize(s, txTo.witness.vtxoutwit[nOutput].vchSurjectionproof); + } else { + ::Serialize(s, (unsigned char) 0); + ::Serialize(s, (unsigned char) 0); + } + } + } } /** Serialize txTo */ @@ -1803,6 +1823,20 @@ uint256 GetSpentScriptsSHA256(const std::vector& outputs_spent) return ss.GetSHA256(); } +template +uint256 GetRangeproofsHash(const T& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (size_t i = 0; i < txTo.vout.size(); i++) { + if (i < txTo.witness.vtxoutwit.size()) { + ss << txTo.witness.vtxoutwit[i].vchRangeproof; + ss << txTo.witness.vtxoutwit[i].vchSurjectionproof; + } else { + ss << (unsigned char) 0; + ss << (unsigned char) 0; + } + } + return ss.GetHash(); +} } // namespace @@ -1850,6 +1884,7 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector&& spent hashSequence = SHA256Uint256(m_sequences_single_hash); hashIssuance = SHA256Uint256(GetIssuanceSHA256(txTo)); hashOutputs = SHA256Uint256(m_outputs_single_hash); + hashRangeproofs = GetRangeproofsHash(txTo); m_bip143_segwit_ready = true; } if (uses_bip341_taproot) { @@ -1962,7 +1997,7 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata } template -uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, unsigned int flags, const PrecomputedTransactionData* cache) { assert(nIn < txTo.vin.size()); @@ -1971,7 +2006,9 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn uint256 hashSequence; uint256 hashIssuance; uint256 hashOutputs; + uint256 hashRangeproofs; const bool cacheready = cache && cache->m_bip143_segwit_ready; + bool fRangeproof = !!(flags & SCRIPT_SIGHASH_RANGEPROOF) && !!(nHashType & SIGHASH_RANGEPROOF); if (!(nHashType & SIGHASH_ANYONECANPAY)) { hashPrevouts = cacheready ? cache->hashPrevouts : SHA256Uint256(GetPrevoutsSHA256(txTo)); @@ -1987,10 +2024,26 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { hashOutputs = cacheready ? cache->hashOutputs : SHA256Uint256(GetOutputsSHA256(txTo)); + + if (fRangeproof) { + hashRangeproofs = cacheready ? cache->hashRangeproofs : SHA256Uint256(GetRangeproofsHash(txTo)); + } } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; hashOutputs = ss.GetHash(); + + if (fRangeproof) { + CHashWriter ss(SER_GETHASH, 0); + if (nIn < txTo.witness.vtxoutwit.size()) { + ss << txTo.witness.vtxoutwit[nIn].vchRangeproof; + ss << txTo.witness.vtxoutwit[nIn].vchSurjectionproof; + } else { + ss << (unsigned char) 0; + ss << (unsigned char) 0; + } + hashRangeproofs = ss.GetHash(); + } } CHashWriter ss(SER_GETHASH, 0); @@ -2019,6 +2072,11 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn } // Outputs (none/one/all, depending on flags) ss << hashOutputs; + if (fRangeproof) { + // This addition must be conditional because it was added after + // the segwit sighash was specified. + ss << hashRangeproofs; + } // Locktime ss << txTo.nLockTime; // Sighash type @@ -2036,7 +2094,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn } // Wrapper to serialize only the necessary parts of the transaction being signed - CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); + CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType, flags); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); @@ -2057,7 +2115,7 @@ bool GenericTransactionSignatureChecker::VerifySchnorrSignature(Span -bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vector& vchSigIn, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const +bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vector& vchSigIn, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -2070,7 +2128,7 @@ bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vecto int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, flags, this->txdata); if (!VerifyECDSASignature(vchSig, pubkey, sighash)) return false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index b32898d6a0..942217d019 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -31,6 +31,10 @@ enum SIGHASH_DEFAULT = 0, //!< Taproot only; implied when sighash byte is missing, and equivalent to SIGHASH_ALL SIGHASH_OUTPUT_MASK = 3, SIGHASH_INPUT_MASK = 0x80, + + // ELEMENTS: + // A flag that means the rangeproofs should be included in the sighash. + SIGHASH_RANGEPROOF = 0x40, }; /** Script verification flags. @@ -140,9 +144,15 @@ enum // Making unknown public key versions (in BIP 342 scripts) non-standard SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20), + // ELEMENTS: + // Signature checking assumes no sighash byte after the DER signature // SCRIPT_NO_SIGHASH_BYTE = (1U << 21), + + // Support/allow SIGHASH_RANGEPROOF. + // + SCRIPT_SIGHASH_RANGEPROOF = (1U << 22), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); @@ -160,7 +170,7 @@ struct PrecomputedTransactionData bool m_bip341_taproot_ready = false; // BIP143 precomputed data (double-SHA256). - uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance; + uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance, hashRangeproofs; //! Whether the 3 fields above are initialized. bool m_bip143_segwit_ready = false; @@ -223,12 +233,12 @@ static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128; static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT; template -uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, unsigned int flags, const PrecomputedTransactionData* cache = nullptr); class BaseSignatureChecker { public: - virtual bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const + virtual bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const { return false; } @@ -267,7 +277,7 @@ protected: public: GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} - bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; + bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override; bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override; bool CheckLockTime(const CScriptNum& nLockTime) const override; bool CheckSequence(const CScriptNum& nSequence) const override; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index ca052c0e49..7f0a686ad5 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -20,7 +20,7 @@ typedef std::vector valtype; MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const +bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const { CKey key; if (!provider.GetKey(address, key)) @@ -30,7 +30,7 @@ bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provid if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed()) return false; - uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, flags); if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); @@ -71,7 +71,7 @@ static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigd return provider.GetPubKey(address, pubkey); } -static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion) +static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion, unsigned int flags) { CKeyID keyid = pubkey.GetID(); const auto it = sigdata.signatures.find(keyid); @@ -83,7 +83,7 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat if (provider.GetKeyOrigin(keyid, info)) { sigdata.misc_pubkeys.emplace(keyid, std::make_pair(pubkey, std::move(info))); } - if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) { + if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion, flags)) { auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out)); assert(i.second); return true; @@ -100,7 +100,8 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, - std::vector& ret, TxoutType& whichTypeRet, SigVersion sigversion, SignatureData& sigdata) + std::vector& ret, TxoutType& whichTypeRet, SigVersion sigversion, SignatureData& sigdata, + unsigned int flags) { CScript scriptRet; uint160 h160; @@ -118,7 +119,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TxoutType::WITNESS_V1_TAPROOT: return false; case TxoutType::PUBKEY: - if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false; + if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion, flags)) return false; ret.push_back(std::move(sig)); return true; case TxoutType::PUBKEYHASH: { @@ -129,7 +130,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator sigdata.missing_pubkeys.push_back(keyID); return false; } - if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false; + if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion, flags)) return false; ret.push_back(std::move(sig)); ret.push_back(ToByteVector(pubkey)); return true; @@ -152,7 +153,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator // We need to always call CreateSig in order to fill sigdata with all // possible signatures that we can create. This will allow further PSBT // processing to work as it needs all possible signature and pubkey pairs - if (CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) { + if (CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion, flags)) { if (ret.size() < required + 1) { ret.push_back(std::move(sig)); } @@ -207,9 +208,14 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato { if (sigdata.complete) return true; + // We will already activate SIGHASH_RANGEPROOF for signing. This means that + // users using the flag before it activates will produce invalid signatures. + unsigned int signFlags = SCRIPT_SIGHASH_RANGEPROOF; + unsigned int verifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_SIGHASH_RANGEPROOF | additional_flags; + std::vector result; TxoutType whichType; - bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata); + bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata, signFlags); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -221,7 +227,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato // and then the serialized subscript: subscript = CScript(result[0].begin(), result[0].end()); sigdata.redeem_script = subscript; - solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TxoutType::SCRIPTHASH; + solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata, signFlags) && whichType != TxoutType::SCRIPTHASH; P2SH = true; } @@ -230,7 +236,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; TxoutType subType; - solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata); + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata, signFlags); sigdata.scriptWitness.stack = result; sigdata.witness = true; result.clear(); @@ -240,7 +246,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato CScript witnessscript(result[0].begin(), result[0].end()); sigdata.witness_script = witnessscript; TxoutType subType; - solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TxoutType::SCRIPTHASH && subType != TxoutType::WITNESS_V0_SCRIPTHASH && subType != TxoutType::WITNESS_V0_KEYHASH; + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata, signFlags) && subType != TxoutType::SCRIPTHASH && subType != TxoutType::WITNESS_V0_SCRIPTHASH && subType != TxoutType::WITNESS_V0_KEYHASH; result.push_back(std::vector(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; sigdata.witness = true; @@ -255,7 +261,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato sigdata.scriptSig = PushAll(result); // Test solution - sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS|additional_flags, creator.Checker()); + sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, verifyFlags, creator.Checker()); return sigdata.complete; } @@ -268,9 +274,9 @@ private: public: SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {} - bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override + bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override { - if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) { + if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion, flags)) { CPubKey pubkey(vchPubKey); sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig)); return true; @@ -338,6 +344,8 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI stack.witness.clear(); sigversion = SigVersion::WITNESS_V0; } + // We enable SIGHASH_RANGEPROOF for signing. + unsigned int flags = SCRIPT_SIGHASH_RANGEPROOF; if (script_type == TxoutType::MULTISIG && !stack.script.empty()) { // Build a map of pubkey -> signature by matching sigs to pubkeys: assert(solutions.size() > 1); @@ -347,7 +355,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI for (unsigned int i = last_success_key; i < num_pubkeys; ++i) { const valtype& pubkey = solutions[i+1]; // We either have a signature for this pubkey, or we have found a signature and it is valid - if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckECDSASignature(sig, pubkey, next_script, sigversion)) { + if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckECDSASignature(sig, pubkey, next_script, sigversion, flags)) { last_success_key = i + 1; break; } @@ -412,7 +420,7 @@ class DummySignatureChecker final : public BaseSignatureChecker { public: DummySignatureChecker() {} - bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; } + bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override { return true; } }; const DummySignatureChecker DUMMY_CHECKER; @@ -423,7 +431,7 @@ private: public: DummySignatureCreator(char r_len, char s_len) : m_r_len(r_len), m_s_len(s_len) {} const BaseSignatureChecker& Checker() const override { return DUMMY_CHECKER; } - bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override + bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override { // Create a dummy signature that is a valid DER-encoding vchSig.assign(m_r_len + m_s_len + 7, '\000'); diff --git a/src/script/sign.h b/src/script/sign.h index 7ce74c9131..4f280af234 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -30,7 +30,7 @@ public: virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; + virtual bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const =0; }; /** A signature creator for transactions. */ @@ -44,7 +44,7 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator { public: MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn = SIGHASH_ALL); const BaseSignatureChecker& Checker() const override { return checker; } - bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; + bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override; }; /** A signature creator that just produces 71-byte empty signatures. */ diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index e14d2dd72d..6244ed9638 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -21,7 +21,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) static CScript sign_multisig(const CScript& scriptPubKey, const std::vector& keys, const CTransaction& transaction, int whichIn) { - uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE, 0); CScript result; result << OP_0; // CHECKMULTISIG bug workaround diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 0163b44f88..0cf9049f38 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -339,7 +339,7 @@ public: TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32, SigVersion sigversion = SigVersion::BASE, CAmount amount = 0) { - uint256 hash = SignatureHash(script, spendTx, 0, nHashType, amount, sigversion); + uint256 hash = SignatureHash(script, spendTx, 0, nHashType, amount, sigversion, 0); std::vector vchSig, r, s; uint32_t iter = 0; do { @@ -1031,7 +1031,7 @@ BOOST_AUTO_TEST_CASE(script_cltv_truncated) static CScript sign_multisig(const CScript& scriptPubKey, const std::vector& keys, const CTransaction& transaction) { - uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0); CScript result; // @@ -1235,15 +1235,15 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) // A couple of partially-signed versions: std::vector sig1; - uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0); BOOST_CHECK(keys[0].Sign(hash1, sig1)); sig1.push_back(SIGHASH_ALL); std::vector sig2; - uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, SigVersion::BASE); + uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, SigVersion::BASE, 0); BOOST_CHECK(keys[1].Sign(hash2, sig2)); sig2.push_back(SIGHASH_NONE); std::vector sig3; - uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, SigVersion::BASE); + uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, SigVersion::BASE, 0); BOOST_CHECK(keys[2].Sign(hash3, sig3)); sig3.push_back(SIGHASH_SINGLE); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index bc862de78a..2e38c0f6b0 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -126,7 +126,8 @@ BOOST_AUTO_TEST_CASE(sighash_test) int nRandomTests = 50000; #endif for (int i=0; i vchSig; - uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE, 0); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); spends[i].vin[0].scriptSig << vchSig; @@ -187,7 +187,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign, with a non-DER signature { std::vector vchSig; - uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER vchSig.push_back((unsigned char)SIGHASH_ALL); @@ -260,7 +260,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign std::vector vchSig; - uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; @@ -288,7 +288,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Sign std::vector vchSig; - uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE); + uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE, 0); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; diff --git a/src/validation.cpp b/src/validation.cpp index 868d670485..a4ee969da3 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1000,7 +1000,14 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, Precompute TxValidationState &state = args.m_state; - constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; + unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; + + // Temporarily add additional script flags based on the activation of + // Dynamic Federations. This can be included in the + // STANDARD_LOCKTIME_VERIFY_FLAGS in a release post-activation. + if (IsDynaFedEnabled(::ChainActive().Tip(), args.m_chainparams.GetConsensus())) { + scriptVerifyFlags |= SCRIPT_SIGHASH_RANGEPROOF; + } // Check input scripts and signatures. // This is done last to help prevent CPU exhaustion denial-of-service attacks. @@ -2049,6 +2056,10 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens flags |= SCRIPT_VERIFY_NULLDUMMY; } + if (IsDynaFedEnabled(pindex->pprev, consensusparams)) { + flags |= SCRIPT_SIGHASH_RANGEPROOF; + } + return flags; } diff --git a/test/functional/feature_sighash_rangeproof.py b/test/functional/feature_sighash_rangeproof.py new file mode 100755 index 0000000000..04a4cad107 --- /dev/null +++ b/test/functional/feature_sighash_rangeproof.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 The Elements Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +""" +Test the post-dynafed elements-only SIGHASH_RANGEPROOF sighash flag. +""" + +import struct +from test_framework.test_framework import BitcoinTestFramework +from test_framework.address import base58_to_byte +from test_framework.script import ( + hash160, + LegacySignatureHash, + SegwitV0SignatureHash, + SIGHASH_ALL, + SIGHASH_SINGLE, + SIGHASH_NONE, + SIGHASH_ANYONECANPAY, + SIGHASH_RANGEPROOF, + CScript, + CScriptOp, + FindAndDelete, + OP_CODESEPARATOR, + OP_CHECKSIG, + OP_DUP, + OP_EQUALVERIFY, + OP_HASH160, +) +from test_framework.key import ECKey + +from test_framework.messages import ( + CBlock, + CTransaction, + CTxOut, + FromHex, + WitToHex, + hash256, uint256_from_str, ser_uint256, ser_string, ser_vector +) + +from test_framework import util +from test_framework.util import ( + assert_equal, + hex_str_to_bytes, + assert_raises_rpc_error, +) + +from test_framework.blocktools import add_witness_commitment + +def get_p2pkh_script(pubkeyhash): + """Get the script associated with a P2PKH.""" + return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)]) + +class SighashRangeproofTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 3 + # We want to test activation of dynafed + args = ["-con_dyna_deploy_start=1000", "-blindedaddresses=1", "-initialfreecoins=2100000000000000", "-con_blocksubsidy=0", "-con_connect_genesis_outputs=1", "-txindex=1"] + self.extra_args = [args] * self.num_nodes + self.extra_args[0].append("-anyonecanspendaremine=1") # first node gets the coins + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def prepare_tx_signed_with_sighash(self, address_type, sighash_rangeproof_aware): + # Create a tx that is signed with a specific version of the sighash + # method. + # If `sighash_rangeproof_aware` is + # true, the sighash will contain the rangeproofs if SIGHASH_RANGEPROOF is set + # false, the sighash will NOT contain the rangeproofs if SIGHASH_RANGEPROOF is set + + addr = self.nodes[1].getnewaddress("", address_type) + assert len(self.nodes[1].getaddressinfo(addr)["confidential_key"]) > 0 + self.nodes[0].sendtoaddress(addr, 1.0) + self.nodes[0].generate(1) + self.sync_all() + utxo = self.nodes[1].listunspent(1, 1, [addr])[0] + utxo_tx = FromHex(CTransaction(), self.nodes[1].getrawtransaction(utxo["txid"])) + utxo_spk = CScript(hex_str_to_bytes(utxo["scriptPubKey"])) + utxo_value = utxo_tx.vout[utxo["vout"]].nValue + + assert len(utxo["amountblinder"]) > 0 + sink_addr = self.nodes[2].getnewaddress() + unsigned_hex = self.nodes[1].createrawtransaction( + [{"txid": utxo["txid"], "vout": utxo["vout"]}], + {sink_addr: 0.9, "fee": 0.1} + ) + blinded_hex = self.nodes[1].blindrawtransaction(unsigned_hex) + blinded_tx = FromHex(CTransaction(), blinded_hex) + signed_hex = self.nodes[1].signrawtransactionwithwallet(blinded_hex)["hex"] + signed_tx = FromHex(CTransaction(), signed_hex) + + # Make sure that the tx the node produced is always valid. + test_accept = self.nodes[0].testmempoolaccept([signed_hex])[0] + assert test_accept["allowed"], "not accepted: {}".format(test_accept["reject-reason"]) + + # Prepare the keypair we need to re-sign the tx. + wif = self.nodes[1].dumpprivkey(addr) + (b, v) = base58_to_byte(wif) + privkey = ECKey() + privkey.set(b[0:32], len(b) == 33) + pubkey = privkey.get_pubkey() + + # Now we need to replace the signature with an equivalent one with the new sighash set. + hashtype = SIGHASH_ALL | SIGHASH_RANGEPROOF + if address_type == "legacy": + if sighash_rangeproof_aware: + (sighash, _) = LegacySignatureHash(utxo_spk, blinded_tx, 0, hashtype) + else: + (sighash, _) = LegacySignatureHash(utxo_spk, blinded_tx, 0, hashtype, enable_sighash_rangeproof=False) + signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode('latin-1') + assert len(signature) <= 0xfc + assert len(pubkey.get_bytes()) <= 0xfc + signed_tx.vin[0].scriptSig = CScript( + struct.pack(" 0 + block.vtx.append(tx) + block.hashMerkleRoot = block.calc_merkle_root() + add_witness_commitment(block) + block.solve() + block_hex = WitToHex(block) + + # First test the testproposed block RPC. + if assert_valid: + self.nodes[0].testproposedblock(block_hex) + else: + assert_raises_rpc_error(-25, "block-validation-failed", self.nodes[0].testproposedblock, block_hex) + + # Then try submit the block and check if it was accepted or not. + pre = self.nodes[0].getblockcount() + self.nodes[0].submitblock(block_hex) + post = self.nodes[0].getblockcount() + + if assert_valid: + # assert block was accepted + assert pre < post + else: + # assert block was not accepted + assert pre == post + + def run_test(self): + util.node_fastmerkle = self.nodes[0] + ADDRESS_TYPES = ["legacy", "bech32", "p2sh-segwit"] + + # Different test scenarios. + # - before activation, using the flag is non-standard + # - before activation, using the flag but a non-flag-aware signature is legal + # - after activation, using the flag but a non-flag-aware signature is illegal + # - after activation, using the flag is standard (and thus also legal) + + # Mine come coins for node 0. + self.nodes[0].generate(200) + self.sync_all() + + # Ensure that if we use the SIGHASH_RANGEPROOF flag before it's activated, + # - the tx is not accepted in the mempool and + # - the tx is accepted if manually mined in a block + for address_type in ADDRESS_TYPES: + self.log.info("Pre-activation for {} address".format(address_type)) + tx = self.prepare_tx_signed_with_sighash(address_type, False) + self.assert_tx_standard(tx, False) + self.assert_tx_valid(tx, True) + + # Activate dynafed (nb of blocks taken from dynafed activation test) + self.nodes[0].generate(1006 + 1 + 144 + 144) + assert_equal(self.nodes[0].getblockchaininfo()["softforks"]["dynafed"]["bip9"]["status"], "active") + + self.sync_all() + + # Test that the use of SIGHASH_RANGEPROOF is legal and standard + # after activation. + for address_type in ADDRESS_TYPES: + self.log.info("Post-activation for {} address".format(address_type)) + tx = self.prepare_tx_signed_with_sighash(address_type, True) + self.assert_tx_standard(tx, True) + self.assert_tx_valid(tx, True) + + # Ensure that if we then use the old sighash algorith that doesn't hash + # the rangeproofs, the signature is no longer valid. + for address_type in ADDRESS_TYPES: + self.log.info("Post-activation invalid sighash for {} address".format(address_type)) + tx = self.prepare_tx_signed_with_sighash(address_type, False) + self.assert_tx_standard(tx, False) + self.assert_tx_valid(tx, False) + +if __name__ == '__main__': + SighashRangeproofTest().main() + diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index 04fe924cec..d7b50aab51 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -212,11 +212,11 @@ def default_sighash(ctx): # BIP143 signature hash scriptcode = get(ctx, "scriptcode") utxos = get(ctx, "utxos") - return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue) + return SegwitV0SignatureHash(scriptcode, tx, idx, hashtype, utxos[idx].nValue, enable_sighash_rangeproof=False) else: # Pre-segwit signature hash scriptcode = get(ctx, "scriptcode") - return LegacySignatureHash(scriptcode, tx, idx, hashtype)[0] + return LegacySignatureHash(scriptcode, tx, idx, hashtype, enable_sighash_rangeproof=False)[0] def default_tweak(ctx): """Default expression for "tweak": None if a leaf is specified, tap[0] otherwise.""" diff --git a/test/functional/feature_txwitness.py b/test/functional/feature_txwitness.py index 71737909d6..815abfc348 100755 --- a/test/functional/feature_txwitness.py +++ b/test/functional/feature_txwitness.py @@ -14,7 +14,7 @@ """ -from test_framework.messages import CTransaction, CBlock, ser_uint256, FromHex, uint256_from_str, CTxOut, ToHex, CTxIn, COutPoint, OUTPOINT_ISSUANCE_FLAG, ser_string +from test_framework.messages import CTransaction, CBlock, ser_uint256, FromHex, uint256_from_str, CTxOut, ToHex, WitToHex, CTxIn, COutPoint, OUTPOINT_ISSUANCE_FLAG, ser_string from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, hex_str_to_bytes, assert_raises_rpc_error, assert_greater_than from test_framework import util @@ -125,10 +125,6 @@ class TxWitnessTest(BitcoinTestFramework): assert_equal(block.hash, self.nodes[0].getbestblockhash()) def test_coinbase_witness(self): - - def WitToHex(obj): - return obj.serialize(with_witness=True).hex() - block = self.nodes[0].getnewblockhex() block_struct = FromHex(CBlock(), block) diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 39ad50655a..19ec94bc42 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -205,6 +205,11 @@ def FromHex(obj, hex_string): def ToHex(obj): return obj.serialize().hex() +# Convert a binary-serializable object to hex (eg for submission via RPC) +# This variant also serializes the witness. +def WitToHex(obj): + return obj.serialize(with_witness=True).hex() + # Objects that map to bitcoind objects, which can be serialized/deserialized @@ -827,8 +832,11 @@ class CTransaction: r += self.wit.serialize() return r - def serialize(self): - return self.serialize_with_witness() + def serialize(self, with_witness=True): + if with_witness: + return self.serialize_with_witness() + else: + return self.serialize_without_witness() def rehash(self): self.sha256 = None diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index a785db7c96..00cc4028ed 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -21,6 +21,7 @@ from .messages import ( CTxOutAsset, CTxOutValue, hash256, + ser_compact_size, ser_string, ser_uint256, ser_vector, @@ -604,6 +605,8 @@ SIGHASH_ALL = 1 SIGHASH_NONE = 2 SIGHASH_SINGLE = 3 SIGHASH_ANYONECANPAY = 0x80 +# ELEMENTS: +SIGHASH_RANGEPROOF = 0x40 def FindAndDelete(script, sig): """Consensus critical, see FindAndDelete() in Satoshi codebase""" @@ -622,7 +625,7 @@ def FindAndDelete(script, sig): r += script[last_sop_idx:] return CScript(r) -def LegacySignatureHash(script, txTo, inIdx, hashtype): +def LegacySignatureHash(script, txTo, inIdx, hashtype, enable_sighash_rangeproof=True): """Consensus-correct SignatureHash Returns (hash, err) to precisely match the consensus-critical behavior of @@ -670,7 +673,18 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype): s = b"" s += struct.pack(" inIdx: + wit = txTo.wit.vtxoutwit[inIdx] + serialize_rangeproofs = ser_string(wit.vchRangeproof) + ser_string(wit.vchSurjectionproof) + hashRangeproofs = uint256_from_str(hash256(serialize_rangeproofs)) + ss = bytes() ss += struct.pack("