BIP141: Other consensus critical limits, and BIP145
Includes changes by Suhas Daftuar, Luke-jr, and mruddy.
This commit is contained in:
78
src/main.cpp
78
src/main.cpp
@@ -684,8 +684,8 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c
|
||||
// have been mined or received.
|
||||
// 100 orphans, each of which is at most 99,999 bytes big is
|
||||
// at most 10 megabytes of orphans and somewhat more byprev index (in the worst case):
|
||||
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
|
||||
if (sz >= MAX_STANDARD_TX_SIZE)
|
||||
unsigned int sz = GetTransactionCost(tx);
|
||||
if (sz >= MAX_STANDARD_TX_COST)
|
||||
{
|
||||
LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
|
||||
return false;
|
||||
@@ -1018,8 +1018,24 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags)
|
||||
{
|
||||
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR;
|
||||
|
||||
if (tx.IsCoinBase())
|
||||
return nSigOps;
|
||||
|
||||
if (flags & SCRIPT_VERIFY_P2SH) {
|
||||
nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
{
|
||||
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]);
|
||||
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, i < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[i].scriptWitness : NULL, flags);
|
||||
}
|
||||
return nSigOps;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1033,7 +1049,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
||||
if (tx.vout.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
|
||||
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
||||
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE)
|
||||
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
|
||||
// Check for negative or overflow output values
|
||||
@@ -1239,8 +1255,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
if (fRequireStandard && !AreInputsStandard(tx, view))
|
||||
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
|
||||
|
||||
unsigned int nSigOps = GetLegacySigOpCount(tx);
|
||||
nSigOps += GetP2SHSigOpCount(tx, view);
|
||||
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||
|
||||
CAmount nValueOut = tx.GetValueOut();
|
||||
CAmount nFees = nValueIn-nValueOut;
|
||||
@@ -1263,7 +1278,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
}
|
||||
}
|
||||
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp);
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
|
||||
unsigned int nSize = entry.GetTxSize();
|
||||
|
||||
// Check that the transaction doesn't have an excessive number of
|
||||
@@ -1271,9 +1286,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
// itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
|
||||
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
|
||||
// merely non-standard transaction.
|
||||
if ((nSigOps > MAX_STANDARD_TX_SIGOPS) || (nBytesPerSigOp && nSigOps > nSize / nBytesPerSigOp))
|
||||
if ((nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) || (nBytesPerSigOp && nSigOpsCost > nSize * WITNESS_SCALE_FACTOR / nBytesPerSigOp))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
|
||||
strprintf("%d", nSigOps));
|
||||
strprintf("%d", nSigOpsCost));
|
||||
|
||||
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
|
||||
if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
|
||||
@@ -2439,7 +2454,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
std::vector<int> prevheights;
|
||||
CAmount nFees = 0;
|
||||
int nInputs = 0;
|
||||
unsigned int nSigOps = 0;
|
||||
int64_t nSigOpsCost = 0;
|
||||
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
|
||||
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||
vPos.reserve(block.vtx.size());
|
||||
@@ -2449,10 +2464,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
const CTransaction &tx = block.vtx[i];
|
||||
|
||||
nInputs += tx.vin.size();
|
||||
nSigOps += GetLegacySigOpCount(tx);
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
@@ -2483,18 +2494,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__),
|
||||
REJECT_INVALID, "bad-txns-nonfinal");
|
||||
}
|
||||
}
|
||||
|
||||
if (fStrictPayToScriptHash)
|
||||
{
|
||||
// Add in sigops done by pay-to-script-hash inputs;
|
||||
// this is to prevent a "rogue miner" from creating
|
||||
// an incredibly-expensive-to-validate block.
|
||||
nSigOps += GetP2SHSigOpCount(tx, view);
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
}
|
||||
// GetTransactionSigOpCost counts 3 types of sigops:
|
||||
// * legacy (always)
|
||||
// * p2sh (when P2SH enabled in flags and excludes coinbase)
|
||||
// * witness (when witness enabled in flags and excludes coinbase)
|
||||
nSigOpsCost += GetTransactionSigOpCost(tx, view, flags);
|
||||
if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST)
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
nFees += view.GetValueIn(tx)-tx.GetValueOut();
|
||||
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
@@ -3417,9 +3429,11 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||
// All potential-corruption validation must be done before we do any
|
||||
// transaction validation, as otherwise we may mark the header as invalid
|
||||
// because we receive the wrong transactions for it.
|
||||
// Note that witness malleability is checked in ContextualCheckBlock, so no
|
||||
// checks that use witness data may be performed here.
|
||||
|
||||
// Size limits
|
||||
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_SIZE)
|
||||
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_BASE_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
|
||||
|
||||
// First transaction must be coinbase, the rest must not be
|
||||
@@ -3440,7 +3454,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||
{
|
||||
nSigOps += GetLegacySigOpCount(tx);
|
||||
}
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
|
||||
|
||||
if (fCheckPOW && fCheckMerkleRoot)
|
||||
@@ -3621,6 +3635,16 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
|
||||
}
|
||||
}
|
||||
|
||||
// After the coinbase witness nonce and commitment are verified,
|
||||
// we can check if the block cost passes (before we've checked the
|
||||
// coinbase witness, it would be possible for the cost to be too
|
||||
// large by filling up the coinbase witness, which doesn't change
|
||||
// the block hash, so we couldn't mark the block as permanently
|
||||
// failed).
|
||||
if (GetBlockCost(block) > MAX_BLOCK_COST) {
|
||||
return state.DoS(100, error("ContextualCheckBlock(): cost limit failed"), REJECT_INVALID, "bad-blk-cost");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4284,7 +4308,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
|
||||
int nLoaded = 0;
|
||||
try {
|
||||
// This takes over fileIn and calls fclose() on it in the CBufferedFile destructor
|
||||
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION);
|
||||
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE+8, SER_DISK, CLIENT_VERSION);
|
||||
uint64_t nRewind = blkdat.GetPos();
|
||||
while (!blkdat.eof()) {
|
||||
boost::this_thread::interruption_point();
|
||||
@@ -4303,7 +4327,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
|
||||
continue;
|
||||
// read size
|
||||
blkdat >> nSize;
|
||||
if (nSize < 80 || nSize > MAX_BLOCK_SIZE)
|
||||
if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE)
|
||||
continue;
|
||||
} catch (const std::exception&) {
|
||||
// no valid block header found; don't complain
|
||||
|
||||
Reference in New Issue
Block a user