upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-08-02 11:04:00 -0300
committerGitHub <noreply@github.com>2026-08-02 11:04:00 -0300
commitfe04a2045c3080797c6f93bf7cd2e12192b8f68f (patch)
tree4f38cde1114c45bc334d096e90fef94780a66733
parent89f6d045c8e3dacbfbb369a3ac89c0c33de22b4c (diff)
Add consensus block size limit (#111, from #110)
CheckBlock() validated block size against MAX_SIZE, the serializer's generic 32 MB guard rather than a block rule; MAX_BLOCK_SIZE did not exist in this tree at all, because Bitcoin's 1 MB cap arrived in 0.3.x, after the 0.1.0 this fork came from. A miner could produce a 32 MB block and every node would download, verify and store it. Original work by mtg0001 in #110, rebased over #109 with the self-test conflict resolved, plus a comment explaining why the inherited count-against-bytes test stays. Measured before choosing the number: 5709 blocks read from blk0001.dat give a largest block of 34,290 bytes and a median of 216, so 1 MB leaves 29x of headroom and rejects nothing that exists. A node built from this branch then synced genesis to 5706 on the live network with zero size-limit rejections and zero CheckBlock errors -- the same test that caught BIP30 rejecting a real block at height 859.
-rw-r--r--src/Makefile1
-rw-r--r--src/main.cpp2
-rw-r--r--src/main.h21
-rw-r--r--src/makefile.mingw1
-rw-r--r--src/selftest.cpp62
5 files changed, 85 insertions, 2 deletions
diff --git a/src/Makefile b/src/Makefile
index 88dc08f..82e7d06 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -140,3 +140,4 @@ tests: test_bip32 bitflash-node
140 ./bitflash-node -selftest=wallet-keypool 140 ./bitflash-node -selftest=wallet-keypool
141 ./bitflash-node -selftest=wallet-hd 141 ./bitflash-node -selftest=wallet-hd
142 ./bitflash-node -selftest=net-message 142 ./bitflash-node -selftest=net-message
143 ./bitflash-node -selftest=consensus-limits
diff --git a/src/main.cpp b/src/main.cpp
index 9bb3c24..1b09a1a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1719,7 +1719,7 @@ bool CBlock::CheckBlock() const
1719 // that can be verified before saving an orphan block. 1719 // that can be verified before saving an orphan block.
1720 1720
1721 // Size limits 1721 // Size limits
1722 if (vtx.empty() || vtx.size() > MAX_SIZE || ::GetSerializeSize(*this, SER_DISK) > MAX_SIZE) 1722 if (!CheckSizeLimits())
1723 return error("CheckBlock() : size limits failed"); 1723 return error("CheckBlock() : size limits failed");
1724 1724
1725 // Check timestamp 1725 // Check timestamp
diff --git a/src/main.h b/src/main.h
index 1b0be59..768f1c7 100644
--- a/src/main.h
+++ b/src/main.h
@@ -29,6 +29,10 @@ static const unsigned int MAX_ORPHAN_BLOCKS = 200;
29// so a block within the size limit could still hold millions of them and take 29// so a block within the size limit could still hold millions of them and take
30// the network minutes of CPU to reject. Same value Bitcoin settled on. 30// the network minutes of CPU to reject. Same value Bitcoin settled on.
31static const unsigned int MAX_BLOCK_SIGOPS = 20000; 31static const unsigned int MAX_BLOCK_SIGOPS = 20000;
32// Consensus block-size ceiling. MAX_SIZE is the serializer's generic safety
33// limit (32 MB), not a block rule. Bitcoin 0.1.0 predated the later 1 MB block
34// cap, so this tree inherited no real block-size consensus limit.
35static const unsigned int MAX_BLOCK_SIZE = 1000000;
32static const int64 COIN = 100000000; 36static const int64 COIN = 100000000;
33// Total Bitflash emission: identical to Bitcoin (21 million). 37// Total Bitflash emission: identical to Bitcoin (21 million).
34// MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in 38// MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in
@@ -1053,6 +1057,23 @@ public:
1053 return RandomXPoWHash((const void*)BEGIN(nVersion), END(nNonce) - BEGIN(nVersion)); 1057 return RandomXPoWHash((const void*)BEGIN(nVersion), END(nNonce) - BEGIN(nVersion));
1054 } 1058 }
1055 1059
1060 bool CheckSizeLimits() const
1061 {
1062 // The middle test compares a transaction *count* against a *byte*
1063 // limit. It is inherited from 0.1.0, where the same line compared
1064 // vtx.size() against MAX_SIZE, and it is inert: the smallest possible
1065 // transaction serializes to more than sixty bytes, so a block holding
1066 // over a million of them cannot also weigh under a megabyte.
1067 //
1068 // Kept rather than removed. This is consensus code, and a rewrite that
1069 // reads better is still a rewrite that can change what the network
1070 // accepts. Written down so the next reader does not have to derive it,
1071 // or "fix" it into something that does.
1072 return (!vtx.empty() &&
1073 vtx.size() <= MAX_BLOCK_SIZE &&
1074 ::GetSerializeSize(*this, SER_DISK) <= MAX_BLOCK_SIZE);
1075 }
1076
1056 1077
1057 uint256 BuildMerkleTree() const 1078 uint256 BuildMerkleTree() const
1058 { 1079 {
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 5025acb..9bbfef1 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -112,3 +112,4 @@ tests: test_bip32.exe bitflash.exe
112 ./bitflash.exe -selftest=wallet-keypool 112 ./bitflash.exe -selftest=wallet-keypool
113 ./bitflash.exe -selftest=wallet-hd 113 ./bitflash.exe -selftest=wallet-hd
114 ./bitflash.exe -selftest=net-message 114 ./bitflash.exe -selftest=net-message
115 ./bitflash.exe -selftest=consensus-limits
diff --git a/src/selftest.cpp b/src/selftest.cpp
index c04adf4..67f96f8 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -531,6 +531,64 @@ static int RunNetMessageSelfTest()
531 return nFail == 0 ? 0 : 1; 531 return nFail == 0 ? 0 : 1;
532} 532}
533 533
534static CBlock MakeSizedConsensusBlock(size_t nScriptBytes)
535{
536 CTransaction tx;
537 tx.vin.push_back(CTxIn(COutPoint(), CScript() << 1 << 1));
538 tx.vout.push_back(CTxOut(0, CScript()));
539 tx.vout[0].scriptPubKey.insert(tx.vout[0].scriptPubKey.end(), nScriptBytes, 0);
540
541 CBlock block;
542 block.vtx.push_back(tx);
543 block.hashMerkleRoot = block.BuildMerkleTree();
544 block.nVersion = 1;
545 block.nTime = GetAdjustedTime();
546 block.nBits = bnProofOfWorkLimit.GetCompact();
547 block.nNonce = 0;
548 return block;
549}
550
551static int RunConsensusLimitsSelfTest()
552{
553 fflush(stdout);
554 printf("consensus-limits self-test\n");
555
556 int nFail = 0;
557 try
558 {
559 nFail += Check(MAX_BLOCK_SIZE == 1000000,
560 "the consensus block-size cap is 1 MB") ? 0 : 1;
561 nFail += Check(MAX_BLOCK_SIZE < MAX_SIZE,
562 "the block cap is tighter than the serializer cap") ? 0 : 1;
563
564 CBlock small = MakeSizedConsensusBlock(100);
565 nFail += Check(small.CheckSizeLimits(),
566 "a small block is within the consensus size limit") ? 0 : 1;
567
568 CBlock oversized = MakeSizedConsensusBlock(MAX_BLOCK_SIZE);
569 unsigned int nSerialized = ::GetSerializeSize(oversized, SER_DISK);
570 nFail += Check(nSerialized > MAX_BLOCK_SIZE && nSerialized <= MAX_SIZE,
571 "the test block sits between 1 MB and the old 32 MB cap") ? 0 : 1;
572 nFail += Check(!oversized.CheckSizeLimits(),
573 "a block above 1 MB is outside the consensus size limit") ? 0 : 1;
574 }
575 catch (const std::exception& e)
576 {
577 printf(" FAIL exception: %s\n", e.what());
578 nFail++;
579 }
580 catch (...)
581 {
582 printf(" FAIL unknown exception\n");
583 nFail++;
584 }
585
586 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
587 nFail, nFail == 1 ? "" : "s");
588 fflush(stdout);
589 return nFail == 0 ? 0 : 1;
590}
591
534int RunSelfTest(const std::string& name) 592int RunSelfTest(const std::string& name)
535{ 593{
536 AttachTerminal(); 594 AttachTerminal();
@@ -541,8 +599,10 @@ int RunSelfTest(const std::string& name)
541 return RunWalletHDSelfTest(); 599 return RunWalletHDSelfTest();
542 if (name == "net-message") 600 if (name == "net-message")
543 return RunNetMessageSelfTest(); 601 return RunNetMessageSelfTest();
602 if (name == "consensus-limits")
603 return RunConsensusLimitsSelfTest();
544 604
545 printf("Unknown self-test '%s'\n", name.c_str()); 605 printf("Unknown self-test '%s'\n", name.c_str());
546 printf("Known self-tests: wallet-keypool, wallet-hd, net-message\n"); 606 printf("Known self-tests: wallet-keypool, wallet-hd, net-message, consensus-limits\n");
547 return 1; 607 return 1;
548} 608}