Substrate: del runtime al consenso propio
Artículo 20 de 20
100%
completado al llegar aquí
Sistemas distribuidosNota de investigación21 min de lectura
Capstone: conectar un consenso BFT propio (estilo HotStuff) a un nodo Substrate
Guía de ingeniería, desde el código del Polkadot SDK, para reemplazar GRANDPA o la producción por un protocolo BFT estilo HotStuff: QC como justificación, sync y pruebas.
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.
GRANDPA: finalizar cadenas, no bloques
Cómo GRANDPA finaliza prefijos de cadena con prevotes, precommits y 2/3-GHOST, por qué tolera f < n/3 y qué pasa cuando la finalidad se detiene.
Blockchain18 min de lectura→Ponerse al día: full, fast y warp sync; light clients
Cómo un nodo nuevo llega al estado actual de una cadena Substrate sin confiar en un peer: full, fast y warp sync, justificaciones GRANDPA y light clients.
Blockchain15 min de lectura→BEEFY: finalidad compacta para puentes
Por qué GRANDPA es caro de verificar en Ethereum y cómo BEEFY firma compromisos ECDSA sobre una raíz MMR, con muestreo aleatorio en el light client del puente.
Blockchain17 min de lectura→Anatomía del consenso en un nodo Substrate
El recorrido de un bloque por el nodo, leído en el código del Polkadot SDK: import queue, BlockImport, fork choice, justificaciones, finalidad y autoría.
Blockchain21 min de lectura→
Un equipo tiene una cadena Substrate con Aura y GRANDPA. El negocio pide finalidad en un tiempo acotado y predecible, y el equipo ya conoce HotStuff: votos sobre propuestas, certificados de quórum y una regla de commit clara. La pregunta del arquitecto es directa: ¿podemos quitar GRANDPA y poner nuestro protocolo? ¿Y si además queremos que el líder de cada vista produzca el bloque, en lugar de Aura?
Este capítulo cierra la ruta respondiendo esa pregunta como una guía de ingeniería. No hay un tutorial oficial: como vimos en la anatomía del consenso, la guía del SDK sobre cambiar el consenso está vacía. Todo lo que sigue se apoya en el código del Polkadot SDK y en los capítulos de investigación de HotStuff, y separa con cuidado lo que el código garantiza de lo que es una decisión de diseño.
Brújula de lectura
Actor, evidencia y estado
Actor
Tu voter BFT, dentro del nodoUna tarea esencial con su pacemaker, su protocolo de red y su envoltorio de importación.
Evidencia
QC guardado como justificaciónUna pareja (engine id, QC codificado) que cualquier nodo puede verificar contra el conjunto de validadores.
Estado
Último bloque final y conjunto de validadores vigenteEl primero lo mueve tu regla de commit; el segundo lo decide el runtime.
El autor integró un mecanismo de consenso propio en una aplicación real construida con Substrate. Los detalles de esa experiencia todavía no están documentados y este capítulo no los describe ni los supone. Cuando se incorporen, irán marcados como experiencia y separados de los hechos documentados. Las preguntas que el autor responderá son:
- ¿Qué protocolo se integró y qué reemplazó: solo la finalidad, solo la producción o ambas?
- ¿Sobre qué versión de Substrate o del Polkadot SDK, y con qué plantilla de nodo de partida?
- ¿Qué formato tuvo la prueba de finalidad y quién la verificaba al importar y al sincronizar?
- ¿Cómo se implementaron el pacemaker y la red de votos: protocolo de notificaciones propio, gossip u otro canal?
- ¿Cómo se gestionaron los cambios de conjunto de validadores y qué papel tuvo el runtime?
- ¿Qué falló primero en pruebas y qué falló primero en producción?
- ¿Qué parte del código o de la documentación interna se puede citar públicamente?
Preguntas guía
- ¿Gadget de finalidad junto a Aura o BABE, o un protocolo único que produce y finaliza?
- ¿Qué es el QC en términos de Substrate y quién lo verifica?
- ¿Dónde vive el pacemaker y cómo viajan los votos por la red del nodo?
- ¿Qué se pierde al retirar GRANDPA (warp sync, light clients, BEEFY) y cómo se recupera?
- ¿Cómo se prueba safety antes de la red real?
Conexiones dentro de la serie
- Antes: Anatomía del consenso dio el mapa del nodo. GRANDPA, Sincronización y light clients y BEEFY son lo que tu protocolo hereda o reemplaza.
- Investigación de HotStuff: QC, highQC y la regla de tres cadenas, view-change y pacemaker, Fast-HotStuff y AggQC de dos cadenas, safety, liveness y responsiveness, Substrate y HotStuff: sincronizar cuando finalizas rápido y verificación formal con TLA+.
La decisión de arquitectura: gadget o protocolo único
Hay precedente de que se puede. La documentación del SDK cita una cadena que construyó "both custom FRAME pallets and custom node-side components": aleph-node, de Cardinal Cryptography. [1] No describimos aquí su diseño; el dato importante es que el SDK reconoce este camino como legítimo.
La primera decisión es de arquitectura. Puedes mantener la producción de bloques existente (Aura o BABE) y reemplazar solo la finalidad, o diseñar un protocolo que haga ambas cosas.
El argumento a favor del gadget viene del propio artículo de GRANDPA: separar producción y finalidad permite "decouple safety and liveness in order to speed up recovery from failures." [2] La cadena sigue creciendo aunque la finalidad se detenga, y la finalidad se pone al día cuando vuelve la red.
El argumento a favor del protocolo único viene de HotStuff: un líder correcto puede llevar al consenso "at the pace of actual (vs. maximum) network delay", con comunicación lineal en el número de réplicas. [3] Si el líder de la vista propone el bloque y el mismo protocolo lo certifica, no hay dos relojes que coordinar. El coste es que, sin quórum, tampoco se producen bloques.
Gadget de finalidad o protocolo único
El QC como justificación
En HotStuff, un QC (quorum certificate) reúne votos firmados de n − f réplicas sobre una propuesta. El artículo de GRANDPA usa el mismo umbral: con f menor que n/3 votantes bizantinos, "We need n − f of voters to agree on finality." [2] El capítulo QC, highQC y la regla de tres cadenas explica cuándo un QC basta para comprometer un bloque.
En Substrate, la prueba de finalidad de un bloque tiene un tipo concreto: Justification = (ConsensusEngineId, EncodedJustification). [4] La traducción es directa: eliges un identificador de motor de cuatro bytes que no choque con los existentes (GRANDPA usa FRNK, Sassafras SASS) y codificas como bytes lo que haga falta para verificar el commit.
¿Qué hace falta? Depende de tu regla. Si comprometes con una regla de tres cadenas, el QC del bloque comprometido no basta: la prueba debe mostrar la cadena de QC consecutivos que dispara el commit. Esto es análisis propio a partir de la regla de HotStuff, no algo que el SDK imponga, y conviene fijarlo por escrito antes de programar. Una justificación mínima suele incluir el hash y la altura del bloque, la vista, el identificador del conjunto de validadores y las firmas o su agregado.
Hay una advertencia en el propio tipo. La justificación viaja fuera del bloque porque, si hay varias pruebas válidas, incluirlas cambiaría el hash del bloque. [4] En el protocolo único, el QC del padre sí puede ir en la propuesta, como el campo de justificación de HotStuff, porque el líder lo fija al proponer. La prueba de finalidad del propio bloque debe seguir viajando aparte. Esta separación es análisis propio y queda a validar en cada diseño.
Para elegir el esquema de firma, el capítulo de Fast-HotStuff y AggQC compara certificados agregados y de dos cadenas. Recuerda además a quién va dirigida la prueba: si un puente debe verificarla fuera de la cadena, el coste de verificación que vimos en BEEFY pasa a ser un requisito de diseño.
Conceptos de HotStuff y su pieza en un nodo Substrate
Cómo leer esta figura
Dos columnas unidas por flechas. A la izquierda, conceptos de HotStuff en azul: pacemaker y vistas, voto firmado, QC con n menos f votos, regla de commit y conjunto de réplicas. A la derecha, en verde, la pieza correspondiente del nodo Substrate: tarea del voter con temporizadores propios, protocolo de notificación propio por gossip, justificación como engine id más QC codificado, finalize_block y notificación de finalidad, y runtime con autoridades por sesión expuestas por runtime API. Debajo, bajo el título un nodo nuevo sincroniza, una fila de cajas: bloque más QC llegan por sync, JustificationImport verifica el QC, una caja ocre pregunta si los firmantes pertenecen al conjunto vigente; si la respuesta es sí, una flecha verde lleva a bloque finalizado; si es no, una flecha roja lleva a se rechaza la justificación.
Las piezas que escribes: voter, red e importación
Con la decisión tomada, el trabajo se reparte en las piezas del mapa del capítulo anterior. El patrón de referencia es GRANDPA, cuyo crate documenta el uso: crear primero un envoltorio de block import con su LinkHalf y después arrancar el voter con ese enlace y una implementación de red. [6] Tu motor puede seguir la misma forma.
El envoltorio de BlockImport. Implementa BlockImport sobre el del cliente. En cada bloque lee las señales de cambio de validadores y, si un bloque llega con justificación de tu motor en justifications, la verifica y lo marca como final. [5] Si el bloque cambia el conjunto y no trae prueba, devuelve needs_justification en ImportedAux para que sync la pida.
El JustificationImport. Implementa también JustificationImport en el mismo objeto, como hace GRANDPA, y pásalo a la import queue como justification_import. Su método on_start devuelve las justificaciones que conviene pedir a la red al arrancar. La import queue usará el Link para pedir justificaciones a sync cuando falten. [7]
El voter y el pacemaker. Es una tarea esencial que recibe notificaciones de importación, decide en qué vista está, envía y recoge votos y, cuando se cumple la regla de commit, llama a finalize_block con la justificación. Finalizar un bloque finaliza implícitamente sus antecesores y dispara notificaciones de finalidad. [8] El pacemaker, con sus temporizadores y certificados de timeout, vive aquí; el capítulo View-change y pacemaker describe qué debe hacer.
La red de votos. El solochain template muestra el patrón: el servicio registra un protocolo de notificaciones propio de GRANDPA en la configuración de red (add_notification_protocol) y entrega el servicio de notificaciones al voter. [9] Tu motor necesita su propio nombre de protocolo y su propio formato de mensajes.
La autoría, si eliges protocolo único. Reemplazas el slot worker de Aura por un worker de vistas que, siendo líder, pide un bloque al Proposer y lo sella; y el verificador de Aura por uno que compruebe el sello del líder y el QC del padre. Si mantienes Aura, puedes además frenar la autoría cuando la finalidad se atrasa: el crate de slots ofrece una estrategia "that back offs if the finalized head is lagging too much behind the tip". [10]
Dos avisos de estabilidad. sp-consensus advierte que gran parte del crate es inestable y que "Implementors of traits should not rely on the interfaces to remain the same." [11] Y el crate de GRANDPA se declara inestable. Fija la versión del SDK y presupuesta el trabajo de cada actualización.
Rotación de validadores desde el runtime
El conjunto de réplicas no debe decidirlo el nodo. En Substrate lo decide el runtime, y el motor lo lee por dos vías: la runtime API del motor, que expone las autoridades actuales, y los digests Consensus que el runtime emite cuando programa un cambio. Es la regla del capítulo anterior: nodo y runtime deben coincidir en el motor.
GRANDPA resuelve el traspaso con una idea que puedes copiar: se acuerda una altura máxima que el conjunto actual puede finalizar, y "once a block with that height is finalized the next set will pick up finalization from there." [6] En un protocolo estilo HotStuff, eso significa que el bloque que aplica el cambio debe ser comprometido por el conjunto saliente antes de que el entrante empiece a votar, y que cada QC lleva el identificador del conjunto que lo firmó.
Un error típico es verificar un QC contra el conjunto de validadores actual del nodo verificador en lugar del conjunto vigente en la altura del bloque. Al sincronizar desde lejos, esa diferencia rechaza pruebas válidas o, peor, acepta pruebas de un conjunto que ya no debería contar.
Lo que se pierde al retirar GRANDPA
Retirar GRANDPA tiene efectos fuera del voter, y los tres capítulos anteriores de la ruta explican cuáles.
Warp sync. La especificación describe warp sync como la descarga de las cabeceras donde cambió el conjunto de autoridades "and by verifying the GRANDPA justifications". [12] Sin GRANDPA, tu motor debe implementar el WarpSyncProvider del SDK, el "Warp sync backend" que genera y verifica pruebas. [13] Y debe conservar las justificaciones de cada cambio de conjunto, como hace el filtro de poda de GRANDPA, porque son los eslabones de esa prueba. [6]
Light clients. Un light client existente sabe verificar las pruebas de los motores que implementa. Si tu prueba de finalidad es nueva, no podrá verificarla sin cambios; qué motores soporta cada cliente ligero concreto queda a validar. El capítulo Substrate y HotStuff: sincronizar cuando finalizas rápido trata este efecto desde la experiencia de una integración, con la salvedad de que se apoya en una nota de ingeniería secundaria.
BEEFY. El README de BEEFY es explícito: el protocolo se apoya en supuestos de GRANDPA "and is required to be built on top of it to work correctly." [14] Si retiras GRANDPA, pierdes BEEFY y, con él, los puentes que dependen de BEEFY. Un QC con firmas agregadas podría servir directamente a un puente, pero ese verificador habría que escribirlo.
Probar safety antes de la red real
Un protocolo BFT no se valida con una red que funciona bien. Se valida con una red que falla de las formas que el modelo permite, y comprobando que nunca se finalizan dos bloques en conflicto. El capítulo Safety, liveness y responsiveness da las propiedades; aquí van como lista de pruebas.
Pruebas mínimas antes de una red con valor
0 de 6 revisados
Antes de escribir el código, conviene modelar la integración. El capítulo de verificación formal con TLA+ muestra cómo expresar la regla de commit y comprobar exhaustivamente estados pequeños. Lo que más se beneficia de un modelo no es el protocolo publicado, sino tu traducción: el traspaso de conjuntos, el arranque con justificaciones viejas y la interacción entre el pacemaker y sync.
Qué cambia respecto de Ethereum
En Ethereum, la arquitectura de gadget es la de la red: Gasper combina una regla de fork choice para la cabeza con Casper FFG como finalidad por épocas. Cuando la finalidad se detiene, la red no se para: aplica el mecanismo de inactivity leak para recuperar la supermayoría. Es el mismo argumento de separar safety y liveness que GRANDPA formula.
La diferencia está en quién puede cambiarlo. En Ethereum, el consenso es una especificación común a todos los clientes. En Substrate, el consenso de tu cadena es código de tu nodo y de tu runtime. Eso te permite poner HotStuff donde Ethereum tiene Casper FFG, pero también te deja sin la red de seguridad de varias implementaciones independientes de la misma especificación. Las pruebas de la sección anterior sustituyen, en parte, esa red.
Laboratorio propuesto
Estado: propuesta. Este laboratorio todavía no está publicado en lab.trautslab.dev; describe lo que se construirá, en local, sin red pública ni fondos reales. Es un protocolo de juguete con fines didácticos, no una implementación para producción.
Objetivo. Recorrer de punta a punta la integración de una finalidad estilo HotStuff en el solochain template, y comprobar safety con fallos inyectados.
Pasos propuestos.
- Mantener Aura para producir bloques y retirar GRANDPA del servicio del nodo: quitar su envoltorio de importación, su voter y su protocolo de red.
- Implementar un voter mínimo que firme votos sobre la red del nodo, con un protocolo de notificaciones propio, y arme un QC por altura.
- Finalizar el bloque cuando se cumpla la regla de commit elegida y guardar el QC como justificación con un engine id propio.
- Inyectar fallos: un líder caído, mensajes retrasados y un votante que equivoca. Registrar en cada caso el último bloque final de cada nodo.
- Arrancar un nodo nuevo y verificar que importa las justificaciones al sincronizar y llega al mismo bloque final.
Evidencia esperada. En todos los nodos, la misma secuencia de bloques finales, sin dos bloques finales distintos a la misma altura en ningún caso de fallo. Con el líder caído, una pausa de la finalidad seguida de un cambio de vista y su reanudación. Con el votante que equivoca, dos votos firmados para la misma vista guardados como evidencia. El nodo nuevo alcanza el mismo bloque final que los demás. Qué regla de commit usar (dos o tres cadenas) y cómo medir los tiempos queda a validar al construir el laboratorio.
Bibliografía académica
- [1]
Parity Technologies, "polkadot_sdk_docs: Substrate," polkadot-sdk, docs/sdk/src/polkadot_sdk/substrate.rs, commit 544e829, 2026.
Lista de crates de consenso y precedente de cadenas con componentes de nodo propios (aleph-node).
- [2]
A. Stewart and E. Kokoris-Kogia, "GRANDPA: a Byzantine Finality Gadget," arXiv:2007.01560, 2020.
Finality gadget, separación de safety y liveness, umbral f < n/3 y n − f votos.
- [3]
M. Yin, D. Malkhi, M. K. Reiter, G. Golan Gueta, and I. Abraham, "HotStuff: BFT Consensus in the Lens of Blockchain," arXiv:1803.05069, 2018.
Protocolo BFT con líder para sincronía parcial, responsiveness y comunicación lineal.
- [4]
Parity Technologies, "sp-runtime: Justification y Justifications," polkadot-sdk, substrate/primitives/runtime/src/lib.rs, commit 544e829, 2026.
Justification como (ConsensusEngineId, EncodedJustification); por qué viaja fuera del bloque.
- [5]
Parity Technologies, "sc-consensus: block import," polkadot-sdk, substrate/client/consensus/common/src/block_import.rs, commit 544e829, 2026.
BlockImportParams (justifications, finalized, fork_choice), ImportedAux (needs_justification, bad_justification), BlockImport y JustificationImport.
- [6]
Parity Technologies, "sc-consensus-grandpa," polkadot-sdk, substrate/client/consensus/grandpa/src/lib.rs, commit 544e829, 2026.
Uso con block_import y LinkHalf, run_grandpa_voter, cambios de autoridades, filtro de poda para warp sync; crate inestable.
- [7]
Parity Technologies, "sc-consensus: import queue," polkadot-sdk, substrate/client/consensus/common/src/import_queue.rs, commit 544e829, 2026.
Link: ganchos de la import queue sobre sync, request_justification; importación de justificaciones.
- [8]
Parity Technologies, "sc-client-api: backend (Finalizer)," polkadot-sdk, substrate/client/api/src/backend.rs, commit 544e829, 2026.
Finalizer: finalize_block con justificación y notificaciones de finalidad.
- [9]
Parity Technologies, "Solochain template: node service," polkadot-sdk, templates/solochain/node/src/service.rs, commit 544e829, 2026.
Registro del protocolo de notificaciones de GRANDPA, proveedor de warp sync, start_aura y run_grandpa_voter.
- [10]
Parity Technologies, "sc-consensus-slots," polkadot-sdk, substrate/client/consensus/slots/src/lib.rs, commit 544e829, 2026.
Estrategia de back-off de la autoría cuando la finalidad se atrasa.
- [11]
Parity Technologies, "sp-consensus: Environment, Proposer y BlockOrigin," polkadot-sdk, substrate/primitives/consensus/common/src/lib.rs, commit 544e829, 2026.
Advertencia de inestabilidad para implementadores; Environment y Proposer; BlockOrigin.
- [12]
Web3 Foundation, "Polkadot Protocol Specification: Synchronization," w3f/polkadot-spec, docs/chap-sync.md, 2026.
Full, fast y warp sync; warp sync descarga fragmentos en cambios de autoridades y verifica justificaciones GRANDPA.
- [13]
Parity Technologies, "sc-network-sync: warp sync strategy," polkadot-sdk, substrate/client/network/sync/src/strategy/warp.rs, commit 544e829, 2026.
Trait WarpSyncProvider: generar y verificar pruebas de warp sync.
- [14]
Parity Technologies, "sc-consensus-beefy: README," polkadot-sdk, substrate/client/consensus/beefy/README.md, commit 544e829, 2026.
BEEFY depende de GRANDPA para funcionar correctamente.
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.
GRANDPA: finalizar cadenas, no bloques
El finality gadget de Substrate: rondas de prevote y precommit, supermayorías que se cruzan, accountable safety, justificaciones y cambios de conjunto de autoridades sin detener la finalidad.
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.
El runtime en Wasm: nodo y runtime hablan por dos puertas
El runtime es un blob Wasm guardado en el estado bajo la clave :code. El nodo lo llama por runtime APIs y el runtime pide ayuda al nodo por host functions. Por qué la segunda puerta casi nunca se ensancha.