基于Merkle Tree为NFT添加白名单功能(翻译)

1、介绍

在我们今天所知道和喜爱的区块链出现之前,Merkle Tree一直存在于密码学和计算机科学领域。如今,我们开始慢慢看到它们在链上更频繁地用于数据验证。在这篇文章中,我将解释Merkle Tree如何在NFT(ERC-721)背景下实现NFT的白名单的功能,以及它们如何提供保证NFT只能由特定的用户认领。

2、什么是Merkle Tree?

Merkle Tree是一种树状结构,树上的每个节点都由一个值表示,这个值是一些哈希函数的结果。哈希函数是单向的,这意味着从一个输入产生一个输出很容易,但从一个输出确定一个输入在计算上是不可行的。Merkle Tree有3种类型的节点,它们是:

a) 叶子节点 - 这些节点位于树的最底部,它们的值是原始数据根据指定的哈希函数进行哈希处理的结果。一棵树上有多少个叶子节点,就有多少个需要哈希的原始数据。例如,如果有7个数据需要被哈希,就会有7个叶子节点。

b) 父节点 - 父节点可以位于树的不同层次,这取决于整个树的大小,但总是位于叶节点之上。父节点的值是由它下面的节点的哈希值决定的,通常从左到右开始。由于不同的输入总是会产生不同的哈希值,不考虑哈希值的碰撞,生成节点哈希值的连接顺序很重要。值得一提的是,根据树的大小,父节点可以由下层的父节点生成。

c) 根节点 - 根节点位于树的顶端,由位于它下面的两个父节点的哈希值连接而成,同样从左到右开始。任何Merkle树上都只有一个根节点,根节点拥有根哈希值。

我知道上面这些描述很复杂,所以请参考下面的图表(图1),以便更好地直观了解这些树的结构。

3、背景介绍

如前所述,在NFT(ERC-721)背景下使用的Merkle Tree,在为选定的参与者群体保留一定数量的NFT的情况下,Merkle Tree本身就是一个白名单。Merkle Tree必须是预先计算的,因此使用某种形式的数据,每个成员都是不同的。在这种情况下,我们可以让一个叶子节点代表我们白名单中的一个钱包地址。 

让我们想象一下,你的项目已经实施了白名单策略,为选定的钱包地址保留了任意数量的NFT,这些地址可能是通过竞争、抽奖或其他一些系统选择的。这些白名单上的地址已经被授予了在某个时间点,在公共铸币之前,出于各种原因,要求获得他们保留的NFT的能力。这些原因可能涉及到避免高额的gas费,奖励创造力,早期参与,社区参与,等等。

由于这些地址是已知的并且是固定的,我们可以使用这些信息来创建一个Merkle Tree。为了证明这一点,让我们使用merkletreejs和keccak256 JavaScript库。注意:为了简单起见,我将只使用7个钱包地址以保持树的大小简洁。

4、JavaScript实现

我们要做的第一件事是推导出我们的叶子结点。如果你记得,在一棵树上位于叶子节点正上方的每个父节点最多只能由两个叶子节点生成。如果叶子节点的数量不平均,那么父节点将单独由一个叶子节点生成。每个叶子节点应该是某种形式的哈希数据,所以在这个例子中,让我们使用keccak256库来哈希我们白名单上的所有地址。我们使用这种特定的哈希算法,因为它将在以后的Solidity智能合约中使用。

一旦我们对白名单上的所有地址进行了哈希,我们就获得了叶子节点,我们现在就可以创建Merkle Tree对象。我们使用merkletreejs库,通过调用新的MerkleTree()函数,将我们的叶子节点作为第一个参数,我们的哈希算法作为第二个参数,{ sortPairs: true }选项作为最后一个参数。最后一个参数是可选的。

现在我们已经得出了一个完整的Merkle Tree,我们可以通过调用Merkle Tree对象的getRoot()方法来获得根哈希值。记住,Merkle Tree的根哈希值是树上根节点正下方的两个父节点的哈希值。在这种情况下,0xf352...和0x3cc0.... 使用toString()方法在控制台记录我们的Merkle Tree,为我们提供了一个很好的可视化的树的结构方式。 

Merkle Tree的巧妙之处在于,它不需要对原始数据块有任何了解就能验证一个节点是否属于我们的树。如果我们试图验证一个叶子节点属于我们的树,只需要知道直接相邻的叶子节点哈希值(如果有的话),以及叶子节点正上方相邻的父节点哈希值就可以了。关于这个工作原理的简短而温馨的解释,我推荐你看看Tara Vancil的这个视频。该信息被称为证明,并将在我们的Solidity智能合约中使用,以验证调用者是我们的白名单中的一员。

5、web端实现

现在我们有了Merkle Tree对象和它的根哈希值,我们准备开始考虑如何在白名单用户试图索取他们的NFT时向我们的智能合约提供Merkle证明。真正需要做的是在我们的项目网站上实现一些JavaScript,类似于上面的内容,在页面上向外部API发出一个获取请求。这个API将接收连接的钱包地址,因为这是我们最初用来生成叶子节点的东西,并返回指定的证明。

在服务器端,你会收到地址,使用keccak256进行哈希,并使用Merkle Tree对象的getHexProof()方法获取证明。下面的图片显示了你可能从这个API调用中返回的一个例子。

 在收到这个证明并作为参数与参与者的交易一起发送后,我们现在可以开始研究如何在我们的智能合约中验证它。

6、智能合约实现

注意:所显示的智能合约例子是用最小的代码量构建的,以显示一个概念证明。它绝不是一个你应该如何编写NFT功能的例子。

为了验证所提供的证明,我们必须做的第一件事是导入OpenZeppelin MerkleProof.sol合约(第6行,图5),这将使我们能够在我们的智能合约代码中使用MerkleProof.verify()函数。接下来需要做的是定义根Merkle哈希值。如果智能合约在白名单确定之前就已经部署到Ethereum主网上,那么就可以假设有一些设置函数可以用来在以后的时间点上更新这个值。在这个例子中,我对根Merkle哈希值进行了硬编码,以便它在部署时被设置(第12行,图5)。

接下来,我们需要验证证明。回顾一下,证明是与交易一起发送的,是一个bytes32类型的值的数组。技术上来说,它们是字符串类型的,但无论如何,Solidity 都会正确地解释这些。我们生成我们的目标叶子节点(第25行,图5),如果你记得,这是一个白名单地址的keccak256哈希。在这个例子中,我们通过哈希msg.sender的值来生成我们的目标叶节点。记住,这个值是不可改变的,不能被恶意改变。 

由于只有白名单地址被用来生成我们的叶子节点,我们已经可以假设,如果一个非白名单地址试图使用有效或无效的证明来调用这个函数,生成的目标叶子节点将根本不存在于我们的Merkle Tree上,验证将失败。这个实现的最后一步只是调用MerkleProof.verify()函数,将提供的证明作为第一个参数,将根Merkle哈希作为第二个参数,将目标叶节点作为最后一个参数。如果这个函数返回false,require语句将失败,交易将被简单地恢复,否则,该函数将继续执行,NFT将被铸造。

原文:https://medium.com/@ItsCuzzo/using-merkle-trees-for-nft-whitelists-523b58ada3f9

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值