Solidity实现默克尔树 Merkle Tree
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
库有三个函数:
-
verify()
函数:利用proof
数来验证leaf
是否属于根为root
的Merkle Tree
中,如果是,则返回true
。它调用了processProof()
函数。 -
processProof()
函数:利用proof
和leaf
依次计算出Merkle Tree
的root
。它调用了_hashPair()
函数。 -
_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相关推荐
- 默克尔树 Merkle Tree
基础概述 默克尔树(Merkle Tree),可以被用于验证任何类型的数据的存储.通常被用作与其他节点的计算机之间进行数据转移的数据完整性以及正确性的校验. 在比特币中.每个区块都有自己的 b ...
- golang 区块链:默克尔树(Merkle Tree)
生成默克尔树 type MerkleTree struct {RootNode *MerkleNode }type MerkleNode struct {Left *MerkleNodeRight * ...
- 默克尔树 Merkle树之Go语言实现
关于merkle树有很多实现.这里参考了一个纯内存实现,有助于理解. 项目地址 https://github.com/cbergoon/merkletree 核心分析 一.结构体 type Conte ...
- 常用的数据结构_三分钟了解区块链常用数据结构「默克尔树」
免责声明:本文旨在传递更多市场信息,不构成任何投资建议.文章仅代表作者观点,不代表火星财经官方立场. 小编:记得关注哦 来源:万向区块链 原文标题:三分钟了解区块链常用数据结构「默克尔树」 默克尔树是 ...
- 默克尔树_默克尔树:正在使用中
默克尔树 Ralph C. Merkle (not pictured above), born 1952, is one of the founding fathers of Public Key C ...
- 稀疏默克尔树(Sparse Merkle Tree)
1. 默克尔树 1.1. 默克尔树的存在证明 1.2. 默克尔树的不存在证明 2. 稀疏默克尔树 2.1. 稀疏默克尔树的存在证明 2.2. 稀疏默克尔树的不存在证明 3. Ref 博客链接:http ...
- Merkle Tree(默克尔树)原理解析
Merkle Tree(默克尔树)原理解析 一.Merkle Tree 1.1 Merkle Tree的特点 二.Hash list 三.Merkle tree VS Hash list 四.Merk ...
- 默克尔树(Merkle Tree)总结
目录 为什么要有默克尔树 简介 Merkle Tree的特点 图解 创建树 检索-文件夹比较 检索-防伪 更新 插入删除 应用 数字签名 P2P网络 可信计算 区块链-简单验证支付 为什么要有默克尔树 ...
- 区块链--默克尔树(Merkle Tree)
Merkle Tree 默克尔树是一种二叉树,由一个根节点.一组中间节点和一些叶子节点组成.形状如下: D0.D1.D2和D3是叶子节点包含的数据,也就是叶子节点的value.继续往上看,N0.N1. ...
最新文章
- 51js 的json编辑器
- Nginx Http认证 实现访问网站或目录密码认证保护
- solr 4.8 mysql_solr 4.8+mysql數據庫數據導入 + mmseg4j中文全文索引 配置筆記
- 开发中使用UEditor编辑器的注意事项
- ccombox获取选择的文本_PC端最好用的翻译软件,支持引擎切换、文本朗读
- python 钉钉机器人发送图片_Python结合钉钉实时自动监控股票行情,上班炒股再也不怕老板发现...
- 在Ant的javac中指定源文件编码方式,以避免警告: 编码 GBK 的不可映射字符的错误...
- [react] super()和super(props)有什么区别?
- Chrome 终究走上了 IE 6 的老路
- 【动态规划笔记】区间dp:括号匹配(删除字符和括号匹配)
- Haproxy+Nginx实现web负载均衡群集
- IE7、IE8、IE9、IE10后各版本的更新及差别
- U分布、T分布、z分位数
- 基于用户的协同过滤Movielens电影推荐系统简单实例
- Kyligence 首场商业产品认证培训走进硅谷,圆满结束
- 计算机系统最主要的弱点,计算机安全弱点及其对应关键技术研究
- android 卫星地图,推荐一款亲测好用,可显示卫星地图,高斯平面直角坐标和计算图幅编号等功能的安卓定位导航软件~...
- Hibernate之load和get的区别
- 【转载】SystemVerilog中子程序调用与参数传递(ref关键字)
- 把最新JAVA面试真题(阿里/京东/菜鸟)整理出来,却被自己菜哭了
热门文章
- Julia 将.jl脚本文件打包为可执行文件
- (秦路)七周成为数据分析师(第七周)——Python
- Java-Jdbc教程
- python列表最多有多少元素_Python中包含一百万个元素的列表会占用多少...
- Python学习记录_通过科赫曲线绘制出完整雪花
- 基于django开发在线考试系统——(二)model模型设计
- Linux-账号和权限管理
- 368所部委直属高校的分化重组
- mysql delphi5_Delphi XE2如何使用dbExpress連接MySQL 5.5
- Layuiadmin+Echarts全国地图数据分布