Skip to main content

Commitment Relayer

The commitment relayer (ghost-commitment-relayer, port 3002) is an HTTP API that provides server-side Poseidon hash computation for clients that cannot perform these operations locally. Mobile devices and constrained browser environments struggle to run circomlibjs efficiently, so this service offloads the computation to the server.

Problem

Poseidon hashing is fundamental to the Ghost Protocol — every commitment, nullifier derivation, token ID computation, and Merkle node hash uses a Poseidon variant. The reference JavaScript implementation (circomlibjs) relies on big-integer arithmetic over the BN254 scalar field, which is computationally expensive:

  • Mobile browsers: JavaScript BigInt performance on iOS Safari and Android Chrome is significantly slower than desktop, making real-time Poseidon computation impractical for interactive flows.
  • React Native: The circomlibjs library depends on Node.js-specific APIs and WASM modules that do not load reliably in React Native environments.
  • Low-power devices: NFC card scanning flows require immediate hash computation; a multi-second Poseidon call on a low-end phone degrades the user experience unacceptably.

The commitment relayer solves this by running Poseidon on the server and exposing the results via a simple HTTP API.

Endpoints

POST /poseidon2

Computes a Poseidon hash over 2 inputs (PoseidonT3).

Request:

{
"inputs": ["0x1a2b...", "0x3c4d..."]
}

Response:

{
"hash": "0x7e8f..."
}

Use cases: Merkle node hashing, nullifier derivation (Poseidon2(nullifier_secret, nullifier_secret)), token ID derivation, access tags.

POST /poseidon4

Computes a Poseidon hash over 4 inputs (PoseidonT5).

Request:

{
"inputs": ["0x...", "0x...", "0x...", "0x..."]
}

Response:

{
"hash": "0x..."
}

Use cases: OpenGhost commitments (Poseidon4(secret, nullifier_secret, data_hash, blinding)).

POST /poseidon7

Computes a Poseidon hash over 7 inputs (PoseidonT8).

Request:

{
"inputs": ["0x...", "0x...", "0x...", "0x...", "0x...", "0x...", "0x..."]
}

Response:

{
"hash": "0x..."
}

Use cases: Ghost Protocol commitments (Poseidon7(secret, nullifier_secret, blinding, token_id, amount, policy_id, policy_params_hash)).

GET /health

Returns service health status.

{
"status": "healthy",
"uptime": 432000
}

Input Format

All inputs must be valid BN254 scalar field elements provided as hex-encoded strings (with or without 0x prefix) or decimal strings. The service validates that each input is in the range [0, p) where:

p = 21888242871839275222246405745257275088548364400416034343698204186575808495617

If any input exceeds the field modulus, the service returns a 400 Bad Request error with a descriptive message.

Security Considerations

Trust Model

Clients that use the commitment relayer are trusting the server to compute hashes correctly. A malicious or compromised server could return incorrect hashes, which would result in:

  • Incorrect commitments: The user would commit a value that does not match their local secret, making the commitment unrevealable.
  • Incorrect nullifiers: Nullifier derivation errors would prevent reveal transactions from being accepted by the circuit.

This trust assumption is acceptable for the current deployment because:

  1. The commitment relayer is operated by the same entity that operates the chain and contracts.
  2. Clients can optionally verify the server's output by computing the hash locally when resources permit (e.g., on desktop).
  3. The correctness of the hash is ultimately verified by the ZK circuit at reveal time — an incorrect commitment simply becomes unredeemable, not exploitable.

CORS Policy

The service enforces a CORS (Cross-Origin Resource Sharing) policy that restricts which origins can call the API:

  • Allowed origins: The Specter webapp domain and localhost for development.
  • Allowed methods: POST, GET, OPTIONS.
  • Allowed headers: Content-Type, Authorization.

Requests from unauthorized origins receive a 403 Forbidden response.

Rate Limiting

To prevent abuse and resource exhaustion, the commitment relayer enforces rate limits:

LimitValue
Per-IP request rate60 requests/minute
Burst allowance10 requests
Max payload size4 KB

Requests exceeding the rate limit receive a 429 Too Many Requests response with a Retry-After header.

Implementation

The service is a Node.js Express application that loads circomlibjs at startup and initializes the Poseidon hash instances for each input width. The initialization is performed once and the hash functions are reused for all subsequent requests:

// Initialization (once at startup)
const poseidon2 = await buildPoseidon(); // PoseidonT3
const poseidon4 = await buildPoseidon(); // PoseidonT5
const poseidon7 = await buildPoseidon(); // PoseidonT8

Each request deserializes the inputs, calls the appropriate Poseidon function, and serializes the output as a hex string.

Deployment

The commitment relayer runs as a PM2-managed process on port 3002 and is accessible through Caddy at relayer.specterchain.com via path-based routing.