Transferir 100 USDT (un token ERC-20) en cadena no añade una entrada del tipo «A pagó 100 USDT a B». Lo que ocurre en realidad es que se reescribe el valor del mapeo balanceOf del contrato: quien llama resta 100 y quien recibe suma 100; en qué slots de almacenamiento acaba recayendo esa reescritura lo deciden la forma del contrato y el compilador, y el protocolo solo impone qué devuelve balanceOf. Al mismo tiempo, se añade un log de eventos al recibo de la transacción; el log es un subproducto que consumen los indexadores fuera de cadena, y la verdad del saldo está únicamente en el valor actual del almacenamiento.
La analogía del libro contable falla aquí. Un libro contable registra lo que ya ha ocurrido y el lector acumula el historial para extraer conclusiones; Ethereum guarda un «mundo actual» que se sobrescribe continuamente, y las transacciones solo lo empujan a la versión siguiente. Para responder «qué es realmente la EVM» hay que definir primero con precisión ese «mundo actual» y, después, quién se encarga de reescribirlo.
El modelo de libro contable de Bitcoin basta porque su estructura de estado está muy limitada
«Libro contable» es una descripción exacta para Bitcoin. La entrada de una transacción referencia la salida de alguna transacción anterior, y esa salida permanece como salida de transacción no gastada (unspent transaction output, UTXO) hasta que otra entrada la gasta. Un UTXO solo puede gastarse una vez, así que el saldo que muestra una cartera es en realidad la suma de varios UTXO.
Bitcoin no carece de estado: el conjunto de UTXO es el estado. Pero la estructura de ese estado está muy limitada. Es un montón de «monedas» que solo pueden crearse y gastarse enteras, no hay un almacén clave-valor genérico y los scripts tampoco guardan variables que persistan entre transacciones. Por eso «libro contable más conjunto de salidas no gastadas» basta para describir el sistema completo: verificar una transacción solo exige comprobar que la salida referenciada existe y que el script de condiciones se cumple, sin consultar ninguna «variable interna de contrato».
Si Ethereum quiere que un contrato mantenga un mapeo de lectura y escritura libres, ese mapeo ya no puede tratarse como un detalle de implementación. Que una transferencia ERC-20 sea válida depende de cuál sea en ese momento el slot de saldo de quien llama en el almacenamiento del contrato. El estado asciende así de «conjunto que se mantiene de paso al validar» a objeto de primera clase sobre el que el protocolo debe comprometerse.
El estado global: comprimir «todo lo que hay ahora mismo» en 32 bytes
El Yellow Paper de Ethereum define el estado global (world state) como un mapeo de direcciones a cuentas. Cada cuenta es una cuaterna:
- nonce: número de transacciones que ya ha enviado esa dirección, o número de contratos que ya ha creado esa cuenta de contrato;
- balance: el saldo, valorado en wei;
- storageRoot: el hash raíz del árbol de Merkle Patricia (MPT) del contenido del almacenamiento de esa cuenta;
- codeHash: el hash Keccak-256 del código de esa cuenta; el código en sí se guarda en la base de datos de estado bajo ese hash.
Aquí conviene distinguir dos cosas. Lo que compromete storageRoot es el espacio clave-valor de la propia cuenta, y ahí acaban todas las variables de estado que declara el autor del contrato; codeHash apunta al programa, que una vez desplegado no lo reescribe ninguna transacción. Comprometer por separado «datos» y «código» con dos campos es la premisa que hará falta una y otra vez al hablar después de conjuntos de lectura/escritura y detección de conflictos (el 0.5 de esta serie, «0.5 Tres clases de almacenamiento que no conviene confundir: Memory, Storage y Transient Storage», desarrollará a qué pertenece cada uno de esos tres tipos de almacenamiento y cuánto vive cada uno).
Todas las cuentas se organizan además en un árbol de Merkle Patricia global cuyo hash raíz es el stateRoot, que se escribe en la cabecera del bloque. Es decir, «cómo es Ethereum entero en este instante» puede comprimirse en 32 bytes, y toda la red solo necesita ponerse de acuerdo sobre esos 32 bytes.
En cadena se guarda la raíz de estado; el estado en sí se queda en cada nodo
Hay aquí un hecho que suele tratarse con vaguedad: el Yellow Paper dice con claridad que el estado global no se almacena en la cadena de bloques, sino que lo mantiene cada nodo por su cuenta, y en cadena solo se conserva el compromiso del stateRoot. Las transacciones y los recibos se publican enteros; el estado, no.
Este reparto determina muchos límites de uso. Para saber el saldo de una cuenta en este instante solo hay dos caminos: reproducir uno mismo todo el historial de transacciones y calcular el estado, o pedir a algún nodo una prueba de Merkle verificable bajo el stateRoot. Los clientes ligeros toman el segundo camino: no necesitan guardar todo el estado, pero sí que alguien les proporcione la prueba, y además la prueba solo puede decir «qué valor tiene esto bajo un stateRoot dado», no que ese stateRoot sea el más reciente. Lo segundo es responsabilidad de la capa de consenso.
De aquí se entiende también la diferencia entre un nodo de archivo y un nodo podado. El estado histórico se acumula sin parar, así que el costo de disco de un nodo que guarda todo el estado histórico crece continuamente; un nodo que solo conserva el estado reciente ocupa menos y arranca más rápido, a cambio de no poder responder consultas de estado sobre cualquier bloque histórico. Eso que llamamos «datos en cadena» hay que descomponerlo en ingeniería en dos cosas: la disponibilidad de transacciones y recibos, y el compromiso de la raíz de estado. Mezclarlas hace perder capacidad de discriminación al evaluar después la disponibilidad de datos y la inflación del estado.
La función de transición de estado: lo que Ethereum especifica es una función
El Yellow Paper define la semántica a nivel de transacción con una sola línea, que se escribe:
σ_{t+1} ≡ Υ(σ_t, T)
Aquí σ es el estado global, T una transacción y Υ la función de transición de estado. Escrito de forma más legible, Y(S, T) → S'. El Yellow Paper subraya a continuación que los cambios de estado válidos solo pueden producirse mediante transacciones, y que los cambios de estado inválidos son muchos más que los válidos, por ejemplo reducir el saldo de una cuenta de la nada sin aumentarlo en la misma cantidad en otro sitio.
Entendido esto, se puede decir de otro modo: lo que de verdad especifica el protocolo de Ethereum es «dados el estado actual y una transacción, cuál es la versión siguiente del estado»; eso de «qué se guarda en cadena» no es más que la entrada y la salida de esa función. A nivel de bloque ocurre lo mismo: el Yellow Paper pliega la misma función a lo largo de la secuencia de transacciones con Π:
Π(σ, B) ≡ Υ(Υ(σ, T₀), T₁)…
Aquí es donde por fin aterriza la expresión «máquina de estados»: el estado es σ, la regla de transición es Υ y el bloque es el lote de transiciones.
El modelo de libro contable y el de máquina de estados tienen costos de verificación distintos
Si se comparan los dos modelos, la diferencia acaba recayendo en cómo se verifica, y no solo en cómo se organizan los datos.
Las entradas de una transacción UTXO se declaran de forma explícita: cada entrada especifica a qué salida de qué transacción referencia. El verificador solo tiene que buscar esas entradas concretas en el conjunto de UTXO y no necesita comprender ninguna estructura global. Por eso la verificación de transacciones en Bitcoin puede localizarse de forma natural, y transacciones sin relación entre sí pueden procesarse de manera independiente. Los modelos de cuentas declarativos (por ejemplo, el mensaje de transacción de Solana, que enumera de antemano las direcciones de cuenta y cuyas instrucciones las referencian por índice) siguen la misma idea.
En el modelo de cuentas, el conjunto de lectura y escritura de una transacción no viene escrito en la transacción. Un contrato puede leer cualquier slot de almacenamiento de cualquier dirección, y solo al terminar la ejecución se sabe qué leyó. El verificador tiene que disponer de una vista del estado idéntica a la de la ejecución; de lo contrario, el resultado no puede reproducirse. Esta diferencia es el punto de partida de toda la línea técnica posterior: la inferencia de conjuntos de lectura/escritura, el control de concurrencia optimista y la detección de conflictos tratan justamente eso de «no saber qué va a tocar una transacción».
Ethereum también intentó acercarse a lo declarativo. La lista de acceso (access list) que introduce EIP-2930 es un campo opcional del nuevo tipo de transacción 0x01: la transacción puede declarar de antemano las direcciones y los slots de almacenamiento a los que va a acceder, y añadirlos a los conjuntos accessed_addresses y accessed_storage_keys para obtener un descuento. El texto del EIP especifica a la vez que los accesos fuera de la lista siguen permitidos, solo que más caros. El conjunto de lectura/escritura no queda por tanto obligado a escribirse en la transacción, y la conclusión anterior no cambia por ello.
El lugar de la EVM en el protocolo: la especificación que dicta «cómo se cambia el estado»
La máquina virtual de Ethereum (Ethereum Virtual Machine, EVM) es la especificación concreta de ejecución de Υ, y abarca un conjunto de semánticas de instrucciones, un conjunto de reglas de medición de gas y un conjunto de semánticas de excepción y terminación. El código de un contrato es una cadena de bytes y solo entra en ejecución cuando lo dispara una transacción u otro contrato mediante una llamada de mensaje (message call).
La EVM no equivale al protocolo de Ethereum. El consenso, la disponibilidad de datos y la liquidación son cada uno otra capa del sistema, y el artículo del mapa de escalabilidad los desglosa específicamente. La EVM responde a una sola cosa: ante un fragmento de bytecode y una entrada, cómo calcular de forma determinista el cambio de estado y el valor devuelto. También decide cuánto cuesta ese cálculo, y las propias reglas de tarificación del gas forman parte del consenso y pueden modificarse con una bifurcación dura; EIP-2929, que elevó notablemente el precio del primer acceso a una cuenta y a un slot de almacenamiento, es un ejemplo.
Hay otro punto que se confunde con facilidad: el modelo de máquina de la EVM no se reduce a la «pila». Cada contexto de ejecución tiene pila (stack), memoria (memory), contador de programa (program counter, PC) y contador de gas, mientras que las variables persistentes del contrato caen en el almacenamiento de la cuenta mediante SLOAD/SSTORE. La pila, la memoria y el bucle de ejecución son el tema de 0.4; las fronteras entre los tres tipos de almacenamiento son el tema de 0.5.
Repetición en toda la red: por qué el resultado debe coincidir bit a bit
Ethereum no tiene un ejecutor central. Cada nodo completo ejecuta por su cuenta el mismo lote de transacciones, calcula su propio stateRoot y luego compara ese valor mediante consenso. Cualquier discrepancia en un detalle de ejecución se convierte directamente en un fallo de consenso, así que la especificación tiene que ser precisa hasta el byte.
De ahí se derivan varias restricciones duras. La aritmética de coma flotante no entró en el conjunto de instrucciones: solo hay aritmética de enteros de 256 bits. Entradas externas como el reloj de pared, el ID de proceso o números aleatorios reales no pueden aparecer en la ruta de consenso; la marca de tiempo y el número de bloque que un contrato puede leer vienen de la cabecera del bloque y son valores fijados por consenso.
El cálculo debe además ser medible y terminar necesariamente; de lo contrario no se podría ejecutar código arbitrario con seguridad dentro de bloques finitos. El gas es la forma de reescribir el «problema de la parada» como «se detiene cuando agota el presupuesto», y por eso sus constantes de medición son parámetros de consenso y no elementos de ajuste del rendimiento. El costo intrínseco de una transferencia normal es de 21000 gas; en el calldata, cada byte cero cuenta 4 gas y cada byte distinto de cero, 16 gas; estas cifras las fija la tabla de tarifas del Yellow Paper. Cambiarlas pertenece a la misma clase de modificación de protocolo que cambiar el tamaño de bloque: requiere una bifurcación dura o la señal de los validadores, y ningún cliente puede decidirlo por su cuenta.
La determinación tiene otra consecuencia que se pasa por alto: la especificación no permite la «conducta indefinida». El Yellow Paper especifica que DIV devuelve 0 cuando el divisor es 0, en lugar de lanzar una excepción. La razón de esta elección es que todos los clientes obtengan el mismo resultado ante la misma entrada inválida, y no tiene que ver con preferencias de diseño de lenguajes. Las pruebas unitarias de los clientes y la suite de pruebas de especificación execution-spec-tests sirven precisamente para fijar este tipo de comportamientos de frontera. La coexistencia de varios clientes independientes (geth, revm, evmone, etc.) es en sí misma un medio de validar la determinación: en cuanto una implementación entiende de forma distinta un caso límite, la cadena se bifurca, así que las discrepancias tienen que detectarse en la fase de pruebas.
Determinación y previsibilidad son dos cosas distintas
Conviene distinguir dos niveles de incertidumbre. La capa de ejecución es determinista: el mismo bytecode, la misma entrada y el mismo estado dan necesariamente el mismo resultado. Pero el resultado que obtiene una transacción no lo decide solo la capa de ejecución. La misma transacción colocada en otra posición del bloque tiene un estado previo distinto y su resultado puede cambiar. Los beneficios del front-running y de los ataques sándwich vienen del derecho de ordenación; la capa de ejecución sigue siendo determinista.
Esta distinción es clave al evaluar propuestas de ejecución paralela. Lo que una reforma paralela debe preservar es que «el resultado de la ejecución equivalga a cierto orden serial»; en cuanto a que «el resultado sea independiente del momento en que se envía la transacción», ese objetivo nunca ha sido viable.
La EVM no es una especificación congelada
Tomar la EVM por un estándar fijo lleva a diagnosticar mal la compatibilidad. La especificación evoluciona con las bifurcaciones duras: PUSH0 (EIP-3855, Shanghai) añade a la máquina de pila una instrucción que empuja la constante 0; EIP-2929 volvió a tarificar los accesos fríos y calientes; la capa de transacción introdujo un límite de 16,777,216 gas por transacción (EIP-7825, activado con Fusaka); EIP-7935 elevó a 60M el límite de gas por bloque predeterminado de los clientes.
Por eso la frase «compatible con EVM» solo tiene sentido si incluye la altura de la bifurcación y el alcance concreto. Hasta qué punto son compatibles el bytecode, los contratos precompilados, JSON-RPC y las herramientas es el ámbito del artículo de las lecturas relacionadas.
En qué condiciones este modelo se convierte en una carga
El diseño centrado en el estado compra computación general; el precio aparece en tres sitios.
Un nodo completo debe conservar a largo plazo una copia del estado actual legible en cualquier momento, y su tamaño crece con las cuentas y las entradas de almacenamiento, lo que eleva sin cesar el umbral de hardware.
Un nodo nuevo que quiera calcular por su cuenta el stateRoot tiene que reproducir las transacciones históricas o depender de la sincronización por instantáneas, de modo que el costo de verificación queda ligado a la longitud del historial.
Como cada transacción puede tocar cualquier punto del estado global, las implementaciones clásicas solo pueden ejecutar una por una, en serie; esta es la restricción original que habrán de abordar todas las discusiones posteriores sobre ejecución paralela.
A la inversa, el fracaso de la analogía del libro contable tampoco es absoluto. Escenarios como la auditoría, la indexación y la conciliación siguen tratando los datos como un flujo de eventos, porque los logs de los recibos están diseñados precisamente para su consumo fuera de cadena. El problema está solo en no tomar esa perspectiva por el modelo del protocolo.
Fuentes
- Ethereum Yellow Paper (Upsilon, Pi, dónde se guarda el estado global, la cuaterna de cuenta, la semántica de DIV, la tabla de tarifas de gas): 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 solo puede gastarse una vez; las entradas referencian salidas concretas): https://developer.bitcoin.org/devguide/transactions.html
- Solana Docs, Transactions (el mensaje de transacción enumera de antemano las direcciones de cuenta y las instrucciones las referencian por índice): https://solana.com/docs/core/transactions
- ethereum.org, Nodes and clients (nodos completos, nodos de archivo y poda de estado; solo los nodos de archivo conservan todo el estado histórico): 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 (campo opcional de las transacciones de tipo 0x01; los accesos fuera de la lista siguen permitidos, solo que más caros): 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 gas): 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 escribe 0 cuando el divisor es 0): https://github.com/ethereum/go-ethereum/blob/master/core/vm/instructions.go
- ethereum/execution-spec-tests (suite de pruebas de conformidad con la especificación): https://github.com/ethereum/execution-spec-tests
Lecturas relacionadas
- Relacionado: Qué significa realmente la compatibilidad con EVM: bytecode, precompilados, JSON-RPC y herramientas
- Relacionado: Mapear la escalabilidad de la cadena de bloques: qué resuelve cada enfoque —paralelismo en L1, L2, sharding y disponibilidad de datos
- Siguiente: Por qué una EVM de un solo hilo limita el TPS: historia de la congestión y el modelo de ejecución
