diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-03 21:49:35 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-03 21:49:35 +0000 |
| commit | db44eac5d347c229539946e8d61f1ff0d1f68001 (patch) | |
| tree | 23db58c0d8454cbab023bf9d94d6a39584b889ab /src | |
| parent | cb8fb6d9d14ecf96b4a71ccc1a51b87bf8dd9bd5 (diff) | |
| parent | ff6af901eaab61b6dbba41bb80f7bb2f40980880 (diff) | |
merge: !11 fix/block-assembly-size into deploy/all-2026-08-24
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 54 | ||||
| -rw-r--r-- | src/main.h | 29 | ||||
| -rw-r--r-- | src/rpc.cpp | 41 | ||||
| -rw-r--r-- | src/selftest.cpp | 157 |
4 files changed, 267 insertions, 14 deletions
diff --git a/src/main.cpp b/src/main.cpp index 6b8e0de..018627d 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -4949,7 +4949,6 @@ bool BitcoinMiner(int nThreadId) | |||
| 4949 | map<uint256, CTxIndex> mapTestPool; | 4949 | map<uint256, CTxIndex> mapTestPool; |
| 4950 | vector<char> vfAlreadyAdded(mapTransactions.size()); | 4950 | vector<char> vfAlreadyAdded(mapTransactions.size()); |
| 4951 | bool fFoundSomething = true; | 4951 | bool fFoundSomething = true; |
| 4952 | unsigned int nBlockSize = 0; | ||
| 4953 | // Assembly must stop below the consensus cap that CheckBlock() | 4952 | // Assembly must stop below the consensus cap that CheckBlock() |
| 4954 | // enforces, or we mine a block our own node then rejects. | 4953 | // enforces, or we mine a block our own node then rejects. |
| 4955 | // | 4954 | // |
| @@ -4960,9 +4959,30 @@ bool BitcoinMiner(int nThreadId) | |||
| 4960 | // meant solving a block and throwing it away -- with this pool at | 4959 | // meant solving a block and throwing it away -- with this pool at |
| 4961 | // ~90% of hashrate, that is the network stalling, not just us. | 4960 | // ~90% of hashrate, that is the network stalling, not just us. |
| 4962 | // | 4961 | // |
| 4963 | // Half the cap, keeping the original margin: the test runs before | 4962 | // The budget test then sat only on this outer while, and the |
| 4964 | // a transaction is added, so the loop can overshoot by one tx. | 4963 | // comment claimed that bounded the overshoot to a single |
| 4965 | while (fFoundSomething && nBlockSize < MAX_BLOCK_SIZE/2) | 4964 | // transaction. It did not. The inner for walks all of |
| 4965 | // mapTransactions with no test of its own, so one pass could append | ||
| 4966 | // the entire mempool: the overshoot was bounded by mempool size, | ||
| 4967 | // which is to say not bounded at all. Ask per transaction, before | ||
| 4968 | // appending, and the overshoot is zero rather than small. | ||
| 4969 | // | ||
| 4970 | // Seeded with what the block already weighs -- header, tx count, | ||
| 4971 | // coinbase -- because CheckBlock() weighs the whole serialized | ||
| 4972 | // block and not just the transactions we chose to add to it. | ||
| 4973 | unsigned int nBlockSize = ::GetSerializeSize(*pblock, SER_DISK); | ||
| 4974 | // Sigops are a budget too, and CheckBlock() enforces one that no | ||
| 4975 | // assembler here counted against. Bytes do not bound validation | ||
| 4976 | // cost: every OP_CHECKSIG is an elliptic-curve verification, so a | ||
| 4977 | // template well inside the size limit can still be refused for | ||
| 4978 | // demanding more work than a block is allowed to. Counted with | ||
| 4979 | // CTransaction::GetSigOpCount, the function CheckBlock() itself | ||
| 4980 | // sums, so the two cannot disagree about what a transaction costs. | ||
| 4981 | // | ||
| 4982 | // Seeded from the coinbase because CheckBlock() sums over all of | ||
| 4983 | // vtx, coinbase included. | ||
| 4984 | unsigned int nBlockSigOps = pblock->vtx[0].GetSigOpCount(); | ||
| 4985 | while (fFoundSomething && nBlockSize < MAX_BLOCK_SIZE_GEN) | ||
| 4966 | { | 4986 | { |
| 4967 | fFoundSomething = false; | 4987 | fFoundSomething = false; |
| 4968 | unsigned int n = 0; | 4988 | unsigned int n = 0; |
| @@ -4974,6 +4994,29 @@ bool BitcoinMiner(int nThreadId) | |||
| 4974 | if (tx.IsCoinBase() || !tx.IsFinal()) | 4994 | if (tx.IsCoinBase() || !tx.IsFinal()) |
| 4975 | continue; | 4995 | continue; |
| 4976 | 4996 | ||
| 4997 | // Skip, not break: a transaction too big for the room left | ||
| 4998 | // says nothing about the next one, which may still fit. | ||
| 4999 | // | ||
| 5000 | // This is also the only ceiling on a single transaction's | ||
| 5001 | // size anywhere on the path into a block. CheckTransaction | ||
| 5002 | // does not cap it and neither does AcceptTransaction, so | ||
| 5003 | // the mempool will hold one of any size the wire allows; | ||
| 5004 | // without this test a lone 1.5 MB transaction offering a | ||
| 5005 | // fat fee is selected, and every template built from that | ||
| 5006 | // mempool is one CheckBlock() throws away. The fee is never | ||
| 5007 | // paid, because the block never confirms. | ||
| 5008 | // | ||
| 5009 | // Deliberately before ConnectInputs and not after: | ||
| 5010 | // ConnectInputs adds this transaction's fee to nFees by | ||
| 5011 | // reference on success. Passing it and then declining the | ||
| 5012 | // transaction would credit the coinbase for a fee no | ||
| 5013 | // transaction in the block pays, and the block would be | ||
| 5014 | // rejected for a wholly different reason. | ||
| 5015 | unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); | ||
| 5016 | unsigned int nTxSigOps = tx.GetSigOpCount(); | ||
| 5017 | if (!BlockTemplateFits(nBlockSize, nBlockSigOps, nTxSize, nTxSigOps)) | ||
| 5018 | continue; | ||
| 5019 | |||
| 4977 | // Transaction fee requirements, mainly only needed for flood control | 5020 | // Transaction fee requirements, mainly only needed for flood control |
| 4978 | // Under 10K (about 80 inputs) is free for first 100 transactions | 5021 | // Under 10K (about 80 inputs) is free for first 100 transactions |
| 4979 | // Base rate is 0.01 per KB | 5022 | // Base rate is 0.01 per KB |
| @@ -4985,7 +5028,8 @@ bool BitcoinMiner(int nThreadId) | |||
| 4985 | swap(mapTestPool, mapTestPoolTmp); | 5028 | swap(mapTestPool, mapTestPoolTmp); |
| 4986 | 5029 | ||
| 4987 | pblock->vtx.push_back(tx); | 5030 | pblock->vtx.push_back(tx); |
| 4988 | nBlockSize += ::GetSerializeSize(tx, SER_NETWORK); | 5031 | nBlockSize += nTxSize; |
| 5032 | nBlockSigOps += nTxSigOps; | ||
| 4989 | vfAlreadyAdded[n] = true; | 5033 | vfAlreadyAdded[n] = true; |
| 4990 | fFoundSomething = true; | 5034 | fFoundSomething = true; |
| 4991 | } | 5035 | } |
| @@ -33,6 +33,35 @@ static const unsigned int MAX_BLOCK_SIGOPS = 20000; | |||
| 33 | // limit (32 MB), not a block rule. Bitcoin 0.1.0 predated the later 1 MB block | 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. | 34 | // cap, so this tree inherited no real block-size consensus limit. |
| 35 | static const unsigned int MAX_BLOCK_SIZE = 1000000; | 35 | static const unsigned int MAX_BLOCK_SIZE = 1000000; |
| 36 | // How much of that cap a template we build may fill. Half, which is what both | ||
| 37 | // assemblers already aimed at -- this only gives the number a name and one | ||
| 38 | // place to live. They had already drifted on how it was enforced, which is the | ||
| 39 | // bug this exists to stop repeating. | ||
| 40 | // | ||
| 41 | // NOT a consensus rule. CheckBlock() decides what the network accepts; this | ||
| 42 | // decides what we are willing to produce, and it has to stay under that. | ||
| 43 | static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE / 2; | ||
| 44 | |||
| 45 | // Room for one more transaction in a template that currently weighs | ||
| 46 | // nBlockSize bytes and spends nBlockSigOps signature operations? | ||
| 47 | // | ||
| 48 | // Asked BEFORE the transaction is appended, never after. A template that has | ||
| 49 | // already overshot is one CheckBlock() rejects, and asking afterwards only | ||
| 50 | // measures the waste. Both assemblers call this, so neither can drift into a | ||
| 51 | // different idea of what fits. | ||
| 52 | // | ||
| 53 | // Size gets a margin (half the cap); sigops do not. There is no cheap way to | ||
| 54 | // spend the sigop budget usefully and no reason to reserve headroom in it, so | ||
| 55 | // assembly holds itself to the same number CheckBlock() enforces -- and to the | ||
| 56 | // same comparison. CheckBlock() fails on `> MAX_BLOCK_SIGOPS`, so a block | ||
| 57 | // sitting exactly on the limit is valid and this has to accept it too. | ||
| 58 | // Assembly and validation disagreeing by one is how the size bug started. | ||
| 59 | inline bool BlockTemplateFits(unsigned int nBlockSize, unsigned int nBlockSigOps, | ||
| 60 | unsigned int nTxSize, unsigned int nTxSigOps) | ||
| 61 | { | ||
| 62 | return (nBlockSize + nTxSize <= MAX_BLOCK_SIZE_GEN && | ||
| 63 | nBlockSigOps + nTxSigOps <= MAX_BLOCK_SIGOPS); | ||
| 64 | } | ||
| 36 | static const int64 COIN = 100000000; | 65 | static const int64 COIN = 100000000; |
| 37 | // Total Bitflash emission: identical to Bitcoin (21 million). | 66 | // Total Bitflash emission: identical to Bitcoin (21 million). |
| 38 | // MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in | 67 | // MAX_MONEY guards against the value overflow bug (CVE-2010-5139), which in |
diff --git a/src/rpc.cpp b/src/rpc.cpp index 1d53fb2..3827cd5 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -303,16 +303,29 @@ static bool RebuildJob() | |||
| 303 | map<uint256, CTxIndex> pool; | 303 | map<uint256, CTxIndex> pool; |
| 304 | vector<char> vfAlreadyAdded(mapTransactions.size()); | 304 | vector<char> vfAlreadyAdded(mapTransactions.size()); |
| 305 | bool fFoundSomething = true; | 305 | bool fFoundSomething = true; |
| 306 | unsigned int sz = 0; | 306 | // The cap is MAX_BLOCK_SIZE_GEN, not MAX_SIZE/2. MAX_SIZE is |
| 307 | // The cap is MAX_BLOCK_SIZE/2, not MAX_SIZE/2. MAX_SIZE is the | 307 | // the 32 MB serialisation ceiling, so half of it is 16 MB -- |
| 308 | // 32 MB serialisation ceiling, so half of it is 16 MB -- this | 308 | // this builder would happily assemble a template that its own |
| 309 | // builder would happily assemble a template that its own | ||
| 310 | // CheckBlock() rejects at the 1 MB consensus cap. This is the | 309 | // CheckBlock() rejects at the 1 MB consensus cap. This is the |
| 311 | // pool's job builder, so a block over the cap is not one | 310 | // pool's job builder, so a block over the cap is not one |
| 312 | // wasted solve, it is every miner on the pool working a | 311 | // wasted solve, it is every miner on the pool working a |
| 313 | // template the network will reject. The inner break bounds the | 312 | // template the network will reject. |
| 314 | // overshoot to a single transaction within a pass. | 313 | // |
| 315 | while (fFoundSomething && sz < MAX_BLOCK_SIZE / 2) | 314 | // The inner break used to bound the overshoot to one |
| 315 | // transaction per pass. One transaction is still too many: | ||
| 316 | // nothing caps how big a single transaction may be, so that | ||
| 317 | // bound was worth up to MAX_SIZE. Asking whether each one fits | ||
| 318 | // before appending it bounds the overshoot at zero instead. | ||
| 319 | // | ||
| 320 | // Seeded with the header, tx count and coinbase, because | ||
| 321 | // CheckBlock() weighs the whole serialized block. | ||
| 322 | unsigned int sz = ::GetSerializeSize(block, SER_DISK); | ||
| 323 | // And the sigop budget, which CheckBlock() enforces and this | ||
| 324 | // builder never counted against. Same counting function | ||
| 325 | // CheckBlock() sums, seeded from the coinbase because it sums | ||
| 326 | // over all of vtx. | ||
| 327 | unsigned int nSigOps = block.vtx[0].GetSigOpCount(); | ||
| 328 | while (fFoundSomething && sz < MAX_BLOCK_SIZE_GEN) | ||
| 316 | { | 329 | { |
| 317 | fFoundSomething = false; | 330 | fFoundSomething = false; |
| 318 | unsigned int n = 0; | 331 | unsigned int n = 0; |
| @@ -323,16 +336,26 @@ static bool RebuildJob() | |||
| 323 | continue; | 336 | continue; |
| 324 | CTransaction& tx = (*mi).second; | 337 | CTransaction& tx = (*mi).second; |
| 325 | if (tx.IsCoinBase() || !tx.IsFinal()) continue; | 338 | if (tx.IsCoinBase() || !tx.IsFinal()) continue; |
| 339 | // Skip rather than stop: one transaction too big for | ||
| 340 | // the room left does not mean the next is. Ordered | ||
| 341 | // before ConnectInputs because ConnectInputs credits | ||
| 342 | // nFees by reference on success, and a transaction we | ||
| 343 | // then decline would leave the coinbase claiming a fee | ||
| 344 | // this block does not collect. | ||
| 345 | unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); | ||
| 346 | unsigned int nTxSigOps = tx.GetSigOpCount(); | ||
| 347 | if (!BlockTemplateFits(sz, nSigOps, nTxSize, nTxSigOps)) | ||
| 348 | continue; | ||
| 326 | map<uint256, CTxIndex> tmp(pool); | 349 | map<uint256, CTxIndex> tmp(pool); |
| 327 | if (!tx.ConnectInputs(txdb, tmp, CDiskTxPos(1,1,1), 0, nFees, | 350 | if (!tx.ConnectInputs(txdb, tmp, CDiskTxPos(1,1,1), 0, nFees, |
| 328 | false, true, tx.GetMinFee(block.vtx.size() < 100))) | 351 | false, true, tx.GetMinFee(block.vtx.size() < 100))) |
| 329 | continue; | 352 | continue; |
| 330 | swap(pool, tmp); | 353 | swap(pool, tmp); |
| 331 | block.vtx.push_back(tx); | 354 | block.vtx.push_back(tx); |
| 332 | sz += ::GetSerializeSize(tx, SER_NETWORK); | 355 | sz += nTxSize; |
| 356 | nSigOps += nTxSigOps; | ||
| 333 | vfAlreadyAdded[n] = true; | 357 | vfAlreadyAdded[n] = true; |
| 334 | fFoundSomething = true; | 358 | fFoundSomething = true; |
| 335 | if (sz > MAX_BLOCK_SIZE / 2) break; | ||
| 336 | } | 359 | } |
| 337 | } | 360 | } |
| 338 | } | 361 | } |
diff --git a/src/selftest.cpp b/src/selftest.cpp index 9f7106f..d11fdd8 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1235,6 +1235,74 @@ static CBlock MakeSizedConsensusBlock(size_t nScriptBytes) | |||
| 1235 | return block; | 1235 | return block; |
| 1236 | } | 1236 | } |
| 1237 | 1237 | ||
| 1238 | // A distinct transaction of a chosen weight and verification cost. nUnique | ||
| 1239 | // only keeps the txids apart, so a mempool built from these is not a pile of | ||
| 1240 | // duplicates a block could never carry anyway. | ||
| 1241 | static CTransaction MakeSizedTx(size_t nPadBytes, unsigned int nSigOps, int nUnique) | ||
| 1242 | { | ||
| 1243 | CTransaction tx; | ||
| 1244 | tx.vin.push_back(CTxIn(COutPoint(), CScript() << (int64)nUnique << 1)); | ||
| 1245 | tx.vout.push_back(CTxOut(0, CScript())); | ||
| 1246 | for (unsigned int i = 0; i < nSigOps; i++) | ||
| 1247 | tx.vout[0].scriptPubKey << OP_CHECKSIG; | ||
| 1248 | tx.vout[0].scriptPubKey.insert(tx.vout[0].scriptPubKey.end(), nPadBytes, 0); | ||
| 1249 | return tx; | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | static CBlock MakeCoinbaseOnlyTemplate() | ||
| 1253 | { | ||
| 1254 | CTransaction txNew; | ||
| 1255 | txNew.vin.resize(1); | ||
| 1256 | txNew.vin[0].prevout.SetNull(); | ||
| 1257 | txNew.vin[0].scriptSig << 1 << 1 << 1; | ||
| 1258 | txNew.vout.resize(1); | ||
| 1259 | txNew.vout[0].scriptPubKey << OP_CHECKSIG; | ||
| 1260 | |||
| 1261 | CBlock block; | ||
| 1262 | block.vtx.push_back(txNew); | ||
| 1263 | block.nVersion = 1; | ||
| 1264 | block.nTime = GetAdjustedTime(); | ||
| 1265 | block.nBits = bnProofOfWorkLimit.GetCompact(); | ||
| 1266 | block.nNonce = 0; | ||
| 1267 | return block; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | // The sum CheckBlock() takes, spelled the same way. | ||
| 1271 | static unsigned int BlockSigOpCount(const CBlock& block) | ||
| 1272 | { | ||
| 1273 | unsigned int n = 0; | ||
| 1274 | for (int i = 0; i < (int)block.vtx.size(); i++) | ||
| 1275 | n += block.vtx[i].GetSigOpCount(); | ||
| 1276 | return n; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | // Drive the selection rule the way both assemblers drive it: ask | ||
| 1280 | // BlockTemplateFits before appending, skip what does not fit, never stop early | ||
| 1281 | // on one that does not. | ||
| 1282 | // | ||
| 1283 | // This mirrors the loops in BitcoinMiner() and RebuildJob() rather than calling | ||
| 1284 | // them -- one needs a CTxDB, a wallet key and cs_main, the other is static in | ||
| 1285 | // rpc.cpp, so neither is reachable from here. What that leaves proven is the | ||
| 1286 | // rule the two of them share and the property it is meant to guarantee, which | ||
| 1287 | // is where the defect actually lived: the budget was right and the enforcement | ||
| 1288 | // was not. | ||
| 1289 | static void SelectIntoTemplate(CBlock& block, const vector<CTransaction>& vMempool) | ||
| 1290 | { | ||
| 1291 | unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK); | ||
| 1292 | unsigned int nBlockSigOps = block.vtx[0].GetSigOpCount(); | ||
| 1293 | for (int i = 0; i < (int)vMempool.size(); i++) | ||
| 1294 | { | ||
| 1295 | const CTransaction& tx = vMempool[i]; | ||
| 1296 | unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); | ||
| 1297 | unsigned int nTxSigOps = tx.GetSigOpCount(); | ||
| 1298 | if (!BlockTemplateFits(nBlockSize, nBlockSigOps, nTxSize, nTxSigOps)) | ||
| 1299 | continue; | ||
| 1300 | block.vtx.push_back(tx); | ||
| 1301 | nBlockSize += nTxSize; | ||
| 1302 | nBlockSigOps += nTxSigOps; | ||
| 1303 | } | ||
| 1304 | } | ||
| 1305 | |||
| 1238 | static int RunConsensusLimitsSelfTest() | 1306 | static int RunConsensusLimitsSelfTest() |
| 1239 | { | 1307 | { |
| 1240 | fflush(stdout); | 1308 | fflush(stdout); |
| @@ -1258,6 +1326,95 @@ static int RunConsensusLimitsSelfTest() | |||
| 1258 | "the test block sits between 1 MB and the old 32 MB cap") ? 0 : 1; | 1326 | "the test block sits between 1 MB and the old 32 MB cap") ? 0 : 1; |
| 1259 | nFail += Check(!oversized.CheckSizeLimits(), | 1327 | nFail += Check(!oversized.CheckSizeLimits(), |
| 1260 | "a block above 1 MB is outside the consensus size limit") ? 0 : 1; | 1328 | "a block above 1 MB is outside the consensus size limit") ? 0 : 1; |
| 1329 | |||
| 1330 | // The other half of the cap. Everything above asks what validation | ||
| 1331 | // refuses; nothing asked what assembly produces, which is why a miner | ||
| 1332 | // that could overshoot the cap by an entire mempool went unnoticed. | ||
| 1333 | nFail += Check(MAX_BLOCK_SIZE_GEN < MAX_BLOCK_SIZE, | ||
| 1334 | "the assembly budget stays under the consensus cap") ? 0 : 1; | ||
| 1335 | |||
| 1336 | // The rule itself, at its edges. The sigop comparison has to match | ||
| 1337 | // CheckBlock(), which fails on `> MAX_BLOCK_SIGOPS` and so accepts a | ||
| 1338 | // block sitting exactly on the limit. | ||
| 1339 | nFail += Check(BlockTemplateFits(MAX_BLOCK_SIZE_GEN - 100, 0, 100, 0), | ||
| 1340 | "a transaction that exactly fills the size budget fits") ? 0 : 1; | ||
| 1341 | nFail += Check(!BlockTemplateFits(MAX_BLOCK_SIZE_GEN - 100, 0, 101, 0), | ||
| 1342 | "one byte past the size budget does not fit") ? 0 : 1; | ||
| 1343 | nFail += Check(BlockTemplateFits(0, 0, 0, MAX_BLOCK_SIGOPS), | ||
| 1344 | "sigops exactly on the limit fit, as CheckBlock() allows") ? 0 : 1; | ||
| 1345 | nFail += Check(!BlockTemplateFits(0, 0, 0, MAX_BLOCK_SIGOPS + 1), | ||
| 1346 | "one sigop past the limit does not fit") ? 0 : 1; | ||
| 1347 | |||
| 1348 | // A mempool several times the consensus cap, in ordinary-sized | ||
| 1349 | // transactions. The old miner tested its budget only on the outer | ||
| 1350 | // loop, so a single pass appended all of this. | ||
| 1351 | { | ||
| 1352 | CBlock block = MakeCoinbaseOnlyTemplate(); | ||
| 1353 | vector<CTransaction> vMempool; | ||
| 1354 | for (int i = 0; i < 300; i++) | ||
| 1355 | vMempool.push_back(MakeSizedTx(10000, 1, i)); | ||
| 1356 | SelectIntoTemplate(block, vMempool); | ||
| 1357 | block.hashMerkleRoot = block.BuildMerkleTree(); | ||
| 1358 | |||
| 1359 | nFail += Check(block.vtx.size() > 1, | ||
| 1360 | "a full mempool still fills a template") ? 0 : 1; | ||
| 1361 | nFail += Check(::GetSerializeSize(block, SER_DISK) <= MAX_BLOCK_SIZE_GEN, | ||
| 1362 | "a template built from a 3 MB mempool respects the budget") ? 0 : 1; | ||
| 1363 | nFail += Check(block.CheckSizeLimits(), | ||
| 1364 | "that template is one CheckBlock() would accept on size") ? 0 : 1; | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | // One transaction too big for the budget. Nothing on the path into the | ||
| 1368 | // mempool caps transaction size, so this is a transaction a peer can | ||
| 1369 | // simply send. It must be skipped and the rest still taken -- skipped, | ||
| 1370 | // not stopped on. | ||
| 1371 | { | ||
| 1372 | CBlock block = MakeCoinbaseOnlyTemplate(); | ||
| 1373 | vector<CTransaction> vMempool; | ||
| 1374 | vMempool.push_back(MakeSizedTx(1500000, 1, 9999)); | ||
| 1375 | for (int i = 0; i < 10; i++) | ||
| 1376 | vMempool.push_back(MakeSizedTx(1000, 1, i)); | ||
| 1377 | |||
| 1378 | unsigned int nHugeSize = ::GetSerializeSize(vMempool[0], SER_NETWORK); | ||
| 1379 | nFail += Check(nHugeSize > MAX_BLOCK_SIZE && nHugeSize <= MAX_SIZE, | ||
| 1380 | "the outsized transaction is one the mempool would hold") ? 0 : 1; | ||
| 1381 | |||
| 1382 | SelectIntoTemplate(block, vMempool); | ||
| 1383 | block.hashMerkleRoot = block.BuildMerkleTree(); | ||
| 1384 | |||
| 1385 | bool fTookHuge = false; | ||
| 1386 | for (int i = 0; i < (int)block.vtx.size(); i++) | ||
| 1387 | if (block.vtx[i].GetHash() == vMempool[0].GetHash()) | ||
| 1388 | fTookHuge = true; | ||
| 1389 | |||
| 1390 | nFail += Check(!fTookHuge, | ||
| 1391 | "a transaction larger than the budget is left out") ? 0 : 1; | ||
| 1392 | nFail += Check(block.vtx.size() == 11, | ||
| 1393 | "the ten that do fit are taken anyway") ? 0 : 1; | ||
| 1394 | nFail += Check(block.CheckSizeLimits(), | ||
| 1395 | "one outsized transaction cannot invalidate a template") ? 0 : 1; | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | // Transactions that are cheap in bytes and expensive to verify. Size | ||
| 1399 | // alone does not bound what a block costs, and CheckBlock() refuses on | ||
| 1400 | // the count regardless of how small the block is. | ||
| 1401 | { | ||
| 1402 | CBlock block = MakeCoinbaseOnlyTemplate(); | ||
| 1403 | vector<CTransaction> vMempool; | ||
| 1404 | for (int i = 0; i < 100; i++) | ||
| 1405 | vMempool.push_back(MakeSizedTx(0, 1000, i)); | ||
| 1406 | SelectIntoTemplate(block, vMempool); | ||
| 1407 | block.hashMerkleRoot = block.BuildMerkleTree(); | ||
| 1408 | |||
| 1409 | nFail += Check(block.vtx.size() > 1, | ||
| 1410 | "sigop-heavy transactions are still selected") ? 0 : 1; | ||
| 1411 | nFail += Check(BlockSigOpCount(block) <= MAX_BLOCK_SIGOPS, | ||
| 1412 | "selection stops at the sigop cap CheckBlock() enforces") ? 0 : 1; | ||
| 1413 | nFail += Check(::GetSerializeSize(block, SER_DISK) < MAX_BLOCK_SIZE_GEN, | ||
| 1414 | "and stops on sigops while still well inside the size budget") ? 0 : 1; | ||
| 1415 | nFail += Check(block.CheckSizeLimits(), | ||
| 1416 | "the sigop-bounded template is within the size limit too") ? 0 : 1; | ||
| 1417 | } | ||
| 1261 | } | 1418 | } |
| 1262 | catch (const std::exception& e) | 1419 | catch (const std::exception& e) |
| 1263 | { | 1420 | { |