⛓️ Blockchain Learning Path GitHub

10 · Oráculos, almacenamiento e indexación

Nivel: Avanzado · ⏱️ Duración estimada: 150 min · Fuente: documentación de Chainlink y de The Graph ⬅️ Currículo · 📚 Bibliografía 🧭 ⬅️ Anterior: 09 · Seguridad y auditoría · 📚 Índice · ➡️ Siguiente: 11 · DAO y gobernanza 📖 Glosario de términos · 🌱 ¿Nuevo en esto? Empieza aquí


🎯 Objetivos

📚 Resultados de aprendizaje

Al finalizar, el estudiante podrá:

  1. Explicar por qué la blockchain no conoce datos del mundo real y qué garantiza un oráculo.
  2. Implementar validaciones de antigüedad, rango y ronda al leer un feed de precios.
  3. Comparar precio spot, TWAP y agregación frente a un ataque de manipulación por flash loan.
  4. Diseñar un subgraph o indexador que exponga eventos históricos de un contrato.
  5. Justificar cuándo un dato debe vivir como estado en cadena y cuándo basta con un evento indexado.
  6. Evaluar una estrategia de pinning para asegurar la disponibilidad de contenido en IPFS.

🗺️ Temas

# Tema Por qué importa
1 El problema del oráculo Todo dato externo importa su propio modelo de confianza a la cadena.
2 Validación de feeds Antigüedad, rango, decimales y ronda completa evitan usar precios inválidos.
3 Spot vs. TWAP El precio instantáneo es fácil de mover; el promedio temporal encarece el ataque.
4 Agregación de fuentes Combinar proveedores reduce el impacto de una fuente comprometida.
5 Eventos e indexación Permiten reconstruir historial off-chain con The Graph y subgraphs.
6 Estado vs. evento Un evento no es verificable por otro contrato; el estado sí.
7 IPFS y CID El direccionamiento por contenido garantiza integridad, no disponibilidad.
8 Aleatoriedad (VRF) El azar verificable evita que un validador manipule sorteos y loterías.

🧠 Modelo mental

Un contrato inteligente es como una persona encerrada en una habitación sin ventanas: puede razonar con impecable lógica, pero solo conoce lo que alguien desliza por debajo de la puerta. El oráculo es ese mensajero. Por muy correcta que sea la lógica interna, si el papel que entra dice un precio falso, la decisión será igual de falsa. Por eso la pregunta clave nunca es "¿el contrato calcula bien?", sino "¿puedo confiar en quién y cómo entró el dato?".

El límite de la analogía: un solo mensajero es un punto único de fallo, así que en la práctica se usan varios mensajeros y se descartan los que llegan tarde o con cifras fuera de rango. Además, distinguir estado de evento es clave: los eventos son como las notas que el mensajero deja apiladas fuera de la habitación (útiles para reconstruir la historia desde afuera), pero el ocupante no puede leerlas ni actuar sobre ellas; solo el estado en cadena está dentro de la habitación.

🧩 Esquema visual

En un oráculo push, varios nodos independientes leen múltiples fuentes, agregan el valor y lo publican en un contrato feed que el protocolo consume con sus propias validaciones.

flowchart LR
    F1["Fuente de datos A"] --> N1["Nodo operador 1"]
    F2["Fuente de datos B"] --> N2["Nodo operador 2"]
    F3["Fuente de datos C"] --> N3["Nodo operador 3"]
    N1 --> AG["Agregación con mediana"]
    N2 --> AG
    N3 --> AG
    AG --> FC["Contrato feed on-chain"]
    FC --> PR["Protocolo consumidor"]
    PR --> VA["Valida antigüedad, rango y ronda"]

La indexación recorre el camino inverso: convierte eventos emitidos en cadena en datos consultables fuera de ella.

flowchart TD
    C["Contrato emite un evento"] --> N["Nodo de la red"]
    N --> I["Indexador o subgraph"]
    I --> DB["Base de datos consultable"]
    DB --> API["API GraphQL"]
    API --> FE["Frontend de la dapp"]

📖 Conceptos y definiciones

🔬 Profundización

Parámetros reales de un feed: heartbeat y deviation threshold

Un feed push de Chainlink no publica un precio nuevo en cada bloque: se actualiza cuando se cumple cualquiera de dos condiciones. El deviation threshold dispara una actualización si el precio observado off-chain se desvía del último publicado más de un porcentaje dado; el heartbeat fuerza una actualización si pasó demasiado tiempo desde la anterior, aunque el precio no se haya movido. Un feed mayor como ETH/USD en Ethereum mainnet opera típicamente con una desviación de ±0.5 % y un heartbeat de 3600 s, mientras que feeds de activos menos líquidos usan umbrales más laxos — verifica siempre los parámetros del feed concreto en vivo, porque cambian por activo y por red.

La consecuencia para el consumidor es directa: tu validación de antigüedad debe tolerar al menos el heartbeat (un dato de 50 minutos puede ser normal en un feed de 3600 s), y tu lógica debe asumir que el precio on-chain puede diferir del de mercado hasta el umbral de desviación. Un contrato que trate esa banda como error se detendrá en operación normal; uno que la ignore por completo subestima su margen de error económico.

Manipulación de precio spot con flash loan: los números

Supón un pool AMM de producto constante con 100 ETH y 200 000 USDC (k = 20 000 000), es decir, un precio spot de 2000 USDC/ETH. Un atacante pide un flash loan de 200 000 USDC y los mete al pool: las reservas pasan a 400 000 USDC y 50 ETH, y el precio spot instantáneo salta a 8000 USDC/ETH — se cuadruplicó dentro de una sola transacción. Si un protocolo de préstamos lee ese spot como oráculo, el atacante deposita ETH "valorado" a 8000, pide prestado contra ese colateral inflado, deshace el swap, devuelve el flash loan y se queda con la diferencia.

Un TWAP encarece esto radicalmente: si la ventana es de 1800 s y un bloque dura ~12 s, un pico de un solo bloque pesa apenas 12 / 1800 ≈ 0.7 % del promedio, así que sostener un precio falso exige mantener el capital en riesgo durante muchos bloques, expuesto al arbitraje. El caso ilustrativo es Mango Markets (octubre de 2022, ~114 M USD): el atacante infló el precio del token MNGO —de baja liquidez— en los mercados que alimentaban el oráculo y usó su posición revalorizada como colateral para vaciar la plataforma. La lección no es "los oráculos fallan", sino que el coste de manipular la fuente debe superar siempre al botín alcanzable con ella.

Oráculos push vs. pull

El modelo push (Chainlink Data Feeds) publica proactivamente en cadena y todos los consumidores leen el mismo valor; el modelo pull u on-demand (Pyth) mantiene los precios firmados off-chain y es el usuario quien los sube en la misma transacción que los consume.

Dimensión Push (Chainlink feeds) Pull (Pyth on-demand)
Quién paga el gas de actualizar Los operadores del feed, de forma continua El consumidor, solo cuando necesita el dato
Frescura Limitada por heartbeat y deviation Precio de hace segundos, firmado off-chain
Coste para el protocolo Lectura barata de un valor ya publicado Verificación de la firma y publicación en cada uso
Riesgo característico Dato añejo dentro de la banda permitida El consumidor puede elegir qué actualización sube; hay que validar el timestamp
Encaja mejor en Préstamos y colateral en L1 Perps y trading de alta frecuencia en L2

Ninguno domina: el push amortiza el coste entre todos los usuarios y simplifica el consumo; el pull ofrece latencia mínima a cambio de trasladar validaciones al integrador.

Los cuatro modos de fallo de un oráculo, con su defensa

"Usa un oráculo fiable" no es un diseño. Un oráculo puede fallar de cuatro maneras distintas y cada una necesita una defensa diferente; confundirlas es cómo se construye un sistema que parece protegido y no lo está.

Modo de fallo Qué ocurre Lo que NO lo detecta Lo que sí
Se detiene El feed deja de actualizarse y devuelve el último precio, correcto en su día Validar el valor: el número es plausible Comprobar el timestamp: rechazar si supera maxAge
Se manipula Alguien mueve el precio spot durante un bloque Validar la antigüedad: el dato es de hace un segundo TWAP o agregación de varias fuentes
Se equivoca La fuente publica un valor real pero absurdo (un flash crash, un error del proveedor) Ni antigüedad ni agregación si todas leen lo mismo Circuit breaker: rango de cordura y pausa
Desaparece El proveedor deja de operar el feed Nada de lo anterior Fallback a otra fuente y plan de migración

La comprobación mínima de una lectura, con las dos primeras defensas juntas:

(, int256 respuesta, , uint256 actualizadoEn, ) = feed.latestRoundData();

if (respuesta <= 0) revert PrecioInvalido();
if (block.timestamp - actualizadoEn > MAX_AGE) revert PrecioObsoleto();
// El feed tiene sus propios decimales: normalizar antes de comparar con nada.
uint256 precio = uint256(respuesta) * 1e18 / (10 ** feed.decimals());

Tres líneas que faltan en la mayoría de las integraciones improvisadas, y que cubren dos de los cuatro modos.

El matiz que decide el diseño: MAX_AGE no es un número universal. Depende del heartbeat del feed —cada cuánto se actualiza por contrato— y de la tolerancia de tu operación. Un feed que actualiza cada hora con un MAX_AGE de 10 minutos revierte constantemente; uno que actualiza cada minuto con un MAX_AGE de un día acepta datos inútiles. Ese número se saca de la documentación del feed, no de la intuición.

💡 En una frase: un oráculo puede darte un dato viejo, manipulado, erróneo o ninguno. Si tu contrato solo se defiende de uno, está protegido contra una cuarta parte del problema.

🎓 Si ya dominas esto — lo que decide la arquitectura de datos
  • Push y pull tienen modelos de coste opuestos. Los feeds push (Chainlink Data Feeds) actualizan cuando se desvía un umbral o vence el heartbeat, y el coste lo asume la red. Los pull (Pyth, API3) exigen que la transacción traiga el dato firmado, lo que da frescura a cambio de complicar el flujo del cliente.
  • El TWAP de Uniswap v3 no es gratis en precisión. Una ventana corta es manipulable; una larga va por detrás en mercados volátiles y puede liquidar mal en un movimiento real. Elegir la ventana es elegir a qué ataque te expones.
  • Los eventos no son un registro permanente. Los nodos pueden podarlos según su política de retención. Si tu sistema depende de reconstruir el estado desde el evento cero, depende de que alguien conserve ese historial: por eso los indexadores mantienen su propia base y no reconsultan la cadena entera.
  • Las reorganizaciones rompen los indexadores ingenuos. Un subgraph que aplica eventos sin gestionar reorgs proyecta estado que la cadena luego descarta. Hay que esperar confirmaciones o implementar deshacer por bloque.
  • Un CID de IPFS garantiza integridad, nunca disponibilidad. El pinning es un contrato de servicio con alguien. Arweave y Filecoin convierten la persistencia en un pago explícito, que es lo honesto: alguien tiene que pagar el almacenamiento perpetuo.
  • La aleatoriedad on-chain no existe sin ayuda. block.timestamp y blockhash los influye quien propone el bloque. Un VRF aporta una prueba criptográfica de que el número no se eligió a conveniencia, y ese es el punto entero.

🧪 Laboratorio guiado

🧪 Estas prácticas están catalogadas y resueltas paso a paso en el catálogo de laboratorios.

  1. Revisa el indexador de eventos del repositorio, ubicado en apps/event-indexer, y observa cómo transforma eventos en datos consultables.

  2. Ejecuta su suite de pruebas para validar que la indexación reproduce el historial esperado.

pnpm test:indexer
  1. En los datos indexados, identifica un evento y razona por qué otro contrato no podría depender de él como fuente de verdad.

  2. Diseña sobre papel el consumo de un feed de precios con validación de antigüedad, rango, ronda y un fallback, y compáralo con un TWAP.

📝 Reto verificable

Especifica el consumo seguro de un oráculo de precios para un contrato hipotético: define validaciones de antigüedad, rango y ronda completa, una fuente de respaldo y un circuit breaker, y explica cómo un TWAP mitigaría un ataque de manipulación por flash loan.

Criterio de aceptación: el diseño enumera cada validación con su umbral y su comportamiento ante fallo; distingue explícitamente cuándo usar spot, TWAP o agregación; y justifica por qué el dato crítico se maneja como estado verificable y no como simple evento.

⚠️ Errores frecuentes

Síntoma Causa y cómo comprobarlo
El contrato usa un precio congelado No se valida la antigüedad; comprueba el timestamp de la última ronda antes de usarla.
Un ataque mueve el precio por un bloque Uso de precio spot manipulable; sustitúyelo por un TWAP o por agregación de fuentes.
Los decimales dan resultados absurdos Se ignoran los decimals del feed; normaliza siempre antes de operar.
Otro contrato "lee" un evento Confusión entre evento y estado; los eventos solo se consumen off-chain.
El NFT pierde su imagen El CID apunta a contenido sin pinning; asegura persistencia con un servicio o con Arweave/Filecoin.
El sorteo parece amañado Aleatoriedad predecible en cadena; usa un VRF con prueba verificable.

🛡️ Seguridad y ética

🔗 Referencias

✅ Criterio de dominio


🧭 Navegación

⬅️ Módulo 09 · Seguridad y auditoría · 📚 Índice del currículo · ➡️ Módulo 11 · DAO y gobernanza


🧠 Autoevaluación del módulo

Responde sin volver atrás. Cada opción incorrecta corresponde a un error frecuente documentado en este mismo módulo: si fallas, la explicación te dice qué releer.