Repository navigation
Consensus: Temporary extended generation maturity for newly mined outputs - #410
kwsantiago wants to merge 4 commits into
Conversation
642d8f2 to
d2f1509
Compare
|
Concept ACK |
11ba7b4 to
58c72b5
Compare
58c72b5 to
c479db0
Compare
c4e8b26 to
8dff2d3
Compare
aa52c4b to
210f338
Compare
|
Tested 210f338, the 21:42Z push, on Ubuntu 24.04, gcc, wallet on. Of the maturity branches open right now this is the one that needs no chain context to answer the question, and that is the part worth keeping whatever else changes. Coverage is a function of the height an output was created at, so The end height cannot be derived from the target spacing, and the body's plan for it needs a number. "The end height is intended to land near the RDTS expiry." The expiry is a median-time-past -- 1819756800, 2027-09-01 00:00 UTC -- and the window ends at a height, so somebody has to convert one to the other. At the tip as I write this, median-time-past 2026-09-12 21:32 UTC, that is 353.1 days away: 130,000 blocks between the two assumptions. 168.8 s is the mean median-time-past step over blocks 971171-971370; the chain has run at 3-4x the target rate through every retarget epoch since the fork, and all four hit the 4x clamp. Pick the end height at 600 s and the window closes in early spring while RDTS runs to September, which puts every coinbase mined in that gap back on the ordinary rule. The same arithmetic applies to the depth. The body is straight about this -- "it is a block count, not a duration, so the wall-clock period it delivers depends on how fast blocks are actually found" -- and the number is 12.7 days, not 45: So as written this is a 12-day lock described as a 45-day one. That may still be the right lock, but it should be chosen knowing which of the two it is. This is the same basis point I raised on #402 and #404; #403 is the branch that answers it by measuring the duration in median-time-past seconds instead of blocks, and nothing in this design stops the depth being expressed that way, since neither end of the window would need a tip for it. The wallet gap, with a shape. You state it up front so this is not a finding, but the transcript is worth having in the thread because it is the first thing an operator meets. On regtest with It sits in the spendable balance, Two smaller things:
The I have also left a comment on #404 with measured numbers on whose coins these locks actually land on, which applies here unchanged. Scripts and raw output: https://gist.github.com/jasonsopko/75e2f3e69cefcfa19911378aa4c5a54d |
210f338 to
f0be007
Compare
|
@jasonsopko there's a counting bug in payout-lag.py:55, linked in your last comment. It adds the full coinbase value for every matching input instead of just the output being spent. I reproduced it with a synthetic 25 BTC coinbase split into 10 and 15 BTC, each output spent once. The script reports 50 BTC spent. Use |
f0be007 to
9f5b42d
Compare
|
@f4u57ox is right, and it is worse than your synthetic case: 11.5x on the window I posted, not 2x. Your repro is exact: I get 50 BTC against a true 25, both when the two outputs are spent by separate transactions and when one transaction takes both. Line 55 added the whole coinbase's value for every matching input, so a hasher spending a 0.01 BTC payout out of a 116-output coinbase was credited the entire block reward. 39 of the 400 transactions in that window take two outputs of the same coinbase, counting it twice again. Selecting Both bugs here are mine, and I only found the second because you made me go back through the loop. The value and the ages of an entire transaction were attributed to the first pool whose coinbase it touched, and 130 of the 400 transactions spend coinbases of more than one pool, 17.2 % of the value. Counted per input, against the pool that created each output: Sherpa is the one that bothers me: 43.8 BTC, 4.5 % of the window, in no row at all, because the one transaction spending it also took an older coinbase belonging to another pool. That walks back what I said about the ages. They are untouched by your bug, which is what your comment says and it is right, but the misattribution moved medians that were not yours to move: Catbus 258 to 165, RIPTIDE 570 to 422, Bitcoin Xor 308 to 265. AlphaPool, the row the argument actually rests on, does not move: 465.8 BTC, median 102, p90 105, 98.7 % of its outputs spent by block 110. Verification, since one wrong number in a thread is enough. I rebuilt the table from the node alone, taking each parent transaction from
Fixed script, regenerated output, and a |
|
Closing boundary — The batch unlock is gone, but the closing boundary still has a sharp payout difference. The reward at Schedule changes — The body already explains that blocks do not guarantee a duration. It should also state that the end height cannot be kept aligned with RDTS by adjusting it after deployment. With |
|
Closing in favor of #419, which lands the same shape: coverage fixed at the creation height, plain height params, no median-time-past. It also carries the wallet and GUI work this deliberately left out. Thanks for the reviews here, the transcripts in particular. |
A generation output created at a height inside the window must be 6480 blocks deep instead of 100. Coverage is fixed when the output is created: outputs mined before the start height keep the ordinary rule, and outputs mined inside the window serve the full period even where that runs past the end height.
Nothing already spendable is immobilized when the window opens, and nothing unlocks in a batch when it closes. Both ends are block heights, so the requirement is a pure function of the height an output was created at and no median-time-past is involved.
6480 blocks is 45 days at the 600s target spacing. It is a block count, not a duration, so the wall-clock period it delivers depends on how fast blocks are actually found. The end height is intended to land near the RDTS expiry.
Mainnet, testnet3, testnet4 and signet unscheduled. Regtest:
-coinbasematuritylong=<start>:<end>:<depth>.Known gap, needed before any start height is set: the wallet and GUI still hardcode the ordinary maturity, so they will offer covered outputs and the resulting sends will sit unbroadcast until the depth is reached.