upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-02 14:14:48 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-02 14:14:48 +0000
commit74c068829967bbff60da3506613844df4da1ae80 (patch)
tree358400d4a170a5777138bd44d048a4cd7f4aaf6b /src/main.cpp
parente4f898c4a6b9900553c64b9804889b5fdae3e55f (diff)
mine: ask whether a transaction fits before appending it
The node miner tested its size budget only on the outer while loop. The inner for walked all of mapTransactions with no test, so a single pass could append the entire mempool -- the overshoot was bounded by mempool size, not by one transaction as the comment claimed. The pool builder did test, but only after the push, which bounds the overshoot at one transaction. Nothing caps how big one transaction may be: neither CheckTransaction nor AcceptTransaction limits it, so the mempool will hold one of any size the wire allows and "one transaction" is worth up to MAX_SIZE. A lone 1.5 MB transaction offering a large fee is therefore enough to make every template unmineable, and the fee is never paid because the block never confirms. Both now ask before appending and skip what does not fit, so the overshoot is zero and an oversized transaction costs the template nothing rather than invalidating it. The budget moves to MAX_BLOCK_SIZE_GEN so the two assemblers cannot drift on it again -- drift is what produced this. The test is ordered before ConnectInputs on purpose: ConnectInputs credits nFees by reference on success, so declining a transaction after calling it would leave the coinbase claiming a fee the block does not collect. Assembly only. CheckBlock, CheckTransaction and AcceptTransaction are untouched: this changes what we produce, not what the network accepts.
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp41
1 files changed, 36 insertions, 5 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 7b3e555..64eb290 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4470,7 +4470,6 @@ bool BitcoinMiner(int nThreadId)
4470 map<uint256, CTxIndex> mapTestPool; 4470 map<uint256, CTxIndex> mapTestPool;
4471 vector<char> vfAlreadyAdded(mapTransactions.size()); 4471 vector<char> vfAlreadyAdded(mapTransactions.size());
4472 bool fFoundSomething = true; 4472 bool fFoundSomething = true;
4473 unsigned int nBlockSize = 0;
4474 // Assembly must stop below the consensus cap that CheckBlock() 4473 // Assembly must stop below the consensus cap that CheckBlock()
4475 // enforces, or we mine a block our own node then rejects. 4474 // enforces, or we mine a block our own node then rejects.
4476 // 4475 //
@@ -4481,9 +4480,19 @@ bool BitcoinMiner(int nThreadId)
4481 // meant solving a block and throwing it away -- with this pool at 4480 // meant solving a block and throwing it away -- with this pool at
4482 // ~90% of hashrate, that is the network stalling, not just us. 4481 // ~90% of hashrate, that is the network stalling, not just us.
4483 // 4482 //
4484 // Half the cap, keeping the original margin: the test runs before 4483 // The budget test then sat only on this outer while, and the
4485 // a transaction is added, so the loop can overshoot by one tx. 4484 // comment claimed that bounded the overshoot to a single
4486 while (fFoundSomething && nBlockSize < MAX_BLOCK_SIZE/2) 4485 // transaction. It did not. The inner for walks all of
4486 // mapTransactions with no test of its own, so one pass could append
4487 // the entire mempool: the overshoot was bounded by mempool size,
4488 // which is to say not bounded at all. Ask per transaction, before
4489 // appending, and the overshoot is zero rather than small.
4490 //
4491 // Seeded with what the block already weighs -- header, tx count,
4492 // coinbase -- because CheckBlock() weighs the whole serialized
4493 // block and not just the transactions we chose to add to it.
4494 unsigned int nBlockSize = ::GetSerializeSize(*pblock, SER_DISK);
4495 while (fFoundSomething && nBlockSize < MAX_BLOCK_SIZE_GEN)
4487 { 4496 {
4488 fFoundSomething = false; 4497 fFoundSomething = false;
4489 unsigned int n = 0; 4498 unsigned int n = 0;
@@ -4495,6 +4504,28 @@ bool BitcoinMiner(int nThreadId)
4495 if (tx.IsCoinBase() || !tx.IsFinal()) 4504 if (tx.IsCoinBase() || !tx.IsFinal())
4496 continue; 4505 continue;
4497 4506
4507 // Skip, not break: a transaction too big for the room left
4508 // says nothing about the next one, which may still fit.
4509 //
4510 // This is also the only ceiling on a single transaction's
4511 // size anywhere on the path into a block. CheckTransaction
4512 // does not cap it and neither does AcceptTransaction, so
4513 // the mempool will hold one of any size the wire allows;
4514 // without this test a lone 1.5 MB transaction offering a
4515 // fat fee is selected, and every template built from that
4516 // mempool is one CheckBlock() throws away. The fee is never
4517 // paid, because the block never confirms.
4518 //
4519 // Deliberately before ConnectInputs and not after:
4520 // ConnectInputs adds this transaction's fee to nFees by
4521 // reference on success. Passing it and then declining the
4522 // transaction would credit the coinbase for a fee no
4523 // transaction in the block pays, and the block would be
4524 // rejected for a wholly different reason.
4525 unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
4526 if (!BlockTemplateFits(nBlockSize, nTxSize))
4527 continue;
4528
4498 // Transaction fee requirements, mainly only needed for flood control 4529 // Transaction fee requirements, mainly only needed for flood control
4499 // Under 10K (about 80 inputs) is free for first 100 transactions 4530 // Under 10K (about 80 inputs) is free for first 100 transactions
4500 // Base rate is 0.01 per KB 4531 // Base rate is 0.01 per KB
@@ -4506,7 +4537,7 @@ bool BitcoinMiner(int nThreadId)
4506 swap(mapTestPool, mapTestPoolTmp); 4537 swap(mapTestPool, mapTestPoolTmp);
4507 4538
4508 pblock->vtx.push_back(tx); 4539 pblock->vtx.push_back(tx);
4509 nBlockSize += ::GetSerializeSize(tx, SER_NETWORK); 4540 nBlockSize += nTxSize;
4510 vfAlreadyAdded[n] = true; 4541 vfAlreadyAdded[n] = true;
4511 fFoundSomething = true; 4542 fFoundSomething = true;
4512 } 4543 }