Coinbase Maturity Scheduling

How Parallax escrows miner subsidies until maturity using deterministic, canonical state updates.

Maturity Parameters
ParameterSymbolValue / SourceNotes
Coinbase maturity blocksM100 blocksBitcoin-like coinbase maturity
Lockbox address0x0000000000000000000000000000000000000042Reserved system account
Key prefixes"maturity:addr:", "maturity:amt:"Keccak256(prefix || big-endian height)
Overview

Parallax escrows block subsidies until a fixed maturity height, releasing them deterministically from a lockbox in the state trie.

  • Each block schedules its own coinbase payout for height + M blocks, where M = 100 blocks.
  • A special lockbox account stores "when" (height) → (addr, amt) pairs as state slots.
  • At each height h, the protocol checks if any payout is due for h and transfers it to the recorded address.
  • This separates pending issuance from spendable supply and smooths reorg handling.
High‑level flow
pseudocode
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(...)
State Keys & Lockbox Address

Two storage slots per unlock height under a reserved system address.

  • For a given unlock height H: store address at schedKeyAddr(H) and amount at schedKeyAmt(H).
  • Both keys are derived by keccak256 of a fixed ASCII prefix plus H encoded as big‑endian uint64.
  • Presence is keyed off the amount slot; payout clears both slots to reclaim trie space.
Key derivation (from consensus.go)
pseudocode
schedKeyAddr(H):
  b = bigEndianUint64(H)
  return keccak256("maturity:addr:" || b)

schedKeyAmt(H):
  b = bigEndianUint64(H)
  return keccak256("maturity:amt:" || b)
Payout Lifecycle

From block inclusion to matured transfer.

  • Block N is mined → reward R_N computed from halving schedule.
  • Schedule (addr=coinbase_N, amt=R_N) at unlock height U = N + M.
  • At height U, node reads (addr_U, amt_U); if amt_U ≠ 0, credit and clear slots.
  • Genesis (height 0) has no spendable subsidy.
Timeline (textual diagram)
pseudocode
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
Validation & Security Properties

Deterministic unlocks, replay‑safe, and reorg‑resilient.

  • Unlock logic is computed from canonical height; no signatures or off‑chain triggers required.
  • Amounts are protocol‑computed (calcBlockReward) — peers cannot inflate payouts.
  • State key presence is the ‘due’ indicator; double‑spend is prevented by clearing after credit.
  • Maturity defers miner spendability, reducing incentives for near‑tip reorgs to capture fees+subsidy immediately.
Due detection & clearing
pseudocode
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)
Reorg Behavior

What happens if the chain reorganizes around unlock heights.

  • On reorg, state is recomputed from the new canonical chain; scheduled entries reflect the canonical sequence of blocks.
  • If a payout occurred at height H on the old tip but not on the new chain, state replay will only credit what is due under the new history.
  • Because entries are keyed by height, "phantom credits" cannot survive a reorg — cleared/uncleared status follows canonical execution.
  • This mirrors how balances and receipts re‑compute during block re‑execution.
Conceptual reorg pseudocode
pseudocode
ReorgTo(newTip):
  rewind state → parent of fork
  for block in pathTo(newTip):
    Execute(block) // schedules & payouts naturally recompute
Configuration

Where the maturity parameter lives and how clients consume it.

  • Maturity M is defined as 100 blocks
  • Clients must display both ‘pending’ (scheduled) and ‘spendable’ balances for miners.
  • Explorers can show upcoming unlocks by scanning for non‑zero schedKeyAmt(h).
  • Wallets should warn miners that reward UTXOs (account credits) are not available until height U.
Explorer hint (pseudo‑RPC)
pseudocode
for h in [current..current+K]:
  if getState(lockbox, schedKeyAmt(h)) != 0:
    // list upcoming payout at height h