# 技术解读：高效的链上动态 Merkle Tree

By [QuarkChain 中文](https://paragraph.com/@quarkchain) · 2022-01-26

---

技术

编按：本文是QuarkChain创始人&CEO周期博士在以太坊技术论坛ethresear.ch发布的一篇技术文章，介绍了一个高效的Merkle tree方案设计。

原文链接：

https://ethresear.ch/t/efficient-on-chain-dynamic-merkle-tree/11054

**简介**

遵循以太坊2.0的无状态客户端的思想，我们实现了一个高效的链上动态Merkle tree（默克尔树）：

*   链上包含性验证；
    
*   链上添加/就地更新；
    
*   O(1) 存储空间成本；
    
*   更新/添加操作的 O(1) 存储写入成本。
    

**背景**

Merkle tree广泛用于以极低存储成本在链上大量成员身份验证，例如 Uniswap 链上空投。无需上传链上所有用户大量的空投信息（比如，地址、数量），空投可以通过以下方式显著节省成本：

*   将树的根哈希存储在链上
    
*   使用链下计算证明用户奖励
    
*   用户通过链上提交证明来获取奖励
    

此外，链上动态 Merkle tree 正在引起人们的兴趣。著名的会计事务所安永（Ernst & Young ，EY) 开发了一种仅能在链上添加的动态 Merkle tree (https://github.com/EYBlockchain/timber 5)。它通过只存储“边界”节点而不是树的所有节点来节省树的存储成本，但是，添加操作的写入成本为 O(log2(N))，这可能会在 EVM 上消耗相当大的 gas。

**基本想法**

类似于现有的静态Merkle tree，它使用默克尔证明来验证包含性，链上动态树的基本思想是在包含验证后重用默克尔证明来更新树的根哈希。树更新的步骤如下：

1.  给定 LeafIndex、oldLeafHash、newLeafHash、oldRootHash、proof
    
2.  用 oldLeafHash 和 proof 计算 rootHash。如果计算出的rootHash != oldRoothHash，则包含验证失败；否则继续
    
3.  使用 newLeafHash 和 proof 计算 newRootHash，其中证明被重用，newRootHash 将是更新后树的根哈希
    

请注意，只有 newRootHash 被写入区块链，因此空间和写入的成本是 O(1)。

**应用**

Merklized ERC20
---------------

ERC20 标准可以修改为 Merklize （账户，余额）的树。任何造币/销毁/转移操作都需要 Merkle 证明。Merklized ERC20 的应用或许可以：

*   链上投票——治理提案投票可以廉价地使用 ERC20 快照并根据快照计算链上投票，而不需要保留 ERC20 余额变化（Compound）或链下快照的所有历史记录。
    
*   远程流动性挖掘——远程链上的合约对本地 ERC20 用户进行空投/流动性挖矿，其中 ERC20 快照通过去中心化预言机定期转发到另一条链。
    

示例代码可以在这里找到：

QuarkChain/DynamicMerkleTree/blob/abe6c7ee8f2fef105649943d5e329e5f5e697f8d/contracts/MerklizedERC20.sol

*   \\
    

> /SPDX-License-Identifier: MITpragma solidity ^0.8.0;

> import "hardhat/console.sol";

> import "@openzeppelin/contracts/token/ERC20/IERC20.sol";import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";import "@openzeppelin/contracts/utils/Context.sol";

> import "./DynamicMerkleTree.sol";

> contract MerklizedERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private \_balances;

> mapping(address => uint256) private \_indices1;

> uint256 private \_totalSupply;

> string private \_name; string private \_symbol;

---

*Originally published on [QuarkChain 中文](https://paragraph.com/@quarkchain/merkle-tree)*
