​Merkle Tree​​​,也叫默克尔树或哈希树,是区块链的底层加密技术,被BTC和Ethereum区块链广泛采用。​​Merkle Tree​​​是一种自下而上构建的加密树,每个叶子是对应数据的哈希,而每个非叶子为它的​​2​​个子节点的哈希。

​Merkle Tree​​允许对大型数据结构的内容进行有效和安全的验证(​​Merkle Proof​​)。对于有​​N​​个叶子结点的​​Merkle Tree​​,在已知​​root​​根值的情况下,验证某个数据是否有效(属于​​Merkle Tree​​叶子结点)只需要​​log(N)​​个数据(也叫​​proof​​),非常高效。如果数据有误,或者给的​​proof​​错误,则无法还原出​​root​​根植。 下面的例子中,叶子​​L1​​的​​Merkle proof​​为​​Hash 0-1​​和​​Hash 1​​:知道这两个值,就能验证​​L1​​的值是不是在​​Merkle Tree​​的叶子中。为什么呢? 因为通过叶子​​L1​​我们就可以算出​​Hash 0-0​​,我们又知道了​​Hash 0-1​​,那么​​Hash 0-0​​和​​Hash 0-1​​就可以联合算出​​Hash 0​​,然后我们又知道​​Hash 1​​,​​Hash 0​​和​​Hash 1​​就可以联合算出​​Top Hash​​,也就是root节点的hash。

生成​​Merkle Tree​

我们可以利用​​网页​​或者Javascript库​​merkletreejs​​来生成​​Merkle Tree​​。

这里我们用网页来生成​​4​​个地址作为叶子结点的​​Merkle Tree​​。叶子结点输入:

[    "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",     "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2",    "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db",    "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB"    ]

在菜单里选上​​Keccak-256​​, ​​hashLeaves​​和​​sortPairs​​选项,然后点击​​Compute​​,​​Merkle Tree​​就生成好了。​​Merkle Tree​​展开为:

└─ 根: eeefd63003e0e702cb41cd0043015a6e26ddb38073cc6ffeb0ba3e808ba8c097   ├─ 9d997719c0a5b5f6db9b8ac69a988be57cf324cb9fffd51dc2c37544bb520d65   │  ├─ 叶子0:5931b4ed56ace4c46b68524cb5bcbf4195f1bbaacbe5228fbd090546c88dd229   │  └─ 叶子1:999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb   └─ 4726e4102af77216b09ccd94f40daa10531c87c4d60bba7f3b3faf5ff9f19b3c      ├─ 叶子2:04a10bfd00977f54cc3450c9b25c9b3a502a089eba0097ba35fc33c4ea5fcb54      └─ 叶子3:dfbe3e504ac4e35541bebad4d0e7574668e16fefa26cd4172f93e18b59ce9486

​Merkle Proof​​验证

通过网站,我们可以得到​​地址0​​的​​proof​​如下,即图2中蓝色结点的哈希值:

[  "0x999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb",  "0x4726e4102af77216b09ccd94f40daa10531c87c4d60bba7f3b3faf5ff9f19b3c"]

我们利用​​MerkleProof​​库来验证:

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));    }}

​MerkleProof​​库有三个函数:

  1. ​verify()​​函数:利用proof数来验证leaf是否属于根为rootMerkle Tree中,如果是,则返回true。它调用了processProof()函数。
  2. ​processProof()​​函数:利用proofleaf依次计算出Merkle Treeroot。它调用了_hashPair()函数。
  3. ​_hashPair()​​函数:用keccak256()函数计算非根节点对应的两个子节点的哈希(排序后)。

我们将​​地址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 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4tokenId = 0proof = [   "0x999bf57501565dbd2fdcea36efa2b9aef8340a8901e3459f4a4c926275d36cdb",   "0x4726e4102af77216b09ccd94f40daa10531c87c4d60bba7f3b3faf5ff9f19b3c" ]

我们可以用​​ownerOf​​函数验证​​tokenId​​为0的​​NFT​​已经铸造给了地址0,合约运行成功!

此时,若再次调用mint函数,虽然该地址能够通过​​Merkle Proof​​验证,但由于地址已经记录在​​mintedAddress​​中,因此该交易会由于​​"Already minted!"​​被中止。

总结

这一讲,我们介绍了​​Merkle Tree​​的概念,如何生成简单的​​Merkle Tree​​,如何利用智能合约验证​​Merkle Tree​​,以及用它来发放​​NFT​​白名单。

在实际使用中,复杂的​​Merkle Tree​​可以利用​​javascript​​库​​merkletreejs​​来生成和管理,链上只需要存储一个根值,非常节省​​gas​​。很多项目方都选择利用​​Merkle Tree​​来发放白名单。

Solidity实现默克尔树 Merkle Tree相关推荐

  1. 默克尔树 Merkle Tree

    基础概述   默克尔树(Merkle Tree),可以被用于验证任何类型的数据的存储.通常被用作与其他节点的计算机之间进行数据转移的数据完整性以及正确性的校验.   在比特币中.每个区块都有自己的 b ...

  2. golang 区块链:默克尔树(Merkle Tree)

    生成默克尔树 type MerkleTree struct {RootNode *MerkleNode }type MerkleNode struct {Left *MerkleNodeRight * ...

  3. 默克尔树 Merkle树之Go语言实现

    关于merkle树有很多实现.这里参考了一个纯内存实现,有助于理解. 项目地址 https://github.com/cbergoon/merkletree 核心分析 一.结构体 type Conte ...

  4. 常用的数据结构_三分钟了解区块链常用数据结构「默克尔树」

    免责声明:本文旨在传递更多市场信息,不构成任何投资建议.文章仅代表作者观点,不代表火星财经官方立场. 小编:记得关注哦 来源:万向区块链 原文标题:三分钟了解区块链常用数据结构「默克尔树」 默克尔树是 ...

  5. 默克尔树_默克尔树:正在使用中

    默克尔树 Ralph C. Merkle (not pictured above), born 1952, is one of the founding fathers of Public Key C ...

  6. 稀疏默克尔树(Sparse Merkle Tree)

    1. 默克尔树 1.1. 默克尔树的存在证明 1.2. 默克尔树的不存在证明 2. 稀疏默克尔树 2.1. 稀疏默克尔树的存在证明 2.2. 稀疏默克尔树的不存在证明 3. Ref 博客链接:http ...

  7. Merkle Tree(默克尔树)原理解析

    Merkle Tree(默克尔树)原理解析 一.Merkle Tree 1.1 Merkle Tree的特点 二.Hash list 三.Merkle tree VS Hash list 四.Merk ...

  8. 默克尔树(Merkle Tree)总结

    目录 为什么要有默克尔树 简介 Merkle Tree的特点 图解 创建树 检索-文件夹比较 检索-防伪 更新 插入删除 应用 数字签名 P2P网络 可信计算 区块链-简单验证支付 为什么要有默克尔树 ...

  9. 区块链--默克尔树(Merkle Tree)

    Merkle Tree 默克尔树是一种二叉树,由一个根节点.一组中间节点和一些叶子节点组成.形状如下: D0.D1.D2和D3是叶子节点包含的数据,也就是叶子节点的value.继续往上看,N0.N1. ...

最新文章

  1. 51js 的json编辑器
  2. Nginx Http认证 实现访问网站或目录密码认证保护
  3. solr 4.8 mysql_solr 4.8+mysql數據庫數據導入 + mmseg4j中文全文索引 配置筆記
  4. 开发中使用UEditor编辑器的注意事项
  5. ccombox获取选择的文本_PC端最好用的翻译软件,支持引擎切换、文本朗读
  6. python 钉钉机器人发送图片_Python结合钉钉实时自动监控股票行情,上班炒股再也不怕老板发现...
  7. 在Ant的javac中指定源文件编码方式,以避免警告: 编码 GBK 的不可映射字符的错误...
  8. [react] super()和super(props)有什么区别?
  9. Chrome 终究走上了 IE 6 的老路
  10. 【动态规划笔记】区间dp:括号匹配(删除字符和括号匹配)
  11. Haproxy+Nginx实现web负载均衡群集
  12. IE7、IE8、IE9、IE10后各版本的更新及差别
  13. U分布、T分布、z分位数
  14. 基于用户的协同过滤Movielens电影推荐系统简单实例
  15. Kyligence 首场商业产品认证培训走进硅谷,圆满结束
  16. 计算机系统最主要的弱点,计算机安全弱点及其对应关键技术研究
  17. android 卫星地图,推荐一款亲测好用,可显示卫星地图,高斯平面直角坐标和计算图幅编号等功能的安卓定位导航软件~...
  18. Hibernate之load和get的区别
  19. 【转载】SystemVerilog中子程序调用与参数传递(ref关键字)
  20. 把最新JAVA面试真题(阿里/京东/菜鸟)整理出来,却被自己菜哭了

热门文章

  1. Julia 将.jl脚本文件打包为可执行文件
  2. (秦路)七周成为数据分析师(第七周)——Python
  3. Java-Jdbc教程
  4. python列表最多有多少元素_Python中包含一百万个元素的列表会占用多少...
  5. Python学习记录_通过科赫曲线绘制出完整雪花
  6. 基于django开发在线考试系统——(二)model模型设计
  7. Linux-账号和权限管理
  8. 368所部委直属高校的分化重组
  9. mysql delphi5_Delphi XE2如何使用dbExpress連接MySQL 5.5
  10. Layuiadmin+Echarts全国地图数据分布