Skip to content

FEATURE: Allow custom hash functions to Internal library of LazyIMT #58

Open
jimjimvalkema wants to merge 9 commits intozk-kit:mainfrom
jimjimvalkema:lazy-imt
Open

FEATURE: Allow custom hash functions to Internal library of LazyIMT #58
jimjimvalkema wants to merge 9 commits intozk-kit:mainfrom
jimjimvalkema:lazy-imt

Conversation

@jimjimvalkema
Copy link

@jimjimvalkema jimjimvalkema commented Oct 16, 2025

Description

I changed the InternalLazyIMT library so the user can provide their own hash function as an function parameter. And also their own default zeros.
I have setup the LazyIMT.sol library so it uses the poseidon hash function so everything behaves as usual.

changes made

  1. Remove PoseidonT3, _defaultZero, SNARK_SCALER_LIMT from InternalLazyIMT.
  2. Call PoseidonT3 through an interface instead like this IHasher(hasher).hash([left,right]) (this also saves gas).
  3. Add _hasher, _defaultZero, SNARK_SCALER_LIMT as added arguments to functions that need them.
  4. Rename SNARK_SCALER_LIMT to hasherLimit since it not every hash function is snark based.
  5. In LazyIMT: added the HASHER_ADDRESS address as a constant, _defaultZero and _hasher function, and set the hasherLimit to the snark field limit

here is a snipet:

in InternalLazyIMT

library InternalLazyIMT {
...
    function _insert(
        LazyIMTData storage self,
        uint256 leaf,
        function(uint256[2] memory) view returns (uint256) hasher,
        uint256 hasherLimit
    ) internal {
...
}

Then this is used as follows in LazyIMT

import {SNARK_SCALAR_FIELD} from "./Constants.sol";
library LazyIMT {
 // This is poseidonT3 from: https://github.com/chancehudson/poseidon-solidity
 address internal constant hasher = 0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93;
 
 uint256 internal constant Z_0 = 0;
 ...
function _hasher(uint256[2] memory input) internal view returns (uint256) {
    return IHasherT3(HASHER_ADDRESS).hash(input);
}

function _defaultZero(uint256 index) internal pure returns (uint256) {
      if (index == 0) return Z_0;
      ...
      revert WrongDefaultZeroIndex();
  }

    function insert(LazyIMTData storage self, uint256 leaf) public {
        InternalLazyIMT._insert(self, leaf, _hasher, SNARK_SCALAR_FIELD);
    }
...
}

test changes

I changed the deploy-imt-test task to deploy poseidon with the deterministic proxy so it always deploys to the same address. See in the poseidon-solidity readme

Breaking changes

I changed error that mention SNARK_SCALAR_LIMIT to instead say hasherLimit, since the hash function is now generic. This might break error handling for users that expect the old error.
LazyIMT now uses the create2 address of poseidonT3 directly, so users using a chain where it isn't deployed yet (or hardhat tests), will need to deploy it them selfs now.

If an user used the Internal version of the libraries for some reason. Then they do have now pass these new parameters like _hasher, _defaultZeros and hasherLimit.

Related Issue(s)

Closes #56

Other information

Calling poseidon with a interface saves from 10k - 100k gas for most functions. But making the libraries generic does add 0~100 gas on average.
In total expect a gas saving of 10k gas on average!
image

Checklist

  • I have read and understand the contributor guidelines and code of conduct.
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have run yarn style without getting any errors
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Important

We do not accept pull requests for minor grammatical fixes (e.g., correcting typos, rewording sentences) or for fixing broken links, unless they significantly improve clarity or functionality. These contributions, while appreciated, are not a priority for merging. If you notice any of these issues, please create a GitHub Issue to report them so they can be properly tracked and addressed.

internalLazyImt can use different hash functions by passing in a hash function as a parameter to
every function that uses the hash function. LazyIMT does this for poseidonT3.

BREAKING CHANGE: InternalLazyIMT requires additional inputs.

Errors mention hasherLimit instead of SNARK_SCALAR_FIELD
…ddress

switched to passing hasher as an function instead of an address. This allows for easier and more gas
efficient intergration of other hash functions that do not have the same interface as poseidon. Like
zemse poseidon2Huff or sha256 and keccak.
@jimjimvalkema jimjimvalkema marked this pull request as ready for review October 17, 2025 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support different hash functions

1 participant