Solidity

使用 javascript merkle tree 為solidity merkletree 驗證生成十六進制證明

  • May 3, 2022

這是我為可靠性驗證生成十六進制證明的 js 程式碼

const express = require('express')
const { MerkleTree } = require('merkletreejs');
const { bufferToHex } = require('ethereumjs-util');
const keccak256 = require('keccak256');
const url = require('url');
const cors = require("cors");
const app = express();

app.use(cors())

//1_000_000_000_000e18
//10000000000000000000000000000

let whitelist = [
   {addr: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", amount: 20000000000000000000000000}, //10_000_000_000e18
   {addr: "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", amount: 20000000000000000000000000},
]

const allLower = whitelist.map(item => item)
const leafNodes = allLower.map(item => keccak256(item.addr, item.amount))
console.log('leafNodes: ', leafNodes)
const merkleTree = new MerkleTree(leafNodes, keccak256, { sortPairs: true })

function getProof(addr, amount) {
   let index = allLower.findIndex(item => item.addr === addr && item.amount === amount);
   const hexProof = merkleTree.getHexProof(leafNodes[index])
   console.log('hexProof: ', hexProof, typeof hexProof)
   return hexProof
}

我的可靠性驗證函式需要 2 個輸入,它們是數量(uint256)和 merkleProof(字節32)。我不斷收到無效的 merkleProof 錯誤。我猜這應該是我使用 js keccak256 的方式是錯誤的,因為它應該只需要一個參數作為輸入,但我不確定。有人可以幫忙嗎

這是solidity merkleproof驗證碼

function claimTokens(uint256 amount, bytes32[] calldata merkleProof) public {
       bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
       bool valid = MerkleProof.verify(merkleProof, merkleRoot, leaf);
       require(valid, "valid proof required.");
       require(!claimed[msg.sender], "Tokens already claimed.");
       claimed[msg.sender] = true;
   
       emit Claim(msg.sender, amount);

       _transfer(address(this), msg.sender, amount);
   }

您的程式碼有幾個問題。例如,我將 Web3.js 用於一些實用功能,但您可以隨意使用最適合您的任何功能。我個人會使用 Web3.js 或 Ethers 中的 keccak256 實現,但我會保留你的。

首先,20000000000000000000000000對於 JavaScript 類型的值太大Number而無法安全地表示,因此您最終可能會因此而出現令人討厭的錯誤。在處理區塊鏈時盡可能使用字元串表示/大數字。然後,您必須確保您的輸入與將在solidity 方面進行散列的內容完全匹配。uint256編碼為 32 個字節。

其次,keccak256您正在使用的實現只需要一個參數,甚至不考慮其他參數,從而導致以下行為:

console.log(keccak256("0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"))
// <Buffer 59 31 b4 ed 56 ac e4 c4 6b 68 52 4c b5 bc bf 41 95 f1 bb aa cb e5 22 8f bd 09 05 46 c8 8d d2 29>

console.log(keccak256("0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", web3.eth.abi.encodeParameter("uint256", "20000000000000000000000000")))
// <Buffer 59 31 b4 ed 56 ac e4 c4 6b 68 52 4c b5 bc bf 41 95 f1 bb aa cb e5 22 8f bd 09 05 46 c8 8d d2 29>

因此,如果您想堅持該實現,則必須將它們作為單個參數提供。

有了這個實現:

const keccak256 = require("keccak256");
const { MerkleTree } = require("merkletreejs");
const Web3 = require("web3");

const web3 = new Web3();

let whitelist = [
 {
   addr: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
   // ABI encode the amount : 32 bytes integer as a hex string
   amount: web3.eth.abi.encodeParameter(
     "uint256",
     "20000000000000000000000000"
   ),
 },
 {
   addr: "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2",
   // ABI encode the amount : 32 bytes integer as a hex string
   amount: web3.eth.abi.encodeParameter(
     "uint256",
     "20000000000000000000000000"
   ),
 },
];

const allLower = whitelist.map((item) => item);

// For each element : concatenate the two hex buffers
// to a single one as this keccak256 implementation only
// expects one input
const leafNodes = allLower.map((item) =>
 keccak256(
   Buffer.concat([
     Buffer.from(item.addr.replace("0x", ""), "hex"),
     Buffer.from(item.amount.replace("0x", ""), "hex"),
   ])
 )
);

const merkleTree = new MerkleTree(leafNodes, keccak256, { sortPairs: true });

function getProof(addr, amount) {
 let index = allLower.findIndex(
   (item) => item.addr === addr && item.amount === amount
 );

 return merkleTree.getHexProof(leafNodes[index]);
}

// Provide amount as a 32 bytes hex string
const proof = getProof(
 "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
 web3.eth.abi.encodeParameter("uint256", "20000000000000000000000000")
);

console.log("Root : ", merkleTree.getHexRoot());
// Root :  0x399f97e5a37a31d24b746bb2d8fa5212fbe3c0f7ceb4b69ce3c9f37d0379ff72

console.log("Proof : ", proof);
// Proof :  ['0x15e7001d27767868de62fcb73e3d14ac4d28c60dda34b0f259e79440b8f27e36']

您可以在可靠性方面成功驗證。我使用了以下契約:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

contract Example {
 
 function verify(uint256 amount, bytes32[] memory proof) public view returns (bool) {
   bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
   bytes32 root = 0x399f97e5a37a31d24b746bb2d8fa5212fbe3c0f7ceb4b69ce3c9f37d0379ff72;
   return MerkleProof.verify(proof, root, leaf);
 }

}

我希望這能回答您的問題,並且我建議統一一切:使用單個庫(Web3 或 Ethers)的 abi 編碼器和 keccak256 實現來避免這些問題。

引用自:https://ethereum.stackexchange.com/questions/127471