Merge #8524: Precompute sighashes
35fe039 Rename to PrecomputedTransactionData (Pieter Wuille) ab48c5e Unit test for sighash caching (Nicolas DORIER) d2c5d04 Precompute sighashes (Pieter Wuille) add missing change from bitcoinconsensus.cpp Signed-off-by: Pasta <Pasta@dash.org>
This commit is contained in:
parent
a83b63186b
commit
9be6722049
@ -91,10 +91,11 @@ int dashconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int
|
||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
||||
return set_error(err, dashconsensus_ERR_TX_SIZE_MISMATCH);
|
||||
|
||||
// Regardless of the verification result, the tx did not error.
|
||||
set_error(err, dashconsensus_ERR_OK);
|
||||
// Regardless of the verification result, the tx did not error.
|
||||
set_error(err, dashconsensus_ERR_OK);
|
||||
|
||||
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, TransactionSignatureChecker(&tx, nIn), NULL);
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, TransactionSignatureChecker(&tx, nIn, txdata), NULL);
|
||||
} catch (const std::exception&) {
|
||||
return set_error(err, dashconsensus_ERR_TX_DESERIALIZE); // Error deserializing
|
||||
}
|
||||
|
@ -1117,9 +1117,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
|
||||
|
||||
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||
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 PrecomputedTransactionData* cache)
|
||||
{
|
||||
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
if (nIn >= txTo.vin.size()) {
|
||||
@ -1162,7 +1193,7 @@ bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vch
|
||||
int nHashType = vchSig.back();
|
||||
vchSig.pop_back();
|
||||
|
||||
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType);
|
||||
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, this->txdata);
|
||||
|
||||
if (!VerifySignature(vchSig, pubkey, sighash))
|
||||
return false;
|
||||
|
@ -94,7 +94,14 @@ enum
|
||||
|
||||
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
|
||||
|
||||
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs;
|
||||
|
||||
PrecomputedTransactionData(const CTransaction& tx);
|
||||
};
|
||||
|
||||
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const PrecomputedTransactionData* cache = NULL);
|
||||
|
||||
class BaseSignatureChecker
|
||||
{
|
||||
@ -122,12 +129,14 @@ class TransactionSignatureChecker : public BaseSignatureChecker
|
||||
private:
|
||||
const CTransaction* txTo;
|
||||
unsigned int nIn;
|
||||
const PrecomputedTransactionData* txdata;
|
||||
|
||||
protected:
|
||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
|
||||
public:
|
||||
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {}
|
||||
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn), txdata(NULL) {}
|
||||
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), txdata(&txdataIn) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const override;
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override;
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override;
|
||||
|
@ -46,7 +46,7 @@ private:
|
||||
bool store;
|
||||
|
||||
public:
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn), store(storeIn) {}
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, PrecomputedTransactionData& txdataIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn, txdataIn), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
};
|
||||
|
@ -104,19 +104,21 @@ BOOST_AUTO_TEST_CASE(sign)
|
||||
}
|
||||
// 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:
|
||||
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++)
|
||||
{
|
||||
CScript sigSave = txTo[i].vin[0].scriptSig;
|
||||
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
|
||||
const CTxOut& output = txFrom.vout[txTo[i].vin[0].prevout.n];
|
||||
bool sigOK = CScriptCheck(output.scriptPubKey, output.nValue, txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)();
|
||||
bool sigOK = CScriptCheck(output.scriptPubKey, output.nValue, txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
|
||||
if (i == j)
|
||||
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
|
||||
else
|
||||
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
|
||||
txTo[i].vin[0].scriptSig = sigSave;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(norecurse)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "test/test_dash.h"
|
||||
|
||||
#include "clientversion.h"
|
||||
#include "checkqueue.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "core_io.h"
|
||||
#include "key.h"
|
||||
@ -139,6 +140,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
||||
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
|
||||
BOOST_CHECK(state.IsValid());
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
{
|
||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||
@ -149,7 +151,7 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
||||
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
||||
verify_flags, TransactionSignatureChecker(&tx, i), &err),
|
||||
verify_flags, TransactionSignatureChecker(&tx, i, txdata), &err),
|
||||
strTest);
|
||||
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
|
||||
}
|
||||
@ -214,6 +216,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
||||
CValidationState state;
|
||||
fValid = CheckTransaction(tx, state) && state.IsValid();
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
|
||||
{
|
||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||
@ -224,7 +227,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
||||
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
||||
verify_flags, TransactionSignatureChecker(&tx, i), &err);
|
||||
verify_flags, TransactionSignatureChecker(&tx, i, txdata), &err);
|
||||
}
|
||||
BOOST_CHECK_MESSAGE(!fValid, strTest);
|
||||
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
|
||||
|
@ -832,7 +832,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
|
||||
// Check against previous transactions
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true))
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata))
|
||||
return false; // state filled in by CheckInputs
|
||||
|
||||
// Check again against just the consensus-critical mandatory script
|
||||
@ -844,7 +845,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
// There is a similar check in CreateNewBlock() to prevent creating
|
||||
// invalid blocks, however allowing such transactions into the mempool
|
||||
// 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",
|
||||
__func__, hash.ToString(), FormatStateMessage(state));
|
||||
@ -1331,7 +1332,8 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
||||
|
||||
bool CScriptCheck::operator()() {
|
||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) {
|
||||
PrecomputedTransactionData txdata(*ptxTo);
|
||||
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, txdata, cacheStore), &error)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -1390,7 +1392,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
||||
}
|
||||
}// 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())
|
||||
{
|
||||
@ -1424,7 +1426,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
||||
const CAmount amount = coin.out.nValue;
|
||||
|
||||
// Verify signature
|
||||
CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheStore);
|
||||
CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheStore, &txdata);
|
||||
if (pvChecks) {
|
||||
pvChecks->push_back(CScriptCheck());
|
||||
check.swap(pvChecks->back());
|
||||
@ -1437,7 +1439,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
||||
// avoid splitting the network between upgraded and
|
||||
// non-upgraded nodes.
|
||||
CScriptCheck check2(scriptPubKey, amount, tx, i,
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore);
|
||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
|
||||
if (check2())
|
||||
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
||||
}
|
||||
@ -2015,6 +2017,9 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressUnspentIndex;
|
||||
std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> > spentIndex;
|
||||
|
||||
std::vector<PrecomputedTransactionData> txdata;
|
||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
||||
|
||||
bool fDIP0001Active_context = pindex->nHeight >= Params().GetConsensus().DIP0001Height;
|
||||
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||
@ -2028,6 +2033,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
|
||||
txdata.emplace_back(tx);
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
if (!view.HaveInputs(tx))
|
||||
@ -2102,7 +2108,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
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",
|
||||
tx.GetHash().ToString(), FormatStateMessage(state));
|
||||
control.Add(vChecks);
|
||||
|
@ -46,6 +46,7 @@ class CBlockPolicyEstimator;
|
||||
class CTxMemPool;
|
||||
class CValidationInterface;
|
||||
class CValidationState;
|
||||
class PrecomputedTransactionData;
|
||||
struct ChainTxData;
|
||||
|
||||
struct LockPoints;
|
||||
@ -233,7 +234,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 945 * 1024 * 1024;
|
||||
*
|
||||
* Note that we guarantee that either the proof-of-work is valid on pblock, or
|
||||
* (and possibly also) BlockChecked will have been called.
|
||||
*
|
||||
*
|
||||
* Call without cs_main held.
|
||||
*
|
||||
* @param[in] pblock The block we want to process.
|
||||
@ -377,7 +378,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& ma
|
||||
* instead of being performed inline.
|
||||
*/
|
||||
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 */
|
||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
|
||||
@ -450,12 +451,13 @@ private:
|
||||
unsigned int nFlags;
|
||||
bool cacheStore;
|
||||
ScriptError error;
|
||||
PrecomputedTransactionData *txdata;
|
||||
|
||||
public:
|
||||
CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
|
||||
CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) :
|
||||
CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
|
||||
scriptPubKey(scriptPubKeyIn),
|
||||
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()();
|
||||
|
||||
@ -466,6 +468,7 @@ public:
|
||||
std::swap(nFlags, check.nFlags);
|
||||
std::swap(cacheStore, check.cacheStore);
|
||||
std::swap(error, check.error);
|
||||
std::swap(txdata, check.txdata);
|
||||
}
|
||||
|
||||
ScriptError GetScriptError() const { return error; }
|
||||
|
Loading…
Reference in New Issue
Block a user