---
id: 26
title: "Estado global de un solo hilo: el punto de origen del cuello de botella de rendimiento de la EVM"
slug: 0-12-single-thread-global-state
date: 2026/09/29
summary: "El Yellow Paper de Ethereum escribe la transición de estado a nivel de bloque como una llamada anidada, así que el orden de evaluación forma parte de la especificación. De dónde viene la semántica de la ejecución serial transacción a transacción, por qué los conflictos de conjuntos de lectura/escritura no se pueden determinar de antemano, qué se compra con el determinismo y la simplicidad de la verificación, y qué propuestas responden de frente a este punto de origen son el hilo conductor a partir de aquí."
keywords: EVM de un solo hilo,estado global,conflicto de conjuntos de lectura/escritura,determinismo,lista de acceso a nivel de bloque
heroImage: /images/articles/photos/0-12-single-thread-global-state.jpg
---

La sección 2 del Yellow Paper describe Ethereum como una máquina de estados impulsada por transacciones. Las convenciones de notación de esa formalización son: σ es el estado global, T una transacción y Υ la función de transición de estado a nivel de transacción, que aplica una transacción al estado; B un bloque y Π la función de transición de estado a nivel de bloque, con las transacciones del bloque ordenadas como T₀, T₁… Sobre esa notación, el Yellow Paper da dos ecuaciones: la transición de estado de una sola transacción es `σ_{t+1} ≡ Υ(σ_t, T)` y la transición a nivel de bloque es `Π(σ, B) ≡ Υ(Υ(σ, T_0), T_1)…`. La segunda merece un momento de atención: escribe la ejecución del bloque entero como una llamada anidada de funciones, donde la entrada de la segunda transacción es el estado que queda tras ejecutar la primera.

El orden de evaluación forma parte, por tanto, de la especificación, y el cliente no tiene margen para elegirlo por su cuenta. La pregunta es por qué solo se puede definir así, qué se perdería si se permitiera evaluar dos transacciones a la vez y cómo esta elección encierra el rendimiento de la EVM en un solo hilo. Aquí solo se trata esa capa, es decir, el punto de origen del diseño.

## El orden está escrito en la especificación, no es libertad de planificación del cliente

La formulación del Yellow Paper acota lo que debe hacer la capa de ejecución: dado un estado padre y una secuencia de transacciones, aplicar la función de transición de estado una y otra vez hasta obtener un estado. Una de las condiciones de validez de la cabecera del bloque es que la raíz que resulta de plegar ese estado a través del trie sea igual al stateRoot de la cabecera. Una transacción termina de ejecutarse y su estado se escribe por completo antes de que la siguiente empiece a leer estado: esa relación de precedencia forma parte de la definición y no deja espacio para optimizaciones.

La capa de ejecución tampoco tiene derecho de ordenación. El orden de las transacciones lo fija la lista que produce la capa de consenso y la capa de ejecución solo evalúa en el orden dado. Este reparto evita que consenso y ejecución tengan que ponerse de acuerdo sobre «el resultado después de una ejecución entrelazada»: basta con acordar «qué transacciones se han incluido y en qué orden». Si un nodo reproduce el mismo bloque en otro orden, la raíz que obtiene no cuadra y su resultado se declara directamente erróneo.

```python
# Transición de estado a nivel de bloque: la entrada de la segunda transacción es la salida de la primera
state = parent_state
for tx in block.transactions:
    state = apply_transaction(state, tx)      # Υ(σ, T)
state = apply_withdrawals(state, block.withdrawals)  # Los retiros se ejecutan después de todas las transacciones
assert trie_root(state) == block.header.state_root
```

## Las restricciones duras del orden: nonce, saldo y gas acumulado

Antes de que el código del contrato se interprete, la capa de protocolo ya exige un orden. Entre las comprobaciones de validez inicial de una transacción que enumera la sección 6 del Yellow Paper hay una que obliga a que el nonce de la transacción sea igual al nonce actual de la cuenta emisora. Dos transacciones del mismo emisor tienen así un orden total impuesto por el protocolo: adelantar la segunda invalida la transacción de inmediato, en lugar de dar un resultado distinto. Otra comprobación exige que la cuenta emisora no tenga código desplegado (EIP-3607), lo que también supone leer el estado después de ejecutar una transacción.

El saldo y el gas también fijan el orden en esa misma capa. Antes de ejecutar cada transacción hay que comprobar que el saldo del emisor alcanza para cubrir el pago por adelantado, y ese saldo es el resultado de haber ejecutado la transacción anterior; el límite de gas del bloque es una restricción de nivel de bloque y el gas acumulado del recibo se suma a partir de las transacciones previas (en la parte de recibos de bloque, el Yellow Paper escribe el valor acumulado de la n-ésima transacción como la suma del acumulado anterior y el consumo de esta). Cuánto gas puede usar la transacción siguiente depende de cuánto se ha gastado ya.

Es decir, incluso sin tener en cuenta el almacenamiento de los contratos, la premisa de «estados sucesivos» ya se sostiene en las reglas del protocolo. La ejecución de contratos no hace más que añadir dependencias sobre una secuencia que ya viene encadenada.

## De dónde vienen los conflictos: los conjuntos de lectura/escritura se cruzan y solo se conocen a posteriori

Para decidir si dos transacciones pueden ejecutarse a la vez, lo habitual es comparar sus conjuntos de lectura/escritura (Read/Write Set), es decir, el conjunto de posiciones de estado que una transacción lee o escribe. Basta con que una posición escrita por una de ellas sea leída o escrita por la otra para que el orden de ejecución afecte al resultado: esas transacciones se llaman conflictivas.

Los conflictos son frecuentes en cargas reales. El intercambio en el pool de un creador de mercado automatizado (Automated Market Maker, AMM) reescribe las reservas y el acumulador de precios, y la liquidación de un préstamo que llega justo después necesita leer el precio del mismo pool: el orden de las dos transacciones decide directamente si la liquidación se dispara y quién asume la pérdida. Nadie tiene que fabricar esta dependencia a propósito; aparece en cuanto dos contratos comparten estado.

Lo más incómodo es que el ejecutor no ve los conjuntos de lectura/escritura. La EVM permite que una transacción llame a cualquier dirección y lea o escriba cualquier slot de almacenamiento, y tanto el destino de la llamada como el número de slot pueden calcularse en tiempo de ejecución:

```solidity
// El destino de la llamada y los parámetros se determinan en tiempo de ejecución; el análisis estático no puede dar el conjunto de lectura/escritura
function dispatch(bytes32 poolId, bytes calldata payload) external {
    address pool = pools[poolId];        // El destino viene del almacenamiento y puede ser cualquier contrato registrado
    (bool ok, ) = pool.call(payload);    // Qué slots se tocan depende de pool y de payload
    require(ok, "call failed");
}
```

En los tipos mapping, el número de slot se obtiene concatenando la clave con la posición del slot y aplicando keccak256; la clave puede ser una entrada de tiempo de ejecución, y la posición del slot tampoco es necesariamente fija en tiempo de compilación. Por eso «qué slots va a tocar esta transacción» no se puede leer del bytecode antes de ejecutarla: solo se observa con la ejecución real. La sección de motivación de EIP-7928 lo dice sin rodeos: sin saber de antemano a qué direcciones y slots de almacenamiento se va a acceder, la ejecución de transacciones no puede paralelizarse.

## Qué compra el hilo único: determinismo y verificación como reejecución

El beneficio de este diseño es concreto. Todos los nodos evalúan en el mismo orden y obtienen la misma raíz de estado; el verificador no necesita confiar en un planificador ni razonar sobre las posibles formas de entrelazar la ejecución: le basta con reproducir el mismo bloque con las mismas reglas y comparar el stateRoot de la cabecera. Que varios clientes independientes, en lenguajes distintos y sobre hardware distinto, coincidan en un mismo valor se debe precisamente a que este diseño comprime la incertidumbre en «entrada más orden».

La semántica de fallo también depende del orden. REVERT y la reversión por excepción se apoyan en instantáneas de estado registradas durante la ejecución para deshacer capa por capa, y la granularidad de la reversión es la pila de llamadas y la transacción; los reembolsos de gas, `cumulativeGasUsed` y el bit de estado del recibo solo tienen un significado único cuando el orden de las transacciones está fijado. Si se permitiera avanzar varias transacciones entrelazadas, habría que definir con una semántica completamente nueva eso de «lo ya ejecutado se revierte y lo posterior se conserva».

Así que la ejecución serial transacción a transacción es ciertamente un compromiso, pero lo que compra —determinismo verificable por toda la red y una forma de verificar que no necesita pruebas de corrección adicionales— es el fundamento mismo de una cadena pública; explicarlo como «pereza de implementación» no se sostiene.

## Los clientes sí aprovechan todos los núcleos, pero fuera de la semántica

Visto desde la ingeniería real, los clientes dominantes no desperdician la máquina. En el caso de Reth: la recuperación de firmas de transacciones se reparte en un grupo de hilos para ejecutarse en paralelo; el hash de las actualizaciones de estado y la construcción de la raíz de estado se encargan a tareas paralelas de trie disperso, donde varios workers se reparten la generación de pruebas y la lectura de nodos del trie, y si una tarea expira o falla se recurre al cálculo en serie; en la ruta de procesamiento de bloques también se ejecutan transacciones por anticipado en un grupo de hilos aparte, para precalentar la caché de la ejecución formal posterior.

La ejecución formal sigue siendo secuencial. En el flujo de procesamiento de bloques de Reth, la ruta predeterminada, la que no lleva una lista de acceso a nivel de bloque, hace una ejecución EVM secuencial en el orden del bloque y entrega las actualizaciones de estado como flujo de datos a la tarea de raíz de estado; los bloques que llevan BAL tienen además una rama de ejecución paralela, pero depende de la bifurcación Amsterdam y de la existencia de la BAL (es decir, de la ruta de lista de acceso a nivel de bloque que se trata más adelante). La ejecución de precalentamiento solo llena la caché y sus resultados no se confirman directamente. Esta frontera la deja el punto de origen del diseño: los núcleos pueden acelerar la verificación de firmas, el hashing, la generación de pruebas y el llenado de caché, pero no pueden acortar «la única cadena de evaluación que existe en la semántica».

Conviene aclarar de paso una idea: un solo hilo no significa que la máquina trabaje con un único núcleo, sino que en la semántica solo hay un orden de evaluación. Por eso la pregunta clave de la escalabilidad no es cuántos núcleos hay, sino «si esta cadena de evaluación puede ensancharse».

## El precio: el paralelismo se cede fuera del protocolo

La especificación fija el orden, así que si la capa de ejecución quiere avanzar transacciones con varios núcleos, la única dirección legítima es encontrar un entrelazado equivalente al orden normativo y hacer converger el resultado en el mismo estado. Eso exige conocer o descubrir antes los conjuntos de lectura/escritura: o bien los declara por adelantado quien origina la transacción o quien construye el bloque, o bien se hace un seguimiento dinámico en tiempo de ejecución y se gestionan los conflictos. Lo primero traslada la carga al protocolo y a la cadena de herramientas; lo segundo, al runtime y a las reversiones.

La premisa del estado global hace más visible el precio. El estado es un árbol compartido por todos los nodos y cada actualización de una transacción reescribe nodos a lo largo de la ruta de la hoja a la raíz, de modo que el acceso al estado forma parte por sí mismo de la ruta crítica. La E/S de disco y el ancho de banda de red pueden escalar horizontalmente; esta cadena de «tomar estado, calcular el resultado, escribir el estado de vuelta y tomar el estado de la transacción siguiente» no puede.

## La respuesta directa: declarar por adelantado los conjuntos de lectura/escritura

La respuesta directa a este punto de origen consiste en aportar la información que falta. La lista de acceso a nivel de bloque (Block-Level Access List, BAL) que propone EIP-7928 añade a la cabecera un campo `block_access_list_hash` que registra todas las cuentas y posiciones de almacenamiento a las que se accedió durante la ejecución del bloque, junto con su valor después de la ejecución. Con esta declaración, un cliente puede leer de disco en paralelo, validar transacciones en paralelo y calcular la raíz de estado en paralelo, e incluso actualizar el estado sin ejecutar.

El precio también está escrito en la propuesta. La lista de acceso tiene que generarse, normalmente después de la ejecución por quien construye el bloque, y otros nodos deben verificar que es correcta; el orden, la unicidad y el determinismo de las transacciones hay que volver a definirlos, porque la premisa del paralelismo es que las posiciones de las que depende cada transacción ya estén declaradas. El estado que figura en el cuerpo de EIP-7928 es en revisión por pares y aún no está cerrado; la actualización Glamsterdam ya ha incluido la BAL en el alcance de pruebas de la red de desarrollo, sin fecha fijada para su activación en la mainnet, y el avance puede seguirse en la página de hoja de ruta de Glamsterdam de ethereum.org, mientras que los detalles de la red de desarrollo los mantiene una página de seguimiento de terceros. No elimina el punto de origen del diseño: solo añade a la ejecución paralela una declaración previa que debe verificarse.

Por cierto, la lista de acceso a nivel de transacción que introdujo EIP-2930 es opcional: la especificación no obliga a la transacción a declarar qué cuentas y slots va a visitar, así que no constituye una premisa de paralelismo sobre la que se pueda confiar. Esa es la razón de que EIP-7928 quiera elevarla al nivel de bloque y hacer su registro obligatorio.

## Contraejemplos y límites: el orden existe, los conflictos no siempre aparecen

Explicar bien el punto de origen del diseño exige también señalar sus límites. El orden es obligatorio, pero los conflictos dependen de la carga. Transacciones como los pagos por lotes, la liquidación y el reparto de airdrops solo tocan en su mayoría el saldo de su respectivo destinatario, y sus conjuntos de lectura/escritura apenas se solapan, así que en teoría podrían paralelizarse en gran medida; en cambio, cargas como los pools de AMM populares, las liquidaciones del mercado de préstamos y las acuñaciones masivas de NFT tienen conjuntos de lectura/escritura muy solapados, y el espacio de paralelismo se lo come el propio conflicto. Un mismo modelo de ejecución se comporta de forma muy distinta según la carga, de modo que al hablar de beneficios del paralelismo hay que describir el perfil de carga y la tasa de conflicto.

Otro límite está en dónde recae el determinismo. La ejecución paralela no elimina la exigencia de determinismo: relaja el objeto que debe conservarse, de un orden de evaluación real a una clase de ejecuciones serializables equivalentes a ese orden. El costo de la detección de conflictos, la verificación y la reejecución selectiva vuelve a recaer sobre el rendimiento, así que el paralelismo se parece más a reducir el tramo serial a las rutas con conflicto que a eliminar la serialidad por completo.

Hay una premisa más que se pasa por alto con facilidad: por mucho que se paralelice la fase de ejecución, al final todo tiene que converger en el mismo árbol de estado global y en la misma raíz. Las transacciones de las rutas con conflicto deben reejecutarse en el orden normativo, y el acceso al estado entre fragmentos exige coordinación adicional. Es también una restricción de la que no se puede prescindir cuando más adelante se hable de sharding de estado.

## Reparto de tareas: el punto de origen está aquí; el techo y la congestión histórica, en el otro artículo

Aquí solo hay que responder «por qué es obligatorio serializar transacción a transacción y qué cuesta». Cuán alto es el techo de rendimiento que impone esta frontera, cómo lo han puesto al descubierto una y otra vez las congestiones desde 2017 y por qué los ajustes del límite de gas y EIP-1559 no pueden cambiar el modelo de ejecución pertenecen al ámbito del ya publicado «Por qué una EVM de un solo hilo limita el TPS: historia de la congestión y el modelo de ejecución»; sus datos y su proceso de reforma no se repiten aquí. Los dos artículos forman juntos una cadena completa: primero se aclara el punto de origen y después se mide el techo.

## Fuentes

- [Ethereum Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf): la función de transición de estado de la sección 2 (ecuación 1) y la definición anidada a nivel de bloque (ecuación 2), la validez global del capítulo 4, la validez inicial de las transacciones de la sección 6 (nonce, saldo y EIP-3607) y la definición del gas acumulado del recibo de bloque (ecuación 186); la explicación del espacio de prueba del apéndice D.1.
- [EIP-7928: Block-Level Access Lists](https://eips.ethereum.org/EIPS/eip-7928) (Review, creado el 2025-03-31): la motivación de que la ejecución no puede paralelizarse sin conocer los conjuntos de lectura/escritura, el campo `block_access_list_hash`, los requisitos de orden y determinismo y el problema de que la lista de acceso de EIP-2930 no sea obligatoria.
- [EIP-3607](https://eips.ethereum.org/EIPS/eip-3607): rechaza las transacciones procedentes de cuentas con código desplegado.
- [ethereum.org: Glamsterdam](https://ethereum.org/roadmap/glamsterdam/): el criterio de progreso según el cual la BAL ya está incluida en el alcance de pruebas de la red de desarrollo de Glamsterdam, sin fecha fijada para la mainnet.
- [reth: documentación de stages](https://github.com/paradigmxyz/reth/blob/main/docs/crates/stages.md): el reparto de responsabilidades entre SenderRecoveryStage y ExecutionStage.
- [reth sender_recovery.rs](https://github.com/paradigmxyz/reth/blob/main/crates/stages/stages/src/stages/sender_recovery.rs): la recuperación de firmas se ejecuta en paralelo en el grupo de hilos de rayon.
- [código fuente de reth_trie_parallel::state_root_task](https://github.com/paradigmxyz/reth/blob/main/crates/trie/parallel/src/state_root_task.rs): la tarea de raíz de estado recibe un gancho de ejecución (correspondiente a la ejecución serial) o un flujo de actualizaciones hasheadas.
- [reth_engine_tree::tree::state_root_strategy](https://github.com/paradigmxyz/reth/blob/main/crates/engine/tree/src/tree/state_root_strategy/mod.rs): cuando una tarea de trie disperso expira o falla, se recurre al cálculo de la raíz de estado en serie.
- [código fuente de reth payload_validator](https://github.com/paradigmxyz/reth/blob/main/crates/engine/tree/src/tree/payload_validator.rs) y [código fuente de payload_processor::prewarm](https://github.com/paradigmxyz/reth/blob/main/crates/engine/tree/src/tree/payload_processor/prewarm.rs): ejecución EVM secuencial combinada con precalentamiento de preejecución en paralelo y tareas de raíz de estado en paralelo, y la rama de ejecución paralela cuando se lleva BAL.

## Lecturas relacionadas

- Siguiente: [Por qué una EVM de un solo hilo limita el TPS: historia de la congestión y el modelo de ejecución](/es/blog/evm-single-thread-bottleneck)
- Relacionado: [Tres caminos hacia la ejecución paralela: planificación determinista, OCC optimista y modelos de objetos](/es/blog/parallel-execution-approaches)
- Relacionado: [Introducción al control de concurrencia optimista (OCC): de las bases de datos a la ejecución en cadena](/es/blog/optimistic-concurrency-control-intro)
