mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 04:52:59 +01:00
Merge #8524: Precompute sighashes
35fe039
Rename to PrecomputedTransactionData (Pieter Wuille)ab48c5e
Unit test for sighash caching (Nicolas DORIER)d2c5d04
Precompute sighashes (Pieter Wuille)
This commit is contained in:
commit
19b0f33de0
22
src/main.cpp
22
src/main.cpp
@ -1496,12 +1496,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||||||
|
|
||||||
// Check against previous transactions
|
// Check against previous transactions
|
||||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||||
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true)) {
|
PrecomputedTransactionData txdata(tx);
|
||||||
|
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) {
|
||||||
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
|
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
|
||||||
// need to turn both off, and compare against just turning off CLEANSTACK
|
// need to turn both off, and compare against just turning off CLEANSTACK
|
||||||
// to see if the failure is specifically due to witness validation.
|
// to see if the failure is specifically due to witness validation.
|
||||||
if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) &&
|
if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
|
||||||
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true)) {
|
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) {
|
||||||
// Only the witness is wrong, so the transaction itself may be fine.
|
// Only the witness is wrong, so the transaction itself may be fine.
|
||||||
state.SetCorruptionPossible();
|
state.SetCorruptionPossible();
|
||||||
}
|
}
|
||||||
@ -1517,7 +1518,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||||||
// There is a similar check in CreateNewBlock() to prevent creating
|
// There is a similar check in CreateNewBlock() to prevent creating
|
||||||
// invalid blocks, however allowing such transactions into the mempool
|
// invalid blocks, however allowing such transactions into the mempool
|
||||||
// can be exploited as a DoS attack.
|
// can be exploited as a DoS attack.
|
||||||
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
|
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata))
|
||||||
{
|
{
|
||||||
return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
|
return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
|
||||||
__func__, hash.ToString(), FormatStateMessage(state));
|
__func__, hash.ToString(), FormatStateMessage(state));
|
||||||
@ -1914,7 +1915,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
|||||||
bool CScriptCheck::operator()() {
|
bool CScriptCheck::operator()() {
|
||||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||||
const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL;
|
const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL;
|
||||||
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) {
|
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1973,7 +1974,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||||||
}
|
}
|
||||||
}// namespace Consensus
|
}// namespace Consensus
|
||||||
|
|
||||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks)
|
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
@ -2000,7 +2001,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
|||||||
assert(coins);
|
assert(coins);
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
CScriptCheck check(*coins, tx, i, flags, cacheStore);
|
CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata);
|
||||||
if (pvChecks) {
|
if (pvChecks) {
|
||||||
pvChecks->push_back(CScriptCheck());
|
pvChecks->push_back(CScriptCheck());
|
||||||
check.swap(pvChecks->back());
|
check.swap(pvChecks->back());
|
||||||
@ -2013,7 +2014,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
|||||||
// avoid splitting the network between upgraded and
|
// avoid splitting the network between upgraded and
|
||||||
// non-upgraded nodes.
|
// non-upgraded nodes.
|
||||||
CScriptCheck check2(*coins, tx, i,
|
CScriptCheck check2(*coins, tx, i,
|
||||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore);
|
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
|
||||||
if (check2())
|
if (check2())
|
||||||
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
||||||
}
|
}
|
||||||
@ -2409,6 +2410,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||||
vPos.reserve(block.vtx.size());
|
vPos.reserve(block.vtx.size());
|
||||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||||
|
std::vector<PrecomputedTransactionData> txdata;
|
||||||
|
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
||||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||||
{
|
{
|
||||||
const CTransaction &tx = block.vtx[i];
|
const CTransaction &tx = block.vtx[i];
|
||||||
@ -2455,13 +2458,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||||
REJECT_INVALID, "bad-blk-sigops");
|
REJECT_INVALID, "bad-blk-sigops");
|
||||||
|
|
||||||
|
txdata.emplace_back(tx);
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
nFees += view.GetValueIn(tx)-tx.GetValueOut();
|
nFees += view.GetValueIn(tx)-tx.GetValueOut();
|
||||||
|
|
||||||
std::vector<CScriptCheck> vChecks;
|
std::vector<CScriptCheck> vChecks;
|
||||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||||
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, nScriptCheckThreads ? &vChecks : NULL))
|
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL))
|
||||||
return error("ConnectBlock(): CheckInputs on %s failed with %s",
|
return error("ConnectBlock(): CheckInputs on %s failed with %s",
|
||||||
tx.GetHash().ToString(), FormatStateMessage(state));
|
tx.GetHash().ToString(), FormatStateMessage(state));
|
||||||
control.Add(vChecks);
|
control.Add(vChecks);
|
||||||
|
@ -38,6 +38,7 @@ class CScriptCheck;
|
|||||||
class CTxMemPool;
|
class CTxMemPool;
|
||||||
class CValidationInterface;
|
class CValidationInterface;
|
||||||
class CValidationState;
|
class CValidationState;
|
||||||
|
class PrecomputedTransactionData;
|
||||||
|
|
||||||
struct CNodeStateStats;
|
struct CNodeStateStats;
|
||||||
struct LockPoints;
|
struct LockPoints;
|
||||||
@ -347,7 +348,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
|
|||||||
* instead of being performed inline.
|
* instead of being performed inline.
|
||||||
*/
|
*/
|
||||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
|
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
|
||||||
unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks = NULL);
|
unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL);
|
||||||
|
|
||||||
/** Apply the effects of this transaction on the UTXO set represented by view */
|
/** Apply the effects of this transaction on the UTXO set represented by view */
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
|
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
|
||||||
@ -421,12 +422,13 @@ private:
|
|||||||
unsigned int nFlags;
|
unsigned int nFlags;
|
||||||
bool cacheStore;
|
bool cacheStore;
|
||||||
ScriptError error;
|
ScriptError error;
|
||||||
|
PrecomputedTransactionData *txdata;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
|
CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
|
||||||
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) :
|
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||||
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue),
|
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue),
|
||||||
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { }
|
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { }
|
||||||
|
|
||||||
bool operator()();
|
bool operator()();
|
||||||
|
|
||||||
@ -438,6 +440,7 @@ public:
|
|||||||
std::swap(nFlags, check.nFlags);
|
std::swap(nFlags, check.nFlags);
|
||||||
std::swap(cacheStore, check.cacheStore);
|
std::swap(cacheStore, check.cacheStore);
|
||||||
std::swap(error, check.error);
|
std::swap(error, check.error);
|
||||||
|
std::swap(txdata, check.txdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptError GetScriptError() const { return error; }
|
ScriptError GetScriptError() const { return error; }
|
||||||
|
@ -84,8 +84,8 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
|
|||||||
|
|
||||||
// Regardless of the verification result, the tx did not error.
|
// Regardless of the verification result, the tx did not error.
|
||||||
set_error(err, bitcoinconsensus_ERR_OK);
|
set_error(err, bitcoinconsensus_ERR_OK);
|
||||||
|
PrecomputedTransactionData txdata(tx);
|
||||||
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount), NULL);
|
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL);
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
|
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
|
||||||
}
|
}
|
||||||
|
@ -1108,9 +1108,40 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint256 GetPrevoutHash(const CTransaction& txTo) {
|
||||||
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
|
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
|
||||||
|
ss << txTo.vin[n].prevout;
|
||||||
|
}
|
||||||
|
return ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 GetSequenceHash(const CTransaction& txTo) {
|
||||||
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
|
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
|
||||||
|
ss << txTo.vin[n].nSequence;
|
||||||
|
}
|
||||||
|
return ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 GetOutputsHash(const CTransaction& txTo) {
|
||||||
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
|
for (unsigned int n = 0; n < txTo.vout.size(); n++) {
|
||||||
|
ss << txTo.vout[n];
|
||||||
|
}
|
||||||
|
return ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
} // anon namespace
|
} // anon namespace
|
||||||
|
|
||||||
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion)
|
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
||||||
|
{
|
||||||
|
hashPrevouts = GetPrevoutHash(txTo);
|
||||||
|
hashSequence = GetSequenceHash(txTo);
|
||||||
|
hashOutputs = GetOutputsHash(txTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||||
{
|
{
|
||||||
if (sigversion == SIGVERSION_WITNESS_V0) {
|
if (sigversion == SIGVERSION_WITNESS_V0) {
|
||||||
uint256 hashPrevouts;
|
uint256 hashPrevouts;
|
||||||
@ -1118,27 +1149,16 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
|
|||||||
uint256 hashOutputs;
|
uint256 hashOutputs;
|
||||||
|
|
||||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
||||||
CHashWriter ss(SER_GETHASH, 0);
|
hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
|
||||||
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
|
|
||||||
ss << txTo.vin[n].prevout;
|
|
||||||
}
|
|
||||||
hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
||||||
CHashWriter ss(SER_GETHASH, 0);
|
hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo);
|
||||||
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
|
|
||||||
ss << txTo.vin[n].nSequence;
|
|
||||||
}
|
|
||||||
hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
||||||
CHashWriter ss(SER_GETHASH, 0);
|
hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo);
|
||||||
for (unsigned int n = 0; n < txTo.vout.size(); n++) {
|
|
||||||
ss << txTo.vout[n];
|
|
||||||
}
|
|
||||||
hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction
|
|
||||||
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
|
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
|
||||||
CHashWriter ss(SER_GETHASH, 0);
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
ss << txTo.vout[nIn];
|
ss << txTo.vout[nIn];
|
||||||
@ -1209,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn
|
|||||||
int nHashType = vchSig.back();
|
int nHashType = vchSig.back();
|
||||||
vchSig.pop_back();
|
vchSig.pop_back();
|
||||||
|
|
||||||
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
|
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
|
||||||
|
|
||||||
if (!VerifySignature(vchSig, pubkey, sighash))
|
if (!VerifySignature(vchSig, pubkey, sighash))
|
||||||
return false;
|
return false;
|
||||||
|
@ -98,13 +98,20 @@ enum
|
|||||||
|
|
||||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||||
|
|
||||||
|
struct PrecomputedTransactionData
|
||||||
|
{
|
||||||
|
uint256 hashPrevouts, hashSequence, hashOutputs;
|
||||||
|
|
||||||
|
PrecomputedTransactionData(const CTransaction& tx);
|
||||||
|
};
|
||||||
|
|
||||||
enum SigVersion
|
enum SigVersion
|
||||||
{
|
{
|
||||||
SIGVERSION_BASE = 0,
|
SIGVERSION_BASE = 0,
|
||||||
SIGVERSION_WITNESS_V0 = 1,
|
SIGVERSION_WITNESS_V0 = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion);
|
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL);
|
||||||
|
|
||||||
class BaseSignatureChecker
|
class BaseSignatureChecker
|
||||||
{
|
{
|
||||||
@ -133,12 +140,14 @@ private:
|
|||||||
const CTransaction* txTo;
|
const CTransaction* txTo;
|
||||||
unsigned int nIn;
|
unsigned int nIn;
|
||||||
const CAmount amount;
|
const CAmount amount;
|
||||||
|
const PrecomputedTransactionData* txdata;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn) {}
|
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {}
|
||||||
|
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const;
|
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const;
|
||||||
bool CheckLockTime(const CScriptNum& nLockTime) const;
|
bool CheckLockTime(const CScriptNum& nLockTime) const;
|
||||||
bool CheckSequence(const CScriptNum& nSequence) const;
|
bool CheckSequence(const CScriptNum& nSequence) const;
|
||||||
|
@ -22,7 +22,7 @@ private:
|
|||||||
bool store;
|
bool store;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {}
|
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, txdataIn), store(storeIn) {}
|
||||||
|
|
||||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||||
};
|
};
|
||||||
|
@ -107,12 +107,13 @@ BOOST_AUTO_TEST_CASE(sign)
|
|||||||
}
|
}
|
||||||
// All of the above should be OK, and the txTos have valid signatures
|
// All of the above should be OK, and the txTos have valid signatures
|
||||||
// Check to make sure signature verification fails if we use the wrong ScriptSig:
|
// Check to make sure signature verification fails if we use the wrong ScriptSig:
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++) {
|
||||||
|
PrecomputedTransactionData txdata(txTo[i]);
|
||||||
for (int j = 0; j < 8; j++)
|
for (int j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
CScript sigSave = txTo[i].vin[0].scriptSig;
|
CScript sigSave = txTo[i].vin[0].scriptSig;
|
||||||
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
|
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
|
||||||
bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)();
|
bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
|
||||||
if (i == j)
|
if (i == j)
|
||||||
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
|
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
|
||||||
else
|
else
|
||||||
@ -120,6 +121,7 @@ BOOST_AUTO_TEST_CASE(sign)
|
|||||||
txTo[i].vin[0].scriptSig = sigSave;
|
txTo[i].vin[0].scriptSig = sigSave;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(norecurse)
|
BOOST_AUTO_TEST_CASE(norecurse)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "test/test_bitcoin.h"
|
#include "test/test_bitcoin.h"
|
||||||
|
|
||||||
#include "clientversion.h"
|
#include "clientversion.h"
|
||||||
|
#include "checkqueue.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
#include "core_io.h"
|
#include "core_io.h"
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
@ -153,6 +154,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||||||
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
|
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
|
||||||
BOOST_CHECK(state.IsValid());
|
BOOST_CHECK(state.IsValid());
|
||||||
|
|
||||||
|
PrecomputedTransactionData txdata(tx);
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||||
@ -168,7 +170,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||||
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL;
|
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL;
|
||||||
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
||||||
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err),
|
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err),
|
||||||
strTest);
|
strTest);
|
||||||
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
|
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
|
||||||
}
|
}
|
||||||
@ -237,6 +239,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||||||
CValidationState state;
|
CValidationState state;
|
||||||
fValid = CheckTransaction(tx, state) && state.IsValid();
|
fValid = CheckTransaction(tx, state) && state.IsValid();
|
||||||
|
|
||||||
|
PrecomputedTransactionData txdata(tx);
|
||||||
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
|
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
|
||||||
{
|
{
|
||||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||||
@ -252,7 +255,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||||||
}
|
}
|
||||||
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL;
|
const CScriptWitness *witness = (i < tx.wit.vtxinwit.size()) ? &tx.wit.vtxinwit[i].scriptWitness : NULL;
|
||||||
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
||||||
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err);
|
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
|
||||||
}
|
}
|
||||||
BOOST_CHECK_MESSAGE(!fValid, strTest);
|
BOOST_CHECK_MESSAGE(!fValid, strTest);
|
||||||
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
|
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
|
||||||
@ -419,6 +422,86 @@ void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
|
|||||||
script = PushAll(stack);
|
script = PushAll(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
|
||||||
|
CMutableTransaction mtx;
|
||||||
|
mtx.nVersion = 1;
|
||||||
|
|
||||||
|
CKey key;
|
||||||
|
key.MakeNewKey(false);
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
keystore.AddKeyPubKey(key, key.GetPubKey());
|
||||||
|
CKeyID hash = key.GetPubKey().GetID();
|
||||||
|
CScript scriptPubKey = CScript() << OP_0 << std::vector<unsigned char>(hash.begin(), hash.end());
|
||||||
|
|
||||||
|
vector<int> sigHashes;
|
||||||
|
sigHashes.push_back(SIGHASH_NONE | SIGHASH_ANYONECANPAY);
|
||||||
|
sigHashes.push_back(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY);
|
||||||
|
sigHashes.push_back(SIGHASH_ALL | SIGHASH_ANYONECANPAY);
|
||||||
|
sigHashes.push_back(SIGHASH_NONE);
|
||||||
|
sigHashes.push_back(SIGHASH_SINGLE);
|
||||||
|
sigHashes.push_back(SIGHASH_ALL);
|
||||||
|
|
||||||
|
// create a big transaction of 4500 inputs signed by the same key
|
||||||
|
for(uint32_t ij = 0; ij < 4500; ij++) {
|
||||||
|
uint32_t i = mtx.vin.size();
|
||||||
|
uint256 prevId;
|
||||||
|
prevId.SetHex("0000000000000000000000000000000000000000000000000000000000000100");
|
||||||
|
COutPoint outpoint(prevId, i);
|
||||||
|
|
||||||
|
mtx.vin.resize(mtx.vin.size() + 1);
|
||||||
|
mtx.vin[i].prevout = outpoint;
|
||||||
|
mtx.vin[i].scriptSig = CScript();
|
||||||
|
|
||||||
|
mtx.vout.resize(mtx.vout.size() + 1);
|
||||||
|
mtx.vout[i].nValue = 1000;
|
||||||
|
mtx.vout[i].scriptPubKey = CScript() << OP_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign all inputs
|
||||||
|
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
||||||
|
bool hashSigned = SignSignature(keystore, scriptPubKey, mtx, i, 1000, sigHashes.at(i % sigHashes.size()));
|
||||||
|
assert(hashSigned);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTransaction tx;
|
||||||
|
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
WithOrVersion(&ssout, 0) << mtx;
|
||||||
|
WithOrVersion(&ssout, 0) >> tx;
|
||||||
|
|
||||||
|
// check all inputs concurrently, with the cache
|
||||||
|
PrecomputedTransactionData txdata(tx);
|
||||||
|
boost::thread_group threadGroup;
|
||||||
|
CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
||||||
|
CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);
|
||||||
|
|
||||||
|
for (int i=0; i<20; i++)
|
||||||
|
threadGroup.create_thread(boost::bind(&CCheckQueue<CScriptCheck>::Thread, boost::ref(scriptcheckqueue)));
|
||||||
|
|
||||||
|
CCoins coins;
|
||||||
|
coins.nVersion = 1;
|
||||||
|
coins.fCoinBase = false;
|
||||||
|
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
||||||
|
CTxOut txout;
|
||||||
|
txout.nValue = 1000;
|
||||||
|
txout.scriptPubKey = scriptPubKey;
|
||||||
|
coins.vout.push_back(txout);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
||||||
|
std::vector<CScriptCheck> vChecks;
|
||||||
|
CScriptCheck check(coins, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
|
||||||
|
vChecks.push_back(CScriptCheck());
|
||||||
|
check.swap(vChecks.back());
|
||||||
|
control.Add(vChecks);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool controlCheck = control.Wait();
|
||||||
|
assert(controlCheck);
|
||||||
|
|
||||||
|
threadGroup.interrupt_all();
|
||||||
|
threadGroup.join_all();
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_witness)
|
BOOST_AUTO_TEST_CASE(test_witness)
|
||||||
{
|
{
|
||||||
CBasicKeyStore keystore, keystore2;
|
CBasicKeyStore keystore, keystore2;
|
||||||
|
Loading…
Reference in New Issue
Block a user