Solidity – JavaScript Merkle Tree Verification Issues

solidity

The problem is that the calculated rootMerkle in solidity doesn't match the one in javascript and i can't figure out why.

My javascript code is the next. It can be seen that it verifies good here.

const MerkleTree = require('merkletreejs')
const keccak256 = require('keccak256')

const buf2hex = x => '0x'+x.toString('hex')

const leaves = ['0x00000a86986e8ba3557992df02883e4a646e8f25 50000000000000000000', '0x00009c99bffc538de01866f74cfec4819dc467f3 75000000000000000000', '0x00035a5f2c595c3bb53aae4528038dd7a85641c3 50000000000000000000', '0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000'].map(x => keccak256(x)).sort(Buffer.compare) 
const tree = new MerkleTree(leaves, keccak256)
const root = tree.getRoot()
const hexroot = buf2hex(root)
const leaf = keccak256('0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000')
const hexleaf = buf2hex(keccak256('0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000'))
const proof = tree.getProof(keccak256('0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000'))
const hexproof = tree.getProof(keccak256('0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000')).map(x => b$
console.log(hexroot)
console.log(hexleaf)
console.log(hexproof)

const verified = tree.verify(proof, leaf, root)
console.log(verified)

and the console output

0x0c8879b51283eb6a63e3ab339ae7771cdf410721556a300c0bb3a444aa2f8da5
0xdbd6fa463060a653969989f334f15d9f98575e52c3deeadac6d6abc269ae37a4
[ '0xec80acebb7b19acb852d258701fab8084200f069fca87127db146d694018a3a2',
'0xc25c23aa0f6518c0a443affc45a63232b544e8a070ccdf2f2947f28688027f25' ]
true

The part of the solidity code verifying the merkle tree is the next:

function checkProof(bytes32[] proof, bytes32 hash) view internal returns (bool) {
    bytes32 el;
    bytes32 h = hash;

    for (uint i = 0; i <= proof.length - 1; i += 1) {
        el = proof[i];

        if (h < el) {
            h = keccak256(h, el);
        } else {
            h = keccak256(el, h);
        }
    }

    return h == merkleRoot;
}

The problem is that h (the calculated root with proof and leaf) and merkleRoot (supplied root) does not match. Can you see where is the problem? Thank you very much.

Best Answer

Something is wrong with the ordering. What you can do is pass the positions of the proof as an additional argument to the smart contract to guarantee that the ordering is correct:

    const leaves = ['0x00000a86986e8ba3557992df02883e4a646e8f25 50000000000000000000', '0x00009c99bffc538de01866f74cfec4819dc467f3 75000000000000000000', '0x00035a5f2c595c3bb53aae4528038dd7a85641c3 50000000000000000000', '0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000'].map(x => keccak256(x))
    const tree = new MerkleTree(leaves, keccak256)
    const root = tree.getRoot()
    const hexroot = buf2hex(root)
    const leaf = keccak256('0x1e27c325ba246f581a6dcaa912a8e80163454c75 10000000000000000000')
    const hexleaf = buf2hex(leaf)
    const proof = tree.getProof(leaf)
    const hexproof = tree.getProof(leaf).map(x => buf2hex(x.data))
    const positions = tree.getProof(leaf).map(x => x.position === 'right' ? 1 : 0)

    console.log(hexroot)
    console.log(hexleaf)
    console.log(hexproof)
    console.log(positions)

    const verified = await contract.verify.call(hexroot, hexleaf, hexproof, positions)
    assert.equal(verified, true)

    assert.equal(tree.verify(proof, leaf, root), true)

MerkleProof.sol

pragma solidity ^0.4.23;

contract MerkleProof {
  function verify(
    bytes32 root,
    bytes32 leaf,
    bytes32[] proof,
    uint256[] positions
  )
    public
    pure
    returns (bool)
  {
    bytes32 computedHash = leaf;

    for (uint256 i = 0; i < proof.length; i++) {
      bytes32 proofElement = proof[i];

      if (positions[i] == 1) {
        computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
      } else {
        computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
      }
    }

    return computedHash == root;
  }
}

truffle test output

0x0c8879b51283eb6a63e3ab339ae7771cdf410721556a300c0bb3a444aa2f8da5
0xdbd6fa463060a653969989f334f15d9f98575e52c3deeadac6d6abc269ae37a4
[ '0xec80acebb7b19acb852d258701fab8084200f069fca87127db146d694018a3a2',
  '0xc25c23aa0f6518c0a443affc45a63232b544e8a070ccdf2f2947f28688027f25' ]
[ 1, 0 ]
        ✓ should return true for valid merkle proof22 (38ms)


  1 passing (95ms)
Related Topic