Échelonnement de la maturité coinbase

Comment Parallax met en séquestre les subventions des mineurs jusqu'à maturité à l'aide de mises à jour d'état déterministes et canoniques.

Paramètres de maturité
ParamètreSymboleValeur / SourceNotes
Blocs de maturité coinbase
M
100 blocs
Maturité coinbase à la Bitcoin
Adresse lockbox
0x0000000000000000000000000000000000000042
Compte système réservé
Préfixes de clés
"maturity:addr:", "maturity:amt:"
Keccak256(préfixe || hauteur big-endian)
Vue d'ensemble
Parallax met en séquestre les subventions de bloc jusqu'à une hauteur de maturité fixe et les libère de manière déterministe depuis un lockbox dans le state trie.
  • Chaque bloc programme son propre versement coinbase pour la hauteur + M, avec M = 100 blocs.
  • Un compte lockbox dédié stocke des paires "quand" (hauteur) → (adresse, montant) sous forme de slots d'état.
  • À chaque hauteur h, le protocole vérifie si un versement est dû pour h et le transfère à l'adresse enregistrée.
  • Cela sépare l'émission en attente de l'offre dépensable et facilite la gestion des réorganisations.
Flux de haut niveau
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(...)
Clés d'état et adresse lockbox
Deux slots de stockage par hauteur de déblocage, sous une adresse système réservée.
  • Pour une hauteur de déblocage H donnée : stocker l'adresse à schedKeyAddr(H) et le montant à schedKeyAmt(H).
  • Les deux clés sont dérivées par keccak256 d'un préfixe ASCII fixe suivi de H encodé en uint64 big-endian.
  • La présence est repérée sur le slot de montant ; le versement efface les deux slots pour libérer l'espace du trie.
Dérivation des clés (extrait de consensus.go)
pseudocode
schedKeyAddr(H):
  b = bigEndianUint64(H)
  return keccak256("maturity:addr:" || b)

schedKeyAmt(H):
  b = bigEndianUint64(H)
  return keccak256("maturity:amt:" || b)
Cycle de vie du versement
De l'inclusion dans un bloc au transfert une fois arrivé à maturité.
  • Le bloc N est miné → la récompense R_N est calculée à partir du calendrier de halving.
  • Planification de (addr=coinbase_N, amt=R_N) à la hauteur de déblocage U = N + M.
  • À la hauteur U, le nœud lit (addr_U, amt_U) ; si amt_U ≠ 0, il crédite et efface les slots.
  • Le genesis (hauteur 0) n'a pas de subvention dépensable.
Chronologie (diagramme textuel)
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 et propriétés de sécurité
Déblocages déterministes, à l'épreuve du rejeu et résistants aux réorganisations.
  • La logique de déblocage est calculée à partir de la hauteur canonique ; aucune signature ni déclencheur hors-chaîne n'est requis.
  • Les montants sont calculés par le protocole (calcBlockReward) — les pairs ne peuvent pas gonfler les versements.
  • La présence d'une clé d'état fait office d'indicateur « dû » ; le double versement est empêché par l'effacement après crédit.
  • La maturité diffère la dépense par le mineur, ce qui réduit l'incitation à des réorgs près de la tête de chaîne pour capturer immédiatement frais et subvention.
Détection de l'échéance et effacement
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)
Comportement en cas de réorganisation
Ce qui se passe si la chaîne se réorganise autour des hauteurs de déblocage.
  • Lors d'une réorganisation, l'état est recalculé à partir de la nouvelle chaîne canonique ; les entrées planifiées reflètent la séquence canonique des blocs.
  • Si un versement a eu lieu à la hauteur H sur l'ancienne tête mais pas sur la nouvelle chaîne, le rejeu d'état ne créditera que ce qui est dû selon la nouvelle histoire.
  • Comme les entrées sont indexées par la hauteur, aucun « crédit fantôme » ne peut survivre à une réorganisation — le statut effacé/non effacé suit l'exécution canonique.
  • Cela reflète la manière dont les soldes et les reçus sont recalculés lors de la ré-exécution des blocs.
Pseudocode conceptuel de réorganisation
pseudocode
ReorgTo(newTip):
  rewind state → parent of fork
  for block in pathTo(newTip):
    Execute(block) // schedules & payouts naturally recompute
Configuration
Où vit le paramètre de maturité et comment les clients le consomment.
  • La maturité M est définie à 100 blocs
  • Les clients doivent afficher aux mineurs à la fois les soldes « en attente » (planifiés) et « dépensables ».
  • Les explorateurs peuvent afficher les déblocages à venir en scannant les schedKeyAmt(h) non nuls.
  • Les portefeuilles devraient avertir les mineurs que les récompenses (crédits de compte) ne sont pas disponibles avant la hauteur U.
Indice explorateur (pseudo-RPC)
pseudocode
for h in [current..current+K]:
  if getState(lockbox, schedKeyAmt(h)) != 0:
    // list upcoming payout at height h