Browse Source

Precompute sighashes

Original version by Nicolas Dorier. Precomputing version by Pieter Wuille.
0.13
Pieter Wuille 8 years ago
parent
commit
b8c79a057c
  1. 22
      src/main.cpp
  2. 9
      src/main.h
  3. 4
      src/script/bitcoinconsensus.cpp
  4. 54
      src/script/interpreter.cpp
  5. 13
      src/script/interpreter.h
  6. 2
      src/script/sigcache.h
  7. 6
      src/test/script_P2SH_tests.cpp
  8. 87
      src/test/transaction_tests.cpp
  9. 6
      src/txmempool.cpp

22
src/main.cpp

@ -1497,12 +1497,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();
} }
@ -1518,7 +1519,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));
@ -1915,7 +1916,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;
@ -1974,7 +1975,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())
{ {
@ -2001,7 +2002,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());
@ -2014,7 +2015,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())));
} }
@ -2412,6 +2413,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];
@ -2458,13 +2461,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);

9
src/main.h

@ -39,6 +39,7 @@ class CTxMemPool;
class CValidationInterface; class CValidationInterface;
class CValidationState; class CValidationState;
struct 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);
@ -408,12 +409,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()();
@ -425,6 +427,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; }

4
src/script/bitcoinconsensus.cpp

@ -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
} }

54
src/script/interpreter.cpp

@ -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;

13
src/script/interpreter.h

@ -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;

2
src/script/sigcache.h

@ -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;
}; };

6
src/test/script_P2SH_tests.cpp

@ -107,18 +107,20 @@ 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
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j)); BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
txTo[i].vin[0].scriptSig = sigSave; txTo[i].vin[0].scriptSig = sigSave;
} }
}
} }
BOOST_AUTO_TEST_CASE(norecurse) BOOST_AUTO_TEST_CASE(norecurse)

87
src/test/transaction_tests.cpp

@ -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;

6
src/txmempool.cpp

@ -737,7 +737,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
waitingOnDependants.push_back(&(*it)); waitingOnDependants.push_back(&(*it));
else { else {
CValidationState state; CValidationState state;
assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, NULL)); PrecomputedTransactionData txdata(tx);
assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, txdata, NULL));
UpdateCoins(tx, mempoolDuplicate, 1000000); UpdateCoins(tx, mempoolDuplicate, 1000000);
} }
} }
@ -751,7 +752,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
stepsSinceLastRemove++; stepsSinceLastRemove++;
assert(stepsSinceLastRemove < waitingOnDependants.size()); assert(stepsSinceLastRemove < waitingOnDependants.size());
} else { } else {
assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, NULL)); PrecomputedTransactionData txdata(entry->GetTx());
assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, txdata, NULL));
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
stepsSinceLastRemove = 0; stepsSinceLastRemove = 0;
} }

Loading…
Cancel
Save