Polkadot separa la producción de bloques (BABE) de la finalidad (GRANDPA). La cabeza finalizada es el bloque irreversible certificado por un conjunto de autoridades validadoras de 2/3+1, mientras que la mejor cabeza es optimista y puede revertirse. A través de JSON-RPC, use chain_getFinalizedHead y chain_subscribeFinalizedHeads para rastrear la cabeza segura, y waitForFinalized de polkadot.js para sondear hasta que un bloque específico sea finalizado. Este artículo explica el mecanismo y proporciona un script ejecutable para medir la diferencia entre mejor y finalizado.
Respuesta directa: Por qué debes rastrear la cabeza finalizada, no la mejor cabeza
Cuando construyes sobre Polkadot, no debes tratar el último bloque devuelto por chain_getBlock o chain_getHead como permanente. Ese bloque es la mejor cabeza producida por BABE (el motor de producción de bloques) y puede revertirse si una bifurcación se vuelve canónica. El único bloque que es irreversible es la cabeza finalizada, certificada por GRANDPA (Acuerdo de Prefijo Derivado Recursivo Basado en GHOST). A través de JSON-RPC, puedes consultarla con chain_getFinalizedHead, suscribirte a ella con chain_subscribeFinalizedHeads, y en polkadot.js usar api.rpc.chain.getFinalizedHead() o api.derive.chain.waitForFinalized() para esperar hasta que un bloque específico sea finalizado. Este artículo explica el mecanismo de GRANDPA, lo mapea a los métodos JSON-RPC y proporciona un script reproducible para observar la diferencia en la práctica.
Para un contexto más amplio sobre la red y los endpoints de Polkadot, consulta la página de red de Polkadot y la guía RPC de Polkadot.
Mecanismo de finalidad GRANDPA: Cómo Polkadot logra la irreversibilidad
El consenso de Polkadot separa la producción de bloques de la finalidad. BABE (Asignación Ciega para la Extensión de Blockchain) produce bloques en ranuras, con un bloque elegido aleatoriamente por ranura. Esto es optimista y rápido, pero un bloque puede ser superado si una bifurcación diferente gana más trabajo. GRANDPA es un gadget de finalidad que se ejecuta en paralelo: un conjunto de autoridades validadoras con permiso vota sobre un prefijo de cadena (un ancestro GHOST) en lugar de sobre un solo bloque. Una vez que más de dos tercios del conjunto de autoridades ponderado vota por un prefijo, producen una justificación firmada que finaliza ese prefijo de manera irreversible. Esta justificación es una prueba criptográfica que cualquier cliente ligero puede verificar.
La implicación clave es que la cadena tiene dos nociones de 'cabeza': la mejor cabeza (bloque con más trabajo de BABE) y la cabeza finalizada (certificada por GRANDPA). Para cualquier efecto irreversible (un pago, una marca de agua de indexador o un estado de cuenta sobre el que actuarás), debes confiar en la cabeza finalizada. Para interfaces especulativas (por ejemplo, mostrar transacciones recientes que podrían revertirse), la mejor cabeza es aceptable. Esta distinción está documentada en la wiki de Polkadot sobre GRANDPA y en el artículo de GRANDPA.
Los cambios en el conjunto de autoridades ocurren entre eras. Cuando el conjunto de validadores cambia, GRANDPA usa un nuevo conjunto de autoridades, y las justificaciones de eras anteriores siguen siendo válidas para bloques históricos. Por eso puedes verificar la finalidad de bloques antiguos incluso después de que el conjunto cambie.
Mapeo de la finalidad a JSON-RPC: Métodos y espacios de nombres
El JSON-RPC de Polkadot expone varios métodos para consultar la finalidad. Los más importantes son:
chain_getFinalizedHeaddevuelve el hash del último bloque finalizado. Este es el marcador canónico de 'seguro de confiar'.
chain_getHead(ochain_getBlockcon el parámetro 'latest') devuelve el mejor bloque, que no es final.
chain_subscribeFinalizedHeadsempuja un flujo de encabezados de bloques finalizados a medida que se finalizan.
chain_subscribeAllHeadsempuja tanto las cabezas mejor como finalizada (y posiblemente otras), permitiéndote compararlas.
- El espacio de nombres
grandpa_*(por ejemplo,grandpa_proveFinality,grandpa_roundState,grandpa_submitGrandpaExtrinsic) proporciona justificaciones y evidencia de ronda. Estos métodos pueden no estar expuestos en todos los endpoints públicos; consulta la documentación de tu proveedor.
En polkadot.js, api.rpc.chain.getFinalizedHead() y api.rpc.chain.subscribeFinalizedHeads() envuelven estos métodos. Además, api.derive.chain.waitForFinalized(blockHash) sondea la cabeza finalizada hasta que el bloque dado sea finalizado, lanzando un RpcError o TimeoutError si la finalidad se detiene. Esta es la forma recomendada de esperar la finalidad en las aplicaciones.
Para una inmersión más profunda en los métodos RPC y la selección de endpoints, consulta la guía RPC de Polkadot y la guía de WebSocket RPC de Polkadot.
Ejemplo práctico: Midiendo la diferencia entre la mejor cabeza y la finalizada
Para ver la diferencia entre las cabezas mejor y finalizada en la práctica, ejecuta el siguiente script de Node.js. Se suscribe tanto a allHeads como a finalizedHeads, imprime la diferencia de número de bloque durante un intervalo de 60 segundos, y luego usa waitForFinalized para confirmar que un bloque específico está finalizado. Necesitas un endpoint de Polkadot (por ejemplo, wss://rpc.polkadot.io) y el paquete @polkadot/api.
Reemplaza YOUR_ENDPOINT con tu propio endpoint. Si usas el servicio API de OnFinality, obtienes un endpoint dedicado con acceso a todos los métodos RPC. Ten en cuenta que los endpoints públicos pueden tener límites de velocidad; consulta precios RPC para más detalles.
// Guarda como finality-check.js
// Ejecuta: node finality-check.js
const { ApiPromise, WsProvider } = require('@polkadot/api');
const WS_URL = process.env.WS_URL || 'wss://rpc.polkadot.io';
const INTERVAL_MS = 60000; // 60 segundos
async function main() {
const provider = new WsProvider(WS_URL);
const api = await ApiPromise.create({ provider });
console.log('Conectado a', WS_URL);
// Suscribirse a todas las cabezas (mejor y finalizada)
const unsubAll = await api.rpc.chain.subscribeAllHeads((header) => {
console.log(`Todas las cabezas: #${header.number} hash=${header.hash}`);
});
// Suscribirse a las cabezas finalizadas
const unsubFinalized = await api.rpc.chain.subscribeFinalizedHeads((header) => {
console.log(`Cabeza finalizada: #${header.number} hash=${header.hash}`);
});
// Esperar el intervalo
await new Promise(resolve => setTimeout(resolve, INTERVAL_MS));
// Obtener la mejor y la finalizada actuales
const best = await api.rpc.chain.getHead();
const finalized = await api.rpc.chain.getFinalizedHead();
const bestHeader = await api.rpc.chain.getHeader(best);
const finalizedHeader = await api.rpc.chain.getHeader(finalized);
console.log(`\nDespués de ${INTERVAL_MS/1000}s:`);
console.log(`Mejor bloque: #${bestHeader.number}`);
console.log(`Bloque finalizado: #${finalizedHeader.number}`);
console.log(`Diferencia: ${bestHeader.number - finalizedHeader.number} bloques`);
// Esperar a que un bloque específico sea finalizado (por ejemplo, el mejor bloque que acabamos de ver)
try {
const finalizedHash = await api.derive.chain.waitForFinalized(best);
console.log(`El bloque #${bestHeader.number} está finalizado con hash ${finalizedHash}`);
} catch (e) {
console.error('waitForFinalized falló:', e.message);
}
// Limpieza
await unsubAll();
await unsubFinalized();
await api.disconnect();
}
main().catch(console.error);Salida esperada y tabla de resultados
Cuando ejecutes el script, verás un flujo de encabezados. La cabeza finalizada típicamente se retrasa unos pocos bloques respecto a la mejor cabeza, pero la diferencia puede crecer si los validadores están fuera de línea o la red está congestionada. Los números exactos varían según las condiciones de la red y no son fijos. Completa la tabla a continuación con tus observaciones para caracterizar el comportamiento de tu endpoint.
Nota: El script usa waitForFinalized en el mejor bloque al final del intervalo. Si la finalidad es lenta, esto puede agotar el tiempo. El tiempo de espera es configurable en polkadot.js; por defecto puede lanzar una excepción después de un cierto período.
- Registra el número del mejor bloque y del bloque finalizado en varios puntos durante el intervalo.
- Calcula la diferencia (mejor - finalizado) y observa cualquier cambio.
- Si usas un endpoint personalizado, compara la diferencia con un endpoint público para ver si hay diferencias en la propagación de la finalidad.
- Si ves una diferencia grande (por ejemplo, > 10 bloques), investiga si la red está bajo estrés o si tu endpoint no está al día.
| Tiempo (s) | Mejor Bloque | Bloque Finalizado | Diferencia |
|------------|--------------|-------------------|------------|
| 0 | | | |
| 15 | | | |
| 30 | | | |
| 45 | | | |
| 60 | | | |Lista de verificación de fallos y soluciones: Cuando la finalidad se detiene o se retrasa
El retraso en la finalidad es normal, pero un período prolongado en el que la cabeza finalizada no avanza indica un problema. Aquí tienes una lista de verificación para diagnosticarlo y manejarlo:
- Comprueba si la mejor cabeza avanza: Si la mejor cabeza también está detenida, el nodo puede estar desconectado o la red está caída. Verifica tu conexión WebSocket y prueba con otro endpoint.
- Comprueba la salud del conjunto de autoridades: Si los validadores están fuera de línea, GRANDPA no puede finalizar. Puedes consultar
grandpa_roundState(si está disponible) para ver la ronda actual y los votos. Los endpoints públicos pueden no exponer esto; usa un endpoint dedicado del servicio API de OnFinality si es necesario.
- Decide si actuar sobre la 'mejor': Si la finalidad se detiene, debes decidir si continúas actuando sobre la mejor cabeza. Para operaciones irreversibles, es más seguro detenerse hasta que la finalidad se reanude. Para interfaces especulativas, puedes continuar pero indica claramente que los bloques no son finales.
- Usa
waitForFinalizedcon un tiempo de espera: En tu aplicación, siempre establece un tiempo de espera razonable al esperar la finalidad. Si se agota, registra el error y alerta a tu equipo.
- Monitorea la disponibilidad de justificaciones: Si necesitas probar la finalidad, usa
grandpa_proveFinalitypara obtener una justificación. Ten en cuenta que las justificaciones históricas pueden ser podadas en nodos completos; los nodos de archivo pueden retenerlas. Consulta Consultando el estado histórico de Polkadot a través de RPC para más información.
- Comprende los cambios en el conjunto de autoridades: Cuando el conjunto de validadores cambia, la finalidad puede pausarse brevemente. Esto es normal y debería resolverse en unas pocas rondas.
Limitaciones y compensaciones de las consultas de finalidad
Si bien los métodos JSON-RPC son estándar, hay limitaciones a tener en cuenta:
- Disponibilidad del endpoint: No todos los endpoints públicos exponen los métodos
grandpa_*. Si los necesitas, usa un endpoint dedicado de un proveedor como el servicio API de OnFinality.
- Poda de justificaciones históricas: Los nodos completos pueden podar justificaciones antiguas para ahorrar espacio. Los nodos de archivo tienen más probabilidades de retenerlas, pero son más costosos. Verifica la configuración de tu nodo.
- La latencia de finalidad es variable: El tiempo hasta la finalidad depende de las condiciones de la red, el rendimiento de los validadores y el número de autoridades. No asumas una latencia fija; siempre diseña para la finalidad eventual.
waitForFinalizedpuede lanzar una excepción: Si el bloque nunca se finaliza (por ejemplo, debido a una bifurcación), la promesa puede rechazarse. Maneja los errores con elegancia.
- Privacidad y límites de velocidad: Los endpoints públicos pueden limitar la velocidad de las suscripciones. Para producción, usa un endpoint dedicado con límites más altos. Consulta precios RPC para opciones.
Para una comparación con otros modelos de finalidad, consulta el artículo sobre etapas de finalidad OP-Stack.
Próximos pasos: Construye integraciones fiables de Polkadot
Ahora que entiendes la finalidad de GRANDPA y cómo consultarla, puedes construir integraciones más fiables. Comienza usando chain_getFinalizedHead para cualquier estado sobre el que pretendas actuar, y suscríbete a chain_subscribeFinalizedHeads para actualizaciones en tiempo real. Usa waitForFinalized cuando necesites confirmar que una transacción específica es irreversible.
Para más lecturas, explora el centro de aprendizaje de OnFinality para más guías sobre Polkadot y otras redes. Si te preocupa la latencia, consulta Latencia RPC de Polkadot. Para mejores prácticas de WebSocket, consulta WebSocket RPC de Polkadot. Y para consultas de estado histórico, consulta Consultando el estado histórico de Polkadot a través de RPC.
Si necesitas un endpoint de nivel de producción con acceso a todos los métodos RPC, considera el servicio API de OnFinality. Proporcionamos endpoints dedicados con alta disponibilidad y baja latencia, para que puedas concentrarte en construir en lugar de administrar infraestructura.