Substrate: del runtime al consenso propio
Artículo 3 de 20
15%
completado al llegar aquí
Sistemas distribuidosNota de investigación15 min de lectura
El runtime en Wasm: nodo y runtime hablan por dos puertas
Dónde vive el runtime de Substrate (la clave :code del estado) y cómo conversa con el nodo: host functions de sp_io y runtime APIs de sp_api.
Ruta Substrate: 0/20
Prerequisitos recomendados
Si alguno de estos conceptos todavía está medio nebuloso, empieza por aquí. Te ahorra tropiezos y un par de cejas fruncidas.
Qué es Substrate hoy: el Polkadot SDK por dentro
Qué es Substrate dentro del Polkadot SDK, cómo se reparten FRAME, Cumulus y XCM, y cómo ubicar un crate por su prefijo sc-, sp- o pallet-.
Blockchain14 min de lectura→La cadena como función de transición de estado
Estado N más bloque N da estado N+1: qué significa que el runtime de Substrate sea la función de transición de estado y qué queda para el nodo.
Blockchain14 min de lectura→
En una reunión de diseño, alguien propone algo razonable a primera vista: "Nuestro pallet necesita el precio del dólar. Agreguemos al nodo una función que el runtime pueda llamar para pedirlo por HTTP." Suena a una línea de código. En Substrate es una decisión que el equipo tendría que mantener durante toda la vida de la cadena, y además rompería el determinismo que vimos en el capítulo anterior. Para entender por qué, hay que saber dónde vive el runtime y por qué puertas habla con el nodo.
Este capítulo responde dos preguntas concretas. Primero, dónde está físicamente el runtime: en el estado de la cadena, bajo una clave llamada :code. Segundo, cómo se comunican nodo y runtime: por dos canales con dirección opuesta, las runtime APIs y las host functions.
Brújula de lectura
Actor, evidencia y estado
Actor
Nodo como ejecutorCarga el blob Wasm del estado del bloque padre y lo ejecuta en un entorno aislado.
Evidencia
Clave :code y spec_versionEl código vigente está en el estado; su versión dice si dos runtimes son el mismo.
Estado
Bytes opacos para el nodoSalvo unas pocas claves well-known, solo el runtime sabe qué significa cada valor.
Preguntas guía
- ¿Por qué el runtime se compila a Wasm y se guarda dentro del estado?
- ¿Qué son las host functions y las runtime APIs, y en qué dirección va cada una?
- ¿Qué claves del estado entiende el nodo y cuáles le resultan opacas?
- ¿Por qué agregar una host function es un compromiso de por vida?
Conexiones dentro de la serie
- Antes: La cadena como función de transición de estado define el runtime como la STF; Qué es Substrate hoy presenta los crates
sp_ioysp_api. - Contraste: EVM: stack, memory, storage y calldata explica la máquina virtual de Ethereum, que ejecuta contratos ajenos con gas.
- Después: FRAME: pallets que se ensamblan en un runtime muestra cómo se escribe ese runtime sin hacerlo todo a mano.
Wasm y el "meta protocolo": guardar la función dentro del estado
WebAssembly (Wasm) es un formato de código binario portable: se compila una vez y lo ejecuta cualquier máquina que tenga un intérprete o compilador de Wasm. Para Substrate tiene una ventaja clave: el mismo blob produce el mismo comportamiento en un servidor Linux, en un Mac o en un validador con otra arquitectura.
La documentación llama a este diseño el WASM Meta Protocol. Parte de una observación: el software de una cadena tradicional es un artefacto monolítico, y actualizar cualquier parte obliga a actualizar todo. Substrate mezcla esa realidad con una idea de las plataformas de contratos, guardar código en el estado, y la lleva al extremo: guarda la STF completa en el estado principal de la cadena. [1]
La frase central es esta: "The Runtime is stored under a special key in the state (see [sp_core::storage::well_known_keys]) and can be updated as a part of the state transition function's execution, just like a user's account balance can be updated." [1] El runtime puede cambiarse a sí mismo, igual que cambia un saldo. De ahí salen las actualizaciones sin bifurcación.
La especificación del protocolo Polkadot nombra la clave: el runtime vigente se guarda bajo los bytes 3A,63,6F,64,65, "which is the ASCII byte representation of the string :code". [2] El código del SDK define la misma constante:
// Fuente: substrate/primitives/storage/src/lib.rs (paritytech/polkadot-sdk)
pub mod well_known_keys {
/// Wasm code of the runtime.
pub const CODE: &[u8] = b":code";
// ...
pub const HEAP_PAGES: &[u8] = b":heappages";
}El fragmento es ilustrativo: se omitieron comentarios y otras constantes del módulo. [3] Dos detalles de la especificación completan el cuadro. El blob puede venir comprimido con zstd, marcado con un prefijo de 8 bytes y con un límite de descompresión para evitar "bombas" de compresión. Y como el runtime puede cambiar en cualquier bloque, el ejecutor "must always load the latest Runtime from storage", tomando el estado del bloque padre al importar o el del mejor bloque al producir. [2]
Las dos puertas entre nodo y runtime
Nodo y runtime son programas distintos: uno es un binario nativo, el otro un blob Wasm encerrado en un entorno aislado (sandbox). Necesitan hablarse, y lo hacen por dos canales con sentido opuesto. [1]
Runtime APIs: el nodo llama al runtime. Son funciones que el runtime exporta para que el nodo las invoque. Se definen con las macros de sp_api. La más fundamental es sp_api::Core, que según la documentación cualquier cadena debe implementar "in order to be able to (re) execute blocks". [1] Su método execute_block recibe un bloque y lo aplica. Un runtime puede exponer más APIs; los motores de consenso, por ejemplo, suelen pedir una propia.
Host functions: el runtime llama al nodo. El runtime vive aislado: no puede abrir archivos ni sockets. Cuando necesita algo del mundo exterior, se lo pide al nodo por una host function. Todas están definidas en sp_io. [1] El ejemplo canónico es sp_io::storage: el conjunto de funciones con las que el runtime lee y escribe el estado, que físicamente guarda la base de datos del nodo. El glosario resume: las host functions son la API del nodo, provistas por el host al runtime Wasm. [4]
Nodo y runtime: dos puertas y un estado compartido
Cómo leer esta figura
A la izquierda, una caja azul Nodo, binario nativo, con red, base de datos, consenso, RPC y ejecutor Wasm. A la derecha, una caja verde Runtime, blob Wasm, con la STF: saldos, reglas, gobierno y lógica propia, sin acceso directo a disco ni red. Una flecha azul superior va del nodo al runtime con el rótulo runtime APIs, sp_api, y debajo Core::execute_block. Una flecha verde inferior va del runtime al nodo con el rótulo host functions, sp_io, y debajo storage::get y set. Abajo, una franja Estado con tres cajas: una violeta con la clave :code apuntando a blob Wasm, otra con :heappages, y otra gris con un par de bytes hexadecimales. Bajo las dos primeras dice claves well-known: el nodo sí las entiende; bajo la tercera, bytes opacos para el nodo. Una flecha discontinua sube de :code al nodo con el rótulo carga el runtime vigente en el estado.
Con las dos puertas a la vista, podemos seguir la ejecución de un bloque paso a paso, tal como la describe la documentación del SDK. [1]
Flujo visual
Cómo importa un bloque un nodo Substrate
El estado es opaco para el nodo
Aunque la base de datos vive en el nodo, el nodo no entiende casi nada de lo que guarda. La documentación lo explica con precisión: el runtime es el único componente que puede actualizar el estado y el único que puede interpretarlo por completo. [1] Para el nodo, las claves y los valores son "opaque bytes": no sabe si un valor es un número o una lista.
Las excepciones son las claves well-known (claves conocidas), que el nodo sí sabe leer. La más importante es :code, sin la cual no podría cargar el runtime. Otra es :heappages, que indica cuántas páginas de memoria lineal Wasm necesita el runtime para ejecutarse. [3] Fuera de ese puñado, el nodo guarda lo que el runtime le pide y devuelve lo que el runtime le pregunta.
Esta opacidad no es un descuido: es lo que permite actualizar sin bifurcación. Como el nodo no conoce los detalles de la STF, la STF puede cambiar sin que el nodo se entere. [1] Si el nodo supiera, por ejemplo, que el saldo de una cuenta es un entero de 128 bits en tal posición, un cambio de formato en el runtime obligaría a cambiar también el nodo.
Runtime nativo, versiones y el costo de una host function
Durante años, el nodo llevaba además una copia nativa del runtime compilada dentro del binario, el runtime nativo, para ganar velocidad y facilitar la depuración. La documentación es clara sobre su destino: ninguno de los dos argumentos se sostuvo "and the native runtime is being fully removed from the node-sdk". [1] Hoy todas las cadenas Substrate ejecutan solo Wasm. Si un material viejo habla de "estrategia de ejecución nativa", trátalo como historia.
De esa época queda una pieza vigente: la versión del runtime. Cada runtime expone, por la runtime API Core::version, una estructura RuntimeVersion. Su campo spec_version es la clave principal para saber si dos runtimes son el mismo, y por eso la documentación insiste en subirlo antes de cada actualización. [1] Lo retomaremos al hablar de actualizaciones.
Ahora sí, la propuesta del inicio. Un nodo debe implementar todas las host functions que el runtime necesita en cualquier momento. Como el runtime cambia con el tiempo y un nodo normalmente quiere poder re-ejecutar todos los bloques anteriores, debe mantener también las host functions viejas. De ahí la advertencia: "This implies that adding a new host function is a big commitment and should be done with care." En Polkadot, agregar una host function siempre requiere un RFC. [1]
La función HTTP propuesta tiene además otro problema: dos nodos que pidan el precio en momentos distintos obtienen respuestas distintas, y la STF deja de ser determinista. Las necesidades de datos externos se resuelven por otros caminos (los offchain workers, que la ruta trata más adelante, o datos que entran en el bloque). Una host function nueva casi nunca es la respuesta.
Qué cambia respecto de Ethereum
Si vienes de la EVM, la palabra "máquina virtual" puede llevarte a pensar que el runtime Wasm es como un contrato grande. No lo es, y la diferencia más útil está en la confianza. La EVM ejecuta contratos que escribió cualquiera; desde el punto de vista de la cadena, es código ajeno y no confiable. Por eso cada instrucción tiene un costo en gas y la ejecución se corta al agotarlo. [5]
El runtime de Substrate es otra cosa. La documentación de pesos lo dice sin matices: "The runtime is trusted code, and it is assumed to be written by the same team/developers who are running the blockchain itself." [5] Por eso, "In Polkadot-SDK, the WASM runtime is not assumed to be metered": no se cuenta gas instrucción por instrucción. En su lugar, el costo de cada operación se estima por adelantado con benchmarks y se expresa como peso (weight), un tema con capítulo propio.
La tercera diferencia es de ubicación. En Ethereum, el código de cada contrato vive en el estado, pero las reglas del protocolo viven en los clientes. En Substrate, las reglas de la propia cadena son el código que vive en el estado, bajo :code. Es la idea de "código en el estado" aplicada a toda la STF, no a un contrato.
Qué llevarte de este capítulo
El runtime es un blob Wasm guardado en el estado bajo :code, y el nodo lo carga del estado del bloque padre cada vez que importa un bloque. Nodo y runtime se hablan por dos puertas: el nodo llama al runtime por runtime APIs (sp_api) y el runtime pide servicios al nodo por host functions (sp_io). El estado es opaco para el nodo salvo unas pocas claves well-known. Agregar una host function obliga al nodo a mantenerla para siempre, así que casi nunca es la solución. Con esto claro, el siguiente paso es ver cómo se escribe un runtime con FRAME.
Bibliografía académica
- [1]
Parity Technologies, "WASM Meta Protocol," polkadot_sdk_docs, paritytech/polkadot-sdk, docs/sdk/src/reference_docs/wasm_meta_protocol.rs. [Online]. Available: https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/sdk/src/reference_docs/wasm_meta_protocol.rs (consultado 2026-11-13).
Runtime en el estado, host functions, runtime APIs, nodo frente a runtime y runtime nativo.
- [2]
Web3 Foundation, "State and Runtime," Polkadot Protocol Specification, w3f/polkadot-spec, docs/chap-state.md. [Online]. Available: https://raw.githubusercontent.com/w3f/polkadot-spec/main/docs/chap-state.md (consultado 2026-11-13).
Clave :code, carga del runtime desde el estado y compresión zstd.
- [3]
Parity Technologies, "sp-storage: well_known_keys," paritytech/polkadot-sdk, substrate/primitives/storage/src/lib.rs. [Online]. Available: https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/substrate/primitives/storage/src/lib.rs (consultado 2026-11-13).
Constantes CODE (:code) y HEAP_PAGES (:heappages).
- [4]
Parity Technologies, "Glossary," polkadot_sdk_docs, paritytech/polkadot-sdk, docs/sdk/src/reference_docs/glossary.rs. [Online]. Available: https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/sdk/src/reference_docs/glossary.rs (consultado 2026-11-13).
Definiciones de estado, STF, host, nodo, dispatchable, extrinsic y pallet.
- [5]
Parity Technologies, "FRAME Benchmarking and Weights," polkadot_sdk_docs, paritytech/polkadot-sdk, docs/sdk/src/reference_docs/frame_benchmarking_weight.rs. [Online]. Available: https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/sdk/src/reference_docs/frame_benchmarking_weight.rs (consultado 2026-11-13).
El runtime Wasm no se mide con gas: es código confiable que se pesa por adelantado.
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.
Ethereum: de historia a labs
Serie progresiva para entender Ethereum desde el whitepaper, forks y The Merge hasta EVM, Solidity, proof-of-stake, rollups, account abstraction, DeFi, observabilidad y laboratorios reproducibles.
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.
Qué es Substrate hoy: el Polkadot SDK por dentro
Substrate ya no es un repositorio aparte: es el framework base del Polkadot SDK. Un mapa de nombres, piezas y crates para empezar sin perderse.
La cadena como función de transición de estado
El modelo mental que sostiene toda la ruta: una función determinista toma el estado y un bloque y devuelve el estado siguiente. En Substrate esa función se llama runtime.
FRAME: pallets que se ensamblan en un runtime
Un pallet encapsula una responsabilidad; el runtime es la suma de pallets configurados. Anatomía de un pallet, cómo se ensamblan y por qué un pallet no es un contrato inteligente.
Tu primer nodo local: templates, omni-node y chain spec
Del runtime compilado a bloques locales: omni-node con --dev-block-time, chain-spec-builder y presets de génesis, y cuándo necesitas un nodo propio porque quieres cambiar el consenso.