From f1136200a67fc1df894d45fba51b40f748e0b889 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 8 Sep 2012 17:33:10 +0200 Subject: Move VerifySignature to main --- src/main.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 28fbc8b45..c0446370a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1348,6 +1348,17 @@ bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const return true; } +bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +{ + assert(nIn < txTo.vin.size()); + const CTxIn& txin = txTo.vin[nIn]; + if (txin.prevout.n >= txFrom.vout.size()) + return false; + const CTxOut& txout = txFrom.vout[txin.prevout.n]; + + return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, flags, nHashType); +} + bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const { if (!IsCoinBase()) -- cgit v1.2.3 From 2800ce7367760a9bb8fd5209feddf71e574a592d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 1 Dec 2012 22:30:06 +0100 Subject: Add CScriptCheck: a closure representing a script check --- src/main.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index c0446370a..4441c65fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1348,15 +1348,16 @@ bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const return true; } +bool CScriptCheck::operator()() const { + const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; + if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) + return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().substr(0,10).c_str()); + return true; +} + bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) { - assert(nIn < txTo.vin.size()); - const CTxIn& txin = txTo.vin[nIn]; - if (txin.prevout.n >= txFrom.vout.size()) - return false; - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, flags, nHashType); + return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const -- cgit v1.2.3 From 1d70f4bde8f6adc4df65397f486186a694a74c60 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 1 Dec 2012 22:51:10 +0100 Subject: Remove CheckSig_mode and move logic out of CheckInputs() --- src/main.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 4441c65fd..7b37d0644 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -772,7 +772,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.CheckInputs(view, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) + if (!tx.CheckInputs(view, true, SCRIPT_VERIFY_P2SH)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -1360,7 +1360,7 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)(); } -bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const +bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags) const { if (!IsCoinBase()) { @@ -1410,8 +1410,7 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod // Skip ECDSA signature verification when connecting blocks // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. - if (csmode == CS_ALWAYS || - (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) { + if (fScriptChecks) { for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint &prevout = vin[i].prevout; const CCoins &coins = inputs.GetCoins(prevout.hash); @@ -1577,6 +1576,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust // verify that the view's current state corresponds to the previous block assert(pindex->pprev == view.GetBestBlock()); + bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(); + // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. // If such overwrites are allowed, coinbases and transactions depending upon those @@ -1637,7 +1638,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nFees += tx.GetValueIn(view)-tx.GetValueOut(); - if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE)) + if (!tx.CheckInputs(view, fScriptChecks, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE)) return false; } @@ -3924,7 +3925,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; - if (!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_P2SH)) + if (!tx.CheckInputs(viewTemp, true, SCRIPT_VERIFY_P2SH)) continue; CTxUndo txundo; -- cgit v1.2.3 From f9cae832e6f56c6abe89b3bf05d1f176c2a7c913 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 1 Dec 2012 23:04:14 +0100 Subject: 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. --- src/main.cpp | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 7b37d0644..18a1c59e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include "net.h" #include "init.h" #include "ui_interface.h" +#include "checkqueue.h" #include #include #include @@ -40,6 +41,7 @@ uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; set 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 *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 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 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 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; -- cgit v1.2.3 From ef0f422519de4a3ce47d923e5f8f90cd12349f3e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 8 Dec 2012 22:49:04 +0100 Subject: Remove contention on signature cache during block validation Since block validation happens in parallel, multiple threads may be accessing the signature cache simultaneously. To prevent contention: * Turn the signature cache lock into a shared mutex * Make reading from the cache only acquire a shared lock * Let block validations not store their results in the cache --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index 18a1c59e1..92be0cfd3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1627,6 +1627,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust int64 nBIP16SwitchTime = 1333238400; bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); + unsigned int flags = SCRIPT_VERIFY_NOCACHE | + (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE); + CBlockUndo blockundo; CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -1663,7 +1666,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust nFees += tx.GetValueIn(view)-tx.GetValueOut(); std::vector vChecks; - if (!tx.CheckInputs(view, fScriptChecks, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, nScriptCheckThreads ? &vChecks : NULL)) + if (!tx.CheckInputs(view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } -- cgit v1.2.3