mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Merge a993a7c675 into merged_master (Elements PR #960)
Several conflicts in the C++ code related to the new `flags` parameter to `CheckSignature` and the corresponding function being renamed upstream to `CheckSignatureECDSA`. Several conflicts in the test harness as Steven sorta pulled the new upstream ECKey module into the Python code, and the actual upstream code was slightly different. Also needed to update the feature_taproot code to always use the non-RANGEPROOF sighash since dynafed is not enabled in the Taproot test. Also had to pull the `set_wif` method out of `ECKey` and inline it because otherwise it triggers a "circular inclusion" error between script.py (which would pull in `base58_to_bytes` from address.py) and address.py (which now pulls in some taproot EC related stuff from script.py). Noticed that #960 does not test the "sighash rangeproof flag set but no witnesses" case.
This commit is contained in:
commit
22cf380984
18 changed files with 410 additions and 65 deletions
|
|
@ -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<unsigned char>(SIGHASH_ALL));
|
||||
witness.stack.push_back(ToByteVector(pubkey));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
|
||||
{
|
||||
std::vector<unsigned char> 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<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override
|
||||
{
|
||||
CKey key;
|
||||
if (!provider.GetKey(keyid, key))
|
||||
|
|
|
|||
|
|
@ -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<unsigned char> &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<std::vector<unsigned char> >& 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<typename S>
|
||||
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<CTxOut>& outputs_spent)
|
|||
return ss.GetSHA256();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
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<CTxOut>&& 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 <class T>
|
||||
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<T> txTmp(txTo, scriptCode, nIn, nHashType);
|
||||
CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType, flags);
|
||||
|
||||
// Serialize and hash
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
|
|
@ -2057,7 +2115,7 @@ bool GenericTransactionSignatureChecker<T>::VerifySchnorrSignature(Span<const un
|
|||
}
|
||||
|
||||
template <class T>
|
||||
bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
bool GenericTransactionSignatureChecker<T>::CheckECDSASignature(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const
|
||||
{
|
||||
CPubKey pubkey(vchPubKey);
|
||||
if (!pubkey.IsValid())
|
||||
|
|
@ -2070,7 +2128,7 @@ bool GenericTransactionSignatureChecker<T>::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;
|
||||
|
|
|
|||
|
|
@ -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<unsigned char> &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 <class T>
|
||||
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<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
|
||||
virtual bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& 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<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override;
|
||||
bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> 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;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ typedef std::vector<unsigned char> 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<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
|
||||
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& 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<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion)
|
||||
static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& 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<valtype>& ret, TxoutType& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
|
||||
std::vector<valtype>& 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<valtype> 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<unsigned char>(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<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& 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<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { return true; }
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& 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<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& 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');
|
||||
|
|
|
|||
|
|
@ -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<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
|
||||
virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& 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<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion, unsigned int flags) const override;
|
||||
};
|
||||
|
||||
/** A signature creator that just produces 71-byte empty signatures. */
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup)
|
|||
static CScript
|
||||
sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& 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
|
||||
|
|
|
|||
|
|
@ -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<unsigned char> 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<CKey>& 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<unsigned char> 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<unsigned char> 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<unsigned char> 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ BOOST_AUTO_TEST_CASE(sighash_test)
|
|||
int nRandomTests = 50000;
|
||||
#endif
|
||||
for (int i=0; i<nRandomTests; i++) {
|
||||
int nHashType = InsecureRand32();
|
||||
// In randomized test, we disable SIGHASH_RANGEPROOF.
|
||||
int nHashType = InsecureRand32() & ~SIGHASH_RANGEPROOF;
|
||||
CMutableTransaction txTo;
|
||||
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
|
||||
CScript scriptCode;
|
||||
|
|
@ -135,7 +136,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
|
|||
|
||||
uint256 sh, sho;
|
||||
sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType);
|
||||
sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE);
|
||||
sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE, SCRIPT_SIGHASH_RANGEPROOF);
|
||||
#if defined(PRINT_SIGHASH_JSON)
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << txTo;
|
||||
|
|
@ -201,7 +202,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
|
|||
continue;
|
||||
}
|
||||
|
||||
sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE);
|
||||
sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE, 0);
|
||||
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
|||
|
||||
// Sign:
|
||||
std::vector<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
221
test/functional/feature_sighash_rangeproof.py
Executable file
221
test/functional/feature_sighash_rangeproof.py
Executable file
|
|
@ -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("<B", len(signature)) + signature
|
||||
+ struct.pack("<B", len(pubkey.get_bytes())) + pubkey.get_bytes()
|
||||
)
|
||||
elif address_type == "bech32" or address_type == "p2sh-segwit":
|
||||
assert signed_tx.wit.vtxinwit[0].scriptWitness.stack[1] == pubkey.get_bytes()
|
||||
pubkeyhash = hash160(pubkey.get_bytes())
|
||||
script = get_p2pkh_script(pubkeyhash)
|
||||
if sighash_rangeproof_aware:
|
||||
sighash = SegwitV0SignatureHash(script, blinded_tx, 0, hashtype, utxo_value)
|
||||
else:
|
||||
sighash = SegwitV0SignatureHash(script, blinded_tx, 0, hashtype, utxo_value, enable_sighash_rangeproof=False)
|
||||
signature = privkey.sign_ecdsa(sighash) + chr(hashtype).encode('latin-1')
|
||||
signed_tx.wit.vtxinwit[0].scriptWitness.stack[0] = signature
|
||||
else:
|
||||
assert False
|
||||
|
||||
signed_tx.rehash()
|
||||
return signed_tx
|
||||
|
||||
def assert_tx_standard(self, tx, assert_standard=True):
|
||||
# Test the standardness of the tx by submitting it to the mempool.
|
||||
|
||||
test_accept = self.nodes[0].testmempoolaccept([WitToHex(tx)])[0]
|
||||
if assert_standard:
|
||||
assert test_accept["allowed"], "tx was not accepted: {}".format(test_accept["reject-reason"])
|
||||
else:
|
||||
assert not test_accept["allowed"], "tx was accepted"
|
||||
|
||||
def assert_tx_valid(self, tx, assert_valid=True):
|
||||
# Test the validity of the transaction by manually mining a block that contains the tx.
|
||||
|
||||
block = FromHex(CBlock(), self.nodes[2].getnewblockhex())
|
||||
assert len(block.vtx) > 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()
|
||||
|
||||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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("<i", txtmp.nVersion)
|
||||
s += ser_vector(txtmp.vin)
|
||||
s += ser_vector(txtmp.vout)
|
||||
# If SIGHASH_RANGEPROOF is set, we need to add the rangeproof serialization after each output
|
||||
if enable_sighash_rangeproof and hashtype & SIGHASH_RANGEPROOF:
|
||||
s += ser_compact_size(len(txtmp.vout))
|
||||
for i in range(len(txtmp.vout)):
|
||||
s += txtmp.vout[i].serialize()
|
||||
if i < len(txtmp.wit.vtxoutwit):
|
||||
s += ser_string(txtmp.wit.vtxoutwit[i].vchRangeproof)
|
||||
s += ser_string(txtmp.wit.vtxoutwit[i].vchSurjectionproof)
|
||||
else:
|
||||
s += bytes([0, 0])
|
||||
else:
|
||||
s += ser_vector(txtmp.vout)
|
||||
s += struct.pack("<I", txtmp.nLockTime)
|
||||
|
||||
# add sighash type
|
||||
|
|
@ -684,12 +698,13 @@ def LegacySignatureHash(script, txTo, inIdx, hashtype):
|
|||
# Performance optimization probably not necessary for python tests, however.
|
||||
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
|
||||
# for version 0 witnesses.
|
||||
def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
|
||||
def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount, enable_sighash_rangeproof=True):
|
||||
|
||||
hashPrevouts = 0
|
||||
hashSequence = 0
|
||||
hashIssuance = 0
|
||||
hashOutputs = 0
|
||||
hashRangeproofs = 0
|
||||
|
||||
if not (hashtype & SIGHASH_ANYONECANPAY):
|
||||
serialize_prevouts = bytes()
|
||||
|
|
@ -715,10 +730,24 @@ def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
|
|||
for o in txTo.vout:
|
||||
serialize_outputs += o.serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
if enable_sighash_rangeproof and hashtype & SIGHASH_RANGEPROOF:
|
||||
serialize_rangeproofs = bytes()
|
||||
for wit in txTo.wit.vtxoutwit:
|
||||
serialize_rangeproofs += ser_string(wit.vchRangeproof) + ser_string(wit.vchSurjectionproof)
|
||||
hashRangeproofs = uint256_from_str(hash256(serialize_rangeproofs))
|
||||
|
||||
elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
|
||||
serialize_outputs = txTo.vout[inIdx].serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
if enable_sighash_rangeproof and hashtype & SIGHASH_RANGEPROOF:
|
||||
serialize_rangeproofs = b'\x00'
|
||||
if len(txTo.wit.vtxoutwit) > 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("<i", txTo.nVersion)
|
||||
ss += ser_uint256(hashPrevouts)
|
||||
|
|
@ -729,6 +758,8 @@ def SegwitV0SignatureHash(script, txTo, inIdx, hashtype, amount):
|
|||
ss += amount.serialize()
|
||||
ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
|
||||
ss += ser_uint256(hashOutputs)
|
||||
if enable_sighash_rangeproof and hashtype & SIGHASH_RANGEPROOF:
|
||||
ss += ser_uint256(hashRangeproofs)
|
||||
ss += struct.pack("<i", txTo.nLockTime)
|
||||
ss += struct.pack("<I", hashtype)
|
||||
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ BASE_SCRIPTS = [
|
|||
'feature_initial_reissuance_token.py',
|
||||
'feature_progress.py',
|
||||
'feature_dynafed.py',
|
||||
'feature_sighash_rangeproof.py',
|
||||
# Longest test should go first, to favor running tests in parallel
|
||||
'wallet_hd.py',
|
||||
'wallet_hd.py --descriptors',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue