Ethereum: de historia a labs

Artículo 5 de 48

10%

completado al llegar aquí

Sistemas distribuidosNota de investigación13 min de lectura

Berlin, London y EIP-1559: gas deja de ser solo subasta

Berlin y London paso a paso: accesos fríos y calientes, transacciones tipadas, access lists y cómo EIP-1559 divide la fee entre quema y propina.

Ruta Ethereum: 0/48

Antes de agosto de 2021, enviar 1 ETH era una puja. Fijabas un precio por unidad de gas, por ejemplo 200 gwei, y pagabas 21,000 × 200 = 4,200,000 gwei (0,0042 ETH) si un minero la aceptaba. Si la red se congestionaba, la transacción quedaba esperando y no sabías cuánto subir la oferta. Después de London, la misma transferencia paga una tarifa base que fija el protocolo y una propina que eliges tú.

Ese cambio llegó con dos forks de 2021. Berlin, el 15 de abril en el bloque 12,244,000, cambió cómo se cobra el acceso al estado y preparó el formato de transacciones. London, el 5 de agosto en el bloque 12,965,000, rediseñó el mercado de fees.

Brújula de lectura

Actor, evidencia y estado

Con EIP-1559 el precio de una transacción ya no lo decide una sola parte.

Actor

Protocolo y emisor

El protocolo fija la base fee de cada bloque; el emisor fija su máximo y su propina.

Evidencia

Bloque y receipt

El bloque trae baseFeePerGas; el receipt trae gasUsed y effectiveGasPrice.

Estado

Saldo del emisor

La base fee se quema, la propina va al productor del bloque y el resto del máximo no se cobra.

Preguntas guía

  • ¿Por qué leer un slot de storage cuesta 2100 de gas la primera vez y 100 la segunda dentro de la misma transacción?
  • ¿Qué es un tipo de transacción y por qué hizo falta un sobre común para todos?
  • Si declaras un máximo de 30 gwei y la base fee es 10, ¿cuánto pagas?
  • ¿Qué parte de la fee recibe quien produce el bloque y qué parte desaparece?

Conexiones dentro de la serie

Historia de Berlin: el acceso al estado se paga según temperatura

Berlin incluyó cuatro EIPs: EIP-2565 (abarata la exponenciación modular), EIP-2718, EIP-2929 y EIP-2930. Los tres últimos trabajan juntos.

El problema de fondo venía de 2016. EIP-2929 recuerda que, en los ataques de denegación de servicio de ese año, una estrategia eficaz fue enviar transacciones que tocaban muchas cuentas. El aumento de costos de Tangerine Whistle no bastó: leer una cuenta del disco seguía siendo barato en gas y caro en tiempo para el nodo.

La solución distingue accesos fríos y calientes. Durante una transacción, el cliente mantiene dos conjuntos: direcciones accedidas y pares (dirección, slot) accedidos. El primer SLOAD de un slot cuesta 2100; los siguientes, 100. El primer acceso a una cuenta con CALL, BALANCE o EXTCODE* cuesta 2600; los siguientes, 100. El emisor, el destinatario y los precompilados empiezan calientes.

La idea refleja algo real: el primer acceso obliga al nodo a buscar el dato en la base de datos; los siguientes ya están en memoria. Para un desarrollador, el efecto práctico es que el orden de los accesos y la cantidad de cuentas distintas que toca una transacción pasan a importar en el costo.

EIP-2718 definió un sobre de transacción tipada: una transacción nueva es TransactionType || TransactionPayload, donde el tipo es un byte entre 0 y 0x7f. Las transacciones anteriores siguen siendo válidas como "legacy". Así se pueden añadir formatos nuevos sin ambigüedad.

El primer tipo nuevo fue el 0x01 de EIP-2930: una transacción que declara de antemano una access list, la lista de direcciones y slots que va a tocar. Esos elementos entran calientes, con un costo por adelantado de 2400 de gas por dirección y 1900 por slot.

Traza de ejecución de una fee con EIP-1559

EIP-1559 introduce la transacción tipo 0x02. En lugar de un único gasPrice, trae dos campos: max_fee_per_gas, lo máximo que aceptas pagar por unidad de gas, y max_priority_fee_per_gas, la propina máxima para quien produce el bloque. Cada bloque, a su vez, trae una base_fee_per_gas que calcula el protocolo.

Figura 1

Cómo se reparte la fee de una transacción tipo 2

Fuentes:

[5]
Cómo se reparte la fee de una transacción tipo 2
Cómo leer esta figura

Diagrama de flujo. Arriba a la izquierda, la transacción tipo 2 con los campos max_fee_per_gas y max_priority_fee_per_gas. Debajo, el bloque con base_fee_per_gas fijado por el protocolo. Ambas cajas llevan a una caja central con tres fórmulas: p igual al mínimo entre la propina y el máximo menos la base; el precio efectivo igual a la base más p; y la condición de que la transacción solo es incluible si el máximo es mayor o igual que la base. De la caja central salen tres flechas: la base por el gas usado se quema; p por el gas usado va al productor del bloque; y el máximo menos el precio efectivo, por el gas, no se cobra.

Visual original de TrautsLab basado en la implementación de referencia de EIP-1559. La propina efectiva es la menor entre la propina declarada y lo que queda del máximo tras pagar la base fee.

La regla en tres pasos, con el ejemplo de la documentación oficial: una transferencia usa 21,000 de gas, la base fee del bloque es 10 gwei y la propina es 2 gwei.

  1. La transacción entra al bloque solo si su máximo es al menos la base fee.
  2. La propina efectiva es min(propina, máximo − base). Con un máximo de 30 gwei, es 2 gwei.
  3. El precio efectivo es base más propina: 12 gwei. Pagas 21,000 × 12 = 252,000 gwei. De eso, 21,000 × 10 = 210,000 gwei se queman: desaparecen del suministro. Los 42,000 gwei de propina van al productor del bloque. La diferencia hasta tu máximo nunca se cobra.

El siguiente código reproduce esas reglas. Es ilustrativo: sigue la implementación de referencia del EIP, pero omite la validación del resto del bloque.

python
# Ilustrativo: reglas de EIP-1559 tomadas de la implementación de referencia
# del EIP y reducidas a lo esencial. Todos los valores están en wei.
ELASTICITY_MULTIPLIER = 2
BASE_FEE_MAX_CHANGE_DENOMINATOR = 8
 
 
def next_base_fee(parent_base_fee: int, parent_gas_used: int, parent_gas_limit: int) -> int:
    target = parent_gas_limit // ELASTICITY_MULTIPLIER
    if parent_gas_used == target:
        return parent_base_fee
    if parent_gas_used > target:
        delta = parent_base_fee * (parent_gas_used - target) // target // BASE_FEE_MAX_CHANGE_DENOMINATOR
        return parent_base_fee + max(delta, 1)
    delta = parent_base_fee * (target - parent_gas_used) // target // BASE_FEE_MAX_CHANGE_DENOMINATOR
    return parent_base_fee - delta
 
 
def split_fee(base_fee: int, max_fee: int, max_priority_fee: int, gas_used: int) -> dict:
    assert max_fee >= base_fee, "max_fee menor que la base fee: no entra en este bloque"
    priority = min(max_priority_fee, max_fee - base_fee)
    return {
        "quemado": base_fee * gas_used,
        "al_productor": priority * gas_used,
        "precio_efectivo": base_fee + priority,
    }
 
 
GWEI = 10**9
print(split_fee(10 * GWEI, 30 * GWEI, 2 * GWEI, 21_000))
print(next_base_fee(100 * GWEI, 30_000_000, 30_000_000) / GWEI)  # bloque lleno
print(next_base_fee(100 * GWEI, 0, 30_000_000) / GWEI)  # bloque vacío
Texto
{'quemado': 210000000000000, 'al_productor': 42000000000000, 'precio_efectivo': 12000000000}
112.5
87.5

Invariante del mercado de fees: la base fee sigue al objetivo

El protocolo define un objetivo de gas por bloque igual a la mitad del límite de gas. Si el bloque padre usó más que el objetivo, la base fee sube; si usó menos, baja. El cambio máximo por bloque es un octavo, es decir, un 12,5 %. Un bloque lleno sube la base fee de 100 a 112,5 gwei; uno vacío la baja a 87,5.

Esto tiene dos consecuencias. Los bloques pasan a tener tamaño variable: pueden llegar al doble del objetivo durante un pico breve, y la base fee creciente reduce la demanda hasta volver al objetivo. Y una wallet puede calcular con certeza la base fee máxima del bloque siguiente, porque el salto está acotado. La documentación oficial usa justamente este argumento: la base fee del próximo bloque nunca supera 1,125 veces la actual.

El EIP explica por qué la base fee se quema en lugar de pagarse al minero. Si la cobrara quien produce el bloque, tendría un incentivo para manipularla y cobrar más a los usuarios. Al quemarla, solo la propina queda como incentivo para incluir transacciones, y solo ETH sirve para pagar el uso de la red.

London trajo además BASEFEE (EIP-3198), un opcode que permite a un contrato leer la base fee del bloque actual.

Riesgo operativo: refunds, prefijo 0xEF y lo que EIP-1559 no promete

London también recortó los reembolsos de gas. Antes, liberar storage o destruir un contrato devolvía gas al final de la transacción, hasta la mitad del gas usado. El EIP explica que ese mecanismo dio lugar a GasToken: usar slots de storage como una "batería" que se llena cuando el gas es barato y se vacía cuando es caro, a costa de inflar el estado. EIP-3529 eliminó el reembolso de SELFDESTRUCT, redujo el de SSTORE y bajó el tope a un quinto del gas usado. Cualquier contrato cuyo modelo económico dependiera de esos reembolsos dejó de funcionar como se diseñó.

EIP-3541 prohibió desplegar código nuevo que empiece con el byte 0xEF. El código ya desplegado no se vio afectado. La motivación es reservar ese prefijo para un futuro formato de contratos validado al desplegar.

Diagnóstico

Me cobró mucho gas: tres preguntas distintas

Reglas de decisión rápida

  • ¿La base fee era alta en ese bloque?

    baseFeePerGas del bloque muy por encima de lo habitual.

    → Es congestión: la red estaba sobre el objetivo varios bloques seguidos. No es un fallo del contrato.

  • ¿La propina era exagerada?

    effectiveGasPrice muy por encima de la base fee.

    → Revisa cómo la wallet o el backend fija max_priority_fee_per_gas.

  • ¿El contrato usó mucho gas?

    gasUsed del receipt alto para la operación.

    → Mira accesos fríos repetidos a cuentas y slots; una access list o menos accesos distintos pueden ayudar.

Cuando un usuario se queja del costo, separa el precio por unidad de la cantidad de gas usada.

Lo que EIP-1559 no hace es crear espacio de bloque. Si la demanda supera la capacidad de forma sostenida, la base fee sube hasta que parte de los usuarios deja de enviar transacciones. El EIP mejora la previsibilidad del precio, no su nivel.

Referencias

Bibliografía académica

Formato IEEENumerado para ingeniería, sistemas y protocolos.
  1. [1]

    ethereum.org, "Timeline of all Ethereum forks (2014 to present)."

    official docsID: ETH-HISTORY-FORKSAbrir fuente (abre en otra pestaña)

    Fechas, bloques y EIPs de Berlin y London, con el ejemplo de fee previo a London.

  2. [2]

    V. Buterin and M. Swende, "EIP-2929: Gas cost increases for state access opcodes," Ethereum Improvement Proposals, no. 2929, Sep. 2020.

    Costos de acceso frío (2100 y 2600) y caliente (100) por transacción.

  3. [3]

    M. Zoltu, "EIP-2718: Typed Transaction Envelope," Ethereum Improvement Proposals, no. 2718, Jun. 2020.

    Sobre TransactionType || TransactionPayload con tipos de 0 a 0x7f.

  4. [4]

    V. Buterin and M. Swende, "EIP-2930: Optional access lists," Ethereum Improvement Proposals, no. 2930, Aug. 2020.

    Transacción tipo 0x01 con access list; 2400 por dirección y 1900 por slot.

  5. [5]

    V. Buterin, E. Conner, R. Dudley, M. Slipper, I. Norden, and A. Bakhta, "EIP-1559: Fee market change for ETH 1.0 chain," Ethereum Improvement Proposals, no. 1559, Apr. 2019.

    Transacción tipo 2, base fee quemada, propina y regla de ajuste por bloque.

  6. [6]

    ethereum.org, "Gas and fees."

    official docsID: ETH-GAS-DOCSAbrir fuente (abre en otra pestaña)

    Ejemplo de 21,000 gas con base fee de 10 gwei y propina de 2 gwei; ajuste máximo de 12,5 % por bloque.

  7. [7]

    A. Bakhta and V. Buterin, "EIP-3198: BASEFEE opcode," Ethereum Improvement Proposals, no. 3198, Jan. 2021.

    Opcode BASEFEE (0x48) que devuelve la base fee del bloque actual.

  8. [8]

    V. Buterin and M. Swende, "EIP-3529: Reduction in refunds," Ethereum Improvement Proposals, no. 3529, Apr. 2021.

    Elimina el reembolso de SELFDESTRUCT y baja el tope de reembolso de gas_used/2 a gas_used/5.

  9. [9]

    A. Beregszaszi et al., "EIP-3541: Reject new contract code starting with the 0xEF byte," Ethereum Improvement Proposals, no. 3541, Mar. 2021.

    Rechaza código nuevo que empiece con 0xEF para reservarlo a un formato futuro.

Fuentes primarias, papers seminales y especificaciones técnicas citadas en esta investigación.

Rutas vecinas

Si este tema te abrió hambre, sigue por aquí.

Series y labs que comparten área o dependen de esta ruta. Pocas opciones, para no convertir la curiosidad en menú infinito.

Sigue leyendo

Ideas conectadas con este artículo

Una ruta corta para continuar sin abrir veinte pestañas y perder el hilo.

Los comentarios se cargan al final de la lectura para no meter peso antes de tiempo.