From fe04a2045c3080797c6f93bf7cd2e12192b8f68f Mon Sep 17 00:00:00 2001 From: Bitflash Date: Sun, 2 Aug 2026 11:04:00 -0300 Subject: 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. --- src/selftest.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'src/selftest.cpp') 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() return nFail == 0 ? 0 : 1; } +static CBlock MakeSizedConsensusBlock(size_t nScriptBytes) +{ + CTransaction tx; + tx.vin.push_back(CTxIn(COutPoint(), CScript() << 1 << 1)); + tx.vout.push_back(CTxOut(0, CScript())); + tx.vout[0].scriptPubKey.insert(tx.vout[0].scriptPubKey.end(), nScriptBytes, 0); + + CBlock block; + block.vtx.push_back(tx); + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = GetAdjustedTime(); + block.nBits = bnProofOfWorkLimit.GetCompact(); + block.nNonce = 0; + return block; +} + +static int RunConsensusLimitsSelfTest() +{ + fflush(stdout); + printf("consensus-limits self-test\n"); + + int nFail = 0; + try + { + nFail += Check(MAX_BLOCK_SIZE == 1000000, + "the consensus block-size cap is 1 MB") ? 0 : 1; + nFail += Check(MAX_BLOCK_SIZE < MAX_SIZE, + "the block cap is tighter than the serializer cap") ? 0 : 1; + + CBlock small = MakeSizedConsensusBlock(100); + nFail += Check(small.CheckSizeLimits(), + "a small block is within the consensus size limit") ? 0 : 1; + + CBlock oversized = MakeSizedConsensusBlock(MAX_BLOCK_SIZE); + unsigned int nSerialized = ::GetSerializeSize(oversized, SER_DISK); + nFail += Check(nSerialized > MAX_BLOCK_SIZE && nSerialized <= MAX_SIZE, + "the test block sits between 1 MB and the old 32 MB cap") ? 0 : 1; + nFail += Check(!oversized.CheckSizeLimits(), + "a block above 1 MB is outside the consensus size limit") ? 0 : 1; + } + catch (const std::exception& e) + { + printf(" FAIL exception: %s\n", e.what()); + nFail++; + } + catch (...) + { + printf(" FAIL unknown exception\n"); + nFail++; + } + + printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", + nFail, nFail == 1 ? "" : "s"); + fflush(stdout); + return nFail == 0 ? 0 : 1; +} + int RunSelfTest(const std::string& name) { AttachTerminal(); @@ -541,8 +599,10 @@ int RunSelfTest(const std::string& name) return RunWalletHDSelfTest(); if (name == "net-message") return RunNetMessageSelfTest(); + if (name == "consensus-limits") + return RunConsensusLimitsSelfTest(); printf("Unknown self-test '%s'\n", name.c_str()); - printf("Known self-tests: wallet-keypool, wallet-hd, net-message\n"); + printf("Known self-tests: wallet-keypool, wallet-hd, net-message, consensus-limits\n"); return 1; } -- cgit v1.2.3