diff --git a/packages/anchors/src/PoseidonHasher.ts b/packages/anchors/src/PoseidonHasher.ts index c3260c14..61480afb 100644 --- a/packages/anchors/src/PoseidonHasher.ts +++ b/packages/anchors/src/PoseidonHasher.ts @@ -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'; @@ -52,6 +53,11 @@ export class PoseidonHasher { saltHex, signer ); + const { contract: poseidonT7Library } = await deployer.deploy( + PoseidonT7__factory, + saltHex, + signer + ); const libraryAddresses = { ['contracts/hashers/Poseidon.sol:PoseidonT2']: poseidonT2Library.address, @@ -59,6 +65,7 @@ export class PoseidonHasher { ['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, @@ -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); diff --git a/packages/contracts/contracts/hashers/IHasher.sol b/packages/contracts/contracts/hashers/IHasher.sol index 199cd240..03b0ed2f 100644 --- a/packages/contracts/contracts/hashers/IHasher.sol +++ b/packages/contracts/contracts/hashers/IHasher.sol @@ -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); } diff --git a/packages/contracts/contracts/hashers/KeccakHasher.sol b/packages/contracts/contracts/hashers/KeccakHasher.sol index e6d78b02..bc3a177a 100644 --- a/packages/contracts/contracts/hashers/KeccakHasher.sol +++ b/packages/contracts/contracts/hashers/KeccakHasher.sol @@ -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))); } @@ -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); diff --git a/packages/contracts/contracts/hashers/Poseidon.sol b/packages/contracts/contracts/hashers/Poseidon.sol index 27ea134d..5ed313ce 100644 --- a/packages/contracts/contracts/hashers/Poseidon.sol +++ b/packages/contracts/contracts/hashers/Poseidon.sol @@ -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) {} +} diff --git a/packages/contracts/contracts/hashers/PoseidonHasher.sol b/packages/contracts/contracts/hashers/PoseidonHasher.sol index 94829acd..077715a3 100644 --- a/packages/contracts/contracts/hashers/PoseidonHasher.sol +++ b/packages/contracts/contracts/hashers/PoseidonHasher.sol @@ -6,13 +6,15 @@ 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; @@ -20,6 +22,13 @@ contract PoseidonHasher is SnarkConstants, IHasher { 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"); @@ -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"); diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index c7439c9b..4b20ac20 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -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(); });