Skip to main content

Poseidon Hash

The Ghost Protocol uses Poseidon hashing for commitments and nullifiers. This is a ZK-friendly hash function optimized for use in circuits.

Installation

npm install circomlibjs

Basic Usage

import { buildPoseidon } from 'circomlibjs';

const poseidon = await buildPoseidon();
const F = poseidon.F;

// Hash two values
function hash2(a, b) {
return BigInt(F.toString(poseidon([BigInt(a), BigInt(b)])));
}

Commitment Computation

function computeCommitment(secret, nullifierSecret, dataHash, blinding) {
const inner1 = hash2(secret, nullifierSecret);
const inner2 = hash2(dataHash, blinding);
return hash2(inner1, inner2);
}

// Usage
const commitment = computeCommitment(
secretBn,
nullifierSecretBn,
dataHash,
blindingBn
);

Nullifier Computation

function computeNullifier(nullifierSecret, commitment, leafIndex) {
const intermediate = hash2(nullifierSecret, commitment);
return hash2(intermediate, leafIndex);
}

// Usage (after getting leafIndex from commit event)
const nullifier = computeNullifier(
nullifierSecretBn,
commitment,
leafIndex
);

Field Size

All values must be valid BN254 field elements:

const FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;

// Ensure values are in field
const value = someBigInt % FIELD_SIZE;

Complete Example

import { buildPoseidon } from 'circomlibjs';

const FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;

async function createCommitment(dataHash) {
const poseidon = await buildPoseidon();
const F = poseidon.F;

// Generate random secrets
const secret = randomFieldElement();
const nullifierSecret = randomFieldElement();
const blinding = randomFieldElement();

// Compute commitment
const inner1 = F.toString(poseidon([secret, nullifierSecret]));
const inner2 = F.toString(poseidon([dataHash, blinding]));
const commitment = F.toString(poseidon([BigInt(inner1), BigInt(inner2)]));

return {
secret,
nullifierSecret,
blinding,
commitment: '0x' + BigInt(commitment).toString(16).padStart(64, '0'),
};
}

function randomFieldElement() {
const bytes = crypto.getRandomValues(new Uint8Array(32));
return BigInt('0x' + Buffer.from(bytes).toString('hex')) % FIELD_SIZE;
}

Important Notes

Order Matters

The order of inputs to Poseidon matters. Poseidon(a, b)Poseidon(b, a). Always use the exact formula from the documentation.

Commitment Formula
commitment = Poseidon(Poseidon(secret, nullifierSecret), Poseidon(dataHash, blinding))
Nullifier Formula
nullifier = Poseidon(Poseidon(nullifierSecret, commitment), leafIndex)