From 389e9966758125f4e20d059a02396e1577b6367c Mon Sep 17 00:00:00 2001 From: Bitflash Date: Tue, 28 Jul 2026 15:27:57 -0300 Subject: main: cap signature operations per block (#20) MAX_SIZE bounds a block's bytes, not what it costs to check. Every OP_CHECKSIG is an elliptic-curve verification, and blocks may be 32 MB here, so one packed with signature operations takes every node minutes of CPU to reject -- while at this chain's difficulty it stays cheap to mine. There was no counter at all. CheckBlock now sums signature operations across the block and rejects past 20000, the figure Bitcoin settled on. OP_CHECKMULTISIG counts as 20 since it may verify that many. Consensus change. Real blocks here carry one or two operations each -- coinbase pay-to-pubkey and standard pay-to-pubkey-hash spends -- so the ceiling is four orders of magnitude above anything in the chain, but it still needs verifying against the full history before merge. --- src/main.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/main.h') diff --git a/src/main.h b/src/main.h index 40f8d45..fde3a4e 100644 --- a/src/main.h +++ b/src/main.h @@ -18,6 +18,11 @@ static const unsigned int MAX_SIZE = 0x02000000; // Ceiling on transactions held waiting for a parent that has not arrived. // They cost a peer nothing to send and are never validated, only stored. static const unsigned int MAX_ORPHAN_TRANSACTIONS = 100; +// Signature operations allowed in one block. Size alone does not bound what a +// block costs to validate: every OP_CHECKSIG is an elliptic-curve verification, +// so a block within the size limit could still hold millions of them and take +// the network minutes of CPU to reject. Same value Bitcoin settled on. +static const unsigned int MAX_BLOCK_SIGOPS = 20000; static const int64 COIN = 100000000; // Total Bitflash emission: identical to Bitcoin (21 million). // MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in @@ -503,6 +508,16 @@ public: return (vin.size() == 1 && vin[0].prevout.IsNull()); } + unsigned int GetSigOpCount() const + { + unsigned int n = 0; + foreach(const CTxIn& txin, vin) + n += txin.scriptSig.GetSigOpCount(); + foreach(const CTxOut& txout, vout) + n += txout.scriptPubKey.GetSigOpCount(); + return n; + } + bool CheckTransaction() const { // Basic checks that don't depend on any context -- cgit v1.2.3