Circuits
Poseidon2 hash
All hashing in Stealth Pay uses Poseidon2 over BN254 — commitments, nullifiers, and Merkle nodes. It is ZK-friendly (cheap to prove in a circuit) and algebraically native to the BN254 field used by UltraHonk.
Sponge construction
typescript
// hash2: 2-input domain-separated hash
function hash2(a: bigint, b: bigint): bigint {
const iv = mod(2n * TWO_POW_64); // capacity = 2·2^64
let state = [a, b, 0n, iv];
state = permute(state);
return state[0];
}
// hash4: used for commitments
function hash4(a: bigint, b: bigint, c: bigint, d: bigint): bigint {
const iv = mod(4n * TWO_POW_64); // capacity = 4·2^64
let state = [a, b, c, iv];
state = permute(state);
state[0] = mod(state[0] + d);
state = permute(state);
return state[0];
}Usage in Stealth Pay
| Purpose | Call |
|---|---|
| Commitment | hash4(pubkey, token, amount, salt) |
| Nullifier | hash2(privkey, commitment) |
| Merkle node | hash2(left, right) |
| Spending pubkey | hash2(privkey, privkey) |
✦
The TypeScript SDK uses
@zkpassport/poseidon2 which passes the official Barretenberg test vector: permute([0,1,2,3])[0] === 0x01bd538c...01737.