Skip to content

Commit

Permalink
Merge bitcoin#8524: Precompute sighashes
Browse files Browse the repository at this point in the history
35fe039 Rename to PrecomputedTransactionData (Pieter Wuille)
ab48c5e Unit test for sighash caching (Nicolas DORIER)
d2c5d04 Precompute sighashes (Pieter Wuille)
  • Loading branch information
sipa authored and PastaPastaPasta committed Jun 7, 2019
1 parent 809aae7 commit b9a1650
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 13 deletions.
35 changes: 33 additions & 2 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1117,9 +1117,40 @@ class CTransactionSignatureSerializer {
}
};

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()) {
Expand Down Expand Up @@ -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;
Expand Down
13 changes: 11 additions & 2 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/script/sigcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
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, bool storeIn=true, , PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, txdataIn), store(storeIn) {}

bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
};
Expand Down
6 changes: 4 additions & 2 deletions src/test/script_P2SH_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 5 additions & 2 deletions src/test/transaction_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
Expand All @@ -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));
}
Expand Down Expand Up @@ -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))
Expand All @@ -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));
Expand Down
11 changes: 7 additions & 4 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class CBlockPolicyEstimator;
class CTxMemPool;
class CValidationInterface;
class CValidationState;
class PrecomputedTransactionData;
struct ChainTxData;

struct LockPoints;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -450,12 +451,13 @@ class CScriptCheck
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()();

Expand All @@ -466,6 +468,7 @@ class CScriptCheck
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; }
Expand Down

0 comments on commit b9a1650

Please sign in to comment.