diff --git a/doc/release-notes-31384.md b/doc/release-notes-31384.md new file mode 100644 index 00000000000..9256ec16f46 --- /dev/null +++ b/doc/release-notes-31384.md @@ -0,0 +1,34 @@ +- Node and Mining + +--- + +- **PR #31384** fixed an issue where block reserved weight for fixed-size block header, transactions count, + and coinbase transaction was done in two separate places. + Before this pull request, the policy default for the maximum block weight was `3,996,000` WU, calculated by + subtracting `4,000 WU` from the `4,000,000 WU` consensus limit to account for the fixed-size block header, + transactions count, and coinbase transaction. During block assembly, Bitcoin Core clamped custom `-blockmaxweight` + value to not be more than the policy default. + + Additionally, the mining code added another `4,000 WU` to the initial reservation, reducing the effective block template + size to `3,992,000 WU`. + + Due to this issue, the total reserved weight was always `8,000 WU`, meaning that even when specifying a `-blockmaxweight` + higher than the policy default, the actual block size never exceeded `3,992,000 WU`. + + The fix consolidates the reservation into a single place and introduces a new startup option, + `-blockreservedweight` (default: `8,000 WU`). This startup option specifies the reserved weight for + the fixed-size block header, transactions count, and coinbase transaction. + The default value of `-blockreservedweight` was chosen to preserve the previous behavior. + + **Upgrade Note:** The default `-blockreservedweight` ensures backward compatibility for users who relied on the previous behavior. + + Users who manually set `-blockmaxweight` to its maximum value of `4,000,000 WU` should be aware that this + value previously had no effect since it was clamped to `3,996,000 WU`. + + Users lowering `-blockreservedweight` should ensure that the total weight (for the block header, transaction count, and coinbase transaction) + does not exceed the reduced value. + + As a safety check, Bitcoin core will **fail to start** when `-blockreservedweight` init parameter value is lower than `2000` weight units. + + Bitcoin Core will also **fail to start** if the `-blockmaxweight` or `-blockreservedweight` init parameter exceeds + consensus limit of `4,000,000` weight units. diff --git a/src/init.cpp b/src/init.cpp index 10abd503fcf..d46318fd45e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -648,6 +649,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-blockmaxweight=", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); + argsman.AddArg("-blockreservedweight=", strprintf("Reserve space for the fixed-size block header plus the largest coinbase transaction the mining software may add to the block. (default: %d).", DEFAULT_BLOCK_RESERVED_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); argsman.AddArg("-blockmintxfee=", strprintf("Set lowest fee rate (in %s/kvB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); argsman.AddArg("-blockversion=", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION); @@ -1015,6 +1017,23 @@ bool AppInitParameterInteraction(const ArgsManager& args) } } + if (args.IsArgSet("-blockmaxweight")) { + const auto max_block_weight = args.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); + if (max_block_weight > MAX_BLOCK_WEIGHT) { + return InitError(strprintf(_("Specified -blockmaxweight (%d) exceeds consensus maximum block weight (%d)"), max_block_weight, MAX_BLOCK_WEIGHT)); + } + } + + if (args.IsArgSet("-blockreservedweight")) { + const auto block_reserved_weight = args.GetIntArg("-blockreservedweight", DEFAULT_BLOCK_RESERVED_WEIGHT); + if (block_reserved_weight > MAX_BLOCK_WEIGHT) { + return InitError(strprintf(_("Specified -blockreservedweight (%d) exceeds consensus maximum block weight (%d)"), block_reserved_weight, MAX_BLOCK_WEIGHT)); + } + if (block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) { + return InitError(strprintf(_("Specified -blockreservedweight (%d) is lower than minimum safety value of (%d)"), block_reserved_weight, MINIMUM_BLOCK_RESERVED_WEIGHT)); + } + } + nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp); if (!g_wallet_init_interface.ParameterInteraction()) return false; diff --git a/src/ipc/capnp/mining.capnp b/src/ipc/capnp/mining.capnp index 50b07bda708..e57d6679e18 100644 --- a/src/ipc/capnp/mining.capnp +++ b/src/ipc/capnp/mining.capnp @@ -35,7 +35,7 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") { struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") { useMempool @0 :Bool $Proxy.name("use_mempool"); - coinbaseMaxAdditionalWeight @1 :UInt64 $Proxy.name("coinbase_max_additional_weight"); + blockReservedWeight @1 :UInt64 $Proxy.name("block_reserved_weight"); coinbaseOutputMaxAdditionalSigops @2 :UInt64 $Proxy.name("coinbase_output_max_additional_sigops"); } diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 9995adc0d0f..33eeaf91fb2 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -74,11 +74,12 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman) static BlockAssembler::Options ClampOptions(BlockAssembler::Options options) { - Assert(options.coinbase_max_additional_weight <= DEFAULT_BLOCK_MAX_WEIGHT); + Assert(options.block_reserved_weight <= MAX_BLOCK_WEIGHT); + Assert(options.block_reserved_weight >= MINIMUM_BLOCK_RESERVED_WEIGHT); Assert(options.coinbase_output_max_additional_sigops <= MAX_BLOCK_SIGOPS_COST); - // Limit weight to between coinbase_max_additional_weight and DEFAULT_BLOCK_MAX_WEIGHT for sanity: - // Coinbase (reserved) outputs can safely exceed -blockmaxweight, but the rest of the block template will be empty. - options.nBlockMaxWeight = std::clamp(options.nBlockMaxWeight, options.coinbase_max_additional_weight, DEFAULT_BLOCK_MAX_WEIGHT); + // Limit weight to between block_reserved_weight and MAX_BLOCK_WEIGHT for sanity: + // block_reserved_weight can safely exceed -blockmaxweight, but the rest of the block template will be empty. + options.nBlockMaxWeight = std::clamp(options.nBlockMaxWeight, options.block_reserved_weight, MAX_BLOCK_WEIGHT); return options; } @@ -98,14 +99,15 @@ void ApplyArgsManOptions(const ArgsManager& args, BlockAssembler::Options& optio if (const auto parsed{ParseMoney(*blockmintxfee)}) options.blockMinFeeRate = CFeeRate{*parsed}; } options.print_modified_fee = args.GetBoolArg("-printpriority", options.print_modified_fee); + options.block_reserved_weight = args.GetIntArg("-blockreservedweight", options.block_reserved_weight); } void BlockAssembler::resetBlock() { inBlock.clear(); - // Reserve space for coinbase tx - nBlockWeight = m_options.coinbase_max_additional_weight; + // Reserve space for fixed-size block header, txs count, and coinbase tx. + nBlockWeight = m_options.block_reserved_weight; nBlockSigOpsCost = m_options.coinbase_output_max_additional_sigops; // These counters do not include coinbase tx @@ -393,7 +395,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda ++nConsecutiveFailed; if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight > - m_options.nBlockMaxWeight - m_options.coinbase_max_additional_weight) { + m_options.nBlockMaxWeight - m_options.block_reserved_weight) { // Give up if we're close to full and haven't succeeded in a while break; } diff --git a/src/node/miner.h b/src/node/miner.h index 5e5d0b25f30..56101561b11 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -176,7 +176,9 @@ public: /** Construct a new block template */ std::unique_ptr CreateNewBlock(); + /** The number of transactions in the last assembled block (excluding coinbase transaction) */ inline static std::optional m_last_block_num_txs{}; + /** The weight of the last assembled block (including reserved weight for block header, txs count and coinbase tx) */ inline static std::optional m_last_block_weight{}; private: diff --git a/src/node/types.h b/src/node/types.h index 4b0de084ab3..290bbc23f14 100644 --- a/src/node/types.h +++ b/src/node/types.h @@ -14,6 +14,7 @@ #define BITCOIN_NODE_TYPES_H #include +#include #include