txaty / go-merkletree

Go Merkle Tree. High performance, Supporting parallel run, OpenZeppelin sorting pairs.
https://pkg.go.dev/github.com/txaty/go-merkletree
MIT License
114 stars 18 forks source link

openzeppelin merkletree solidity verify? #38

Open duktig666 opened 9 months ago

duktig666 commented 9 months ago

solidity:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {MerkleProof} from "openzeppelin-contracts/utils/cryptography/MerkleProof.sol";

contract MerkleTreeUtil {
    function verifyMerkle(bytes32[] memory merkleProof, bytes32 _merkleRoot, address minterAddress)
        internal
        pure
        returns (bool)
    {
        return MerkleProof.verify(merkleProof, _merkleRoot, keccak256(abi.encode(minterAddress)));
    }
}

contract address:https://holesky.etherscan.io/address/0x2468f946031a903a1047bE72258c86d4fAA489c6#readContract

The following writing method cannot be validated on Solidity:

go:

import (
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/pkg/errors"
    "github.com/stretchr/testify/require"
    "nft-common/cryptor"
    "testing"

    mt "github.com/txaty/go-merkletree"
)

var leaves = []string{
    "0x6EFd53BA1f3DD573EAc80B82b4B6266F6dEFA4bD",
    "0x0F9C1bcC4BA8a85194ABe5B14565eB43CDEe2ed7",
    "0xf852007A9Cb2771a34DAF6fc5d2AD430a638beae",
    "0x2bF965771801faC320eBC067AF5aEC1a0AB62fA2",
    "0x560950f740916F1c6D0750EC1b7ad2bd98691BC7",
    "0x3535d10Fc0E85fDBC810bF828F02C9BcB7C2EBA8",
    "0x680A72cB90322537c664Dedd5F6A1423eD892F0F",
    "0xe583DC38863aB4b5A94da77A6628e2119eaD4B18",
    "0x892e7c8C5E716e17891ABf9395a0de1f2fc84786",
}

func testLeafHashFunc(address common.Address) ([]byte, error) {
    codeBytes, err := cryptor.AbiCoder([]string{"address"}, []interface{}{address})
    if err != nil {
        return nil, errors.Wrap(err, "AbiCoder")
    }
    hash := crypto.Keccak256Hash(codeBytes)
    return hash.Bytes(), nil
}

func TestLeafHash(t *testing.T) {
    for _, leaf := range leaves {
        hashBytes, err := testLeafHashFunc(common.HexToAddress(leaf))
        require.NoError(t, err)
        t.Log(common.BytesToHash(hashBytes).Hex())
    }
}

func LeafHashFunc(addressByte []byte) ([]byte, error) {
    address := common.BytesToAddress(addressByte)
    codeBytes, err := cryptor.AbiCoder([]string{"address"}, []interface{}{address})
    if err != nil {
        return nil, errors.Wrap(err, "AbiCoder")
    }
    hash := crypto.Keccak256Hash(codeBytes)
    return hash.Bytes(), nil
}

type Leave string

func (l Leave) Serialize() ([]byte, error) {
    return hexutil.Decode(string(l))
}

func TestOpenzeppelinSortMerkleTree(t *testing.T) {
    config := &mt.Config{
        HashFunc:           LeafHashFunc,
        NumRoutines:        0,
        Mode:               mt.ModeProofGenAndTreeBuild,
        RunInParallel:      false,
        SortSiblingPairs:   true,
        DisableLeafHashing: false,
    }

    dataBlocks := make([]mt.DataBlock, 0, len(leaves))
    for _, leaf := range leaves {
        dataBlocks = append(dataBlocks, Leave(leaf))
    }

    merkleTree, err := mt.New(config, dataBlocks)
    require.NoError(t, err)

    root := hexutil.Encode(merkleTree.Root)
    t.Log(root)

    proofs := merkleTree.Proofs
    t.Log(ProofsStrings(proofs))

    node := Leave("0x6EFd53BA1f3DD573EAc80B82b4B6266F6dEFA4bD")
    proof, err := merkleTree.Proof(node)
    require.NoError(t, err)
    t.Log(ProofStrings(proof))

    verify, err := merkleTree.Verify(node, proof)
    require.NoError(t, err)
    require.True(t, verify)
}

func ProofStrings(p *mt.Proof) []string {
    res := make([]string, 0)
    for _, sibling := range p.Siblings {
        proof := hexutil.Encode(sibling)
        res = append(res, proof)
    }
    return res
}

func ProofsStrings(p []*mt.Proof) [][]string {
    res := make([][]string, 0)
    for _, proof := range p {
        pp := make([]string, 0)
        for _, sibling := range proof.Siblings {
            pHex := hexutil.Encode(sibling)
            pp = append(pp, pHex)
        }
        res = append(res, pp)
    }
    return res
}
duktig666 commented 9 months ago

But I use TS's merkletreejs to implement it and it can be verified that it passes:

ts:

import {MerkleTree} from "merkletreejs";
import {keccak256, defaultAbiCoder} from "ethers/lib/utils";
import {ethers} from "ethers";

const SHA256 = require('crypto-js/sha256')

const arr0 = [
    "0x6EFd53BA1f3DD573EAc80B82b4B6266F6dEFA4bD",
    "0x0F9C1bcC4BA8a85194ABe5B14565eB43CDEe2ed7",
    "0xf852007A9Cb2771a34DAF6fc5d2AD430a638beae",
    "0x2bF965771801faC320eBC067AF5aEC1a0AB62fA2",
    "0x560950f740916F1c6D0750EC1b7ad2bd98691BC7",
    "0x3535d10Fc0E85fDBC810bF828F02C9BcB7C2EBA8",
    "0x680A72cB90322537c664Dedd5F6A1423eD892F0F",
    "0xe583DC38863aB4b5A94da77A6628e2119eaD4B18",
    "0x892e7c8C5E716e17891ABf9395a0de1f2fc84786",
];

// const arr1 = [
//     1, 2, 3, 4, 5
// ];
//
//
// const arr2 = [
//     "1",
//     "2",
//     "3",
//     "4",
//     "5",
// ];
//
// const arr3 = [
//     "dog", "cat", "mouse", "horse"
// ];

describe("merkle tree test", () => {

    // var arr = arr0.map(x => SHA256(x))
    let merkleTree = new MerkleTree(arr0, keccak256);
    const hexRoot = merkleTree.getHexRoot();

    it("merkle root", () => {
        console.log(hexRoot);
    });

    it("merkle verify", () => {
        const proof = merkleTree.getHexProof("0x892e7c8C5E716e17891ABf9395a0de1f2fc84786");

        // const verify = merkleTree.verify(proof, leaf, hexRoot);
        // expect(verify).toBe(true);
        console.log(proof);
    });
});

const createMerkleTree = (leaves: Buffer[]) =>
    new MerkleTree(leaves, keccak256, {
        hashLeaves: true,
        // sortLeaves: true,
        sortPairs: true,
    });

const toPaddedBuffer = (data: any) =>
    Buffer.from(
        ethers.BigNumber.from(data).toHexString().slice(2).padStart(64, "0"),
        "hex"
    );

const allowListElementsBuffer = (
    leaves: Array<[minter: string]>
) =>
    leaves.map(([minter]) =>
        Buffer.concat(
            [
                minter,
            ].map(toPaddedBuffer)
        )
    );

describe("opensea merkle tree test", () => {
    // Encode the minter address and mintParams.
    const elementsBuffer = allowListElementsBuffer([
        ["0x6EFd53BA1f3DD573EAc80B82b4B6266F6dEFA4bD"],
        ["0x0F9C1bcC4BA8a85194ABe5B14565eB43CDEe2ed7"],
        ["0xf852007A9Cb2771a34DAF6fc5d2AD430a638beae"],
        ["0x2bF965771801faC320eBC067AF5aEC1a0AB62fA2"],
        ["0x560950f740916F1c6D0750EC1b7ad2bd98691BC7"],
        ["0x3535d10Fc0E85fDBC810bF828F02C9BcB7C2EBA8"],
        ["0x680A72cB90322537c664Dedd5F6A1423eD892F0F"],
        ["0xe583DC38863aB4b5A94da77A6628e2119eaD4B18"],
        ["0x892e7c8C5E716e17891ABf9395a0de1f2fc84786"],
    ]);

    // Construct a merkle tree from the allow list elements.
    const merkleTree = createMerkleTree(elementsBuffer);

    // Store the merkle root.
    const root = merkleTree.getHexRoot();

    // const node = Buffer.concat(
    //     [
    //         "0x892e7c8C5E716e17891ABf9395a0de1f2fc84786",
    //     ].map(toPaddedBuffer)
    // )
    //
    // var node2 = Buffer.from(keccak256(elementsBuffer[0]));
    //
    // // Get the leaf at index 0.
    // const leafIndex = merkleTree.getLeafIndex(node2);
    const leaf = merkleTree.getLeaf(0);

    // Get the proof of the leaf to pass into the transaction.
    const proof = merkleTree.getHexProof(leaf);

    it("merkle log", () => {
        console.log(root);
        // console.log(leafIndex);
        console.log(leaf);
        console.log(leaf.toString());
        console.log(proof);
    });

    it("verify", () => {
        var verify = merkleTree.verify(proof, keccak256(elementsBuffer[0]), root)
        console.log(verify);
        console.log(keccak256(elementsBuffer[0]));
    });

    it("verify2", () => {
        const node = Buffer.concat(
            [
                "0x6EFd53BA1f3DD573EAc80B82b4B6266F6dEFA4bD",
            ].map(toPaddedBuffer)
        )

        console.log(keccak256(node));
    });

    it("proofs", () => {
        for (let i = 0; i < 9; i++) {
            console.log("proof", i);
            console.log(merkleTree.getHexProof(merkleTree.getLeaf(i)));
        }
    });
});
txaty commented 7 months ago

Hi~ Thanks for using this library and providing feedbacks.

Seems like there are some issues in LeafHashFunc().

Test TestOpenzeppelinSortMerkleTree can pass when changing only the hash function to the default one or replacing LeafHashFunc() with:

func LeafHashFunc(addressByte []byte) ([]byte, error) {
    hash := crypto.Keccak256Hash(addressByte)
    return hash.Bytes(), nil
}