Merkle樹——驗證NFT白名單
文末有實戰(zhàn)教程,評論區(qū)留下你的實戰(zhàn)心得吧!
在我們今天所知道和喜愛的區(qū)塊鏈出現(xiàn)之前,默克爾樹一直是密碼學和計算機科學領(lǐng)域的一個方面。如今,我們開始慢慢看到它們在鏈上更頻繁地被用于數(shù)據(jù)驗證的目的。
1. 什么是默克爾樹?
默克爾樹是一種樹狀結(jié)構(gòu),樹上的每個節(jié)點都由一個值表示,這個值是一些加密哈希函數(shù)的結(jié)果。哈希函數(shù)是單向的,從一個輸入產(chǎn)生一個輸出很容易,但從一個輸出確定一個輸入在計算上是不可行的。默克爾樹有3種類型的節(jié)點,如下所示:
- 葉子節(jié)點 - 葉子節(jié)點位于樹的最底部,它們的值是原始數(shù)據(jù)的哈希值。一棵樹上有多少個葉子節(jié)點,就有多少個需要哈希的原始數(shù)據(jù)。例如,如果有7個數(shù)據(jù)需要被哈希,就會有7個葉子節(jié)點。
- 父節(jié)點 - 父節(jié)點可以位于樹的不同層次,這取決于整個樹的大小,父節(jié)點總是位于葉節(jié)點之上。父節(jié)點的值是由它下面的節(jié)點的哈希值決定的,通常從左到右開始。由于不同的輸入總是會產(chǎn)生不同的哈希值,不考慮哈希值的碰撞,節(jié)點哈希值的連接順序很重要。
- 根節(jié)點 - 根節(jié)點位于樹的頂端,由位于它下面的兩個父節(jié)點的哈希值連接而成,同樣從左到右開始。任何默克爾樹上都只有一個根節(jié)點,根節(jié)點擁有根哈希值。
2. 默克爾樹結(jié)構(gòu)
簡化它
3. 為什么需要默克爾樹?
3.1 背景
在NFT(ERC-721)的背景下使用Merkle樹,白名單為選定的參與者群體保留一定數(shù)量的代幣。
白名單地址將會預先計算成Merkle對象。
在這種情況下,可以讓一個葉子節(jié)點代表我們白名單中的一個錢包地址的哈希值。
3.2 痛點
前面提到過哈希函數(shù)是單向的——從一個輸入產(chǎn)生一個輸出很容易,但從一個輸出確定一個輸入在計算上是不可行的,并且連接順序也將決定結(jié)果。
Example:
hash(hash1,hash2) != hash(hash2,hash1)
在NFT白名單實例中,將會使用哈希值進行數(shù)據(jù)安全驗證。
因此在這種情況下,直系驗證是非常困難的,需要非常大的計算量和資源。
傳統(tǒng)驗證
驗證根哈希值是否相同,這看似很簡單,但問題就在于,當你去驗證一個地址時,到底將該地址哈希與哪個葉子哈希值進行替換并驗證?這是很難計算得知的。
根哈希驗證的另一個難點(了解即可)
3.3 如何解決——取得默克爾證明
Merkle樹妙處在于它根本不需要與根哈希,父哈希等等進行等量校對。
如果試圖驗證一個葉子節(jié)點屬于我們的樹,只需要知道直接相鄰的葉子節(jié)點哈希值(如果有的話),以及葉子節(jié)點正上方相鄰的父節(jié)點哈希值就可以了。
如果這個Merkle樹有四層,五層甚至一百層,那么返回的哈希值數(shù)量將會相應(yīng)增長!
4. 實戰(zhàn)
實戰(zhàn)將會帶你實例化默克爾樹對象以及取得默克爾樹驗證!
4.1 JavaScript實現(xiàn)
安裝
npm i -D merkletreejs keccak256
merkletree.js
const {MerkleTree} = require('merkletreejs')
const keccak256 = require('keccak256')
let whitelistAddresses = [
"0x262bCDeEf90181676BDC0a247A1954666F8a2815",
"0x262bCDeEf90181676BDC0a247A1954666F8a2816",
"0x262bCDeEf90181676BDC0a247A1954666F8a2817",
"0x262bCDeEf90181676BDC0a247A1954666F8a2818",
"0x262bCDeEf90181676BDC0a247A1954666F8a2819",
"0x262bCDeEf90181676BDC0a247A1954666F8a2820",
"0x262bCDeEf90181676BDC0a247A1954666F8a2821"
]
//buffer化葉子結(jié)點
const leafNodes = whitelistAddresses.map(addr => keccak256(addr))
//實例化默克爾樹
const merkleTree = new MerkleTree(leafNodes,keccak256,{sortPairs:true});
//獲取根哈希值
const rootHash = merkleTree.getRoot();
console.log('Whitelist Merkle Tree\n',merkleTree.toString())
//定義你所需要驗證的地址
const claimingAddress = leafNodes[0]
//取得默克爾樹證明
const hexProof = merkleTree.getHexProof(claimingAddress)
console.log(`Merkle Proof for Address is\n`,hexProof)
//當你傳入一個錯誤的白名單地址時
const errAddress = keccak256('0x98D9897e0F0389158978B384E6ecF3cf93153876');
//取得默克爾證明
const hexProof1 = merkleTree.getHexProof(errAddress)
//將會得到空數(shù)組!
console.log(`Merkle Proof for error Address is\n`,hexProof1)
測試結(jié)果
由于該默克爾樹是由七個葉子節(jié)點組成的,所以這是一個三層結(jié)構(gòu)的樹,因此默克爾證明將會取得三個哈希值作為憑證。當樹結(jié)構(gòu)為五層,六層… 默克爾證明的哈希值數(shù)量會對應(yīng)增加!
4.2 智能合約實現(xiàn)
Merkletree.sol
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.0;
//導入默克爾樹智能合約
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol";
contract Merkletree{
//該根哈希值需要你用到剛剛js生成的merkle樹的根哈希(記得加上0x)
bytes32 public merkleRoot = 0x3a6036ef5f6da50ea7f3dc72c7c83c1e6d5be6cded8ad495fb4b0bb870f1c093;
//記錄白名單是否被使用過
mapping(address => bool) public whitelistClaimed;
//使用白名單函數(shù)
function whitelistMint(bytes32[] calldata _merkleProof) public{
//要求白名單沒有被使用過
require(!whitelistClaimed[msg.sender],"Address has already claimed");
//初始化葉子哈希
bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
//將葉子哈希傳入merkle樹驗證,查看是否是白名單
require(MerkleProof.verify(_merkleProof, merkleRoot , leaf),"Invalid proof");
//是白名單,記錄該白名單使用過
whitelistClaimed[msg.sender] = true;
}
}
5. 總結(jié)
Merkle樹是區(qū)塊鏈中非常重要的數(shù)據(jù)結(jié)構(gòu),它的應(yīng)用極大地減小了哈希安全驗證的難度,提高了合約安全程度,減少了被攻擊成功的可能性。
感謝閱讀!文章來源:http://www.zghlxwxcb.cn/news/detail-855774.html
Blog by Science_jun文章來源地址http://www.zghlxwxcb.cn/news/detail-855774.html
到了這里,關(guān)于你真的了解區(qū)塊鏈嗎?揭秘白名單背后的技術(shù)原理(附白名單實戰(zhàn)教程)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!