Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Guías de red y protocolo12 min de lectura

Finalidad de Base OP-Stack: Etiquetas de bloque Safe, Finalized y Latest a través de RPC

Comprende las etapas de finalidad de Base OP-Stack y cómo usar las etiquetas de bloque safe, finalized y latest en llamadas JSON-RPC.

TL;DR

En Base, un L2 de OP-Stack, las etiquetas de bloque JSON-RPC 'latest', 'safe' y 'finalized' representan etapas distintas de finalidad. 'latest' es la cabeza del secuenciador y puede reorganizarse, 'safe' está comprometido con un bloque L1 que es poco probable que se reorganice, y 'finalized' es irreversible. Usa 'finalized' para lecturas críticas como retiros o marcas de agua de indexadores, y 'safe' para la mayoría de la lógica de aplicación que requiere estabilidad.

Respuesta directa: ¿Qué significan safe, finalized y latest en Base?

Cuando consultas un endpoint JSON-RPC de Base, las etiquetas de bloque latest, safe y finalized no apuntan todas al mismo bloque. latest es la cabeza de la cadena según la ve el secuenciador y puede ser reorganizada. safe es un bloque cuyo lote ha sido enviado a un bloque L1 que es lo suficientemente antiguo como para considerarse seguro frente a reorganizaciones. finalized es un bloque que está completamente confirmado por la finalidad de L1 y el proceso de derivación de OP-Stack, lo que lo hace irreversible en la práctica. Este artículo explica el mecanismo detrás de estas etapas y cómo usarlas correctamente en tus aplicaciones.

Para obtener detalles autorizados, consulta la documentación de Base sobre el ciclo de vida de las transacciones y la documentación de Optimism sobre la finalidad de las transacciones. Estas son fuentes primarias que describen la semántica exacta y el comportamiento actual.

Cómo la arquitectura OP-Stack crea etapas de finalidad

Base es un L2 de OP-Stack (Optimism). El secuenciador propone bloques y periódicamente envía lotes de transacciones a un contrato L1. El contrato L1, junto con el sistema de juego de disputas y raíces de salida, determina cuándo un bloque L2 se considera seguro o finalizado. Este proceso es asíncrono: el secuenciador produce bloques rápidamente, pero la finalidad en L1 lleva tiempo.

Los tres valores de cabeza expuestos por un nodo de Base son:

  • latest: El bloque más reciente que el secuenciador ha producido. Este bloque aún no está anclado a L1 y puede ser reorganizado si el secuenciador se reorganiza o si ocurre una disputa.

  • safe: Un bloque cuyo lote ha sido comprometido con un bloque L1 que es lo suficientemente antiguo como para que sea poco probable que se reorganice. Este es el marcador práctico de 'seguro para construir' para la mayoría de las aplicaciones.

  • finalized: Un bloque que ha sido completamente confirmado por la finalidad de L1 y la derivación canónica de OP-Stack. Esto es irreversible en la práctica.

La profundidad y el momento exactos de estas etapas son comportamiento documentado, no constantes fijas. Dependen de la finalidad de L1 y de la configuración de OP-Stack. Como desarrollador, no debes asumir un número fijo de bloques o segundos; en su lugar, consulta al nodo para obtener las cabezas segura y finalizada actuales.

Consultando safe, finalized y latest con JSON-RPC

Puedes consultar cada cabeza usando el método estándar eth_getBlockByNumber con el parámetro de etiqueta de bloque. La etiqueta de bloque puede ser una cadena como 'latest', 'safe' o 'finalized', o un objeto de número de bloque EIP-1898. Por ejemplo, para obtener el último bloque: eth_getBlockByNumber('latest', false). Para obtener el bloque seguro: eth_getBlockByNumber('safe', false). Para obtener el bloque finalizado: eth_getBlockByNumber('finalized', false).

Los números de bloque devueltos para safe y finalized típicamente estarán detrás de latest. La diferencia no es una constante fija; varía según las condiciones de L1 y el horario de lotes del secuenciador. Puedes medir la diferencia en tu entorno usando el script a continuación.

Usar latest para lecturas irreversibles es peligroso. Por ejemplo, si estás construyendo un indexador que registra el último bloque como una marca de agua, una reorganización podría hacer que proceses un bloque que luego se descarta. De manera similar, si estás construyendo una prueba de retiro, debes usar un bloque que esté finalizado en L1, no solo el último bloque L2.

Script Node.js reproducible para medir la frontera de finalidad

El siguiente script se conecta a un endpoint de Base, obtiene los números de bloque más reciente, seguro y finalizado, e imprime las diferencias. También vuelve a muestrear la cabeza segura a lo largo del tiempo para observar cómo avanza la frontera de finalidad. Reemplaza YOUR_BASE_ENDPOINT con la URL real de tu endpoint.

Ejecuta el script con Node.js (v18 o posterior). Utiliza la API fetch incorporada, por lo que no se requieren dependencias externas.

const endpoint = 'YOUR_BASE_ENDPOINT';

async function rpc(method, params) {
  const res = await fetch(endpoint, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
  });
  const data = await res.json();
  if (data.error) throw new Error(data.error.message);
  return data.result;
}

async function getHeads() {
  const latest = parseInt(await rpc('eth_blockNumber', []), 16);
  const safe = parseInt((await rpc('eth_getBlockByNumber', ['safe', false])).number, 16);
  const finalized = parseInt((await rpc('eth_getBlockByNumber', ['finalized', false])).number, 16);
  return { latest, safe, finalized };
}

async function main() {
  console.log('Sampling Base finality heads...');
  const samples = [];
  for (let i = 0; i < 5; i++) {
    const heads = await getHeads();
    samples.push(heads);
    console.log(`Sample ${i+1}: latest=${heads.latest}, safe=${heads.safe}, finalized=${heads.finalized}, behind_safe=${heads.latest - heads.safe}, behind_finalized=${heads.latest - heads.finalized}`);
    await new Promise(resolve => setTimeout(resolve, 5000)); // wait 5 seconds
  }
  console.log('\nFill in the table below with your observed values:');
  console.log('| Sample | latest | safe | finalized | behind_safe | behind_finalized |');
  console.log('|--------|--------|------|-----------|-------------|------------------|');
  samples.forEach((s, i) => {
    console.log(`| ${i+1} | ${s.latest} | ${s.safe} | ${s.finalized} | ${s.latest - s.safe} | ${s.latest - s.finalized} |`);
  });
}

main().catch(err => { console.error(err); process.exit(1); });

Salida esperada y tabla de resultados para completar

Cuando ejecutes el script, verás una salida similar a la siguiente (los números reales varían según las condiciones de la red y el endpoint):

Registra tus propias observaciones en la tabla a continuación. Los valores son específicos del entorno y cambiarán con el tiempo. Este método te permite verificar el comportamiento de finalidad en tu endpoint elegido.

  • | Sample | latest | safe | finalized | behind_safe | behind_finalized |
  • |--------|--------|------|-----------|-------------|------------------|
  • | 1 | | | | | |
  • | 2 | | | | | |
  • | 3 | | | | | |
  • | 4 | | | | | |
  • | 5 | | | | | |
Sample 1: latest=12345678, safe=12345670, finalized=12345660, behind_safe=8, behind_finalized=18

¿Qué cabeza deberías usar en cada caso?

Elegir la etiqueta de bloque correcta depende de tu caso de uso. Aquí tienes una guía de decisión:

  • Usa finalized para: operaciones irreversibles como construir pruebas de retiro, finalizar transferencias entre cadenas o registrar marcas de agua permanentes de indexadores. Esto asegura que nunca construyas sobre un bloque que pueda ser reorganizado.

  • Usa safe para: la mayoría de la lógica de aplicación que requiere una vista estable de la cadena, como leer saldos de cuentas, ejecutar llamadas a contratos inteligentes que dependen del estado o construir transacciones que no deberían ser reorganizadas. safe es el valor predeterminado recomendado para lecturas que necesitan ser consistentes.

  • Usa latest para: monitoreo en tiempo real, estado de transacciones orientado al usuario que puede tolerar reorganizaciones, o cuando necesitas el bloque más reciente y entiendes el riesgo de reorganización.

Para operaciones financieras críticas, siempre prefiere finalized o safe sobre latest. Por ejemplo, si estás construyendo un puente que bloquea activos en Base, debes esperar a finalized antes de considerar el depósito irreversible.

Errores comunes y cómo evitarlos

Los desarrolladores a menudo cometen errores al tratar con la finalidad de L2. Aquí están los errores más comunes y sus soluciones:

  • Tratar latest como final: Este es el error más común. latest puede reorganizarse. Siempre usa safe o finalized para cualquier cosa que deba ser permanente.
  • Asumir una profundidad de reorganización fija: La diferencia entre latest y safe no es constante. Depende de las condiciones de L1 y del procesamiento por lotes del secuenciador. Mídela en tu entorno en lugar de codificar un valor fijo.
  • Usar recibos de L2 para finalidad entre cadenas: Un recibo de transacción en Base no significa que la transacción sea final en Ethereum. Los retiros entre cadenas requieren la ventana de prueba de Optimism, que es más larga que la finalidad de L2. Consulta la documentación de Optimism sobre comunicación entre cadenas para más detalles.
  • Ignorar el avance escalonado: safe y finalized no avanzan al mismo ritmo. safe típicamente avanza más rápido que finalized. Tu aplicación debe manejar ambos de forma independiente.
  • No usar EIP-1898 para bloques específicos: Si necesitas consultar un número de bloque específico, usa el formato de objeto EIP-1898, por ejemplo, { blockNumber: '0x...' }, para evitar ambigüedad.

Limitaciones y compensaciones

Aunque safe y finalized proporcionan garantías más fuertes, conllevan compensaciones. Los bloques safe y finalized están detrás de latest, por lo que usarlos significa que tu aplicación ve una vista ligeramente retrasada de la cadena. Para la mayoría de los casos de uso, este retraso es aceptable, pero para aplicaciones en tiempo real como fuentes de precios o paneles en vivo, es posible que necesites usar latest y manejar las reorganizaciones con elegancia.

Además, la semántica exacta de safe y finalized puede evolucionar a medida que OP-Stack cambie. Siempre consulta la documentación oficial para conocer el comportamiento más reciente. La documentación de Base y la documentación de Optimism son las fuentes autorizadas.

Finalmente, ten en cuenta que el comportamiento descrito aquí es específico de los L2 de OP-Stack como Base. Otros L2 (por ejemplo, Arbitrum, zkSync) tienen modelos de finalidad diferentes. No asumas que las mismas etiquetas de bloque significan lo mismo en todas las redes.

Próximos pasos y lecturas adicionales

Ahora que entiendes la finalidad de Base, puedes aplicar este conocimiento para construir aplicaciones más robustas. Para más detalles sobre el uso de RPC de Base, explora los siguientes recursos:

  • Servicio API - Aprende sobre las ofertas de API de OnFinality.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar