From da7447a63ca0a8877c21b7d996bfea5cabf15217 Mon Sep 17 00:00:00 2001 From: Gilcimar Duarte Date: Sun, 2 Aug 2026 10:40:21 -0300 Subject: [PATCH] Add consensus block size limit --- src/Makefile | 1 + src/main.cpp | 2 +- src/main.h | 11 ++++++++ src/makefile.mingw | 1 + src/selftest.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5ded7d6..50bffb6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -139,3 +139,4 @@ tests: test_bip32 bitflash-node ./test_bip32 ./bitflash-node -selftest=wallet-keypool ./bitflash-node -selftest=wallet-hd + ./bitflash-node -selftest=consensus-limits diff --git a/src/main.cpp b/src/main.cpp index 9082bd5..8b367a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1686,7 +1686,7 @@ bool CBlock::CheckBlock() const // that can be verified before saving an orphan block. // Size limits - if (vtx.empty() || vtx.size() > MAX_SIZE || ::GetSerializeSize(*this, SER_DISK) > MAX_SIZE) + if (!CheckSizeLimits()) return error("CheckBlock() : size limits failed"); // Check timestamp diff --git a/src/main.h b/src/main.h index 1b0be59..cf28c2e 100644 --- a/src/main.h +++ b/src/main.h @@ -29,6 +29,10 @@ static const unsigned int MAX_ORPHAN_BLOCKS = 200; // 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; +// Consensus block-size ceiling. MAX_SIZE is the serializer's generic safety +// limit (32 MB), not a block rule. Bitcoin 0.1.0 predated the later 1 MB block +// cap, so this tree inherited no real block-size consensus limit. +static const unsigned int MAX_BLOCK_SIZE = 1000000; 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 @@ -1053,6 +1057,13 @@ class CBlock return RandomXPoWHash((const void*)BEGIN(nVersion), END(nNonce) - BEGIN(nVersion)); } + bool CheckSizeLimits() const + { + return (!vtx.empty() && + vtx.size() <= MAX_BLOCK_SIZE && + ::GetSerializeSize(*this, SER_DISK) <= MAX_BLOCK_SIZE); + } + uint256 BuildMerkleTree() const { diff --git a/src/makefile.mingw b/src/makefile.mingw index 5be3704..045e65f 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -111,3 +111,4 @@ tests: test_bip32.exe bitflash.exe ./test_bip32.exe ./bitflash.exe -selftest=wallet-keypool ./bitflash.exe -selftest=wallet-hd + ./bitflash.exe -selftest=consensus-limits diff --git a/src/selftest.cpp b/src/selftest.cpp index 88e9863..dab4003 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp @@ -470,6 +470,64 @@ static int RunWalletHDSelfTest() 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(); @@ -478,8 +536,10 @@ int RunSelfTest(const std::string& name) return RunWalletKeyPoolSelfTest(); if (name == "wallet-hd") return RunWalletHDSelfTest(); + if (name == "consensus-limits") + return RunConsensusLimitsSelfTest(); printf("Unknown self-test '%s'\n", name.c_str()); - printf("Known self-tests: wallet-keypool, wallet-hd\n"); + printf("Known self-tests: wallet-keypool, wallet-hd, consensus-limits\n"); return 1; }