,Merkle Tree
also called Merkle tree or hash tree, is the underlying encryption technology of the blockchain and is widely used by the BTC and Ethereum blockchains. isMerkle Tree
a bottom-up encrypted tree, each leaf is the hash of the corresponding data, and each non-leaf is the hash of its 2
child nodes.
AllowsMerkle Tree
efficient and secure verification of the contents of large data structures (Merkle Proof
) . For N
a leaf node Merkle Tree
, in the case of knowing root
the root value, verifying whether a piece of data is valid (belonging to Merkle Tree
a leaf node) only requires log(N)
a piece of data (also called proof
), Very efficient. If the data is wrong, or the root is given proof
incorrectly , root
the root cannot be restored. In the following example, the leaves L1
of Merkle proof
are Hash 0-1
and are Hash 1
: Knowing these two values, you can verify whether the value L1
of is Merkle Tree
in the leaf of. why? Because L1
we Hash 0-0
, and we know Hash 0-1
, then andHash 0-0
can be jointly calculated , and then we know , and can be jointly calculated . , which is the hash of the root node.Hash 0-1
Hash 0
Hash 1
Hash 0
Hash 1
Top Hash
Merkle Tree
Generate
We can use webpages or Javascript library merkletreejs to generate Merkle Tree
.
Here we use a web page to generate 4
an address as a leaf node Merkle Tree
. Leaf node input:
[
"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
"0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2",
"0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db",
"0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB"
]
Select the Keccak-256
, hashLeaves
and sortPairs
options in the menu, and then click Compute
, Merkle Tree
and the is generated. expandsMerkle Tree
to:
└─ 根: eeefd63003e0e702cb41cd0043015a6e26ddb38073cc6ffeb0ba3e808ba8c097
├─ 9d997719c0a5b5f6db9b8ac69a988be57cf324cb9fffd51dc2c37544bb520d65
│ ├─ 叶子0:5931b4ed56ace4c46b68524cb5bcbf4195f1bbaacbe5228fbd090546c88dd229
│ └─ 叶子1:999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb
└─ 4726e4102af77216b09ccd94f40daa10531c87c4d60bba7f3b3faf5ff9f19b3c
├─ 叶子2:04a10bfd00977f54cc3450c9b25c9b3a502a089eba0097ba35fc33c4ea5fcb54
└─ 叶子3:dfbe3e504ac4e35541bebad4d0e7574668e16fefa26cd4172f93e18b59ce9486
Merkle Proof
Verify
Through the website, we can 地址0
get proof
as follows, which is the hash value of the blue node in Figure 2:
[
"0x999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb",
"0x4726e4102af77216b09ccd94f40daa10531c87c4d60bba7f3b3faf5ff9f19b3c"
]
We leverage MerkleProof
the library to verify:
library MerkleProof {
/**
* @dev 当通过`proof`和`leaf`重建出的`root`与给定的`root`相等时,返回`true`,数据有效。
* 在重建时,叶子节点对和元素对都是排序过的。
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Returns 通过Merkle树用`leaf`和`proof`计算出`root`. 当重建出的`root`和给定的`root`相同时,`proof`才是有效的。
* 在重建时,叶子节点对和元素对都是排序过的。
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
// Sorted Pair Hash
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? keccak256(abi.encodePacked(a, b)) : keccak256(abi.encodePacked(b, a));
}
}
The libraryMerkleProof
has three functions:
- Function
verify()
: useproof
the number to verifyleaf
whether it belongs to the root of the rootroot
,Merkle Tree
and if so, return ittrue
. It callsprocessProof()
the function. - Function
processProof()
: Calculated usingproof
and in turn . It calls the function.leaf
Merkle Tree
root
_hashPair()
- Function
_hashPair()
: Usekeccak256()
the function to calculate the hash of the two child nodes corresponding to the non-root node (after sorting).
我们将地址0
,root
和对应的proof
输入到verify()
函数,将返回ture
。因为地址0
在根为root
的Merkle Tree
中,且proof
正确。如果改变了其中任意一个值,都将返回false
。
利用Merkle Tree
发放NFT
白名单
一份拥有800个地址的白名单,更新一次所需的gas fee很容易超过1个ETH。而由于Merkle Tree
验证时,leaf
和proof
可以存在后端,链上仅需存储一个root
的值,非常节省gas
,项目方经常用它来发放白名单。很多ERC721
标准的NFT
和ERC20
标准代币的白名单/空投都是利用Merkle Tree
发出的,比如optimism
的空投。
这里,我们介绍如何利用MerkleTree
合约来发放NFT
白名单:
contract MerkleTree is ERC721 {
bytes32 immutable public root; // Merkle树的根
mapping(address => bool) public mintedAddress; // 记录已经mint的地址
// 构造函数,初始化NFT合集的名称、代号、Merkle树的根
constructor(string memory name, string memory symbol, bytes32 merkleroot)
ERC721(name, symbol)
{
root = merkleroot;
}
// 利用Merkle树验证地址并完成mint
function mint(address account, uint256 tokenId, bytes32[] calldata proof)
external
{
require(_verify(_leaf(account), proof), "Invalid merkle proof"); // Merkle检验通过
require(!mintedAddress[account], "Already minted!"); // 地址没有mint过
_mint(account, tokenId); // mint
mintedAddress[account] = true; // 记录mint过的地址
}
// 计算Merkle树叶子的哈希值
function _leaf(address account)
internal pure returns (bytes32)
{
return keccak256(abi.encodePacked(account));
}
// Merkle树验证,调用MerkleProof库的verify()函数
function _verify(bytes32 leaf, bytes32[] memory proof)
internal view returns (bool)
{
return MerkleProof.verify(proof, root, leaf);
}
}
MerkleTree
合约继承了ERC721
标准,并利用了MerkleProof
库。
状态变量
合约中共有两个状态变量:
-
root
存储了Merkle Tree
的根,部署合约的时候赋值。 -
mintedAddress
是一个mapping
,记录了已经mint
过的地址,某地址mint成功后进行赋值。
函数
合约中共有4个函数:
- 构造函数:初始化
NFT
的名称和代号,还有Merkle Tree
的root
。 -
mint()
函数:利用白名单铸造NFT
。参数为白名单地址account
,铸造的tokenId
,和proof
。首先验证address
是否在白名单中,验证通过则把序号为tokenId
的NFT
铸造给该地址,并将它记录到mintedAddress
。此过程中调用了_leaf()
和_verify()
函数。 -
_leaf()
函数:计算了Merkle Tree
的叶子地址的哈希。 -
_verify()
函数:调用了MerkleProof
库的verify()
函数,进行Merkle Tree
验证。
remix
验证
我们使用上面例子的4
个地址作为白名单并生成Merkle Tree
。我们部署MerkleTree
合约,3
个参数分别为:
name = "WTF MerkleTree"
symbol = "WTF"
merkleroot = 0xeeefd63003e0e702cb41cd0043015a6e26ddb38073cc6ffeb0ba3e808ba8c097
接下来运行mint
函数给地址0铸造NFT
,3
个参数分别为:
account = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
tokenId = 0
proof = [ "0x999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb", "0x4726e4102af77216b09ccd94f40daa10531c87c4d60bba7f3b3faf5ff9f19b3c" ]
我们可以用ownerOf
函数验证tokenId
为0的NFT
已经铸造给了地址0,合约运行成功!
此时,若再次调用mint函数,虽然该地址能够通过Merkle Proof
验证,但由于地址已经记录在mintedAddress
中,因此该交易会由于"Already minted!"
被中止。
总结
这一讲,我们介绍了Merkle Tree
的概念,如何生成简单的Merkle Tree
,如何利用智能合约验证Merkle Tree
,以及用它来发放NFT
白名单。
In actual use, complex data Merkle Tree
can be generated and managed using thejavascript
library , and only one root value needs to be stored on the chain, which is very economical . Many project parties choose to use to issue whitelists.merkletreejs
gas
Merkle Tree