El protocolo Graph es una capa de indexación descentralizada que convierte eventos blockchain en bruto en API GraphQL consultables llamadas subgrafos. Los desarrolladores definen un manifiesto, un esquema y manejadores de mapeo; Graph Node escanea bloques a través de un endpoint RPC, ejecuta esos manejadores y almacena las entidades resultantes para consultas rápidas. Este artículo explica el mecanismo de indexación, recorre un flujo de trabajo reproducible de despliegue y consulta de subgrafos, y muestra cómo verificar resultados y solucionar problemas de indexación estancada o inconsistente. Separa el comportamiento documentado del protocolo de las mediciones que debes ejecutar tú mismo contra tu propia infraestructura RPC.
Qué hace el protocolo Graph y dónde encaja
El protocolo Graph es un protocolo de indexación abierto para datos blockchain. En lugar de consultar registros y recibos en bruto desde un nodo RPC en cada carga de página, describes los datos que te interesan en un subgrafo, y los indexadores del protocolo procesan los bloques en una base de datos que responde a consultas GraphQL. La descripción pública es consistente en la documentación del proyecto y en los listados del ecosistema: los subgrafos organizan datos de contratos inteligentes y los sirven a través de GraphQL, mientras que Substreams cubre pipelines de streaming para casos de uso de mayor rendimiento.
El modelo mental práctico es un pipeline de tres partes. Un manifiesto declara qué contratos, eventos y bloques de inicio observar. Un esquema declara las entidades que deseas almacenar. Los manejadores de mapeo son funciones deterministas que transforman cada evento coincidente en escrituras de entidades. Graph Node es la implementación de código abierto en Rust que ejecuta este pipeline contra un endpoint RPC de la cadena.
Este artículo se centra en la mecánica del protocolo y en un flujo de trabajo local reproducible. Para la elección concreta de un endpoint RPC que Graph Node utilizará, consulta las recomendaciones de endpoints en el Asistente RPC en lugar de tratar esta página como una lista de proveedores.
- Subgrafos: indexación declarativa de eventos de contratos en entidades GraphQL.
- Substreams: pipelines de datos en streaming para consumidores de alto volumen o en tiempo real.
- Graph Node: el proceso indexador que lee bloques y escribe el almacén de entidades.
- Endpoint GraphQL: la superficie de consulta que llama tu aplicación.
Suposiciones y requisitos previos
El flujo de trabajo a continuación asume que tienes Node.js y npm instalados, Docker disponible para ejecutar Graph Node localmente, y un endpoint RPC con capacidad de archivo para la cadena que estás indexando. El acceso a archivo es importante porque un subgrafo con un bloque de inicio histórico debe reproducir estado pasado; un endpoint podado fallará o se estancará durante el relleno.
También necesitas una dirección de contrato y el ABI para los eventos que deseas indexar. Mantén el ABI mínimo: solo los eventos y funciones que llaman tus mapeos necesitan estar presentes. Si estás eligiendo entre RPC autoalojado y gestionado para la fuente de datos del indexador, las opciones de nodo dedicado y la guía de endpoints RPC explican las ventajas y desventajas operativas.
Cómo funciona la indexación dentro de Graph Node
Graph Node procesa la cadena en orden de bloques. Para cada bloque, verifica las fuentes de datos del manifiesto, filtra los registros por dirección de contrato y firma de evento, e invoca el manejador correspondiente. Los manejadores reciben el evento y un manejador de almacén; leen o crean entidades y las guardan. Dado que los manejadores deben ser deterministas, el mismo rango de bloques siempre produce el mismo estado de entidad, lo que permite que indexadores independientes coincidan.
Dos modos de fallo dominan los despliegues reales. Primero, un endpoint RPC que no puede servir el rango de bloques requerido o que limita la tasa del indexador causará reintentos y retraso visible. Segundo, un manejador que depende de una entrada no determinista, como la hora del reloj o una llamada HTTP externa, producirá resultados divergentes y será rechazado por las reglas de determinismo del protocolo. Trata la capa RPC como parte de tu superficie de corrección de indexación, no solo como plomería.
La red descentralizada del protocolo añade roles económicos sobre este mecanismo: los indexadores hacen staking y sirven consultas, los curadores señalan qué subgrafos importan, y los delegadores respaldan a los indexadores. Esos roles afectan la economía de consultas y la disponibilidad, pero no cambian el flujo de trabajo de autoría de subgrafos descrito aquí.
Construyendo un manifiesto y esquema de subgrafo mínimo
Comienza con el andamiaje estándar de Graph CLI, luego reemplaza los archivos generados con los detalles de tu contrato. El manifiesto a continuación es una plantilla; sustituye tu dirección de contrato, bloque de inicio y ruta del ABI. El startBlock debe ser el bloque donde se desplegó el contrato a menos que necesites intencionalmente historial anterior.
npm install -g @graphprotocol/graph-cli
graph init my-subgraph \
--from-contract 0xYourContractAddress \
--network mainnet \
--abi ./abis/MyContract.json \
--contract-name MyContractEscribiendo manejadores de mapeo deterministas
El archivo subgraph.yaml generado apunta a un archivo de mapeo. Un manejador debe hacer tres cosas: leer los parámetros del evento, cargar o crear la entidad, y guardarla. Evita cualquier llamada que pueda devolver resultados diferentes en distintas máquinas. Si necesitas valores derivados, calcúlalos a partir de los datos del evento o de entidades que ya estén en el almacén.
El ejemplo a continuación indexa un evento Transfer genérico en una entidad Transfer y actualiza una entidad de saldo acumulado. Es intencionalmente pequeño para que puedas verificar el pipeline de extremo a extremo antes de añadir complejidad.
import { Transfer as TransferEvent } from '../generated/MyContract/MyContract';
import { Transfer, Account } from '../generated/schema';
import { BigInt } from '@graphprotocol/graph-ts';
export function handleTransfer(event: TransferEvent): void {
const id = event.transaction.hash.toHex() + '-' + event.logIndex.toString();
const transfer = new Transfer(id);
transfer.from = event.params.from;
transfer.to = event.params.to;
transfer.value = event.params.value;
transfer.blockNumber = event.block.number;
transfer.save();
const accountId = event.params.to.toHex();
let account = Account.load(accountId);
if (account == null) {
account = new Account(accountId);
account.balance = BigInt.fromI32(0);
}
account.balance = account.balance.plus(event.params.value);
account.save();
}Ejecutando Graph Node localmente contra un endpoint RPC
Una instancia local de Graph Node necesita una base de datos Postgres, un nodo IPFS para los archivos del subgrafo, y una URL RPC para la cadena objetivo. El archivo compose a continuación es un punto de partida; establece RPC_URL en un endpoint que soporte consultas de archivo para tu cadena. No codifiques un endpoint público en un repositorio compartido, porque los límites de tasa y la disponibilidad varían según el proveedor y el plan.
Después de que el stack esté saludable, crea el subgrafo, despliégalo localmente y espera a que el indexador alcance la cabeza de la cadena. El comando graph deploy imprime una URL de estado que puedes consultar periódicamente.
version: '3'
services:
postgres:
image: postgres:14
environment:
POSTGRES_USER: graph
POSTGRES_PASSWORD: graph
POSTGRES_DB: graph-node
ports: ['5432:5432']
ipfs:
image: ipfs/go-ipfs:v0.18.1
ports: ['5001:5001']
graph-node:
image: graphprotocol/graph-node
depends_on: [postgres, ipfs]
ports: ['8000:8000', '8020:8020', '8030:8030']
environment:
postgres_host: postgres
postgres_user: graph
postgres_pass: graph
postgres_db: graph-node
ipfs: 'ipfs:5001'
ethereum: 'mainnet:${RPC_URL}'
GRAPH_LOG: infoConsultando el subgrafo a través de GraphQL
Una vez que la indexación alcanza la cabeza, consulta el endpoint GraphQL local. La consulta a continuación devuelve las diez transferencias más grandes por valor y el saldo actual de una cuenta específica. Usa variables en lugar de interpolación de cadenas para que la consulta siga siendo cacheable y segura.
El resultado de verificación esperado es una respuesta JSON con un objeto data que contiene las entidades solicitadas. Si data es nulo, lee primero el arreglo errors; generalmente nombra el campo o tipo de entidad faltante.
curl -s http://localhost:8000/subgraphs/name/my-subgraph \
-H 'content-type: application/json' \
--data '{"query":"query($account: String!) { transfers(first: 10, orderBy: value, orderDirection: desc) { id from to value blockNumber } account(id: $account) { id balance } }","variables":{"account":"0x0000000000000000000000000000000000000000"}}'Verificando la salud de la indexación y midiendo la latencia de consultas
No confíes en un subgrafo solo porque devuelve filas. Verifica tres cosas: el indexador ha alcanzado la cabeza de la cadena, los recuentos de entidades coinciden con una fuente independiente para un rango de bloques de muestra, y la latencia de consulta es aceptable para tu aplicación. El procedimiento a continuación produce una tabla de resultados que llenas con tus propias mediciones; no se afirman números aquí porque dependen de tu cadena, endpoint y hardware.
Ejecuta cada paso al menos tres veces y registra la mediana. Compara con una llamada directa eth_blockNumber por RPC para confirmar que el indexador no está retrasado.
- Paso 1: llama a
eth_blockNumberen tu URL RPC y registra el bloque cabeza. - Paso 2: consulta el campo
_meta { block { number } }del subgrafo y registra el bloque indexado. - Paso 3: calcula el retraso como cabeza menos bloque indexado; repite después de 60 segundos.
- Paso 4: cronometra diez consultas GraphQL idénticas y registra la latencia mediana.
- Paso 5: verifica una entidad contra el evento en bruto en un explorador de bloques.
Plantilla de tabla de resultados para tus propias ejecuciones
| Ejecución | Bloque cabeza RPC | Bloque indexado | Retraso (bloques) | Latencia mediana de consulta (ms) | Verificación de entidad |
|---|---|---|---|---|---|
| 1 | llenar desde eth_blockNumber | llenar desde _meta | calculado | medido | aprobado/fallido |
| 2 | llenar desde eth_blockNumber | llenar desde _meta | calculado | medido | aprobado/fallido |
| 3 | llenar desde eth_blockNumber | llenar desde _meta | calculado | medido | aprobado/fallido |
Un retraso estable cercano a cero y una verificación de entidad aprobada indican un pipeline saludable. Un retraso creciente con un RPC saludable generalmente apunta a ineficiencia del manejador o contención de la base de datos. Un retraso creciente con errores RPC apunta al endpoint, no al subgrafo.
Solución de problemas de indexación estancada y entidades inconsistentes
Si la indexación deja de avanzar, revisa los registros de Graph Node en busca de errores RPC antes de cambiar el subgrafo. Las causas comunes son un endpoint podado que no puede servir bloques históricos, limitación de tasa durante el relleno, o una reorganización de cadena que obliga al indexador a retroceder. Arregla primero la fuente de datos, luego vuelve a ejecutar.
Si faltan entidades o están duplicadas, inspecciona la construcción del ID de entidad del manejador. Los IDs construidos solo a partir del hash de transacción colisionan cuando una transacción emite múltiples eventos coincidentes; añade el índice de registro como en el ejemplo anterior. Si un manejador lee una entidad que un manejador posterior en el mismo bloque crea, reordena los manejadores o reestructura el esquema para que las escrituras sean independientes.
Para cadenas con reorganizaciones frecuentes, confirma que las suposiciones de finalidad de tu endpoint coinciden con los requisitos de confirmación configurados del subgrafo. El directorio de redes lista las cadenas soportadas, y la guía de confiabilidad cubre los criterios de selección de endpoints.
Limitaciones, compensaciones y próximos pasos
Los subgrafos son excelentes para lecturas con forma de entidad, pero no son un bus de streaming de propósito general. Si necesitas entrega sub-segundo de cada evento a un sumidero personalizado, evalúa Substreams o una suscripción directa a registros. La indexación también añade latencia entre la cabeza de la cadena y el estado consultable, por lo que las aplicaciones que deben leer el bloque más reciente absoluto deberían recurrir a una llamada RPC para esa lectura específica.
Operativamente, el endpoint RPC detrás de Graph Node es una dependencia de primer nivel. Presupuesta acceso a archivo, límites de tasa que acomoden el relleno, y monitoreo que alerte sobre el retraso de indexación. Para comparaciones de costos y planes, consulta la página de precios de RPC.
Como siguiente paso, despliega el mismo subgrafo en una testnet, ejecuta la tabla de verificación nuevamente, y compara el retraso y la latencia entre endpoints. Esa comparación, no una afirmación de proveedor, es la medición que debería impulsar tu elección de producción. Para recomendaciones específicas de endpoints, usa el Asistente RPC.