Nim / C implementations of arithmetic hash functions over the Goldilocks field
Experimental implementation of arithmetic hash functions (like for example Poseidon2)
specialized to the Goldilocks field p = 2^64 - 2^32 + 1. Mostly uses C implementations internally.
Hash functions supported
[x] Poseidon2 (t=12) [ ] Monolith (t=12) [ ] Tip4' (t=12) [ ] Tip5 (t=16)
The Poseidon2 implementation is compatible with Horizen Lab's one at 4. The Monolith implementation is compatible with 6.
Installation
Use the Nimble package manager to add goldilocks_hash to an existing
project. Add the following to its .nimble file:
requires "goldilocks_hash >= 0.0.1 & < 1.0.0"
Conventions
Hash digests consist of 4 field elements (approximately 256 bits).
When constructing binary Merkle trees, we similarly work on units of 4 field elements. We use a custom ``safe'' Merkle tree building convention, which ensures that different inputs can never produce the same Merkle root (except with negligible probability).
When hashing bytes, first we pad the byte sequence to a multiple of 31 bytes using
the 10* padding strategy, and then we convert each 31 byte piece into 4 field
elements by using the lowest 62 bits. We do this for two reasons: 1) to be a
drop-in replacement for the BN254 implementation which also takes 31 bytes at
a time; and 2) because hashing 31/62 bytes with one permutation is almost 11%
more efficient than using only 28/56 bytes at a time.
When hashing field elements, similarly we pad using the 10* strategy. Domain
separation ensures that using different sponge rates, or different types of
input don't produce the same hash.
Usage
Hashing bytes into with the sponge construction:
import goldilocks_hash/poseidon2
let input = [1'u8, 2'u8, 3'u8] # some bytes that you want to hash
let digest: Digest = Sponge.digest(input)
Converting a hash digest (4 field elements) into bytes:
let output: array[32, byte] = digest.toBytes
Combining field elements, useful for constructing a binary Merkle tree:
let left = Sponge.digest( [1'u8, 2'u8, 3'u8] )
let right = Sponge.digest( [4'u8, 5'u8, 6'u8] )
let combination = compress(left, right)
Building Merkle trees:
let input: seq[Digest] = ...
let digest: F = Merkle.digest(input)