123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- const { ethers } = require('hardhat');
- const { expect } = require('chai');
- const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
- const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
- const { StandardMerkleTree } = require('@openzeppelin/merkle-tree');
- const { generators } = require('../../helpers/random');
- const makeTree = (leafs = [ethers.ZeroHash]) =>
- StandardMerkleTree.of(
- leafs.map(leaf => [leaf]),
- ['bytes32'],
- { sortLeaves: false },
- );
- const hashLeaf = leaf => makeTree().leafHash([leaf]);
- const DEPTH = 4n; // 16 slots
- const ZERO = hashLeaf(ethers.ZeroHash);
- async function fixture() {
- const mock = await ethers.deployContract('MerkleTreeMock');
- await mock.setup(DEPTH, ZERO);
- return { mock };
- }
- describe('MerkleTree', function () {
- beforeEach(async function () {
- Object.assign(this, await loadFixture(fixture));
- });
- it('sets initial values at setup', async function () {
- const merkleTree = makeTree(Array.from({ length: 2 ** Number(DEPTH) }, () => ethers.ZeroHash));
- expect(await this.mock.root()).to.equal(merkleTree.root);
- expect(await this.mock.depth()).to.equal(DEPTH);
- expect(await this.mock.nextLeafIndex()).to.equal(0n);
- });
- describe('push', function () {
- it('tree is correctly updated', async function () {
- const leafs = Array.from({ length: 2 ** Number(DEPTH) }, () => ethers.ZeroHash);
- // for each leaf slot
- for (const i in leafs) {
- // generate random leaf and hash it
- const hashedLeaf = hashLeaf((leafs[i] = generators.bytes32()));
- // update leaf list and rebuild tree.
- const tree = makeTree(leafs);
- // push value to tree
- await expect(this.mock.push(hashedLeaf)).to.emit(this.mock, 'LeafInserted').withArgs(hashedLeaf, i, tree.root);
- // check tree
- expect(await this.mock.root()).to.equal(tree.root);
- expect(await this.mock.nextLeafIndex()).to.equal(BigInt(i) + 1n);
- }
- });
- it('revert when tree is full', async function () {
- await Promise.all(Array.from({ length: 2 ** Number(DEPTH) }).map(() => this.mock.push(ethers.ZeroHash)));
- await expect(this.mock.push(ethers.ZeroHash)).to.be.revertedWithPanic(PANIC_CODES.TOO_MUCH_MEMORY_ALLOCATED);
- });
- });
- it('reset', async function () {
- // empty tree
- const zeroLeafs = Array.from({ length: 2 ** Number(DEPTH) }, () => ethers.ZeroHash);
- const zeroTree = makeTree(zeroLeafs);
- // tree with one element
- const leafs = Array.from({ length: 2 ** Number(DEPTH) }, () => ethers.ZeroHash);
- const hashedLeaf = hashLeaf((leafs[0] = generators.bytes32())); // fill first leaf and hash it
- const tree = makeTree(leafs);
- // root should be that of a zero tree
- expect(await this.mock.root()).to.equal(zeroTree.root);
- expect(await this.mock.nextLeafIndex()).to.equal(0n);
- // push leaf and check root
- await expect(this.mock.push(hashedLeaf)).to.emit(this.mock, 'LeafInserted').withArgs(hashedLeaf, 0, tree.root);
- expect(await this.mock.root()).to.equal(tree.root);
- expect(await this.mock.nextLeafIndex()).to.equal(1n);
- // reset tree
- await this.mock.setup(DEPTH, ZERO);
- expect(await this.mock.root()).to.equal(zeroTree.root);
- expect(await this.mock.nextLeafIndex()).to.equal(0n);
- // re-push leaf and check root
- await expect(this.mock.push(hashedLeaf)).to.emit(this.mock, 'LeafInserted').withArgs(hashedLeaf, 0, tree.root);
- expect(await this.mock.root()).to.equal(tree.root);
- expect(await this.mock.nextLeafIndex()).to.equal(1n);
- });
- });
|