Skip to content

FEATURE: Allow custom hash functions to Internal libraries of: IMT#60

Draft
jimjimvalkema wants to merge 19 commits intozk-kit:mainfrom
jimjimvalkema:imt
Draft

FEATURE: Allow custom hash functions to Internal libraries of: IMT#60
jimjimvalkema wants to merge 19 commits intozk-kit:mainfrom
jimjimvalkema:imt

Conversation

@jimjimvalkema
Copy link

Description

I changed the internal library of IMT so the user can provide their own hash function with an address that implements their hash function with the interface that is the same as PoseidonT3(T6 for QuinaryIMT). And also their own default zeros for IMT.
I made the libraries that use the internal libraries use the poseidon so they behave like usual.

changes made

  1. Remove PoseidonT3, _defaultZero, SNARK_SCALER_LIMT from InternalBinaryIMT.
  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 BinaryIMT: add the hasher address as a constant, add _defaultZero, and set the hasherLimit

here is a snipet:

in InternalBinaryIMT

library InternalBinaryIMT {
...
  function _insert(
      BinaryIMTData storage self,
      uint256 leaf,
      address hasher,
      uint256 hasherLimit,
      function(uint256) pure returns (uint256) _defaultZero,
  ) internal returns (uint256) {
...
}

Then this is used as follows in BinaryIMT

import {SNARK_SCALAR_FIELD} from "./Constants.sol";
library BinaryIMT {
 // This is poseidonT3 from: https://github.com/chancehudson/poseidon-solidity
 address internal constant hasher = 0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93;
 
 uint256 internal constant Z_0 = 0;
 ...
 
function _defaultZero(uint256 index) internal pure returns (uint256) {
      if (index == 0) return Z_0;
      ...
      revert WrongDefaultZeroIndex();
  }

  function insert(BinaryIMTData storage self, uint256 leaf) public returns (uint256) {
      return InternalBinaryIMT._insert(self, leaf, hasher, SNARK_SCALAR_FIELD, _defaultZero);
  }
...
}

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.

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

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.

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.

internalBinaryIMT can use different hash functions by passing in a hash function as a parameter to
every function that uses the hash function. BinaryIMT does this for poseidonT3.
renamed _hash to hasher for a more consistent naming
renamed _hash to hasher
InternalQuinaryIMT can use different hash functions by passing in a hash function as a parameter to
…er field checks

SNARK_SCALAR_FIELD checks to outside of the internal library to allow non snark based
…se the hasher was pure

fixed non solidity hash functions not being by making the hash function view instead of pure. This
allows use cases where the developer needs to call a contract with .staticcall() directly to do a
hash
initial measuremnt gas so we can see what changes after each commit
…ction

InternalBinaryIMT can now be used with different hash functions given that the hash functions
contract has the expected interface

BREAKING CHANGE:  InternalBinaryIMTs functions now needs additional parameters
quinary IMT can now be used with different hash functions given that the hash functions contract has
the expected interface

BREAKING CHANGE: InternalQuinaryIMTs functions now require additional parameters
Removed poseidon from the quinary and BinaryIMT and made the deploy:imt-test task deploy poseidon
through nicks methond instead of a linked library

BREAKING CHANGE: gas reports no longer include poseidon hashes
…ent hash functions

moved to a new approach using an address to support different hash functions
…terThanHasherLimit

renamed ValueGreaterThanSnarkScalarField to ValueGreaterThanHasherLimit since not every hash
function is snark based

BREAKING CHANGE: breaks error handeling that expects ValueGreaterThanSnarkScalarField
switched to passing a hasher as a function for better flexibility. This did add some gas 500~2k for
inserts. I made the switch mainly so using native hash functions like keccak dont need a extra
contract.
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