mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-18 22:35:39 +01:00
Parallelize script verification
* During block verification (when parallelism is requested), script check actions are stored instead of being executed immediately. * After every processed transactions, its signature actions are pushed to a CScriptCheckQueue, which maintains a queue and some synchronization mechanism. * Two or more threads (if enabled) start processing elements from this queue, * When the block connection code is finished processing transactions, it joins the worker pool until the queue is empty. As cs_main is held the entire time, and all verification must be finished before the block continues processing, this does not reach the best possible performance. It is a less drastic change than some more advanced mechanisms (like doing verification out-of-band entirely, and rolling back blocks when a failure is detected). The -par=N flag controls the number of threads (1-16). 0 means auto, and is the default.
This commit is contained in:
40
src/main.cpp
40
src/main.cpp
@@ -10,6 +10,7 @@
|
||||
#include "net.h"
|
||||
#include "init.h"
|
||||
#include "ui_interface.h"
|
||||
#include "checkqueue.h"
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
@@ -40,6 +41,7 @@ uint256 hashBestChain = 0;
|
||||
CBlockIndex* pindexBest = NULL;
|
||||
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
|
||||
int64 nTimeBestReceived = 0;
|
||||
int nScriptCheckThreads = 0;
|
||||
bool fImporting = false;
|
||||
bool fReindex = false;
|
||||
bool fBenchmark = false;
|
||||
@@ -1360,10 +1362,13 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in
|
||||
return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
|
||||
}
|
||||
|
||||
bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags) const
|
||||
bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const
|
||||
{
|
||||
if (!IsCoinBase())
|
||||
{
|
||||
if (pvChecks)
|
||||
pvChecks->reserve(vin.size());
|
||||
|
||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||
// for an attacker to attempt to split the network.
|
||||
if (!HaveInputs(inputs))
|
||||
@@ -1416,8 +1421,12 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsi
|
||||
const CCoins &coins = inputs.GetCoins(prevout.hash);
|
||||
|
||||
// Verify signature
|
||||
if (!VerifySignature(coins, *this, i, flags, 0))
|
||||
return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
|
||||
CScriptCheck check(coins, *this, i, flags, 0);
|
||||
if (pvChecks) {
|
||||
pvChecks->push_back(CScriptCheck());
|
||||
check.swap(pvChecks->back());
|
||||
} else if (!check())
|
||||
return DoS(100,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1567,6 +1576,19 @@ void static FlushBlockFile()
|
||||
|
||||
bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
|
||||
|
||||
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
||||
|
||||
void ThreadScriptCheck(void*) {
|
||||
vnThreadsRunning[THREAD_SCRIPTCHECK]++;
|
||||
RenameThread("bitcoin-scriptch");
|
||||
scriptcheckqueue.Thread();
|
||||
vnThreadsRunning[THREAD_SCRIPTCHECK]--;
|
||||
}
|
||||
|
||||
void ThreadScriptCheckQuit() {
|
||||
scriptcheckqueue.Quit();
|
||||
}
|
||||
|
||||
bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
|
||||
{
|
||||
// Check it again in case a previous version let a bad block in
|
||||
@@ -1607,6 +1629,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
||||
|
||||
CBlockUndo blockundo;
|
||||
|
||||
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
|
||||
|
||||
int64 nStart = GetTimeMicros();
|
||||
int64 nFees = 0;
|
||||
int nInputs = 0;
|
||||
@@ -1638,8 +1662,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
||||
|
||||
nFees += tx.GetValueIn(view)-tx.GetValueOut();
|
||||
|
||||
if (!tx.CheckInputs(view, fScriptChecks, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE))
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
if (!tx.CheckInputs(view, fScriptChecks, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, nScriptCheckThreads ? &vChecks : NULL))
|
||||
return false;
|
||||
control.Add(vChecks);
|
||||
}
|
||||
|
||||
CTxUndo txundo;
|
||||
@@ -1656,6 +1682,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
||||
if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
|
||||
return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees));
|
||||
|
||||
if (!control.Wait())
|
||||
return DoS(100, false);
|
||||
int64 nTime2 = GetTimeMicros() - nStart;
|
||||
if (fBenchmark)
|
||||
printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
|
||||
|
||||
if (fJustCheck)
|
||||
return true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user