Introduction

The Merge, le passage tant attendu d’Ethereum du Proof Of Work (PoW) en Proof en Stake (PoS), a été l’évènement le plus préparé et travaillé de l’écosystème blockchain ces dernières années. La fondation Ethereum, les Core Developers et tous ceux qui y ont participé ont réalisé un travail de longue haleine durant plusieurs années pour aboutir à cette prouesse technique. Malgré toute cette minutie, des vulnérabilités ont été découvertes !

Dans cet article, nous allons explorer une de ces vulnérabilités en se basant sur un article publié par des chercheurs français: Ethereum Proof-of-Stake under Scrutiny par Ulysse Pavloff, Yackolley Amoussou-Guenou et Sara Tucci-Piergiovanni. L’article original, en anglais, est disponible gratuitement sur arxiv.

Temps de lecture: ~10min

Ethereum Proof of Stake, Kesako ?

Avant de plonger dans l’eau, il est judicieux de savoir nager !

Cette section est dédiée à reprendre les bases du protocole de consensus d’Ethereum en Proof of Stake. Si vous êtes déjà familier avec celui ci, vous pouvez directement passer à Bouncing Attack.

Commençons par nous pencher sur le nouveau (depuis The Merge) mécanisme de consensus d’Ethereum. Un article explicatif étant déjà sorti sur le blog, Le mécanisme de consensus d’Ethereum après la Fusion par Jean Zundel, nous nous contenterons de détailler ici les principaux éléments utiles pour la suite de cet article.

Si vous souhaitez en savoir plus sur The Merge et le passage d’Ethereum en Proof of Stake, nous vous conseillons de lire l’article Ethereum a fusionné par Nathan Sexer également sorti sur ce blog.

Epoch

Dans la version Proof of Stake d’Ethereum, les blocs sont produits à intervalle régulier en se basant sur la notion du temps propre à chaque validateur, avec l’hypothèse qu’il sera globalement identique chez chacun. Dans le protocole, le temps qui passe est découpé en slots et en epochs.

Un slot dure 12 secondes et une epoch est composée de 32 slots consécutifs.

Quick Maths: Une epoch dure donc 32 * 12s = 6 minutes et 24 secondes.

Finalité

En raison de la nature décentralisée du réseau et la présence de potentiels acteurs défaillants, voir malveillants, les fameux comportements Byzantins, il est possible qu’un bloc initialement ajouté selon un certain validateur soit peu après annulé. On parle alors de bloc orphelin et les transactions qu’il contient sont également “annulées”. Cela se réalise généralement lorsqu’un fork se produit: deux chaines existent en parallèle et seulement l’une des deux sera finalement choisie par les validateurs pour ajouter les nouveaux blocs.

Un bloc qui n’est plus sujet à de tels événements est dit finalisé. Il ne peut plus être annulé et est ajouté de manière irréversible à la blockchain.

Dans Ethereum Proof of Stake, le processus de production de blocs et celui de finalisation sont dissociés: les validateurs continuent d’ajouter de nouveaux blocs à la chaine qu’ils considèrent comme la bonne sans trop se soucier d’où en est la finalisation et en parallèle ils finalisent les blocs lorsque cela est possible. La finalisation est réalisée grâce à un outil appelé “finality gadget”, dont nous allons détailler plus loin certaines composantes.

Checkpoint

Mais alors comment les validateurs font-ils pour finaliser la chaine ?

Ils suivent leur protocole de consensus et votent chacun une fois par epoch avec ce que l’on appelle un vote de checkpoint. Un checkpoint est une paire bloc-epoch où le bloc est celui produit lors du premier slot de l’epoch et chaque vote contient deux checkpoints, une source associée à une cible. Bien sûr, le bloc du checkpoint cible doit être un descendant du bloc du checkpoint source.

Ainsi, les validateurs votent pour une paire bloc-epoch car la finalisation est un processus qui s’effectue epoch par epoch et non individuellement pour chaque bloc.

Ce mécanisme est appelé Casper.

Justification

Pour qu’un checkpoint soit finalisé, il faut d’abord qu’il soit justifié. Pour cela, les validateurs se basent sur les votes de checkpoint: si des validateurs contrôlant 2/3 de tous les ethers stakés dans le réseau transmettent le même vote de checkpoint, avec la même source et la même cible, alors on dit qu’il y a un lien de supermajorité entre la source et la cible ; et la cible est dite justifiée.

Puisque le bloc cible est un descendant du bloc source, un tel lien ne s’effectue que d’une epoch plus ancienne vers une plus récente.

Finalisation

Un lien de supermajorité ne permet pas uniquement de justifier le bloc cible, mais également de finaliser le bloc source ! Cependant, la finalisation ne s’effectue que dans des circonstances précises : le bloc source doit avoir été justifié auparavant (donc qu’il y ai eu précédemment un autre lien de supermajorité avec comme cible le bloc source) et les epochs source et cible doivent être successives. Un cas spécial finalise également le bloc source si les epochs source et cible sont séparé d’une seule autre epoch, qui a elle aussi été justifiée précédemment.

Attention, un bloc finalisé rend également final tous les blocs qui le précèdent dans sa chaine, mais il ne finalise pas toute son epoch ! En effet, le dernier bloc d’une epoch e n’est pas finalisé quand le checkpoint de l’epoch e est finalisé mais bien quand le checkpoint de l'epoch e+1 est finalisé.

Schéma récapitulatif des concepts d’epoch, checkpoint, justification, finalité et lien de supermajorité

Schéma récapitulatif des concepts d’epoch, checkpoint, justification, finalité et lien de supermajorité

Cette infographie schématise toutes les notions abordées dans cette partie. Nous avons représenté une blockchain où chaque epoch ne contient que 3 blocs pour des raisons de simplicité mais c’est le même principe pour les 32 blocs par epoch d’Ethereum. Les hexagones représentent ainsi les epochs (e0, e1, ...) et les doubles flèches entre epochs représentent les liens de supermajorité entre deux checkpoints (c0 et c1 par exemple). Toutes les epochs cibles de ces liens sont justifiés et représentées par un double hexagone. Les epochs et les blocs qui ont été finalisés ont un fond vert. On peut ainsi voir que l’epoch e2', située sur un fork parallèle, a été abandonnée faute de finalisation. Les transactions contenues dans ses blocs sont ainsi “annulées” et ne seront pas considérées dans le futur.

Bouncing Attack

Maintenant que nous comprenons mieux le protocole de consensus d’Ethereum Proof of Stake et les différentes conditions pour finaliser des blocs, nous allons décrire la Bouncing Attack, une menace qui aurait pu permettre à des attaquants malveillants de perturber le réseau !

Implications

La propriété du protocole mise en péril est la liveness, la caractérisation qu’au fur et à mesure que le temps passe, la partie de la chaine qui est finalisée continue de grandir, et ce sans blocage permanent possible. Lorsque cette attaque est effectuée par des agents malveillant, les blocs ne sont ainsi plus finalisés ! Cela implique qu’à tout moment, un bloc précédemment ajouté selon le point de vue d’un validateur peut être abandonné, et ce sans garanti pour les utilisateurs que leurs transactions soient ajoutées de manière permanente !

Source de la définition de la liveness: ethereum.org

Description

Le principe de cette attaque est de “faire rebondir” les validateurs honnêtes entre deux chaines parallèles : ils vont considérer une chaine A comme la chaine correcte et y ajouter les nouveaux blocs, puis considérer que la chaine correcte est désormais une chaine B, puis à nouveau A, puis à nouveau B, … et ainsi de suite. Cette attaque se base sur le fait que la chaine correcte selon le protocole doit démarrer du dernier checkpoint justifié. Il est ainsi possible pour des agents malveillants de diviser l’avis des validateurs honnêtes en justifiant un nouveau checkpoint sur la chaine parallèle après que certains validateurs honnêtes aient déjà partagé leur vote pour le suivant sur la chaine initiale.

Pour réaliser la bouncing attack, il est nécessaire qu’il y ait, lors d’un fork, un checkpoint sur une chaine parallèle pouvant potentiellement être justifié, avec une epoch plus récente que celle de l’actuel dernier checkpoint justifié. Un checkpoint justifiable est un checkpoint qui peut devenir justifié seulement en lui ajoutant les votes des validateurs Byzantins.

Dans ce cas, les agents malveillants peuvent induire les validateurs honnêtes à voter pour un checkpoint sur une différente chaine, ce qui va à nouveau laisser un checkpoint justifiable sur l’autre chaine, et qui leur permet de répéter leur attaque ainsi en allant et venant d’une chaine à l’autre constamment. Dans une telle configuration, il est impossible pour les validateurs honnêtes de finaliser un checkpoint sur une chaine ou l’autre puisqu’aucune des conditions de finalisation précisées précédemment n’est validée.

Exemple

Faisons une exemple d’une telle attaque avec une configuration de 10 validateurs, dont 3 sont Byzantins et contrôlés par l’attaquant.

Etape 1:

Nous avons initialement un fork avec deux chaines A et B distinctes.

Etape 1 de la bouncing attack: la situation initiale

Etape 1 de la bouncing attack: la situation initiale

Chaque hexagone représente cette fois un checkpoint. Comme précédemment, s’il y un double hexagone, alors le checkpoint est justifié et s’il est vert c’est qu’il est finalisé (lui seul et non toute son epoch). Puisque nous considérons les checkpoints pour la finalité, les blocs entre chaque checkpoint ont été omis. Chaque checkpoint représente ainsi en quelque sorte une epoch sur une des chaines. Le nombre inscrit dans chaque checkpoint correspond au nombre de validateur ayant émis un vote de checkpoint le ciblant.

Pour simplifier, nous considérons une situation où chaque validateur a le même nombre d’ether staké dans le réseau et donc que chaque vote à le même poids. Ainsi, puisque nous n’avons que 10 validateurs dans cet exemple, il faut 7 votes pour justifier un checkpoint. La justification des checkpoints c0 et cA1 découle de ce fait. Dans cette situation initiale, on voit que la chaine qui devrait être choisie par les validateurs honnêtes est la chaine A (puisque c’est sur celle ci que se trouve le checkpoint justifié avec l’epoch la plus récente cA1). En parallèle, il est bon de noter que le checkpoint cB2 est justifiable si on y ajoute 3 votes.

Etape 2:

Les validateurs honnêtes continuent leur travail comme prévu.

Etape 2 de la bouncing attack: les validateurs honnêtes continuent comme ils le doivent

Etape 2 de la bouncing attack: les validateurs honnêtes continuent comme ils le doivent

En effet, c’est bien sur la chaine A que se situe le nouveau checkpoint cA3 sur le point d’être justifié. Il représente tous les blocs qui ont été produits entre les deux étapes.

Etape 3:

Cependant, c’était sans compter la présence de l’attaquant qui va entrer en jeu à cet instant précis pour partager sur le réseau les votes de ses 3 validateurs pour le checkpoint justifiable cB2 !

Etape 3 de la bouncing attack: l’attaquant parvient à justifier un checkpoint plus récent sur la chaine parallèle, les validateurs honnêtes vont devoir migrer sur celle ci

Etape 3 de la bouncing attack: l’attaquant parvient à justifier un checkpoint plus récent sur la chaine parallèle, les validateurs honnêtes vont devoir migrer sur celle ci

cB2 était jusqu’alors abandonné, tout comme la chaine B, mais ces nouvelles révélations vont néanmoins forcer les validateurs honnêtes à changer d’avis sur la chaine correcte et à “migrer” vers la chaine B, car elle contient désormais le checkpoint avec l’epoch la plus haute.

Les transactions fraichement ajoutées avec cA3 sont à leur tour abandonnées… jusqu’au rebond suivant ! En effet, cette situation finale est identique à celle initiale si on échange les chaines A et B et il est donc possible pour l’attaquant de continuer ce mouvement de rebond de chaine en chaine.

De plus, on peut voir qu’il est impossible de finaliser un checkpoint sur une chaine ou l’autre selon les règles détaillées en première partie.

Le Correctif

Le correctif de cette vulnérabilité est simple: il consiste à modifier le protocole pour forcer les validateurs honnêtes à ne plus changer leur opinion sur la chaine correcte (celle contenant le checkpoint justifié avec l’epoch la plus haute) après qu’un nombre j définit de slots soient passés dans l’epoch. Ainsi, une seule chaine continuera d’être considérée par les validateurs honnêtes sans que des agents malveillants puissent les faire changer d’avis pour réaliser un aller-retour entre deux chaines. On appelle ce principe une “fixation de vue ou d’avis”.

Actuellement, j = 8 ! Cela signifie que les validateurs ne peuvent changer d’avis lors d’une epoch uniquement lors des 8 premiers slots ! (environ 1min 30s)

Bouncing Attack (le retour)

Selon les auteurs de l’article original sur lequel est basé celui-ci, le correctif décrit précédemment n’est pas suffisant pour se prémunir entièrement de ce type d’attaque.

Description

En effet, il serait toujours possible pour des agents malveillants de réaliser une bouncing attack en la modifiant quelque peu. Elle est permise grâce au pouvoir d’équivocation des Byzantins, qui est la possibilité pour un agent malveillant d’envoyer un message pour une opération donnée à un certain ensemble de validateurs, et un message différent (ou de ne pas en envoyer) au reste des validateurs pour la même opération. Il s’agit d’une action classique possible pour un attaquant et tout protocole doit être résistant à ce type d’action pour être tolérant aux failles byzantines (BFT).

La situation initiale est exactement la même qu’avant, avec un fork entre deux chaines A et B, un checkpoint justifiable sur A et un checkpoint plus vieux justifié sur B. Un attaquant souhaitant réaliser une bouncing attack v2, peut utiliser l’équivocation juste avant que la limite j du correctif soit atteinte, à condition qu’il contrôle le validateur désigné pour proposer un bloc à ce slot. Si c’est le cas, le bloc révélé doit contenir assez de nouveaux votes de la part des Byzantins pour faire changer d’avis 2/3 des validateurs honnêtes de la chaine B à la chaine A. Il s’agit des validateurs qui doivent recevoir le message juste avant la limite. En revanche, il est également nécessaire que le tiers restant des validateurs honnêtes ne reçoit pas le message à temps et continue sur la chaine parallèle B. Puisqu’ils n’ont plus le droit de changer d’avis, chaque groupe de validateurs honnêtes va continuer sur sa chaine pour le reste de l’epoch et une fois qu’elle sera finie, la situation sera la même qu’initialement. Cela peut permettre (si les conditions sont à nouveau favorables) à l’attaquant de recommencer le processus pour créer un nouveau rebond et ainsi de suite, sans permettre la finalisation d’aucun des checkpoints.

Implications

Les conséquences pour la blockchain de cette version 2 de la bouncing attack sont toujours les mêmes : les blocs ne peuvent plus être finalisés durant toute la durée de l’attaque. Cependant, cette fois l’attaque ne peut pas être systématiquement réalisée et a une probabilité décroissante de succès sur une longue durée. De plus, la condition de contrôler finement l’instant de réception des votes pour chacun des validateurs honnêtes du réseau semble difficile à réaliser en pratique. Il est bon de noter qu’une fois l’attaque terminée, le processus de finalisation peut se reprendre naturellement et que la blockchain est perturbée uniquement au cours de l’attaque.

La probabilité de réaliser une telle attaque sur k epochs est : P(bouncingk times) = (1 − αj)k avec α la proportion de validateurs honnêtes. Cette formule s’explique par le fait qu’il est nécessaire d’avoir un validateur Byzantin comme proposeur (le validateur choisit de manière aléatoire pour proposer le bloc du slot) dans les j premiers slots et ce successivement sur toute la série d’epochs attaquées.

Pour rappel, j = 8 actuellement !

 

Prenons comme exemple un attaquant contrôlant 10% des validateurs et souhaitant réaliser une bouncing attack sur 9 epochs (environ une heure). La probabilité de succès en cas de situation favorable est de (1-0.98)9 = 0.006 = 0.6%.

Conclusion

Pour conclure, nous avons décrit une attaque pouvant perturber Ethereum Proof of Stake en empêchant de finaliser des blocs sur une longue période. Nous avons décrit comment elle avait été corrigée par la communauté mais également évoqué que le risque de réaliser quand même cette attaque existe toujours. A ce jour, cette vulnérabilité, certes compliquée à exploiter et nécessitant de gros moyens pour être menée à bien, n’a pas été corrigée à notre connaissance.

 

Nous souhaitons remercier fortement pour leur travail les auteurs de l’article scientifique original sur lequel celui ci se base : Ethereum Proof-of-Stake under Scrutiny.

 

Si vous voulez en apprendre plus, voici quelques sources:

Maxper
Maxper Web3 Dev, Blockchain Builder et étudiant-chercheur en systèmes distribués
flèche rouge

Retour en haut

Articles similaires

Articles similaires

Rejoindre discord

img
img