tests for the weaver logic

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-07-23 18:23:32 +03:00
parent f4801edf73
commit cb7c52f655
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
2 changed files with 214 additions and 0 deletions

View File

@ -2,6 +2,7 @@
src = "src"
out = "out"
libs = ["lib", "dependencies"]
gas_limit = "18446744073709551615"
optimizer = true
optimizer_runs = 10000
@ -18,6 +19,7 @@ gas_reports = [
"GhostBondingCalculator",
"GhostTreasury",
"GhostGovernorExposed",
"GatekeeperVerification",
]
remappings = [
"@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.0.2/",

View File

@ -0,0 +1,212 @@
pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol";
import {Gatekeeper} from "../../src/Gatekeeper.sol";
import {Hashes} from "../../src/libraries/Hashes.sol";
import {Checkpoints} from "../../src/libraries/Checkpoints.sol";
contract GatekeeperVerification is Gatekeeper {
using Checkpoints for Checkpoints.Trace256;
using Checkpoints for Checkpoints.Trace160;
constructor(
address staking,
uint256 ghostedSupply,
uint256 existential,
uint48 deployedAt,
uint104 amountIn,
uint104 amountOut
) Gatekeeper(
staking,
ghostedSupply,
existential,
deployedAt,
amountIn,
amountOut
) {}
function filledEntries(uint256 session) public view returns (uint256) {
return _filledEntries[session];
}
function slotLengths(uint256 session, uint256 globalIndex) public view returns (uint160) {
uint256 slotIndex = globalIndex % SLOTS;
return _slotLenghts[session][slotIndex].latest();
}
function slotValues(uint256 session, uint256 globalIndex) public view returns (bytes32) {
uint256 slotIndex = globalIndex % SLOTS;
uint256 valueIndex = globalIndex / SLOTS;
return _slotValues[session][slotIndex][valueIndex];
}
function treeNodesLatest(uint256 session, uint256 globalIndex) public view returns (bytes32) {
uint256 slotIndex = globalIndex % SLOTS;
return bytes32(_treeNodes[session][slotIndex].latest());
}
function computePreimage(uint256 globalIndex, uint256 a, bytes32 r) public pure returns (bytes32) {
uint256 valueIndex = globalIndex / SLOTS;
return _computeArgumentsHash(valueIndex, a, r);
}
function testVerify(
bytes32[] calldata proof,
bytes32[] calldata values,
uint256 globalIndex,
uint256 amount,
bytes32 who
) public pure returns (bytes32) {
if (proof.length != DEPTH) { return bytes32(0); }
if (values.length == 0) { return bytes32(0); }
uint256 slotIndex = globalIndex % SLOTS;
uint256 valueIndex = globalIndex / SLOTS;
if (valueIndex >= values.length) { return bytes32(0); }
bytes32 computedHash = _computeArgumentsHash(valueIndex, amount, who);
if (computedHash != values[valueIndex]) { return bytes32(0); }
computedHash = bytes32(0);
uint256 i;
for (; i < values.length;) {
bytes32 currHash = Hashes.efficientKeccak256(values[i]);
computedHash = Hashes.efficientKeccak256(computedHash, currHash);
unchecked { ++i; }
}
i = 0;
for (; i < proof.length; ) {
if (slotIndex % 2 == 0) {
computedHash = Hashes.efficientKeccak256(computedHash, proof[i]);
} else {
computedHash = Hashes.efficientKeccak256(proof[i], computedHash);
}
slotIndex >>= 1;
unchecked { ++i; }
}
return computedHash;
}
}
contract GatekeeperWeaverTest is Test {
address constant ALICE = 0x0000000000000000000000000000000000000001;
address constant BOB = 0x0000000000000000000000000000000000000002;
uint256 constant EXISTENTIAL = 1337;
uint256 constant AMOUNT = 1 * 1e7;
GatekeeperVerification gatekeeper;
function setUp() public {
gatekeeper = new GatekeeperVerification(ALICE, 0, EXISTENTIAL, 0, 0, 0);
}
function test_insertationWorksAsExpected() public {
uint256 currentSession = gatekeeper.currentSession();
uint256 maxCount = gatekeeper.ENTRIES();
uint256 globalIndex;
(bytes32[] memory whos, uint256[] memory amounts) = _prepareArrays(maxCount);
for (uint256 i = 0; i < maxCount; i++) {
if (i % 5 == 0) { vm.roll(block.number + 1); }
globalIndex = _insertWithAssert(currentSession, amounts[i], whos[i]);
assertTrue(_verifyProof(globalIndex, currentSession, block.number, amounts[i], whos[i]));
}
globalIndex = _insertWithAssert(currentSession, amounts[69], whos[69]);
uint256 newSession = gatekeeper.currentSession();
assertEq(currentSession + 1, newSession);
vm.roll(block.number + 1337);
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(0, currentSession, block.number, amounts[0], whos[0]));
assertTrue(_verifyProof(69, currentSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentSession, block.number, amounts[420], whos[420]));
assertTrue(_verifyProof(1337, currentSession, block.number, amounts[1337], whos[1337]));
assertTrue(_verifyProof(2047, currentSession, block.number, amounts[2047], whos[2047]));
assertTrue(_verifyProof(0, currentSession, 100, amounts[0], whos[0]));
assertTrue(_verifyProof(69, currentSession, 100, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentSession, 100, amounts[420], whos[420]));
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
assertFalse(_verifyProof(1337, currentSession, 100, amounts[1337], whos[1337]));
assertFalse(_verifyProof(2047, currentSession, 100, amounts[2047], whos[2047]));
}
function _verifyProof(
uint256 globalIndex,
uint256 session,
uint256 atBlock,
uint256 amount,
bytes32 who
) private view returns (bool) {
bytes32[] memory proof = gatekeeper.getProof(globalIndex, session, atBlock);
bytes32[] memory values = gatekeeper.getSlotValues(globalIndex, session, atBlock);
bytes32 root1 = gatekeeper.testVerify(proof, values, globalIndex, amount, who);
(bytes32 root2, uint256 entries) = gatekeeper.getRoot(session, atBlock);
assertTrue(entries <= gatekeeper.ENTRIES());
return root1 == root2;
}
function _insertWithAssert(
uint256 session,
uint256 amount,
bytes32 who
) private returns (uint256 globalIndex) {
uint256 targetSlot = gatekeeper.filledEntries(session);
bytes32 previousHash = gatekeeper.treeNodesLatest(session, targetSlot);
uint256 prevEntries = gatekeeper.filledEntries(session);
uint160 prevLength = gatekeeper.slotLengths(session, targetSlot);
vm.prank(ALICE);
globalIndex = gatekeeper.ghost(who, amount);
if (session + 1 == gatekeeper.currentSession()) {
assertEq(prevLength, gatekeeper.DEPTH());
assertEq(gatekeeper.slotLengths(session + 1, 0), 1);
assertEq(prevEntries, gatekeeper.ENTRIES());
assertEq(gatekeeper.filledEntries(session + 1), 1);
session += 1;
previousHash = bytes32(0);
prevLength = gatekeeper.slotLengths(session, 0);
} else {
assertEq(prevLength + 1, gatekeeper.slotLengths(session, targetSlot));
assertEq(prevEntries + 1, gatekeeper.filledEntries(session));
}
{
bytes32 preimage1 = gatekeeper.slotValues(session, globalIndex);
bytes32 preimage2 = gatekeeper.computePreimage(globalIndex, amount, who);
assertEq(preimage1, preimage2);
}
{
bytes32 nodeHash1 = gatekeeper.treeNodesLatest(session, globalIndex);
bytes32 preimageHash = Hashes.efficientKeccak256(
gatekeeper.computePreimage(globalIndex, amount, who)
);
bytes32 nodeHash2 = Hashes.efficientKeccak256(previousHash, preimageHash);
assertEq(nodeHash1, nodeHash2);
}
}
function _prepareArrays(uint256 count) private pure returns (bytes32[] memory, uint256[] memory) {
bytes32[] memory whos = new bytes32[](count);
uint256[] memory amounts = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
whos[i] = keccak256(abi.encodePacked("user", i));
amounts[i] = EXISTENTIAL + 1 + i;
}
return (whos, amounts);
}
}