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

Solana getEpochInfo y el calendario de líderes: leer líneas de tiempo de slots

Una guía práctica para leer líneas de tiempo de slots de Solana vía RPC: límites de época, calendarios de líderes, mapeo de slot a tiempo y verificaciones de reconciliación.

TL;DR

Las líneas de tiempo de slots de Solana se leen combinando getEpochInfo, getLeaderSchedule, getSlot y getBlockTime. getEpochInfo devuelve absoluteSlot, blockHeight, epoch, slotIndex, slotsInEpoch y transactionCount; getLeaderSchedule asigna índices de slot a identidades de validador para una época. absoluteSlot cuenta cada slot desde el génesis, mientras que blockHeight cuenta solo los slots que produjeron bloques, por lo que restarlos estima los slots omitidos en una ventana. La aritmética de épocas es segura solo cuando slotsInEpoch se lee del clúster, no se codifica de forma fija. El mapeo de slot a tiempo requiere getBlockTime, que devuelve null para slots sin bloques, por lo que las líneas de tiempo deben llevar la marca de tiempo conocida más cercana. Los límites de época son la ventana de lectura más riesgosa porque el calendario de líderes, la información de época y el rango de slots cambian juntos.

Primitivas de la línea de tiempo de slots y sus fuentes RPC

Una línea de tiempo de slots de Solana responde cuatro preguntas: a qué época pertenece un slot, qué validador está programado para liderarlo, cuándo termina la época y a qué instante de tiempo real se mapea el slot. Las referencias oficiales de métodos RPC de Solana para getEpochInfo y getLeaderSchedule definen los campos y la semántica usados en toda esta guía. La especificación JSON-RPC 2.0 rige el enmarcado de solicitudes y respuestas, mientras que la especificación JSON-RPC de Ethereum no es aplicable a la semántica de los métodos de Solana; Solana usa su propio espacio de nombres de métodos.

getEpochInfo devuelve absoluteSlot, blockHeight, epoch, slotIndex, slotsInEpoch y transactionCount. getLeaderSchedule devuelve un mapa de identidad de validador a un arreglo de índices de slot para una época dada. getSlot devuelve el slot actual, y getBlockTime devuelve una marca de tiempo Unix para un slot que produjo un bloque. Juntos, estos métodos permiten reconstruir una línea de tiempo sin depender de suposiciones no documentadas.

La distinción entre absoluteSlot y blockHeight es la fuente más común de errores silenciosos. absoluteSlot cuenta cada slot desde el génesis, haya producido un bloque o no. blockHeight cuenta solo los slots que produjeron bloques. Restar blockHeight de absoluteSlot dentro de una ventana estima los slots omitidos en esa ventana, pero confundir los dos desplaza cada cálculo de época. Para la aritmética de épocas, usa absoluteSlot. Para la densidad de producción de bloques, usa blockHeight.

  • getEpochInfo: absoluteSlot, blockHeight, epoch, slotIndex, slotsInEpoch, transactionCount.
  • getLeaderSchedule: identidad de validador a arreglo de índices de slot para una época.
  • getSlot: número de slot actual.
  • getBlockTime: marca de tiempo Unix para un slot que produjo un bloque, null en caso contrario.

Aritmética de épocas en la que es seguro confiar

La relación epoch = absoluteSlot / slotsInEpoch y slotIndex = absoluteSlot % slotsInEpoch es segura de usar cuando slotsInEpoch se lee de getEpochInfo. Las partes que no son constantes en la práctica son slotsInEpoch y el propio calendario de épocas. Estos son parámetros de tiempo de ejecución, y la red ha cambiado la temporización de slots antes. El código que codifica de forma fija 432000 slots por época o 400 ms por slot se desviará cuando cambien los parámetros del clúster.

Lee slotsInEpoch de getEpochInfo en cada reconstrucción de línea de tiempo en lugar de almacenarlo en caché durante ventanas largas. Si usas caché, registra el par epoch y slotsInEpoch e invalídalo al cambiar de época. El calendario de épocas es una propiedad del clúster, documentada y variable según el clúster, por lo que un valor observado en un clúster no debe asumirse para otro.

Para lectores en producción, el patrón seguro es obtener getEpochInfo, calcular epoch y slotIndex a partir de absoluteSlot y slotsInEpoch, y luego obtener getLeaderSchedule para esa época. Si la época cambia entre las dos llamadas, vuelve a obtener getEpochInfo y repite. Esto evita emparejar un número de época antiguo con un índice de slot nuevo.

  • Usa absoluteSlot para la aritmética de épocas, no blockHeight.
  • Lee slotsInEpoch de getEpochInfo; no lo codifiques de forma fija.
  • Trata el calendario de épocas como una propiedad del clúster, documentada y variable según el clúster.
  • Vuelve a obtener getEpochInfo después de cualquier solicitud de slot que haya cruzado un límite de época.

Formas del calendario de líderes y disponibilidad histórica

getLeaderSchedule tiene dos formas prácticas: una llamada sin argumento de época devuelve el calendario de la época actual, y una llamada con argumento de época devuelve el calendario de esa época específica. La respuesta asigna la identidad del validador a un arreglo de índices de slot. Los índices de slot son relativos a la época, por lo que el índice de slot 0 es el primer slot de esa época, no el génesis.

Un calendario histórico de una época antigua puede no estar disponible desde un nodo podado. Esto debe tratarse como un límite esperado en lugar de un error. Los proveedores documentan diferentes ventanas de retención, y la retención es una propiedad del proveedor. Si necesitas calendarios históricos, verifica la retención con tu proveedor antes de diseñar un trabajo de relleno.

La relación entre los índices de slot y la identidad del líder es directa: para una época dada, encuentra el validador cuyo arreglo contiene el índice de slot. Si ningún arreglo de validador contiene el índice, el calendario que obtuviste no cubre la época que crees que cubre, que es la verificación de reconciliación descrita más adelante.

  • Sin argumento de época: calendario de la época actual.
  • Con argumento de época: calendario de esa época, si se conserva.
  • Los índices de slot son relativos a la época, no relativos al génesis.
  • La disponibilidad de calendarios históricos es una propiedad de retención del proveedor, documentada y variable según el proveedor.

Mapear un slot a tiempo real

getBlockTime(slot) devuelve una marca de tiempo Unix para un slot que produjo un bloque y null para uno que no lo hizo. Una reconstrucción de línea de tiempo debe manejar los null llevando la marca de tiempo conocida más cercana y registrando la interpolación en lugar de descartar el slot. Descartar slots null hace que la línea de tiempo parezca más densa de lo que es y oculta los slots omitidos.

El patrón correcto es recorrer el rango de slots, llamar a getBlockTime para cada slot y, cuando el resultado sea null, llevar la última marca de tiempo conocida hacia adelante y marcar la entrada como interpolada. Cuando un slot posterior devuelva una marca de tiempo, opcionalmente puedes rellenar las entradas interpoladas con una estimación lineal, pero mantén la marca de interpolación para que los consumidores posteriores sepan que el valor es derivado.

Para líneas de tiempo gruesas, puedes muestrear getBlockTime a intervalos e interpolar entre muestras. Para líneas de tiempo precisas, llama a getBlockTime por slot. La disyuntiva es volumen de solicitudes frente a precisión. Ambos enfoques deben manejar los null explícitamente.

  • getBlockTime devuelve null para slots sin bloques.
  • Lleva la marca de tiempo conocida más cercana y marca las entradas interpoladas.
  • No descartes slots null; descartarlos oculta los slots omitidos.
  • El muestreo reduce el volumen de solicitudes pero aumenta el error de interpolación.

Los límites de época como la ventana de lectura riesgosa

El límite de época es el minuto más riesgoso para los lectores en producción. El calendario de líderes, la información de época y el rango de slots cambian juntos. Una solicitud emitida a través del límite puede emparejar un número de época antiguo con un índice de slot nuevo, produciendo una línea de tiempo que parece válida pero es internamente inconsistente.

Una lectura consistente debe volver a obtener getEpochInfo después de cualquier solicitud de slot que haya cruzado un límite. Detecta el límite comparando el campo epoch antes y después de la solicitud de slot. Si la época cambió, descarta el resultado intermedio y repite la lectura. Esto es barato comparado con depurar un líder mal atribuido.

Para trabajos por lotes, fija la época al inicio del lote y verifícala al final. Si la época cambió a mitad del lote, divide el lote en el límite y vuelve a ejecutar la segunda mitad con la nueva época. Esto mantiene cada lote internamente consistente.

  • El calendario de líderes, la información de época y el rango de slots cambian juntos.
  • Vuelve a obtener getEpochInfo después de cualquier solicitud de slot que haya cruzado un límite.
  • Fija la época al inicio del lote y verifícala al final del lote.
  • Divide los lotes en el límite en lugar de mezclar épocas.

Verificación de reconciliación: cobertura del calendario frente a slotsInEpoch

Una verificación de reconciliación confirma que el calendario que obtuviste cubre la época que crees que cubre. Suma los conteos de slots programados de los líderes para una época y compáralos con slotsInEpoch de getEpochInfo. Si la suma es igual a slotsInEpoch, el calendario cubre la época completa. Si la suma es menor, el calendario es parcial o la época aún está en curso.

Para una época completada, una suma menor que slotsInEpoch indica datos de calendario faltantes, lo que puede ser un límite de retención. Para una época en curso, una suma menor que slotsInEpoch es esperada porque los slots restantes aún no se han programado o el calendario se está sirviendo de forma incremental.

Registra el resultado de la reconciliación junto con la línea de tiempo. Una línea de tiempo con una reconciliación fallida debe marcarse en lugar de consumirse silenciosamente. Esto es especialmente importante para flujos de trabajo financieros o contables donde un líder mal atribuido cambia la atribución de recompensas.

  • Suma los conteos de slots programados por época y compáralos con slotsInEpoch.
  • Suma igual: cobertura completa. Menor: parcial o en curso.
  • Marca la reconciliación fallida en lugar de consumirla silenciosamente.
  • Los límites de retención pueden causar calendarios parciales para épocas antiguas.

Ejemplo ejecutable en Node.js: tabla de época, líder y hora de finalización

El siguiente fragmento de Node.js lee getEpochInfo y getLeaderSchedule, imprime el índice de slot, la época, el validador propietario de un slot elegido y la hora de finalización de la época como una tabla. Usa la API global fetch disponible en Node.js 18 y posteriores. Reemplaza el endpoint RPC con el endpoint de tu proveedor.

El fragmento calcula el slot de finalización de la época como (epoch + 1) * slotsInEpoch - 1 y estima la hora de finalización muestreando getBlockTime en el slot actual y extrapolando usando la duración de slot observada. La extrapolación es una estimación, no un valor medido, y debe etiquetarse como tal en cualquier salida.

const RPC_URL = process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com';

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

async function main() {
  const epochInfo = await rpc('getEpochInfo');
  const { absoluteSlot, blockHeight, epoch, slotIndex, slotsInEpoch } = epochInfo;

  const schedule = await rpc('getLeaderSchedule', [epoch]);
  const leaderForSlot = (targetSlotIndex) => {
    for (const [validator, slots] of Object.entries(schedule)) {
      if (slots.includes(targetSlotIndex)) return validator;
    }
    return null;
  };

  const chosenSlotIndex = slotIndex;
  const owner = leaderForSlot(chosenSlotIndex);

  const epochEndSlot = (epoch + 1) * slotsInEpoch - 1;
  const currentBlockTime = await rpc('getBlockTime', [absoluteSlot]);
  const sampleSlot = Math.max(0, absoluteSlot - 100);
  const sampleBlockTime = await rpc('getBlockTime', [sampleSlot]);

  let estimatedEndTime = null;
  if (currentBlockTime && sampleBlockTime && absoluteSlot > sampleSlot) {
    const msPerSlot = ((currentBlockTime - sampleBlockTime) * 1000) / (absoluteSlot - sampleSlot);
    estimatedEndTime = new Date((currentBlockTime * 1000) + (epochEndSlot - absoluteSlot) * msPerSlot);
  }

  const rows = [
    { field: 'absoluteSlot', value: absoluteSlot },
    { field: 'blockHeight', value: blockHeight },
    { field: 'epoch', value: epoch },
    { field: 'slotIndex', value: slotIndex },
    { field: 'slotsInEpoch', value: slotsInEpoch },
    { field: 'chosenSlotIndex', value: chosenSlotIndex },
    { field: 'leader', value: owner || 'not found in schedule' },
    { field: 'epochEndSlot', value: epochEndSlot },
    { field: 'estimatedEndTime', value: estimatedEndTime ? estimatedEndTime.toISOString() : 'unavailable' }
  ];

  console.table(rows);
}

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

Tabla de resultados: medir contra tu propio endpoint

Dado que la temporización de slots, la duración de época y la retención de calendarios son propiedades del clúster y del proveedor, documentadas y variables según el clúster, debes medir contra tu propio endpoint en lugar de confiar en números publicados. La tabla a continuación es una plantilla para completar con tus propias observaciones. No trates ninguna fila como una constante universal.

Ejecuta el fragmento de Node.js anterior en varias horas del día y a través de al menos un límite de época. Registra el slotsInEpoch observado, la duración de slot observada derivada de las muestras de getBlockTime, la suma de cobertura del calendario y si getLeaderSchedule devolvió un calendario para una época antigua. Esto te da una línea base específica de tu proveedor.

Si tu proveedor devuelve un calendario parcial para una época antigua, registra la época más antigua para la que hay un calendario completo disponible. Ese es tu límite de retención. Diseña los trabajos de relleno para mantenerse dentro de él o para recurrir a una fuente de datos diferente.

  • slotsInEpoch observado: complétalo desde getEpochInfo.
  • Duración de slot observada: derívala de las muestras de getBlockTime.
  • Suma de cobertura del calendario: suma los conteos de slots de líderes y compárala con slotsInEpoch.
  • Época más antigua completamente cubierta: tu límite de retención.
  • Comportamiento en el límite: si la época cambió a mitad de la lectura y requirió volver a obtener.

Limitaciones y disyuntivas

La temporización de slots, la duración de época y la retención de calendarios son propiedades del clúster y del proveedor, documentadas y variables según el clúster. Cualquier línea de tiempo construida sobre constantes codificadas de forma fija se desviará. El enfoque seguro es leer los parámetros del clúster y registrarlos junto con la línea de tiempo.

getBlockTime devuelve null para slots sin bloques, por lo que las líneas de tiempo precisas requieren llamadas por slot y manejo explícito de null. El muestreo reduce el volumen de solicitudes pero aumenta el error de interpolación. No hay almuerzo gratis; elige según si tu flujo de trabajo necesita precisión o tendencia.

Los calendarios históricos de líderes pueden no estar disponibles desde nodos podados. Este es un límite esperado, no un error. Si tu flujo de trabajo requiere calendarios históricos, verifica la retención con tu proveedor y diseña un respaldo. Para lectura relacionada sobre compromiso y confirmación, consulta Niveles de compromiso de Solana y confirmación de transacciones.

  • Las constantes codificadas de forma fija se desvían; léelas del clúster.
  • Las marcas de tiempo de bloque null requieren manejo explícito.
  • El muestreo intercambia precisión por volumen de solicitudes.
  • La retención de calendarios históricos es una propiedad del proveedor.

Solución de problemas comunes de líneas de tiempo

El error más común es confundir absoluteSlot con blockHeight. Si tu cálculo de época está desviado por un margen grande, verifica qué campo usaste. La aritmética de épocas requiere absoluteSlot. La densidad de producción de bloques requiere blockHeight.

El segundo error más común es codificar de forma fija slotsInEpoch. Si tu slotIndex es incorrecto cerca de un límite de época, verifica que slotsInEpoch provenga de getEpochInfo para la misma época que el slot que estás inspeccionando. Una discrepancia aquí produce un slotIndex plausible pero incorrecto.

El tercero es descartar marcas de tiempo de bloque null. Si tu línea de tiempo no muestra vacíos pero el clúster tiene slots omitidos, estás descartando null. Lleva la marca de tiempo conocida más cercana y marca las entradas interpoladas. Para patrones relacionados de paginación y análisis, consulta Paginación de getSignaturesForAddress en Solana y Transacciones versionadas de Solana y análisis de getBlock.

  • Aritmética de época incorrecta: verifica absoluteSlot frente a blockHeight.
  • slotIndex incorrecto: verifica que slotsInEpoch coincida con la época.
  • Vacíos faltantes: estás descartando marcas de tiempo de bloque null.
  • Líder mal atribuido: vuelve a obtener getEpochInfo después de cruzar un límite.

Próximos pasos y lectura relacionada

Comienza ejecutando el fragmento de Node.js contra el endpoint de tu proveedor y completando la tabla de resultados. Luego extiende la línea de tiempo para cubrir una época completa y verifica la comprobación de reconciliación. Una vez que tu línea de tiempo sea consistente, intégrala con tu flujo de trabajo de contabilidad de recompensas o monitoreo.

Para la selección de endpoints y el comportamiento específico del proveedor, consulta la Guía de endpoints RPC (RPC Assistant). Para detalles de la red Solana, consulta /en/networks/solana. Para precios y opciones de servicio, consulta Precios de RPC y Servicio de API.

Para temas relacionados de Solana, consulta Expiración de blockhash y nonces duraderos en Solana y el centro de aprendizaje de OnFinality. Estos cubren mecánicas adyacentes que a menudo aparecen en los mismos flujos de trabajo que las líneas de tiempo de slots.

  • Ejecuta el fragmento y completa la tabla de resultados.
  • Verifica la reconciliación durante una época completa.
  • Integra con contabilidad de recompensas o monitoreo.
  • Revisa la retención del proveedor antes de rellenar calendarios históricos.

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