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
circomlibjslibrary 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:
- The commitment relayer is operated by the same entity that operates the chain and contracts.
- Clients can optionally verify the server's output by computing the hash locally when resources permit (e.g., on desktop).
- 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:
| Limit | Value |
|---|---|
| Per-IP request rate | 60 requests/minute |
| Burst allowance | 10 requests |
| Max payload size | 4 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.