upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-28 15:27:57 -0300
committerGitHub <noreply@github.com>2026-07-28 15:27:57 -0300
commit389e9966758125f4e20d059a02396e1577b6367c (patch)
tree6a32feac1c5549eeab1e4b3557f50929e7244d6c /src
parent63764708903cf93476b0fa43580e23ff75ffceba (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp10
-rw-r--r--src/main.h15
-rw-r--r--src/script.h23
3 files changed, 48 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 17e446e..a75bf54 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1261,6 +1261,16 @@ bool CBlock::CheckBlock() const
1261 if (!tx.CheckTransaction()) 1261 if (!tx.CheckTransaction())
1262 return error("CheckBlock() : CheckTransaction failed"); 1262 return error("CheckBlock() : CheckTransaction failed");
1263 1263
1264 // Cap the work a block can demand. MAX_SIZE bounds the bytes, not the cost:
1265 // every signature operation is an elliptic-curve verification, and at this
1266 // chain's difficulty a block packed with them is affordable to mine and
1267 // expensive for everyone else to reject.
1268 unsigned int nSigOps = 0;
1269 foreach(const CTransaction& tx, vtx)
1270 nSigOps += tx.GetSigOpCount();
1271 if (nSigOps > MAX_BLOCK_SIGOPS)
1272 return error("CheckBlock() : out-of-bounds signature operation count");
1273
1264 // Reject blocks carrying the same transaction twice (CVE-2012-2459). 1274 // Reject blocks carrying the same transaction twice (CVE-2012-2459).
1265 // 1275 //
1266 // BuildMerkleTree duplicates the last hash when a level has an odd width, 1276 // BuildMerkleTree duplicates the last hash when a level has an odd width,
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;
18// Ceiling on transactions held waiting for a parent that has not arrived. 18// Ceiling on transactions held waiting for a parent that has not arrived.
19// They cost a peer nothing to send and are never validated, only stored. 19// They cost a peer nothing to send and are never validated, only stored.
20static const unsigned int MAX_ORPHAN_TRANSACTIONS = 100; 20static const unsigned int MAX_ORPHAN_TRANSACTIONS = 100;
21// Signature operations allowed in one block. Size alone does not bound what a
22// block costs to validate: every OP_CHECKSIG is an elliptic-curve verification,
23// so a block within the size limit could still hold millions of them and take
24// the network minutes of CPU to reject. Same value Bitcoin settled on.
25static const unsigned int MAX_BLOCK_SIGOPS = 20000;
21static const int64 COIN = 100000000; 26static const int64 COIN = 100000000;
22// Total Bitflash emission: identical to Bitcoin (21 million). 27// Total Bitflash emission: identical to Bitcoin (21 million).
23// MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in 28// MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in
@@ -503,6 +508,16 @@ public:
503 return (vin.size() == 1 && vin[0].prevout.IsNull()); 508 return (vin.size() == 1 && vin[0].prevout.IsNull());
504 } 509 }
505 510
511 unsigned int GetSigOpCount() const
512 {
513 unsigned int n = 0;
514 foreach(const CTxIn& txin, vin)
515 n += txin.scriptSig.GetSigOpCount();
516 foreach(const CTxOut& txout, vout)
517 n += txout.scriptPubKey.GetSigOpCount();
518 return n;
519 }
520
506 bool CheckTransaction() const 521 bool CheckTransaction() const
507 { 522 {
508 // Basic checks that don't depend on any context 523 // Basic checks that don't depend on any context
diff --git a/src/script.h b/src/script.h
index 27c1b13..26bb65a 100644
--- a/src/script.h
+++ b/src/script.h
@@ -536,6 +536,29 @@ public:
536 } 536 }
537 537
538 538
539 // Signature operations in this script. Each one costs every validating
540 // node an elliptic-curve verification, so blocks are capped on this count
541 // and not only on their size -- a byte of OP_CHECKSIG is far more
542 // expensive to check than a byte of anything else.
543 unsigned int GetSigOpCount() const
544 {
545 unsigned int n = 0;
546 const_iterator pc = begin();
547 opcodetype opcode;
548 vector<unsigned char> vchPushValue;
549 while (pc < end())
550 {
551 if (!GetOp(pc, opcode, vchPushValue))
552 break;
553 if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY)
554 n++;
555 else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY)
556 n += 20;
557 }
558 return n;
559 }
560
561
539 void FindAndDelete(const CScript& b) 562 void FindAndDelete(const CScript& b)
540 { 563 {
541 iterator pc = begin(); 564 iterator pc = begin();