Substrate: del runtime al consenso propio
Artículo 11 de 20
55%
completado al llegar aquí
Sistemas distribuidosNota de investigación17 min de lectura
Producción de bloques por turnos: slots y Aura
Cómo Substrate divide el tiempo en slots, por qué Aura asigna cada slot a A[s mod |A|] y qué falta para que esos bloques sean definitivos.
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.
Tu primer nodo local: templates, omni-node y chain spec
Tres caminos para levantar una cadena Substrate local: polkadot-omni-node con chain spec, un template o el nodo kitchensink, y qué guarda el génesis.
Blockchain16 min de lectura→Extrinsics: transacciones firmadas, sin firmar e inherentes
Qué entra a un bloque Substrate desde fuera del runtime, cómo se codifica un extrinsic versión 5 y qué comprueban las transaction extensions antes de ejecutarlo.
Blockchain16 min de lectura→
Tres empresas comparten una cadena privada construida con Substrate (Polkadot SDK). Cada una opera un nodo validador, y las tres claves de autoridad están en el chain spec desde el génesis. Un lunes, el nodo de la tercera empresa se reinicia por mantenimiento. Nadie toca la configuración, pero el panel de operaciones muestra algo curioso: la cadena sigue creciendo, aunque uno de cada tres turnos queda vacío. Cuando el nodo vuelve, los huecos desaparecen.
Ese comportamiento no es una falla: es exactamente lo que promete Aura, el motor de producción de bloques más simple de Substrate. Este capítulo explica por qué. Al terminar, deberías poder responder quién produce el próximo bloque, cuándo lo hace, qué verifica un nodo cuando recibe un bloque ajeno y por qué una cadena que solo usa Aura todavía puede revertir.
Brújula de lectura
Actor, evidencia y estado
Actor
Autoridad dueña del slotUna lista fija de autoridades se turna: el slot s corresponde a A[s mod |A|].
Evidencia
Número de slot y sello firmadoLa cabecera lleva el slot en un digest previo y la firma del autor en un digest de sello.
Estado
Cadena más larga, todavía revisableAura elige la cadena más larga; la finalidad la aporta otro protocolo.
El reloj compartido: qué es un slot
Un slot es un intervalo de tiempo durante el cual ciertos eventos pueden o deben ocurrir. Así lo define el crate genérico de Substrate para motores basados en slots: "Some consensus algorithms have a concept of slots, which are intervals in time during which certain events can and/or must occur." [1] La idea es vieja y sencilla: en lugar de discutir en cada momento quién habla, se reparte el tiempo en turnos de igual duración y se fija de antemano a quién le toca cada turno.
Para que los turnos sirvan, todos tienen que contar el tiempo de forma parecida. Aura lo asume explícitamente: funciona con una lista de autoridades "who are expected to roughly agree on the current time". [2] No hace falta un reloj perfecto, pero sí uno razonable. Si el reloj de un nodo se adelanta o se atrasa demasiado, ese nodo creerá estar en otro slot, y sus bloques o sus juicios sobre bloques ajenos dejarán de coincidir con los del resto.
¿Cómo sabe un nodo en qué slot está? En el solochain template, el nodo toma la hora del sistema como inherente de timestamp y calcula el slot a partir de ese timestamp y de la duración del slot; el código usa sp_timestamp::InherentDataProvider::from_system_time() y luego from_timestamp_and_slot_duration. [3] La cuenta es una división entera: milisegundos desde la época Unix divididos por la duración del slot en milisegundos. Un inherente, como vimos en el capítulo de extrinsics, es un dato que el autor del bloque inserta y que nadie firma; el timestamp es el ejemplo clásico.
La duración del slot no la decide el nodo, sino el runtime. Aura expone una runtime API con dos funciones: slot_duration() y authorities(). El comentario del código aclara un detalle operativo importante: "Currently, only the value provided by this type at genesis will be used." [4] Cambiar la duración del slot en una cadena viva, por tanto, no es tan simple como publicar un runtime nuevo con otro número.
Aura en una línea: A[s mod |A|]
Con un reloj común y una lista de autoridades, la regla de Aura cabe en una línea. El código la enuncia así: "Time is divided up into discrete slots of t seconds each. For each slot s, the author of that slot is A[s % |A|]." [2] Si hay tres autoridades, el slot 0 es de la primera, el 1 de la segunda, el 2 de la tercera, el 3 vuelve a la primera, y así sucesivamente. Es un round robin puro: no hay sorteo, no hay peso por stake, no hay secreto.
La regla tiene una segunda mitad que importa tanto como la primera: "The author is allowed to issue one block but not more during that slot, and it will be built upon the longest valid chain that has been seen." [2] Un bloque por slot, como máximo, y siempre sobre la cadena válida más larga que el autor conoce. La función que implementa la asignación en el SDK es corta:
// substrate/client/consensus/aura/src/standalone.rs (fragmento ilustrativo)
pub fn slot_author<P: Pair>(slot: Slot, authorities: &[AuthorityId<P>]) -> Option<&AuthorityId<P>> {
if authorities.is_empty() {
return None;
}
let idx = *slot % (authorities.len() as u64);
authorities.get(idx as usize)
}Volvamos al escenario inicial. Con tres autoridades y la tercera apagada, los slots 2, 5, 8 y siguientes no tienen bloque. El autor del slot 3 no espera: construye sobre el bloque del slot 1, que es la punta de la cadena más larga que conoce. La cadena pierde un tercio de su capacidad, pero no se detiene. La figura 1 muestra ese patrón.
Aura: turnos fijos y slots vacíos
Fuentes:
[2]Cómo leer esta figura
Una fila de ocho slots numerados del 0 al 7, cada uno con su autoridad: A0, A1, A2, A0, A1, A2, A0, A1. Debajo, los bloques B0, B1, B3, B4, B6 y B7 en verde y dos casillas vacías en rojo discontinuo en los slots 2 y 5, marcadas como A2 apagado. Flechas curvas unen B1 con B3 y B4 con B6, saltando los huecos. Abajo, dos recuadros: a la izquierda la regla, autor del slot s igual a A[s mod |A|], un bloque como máximo sobre la cadena más larga; a la derecha la advertencia de que Aura produce pero no finaliza y que la cadena más larga puede cambiar hasta que GRANDPA vote.
¿Qué pasa si dos autoridades creen ser dueñas del mismo slot? Con la misma lista y relojes razonables, no debería ocurrir: la regla es determinista. Lo que sí puede pasar es que una autoridad deshonesta o mal configurada firme dos bloques distintos en su propio slot. Eso se llama equivocación, y lo retomamos al hablar de verificación.
Dos mitades: el nodo propone, el runtime informa
Hasta aquí, Aura parece una fórmula. En el código es una colaboración entre el nodo y el runtime. La documentación del SDK lo resume así: "the majority of the implementation of any consensus engine is in the node side, but the runtime also typically needs to expose a custom runtime-api to enable the particular consensus engine to work". [5] En Aura, esa runtime API es AuraApi, la que entrega la duración del slot y la lista de autoridades. [4]
En el lado del nodo trabaja un SlotWorker. El crate de slots ofrece un esqueleto genérico, SimpleSlotWorker, que "tries to claim a slot at its beginning and tries to produce a block if successfully claimed, timing out if block production takes too long". [1] Aura implementa sus piezas: claim_slot comprueba si la clave local es la del autor del slot, y pre_digest_data agrega a la cabecera el número de slot.
Cuando el slot es propio, el worker pide un bloque al Proposer, la pieza que arma el bloque con extrinsics del pool y con inherentes. El Proposer es genérico; como dice sp-consensus, "Proposers are generic over bits of "consensus data" which are engine-specific." [6] Aura le pasa su digest de slot y, al final, sella la cabecera con la firma del autor.
Flujo visual
Un slot propio, paso a paso
Hay un parámetro que se ajusta en el servicio del nodo y que conviene entender: block_proposal_slot_portion, "The proportion of the slot dedicated to proposing." [2] Proponer no puede ocupar todo el slot, porque el bloque todavía tiene que viajar por la red y ser importado por los demás antes de que empiece el siguiente turno. Hay además un máximo, max_block_proposal_slot_portion, que se aplica cuando el nodo concede tiempo extra porque hubo slots sin bloques. [2]
Cómo verifica un nodo un bloque Aura ajeno
Producir es la mitad del trabajo. La otra mitad ocurre en cada nodo que recibe el bloque: la cola de importación (import queue) lo verifica antes de pasarlo al block import. El verificador de Aura comprueba que la cabecera esté firmada por la clave correcta para su slot; según su documentación, "If the slot is too far in the future, an error will be returned." [7]
¿Cuánto es "demasiado lejos"? En el código, el verificador compara contra el slot actual más uno; el comentario dice "we add one to allow for some small drift". [7] Un bloque que declara un slot más adelantado se rechaza con el mensaje "too far in the future". Esto convierte la sincronización de relojes en un requisito operativo: un nodo con el reloj atrasado rechazará bloques legítimos, y un autor con el reloj adelantado verá sus bloques rechazados.
Si el bloque trae cuerpo, el verificador también pide al runtime que revise los inherentes, para comprobar "that the internally-set timestamp in the inherents actually matches the slot set in the seal". [7] Así, el autor no puede declarar un slot en la cabecera y un timestamp incompatible dentro del bloque.
El verificador también puede buscar equivocaciones: dos cabeceras distintas firmadas por el mismo autor para el mismo slot. La función compartida del crate de slots devuelve la prueba si la encuentra, aunque solo dentro de una ventana acotada de slots recientes. [1] Qué se hace con esa prueba (reportarla, castigar) ya no es tarea de Aura, sino del runtime; el capítulo sobre vectores de ataque BFT explica por qué importa.
Por último, el verificador marca el bloque con la regla de elección de cadena: ForkChoiceStrategy::LongestChain. [7] Cuando hay dos ramas, el nodo considera mejor la más larga. Si los relojes se desordenan o la red se parte, cada lado puede seguir creciendo por su cuenta; el capítulo sobre GST y estabilización global explica por qué los protocolos que suponen sincronía parcial no prometen nada útil hasta que la red vuelve a comportarse.
Lo que Aura no hace: finalidad
Aura responde a la pregunta de quién produce y cuándo. No responde a otra igual de importante: cuándo un bloque ya no puede revertirse. Con la regla de la cadena más larga, un bloque está "probablemente" asentado si tiene muchos bloques encima, pero nada impide que aparezca una rama más larga que lo deje fuera. El capítulo sobre el doble gasto muestra cómo se explota esa ventana.
Por eso el solochain template no usa Aura sola. Su servicio crea primero el block import de GRANDPA con sc_consensus_grandpa::block_import y se lo entrega a la cola de importación de Aura, de modo que todo bloque verificado por Aura pasa también por la capa de finalidad. [3] Aura produce; GRANDPA, que estudiaremos en el capítulo 13, finaliza. La separación es deliberada: si la finalidad se detiene, la producción puede seguir.
Decisiones al configurar Aura
Reglas de decisión rápida
¿Las autoridades son pocas, conocidas y estables?
Consorcio, red de pruebas, parachain con collators fijos.
→ Aura más una finalidad es razonable
¿Preocupa que se sepa de antemano quién produce?
Ataques de denegación de servicio dirigidos al próximo autor.
→ Evaluar BABE o Sassafras
¿Slots cortos con bloques pesados?
La propuesta no termina dentro de la proporción del slot.
→ Alargar el slot o reducir el peso máximo
¿Relojes sin NTP ni monitoreo?
Bloques rechazados por 'too far in the future'.
→ Sincronizar relojes antes de culpar al consenso
Qué cambia respecto de Ethereum
Quien viene de la ruta de Ethereum ya conoce los slots. En la Beacon Chain, el tiempo se divide en slots de 12 segundos y en cada slot se elige un validador que propone; el capítulo sobre la Beacon Chain lo detalla. La diferencia está en cómo se elige. En Ethereum, la selección depende del saldo efectivo de cada validador y de aleatoriedad del protocolo. En Aura, el turno es una cuenta modular sobre una lista fija, sin peso ni azar.
La segunda diferencia es la regla de elección de cadena. Ethereum combina LMD-GHOST, que cuenta votos de testigos, con Casper FFG para la finalidad; lo vimos en Gasper. Aura usa la cadena más larga y deja toda la finalidad a otro protocolo. El patrón de fondo es el mismo en ambos mundos (un mecanismo produce, otro finaliza), pero en Substrate cada mitad es una pieza intercambiable del nodo, y por eso el mismo SDK ofrece Aura, BABE y otros motores.
La tercera diferencia es de escala y de propósito. Aura está pensada para conjuntos de autoridades conocidos y moderados: redes privadas, entornos de prueba y, como veremos en el capítulo 15, parachains cuyos collators se turnan. No intenta resolver la selección abierta de cientos de miles de validadores.
Para seguir: el capítulo anterior, offchain workers, cerró la parte del runtime. El siguiente, BABE, reemplaza el turno fijo por una lotería secreta.
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.
Objetivo. Ver con los propios ojos la regla A[s mod |A|], los slots vacíos y el efecto del reloj sobre la verificación.
Pasos propuestos.
- Levantar el solochain template con tres autoridades en un chain spec local, cada una con su propia clave Aura.
- Apagar una autoridad y observar en los logs los slots que quedan vacíos.
- Desfasar el reloj de un nodo (en un contenedor o una máquina virtual) y registrar qué bloques se rechazan o se difieren.
Evidencia esperada. Con tres autoridades activas, un bloque por slot. Con una apagada, huecos regulares cada tres slots y bloques que saltan sobre el hueco. Con un reloj adelantado más de un slot, rechazos del tipo "too far in the future" en los demás nodos. Los valores exactos de duración de slot y los mensajes de log dependen de la versión del template y quedan a validar al construir el laboratorio.
Bibliografía académica
- [1]
Parity Technologies, "sc-consensus-slots: slot-based consensus engines," polkadot-sdk, substrate/client/consensus/slots/src/lib.rs, commit 544e829, 2026.
Definición de slot, SlotWorker y SimpleSlotWorker, proporción del slot dedicada a proponer.
- [2]
Parity Technologies, "sc-consensus-aura: Aura (Authority-round) consensus in Substrate," polkadot-sdk, substrate/client/consensus/aura/src/lib.rs, commit 544e829, 2026.
Asignación A[s % |A|], un bloque por slot sobre la cadena más larga y supuesto de relojes aproximadamente de acuerdo.
- [3]
Parity Technologies, "Solochain template: node service," polkadot-sdk, templates/solochain/node/src/service.rs, 2026.
Cómo el template conecta Aura, el inherente de timestamp y el block import de GRANDPA.
- [4]
Parity Technologies, "sp-consensus-aura: primitives for Aura," polkadot-sdk, substrate/primitives/consensus/aura/src/lib.rs, commit 544e829, 2026.
Runtime API AuraApi: slot_duration() y authorities().
- [5]
Parity Technologies, "polkadot_sdk_docs: (Omni) Node," polkadot-sdk, docs/sdk/src/reference_docs/omni_node.rs, commit 544e829, 2026.
El consenso vive sobre todo en el nodo, pero el runtime expone una runtime API propia del motor.
- [6]
Parity Technologies, "sp-consensus: Environment and Proposer," polkadot-sdk, substrate/primitives/consensus/common/src/lib.rs, commit 544e829, 2026.
Proposer genérico sobre datos de consenso propios de cada motor.
- [7]
Parity Technologies, "sc-consensus-aura: import queue," polkadot-sdk, substrate/client/consensus/aura/src/import_queue.rs, commit 544e829, 2026.
Verificación de cabeceras Aura: firma del autor del slot, margen de un slot, chequeo de inherentes y fork choice LongestChain.
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.
BABE: una lotería VRF para producir bloques
La lotería secreta de BABE: umbral por peso, constante c, slots primarios y secundarios, épocas que fijan la aleatoriedad con antelación y una regla de cadena que cuenta bloques primarios.
Anatomía del consenso en un nodo Substrate
No hay guía oficial para cambiar el consenso de un nodo Substrate. Este capítulo la reconstruye desde el código: por dónde entra un bloque, quién lo verifica, quién lo finaliza y dónde se enchufa cada motor.
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.
Sassafras: un solo productor por slot, en secreto
Sassafras promete el intervalo fijo de Aura con el secreto de BABE. Este capítulo explica el mecanismo de tickets y separa con cuidado lo que existe en el código de lo que es intención de investigación.