Skip to content

Commit

Permalink
impl spong-hash Poseidon for arbitrary len inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
dev0x1 committed Oct 10, 2023
1 parent 71a5502 commit c4f7e28
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 5 deletions.
16 changes: 16 additions & 0 deletions packages/anchors/src/PoseidonHasher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
PoseidonT4__factory,
PoseidonT5__factory,
PoseidonT6__factory,
PoseidonT7__factory,
} from '@webb-tools/contracts';
import { Deployer } from '@webb-tools/create2-utils';
import { poseidon_gencontract as poseidonContract } from 'circomlibjs';
Expand Down Expand Up @@ -52,13 +53,19 @@ export class PoseidonHasher {
saltHex,
signer
);
const { contract: poseidonT7Library } = await deployer.deploy(
PoseidonT7__factory,
saltHex,
signer
);

const libraryAddresses = {
['contracts/hashers/Poseidon.sol:PoseidonT2']: poseidonT2Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT3']: poseidonT3Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT4']: poseidonT4Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT5']: poseidonT5Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT6']: poseidonT6Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT7']: poseidonT7Library.address,
};
const { contract } = await deployer.deploy(
PoseidonHasher__factory,
Expand Down Expand Up @@ -112,12 +119,21 @@ export class PoseidonHasher {
const poseidonT6Library = await poseidonT6LibraryFactory.deploy();
await poseidonT6Library.deployed();

const poseidonT7LibraryFactory = new ethers.ContractFactory(
poseidonABI(6),
poseidonBytecode(6),
signer
);
const poseidonT7Library = await poseidonT7LibraryFactory.deploy();
await poseidonT7Library.deployed();

const libraryAddresses = {
['contracts/hashers/Poseidon.sol:PoseidonT2']: poseidonT2Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT3']: poseidonT3Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT4']: poseidonT4Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT5']: poseidonT5Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT6']: poseidonT6Library.address,
['contracts/hashers/Poseidon.sol:PoseidonT7']: poseidonT7Library.address,
};
const factory = new PoseidonHasher__factory(libraryAddresses, signer);

Expand Down
18 changes: 14 additions & 4 deletions packages/contracts/contracts/hashers/IHasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ pragma solidity ^0.8.18;
/// @author Webb Technologies.
/// @notice This contract is meant to be used to generalize over different hash functions.
interface IHasher {
function hash3(uint256[3] memory array) external view returns (uint256);
function hash1(uint256 value) external pure returns (uint256);

function hash4(uint256[4] memory array) external view returns (uint256);
function hash2(uint256[2] calldata array) external pure returns (uint256);

function hash3(uint256[3] calldata array) external pure returns (uint256);

function hash4(uint256[4] calldata array) external pure returns (uint256);

function hash5(uint256[5] calldata array) external pure returns (uint256);

function hash6(uint256[6] calldata array) external pure returns (uint256);

function hash(uint256[] calldata values) external pure returns (uint256);

/// @dev provides a 2 elemtns hash with left and right elements
function hashLeftRight(uint256 _left, uint256 _right) external view returns (uint256);
function hashLeftRight(uint256 _left, uint256 _right) external pure returns (uint256);

/// @dev provides Zero (Empty) elements for a IHasher based MerkleTree. Up to 32 levels
function zeros(uint256 i) external view returns (bytes32);
function zeros(uint256 i) external pure returns (bytes32);
}
20 changes: 20 additions & 0 deletions packages/contracts/contracts/hashers/KeccakHasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import "./IHasher.sol";
/// @author Webb Technologies.
/// @notice This contract is intended to be used with keccak merkle trees.
contract KeccakHasher is IHasher {
function hash1(uint256 value) public pure override returns (uint256) {
return uint256(keccak256(abi.encodePacked(value)));
}

function hash2(uint256[2] memory array) public pure override returns (uint256) {
return uint256(keccak256(abi.encodePacked(array)));
}

function hash3(uint256[3] memory array) public pure override returns (uint256) {
return uint256(keccak256(abi.encodePacked(array)));
}
Expand All @@ -19,6 +27,18 @@ contract KeccakHasher is IHasher {
return uint256(keccak256(abi.encodePacked(array)));
}

function hash5(uint256[5] memory array) public pure override returns (uint256) {
return uint256(keccak256(abi.encodePacked(array)));
}

function hash6(uint256[6] memory array) public pure override returns (uint256) {
return uint256(keccak256(abi.encodePacked(array)));
}

function hash(uint256[] memory array) public pure override returns (uint256) {
return uint256(keccak256(abi.encodePacked(array)));
}

function hashLeftRight(uint256 _left, uint256 _right) public pure override returns (uint256) {
uint256 output = uint256(_left);
uint256 right = uint256(_right);
Expand Down
4 changes: 4 additions & 0 deletions packages/contracts/contracts/hashers/Poseidon.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ library PoseidonT5 {
library PoseidonT6 {
function poseidon(uint256[5] memory input) public pure returns (uint256) {}
}

library PoseidonT7 {
function poseidon(uint256[6] memory input) public pure returns (uint256) {}
}
50 changes: 49 additions & 1 deletion packages/contracts/contracts/hashers/PoseidonHasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,29 @@
pragma solidity ^0.8.18;

import "./IHasher.sol";
import { PoseidonT2, PoseidonT3, PoseidonT4, PoseidonT5, PoseidonT6 } from "./Poseidon.sol";
import { PoseidonT2, PoseidonT3, PoseidonT4, PoseidonT5, PoseidonT6, PoseidonT7 } from "./Poseidon.sol";
import { SnarkConstants } from "./SnarkConstants.sol";

/// @title Poseidon hash functions for 2, 4, 5, and 11 input elements.
/// @author Webb Technologies.
/// @notice This contract is meant to be used for poseidon merkle trees and other poseidon based hashing.
contract PoseidonHasher is SnarkConstants, IHasher {
uint32 internal constant BATCH_SIZE = 6;

function hash1(uint256 value) public pure returns (uint256) {
require(value < SNARK_SCALAR_FIELD, "Value not in field");
uint256[1] memory input;
input[0] = value;
return PoseidonT2.poseidon(input);
}

function hash2(uint256[2] memory array) public pure override returns (uint256) {
for (uint256 i = 0; i < array.length; i++) {
require(array[i] < SNARK_SCALAR_FIELD, "Value not in field");
}
return PoseidonT3.poseidon(array);
}

function hash3(uint256[3] memory array) public pure override returns (uint256) {
for (uint256 i = 0; i < array.length; i++) {
require(array[i] < SNARK_SCALAR_FIELD, "Value not in field");
Expand All @@ -34,6 +43,45 @@ contract PoseidonHasher is SnarkConstants, IHasher {
return PoseidonT5.poseidon(array);
}

function hash5(uint256[5] memory array) public pure override returns (uint256) {
for (uint256 i = 0; i < array.length; i++) {
require(array[i] < SNARK_SCALAR_FIELD, "Value not in field");
}
return PoseidonT6.poseidon(array);
}

function hash6(uint256[6] memory array) public pure override returns (uint256) {
for (uint256 i = 0; i < array.length; i++) {
require(array[i] < SNARK_SCALAR_FIELD, "Value not in field");
}
return PoseidonT7.poseidon(array);
}

function hash(uint256[] calldata values) public pure returns (uint256) {
uint256[BATCH_SIZE] memory frame = [uint256(0), 0, 0, 0, 0, 0];
bool dirty = false;
uint256 fullHash = 0;
uint32 k = 0;
for (uint32 i = 0; i < values.length; i++) {
dirty = true;
frame[k] = values[i];
if (k == BATCH_SIZE - 1) {
fullHash = PoseidonT7.poseidon(frame);
dirty = false;
frame = [uint256(0), 0, 0, 0, 0, 0];
frame[0] = fullHash;
k = 1;
} else {
k++;
}
}
if (dirty) {
// we haven't hashed something in the main sponge loop and need to do hash here
fullHash = PoseidonT7.poseidon(frame);
}
return fullHash;
}

function hashLeftRight(uint256 _left, uint256 _right) public pure override returns (uint256) {
require(uint256(_left) < SNARK_SCALAR_FIELD, "_left should be inside the field");
require(uint256(_right) < SNARK_SCALAR_FIELD, "_right should be inside the field");
Expand Down
1 change: 1 addition & 0 deletions packages/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ subtask('typechain-generate-types', async (taskArgs, hre, runSuper) => {
await buildPoseidon(3);
await buildPoseidon(4);
await buildPoseidon(5);
await buildPoseidon(6);
await runSuper();
});

Expand Down

0 comments on commit c4f7e28

Please sign in to comment.