TheBojda / zk-merkle-tree

JavaScript library for anonymous voting on Ethereum blockchain using zero-knowledge proof
MIT License
58 stars 13 forks source link

If a hashFn returns an odd length string, the buffer removes the last digit #7

Open Matan-Fridman opened 6 months ago

Matan-Fridman commented 6 months ago

My hash fn sometimes returns odd length strings and returns them as the hash of two leaves. Then behind the scenes this string probably gets turned to a buffer before it gets sent to the fn again. The problem is that a buffer from a hex string needs to be even in order to remain untouched. If its odd length it will remove the last digit. Is there a solution?

TheBojda commented 6 months ago

Could you paste some code here to see the problem better?

Matan-Fridman commented 6 months ago

Of courswe , thanks. Code: const tree = new MerkleTree(elems, treeHashFn,)

const treeHashFn = (values:Bufffer[]) =>{ const val = values.map((e)=>e.toString("hex")) // switch the 2 values into hex strings for the hashing const hash = mimc.F.toString(mimc.multiHash(val)) //hash [hex1, hex2] console.log("hash:", hash) return hash } // hash result: 14104875225146322184333458663331830026478753251653398840528918417862429045674

Now im assuming to calculate the next level of the tree it takes this result and changes into a buffer because in the next step this leaf gets sent to the hashFn: <Buffer 14 10 48 75 22 51 46 32 21 84 33 34 58 66 33 31 83 00 26 47 87 53 25 16 53 39 88 40 52 89 18 41 78 62 42 90 45 67>, as hex string: 1410487522514632218433345866333183002647875325165339884052891841786242904567 As you can see the last digit , 4, is not included in the buffer and so the next hash will be incorrect. Is there a way to bypass this or perhaps save the hashing results as string instead of buffers in the tree?

Thanks for the help and please let me know if you need more information.

process example with 4 leafs: leaf 1 and 2: values to hash: [ '6721386053596942657499136420107085293489819306492677961305769503065434130472', '9523846939162501634457677040696707166045357078299403910716393700565177591185' ] hash result: 14104875225146322184333458663331830026478753251653398840528918417862429045674

leaf 3 and 4: values to hash: [ '6590299499582611792156487232666119892727223198299517924489421909519493268587', '2109157874548174376358068484317679259521016273550960803979345000605185685917' ] hash result: 1510539597974527676667158189280148971132842972296509756333925416105675680238

NEXT LAYER: values to hash: [ '1410487522514632218433345866333183002647875325165339884052891841786242904567', '1510539597974527676667158189280148971132842972296509756333925416105675680238' ] As you can see the last digit is gone.

TheBojda commented 6 months ago

Maybe the problem is the finite field. In ZKP every number is represented as X mod P where P is a large prime. Are you using these numbers everywhere?

I'm not fully sure, but maybe you should use mimc.F.toString instead e.toString

Matan-Fridman commented 6 months ago

Thanks for the reply. I have tried doing that but what it does is change the buffer completely causing incorrect hashes and it still has the same problem. I'm pretty sure the issue is with the bufferifying of the string since this: Buffer.from("14104875225146322184333458663331830026478753251653398840528918417862429045674", "hex") results in: <Buffer 14 10 48 75 22 51 46 32 21 84 33 34 58 66 33 31 83 00 26 47 87 53 25 16 53 39 88 40 52 89 18 41 78 62 42 90 45 67>// the buffer removes the 4 in the end same as in the code. I think it's just a compatibility error

SOLUTION: It indeed was being turned into a buffer in bufferifyFn. I removed all of the return buffer.from in order to just return the hashed result and now it works. Now it returns the whole result as string. bufferifyFn(f) { return (value) => { const v = f(value); return v // if (buffer_1.Buffer.isBuffer(v)) { // return v; // } // if (this.isHexString(v)) { // return buffer_1.Buffer.from(v.replace('0x', ''), 'hex'); // } // if (typeof v === 'string') { // return buffer_1.Buffer.from(v); // } // if (typeof v === 'bigint') { // return buffer_1.Buffer.from(value.toString(16), 'hex'); // } // if (ArrayBuffer.isView(v)) { // return buffer_1.Buffer.from(v.buffer, v.byteOffset, v.byteLength); // } // // crypto-js support // return buffer_1.Buffer.from(f(crypto_js_1.default.enc.Hex.parse(value.toString('hex'))).toString(crypto_js_1.default.enc.Hex), 'hex'); }; }

Thanks for the help. Great package btw