Ethereum: de historia a labs
Artículo 7 de 48
15%
completado al llegar aquí
Sistemas distribuidosNota de investigación21 min de lectura
Cuentas, claves y direcciones sin humo
Cómo una clave secp256k1 se convierte en dirección con Keccak-256, qué valida el checksum EIP-55 y qué cambia EIP-7702 para quien custodia claves.
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 cliente pide retirar fondos a 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf. Antes de firmar, el equipo de custodia debe poder responder tres preguntas: qué clave controla esa dirección, si el texto está bien escrito y si detrás hay una persona, un contrato o una cuenta que delegó su código. Este capítulo responde las tres con la especificación en la mano.
Ethereum tiene dos tipos de cuenta. Una cuenta de propiedad externa (EOA, externally owned account) la controla quien tenga su clave privada. Una cuenta de contrato la controla su código: no tiene clave privada y solo actúa cuando recibe un mensaje. [1]
Brújula de lectura
Actor, evidencia y estado
Actor
Titular de la clave privadaQuien conoce k firma; ningún servidor de Ethereum guarda esa clave.
Evidencia
Firma ECDSA (v, r, s)De la firma y el hash se recupera la clave pública y, con ella, la dirección.
Estado
nonce, balance, storageRoot, codeHashLos cuatro campos de cada cuenta en el estado global.
Preguntas guía
- ¿Qué operación matemática convierte una clave privada en una dirección y por qué no se puede invertir?
- ¿Qué detecta exactamente el checksum EIP-55 y qué no detecta?
- ¿Por qué una transacción no lleva un campo
fromy aun así la red sabe quién la envió? - ¿Qué cambia para la custodia cuando una EOA puede delegar su código a un contrato?
Conexiones dentro de la serie
- Antes: Por qué Ethereum necesitaba The Merge cierra la parte histórica. Si quieres ver por qué las firmas cambian lo que un sistema distribuido puede garantizar, lee por qué las firmas criptográficas reducen los nodos necesarios.
- Después: Una transacción Ethereum por dentro usa estas claves para firmar y pagar gas.
- Más adelante: EIP-7702: cuando una EOA aprende trucos de smart account y Approvals, permit y phishing de firmas profundizan en los riesgos de firmar.
Los cuatro campos de una cuenta
Piensa en el estado de Ethereum como un mapa gigante cuya clave es una dirección de 20 bytes. Cada valor es una cuenta con cuatro campos. [1]
nonce: en una EOA, cuántas transacciones envió; en un contrato, uno más que los contratos que creó (desde EIP-161 el nonce de un contrato empieza en 1). Solo se puede ejecutar una transacción por cada nonce, y eso impide reenviar una transacción ya firmada.balance: los wei que posee la cuenta. Un ether son wei.storageRoot: la raíz de un árbol de Merkle que resume el almacenamiento persistente del contrato.codeHash: el hash Keccak-256 del código. En una EOA es el hash de la cadena vacía, salvo que haya delegado su código con EIP-7702.
El yellow paper usa la misma definición formal: una cuenta "vacía" es la que tiene codeHash igual a , nonce cero y balance cero. [2] La regla práctica es directa: para saber si una dirección tiene código, no mires la interfaz de la wallet; consulta codeHash o el código en un bloque concreto.
Qué objeto tengo delante
Traza de derivación: de la clave privada a la dirección
Ethereum firma con ECDSA sobre la curva secp256k1, la misma curva que usa Bitcoin. Una clave privada es un entero elegido al azar en el rango , donde es el orden del grupo de la curva: [2]
Que ocupe 32 bytes no significa que cualquier valor de 32 bytes sea una clave válida. Un generador correcto rechaza el cero y cualquier valor mayor o igual que . La probabilidad de caer fuera del rango al azar es despreciable, pero un importador de claves debe validar el rango igual.
La clave pública es el punto , donde es el punto generador de la curva. Multiplicar es barato; volver de a es el problema del logaritmo discreto, que se considera inviable con los recursos actuales. El yellow paper representa como 64 bytes: las coordenadas e concatenadas, sin el byte de prefijo 0x04 que usan otros formatos.
La dirección es el tramo final de un hash: [2]
En palabras: se calcula Keccak-256 de los 64 bytes de la clave pública y se conservan los últimos 160 bits, es decir, los bytes 12 a 31. El resultado son 20 bytes, que se escriben como 40 caracteres hexadecimales con el prefijo 0x. [1]
De la clave privada a la dirección con checksum
Cómo leer esta figura
Fila superior: una caja roja con la clave privada k de 32 bytes, entre 1 y n menos 1, una flecha azul rotulada secp256k1 y fácil hacia la clave pública K igual a k por G, de 64 bytes, y otra flecha rotulada Keccak-256 hacia un hash de 32 bytes. Una flecha roja discontinua vuelve de la clave pública a la privada con el rótulo inviable. Fila inferior: del hash baja una flecha a la dirección de 20 bytes, que son los bytes 12 a 31 del hash, escrita en minúsculas. De ahí una flecha rotulada Keccak-256 del hex y nibble mayor o igual que 8 lleva a la forma EIP-55 con mayúsculas, y una última flecha lleva a la verificación que hace la wallet. Una nota indica que con k igual a 1 la dirección es 0x7E5F4552 hasta 29395Bdf.
Un detalle que rompe implementaciones: el Keccak-256 de Ethereum es la versión que ganó el concurso SHA-3, no el estándar SHA-3 final publicado después. [2] Los dos producen salidas distintas para la misma entrada. Si un servicio usa una implementación de SHA3-256 (el estándar final) en lugar de Keccak-256, obtendrá direcciones equivocadas sin ningún error visible. Una prueba rápida: Keccak-256 de la cadena vacía empieza por c5d24601, mientras que SHA3-256 de la cadena vacía empieza por a7ffc6f8.
EIP-55: el checksum vive en las mayúsculas
Una dirección en minúsculas no tiene control de errores: si alguien cambia un carácter, sigue siendo una dirección válida de otra persona. EIP-55 añade un checksum sin cambiar la longitud, usando la diferencia entre mayúsculas y minúsculas de las letras a a f. [3]
El algoritmo tiene cuatro pasos:
- Escribe la dirección en hexadecimal en minúsculas, sin
0x. - Calcula Keccak-256 de esos 40 caracteres tratados como texto ASCII, no como bytes.
- Recorre la dirección. Para cada carácter que sea letra, mira el carácter en la misma posición del hash.
- Si ese carácter del hash vale 8 o más, escribe la letra en mayúscula; si no, en minúscula. Los dígitos no cambian.
Con unos 15 bits de control por dirección en promedio, la probabilidad de que un error de tipeo aleatorio pase el control es 0,0247 %, según la propia especificación. [3] Hay dos límites que un equipo de custodia debe conocer. Una dirección escrita toda en minúsculas o toda en mayúsculas no trae checksum, así que no hay nada que verificar. Y el checksum solo detecta errores de escritura: no protege contra una dirección correcta del atacante pegada en lugar de la tuya.
El siguiente fragmento es ilustrativo y reproduce el cálculo completo con la clave de prueba , que es pública y nunca debe recibir fondos:
// Ilustrativo. Requiere: npm i @noble/curves@1.4.0 @noble/hashes@1.4.0
import { secp256k1 } from '@noble/curves/secp256k1';
import { keccak_256 } from '@noble/hashes/sha3';
import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';
const k = new Uint8Array(32); k[31] = 1; // clave de prueba k = 1
const pub = secp256k1.getPublicKey(k, false); // 65 bytes: 0x04 || x || y
const addr = bytesToHex(keccak_256(pub.slice(1)).slice(12)); // últimos 20 bytes
const h = bytesToHex(keccak_256(utf8ToBytes(addr))); // hash del texto en minúsculas
let eip55 = '0x';
for (let i = 0; i < 40; i++) {
eip55 += parseInt(h[i], 16) >= 8 ? addr[i].toUpperCase() : addr[i];
}
console.log(eip55); // 0x7E5F4552091A69125d5DfCb7b8C2659029395BdfFirmas recuperables: por qué no hay campo from
Una transacción firmada no incluye la dirección del emisor. Incluye una firma y la red recupera la clave pública a partir de la firma y del hash de la transacción; de esa clave pública sale la dirección. El yellow paper define el emisor exactamente así, aplicando la misma función de dirección al resultado de . [2] El valor indica la paridad de la coordenada para elegir entre los dos puntos candidatos.
Tres reglas cierran huecos de esa recuperación:
- Firma canónica (low-s). Para toda firma válida existe otra válida . Desde Homestead, una transacción con es inválida. Así nadie puede cambiar el hash de tu transacción reenviándola con la firma "espejo". [4] El precompilado
ecrecoversigue aceptando ambos valores, así que un contrato que verifica firmas debe aplicar esa regla por su cuenta. - Chain ID. EIP-155 incluye el identificador de la red en lo que se firma, de modo que una transacción firmada con EIP-155 para mainnet (chain ID 1) no es válida en otra red. Una firma en el formato anterior, sin chain ID, no tiene esa protección. [5]
- Nonce. Cada transacción consume el siguiente nonce del emisor y no puede repetirse. [1]
Firmar un mensaje no es lo mismo que firmar una transacción. EIP-191 antepone el byte 0x19 para que los datos firmados nunca sean una transacción RLP válida; la variante usada por personal_sign añade "\x19Ethereum Signed Message:\n" y la longitud del mensaje. [6] EIP-712 define cómo firmar datos estructurados con un dominio explícito (nombre, versión, chain ID y contrato verificador, entre otros campos). [7] Para una política de custodia, cada formato de firma es un riesgo distinto y debe tener su propia regla de aprobación.
Direcciones de contrato: CREATE, CREATE2 y colisiones
Un contrato no tiene clave, pero sí dirección. Con el opcode CREATE, la dirección son los últimos 20 bytes de Keccak-256 de la codificación RLP del par (emisor, nonce). [2] Por ejemplo, la primera cuenta de prueba de Anvil, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, despliega su primer contrato con nonce 0 en 0x5FbDB2315678afecb367f032d93F642f64180aa3. Cualquiera puede calcularlo antes del despliegue.
CREATE2 elimina la dependencia del nonce: la dirección sale de keccak256(0xff ++ emisor ++ salt ++ keccak256(init_code)), tomando los últimos 20 bytes. [8] Eso permite saber hoy dónde vivirá un contrato que se desplegará mañana, y es la base de muchas wallets de contrato.
Las direcciones de 160 bits tienen un límite de seguridad conocido. EIP-3607 estima que encontrar una colisión entre una EOA y un contrato cuesta del orden de operaciones, factible con un presupuesto muy grande. Por eso la red rechaza transacciones cuyo emisor ya tiene código desplegado (desde Pectra, con una sola excepción: el indicador de delegación de EIP-7702 que veremos abajo): así nadie puede desplegar un contrato atractivo y luego vaciarlo con la clave de una EOA que colisiona en la misma dirección. [9]
Riesgo operativo para custodia: semillas, rutas y delegación
Casi ninguna wallet guarda claves sueltas. Guarda una frase mnemónica (BIP-39): de 12 a 24 palabras de las que se deriva una semilla con PBKDF2, HMAC-SHA512 y 2048 iteraciones, usando la frase como contraseña y "mnemonic" más una passphrase opcional como sal. [10] De esa semilla, BIP-32 deriva un árbol de claves sobre secp256k1. [11]
BIP-44 fija la ruta m / purpose' / coin_type' / account' / change / address_index, y el registro SLIP-44 asigna a Ether el coin type 60. [12] [13] Por eso la ruta habitual de la primera cuenta es m/44'/60'/0'/0/0; es, por ejemplo, la ruta por defecto con la que Anvil deriva sus cuentas de prueba. [14] La comilla marca un nivel endurecido (hardened): su derivación requiere la clave privada del padre.
La advertencia de BIP-32 que más importa a custodia es esta: quien conoce una clave pública extendida (xpub) del padre y cualquier clave privada hija no endurecida puede reconstruir la clave privada del padre y, con ella, todo el subárbol. [11] Una xpub no es "solo pública" y no debe circular como si lo fuera.
Desde Pectra, activado en mainnet el 7 de mayo de 2025, una EOA puede tener código. [15] EIP-7702 crea la transacción tipo 0x04 con una lista de autorizaciones [chain_id, address, nonce, y_parity, r, s]. Cada autorización firmada escribe en la EOA el indicador 0xef0100 || address, y desde entonces cualquier llamada a esa EOA ejecuta el código del contrato delegado. [16]
Tres consecuencias directas para un equipo de custodia, todas tomadas de la especificación: [16]
- Una autorización con
chain_idigual a 0 es válida en cualquier cadena que implemente EIP-7702. - La delegación persiste: si la transacción que la contiene revierte, el indicador ya escrito no se deshace. Se elimina delegando a la dirección cero.
- La clave privada original conserva el control total. Un contrato delegado mal escrito puede permitir que un tercero tome casi el control completo de la cuenta.
Controles mínimos antes de firmar con una clave custodiada
0 de 6 revisados
Práctica local: derivar y verificar sin tocar fondos
Este ejercicio usa solo valores públicos de prueba. La mnemónica por defecto de Anvil es test test test test test test test test test test test junk y está definida en el código fuente de Foundry; nunca debe recibir fondos reales. [14]
# Derivar las cuentas 0 y 1 de la mnemónica de prueba (ruta m/44'/60'/0'/0/i)
cast wallet address --mnemonic "test test test test test test test test test test test junk" --mnemonic-index 0
cast wallet address --mnemonic "test test test test test test test test test test test junk" --mnemonic-index 1
# Obtener la dirección a partir de una clave privada de prueba
cast wallet address --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# Aplicar el checksum EIP-55 a una dirección en minúsculas
cast to-check-sum-address 0x7e5f4552091a69125d5dfcb7b8c2659029395bdf
# Calcular la dirección del primer contrato que desplegaría esa cuenta
cast compute-address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --nonce 0Resultado esperado: la cuenta 0 es 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, la cuenta 1 es 0x70997970C51812dc3A010C7d01b50e0d17dc79C8, el checksum devuelve 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf y la dirección de contrato es 0x5FbDB2315678afecb367f032d93F642f64180aa3. Comprobado con Foundry 1.5.1. Estos valores los recalculamos de forma independiente con el fragmento JavaScript anterior. Si tu herramienta da otros, revisa la ruta de derivación o la función de hash antes de seguir.
Bibliografía académica
- [1]
ethereum.org, "Ethereum accounts," Ethereum developer documentation. [Online]. Available: https://ethereum.org/developers/docs/accounts/
Tipos de cuenta, los cuatro campos de una cuenta y derivación de la dirección.
- [2]
G. Wood, "Ethereum: A secure decentralised generalised transaction ledger," Yellow Paper, Shanghai version efc5f9a, 2025.
Especificación formal de estado, transacciones, firma (apéndice F), gas y EVM.
- [3]
V. Buterin and A. Van de Sande, "EIP-55: Mixed-case checksum address encoding," Ethereum Improvement Proposals, 2016.
Checksum de direcciones con mayúsculas y minúsculas.
- [4]
V. Buterin, "EIP-2: Homestead hard-fork changes," Ethereum Improvement Proposals, 2015.
Firmas con s mayor que n/2 dejan de ser válidas en transacciones.
- [5]
V. Buterin, "EIP-155: Simple replay attack protection," Ethereum Improvement Proposals, 2016.
Chain ID dentro de la firma para evitar replay entre redes.
- [6]
M. H. Swende and N. Johnson, "EIP-191: Signed data standard," Ethereum Improvement Proposals, 2016.
Prefijo 0x19 para que un mensaje firmado no sea una transacción válida.
- [7]
R. Bloemen, L. Logvinov and J. Evans, "EIP-712: Typed structured data hashing and signing," Ethereum Improvement Proposals, 2017.
Firma de datos estructurados con dominio explícito.
- [8]
V. Buterin, "EIP-1014: Skinny CREATE2," Ethereum Improvement Proposals, 2018.
Dirección de contrato determinista a partir de salt e initcode.
- [9]
D. Feist, D. Khovratovich and M. van der Wijden, "EIP-3607: Reject transactions from senders with deployed code," Ethereum Improvement Proposals, 2021.
Colisiones de direcciones de 160 bits y rechazo de emisores con código.
- [10]
M. Palatinus, P. Rusnak, A. Voisine and S. Bowe, "BIP-39: Mnemonic code for generating deterministic keys," Bitcoin Improvement Proposals, 2013.
Frase mnemónica y derivación de la semilla con PBKDF2-HMAC-SHA512.
- [11]
P. Wuille, "BIP-32: Hierarchical deterministic wallets," Bitcoin Improvement Proposals, 2012.
Derivación jerárquica sobre secp256k1, claves endurecidas y riesgo de xpub.
- [12]
M. Palatinus and P. Rusnak, "BIP-44: Multi-account hierarchy for deterministic wallets," Bitcoin Improvement Proposals, 2014.
Ruta m / purpose' / coin_type' / account' / change / address_index.
- [13]
SatoshiLabs, "SLIP-0044: Registered coin types for BIP-0044," SatoshiLabs Improvement Proposals.
Registro de coin types: 60 corresponde a Ether.
- [14]
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.
- [15]
T. Beiko, "EIP-7600: Hardfork meta - Pectra," Ethereum Improvement Proposals, 2024.
EIPs incluidos en Pectra y su activación en mainnet.
- [16]
V. Buterin, S. Wilson, A. Dietrichs and lightclient, "EIP-7702: Set code for EOAs," Ethereum Improvement Proposals, 2024.
Transacción tipo 0x04 y autorizaciones que delegan código a una EOA.
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.
ERC-4337: UserOperation, bundlers y EntryPoint
Account abstraction sin hard fork: smart accounts, UserOperation, bundlers, paymasters y EntryPoint.
EIP-7702: cuando una EOA aprende trucos de smart account
Set code for EOAs, delegación persistente, relación con account abstraction y riesgos de UX.
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.