diff --git a/doc/release-notes-longmaturity-rdts.md b/doc/release-notes-longmaturity-rdts.md new file mode 100644 index 000000000000..423badf4dfde --- /dev/null +++ b/doc/release-notes-longmaturity-rdts.md @@ -0,0 +1,14 @@ +Long coinbase maturity: one year, expiring with RDTS +---------------------------------------------------- + +The temporary softfork that Bitcoin Knots 29.4.2 deployed to hold generated bitcoins for longer is extended: instead of ending at block 979920, it expires together with RDTS. Bitcoins generated by block 973440 or any later block cannot be spent in any block whose parent has a median time past earlier than 2027 September 1st 00:00 UTC. From the first block past that, they are spendable at the original maturity of 100 blocks. + +This is a softfork on top of 29.4.2. Up to block 979919 the rules are the same, so nothing changes until then. Block 979920 is the first that 29.4.2 could accept and this version reject (one spending bitcoins generated since block 973440), so miners and nodes need to upgrade before it, expected around the end of October at the current block rate (the date moves with the hashrate). + +A 29.4.2 node that upgrades only after block 979920 rechecks the blocks it connected from that height on at its first start: it rewinds to block 979919 and reconnects everything above it. A node coming from 29.4.1 or from another implementation has no record of having checked the long maturity rule at all, so it rewinds to block 973439 instead, as it would when upgrading to 29.4.2. Either way the rewind needs the block and undo data of those blocks, so a pruned node that upgrades late may have to reindex. Upgrading before block 979920 avoids all of this. + +Policy and the wallet now apply the same delay to every coin generated by block 973440 or later, measured in time rather than in blocks: the mempool does not relay a spend of such a coin until the median time past of the chain tip is at least one year (365 days) past that of the block that generated it, and the wallet counts the coin as immature until then. Coins generated before block 973440 need only the original maturity of 100 blocks; 29.4.2 policy held them for 6480 blocks, which they all reach by block 979919. For the covered coins this is never sooner than the consensus expiry, which the mempool also enforces directly, so a wallet holding them can no longer select one that consensus would still reject. The GUI shows how many days such a coin still has to wait; the number of blocks to maturity shown for it elsewhere is an estimate, updated with every block. + +A spend held by the consensus rule is rejected as `bad-txns-premature-spend-of-coinbase-long` and one held by the policy delay as `premature-spend-of-coinbase-long`; the original 100-block rule keeps `bad-txns-premature-spend-of-coinbase`. + +`getdeploymentinfo` reports the expiry as `expiry_time` for `long_coinbase_maturity`, the same field RDTS uses, and no longer reports `height_end` or `maturity` for it. The regtest option `-testcoinbasematuritylong` now takes `::`. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e2851d88c986..e9290743e872 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -104,28 +103,25 @@ 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)); + throw std::runtime_error(strprintf("Invalid format (%s) for -testcoinbasematuritylong=::.", *arg)); } int32_t start_height; int32_t enforce_height; - int32_t release_height; + int64_t expiry_time; 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])); - } - 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 (!ParseInt64(values[2], &expiry_time) || expiry_time <= 0) { + throw std::runtime_error(strprintf("Invalid expiry time (%s) for -testcoinbasematuritylong=::.", values[2])); } options.coinbase_maturity_long_start_height = start_height; options.coinbase_maturity_long_enforce_height = enforce_height; - options.coinbase_maturity_long_release_height = release_height; + options.coinbase_maturity_long_release_time = expiry_time; } for (const std::string& strDeployment : args.GetArgs("-vbparams")) { diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index de5a866c7c4f..7043197ea915 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -19,7 +19,7 @@ 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=