XHash

La preuve de travail memory-hard utilisée par Parallax. Hashimoto de type Ethash avec la difficulté ASERT de Parallax, ses règles de consensus et sa gestion des époques.

Détails pertinents pour le consensus
Tels qu'ils apparaissent dans xhash/consensus.go et la configuration de chaîne Parallax.
AspectValeur / ComportementNotes
Fonction de hachage (seal)
Legacy Keccak-256
RLP sur des champs d'en-tête spécifiques
Mappage de cible
target = ⌊(2^256−1)/D⌋
Le résultat doit être ≤ target
MixDigest
Doit être égal au digest calculé
Une différence ⇒ PoW invalide
MTP
Médiane des 11 derniers
Imposé : Time > MTP(parent) ; dérive future ≤ 300 s
Longueur d'époque
Définie en configuration (par ex. 720 blocs)
Détermine la cadence de régénération du cache/DAG
Algorithme de difficulté
ASERT (par bloc, fondé sur une ancre)
aserti3-2d ; le consensus calcule D, XHash l'applique via la PoW
Vue d'ensemble
XHash est le moteur de preuve de travail de Parallax : Hashimoto de type Ethash (light/full) avec câblage de consensus propre à Parallax, mappage de difficulté fondé sur ASERT et gestion des époques.
  • Le minage évalue Hashimoto sur un seal hash d'en-tête et un nonce 64 bits, en utilisant soit un cache (light) soit un dataset (full).
  • La vérification contrôle l'égalité du MixDigest et compare le résultat à une cible dérivée de la difficulté : target = ⌊(2^256−1)/D⌋.
  • Le seal hash utilise Legacy Keccak-256 sur une liste RLP de champs d'en-tête spécifiques.
SealHash
pseudocode
SealHash(header):
  enc = [
    header.ParentHash,
    header.Coinbase,
    header.Root,
    header.TxHash,
    header.ReceiptHash,
    header.Bloom,
    header.Difficulty,
    header.Number,
    header.GasLimit,
    header.GasUsed,
    header.Time,
    header.Extra,
    header.EpochStartTime,
  ]
  if header.BaseFee != nil:
    enc.append(header.BaseFee)
  return keccak256(rlp.encode(enc))
Boucle de minage
Hashimoto light/full, recherche de nonce 64 bits et comparaison de cible en little-endian dans la boucle interne.
  • Les mineurs itèrent nonce ∈ [0..2^64−1], en calculant (digest, result) = hashimoto(cache/dataset, sealHash, nonce).
  • MixDigest doit correspondre exactement au champ d'en-tête ; result doit être ≤ target.
  • Les mineurs full utilisent le dataset par époque ; les vérificateurs/nœuds légers peuvent valider via des caches (pas besoin du DAG complet).
  • Le dataset et le cache sont régénérés aux frontières d'époque (par ex. tous les 720 blocs).
Recherche de nonce (pseudocode)
pseudocode
mine(header, cacheOrDataset):
  target = floor((2^256 - 1) / header.Difficulty)
  nonce  = random64()
  loop:
    (mix, res) = hashimoto(cacheOrDataset, SealHash(header), nonce)
    if mix == header.MixDigest and Big(res) <= target:
      return nonce
    nonce = (nonce + 1) mod 2^64
Vérification d'en-tête
Ce qu'un nœud vérifie avant d'accepter un en-tête PoW.
  • Taille d'Extra ≤ MaximumExtraDataSize.
  • Time ≤ now + 300 s et Time > MTP(parent) (médiane des 11 derniers).
  • La difficulté doit correspondre à celle du moteur de consensus (ASERT) pour le parent et l'en-tête donnés.
  • Seal PoW : égalité du MixDigest et XHash(header) ≤ target(two256m1 / D).
verifySeal (conceptuel)
pseudocode
verifySeal(h):
  require(h.Difficulty > 0)
  if fulldag:
    (mix, res) = hashimotoFull(dataset(epoch(h.Number)), SealHash(h), h.Nonce)
  else:
    (mix, res) = hashimotoLight(datasetSize(h.Number), cache(epoch(h.Number)), SealHash(h), h.Nonce)
  require(mix == h.MixDigest)
  target = floor((2^256 - 1) / h.Difficulty)
  require(Big(res) <= target)
Époques, cache et dataset (DAG)
Cadence de régénération et notes de compatibilité pour les mineurs.
  • Le numéro d'époque est dérivé de la hauteur de bloc ; les tailles de cache et de dataset dépendent de l'époque via datasetSize(height).
  • Parallax utilise des époques plus courtes que l'Ethash hérité (par ex. des époques de 720 blocs) pour obtenir une régénération d'environ 5 jours avec des blocs de 10 minutes.
  • Les nœuds conservent des caches pour rendre la vérification légère ; aucun DAG complet n'est requis pour valider les en-têtes.
Déclencheur de régénération
pseudocode
if newEpoch(height):
  regenerate cache
  if mining full: regenerate dataset
Difficulté et ancres
Interaction entre la PoW et le calendrier de difficulté ASERT.
  • La difficulté est calculée par l'ordonnanceur ASERT ; XHash se contente de l'appliquer via la vérification de la cible.
  • Le consensus maintient une ancre ASERT (hauteur, parentTime, target) et utilise les métadonnées d'en-tête (par ex. EpochStartTime ou des champs dédiés) pour reconstruire le contexte d'ancre.
  • CalcAsertDifficulty(config, anchor, parent, header) dérive la difficulté attendue pour l'en-tête.
  • Les invariants de difficulté et d'ancre sont appliqués lors de la vérification d'en-tête, avant le choix de fork.
Invariants de difficulté (conceptuels)
pseudocode
verifyDifficulty(h, parent, anchor):
  expected = CalcAsertDifficulty(config, anchor, parent, h)
  require(h.Difficulty == expected)