---
id: 21
title: "Qu'est-ce que l'EVM au juste : du registre distribué à la machine à états distribuée"
slug: 0-1-what-is-evm
date: 2026/09/18
summary: "Prendre Ethereum pour un registre consignant des virements laisse échapper son élément le plus essentiel : les variables librement lisibles et inscriptibles du stockage des comptes. D'après la fonction de transition d'état Υ(σ, T) du Yellow Paper, Ethereum est une machine à états distribuée, et l'EVM est la spécification d'exécution de cette transition d'état. Le rejeu bit à bit par l'ensemble du réseau impose en outre un ensemble de contraintes dures à la sémantique d'exécution."
keywords: EVM,état global,fonction de transition d'état,exécution déterministe,Yellow Paper
heroImage: /images/community-bg.png
---

Transférer 100 USDT (un jeton ERC-20) on-chain n'ajoute pas une ligne « A a payé 100 USDT à B ». Ce qui se produit réellement, c'est la réécriture de la valeur de la correspondance `balanceOf` dans le contrat : l'appelant diminue de 100, le destinataire augmente de 100 ; les emplacements de stockage finalement touchés par cette réécriture dépendent de la façon dont le contrat est écrit et du compilateur, le protocole ne contraignant que ce que renvoie `balanceOf`. En parallèle, un journal d'événement (log) est ajouté au reçu de transaction : le journal est un sous-produit destiné aux indexeurs hors chaîne, et la vérité du solde ne réside que dans la valeur courante du stockage.

L'analogie du registre se brise ici. Un registre consigne ce qui a déjà eu lieu, et le lecteur additionne l'historique pour en tirer une conclusion ; Ethereum conserve un « monde courant » continuellement écrasé, que les transactions ne font que pousser vers la version suivante. Pour répondre à la question « qu'est-ce que l'EVM au juste », il faut d'abord définir clairement ce « monde courant », puis examiner qui est chargé de le réécrire.

## Le modèle de registre de Bitcoin suffit, parce que sa structure d'état est extrêmement limitée

Le mot « registre » décrit correctement Bitcoin. L'entrée d'une transaction référence la sortie d'une transaction antérieure ; tant qu'elle n'est pas dépensée par une entrée ultérieure, cette sortie reste une sortie de transaction non dépensée (unspent transaction output, UTXO). Un UTXO ne peut être dépensé qu'une seule fois, et le solde affiché par un portefeuille n'est en réalité que la somme de plusieurs UTXO.

Bitcoin n'est pas dépourvu d'état : l'ensemble des UTXO est l'état. Mais la structure de cet état est très limitée. C'est un tas de « pièces » qui ne peuvent être créées et dépensées qu'en bloc, sans stockage clé-valeur générique, et les scripts ne conservent aucune variable qui persiste d'une transaction à l'autre. « Un registre plus un ensemble de sorties non dépensées » suffit donc à décrire complètement le système : valider une transaction ne demande que de vérifier l'existence des sorties référencées et la satisfaction du script de condition, sans interroger la moindre « variable interne de contrat ».

Pour qu'Ethereum permette à un contrat de détenir une correspondance librement lisible et inscriptible, cette correspondance ne peut plus être traitée comme un détail d'implémentation. La validité d'un transfert ERC-20 dépend de la valeur courante du slot de solde de l'appelant dans le stockage du contrat. L'état passe donc du statut d'« ensemble entretenu accessoirement lors de la validation » à celui d'objet de première classe sur lequel le protocole doit s'engager.

## L'état global : compresser « tout l'instant présent » en 32 octets

Le Yellow Paper d'Ethereum définit l'état global (world state) comme une correspondance des adresses vers les comptes. Chaque compte est un quadruplet :

- nonce : nombre de transactions déjà émises par cette adresse, ou nombre de contrats déjà créés par ce compte de contrat ;
- balance : solde libellé en wei ;
- storageRoot : racine du trie de Merkle Patricia du contenu du stockage de ce compte ;
- codeHash : hachage Keccak-256 du code de ce compte, le code lui-même étant stocké dans la base d'état sous ce hachage.

Il faut ici distinguer deux choses. storageRoot engage l'espace clé-valeur propre au compte, où aboutissent en définitive toutes les variables d'état déclarées par l'auteur du contrat ; codeHash pointe vers le programme, que plus aucune transaction ne réécrit après le déploiement. Engager séparément les « données » et le « code » au moyen de deux champs est une prémisse constamment réutilisée dans les discussions à venir sur les ensembles lecture/écriture et la détection de conflits (l'article 0.5 de la même série, « Ne pas confondre les trois types de stockage : Memory, Storage et Transient Storage », détaillera l'appartenance et la durée de vie de chacun).

Tous les comptes sont ensuite organisés en un trie de Merkle Patricia global, dont le hachage racine est le stateRoot, inscrit dans l'en-tête de bloc. Autrement dit, « à quoi ressemble tout Ethereum à cet instant » se compresse en 32 octets, et le réseau entier n'a besoin de s'accorder que sur ces 32 octets.

## La chaîne ne conserve que la racine d'état, l'état lui-même reste local à chaque nœud

Il y a ici un fait souvent traité avec ambiguïté : le Yellow Paper écrit noir sur blanc que l'état global n'est pas stocké sur la chaîne de blocs, qu'il est maintenu par chaque nœud pour son propre compte, et que la chaîne ne conserve que l'engagement stateRoot. Les transactions et les reçus sont publiés intégralement ; l'état ne l'est pas.

Cette répartition des rôles délimite de nombreuses frontières d'usage. Pour connaître le solde d'un compte à un instant donné, il n'existe que deux voies : rejouer soi-même l'intégralité des transactions historiques afin de recalculer l'état, ou demander à un nœud une preuve de Merkle vérifiable sous un stateRoot donné. Les clients légers empruntent la seconde : ils n'ont pas à conserver tout l'état, mais quelqu'un doit leur fournir les preuves, et une preuve indique seulement « quelle est cette valeur sous un certain stateRoot », sans pouvoir dire que ce stateRoot est le plus récent. Cela relève de la couche de consensus.

On comprend de la même façon la différence entre un nœud d'archive et un nœud élagué. L'état historique s'accumule sans cesse, et le coût disque d'un nœud qui conserve tout l'état historique croît continuellement ; un nœud qui ne garde que l'état récent occupe moins d'espace et démarre plus vite, au prix de l'impossibilité de répondre à une requête d'état sur un bloc historique quelconque. Ce qu'on appelle « données on-chain » se décompose, en ingénierie, en deux choses : la disponibilité des transactions et des reçus, et l'engagement de la racine d'état. Les confondre fera perdre en acuité lorsqu'il s'agira d'évaluer la disponibilité des données et l'inflation de l'état.

## La fonction de transition d'état : Ethereum spécifie une fonction

Le Yellow Paper définit la sémantique au niveau d'une transaction par une formule d'une ligne, notée :

`σ_{t+1} ≡ Υ(σ_t, T)`

où σ est l'état global, T une transaction et Υ la fonction de transition d'état. Sous une écriture plus lisible, cela donne `Y(S, T) → S'`. Le Yellow Paper souligne aussitôt que seules des transactions peuvent produire un changement d'état valide, et que les changements d'état invalides sont bien plus nombreux que les valides — par exemple réduire le solde d'un compte à partir de rien sans l'augmenter d'autant ailleurs.

Une fois cette idée comprise, on peut le formuler autrement : ce que le protocole Ethereum spécifie réellement, c'est « étant donné l'état courant et une transaction, quelle est la version suivante de l'état » ; quant à ce qui est « stocké sur la chaîne », ce n'est que l'entrée et la sortie de cette fonction. Il en va de même au niveau du bloc, où le Yellow Paper replie la même fonction le long de la séquence de transactions au moyen de Π :

`Π(σ, B) ≡ Υ(Υ(σ, T₀), T₁)…`

Le terme de machine à états ne prend corps qu'ici : l'état est σ, la règle de transition est Υ, et le bloc est le lot de transitions.

## Les coûts de vérification diffèrent entre modèle de registre et machine à états

Mettre les deux modèles côte à côte fait apparaître des différences qui portent sur la méthode de vérification, et pas seulement sur l'organisation des données.

Les entrées d'une transaction UTXO sont déclarées explicitement : chaque entrée précise quelle sortie de quelle transaction elle référence. Le validateur n'a qu'à consulter ces entrées précises dans l'ensemble des UTXO, sans avoir à comprendre une quelconque structure globale. La vérification des transactions Bitcoin se prête donc naturellement à la localisation, et des transactions sans lien peuvent être traitées indépendamment. Le modèle de comptes déclaratif (par exemple le message de transaction de Solana, qui liste à l'avance les adresses de comptes et fait référencer les instructions par index) suit la même logique.

Dans le modèle de comptes, l'ensemble lecture/écriture d'une transaction n'est pas inscrit dans la transaction. Un contrat peut lire n'importe quel slot de stockage de n'importe quelle adresse, et ce qu'il a lu ne se sait qu'une fois l'exécution terminée. Le validateur doit détenir une vue d'état identique à celle de l'exécution, faute de quoi le résultat est impossible à reproduire. Cette différence est le point de départ de toute la ligne technique qui suit : inférence des ensembles lecture/écriture, contrôle de concurrence optimiste, détection de conflits traitent tous du même fait — « on ne sait pas ce qu'une transaction va toucher ».

Ethereum a aussi tenté de se rapprocher du déclaratif. La liste d'accès (access list) introduite par l'EIP-2930 est un champ facultatif du nouveau type de transaction 0x01 : une transaction peut déclarer à l'avance les adresses et les slots de stockage qu'elle consultera, afin de les faire entrer dans les ensembles `accessed_addresses` et `accessed_storage_keys` et de bénéficier d'une remise. Le texte de l'EIP précise aussi que les accès hors liste restent autorisés, simplement plus chers. L'ensemble lecture/écriture n'est donc pas rendu obligatoire dans la transaction, et la conclusion ci-dessus n'en est pas modifiée.

## La place de l'EVM dans le protocole : la spécification qui dit « comment modifier l'état »

La machine virtuelle Ethereum (Ethereum Virtual Machine, EVM) est la spécification d'exécution concrète de Υ : une sémantique d'instructions, des règles de comptage du gaz et une sémantique d'exception et de terminaison. Le code d'un contrat est une suite d'octets, qui n'entre en exécution que lorsqu'une transaction ou un autre contrat le déclenche par un appel de message (message call).

L'EVM n'est pas l'équivalent du protocole Ethereum. Consensus, disponibilité des données et règlement relèvent chacun d'un autre système, ce qu'un article consacré à la cartographie de la scalabilité décompose en détail. L'EVM ne répond qu'à une question : face à un bytecode et à une entrée, comment calculer de façon déterministe les changements d'état et la valeur de retour. Il décide aussi de ce que ce calcul coûte, et les règles de tarification du gaz font elles-mêmes partie du consensus : elles peuvent être modifiées par un hard fork, l'EIP-2929 ayant par exemple fortement relevé le prix du premier accès à un compte ou à un slot de stockage.

Il reste un point de confusion fréquent : le modèle de machine de l'EVM ne se réduit pas à une « pile ». Chaque contexte d'exécution dispose d'une pile (stack), d'une mémoire (memory), d'un compteur de programme (program counter, PC) et d'un compteur de gaz, tandis que les variables persistantes du contrat aboutissent dans le stockage du compte via SLOAD/SSTORE. La pile, la mémoire et la boucle d'exécution font l'objet de l'article 0.4 ; les frontières entre les trois types de stockage, de l'article 0.5.

## Rejeu par l'ensemble du réseau : pourquoi le résultat doit être identique bit à bit

Ethereum n'a pas d'exécutant central. Chaque nœud complet exécute indépendamment le même lot de transactions, calcule son propre stateRoot, puis le consensus compare cette valeur. La moindre divergence dans un détail d'exécution se transforme immédiatement en échec de consensus ; la spécification doit donc descendre jusqu'à l'octet.

Quelques contraintes dures en découlent. L'arithmétique en virgule flottante n'est pas entrée dans le jeu d'instructions : il n'existe que de l'arithmétique sur des entiers de 256 bits. Les entrées externes telles que l'horloge murale, l'identifiant de processus ou un vrai tirage aléatoire ne peuvent pas apparaître sur le chemin de consensus ; l'horodatage et le numéro de bloc qu'un contrat peut lire proviennent de l'en-tête de bloc, donc de valeurs fixées par le consensus.

Le calcul doit en outre être mesurable et garanti de se terminer, sans quoi il serait impossible d'exécuter du code arbitraire en toute sécurité dans un bloc de taille finie. Le gaz est le moyen de réécrire le « problème de l'arrêt » en « on s'arrête quand le budget est épuisé » ; ses constantes de comptage relèvent donc de paramètres de consensus et non de réglages de performance. Le coût intrinsèque d'un virement ordinaire est de 21 000 gaz, chaque octet nul de calldata compte pour 4 gaz et chaque octet non nul pour 16 gaz, ces nombres étant fixés par la table de frais du Yellow Paper. Les modifier relève de la même catégorie de changement de protocole que modifier la taille des blocs : cela exige un hard fork ou un signal des validateurs, et non la décision d'un client isolé.

Le déterminisme a une autre conséquence facile à négliger : la spécification n'admet pas de « comportement indéfini ». Le Yellow Paper précise que DIV renvoie 0 lorsque le diviseur vaut 0, au lieu de lever une exception. Ce choix vise à faire produire à tous les clients le même résultat sur la même entrée illégale, indépendamment de toute préférence de conception de langage. Les tests unitaires des clients et la suite de tests de conformité execution-spec-tests servent précisément à verrouiller ce genre de comportements limites. La coexistence de plusieurs clients indépendants (geth, revm, evmone, etc.) est elle-même un moyen de contrôle du déterminisme : dès qu'une implémentation comprend différemment un cas limite, la chaîne se scinde, et les divergences doivent donc être détectées au stade des tests.

## Déterminisme et prédictibilité sont deux choses distinctes

Il faut distinguer deux niveaux d'incertitude. La couche d'exécution est déterministe : même bytecode, même entrée, même état, résultat nécessairement identique. Le résultat qu'obtient une transaction n'est pourtant pas déterminé par la seule couche d'exécution. Placer la même transaction à une position différente dans le bloc change l'état antérieur, donc éventuellement le résultat. Les gains du front-running et du sandwich viennent du droit d'ordonnancement ; la couche d'exécution, elle, reste déterministe.

Cette distinction est essentielle lorsqu'on évalue une solution d'exécution parallèle. Ce que la transformation parallèle doit préserver, c'est « un résultat d'exécution équivalent à un certain ordre sériel » ; quant à « un résultat indépendant du moment de soumission de la transaction », cet objectif n'a jamais tenu.

## L'EVM n'est pas une spécification figée

Prendre l'EVM pour un standard figé conduit à des erreurs de jugement en matière de compatibilité. La spécification évolue au fil des hard forks : PUSH0 (EIP-3855, Shanghai) ajoute à la machine à pile une instruction qui empile la constante 0 ; l'EIP-2929 a retarifé les accès froids et chauds ; la couche de transaction a introduit un plafond de 16 777 216 gaz par transaction (EIP-7825, livré avec Fusaka) ; l'EIP-7935 a porté la limite de gaz par bloc par défaut des clients à 60M.

Une affirmation de « compatibilité EVM » n'a donc de sens qu'accompagnée d'une hauteur de fork et d'un périmètre précis. Le degré de compatibilité respectif du bytecode, des contrats précompilés, de JSON-RPC et de l'outillage relève de l'article mentionné en lecture associée.

## Dans quelles conditions ce modèle devient un fardeau

La conception centrée sur l'état achète le calcul universel, et le prix se lit à trois endroits.

Un nœud complet doit conserver durablement une copie de l'état courant lisible à tout moment, dont la taille croît avec le nombre de comptes et d'entrées de stockage, ce qui relève continuellement le seuil matériel.

Pour qu'un nouveau nœud calcule lui-même le stateRoot, il doit rejouer les transactions historiques ou s'appuyer sur une synchronisation par instantané ; le coût de vérification est donc lié à la longueur de l'historique.

Comme chaque transaction peut toucher n'importe quel point de l'état global, une implémentation classique ne peut exécuter qu'une transaction à la fois : c'est la contrainte originelle que toutes les discussions ultérieures sur l'exécution parallèle auront à traiter.

À l'inverse, l'échec de l'analogie du registre n'est pas absolu. L'audit, l'indexation et la réconciliation continuent de traiter les données comme un flux d'événements, car les journaux des reçus sont justement conçus pour une consommation hors chaîne. Le seul écueil est de ne pas ériger cette perspective en modèle de protocole.

## Sources

- Ethereum Yellow Paper (Upsilon, Pi, le mode de stockage de l'état global, le quadruplet de compte, la sémantique de DIV, la table des frais de gaz) : https://ethereum.github.io/yellowpaper/paper.pdf
- ethereum.org, Ethereum Virtual Machine (EVM) : https://ethereum.org/developers/docs/evm/
- ethereum.org, Understanding the Yellow Paper's EVM Specifications : https://ethereum.org/developers/tutorials/yellow-paper-evm/
- Bitcoin Developer Guide, Transactions (un UTXO ne peut être dépensé qu'une fois, les entrées référencent des sorties précises) : https://developer.bitcoin.org/devguide/transactions.html
- Solana Docs, Transactions (le message de transaction liste à l'avance les adresses de comptes, et les instructions les référencent par index) : https://solana.com/docs/core/transactions
- ethereum.org, Nodes and clients (nœuds complets, nœuds d'archive et élagage de l'état ; seuls les nœuds d'archive conservent tout l'état historique) : https://ethereum.org/developers/docs/nodes-and-clients/
- EIP-2929, Gas cost increases for state access opcodes : https://eips.ethereum.org/EIPS/eip-2929
- EIP-2930, Optional access lists (champ facultatif des transactions de type 0x01 ; les accès hors liste restent autorisés, simplement plus chers) : https://eips.ethereum.org/EIPS/eip-2930
- EIP-3855, PUSH0 instruction : https://eips.ethereum.org/EIPS/eip-3855
- EIP-7825, Transaction Gas Limit Cap (16 777 216 gaz) : https://eips.ethereum.org/EIPS/eip-7825
- EIP-7935, Set default gas limit to 60M : https://eips.ethereum.org/EIPS/eip-7935
- Ethereum Foundation, Fusaka Mainnet Announcement : https://blog.ethereum.org/2025/11/06/fusaka-mainnet-announcement
- go-ethereum, core/vm/instructions.go (opDiv écrit 0 lorsque le diviseur vaut 0) : https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go
- ethereum/execution-spec-tests (suite de tests de conformité à la spécification) : https://github.com/ethereum/execution-spec-tests

## Pour aller plus loin

- Associés : [« Ce que signifie réellement la compatibilité EVM : bytecode, précompilations, JSON-RPC et outillage »](/fr/blog/evm-compatibility-explained)
- Associés : [« Cartographier la scalabilité des chaînes de blocs : ce que résolvent respectivement le parallélisme L1, les L2, le sharding et la DA »](/fr/blog/blockchain-scaling-map)
- Suivant : [« Pourquoi un EVM monothread plafonne le TPS : histoire de la congestion et modèle d'exécution »](/fr/blog/evm-single-thread-bottleneck)
