Further DoS prevention: Verify signatures last
Loop over all inputs doing inexpensive validity checks first, and then loop over them a second time doing expensive signature checks. This helps prevent possible CPU exhaustion attacks where an attacker tries to make a victim waste time checking signatures for invalid transactions.
This commit is contained in:
parent
7a15109c04
commit
4add41a2a6
1 changed files with 16 additions and 5 deletions
21
src/main.cpp
21
src/main.cpp
|
@ -1142,17 +1142,28 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs,
|
||||||
if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
|
if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
|
||||||
return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
|
return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
|
||||||
|
|
||||||
|
// Check for negative or overflow input values
|
||||||
|
nValueIn += txPrev.vout[prevout.n].nValue;
|
||||||
|
if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
||||||
|
return DoS(100, error("ConnectInputs() : txin values out of range"));
|
||||||
|
|
||||||
|
}
|
||||||
|
// The first loop above does all the inexpensive checks.
|
||||||
|
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
||||||
|
// Helps prevent CPU exhaustion attacks.
|
||||||
|
for (unsigned int i = 0; i < vin.size(); i++)
|
||||||
|
{
|
||||||
|
COutPoint prevout = vin[i].prevout;
|
||||||
|
assert(inputs.count(prevout.hash) > 0);
|
||||||
|
CTxIndex& txindex = inputs[prevout.hash].first;
|
||||||
|
CTransaction& txPrev = inputs[prevout.hash].second;
|
||||||
|
|
||||||
// Check for conflicts (double-spend)
|
// Check for conflicts (double-spend)
|
||||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
// 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.
|
// for an attacker to attempt to split the network.
|
||||||
if (!txindex.vSpent[prevout.n].IsNull())
|
if (!txindex.vSpent[prevout.n].IsNull())
|
||||||
return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str());
|
return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str());
|
||||||
|
|
||||||
// Check for negative or overflow input values
|
|
||||||
nValueIn += txPrev.vout[prevout.n].nValue;
|
|
||||||
if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
|
||||||
return DoS(100, error("ConnectInputs() : txin values out of range"));
|
|
||||||
|
|
||||||
// Skip ECDSA signature verification when connecting blocks (fBlock=true)
|
// Skip ECDSA signature verification when connecting blocks (fBlock=true)
|
||||||
// before the last blockchain checkpoint. This is safe because block merkle hashes are
|
// before the last blockchain checkpoint. This is safe because block merkle hashes are
|
||||||
// still computed and checked, and any change will be caught at the next checkpoint.
|
// still computed and checked, and any change will be caught at the next checkpoint.
|
||||||
|
|
Loading…
Reference in a new issue