How Parallax escrows miner subsidies until maturity using deterministic, canonical state updates.
Parameter | Symbol | Value / Source | Notes |
---|---|---|---|
Coinbase maturity blocks | M | 100 blocks | Bitcoin-like coinbase maturity |
Lockbox address | — | 0x0000000000000000000000000000000000000042 | Reserved system account |
Key prefixes | — | "maturity:addr:", "maturity:amt:" | Keccak256(prefix || big-endian height) |
Parallax escrows block subsidies until a fixed maturity height, releasing them deterministically from a lockbox in the state trie.
Finalize(header, state):
h = header.number
R = calcBlockReward(h)
M = 100 blocks
if R > 0:
putScheduledPayout(state, h + M, header.coinbase, R)
if due(h):
(addr, amt) = popDuePayout(state, h)
state.AddBalance(addr, amt)
header.Root = state.IntermediateRoot(...)
Two storage slots per unlock height under a reserved system address.
schedKeyAddr(H):
b = bigEndianUint64(H)
return keccak256("maturity:addr:" || b)
schedKeyAmt(H):
b = bigEndianUint64(H)
return keccak256("maturity:amt:" || b)
From block inclusion to matured transfer.
N: mine block, schedule payout for U=N+M
...
U-1: pending only
U: popDuePayout → AddBalance(addr_U, amt_U) → clear slots
U+1: nothing due for U anymore
Deterministic unlocks, replay‑safe, and reorg‑resilient.
popDuePayout(state, H):
rawAmt = state.Get(lockbox, schedKeyAmt(H))
if rawAmt == 0: return (zero, 0, false)
rawAddr = state.Get(lockbox, schedKeyAddr(H))
state.Set(lockbox, schedKeyAmt(H), 0)
state.Set(lockbox, schedKeyAddr(H), 0)
return (Address(rawAddr), BigInt(rawAmt), true)
What happens if the chain reorganizes around unlock heights.
ReorgTo(newTip):
rewind state → parent of fork
for block in pathTo(newTip):
Execute(block) // schedules & payouts naturally recompute
Where the maturity parameter lives and how clients consume it.
for h in [current..current+K]:
if getState(lockbox, schedKeyAmt(h)) != 0:
// list upcoming payout at height h