Ethereum: de historia a labs
Artículo 12 de 48
25%
completado al llegar aquí
Sistemas distribuidosNota de investigación13 min de lectura
JSON-RPC: hablar con Ethereum sin magia
Cómo es una petición JSON-RPC, qué significan latest, safe y finalized, cómo fijar el bloque de una lectura y qué confianza depositas en un proveedor RPC.
Ruta Ethereum: 0/48
Prerequisitos recomendados
Si alguno de estos conceptos todavía está medio nebuloso, empieza por aquí. Te ahorra tropiezos y un par de cejas fruncidas.
Un panel de tesorería muestra un saldo de 5 ETH. Un minuto después muestra 4 y luego vuelve a 5. Nadie movió fondos: el panel preguntaba por latest a dos nodos distintos detrás de un balanceador, y cada uno veía una punta de cadena diferente. El error no estaba en Ethereum, sino en no decir qué bloque se estaba leyendo.
JSON-RPC es la interfaz con la que casi todo el software habla con un nodo de Ethereum: wallets, frontends, indexadores y scripts. Es un protocolo de llamada remota sin estado que viaja en JSON. [1] La lista de métodos eth_* y sus esquemas están especificados en el repositorio execution-apis. [2]
Brújula de lectura
Actor, evidencia y estado
Actor
Nodo o proveedor RPCResponde según su propia vista de la cadena y sus propias políticas.
Evidencia
Respuesta fijada a un bloqueNúmero o hash de bloque, y una prueba Merkle cuando la confianza importa.
Estado
Lectura, no escrituraSolo eth_sendRawTransaction propone un cambio; el resto observa o simula.
Preguntas guía
- ¿Qué viaja exactamente en una petición y una respuesta JSON-RPC?
- ¿Qué diferencia hay entre leer en
latest,safeyfinalized? - ¿Cómo haces que una lectura sea reproducible días después?
- ¿Qué parte de la seguridad de tu dApp depende del proveedor RPC?
Conexiones dentro de la serie
- Antes: ABI, calldata y eventos explica los bytes que viajan dentro de
eth_callyeth_getLogs. - Después: Foundry, Anvil y Cast monta el taller local que usamos en los ejercicios.
- Más adelante: Clientes y nodos, Indexadores: eventos, reorgs e idempotencia y Observabilidad Ethereum para dApps. Para la finalidad que hay detrás de
finalized, ver Casper FFG.
Una petición JSON-RPC por dentro
Cada petición es un objeto JSON con cuatro campos: jsonrpc (siempre "2.0"), method, params y un id que la respuesta repite para emparejarlas. [1]
# Ilustrativo contra un nodo local (Anvil escucha en 8545 por defecto)
curl -s -X POST http://127.0.0.1:8545 \
-H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x70997970C51812dc3A010C7d01b50e0d17dc79C8","latest"],"id":1}'{"jsonrpc":"2.0","id":1,"result":"0x21e19e0c9bab2400000"}El resultado es el saldo en wei escrito en hexadecimal: 0x21e19e0c9bab2400000 son wei, los 10 000 ETH con los que la CLI de Anvil financia por defecto cada cuenta de prueba (opción --balance). [3] Lo importante es el formato. La especificación distingue dos codificaciones hexadecimales. Las cantidades (enteros) van con 0x y sin ceros a la izquierda: cero se escribe 0x0, nunca 0x ni 0x00. Los datos (direcciones, hashes, bytecode) van con 0x y dos dígitos por byte, de modo que 0x vacío es válido y 0xf0f0f no lo es. [1] Un servicio en Java o Rust que parsea cantidades como si fueran datos acaba rechazando respuestas válidas.
Familias de métodos y qué hace cada una
Los métodos eth_* se agrupan por lo que hacen con el estado: [2]
| Familia | Métodos representativos | Qué hace |
|---|---|---|
| Leer estado | eth_getBalance, eth_getCode, eth_getStorageAt, eth_getTransactionCount | Consulta campos de una cuenta en un bloque |
| Simular | eth_call, eth_estimateGas, eth_simulateV1 | Ejecuta sin crear transacción ni cambiar estado |
| Enviar | eth_sendRawTransaction | Entrega una transacción ya firmada y devuelve su hash |
| Historia | eth_getBlockByNumber, eth_getTransactionReceipt, eth_getLogs | Lee bloques, receipts y eventos |
| Comisiones | eth_feeHistory, eth_maxPriorityFeePerGas, eth_blobBaseFee | Datos para fijar maxFee y la propina |
| Pruebas | eth_getProof | Devuelve cuenta y slots con su prueba Merkle |
Según la especificación, eth_call ejecuta una llamada de inmediato sin crear una transacción en la cadena. [2] Es el modo de leer funciones view y de reproducir un revert para leer su motivo. eth_estimateGas usa el mismo mecanismo para estimar cuánto gas hará falta; como vimos en el capítulo de la EVM, la estimación depende del contexto exacto de ejecución.
Hay dos métodos que un equipo de custodia debe tratar aparte: eth_sendTransaction firma y envía, y eth_sign devuelve una firma EIP-191. [2] Ambos suponen que el nodo guarda la clave privada. En un proveedor compartido no deberían existir; en tu propia infraestructura, la firma debe vivir en un componente separado y auditado, y el nodo solo debe recibir transacciones ya firmadas con eth_sendRawTransaction.
Invariante de lectura: fijar el bloque
Todo método que lee estado acepta un parámetro de bloque. La especificación define cinco etiquetas: [2]
earliest: el bloque más bajo que el cliente tiene disponible.finalized: el bloque más reciente con seguridad criptoeconómica; no se puede reorganizar salvo por intervención manual coordinada por la comunidad.safe: el más reciente que está a salvo de reorganizaciones bajo mayoría honesta y ciertos supuestos de sincronía.latest: el más reciente de la cadena canónica que ve el cliente; puede salir de la cadena incluso en condiciones normales.pending: una muestra del siguiente bloque construida por el cliente con transacciones de su mempool.
Etiquetas de bloque: de lo más firme a lo más fresco
Fuentes:
[2]Cómo leer esta figura
Una fila de ocho bloques unidos por flechas y un noveno, discontinuo, al final. De izquierda a derecha se marcan: earliest, el más antiguo que el nodo conserva; finalized, en verde, cuya reversión exige intervención manual coordinada por la comunidad; safe, en azul, a salvo de reorganizaciones con mayoría honesta y sincronía; latest, en ocre, que puede reorganizarse en condiciones normales; y pending, discontinuo, una muestra local construida desde el mempool. Debajo, una flecha de doble punta va de más firme, a la izquierda, a más fresco, a la derecha.
El invariante útil es este: una lectura sin bloque fijo no es reproducible. Si dos llamadas usan latest, pueden leer bloques distintos, como en el panel del ejemplo. La regla práctica tiene tres pasos: resolver primero el bloque con eth_getBlockByNumber, guardar su número y su hash, y hacer todas las lecturas siguientes contra ese bloque.
Fijar por número no basta si hay una reorganización: el mismo número puede pasar a ser otro bloque. EIP-1898 permite pasar un objeto {"blockHash": ..., "requireCanonical": true} en lugar de un número; con requireCanonical, el nodo debe dar error si ese hash ya no está en la cadena canónica. [4] Para contabilidad y conciliación, lo razonable es leer en finalized; para la interfaz de usuario, latest con un aviso de que el dato puede cambiar.
Cuello de botella: eth_getLogs y los rangos de bloques
eth_getLogs devuelve los logs que coinciden con un filtro de direcciones y topics en un rango de bloques inclusivo. La especificación es estricta: si fromBlock o toBlock quedan por encima de la punta de la cadena, o si fromBlock es mayor que toBlock, el cliente debe responder con el error -32602 y no puede recortar el rango ni devolver un resultado parcial. [2] La misma especificación define para este método el error 4444 con el mensaje Pruned history unavailable, que devuelve un nodo que ya no guarda los bloques pedidos.
En la práctica, los proveedores añaden límites propios de rango y de tamaño de respuesta, y cada uno los documenta a su manera. Un indexador robusto pide rangos pequeños, guarda el último bloque procesado y vuelve a pedir el tramo reciente cuando detecta una reorganización. Cada log trae el campo removed, que vale true cuando una reorganización lo eliminó. [1]
Topología: cliente de ejecución, de consenso y proveedor
Desde The Merge, un nodo de Ethereum son dos programas. El cliente de ejecución recibe transacciones, las ejecuta en la EVM y guarda el estado; es quien sirve JSON-RPC. El cliente de consenso implementa proof-of-stake y le dice al de ejecución qué bloques forman la cadena. [5] Se comunican por la Engine API, que escucha por defecto en el puerto 8551 y exige autenticación JWT. [6] Ese puerto no debe exponerse a internet; el de JSON-RPC, tampoco sin autenticación delante.
El tipo de nodo decide qué preguntas puedes hacer. Un nodo completo conserva el estado de los bloques recientes, típicamente los últimos 128, y puede regenerar estados antiguos bajo demanda; un nodo de archivo guarda todos los estados históricos y es el que hace falta para preguntar con fiabilidad por un saldo de hace años. [5]
Un proveedor RPC es un tercero que opera nodos por ti. Simplifica la operación, pero cada respuesta pasa a depender de su honestidad, de su disponibilidad y de qué nodo concreto atendió la petición. Hay dos defensas que no requieren confiar: comparar respuestas de varios proveedores y verificar datos críticos con eth_getProof contra cabeceras que obtienes por un canal propio. [7]
Controles mínimos para un cliente RPC en producción
0 de 6 revisados
Sesión curl contra Anvil: leer sin herramientas mágicas
Este ejercicio usa solo curl y cast rpc, que envía métodos JSON-RPC en crudo. Anvil escucha en el puerto 8545 y usa el chain ID 31337. [8]
# Terminal 1
anvil
# Terminal 2: chain ID (31337 = 0x7a69) y número de bloque
curl -s -X POST http://127.0.0.1:8545 -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
curl -s -X POST http://127.0.0.1:8545 -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":2}'
# Lo mismo con cast rpc, y una lectura fijada a un bloque concreto
export ETH_RPC_URL=http://127.0.0.1:8545
cast rpc eth_getBlockByNumber latest false
cast rpc eth_getBalance 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 0x0Qué observar: eth_chainId devuelve 0x7a69; el número de bloque es una cantidad hexadecimal sin ceros a la izquierda; eth_getBlockByNumber devuelve el hash que usarías para fijar lecturas posteriores. Repite la lectura del saldo en 0x0 después de enviar una transferencia y comprueba que no cambia, mientras que en latest sí lo hace. Esa diferencia es la reproducibilidad del invariante anterior.
Bibliografía académica
- [1]
ethereum.org, "JSON-RPC API," Ethereum developer documentation. [Online]. Available: https://ethereum.org/developers/docs/apis/json-rpc/
Codificación hex de cantidades y datos, parámetro de bloque y ejemplos curl.
- [2]
Ethereum, "Execution APIs: JSON-RPC specification," ethereum/execution-apis, GitHub repository.
Especificación OpenRPC de métodos eth_*, block tags, receipts y logs.
- [3]
Foundry contributors, "anvil/src/cmd.rs," foundry-rs/foundry, GitHub repository.
Opciones de la CLI de Anvil; --balance vale 10000 ETH por defecto.
- [4]
C. Cooper, "EIP-1898: Add blockHash to defaultBlock methods," Ethereum Improvement Proposals, 2019.
Consultas de estado fijadas por hash de bloque.
- [5]
ethereum.org, "Nodes and clients," Ethereum developer documentation. [Online]. Available: https://ethereum.org/developers/docs/nodes-and-clients/
Clientes de ejecución y consenso; nodos full, archive y light.
- [6]
Ethereum, "Engine API: common definitions," ethereum/execution-apis, GitHub repository.
Puerto 8551 y autenticación JWT entre cliente de consenso y de ejecución.
- [7]
S. Jentzsch and C. Jentzsch, "EIP-1186: RPC-method to get Merkle proofs - eth_getProof," Ethereum Improvement Proposals, 2018.
Pruebas Merkle de cuenta y storage por RPC.
- [8]
Foundry contributors, "anvil/src/config.rs," foundry-rs/foundry, GitHub repository.
Valores por defecto de Anvil: puerto 8545, chain ID 31337 y mnemónica de prueba.
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.
Series relacionadas
HotStuff: de cero a estado del arte
Ruta progresiva para entender consenso BFT moderno: quórums, sincronía parcial, locks, taxonomía one/two/three-chain, QC/highQC, multipipeline, view-change, responsiveness, AggQC, Fast-HotStuff, sync y frontera DAG-BFT.
Substrate: del runtime al consenso propio
Serie de 20 capítulos, de neófito a experto, sobre Substrate (Polkadot SDK): runtime en Wasm, FRAME, extrinsics, estado, pesos, actualizaciones sin fork, Aura, BABE, GRANDPA, sincronización, parachains, XCM, Sassafras, BEEFY y cómo conectar un consenso BFT propio a un nodo.
Ruta práctica: activos digitales en la operación
Nueve módulos, de menor a mayor dependencia, para decidir cómo usar custodia, wallets, llaves, firmas multifirma y MPC, AML/KYT y stablecoins con Fireblocks, BitGo, Kaleido, Circle y Tether.
Labs para convertir lectura en práctica
Prueba de trabajo y reajuste de dificultad
Laboratorio determinista para medir el costo de la prueba de trabajo y ver cómo el reajuste por altura de bloque estabiliza el tiempo entre bloques.
Consensus benchmark
Banco reproducible para comparar consenso BFT y documentar los trade-offs de HotStuff-2.
Ethereum local: Anvil, wallet y primera transacción
Laboratorio base para levantar una red local, crear cuentas, firmar transacciones y observar receipts sin gastar ETH real.
RPC y Cast: leer Ethereum con lupa
Ejercicios con JSON-RPC y Cast para consultar bloques, nonces, gas, receipts, logs y errores reproducibles.
Sigue leyendo
Ideas conectadas con este artículo
Una ruta corta para continuar sin abrir veinte pestañas y perder el hilo.
Clientes y nodos: diversidad antes que monocultivo
Execution clients, consensus clients, node operators, client diversity, APIs y por qué Ethereum no debe depender de una sola implementación.
Ethereum: mapa de lectura antes de tocar Solidity
Ruta completa para aprender Ethereum desde historia y arquitectura hasta EVM, proof-of-stake, L2, seguridad, DeFi y labs reproducibles.
Antes de Ethereum: Bitcoin, scripts y la máquina mundial
Historia inicial de Ethereum: qué problema vio el whitepaper, por qué los contratos generales importaban y qué cambió frente a Bitcoin.
Frontier, Homestead y The DAO: la red aprendió con golpes
De mainnet inicial a The DAO: primeros forks, tensiones sociales y por qué la historia de Ethereum no es solo técnica.