Este artículo explica cómo consultar datos históricos de Solana mediante JSON-RPC, cubriendo la diferencia entre nodos completos y nodos de archivo, métodos clave como getSignaturesForAddress y getTransaction, un patrón de paginación ejecutable, límites prácticos y consideraciones de producción.
Respuesta Directa: Cómo Consultar Datos Históricos de Solana
Para consultar datos históricos de Solana a través de RPC, se utiliza un conjunto de métodos JSON-RPC que devuelven firmas de transacción, detalles de transacción y datos de bloques. El método principal es getSignaturesForAddress, que devuelve una lista de firmas de transacción para una dirección dada, paginada con los parámetros before y limit. Luego se obtienen los detalles completos de cada transacción con getTransaction. Para datos a nivel de bloque, se utilizan getBlock y getBlocks. Sin embargo, la disponibilidad de datos históricos depende del tipo de nodo: un nodo completo estándar retiene solo una ventana limitada de slots recientes (típicamente unos pocos días), mientras que un nodo de archivo almacena todo el ledger desde el génesis, limitado solo por el espacio en disco. Los endpoints RPC públicos a menudo ejecutan nodos completos, por lo que pueden devolver errores para slots antiguos. Para acceso de producción sostenido, se necesita un endpoint de archivo dedicado o un indexador.
Esta guía explica los mecanismos, proporciona un script Node.js ejecutable y discute límites prácticos y patrones de producción. Para una referencia rápida, consulta la Guía de API de Solana y la página de red de Solana.
Qué Significa 'Histórico' en Solana: Nodos Completos vs. Nodos de Archivo
En Solana, los datos 'históricos' no se definen por tiempo sino por número de slot. Un nodo completo (también llamado validador o nodo RPC) retiene una ventana móvil de slots recientes, típicamente los últimos días, para servir consultas en vivo. Esta ventana es configurable mediante el parámetro --limit-ledger-size, pero por defecto, los nodos eliminan slots antiguos para ahorrar espacio en disco. Cuando se consulta un slot eliminado, el nodo devuelve un error como "Slot X was skipped, or missing due to ledger jump to recent snapshot".
Un nodo de archivo, por otro lado, almacena todo el ledger desde el génesis, limitado solo por la capacidad del disco RocksDB. Los nodos de archivo son esenciales para consultar datos más antiguos que la ventana del nodo completo. La documentación de Solana establece que los nodos de archivo son 'un nodo que almacena todos los bloques desde el génesis' y se utilizan para consultas históricas. Los métodos JSON-RPC que devuelven datos históricos incluyen: getSignaturesForAddress, getTransaction, getBlock, getBlockTime, getBlocks, getTransactionCount y getHighestSnapshotSlot. Estos métodos funcionan tanto en nodos completos como en nodos de archivo, pero la profundidad de historia a la que pueden acceder depende de la retención del nodo.
Para una inmersión más profunda sobre cómo los nodos de Solana gestionan el almacenamiento del ledger, consulta la documentación de validadores de Solana.
- Nodo completo: retiene una ventana limitada de slots recientes (por ejemplo, los últimos 2 días).
- Nodo de archivo: almacena todo el ledger desde el génesis, limitado por el disco.
- Los slots eliminados devuelven errores como 'Slot was skipped, or missing due to ledger jump to recent snapshot'.
Métodos RPC Clave para Datos Históricos
Los siguientes métodos JSON-RPC son tus herramientas principales para consultar datos históricos en Solana:
getSignaturesForAddress devuelve una lista de firmas de transacción para una dirección dada, ordenadas de más reciente a más antigua. Acepta los parámetros before (una firma desde la cual comenzar, exclusiva) y limit (máximo 1000) para la paginación. Cada elemento incluye signature, slot, err, memo, blockTime y confirmationStatus.
getTransaction obtiene una sola transacción por firma. Acepta un parámetro encoding (por ejemplo, jsonParsed) y un commitment (por ejemplo, confirmed). La respuesta incluye slot, blockTime, meta (con err, fee, preBalances, postBalances, innerInstructions, logMessages) y transaction (con message y signatures).
getBlock devuelve un bloque por número de slot, incluyendo transacciones y recompensas. getBlocks devuelve una lista de slots de bloques dentro de un rango. getBlockTime devuelve la marca de tiempo para un slot dado. getTransactionCount devuelve el número total de transacciones procesadas por el nodo (no histórico per se, pero útil para contexto). getHighestSnapshotSlot devuelve el slot más alto para el cual hay un snapshot disponible, lo que puede indicar el punto más temprano que un nodo completo puede servir sin datos de archivo.
Para una referencia completa, consulta la documentación de la API RPC de Solana.
Ejemplo Ejecutable: Paginación a Través del Historial de una Dirección
A continuación se muestra un script Node.js autónomo que pagina hacia atrás a través del historial de transacciones de una dirección y obtiene los detalles de transacción parseados. Utiliza la API fetch (Node 18+) y un endpoint RPC público (reemplázalo con tu propio endpoint). El script imprime la firma, el slot, la hora del bloque y cualquier error para cada transacción.
Para ejecutarlo, guárdalo como solana-history.js y ejecútalo con node solana-history.js. Obtendrá hasta 1000 firmas por página y luego obtendrá cada transacción, con un pequeño retraso para evitar límites de tasa. El script se detiene cuando se devuelven menos del límite o cuando ocurre un error.
const endpoint = 'https://api.mainnet-beta.solana.com'; // Reemplaza con tu endpoint RPC
const address = 'YourBase58AddressHere'; // Reemplaza con la dirección a consultar
async function rpcCall(method, params) {
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
});
const json = await response.json();
if (json.error) throw new Error(json.error.message);
return json.result;
}
async function getHistory() {
let before = undefined;
let total = 0;
while (true) {
const params = [address, { limit: 1000, before: before }];
const signatures = await rpcCall('getSignaturesForAddress', params);
if (signatures.length === 0) break;
for (const sigInfo of signatures) {
const tx = await rpcCall('getTransaction', [sigInfo.signature, { encoding: 'jsonParsed' }]);
console.log(`Signature: ${sigInfo.signature}`);
console.log(`Slot: ${sigInfo.slot}, BlockTime: ${sigInfo.blockTime}`);
if (tx && tx.meta) {
console.log(`Error: ${tx.meta.err || 'None'}`);
console.log(`Fee: ${tx.meta.fee}`);
}
total++;
await new Promise(resolve => setTimeout(resolve, 200)); // Cortesía de límite de tasa
}
before = signatures[signatures.length - 1].signature;
if (signatures.length < 1000) break;
}
console.log(`Total transactions fetched: ${total}`);
}
getHistory().catch(err => console.error(err));Forma Esperada del JSON y Significado de los Campos
Cuando llamas a getSignaturesForAddress, cada elemento en el array de resultados se ve así:
La signature es la firma de transacción codificada en base58. slot es el número de slot en el que se incluyó la transacción. err es null si la transacción tuvo éxito, o un objeto que describe el error. memo es una cadena de memo opcional. blockTime es la marca de tiempo Unix del bloque. confirmationStatus indica el nivel de confirmación (por ejemplo, 'finalized').
Cuando llamas a getTransaction, la respuesta incluye un slot, blockTime y un objeto meta. El meta contiene err (null si éxito), fee (en lamports), preBalances y postBalances (arrays de saldos antes y después), innerInstructions y logMessages. El objeto transaction contiene message (con accountKeys, instructions, etc.) y el array signatures.
Para verificar los datos, puedes contrastar el blockTime con el slot usando getBlockTime, o comparar los preBalances y postBalances con la tarifa para asegurar consistencia.
- Elemento de resultado de getSignaturesForAddress: { signature, slot, err, memo, blockTime, confirmationStatus }
- Resultado de getTransaction: { slot, blockTime, meta: { err, fee, preBalances, postBalances, innerInstructions, logMessages }, transaction: { message, signatures } }
Fallos Comunes y Cómo Solucionarlos
Al consultar datos históricos, puedes encontrar varios errores comunes:
Slot was skipped, or missing due to ledger jump to recent snapshot: Esto ocurre cuando el nodo ha eliminado el slot. Solución: usa un nodo de archivo o un proveedor de datos históricos dedicado. Los RPC públicos a menudo tienen historial limitado.
- Límite de tasa: Los endpoints públicos pueden devolver HTTP 429 o errores JSON-RPC como
"Too many requests". Solución: implementa retroceso exponencial, reduce la frecuencia de solicitudes o usa un endpoint dedicado con límites más altos. OnFinality ofrece servicios de API dedicados con límites de tasa escalables.
- Tamaño de respuesta:
getTransactionconjsonParsedpuede ser grande para transacciones complejas. Solución: usa codificaciónjsonsi no necesitas instrucciones parseadas, o obtén solo campos específicos usandogetSignaturesForAddressy luegogetTransactionselectivamente.
- Huecos de slots:
getBlockspuede devolver huecos si se omiten bloques. Solución: maneja los slots faltantes con gracia verificando respuestas null.
- Endpoints no-archivo: Si necesitas datos más antiguos que unos pocos días, asegúrate de que tu endpoint sea un nodo de archivo. Verifica con
getHighestSnapshotSlotpara ver el slot más temprano disponible.
Compensaciones y Limitaciones
Consultar datos históricos a través de RPC tiene compensaciones inherentes. Primero, la ventana de retención de un nodo completo no es un punto de referencia fijo; depende de la configuración del nodo, el espacio en disco y la configuración de poda. Los endpoints de OnFinality, por ejemplo, pueden tener diferentes políticas de retención, así que siempre verifica la disponibilidad real para tu caso de uso.
Segundo, paginar a través del historial completo de una dirección puede ser lento y consumir muchos recursos, especialmente para direcciones de alta actividad. Cada página de 1000 firmas requiere 1000 llamadas separadas a getTransaction, lo que puede alcanzar límites de tasa y tomar tiempo significativo. Para producción, considera usar un indexador o un servicio de datos que proporcione acceso masivo.
Tercero, los endpoints RPC públicos no están diseñados para consultas históricas pesadas. Son compartidos y con límites de tasa. Para historial de producción sostenido, debes usar un endpoint dedicado o de archivo, o un indexador como el acceso general a datos históricos de blockchain de OnFinality.
Finalmente, los métodos JSON-RPC devuelven datos crudos; debes manejar el parseo y almacenamiento tú mismo. Para análisis a gran escala, un almacén de datos o indexador es más eficiente.
- La retención del nodo completo es configurable y no es un punto de referencia fijo.
- La paginación es lenta para historiales grandes; considera indexadores.
- Los RPC públicos tienen límites de tasa; usa endpoints dedicados para producción.
- Los datos RPC crudos requieren procesamiento adicional para análisis.
Patrones de Producción y Próximos Pasos
Para aplicaciones de producción que necesitan datos históricos confiables, considera los siguientes patrones:
- Usa un endpoint de archivo: Si necesitas historial completo, suscríbete a un servicio RPC de archivo. OnFinality proporciona endpoints RPC de Solana con retención configurable y alta disponibilidad.
- Cache e indexa: En lugar de consultar RPC repetidamente, construye un índice local de transacciones usando
getSignaturesForAddressygetTransaction, y almacénalas en una base de datos. Esto reduce la carga RPC y acelera las consultas.
- Usa webhooks o streaming: Para datos en tiempo real, usa las suscripciones WebSocket de Solana para capturar transacciones a medida que ocurren y almacenarlas para análisis posterior.
- Considera indexadores de terceros: Servicios como Helius o QuickNode ofrecen APIs de datos históricos que abstraen la complejidad. Sin embargo, pueden tener sus propias limitaciones y costos.
- Monitorea límites de tasa: Implementa lógica de reintento robusta con retroceso exponencial y respeta el encabezado
Retry-Aftersi está presente.
Para más orientación, explora la Guía de API de Solana y la página de precios para elegir un plan que se ajuste a tus necesidades. También, lee sobre acceso a datos históricos de blockchain para una perspectiva más amplia.