emit event during the weaver insertion

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-09-14 10:46:40 +03:00
parent 635853f170
commit b1e9be968e
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
8 changed files with 59 additions and 63 deletions

View File

@ -109,14 +109,12 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
return (publicKey, state.parity, state.session); return (publicKey, state.parity, state.session);
} }
function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) { function ghost(bytes32 receiver, uint256 amount) external override {
if (msg.sender != staking) revert NotStaking(); if (msg.sender != staking) revert NotStaking();
if (amount < EXISTENTIAL_DEPOSIT) revert NonExistentAmount(); if (amount < EXISTENTIAL_DEPOSIT) revert NonExistentAmount();
IStorageHistory(storageHistory).increaseBridgeIn(amount); IStorageHistory(storageHistory).increaseBridgeIn(amount);
_insertTreeNode(receiver, amount);
emit Ghosted(receiver, amount);
return _insertTreeNode(receiver, amount);
} }
function verify( function verify(

View File

@ -14,7 +14,6 @@ interface IGatekeeper {
error InsufficientValue(); error InsufficientValue();
error ExecutionReverted(); error ExecutionReverted();
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
event Recalled(address indexed receiver, uint256 indexed amount); event Recalled(address indexed receiver, uint256 indexed amount);
event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity); event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity);
event VoluntaryVerification(address indexed sender, uint256 indexed gasSpent); event VoluntaryVerification(address indexed sender, uint256 indexed gasSpent);
@ -29,6 +28,6 @@ interface IGatekeeper {
function govern(uint256 session, uint256 payload, bytes calldata call) external returns (bytes calldata); function govern(uint256 session, uint256 payload, bytes calldata call) external returns (bytes calldata);
function recall(uint256 session, uint256 amount, uint256 payload) external; function recall(uint256 session, uint256 amount, uint256 payload) external;
function rotate(uint256 session, bytes32 publicKey, uint8 parity) external; function rotate(uint256 session, bytes32 publicKey, uint8 parity) external;
function ghost(bytes32 receiver, uint256 amount) external returns (uint256); function ghost(bytes32 receiver, uint256 amount) external;
function initialize(address previousAddress) external; function initialize(address previousAddress) external;
} }

View File

@ -2,6 +2,8 @@
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
interface IWeaver { interface IWeaver {
event ThreadInserted(uint256 index, uint256 session);
function currentWeavingSession() external view returns (uint256); function currentWeavingSession() external view returns (uint256);
function startWeavingSession() external view returns (uint256); function startWeavingSession() external view returns (uint256);
function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory); function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory);

View File

@ -14,8 +14,8 @@ contract WeaverMock is Weaver {
return _previousAddress; return _previousAddress;
} }
function insertTreeNode(bytes32 receiver, uint256 amount) external returns (uint256) { function insertTreeNode(bytes32 receiver, uint256 amount) external {
return _insertTreeNode(receiver, amount); _insertTreeNode(receiver, amount);
} }
function computeArgumentsHash( function computeArgumentsHash(

View File

@ -134,9 +134,10 @@ abstract contract Weaver is IWeaver {
return proof; return proof;
} }
function _insertTreeNode(bytes32 who, uint256 amount) internal returns (uint256 globalIndex) { function _insertTreeNode(bytes32 who, uint256 amount) internal {
uint256 session = currentWeavingSession; uint256 session = currentWeavingSession;
globalIndex = _filledEntries[session]; uint256 globalIndex = _filledEntries[session];
if (globalIndex >= ENTRIES) { if (globalIndex >= ENTRIES) {
unchecked { ++session; } unchecked { ++session; }
currentWeavingSession = session; currentWeavingSession = session;
@ -158,6 +159,8 @@ abstract contract Weaver is IWeaver {
_slotValues[session][slotIndex].push(preimage); _slotValues[session][slotIndex].push(preimage);
unchecked { ++_filledEntries[session]; } unchecked { ++_filledEntries[session]; }
emit ThreadInserted(globalIndex, session);
} }
function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) { function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) {

View File

@ -203,7 +203,7 @@ contract GatekeeperTest is Test {
Gatekeeper gatekeeper; Gatekeeper gatekeeper;
MockStaking staking; MockStaking staking;
event Ghosted(bytes32 indexed receiver, uint256 indexed amount); event ThreadInserted(uint256 index, uint256 session);
function setUp() public { function setUp() public {
vm.prank(ALICE, ALICE); vm.prank(ALICE, ALICE);
@ -247,7 +247,7 @@ contract GatekeeperTest is Test {
bytes32 receiver = bytes32(abi.encodePacked(ALICE)); bytes32 receiver = bytes32(abi.encodePacked(ALICE));
vm.expectEmit(true, true, true, false, address(gatekeeper)); vm.expectEmit(true, true, true, false, address(gatekeeper));
emit Ghosted(receiver, ghostAmount); emit ThreadInserted(0, 0);
vm.prank(ALICE); vm.prank(ALICE);
staking.runGhost(receiver, ghostAmount); staking.runGhost(receiver, ghostAmount);
} }

View File

@ -1,6 +1,6 @@
pragma solidity 0.8.20; pragma solidity 0.8.20;
import {Test} from "forge-std/Test.sol"; import {Test, Vm} from "forge-std/Test.sol";
import {Gatekeeper} from "../../src/Gatekeeper.sol"; import {Gatekeeper} from "../../src/Gatekeeper.sol";
import {Hashes} from "../../src/libraries/Hashes.sol"; import {Hashes} from "../../src/libraries/Hashes.sol";
@ -21,9 +21,9 @@ contract MockStaking {
governor = msg.sender; governor = msg.sender;
} }
function ghost(bytes32 receiver, uint256 amount) external returns (uint256) { function ghost(bytes32 receiver, uint256 amount) external {
require(msg.sender == governor); require(msg.sender == governor);
return gatekeeper.ghost(receiver, amount); gatekeeper.ghost(receiver, amount);
} }
function createNewGatekeeper() external { function createNewGatekeeper() external {
@ -127,25 +127,25 @@ contract GatekeeperWeaverTest is Test {
} }
function test_insertationWorksAsExpected() public { function test_insertationWorksAsExpected() public {
uint256 currentWeavingSession = gatekeeper.currentWeavingSession();
uint256 maxCount = gatekeeper.ENTRIES(); uint256 maxCount = gatekeeper.ENTRIES();
uint256 currentWeavingSession;
uint256 newWeavingSession;
uint256 globalIndex; uint256 globalIndex;
(bytes32[] memory whos, uint256[] memory amounts) = _prepareArrays(maxCount); (bytes32[] memory whos, uint256[] memory amounts) = _prepareArrays(maxCount);
vm.recordLogs();
for (uint256 i = 0; i < maxCount; i++) { for (uint256 i = 0; i < maxCount; i++) {
if (i % 5 == 0) { vm.roll(block.number + 1); } if (i % 5 == 0) { vm.roll(block.number + 1); }
globalIndex = _insertWithAssert(currentWeavingSession, amounts[i], whos[i]); (globalIndex, currentWeavingSession) = _insertWithAssert(amounts[i], whos[i]);
assertTrue(_verifyProof(globalIndex, currentWeavingSession, block.number, amounts[i], whos[i])); assertTrue(_verifyProof(globalIndex, currentWeavingSession, block.number, amounts[i], whos[i]));
} }
globalIndex = _insertWithAssert(currentWeavingSession, amounts[69], whos[69]); (globalIndex, newWeavingSession) = _insertWithAssert(amounts[69], whos[69]);
uint256 newSession = gatekeeper.currentWeavingSession(); assertEq(currentWeavingSession + 1, newWeavingSession);
assertEq(currentWeavingSession + 1, newSession);
vm.roll(block.number + 1337); vm.roll(block.number + 1337);
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69])); assertTrue(_verifyProof(globalIndex, newWeavingSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(0, currentWeavingSession, block.number, amounts[0], whos[0])); assertTrue(_verifyProof(0, currentWeavingSession, block.number, amounts[0], whos[0]));
assertTrue(_verifyProof(69, currentWeavingSession, block.number, amounts[69], whos[69])); assertTrue(_verifyProof(69, currentWeavingSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentWeavingSession, block.number, amounts[420], whos[420])); assertTrue(_verifyProof(420, currentWeavingSession, block.number, amounts[420], whos[420]));
@ -156,7 +156,7 @@ contract GatekeeperWeaverTest is Test {
assertTrue(_verifyProof(69, currentWeavingSession, 100, amounts[69], whos[69])); assertTrue(_verifyProof(69, currentWeavingSession, 100, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentWeavingSession, 100, amounts[420], whos[420])); assertTrue(_verifyProof(420, currentWeavingSession, 100, amounts[420], whos[420]));
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69])); assertFalse(_verifyProof(globalIndex, newWeavingSession, 100, amounts[69], whos[69]));
assertFalse(_verifyProof(1337, currentWeavingSession, 100, amounts[1337], whos[1337])); assertFalse(_verifyProof(1337, currentWeavingSession, 100, amounts[1337], whos[1337]));
assertFalse(_verifyProof(2047, currentWeavingSession, 100, amounts[2047], whos[2047])); assertFalse(_verifyProof(2047, currentWeavingSession, 100, amounts[2047], whos[2047]));
@ -174,7 +174,7 @@ contract GatekeeperWeaverTest is Test {
staking.ghost(whos[i], amounts[i]); staking.ghost(whos[i], amounts[i]);
} }
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69])); assertTrue(_verifyProof(globalIndex, newWeavingSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(0, currentWeavingSession, block.number, amounts[0], whos[0])); assertTrue(_verifyProof(0, currentWeavingSession, block.number, amounts[0], whos[0]));
assertTrue(_verifyProof(69, currentWeavingSession, block.number, amounts[69], whos[69])); assertTrue(_verifyProof(69, currentWeavingSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentWeavingSession, block.number, amounts[420], whos[420])); assertTrue(_verifyProof(420, currentWeavingSession, block.number, amounts[420], whos[420]));
@ -185,7 +185,7 @@ contract GatekeeperWeaverTest is Test {
assertTrue(_verifyProof(69, currentWeavingSession, 100, amounts[69], whos[69])); assertTrue(_verifyProof(69, currentWeavingSession, 100, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentWeavingSession, 100, amounts[420], whos[420])); assertTrue(_verifyProof(420, currentWeavingSession, 100, amounts[420], whos[420]));
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69])); assertFalse(_verifyProof(globalIndex, newWeavingSession, 100, amounts[69], whos[69]));
assertFalse(_verifyProof(1337, currentWeavingSession, 100, amounts[1337], whos[1337])); assertFalse(_verifyProof(1337, currentWeavingSession, 100, amounts[1337], whos[1337]));
assertFalse(_verifyProof(2047, currentWeavingSession, 100, amounts[2047], whos[2047])); assertFalse(_verifyProof(2047, currentWeavingSession, 100, amounts[2047], whos[2047]));
@ -212,41 +212,43 @@ contract GatekeeperWeaverTest is Test {
} }
function _insertWithAssert( function _insertWithAssert(
uint256 session,
uint256 amount, uint256 amount,
bytes32 who bytes32 who
) private returns (uint256 globalIndex) { ) private returns (uint256 globalIndex, uint256 emittedSession) {
uint256 targetSlot = gatekeeper.filledEntries(session); uint256 preSession = gatekeeper.currentWeavingSession();
bytes32 previousHash = gatekeeper.treeNodesLatest(session, targetSlot);
uint256 prevEntries = gatekeeper.filledEntries(session); uint256 targetSlot = gatekeeper.filledEntries(preSession);
uint160 prevLength = gatekeeper.slotLengths(session, targetSlot); bytes32 previousHash = gatekeeper.treeNodesLatest(preSession, targetSlot);
uint256 prevEntries = gatekeeper.filledEntries(preSession);
uint160 prevLength = gatekeeper.slotLengths(preSession, targetSlot);
vm.prank(ALICE); vm.prank(ALICE);
globalIndex = staking.ghost(who, amount); staking.ghost(who, amount);
(globalIndex, emittedSession) = _getInsertedDataFromLogs();
if (preSession != emittedSession) {
assertEq(preSession + 1, emittedSession);
assertEq(emittedSession, gatekeeper.currentWeavingSession());
if (session + 1 == gatekeeper.currentWeavingSession()) {
assertEq(prevLength, gatekeeper.DEPTH()); assertEq(prevLength, gatekeeper.DEPTH());
assertEq(gatekeeper.slotLengths(session + 1, 0), 1); assertEq(gatekeeper.slotLengths(emittedSession, 0), 1);
assertEq(prevEntries, gatekeeper.ENTRIES()); assertEq(prevEntries, gatekeeper.ENTRIES());
assertEq(gatekeeper.filledEntries(session + 1), 1); assertEq(gatekeeper.filledEntries(emittedSession), 1);
assertEq(session + 1, gatekeeper.currentWeavingSession());
session += 1;
previousHash = bytes32(0); previousHash = bytes32(0);
prevLength = gatekeeper.slotLengths(session, 0);
} else { } else {
assertEq(prevLength + 1, gatekeeper.slotLengths(session, targetSlot)); assertEq(prevLength + 1, gatekeeper.slotLengths(emittedSession, targetSlot));
assertEq(prevEntries + 1, gatekeeper.filledEntries(session)); assertEq(prevEntries + 1, gatekeeper.filledEntries(emittedSession));
} }
{ {
bytes32 preimage1 = gatekeeper.slotValues(session, globalIndex); bytes32 preimage1 = gatekeeper.slotValues(emittedSession, globalIndex);
bytes32 preimage2 = gatekeeper.computePreimage(globalIndex, amount, who); bytes32 preimage2 = gatekeeper.computePreimage(globalIndex, amount, who);
assertEq(preimage1, preimage2); assertEq(preimage1, preimage2);
} }
{ {
bytes32 nodeHash1 = gatekeeper.treeNodesLatest(session, globalIndex); bytes32 nodeHash1 = gatekeeper.treeNodesLatest(emittedSession, globalIndex);
bytes32 preimageHash = Hashes.efficientKeccak256( bytes32 preimageHash = Hashes.efficientKeccak256(
gatekeeper.computePreimage(globalIndex, amount, who) gatekeeper.computePreimage(globalIndex, amount, who)
); );
@ -268,4 +270,17 @@ contract GatekeeperWeaverTest is Test {
return (whos, amounts); return (whos, amounts);
} }
function _getInsertedDataFromLogs() private returns (uint256 index, uint256 session) {
Vm.Log[] memory entries = vm.getRecordedLogs();
bytes32 targetTopic = keccak256("ThreadInserted(uint256,uint256)");
for (uint256 i = entries.length; i > 0; i--) {
if (entries[i - 1].topics.length > 0 && entries[i - 1].topics[0] == targetTopic) {
(index, session) = abi.decode(entries[i - 1].data, (uint256, uint256));
return (index, session);
}
}
revert("Target event not found in logs");
}
} }

View File

@ -86,7 +86,6 @@ contract StakingTest is Test {
event DistributorSet(address distributor); event DistributorSet(address distributor);
event WarmupSet(uint256 warmup); event WarmupSet(uint256 warmup);
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
function setUp() public { function setUp() public {
vm.startPrank(INITIALIZER); vm.startPrank(INITIALIZER);
@ -640,23 +639,6 @@ contract StakingTest is Test {
assertEq(ghst.totalSupply(), 0); assertEq(ghst.totalSupply(), 0);
} }
function test_ghostTokensEmitsEvent() public {
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
uint256 aliceBalance = stnk.balanceOf(ALICE);
vm.startPrank(ALICE);
stnk.approve(address(staking), aliceBalance);
uint256 ghstBalance = staking.wrap(ALICE, aliceBalance);
vm.stopPrank();
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
vm.expectEmit(true, true, true, false, staking.gatekeeper());
emit Ghosted(receiver, ghstBalance);
vm.prank(ALICE);
staking.ghost(receiver, ghstBalance);
}
function test_breakoutLogicWorks() public { function test_breakoutLogicWorks() public {
uint256 initialIndex = staking.index(); uint256 initialIndex = staking.index();
bytes32 receiver = bytes32(abi.encodePacked(ALICE)); bytes32 receiver = bytes32(abi.encodePacked(ALICE));
@ -678,9 +660,6 @@ contract StakingTest is Test {
uint256 range = (payout * 3) / 100; uint256 range = (payout * 3) / 100;
requestedPayout = (pseudoRandom % range) + 1; requestedPayout = (pseudoRandom % range) + 1;
vm.expectEmit(true, true, true, false, staking.gatekeeper());
emit Ghosted(receiver, requestedPayout);
vm.prank(ALICE); vm.prank(ALICE);
staking.breakout(receiver, requestedPayout); staking.breakout(receiver, requestedPayout);
expectedPayout += requestedPayout; expectedPayout += requestedPayout;