第二步,需要生成参与 mint 地址的 Merkle 证明,假设使用 0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33 地址进行 mint 操作:
let leaf = keccak256('0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33');
let proof = tree.getHexProof(leaf);
console.log('Proof of 0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33: ', proof);
对应生成的证明为
Proof of 0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33: [
'0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4',
'0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b',
'0xb0d6f760008340e3f60414d84b305702faa6418f44f31de07b10e05bf369eb3b',
'0x4c880bf401add28c4e51270dfe16b28c3ca1b3d263ff7c5863fc8214b4046364'
]
同时我们将生成一个假的证明:
// another proof, for example
let anotherWhitelistAddresses = [
'0x169841AA3024cfa570024Eb7Dd6Bf5f774092088',
'0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33',
'0x0a290c8cE7C35c40F4F94070a9Ed592fC85c62B9',
'0x43Be076d3Cd709a38D2f83Cd032297a194196517',
];
let anotherLeafNodes = anotherWhitelistAddresses.map(address => keccak256(address));
let badTree = new MerkleTree(anotherLeafNodes, keccak256, { sortPairs: true });
let badLeaf = keccak256('0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33');
let badProof = badTree.getHexProof(badLeaf);
console.log('Bad proof of 0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33: ', badProof);
// Bad proof of 0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33: [
// '0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4',
// '0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b'
// ]
调用 mint ,传入非法 Merkle 证明:["0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4","0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b"] ,可以看到交易失败,显示 Fail with error 'Address does not exist in list ;
验证 0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33 地址对应 mint 状态是否为 false;
Merkle 树现在普遍用来做线上数据验证。这篇文章主要解释和实现使用 Merkle 树做 NFT 白名单验证。
使用 Merkle 树做 NFT 白名单验证,简单来说就是将所有的白名单钱包地址做为 Merkle 树的叶节点生成一棵 Merkle 树,在部署的NFT 合约中只存储 Merkle 树的 root hash,这样避免了在合约中存储所有白名单地址带来的高额 gas 费用。在 mint 时,前端生成钱包地址的 Merkle proof,调用合约进行验证即可。
一次验证过程前端和合约运行过程如图:
图片来自 [3]
Merkle 树
详情请参见:https://en.wikipedia.org/wiki/Merkle_tree
图片来自 [1]
比如,以水果单词作为叶节点,生成 Merkle 树的结构如下:
图片来自 [2]
合约实现
我们简单实现 Merkle 验证的过程,此合约包含以下功能:
setSaleMerkleRoot
isValidMerkleProof
mint
Merkle proof 证明生成
调用合约验证的 Merkle proof 需要在前端生成。生成过程需要用到
merkletreejs
和keccak256
两个库,前者用于创建 Merkle 树,后者用于生成哈希。第一步,生成白名单地址的 Merkle 树:
我们可以看到根哈希值为
0xc7ec7ffb250de2b95a1c690751b2826ec9d2999dd9f5c6f8816655b1590ca544
,这个值在调用合约函数setSaleMerkleRoot
时需要用到,会保存在合约中。生成的 Merkle 证明需要存储在页面中,也可以存在 IPFS 中,在使用时加载使用。第二步,需要生成参与 mint 地址的 Merkle 证明,假设使用
0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33
地址进行 mint 操作:对应生成的证明为
同时我们将生成一个假的证明:
验证过程
此过程将使用 Remix IDE 进行部署和测试:
使用 Remix 将合约部署到以太坊测试网
Rinkeby
中,得到合约地址为:0xb3E2409199855ea9676dc5CFc9DefFd4A1b93eFe
;调用
setSaleMerkleRoot
设置 Merkle 根哈希为0xc7ec7ffb250de2b95a1c690751b2826ec9d2999dd9f5c6f8816655b1590ca544
;调用
mint
,传入非法 Merkle 证明:["0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4","0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b"]
,可以看到交易失败,显示Fail with error 'Address does not exist in list
;验证
0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33
地址对应 mint 状态是否为 false;调用
mint
,传入合法的 Merkle 证明:["0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4","0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b","0xb0d6f760008340e3f60414d84b305702faa6418f44f31de07b10e05bf369eb3b","0x4c880bf401add28c4e51270dfe16b28c3ca1b3d263ff7c5863fc8214b4046364"]
,可以看到交易成功;验证
0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33
地址对应 mint 状态是否为true
。。
参考文章