Saltar a contenido

Servicio de anclaje

Traducción informativa de la versión en inglés, actualizada el 25/10/2026. Si hay diferencias, prevalece el original inglés. Los identificadores y los bloques de código, incluidos los comentarios, se conservan sin traducir.

Servicio HTTP destinado a anclar hashes de Citizenship Bundles en blockchains L2 (Base, Arbitrum, Optimism), individualmente o en lotes bajo una raíz Merkle. Actualmente solo los registra de forma local (consulta la nota siguiente).

El envío on-chain todavía no está implementado. Los hashes y las raíces de lotes se registran localmente con tx_hash: null y onchain: false; el servicio nunca inventa un hash de transacción. ANCHOR_RPC_URL, ANCHOR_PRIVATE_KEY y ANCHOR_CONTRACT todavía no se utilizan. El estado está en memoria (consulta Persistencia).

Inicio rápido

# Start the service, from the repo root after installing the root dependencies
node services/anchor/index.js

# Or with Docker, from the repo root
docker compose -f docker/docker-compose.yml up anchor

Por defecto, el servicio escucha en http://localhost:3001.

Endpoints

GET /health

Comprobación del estado del servicio.

curl http://localhost:3001/health
{
  "ok": true,
  "service": "dcp-anchor",
  "mode": "batch",
  "pending": 0,
  "total_anchored": 5
}

total_anchored cuenta registros locales, uno por lote, no hashes. Una respuesta de estado saludable no acredita ninguna operación on-chain.

POST /anchor

Envía un hash de bundle para anclarlo: sha256: seguido de 64 caracteres hexadecimales en minúscula (el prefijo es opcional). Cualquier otro formato, o un cuerpo mayor de 1 MB, devuelve 400.

curl -X POST http://localhost:3001/anchor \
  -H "Content-Type: application/json" \
  -d '{"bundle_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"}'

Modo individual — Registra el hash inmediatamente, de forma local: anchored: true significa que este servicio lo registró, no que esté on-chain (onchain: false, tx_hash: null):

{
  "anchored": true,
  "hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "count": 1,
  "timestamp": "2025-01-01T00:00:00.000Z",
  "tx_hash": null,
  "onchain": false,
  "chain": "base"
}

Modo por lotes — Acepta y encola (HTTP 202):

{
  "accepted": true,
  "bundle_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "position": 3,
  "hint": "Will be anchored in next batch (interval: 60000ms)"
}

GET /status/:hash

Consulta el estado de un hash anclado.

curl http://localhost:3001/status/sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
{
  "anchored": true,
  "hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
  "count": 1,
  "timestamp": "2025-01-01T00:00:00.000Z",
  "tx_hash": null,
  "onchain": false,
  "chain": "base"
}

Si todavía no está anclado pero está pendiente:

{
  "anchored": false,
  "pending": true
}

Un registro solo se encuentra por el hash que almacena. En modo individual, y para un lote de un solo hash, es el hash enviado (con el prefijo sha256:). Para un lote de varios hashes, es únicamente su raíz Merkle, de modo que cada uno de esos hashes pasa a responder {"anchored": false, "pending": false}; todavía no hay pruebas de inclusión por hash. Mientras está pendiente, el hash se compara tal como se envió (con o sin prefijo). Los registros están en memoria y se pierden al reiniciar.

GET /anchored

Enumera todos los registros anclados.

curl http://localhost:3001/anchored
{
  "records": [
    { "hash": "sha256:...", "count": 1, "timestamp": "...", "tx_hash": null, "onchain": false, "chain": "base" }
  ]
}

POST /flush

Fuerza el procesamiento del lote pendiente (solo en modo por lotes). Requiere el token del operador; sin OPERATOR_TOKEN configurado, el endpoint permanece bloqueado (401).

curl -X POST http://localhost:3001/flush -H "Authorization: Bearer $OPERATOR_TOKEN"
{ "flushed": true }

Configuración

Variables de entorno

Variable Valor predeterminado Descripción
PORT 3001 Puerto HTTP
ANCHOR_MODE "batch" Modo de anclaje: "individual" o "batch"
BATCH_INTERVAL_MS 60000 Intervalo de procesamiento automático en ms (modo por lotes)
ANCHOR_RPC_URL — URL JSON-RPC del nodo L2 (todavía no se usa; si se configura, se registra una advertencia)
ANCHOR_PRIVATE_KEY — Clave privada de la cartera para firmar transacciones (todavía no se usa)
ANCHOR_CONTRACT — Dirección del contrato DCPAnchor desplegado (todavía no se usa)
ANCHOR_CHAIN "base" Identificador de la cadena
OPERATOR_TOKEN — Token Bearer para POST /flush. Sin configurar: bloqueado

Modos de funcionamiento

Individual (ANCHOR_MODE=individual): Cada hash obtiene su propio registro local de inmediato. Cuando se conecte el envío on-chain, cada uno sería una transacción separada.

Por lotes (ANCHOR_MODE=batch): Los hashes se acumulan y se registran como una única raíz Merkle cada BATCH_INTERVAL_MS milisegundos, o inmediatamente mediante POST /flush. Cuando se conecte el envío on-chain, cada lote sería una transacción.

Integración con DCPAnchor.sol

Una vez conectado, el servicio interactuará con el contrato inteligente DCPAnchor.sol:

  • Modo individual: Llamará a anchorBundle(bytes32 bundleHash)
  • Modo por lotes: Calculará la raíz Merkle de los hashes pendientes y llamará a anchorBatch(bytes32 merkleRoot, uint256 count)

Consulta contracts/ethereum/README.md para conocer los detalles del contrato.

Desarrollo

# Start in development mode, from the repo root
PORT=3001 ANCHOR_MODE=batch node services/anchor/index.js

# Test with curl
curl http://localhost:3001/health
curl -X POST http://localhost:3001/anchor \
  -H "Content-Type: application/json" \
  -d '{"bundle_hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"}'

Licencia

Apache-2.0