Ethereum: de historia a labs
Artículo 13 de 48
27%
completado al llegar aquí
Sistemas distribuidosNota de investigación14 min de lectura
Foundry, Anvil y Cast: taller local para Ethereum
Forge, Anvil y Cast paso a paso: compilar, probar con fuzzing, desplegar en una cadena local 31337 y leer storage por RPC sin gastar gas real.
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.
Imagina que quieres comprobar algo muy concreto: después de llamar setNumber(42) en un contrato, ¿el valor 42 está de verdad en el slot 0 de su storage? Con una testnet pública necesitas ETH de prueba, esperar bloques y confiar en un proveedor RPC. Con Foundry lo haces en tu máquina en menos de un minuto, y puedes repetirlo cien veces con el mismo resultado.
Foundry es un conjunto de herramientas de línea de comandos para desarrollar contratos de Ethereum. Trae cuatro binarios: forge compila, prueba, despliega y verifica contratos; cast habla con cualquier nodo por JSON-RPC; anvil levanta un nodo Ethereum local; chisel es un REPL de Solidity [1]. Para alguien que viene de Rust la sensación es familiar: Foundry está escrito en Rust y se instala con un gestor de versiones propio.
Ciclo local
Del código fuente a la evidencia leída por RPC
Brújula de lectura
Actor, evidencia y estado
Actor
Desarrollador con una cuenta de AnvilFirma con una clave de prueba pública; nadie más compite por el bloque.
Evidencia
Receipt, retorno y slotCast lee por JSON-RPC lo mismo que leería de un nodo real.
Estado
Cadena efímera en memoriaDesaparece al cerrar Anvil, salvo que la vuelques a un archivo.
Preguntas guía
- ¿Qué parte del ciclo corre sin nodo y qué parte necesita uno escuchando en un puerto?
- ¿Qué diferencia hay entre una prueba que pasa en
forge testy un contrato desplegado en Anvil? - ¿Por qué el chain id de la red local importa para la seguridad de tus firmas?
- ¿Qué comportamientos de mainnet no aparecen nunca en una cadena local?
Cuatro binarios y una cadena de juguete
La instalación oficial usa foundryup, un instalador que también actúa como gestor de versiones. Ejecutar foundryup instala la última versión estable de forge, cast, anvil y chisel; foundryup --install <versión> fija una versión concreta [2]. En un equipo, fijar la versión es la primera decisión de reproducibilidad: si dos personas usan versiones distintas, las trazas y los errores pueden no coincidir.
curl -L https://foundry.paradigm.xyz | bash # instala foundryup
foundryup # última versión estable
forge --version && anvil --version && cast --versionAnvil es un nodo Ethereum que vive solo en memoria. Al arrancar sin flags crea diez cuentas de desarrollo con 10.000 ETH cada una, derivadas de la frase mnemónica pública test test test test test test test test test test test junk [3]. Por defecto escucha en 127.0.0.1:8545 y usa el chain id 31337 [4]. El chain id es el número que identifica la red y que entra en la firma de cada transacción desde EIP-155, para que una transacción firmada para una red no sea válida en otra [5].
Cast es el cliente. Cada subcomando se traduce a una o varias llamadas JSON-RPC, y lee el endpoint de --rpc-url o de la variable ETH_RPC_URL [6]. Esa es la idea que conecta este capítulo con el anterior: Cast no tiene un canal especial hacia Anvil. Habla con él igual que hablaría con un nodo de mainnet, así que lo que aprendes aquí sirve después contra cualquier red.
Traza de ejecución: de forge init a un slot leído con Cast
forge init hello_foundry crea un proyecto con src/, test/, script/ y lib/, más un contrato Counter con su prueba y su script de despliegue [1]. El contrato guarda un único uint256 public number y expone setNumber e increment. Es pequeño a propósito: con una sola variable sabemos exactamente qué slot debe cambiar.
forge init hello_foundry && cd hello_foundry
forge build # artefactos en out/
forge test # pruebas en EVM local, sin nodo
anvil # en otra terminal: nodo en 127.0.0.1:8545El script generado (script/Counter.s.sol) envuelve new Counter() entre vm.startBroadcast() y vm.stopBroadcast(). Sin --broadcast, forge script solo simula; con --broadcast firma y envía las transacciones al RPC indicado [7]. La clave de abajo es la de la primera cuenta de Anvil, publicada en la documentación; nunca debe usarse fuera de una red local.
export ETH_RPC_URL=http://127.0.0.1:8545
export PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
forge script script/Counter.s.sol --broadcast --rpc-url $ETH_RPC_URL --private-key $PKSi es la primera transacción de esa cuenta (nonce 0) en un Anvil recién iniciado, la dirección del contrato es determinista: 0x5FbDB2315678afecb367f032d93F642f64180aa3. Con CREATE, la dirección depende solo del emisor y de su nonce, así que la puedes calcular antes de desplegar. Ahora leemos y escribimos desde fuera.
C=0x5FbDB2315678afecb367f032d93F642f64180aa3
cast call $C "number()(uint256)" # 0
cast send $C "setNumber(uint256)" 42 --private-key $PK
cast call $C "number()(uint256)" # 42
cast storage $C 0 # 0x...002a (42 en hexadecimal)Las dos últimas líneas cuentan la misma historia desde capas distintas. cast call usa el ABI: codifica la llamada, ejecuta eth_call y decodifica el retorno. cast storage ignora el ABI y pide el contenido crudo del slot 0. Que ambos coincidan es la evidencia que buscábamos al principio, y es la base del capítulo sobre storage layout.
El ciclo local de Foundry y dónde queda cada evidencia
Cómo leer esta figura
Diagrama en dos filas. Fila superior, de izquierda a derecha: src/Counter.sol pasa por forge build a out/ con ABI y bytecode; out/ pasa por forge script --broadcast a Anvil, que escucha en 127.0.0.1:8545 con chain id 31337; Anvil pasa por cast call y cast storage a una caja de evidencia con retorno, slot y receipt. Fila inferior: desde out/ baja una flecha a forge test, que ejecuta la EVM dentro del proceso sin nodo ni RPC, y de ahí a trazas -vvv y fuzzing de 256 casos. A la derecha, una caja cast rpc anvil_* para impersonar, mover el tiempo y tomar snapshots apunta hacia Anvil. Una nota indica que la cadena local es efímera y no toca mainnet.
Pruebas que eligen sus propios datos
En Foundry las pruebas se escriben en Solidity. Un archivo .t.sol define un contrato que hereda de Test; setUp() corre antes de cada prueba y cada función que empieza por test es un caso [8]. Para quien viene de JUnit o de cargo test el modelo es conocido. Lo nuevo es que la prueba vive dentro de la misma EVM que el contrato, así que puede llamar funciones, leer storage y comprobar reverts sin capa de traducción.
Si una función de prueba recibe parámetros, Forge la convierte en una prueba de fuzzing: genera entradas aleatorias y la ejecuta 256 veces por defecto. La plantilla ya trae una: testFuzz_SetNumber(uint256 x) comprueba que setNumber(x) seguido de number() devuelve x para cualquier x. Para acotar entradas existen vm.assume, que descarta casos, y bound, que los recorta a un rango.
Cuando algo falla, la verbosidad es la herramienta de depuración. -vvv muestra la traza de las pruebas que fallan, -vvvv la de todas y -vvvvv añade los cambios de storage. Si el fuzzer encuentra un fallo, informa la semilla; fijarla con --fuzz-seed ayuda a reproducirlo, aunque la documentación advierte que también hay que mantener la versión de Foundry, la configuración y el corpus.
Un detalle que confunde al principio: Forge ejecuta cada llamada externa de primer nivel de una prueba como una transacción separada. Esto da una contabilidad de gas más precisa, pero significa que una prueba no siempre equivale a una sola transacción con accesos "calientes" compartidos. Si tu aserción depende de eso, la documentación indica --no-isolate.
Manipular la cadena local sin engañarte
Anvil acepta métodos RPC propios, fuera del estándar, para preparar escenarios difíciles de reproducir en una red real [9]. Con anvil_impersonateAccount envías transacciones como cualquier dirección sin conocer su clave. Con evm_increaseTime y evm_mine adelantas el reloj, útil para probar un timelock. Con evm_snapshot y evm_revert guardas un punto y vuelves a él; el snapshot se consume al revertir.
SNAP=$(cast rpc evm_snapshot) # punto de retorno
cast rpc evm_increaseTime 3600 && cast rpc evm_mine
cast rpc anvil_setStorageAt $C 0x0 0x0000000000000000000000000000000000000000000000000000000000000007
cast call $C "number()(uint256)" # 7: el slot 0 se escribió a mano
cast rpc evm_revert $SNAP # vuelve al estado guardadoEstas palancas son potentes y peligrosas para el razonamiento. Escribir un slot a mano demuestra cómo se lee el storage, pero también puede producir un estado que ninguna transacción válida alcanzaría. La regla práctica: usa la manipulación para preparar precondiciones y deja que el contrato produzca la postcondición mediante llamadas normales.
Para fijar el punto de partida de un lab, Anvil puede volcar y cargar estado con --dump-state, --load-state o --state, y bifurcar una red real con --fork-url y --fork-block-number [3]. Un fork añade una dependencia externa: el bloque elegido y el proveedor RPC que sirve el estado histórico. Fijar el número de bloque es lo que hace repetible un fork.
Riesgo operativo: lo que Anvil no puede prometer
Las cuentas por defecto de Anvil son públicas. La documentación lo advierte y recomienda una mnemónica propia al bifurcar mainnet o hablar con proveedores RPC públicos [3]. Cualquier fondo enviado a esas direcciones en una red real puede ser retirado por cualquiera. Para redes reales, la guía de scripting recomienda un keystore cifrado (--account) o una cartera hardware (--ledger) en lugar de --private-key [7].
El segundo riesgo es de interpretación. Por defecto Anvil mina en cuanto llega una transacción lista; existen modos por intervalo (--block-time) y manual (--no-mining) para modelar bloques y orden [9]. Aun así no hay otros usuarios en el mempool, no hay buscadores de MEV, la latencia es cero y el precio del gas no sube porque tu transacción sea urgente. Una prueba local verde demuestra que el código cumple la propiedad escrita bajo esas condiciones; no demuestra que sea seguro en mainnet.
El tercer riesgo es de evidencia. Un lab que termina en "los tests pasan" no deja nada que otra persona pueda verificar. Lo mínimo que conviene guardar es la versión de Foundry, la dirección desplegada, el hash de la transacción, el estado leído antes y después, y cómo se apaga el entorno. Los scripts ya escriben los receipts en broadcast/, así que esa evidencia sale casi gratis.
Qué demuestra cada entorno
Conexiones dentro de la serie
- Antes: JSON-RPC: hablar con Ethereum sin magia explica los métodos que Cast usa por debajo, como
eth_callyeth_getStorageAt. - También útil: EVM: stack, memory, storage y calldata describe qué es el slot que leímos con
cast storage. - Después: Solidity sin magia: contratos como máquinas de estado usa este taller para leer contratos como transiciones de estado.
- Más adelante: Testing serio: unit, fork, fuzz e invariantes profundiza en las pruebas que aquí solo se presentan.
Bibliografía académica
- [1]
Foundry contributors, "Getting Started," Foundry Documentation.
Tabla de las cuatro herramientas, forge init/build/test, anvil y cast send con la cuenta 0 de Anvil.
- [2]
Foundry contributors, "Installation," Foundry Documentation.
foundryup, versiones fijas, verificación SHA-256 y ruta ~/.foundry.
- [3]
Foundry contributors, "Anvil," Foundry Documentation.
Nodo en memoria, 10 cuentas con 10.000 ETH, mnemónica por defecto y advertencia sobre claves públicas.
- [4]
Foundry contributors, "crates/anvil/src/config.rs," foundry-rs/foundry, GitHub.
Constantes NODE_PORT = 8545, CHAIN_ID = 31337 y DEFAULT_MNEMONIC.
- [5]
V. Buterin, "EIP-155: Simple replay attack protection," Ethereum Improvement Proposals, no. 155, 2016.
Incluye chainid en el hash firmado para evitar replays entre redes.
- [6]
Foundry contributors, "Reading chain data," Foundry Documentation.
ETH_RPC_URL, cast call con firma de retorno, cast storage, cast receipt y overrides de estado.
- [7]
Foundry contributors, "Scripting," Foundry Documentation.
vm.startBroadcast, simulación frente a --broadcast, opciones de firma y archivos en broadcast/.
- [8]
Foundry contributors, "Testing," Foundry Documentation.
Convenciones .t.sol, setUp, aislamiento de llamadas, niveles -v, fuzzing con 256 ejecuciones, semilla, vm.assume y bound.
- [9]
Foundry contributors, "Custom methods," Foundry Documentation.
anvil_impersonateAccount, evm_increaseTime, evm_snapshot/evm_revert, anvil_setStorageAt y control de minado.
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.
Testing serio: unit, fork, fuzz e invariantes
Cómo probar contratos con unit tests, fork tests, fuzzing e invariants antes de confiar en un deploy.
De la serie a los labs: cómo practicar sin quemar ETH real
Mapa de laboratorios reproducibles para practicar Ethereum con Foundry, Anvil, RPC, contratos vulnerables, rollups mock e indexadores.
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.