diff --git a/doc/release-notes-coinbase-rolling.md b/doc/release-notes-coinbase-rolling.md new file mode 100644 index 000000000000..ca26ed1fdf9b --- /dev/null +++ b/doc/release-notes-coinbase-rolling.md @@ -0,0 +1,40 @@ +RDTS continues; rolling 40320-block coinbase maturity +---------------------------------------------------- + +On mainnet, RDTS (BIP110) no longer expires: `RdtsExpiryTime` was +1819756800 (2027-09-01 00:00 UTC) and is now `INT64_MAX`. Its rules (the +800000-weight block limit, 34-byte output scripts, the OP_RETURN data limit +and the input rules it gates) stay in force at every height from the BLAKE2b +fork. Keeping rules in force is strictly more restrictive than letting them +lapse, so this is a soft fork. + +The rolling coinbase maturity continues with RDTS, as #467 made it, at 40320 +blocks (~280 days, about the length of a human pregnancy) instead of 6480 +(~45 days). Rewards mined from height 973440 each mature 40320 blocks after +the block that mined them; there is no hold until a date and no release. The +rule matches #419's valid-block set before height 979920 (no covered reward +can reach 6480 confirmations sooner) and only restricts it from 979920 +onward, where #419 returns covered rewards to 100 and #467 keeps 6480 until +2027-09-01. Testnet4 keeps its original height-based window; other public +networks and default regtest are unchanged. + +The existing revalidation marker extends beyond #419's height window, bounded +by parent MTP. A #419 node upgrading after 979920 rewinds to 979919, even +when its tip is already past expiry, to recheck pre-expiry history and cached +descendants. Without a marker, revalidation begins at enforcement minus one. +Pruned nodes missing required history refuse startup before disconnecting +blocks and must reindex. Nodes that ran different experimental rules sharing +the marker must also reindex. Assumeutxo background validation must complete +pre-snapshot coverage. + +Mempool admission, reorg filtering, wallet credit, coin selection and GUI +maturity retain #419 behavior: every coinbase needs the configured long +depth for policy and wallet availability, including after consensus expiry. +Policy therefore remains stricter than consensus after expiry. Wallet credit +retains its one-confirmation margin. RPC category help describes applicable +maturity; deployment reporting shows the configured long depth, parent-MTP +expiry and next-block active state. GBT stops advertising the rule at expiry. + +Configured regtest accepts `-testcoinbasematuritylong=start:enforce:release[:maturity]` +and `-testcoinbasematuritylongexpiry=time`. The expiry defaults to the configured +RDTS expiry, or INT64_MAX if none, and must match `-rdtsexpiry` when both are set. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e2851d88c986..1a2c3243f4d9 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -103,31 +103,50 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti if (const auto arg{args.GetArg("-testcoinbasematuritylong")}; arg) { const auto values{SplitString(*arg, ':')}; - if (values.size() != 3) { - throw std::runtime_error(strprintf("Invalid format (%s) for -testcoinbasematuritylong=::.", *arg)); + if (values.size() < 3 || values.size() > 4) { + throw std::runtime_error(strprintf("Invalid format (%s) for -testcoinbasematuritylong=::[:].", *arg)); } int32_t start_height; int32_t enforce_height; int32_t release_height; if (!ParseInt32(values[0], &start_height) || start_height < 0 || start_height >= std::numeric_limits::max()) { - throw std::runtime_error(strprintf("Invalid start height (%s) for -testcoinbasematuritylong=::.", values[0])); + throw std::runtime_error(strprintf("Invalid start height (%s) for -testcoinbasematuritylong=::[:].", values[0])); } if (!ParseInt32(values[1], &enforce_height) || enforce_height < 2 || enforce_height >= std::numeric_limits::max()) { - throw std::runtime_error(strprintf("Invalid enforce height (%s) for -testcoinbasematuritylong=::.", values[1])); + throw std::runtime_error(strprintf("Invalid enforce height (%s) for -testcoinbasematuritylong=::[:].", values[1])); } if (!ParseInt32(values[2], &release_height) || release_height <= start_height || release_height <= enforce_height || release_height >= std::numeric_limits::max()) { - throw std::runtime_error(strprintf("Invalid release height (%s) for -testcoinbasematuritylong=::.", values[2])); + throw std::runtime_error(strprintf("Invalid release height (%s) for -testcoinbasematuritylong=::[:].", values[2])); } if (release_height - start_height <= COINBASE_MATURITY) { throw std::runtime_error(strprintf("Invalid height range (%s) for -testcoinbasematuritylong=::: release_height - start_height must exceed COINBASE_MATURITY.", *arg)); } + if (values.size() == 4) { + int32_t maturity; + if (!ParseInt32(values[3], &maturity) || maturity < COINBASE_MATURITY || maturity >= std::numeric_limits::max()) { + throw std::runtime_error(strprintf("Invalid maturity (%s) for -testcoinbasematuritylong.", values[3])); + } + options.coinbase_maturity_long_maturity = maturity; + } + options.coinbase_maturity_long_start_height = start_height; options.coinbase_maturity_long_enforce_height = enforce_height; options.coinbase_maturity_long_release_height = release_height; } + if (const auto arg{args.GetArg("-testcoinbasematuritylongexpiry")}; arg) { + int64_t expiry; + if (!options.coinbase_maturity_long_start_height || !ParseInt64(*arg, &expiry) || expiry <= 1296688602) { + throw std::runtime_error("Invalid expiry for -testcoinbasematuritylongexpiry: requires -testcoinbasematuritylong and a time after regtest genesis (1296688602)."); + } + if (options.rdts_expiry_time && *options.rdts_expiry_time != expiry) { + throw std::runtime_error("-testcoinbasematuritylongexpiry must match -rdtsexpiry when both are specified."); + } + options.coinbase_maturity_long_expiry_time = expiry; + } + for (const std::string& strDeployment : args.GetArgs("-vbparams")) { std::vector vDeploymentParams = SplitString(strDeployment, ':'); if (vDeploymentParams.size() < 3 || 7 < vDeploymentParams.size()) { diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index de5a866c7c4f..88bebb86dbfd 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -19,7 +19,8 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv, blake2b). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-rdtsexpiry=