diff --git a/src/Gatekeeper.sol b/src/Gatekeeper.sol index 3dc4bec..54de0e5 100644 --- a/src/Gatekeeper.sol +++ b/src/Gatekeeper.sol @@ -109,14 +109,12 @@ contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard { 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 (amount < EXISTENTIAL_DEPOSIT) revert NonExistentAmount(); IStorageHistory(storageHistory).increaseBridgeIn(amount); - - emit Ghosted(receiver, amount); - return _insertTreeNode(receiver, amount); + _insertTreeNode(receiver, amount); } function verify( diff --git a/src/interfaces/IGatekeeper.sol b/src/interfaces/IGatekeeper.sol index 8cfb6f6..839df8e 100644 --- a/src/interfaces/IGatekeeper.sol +++ b/src/interfaces/IGatekeeper.sol @@ -14,7 +14,6 @@ interface IGatekeeper { error InsufficientValue(); error ExecutionReverted(); - event Ghosted(bytes32 indexed receiver, uint256 indexed amount); event Recalled(address indexed receiver, uint256 indexed amount); event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity); 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 recall(uint256 session, uint256 amount, uint256 payload) 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; } diff --git a/src/interfaces/IWeaver.sol b/src/interfaces/IWeaver.sol index f6cd936..d7867cf 100644 --- a/src/interfaces/IWeaver.sol +++ b/src/interfaces/IWeaver.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.20; interface IWeaver { + event ThreadInserted(uint256 index, uint256 session); + function currentWeavingSession() external view returns (uint256); function startWeavingSession() external view returns (uint256); function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory); diff --git a/src/mocks/WeaverMock.sol b/src/mocks/WeaverMock.sol index 1583706..053ff75 100644 --- a/src/mocks/WeaverMock.sol +++ b/src/mocks/WeaverMock.sol @@ -14,8 +14,8 @@ contract WeaverMock is Weaver { return _previousAddress; } - function insertTreeNode(bytes32 receiver, uint256 amount) external returns (uint256) { - return _insertTreeNode(receiver, amount); + function insertTreeNode(bytes32 receiver, uint256 amount) external { + _insertTreeNode(receiver, amount); } function computeArgumentsHash( diff --git a/src/types/Weaver.sol b/src/types/Weaver.sol index 333436b..8bf06cb 100644 --- a/src/types/Weaver.sol +++ b/src/types/Weaver.sol @@ -134,9 +134,10 @@ abstract contract Weaver is IWeaver { return proof; } - function _insertTreeNode(bytes32 who, uint256 amount) internal returns (uint256 globalIndex) { + function _insertTreeNode(bytes32 who, uint256 amount) internal { uint256 session = currentWeavingSession; - globalIndex = _filledEntries[session]; + uint256 globalIndex = _filledEntries[session]; + if (globalIndex >= ENTRIES) { unchecked { ++session; } currentWeavingSession = session; @@ -158,6 +159,8 @@ abstract contract Weaver is IWeaver { _slotValues[session][slotIndex].push(preimage); unchecked { ++_filledEntries[session]; } + + emit ThreadInserted(globalIndex, session); } function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) { diff --git a/test/gatekeeper/Gatekeeper.t.sol b/test/gatekeeper/Gatekeeper.t.sol index efa83ec..b779e97 100644 --- a/test/gatekeeper/Gatekeeper.t.sol +++ b/test/gatekeeper/Gatekeeper.t.sol @@ -203,7 +203,7 @@ contract GatekeeperTest is Test { Gatekeeper gatekeeper; MockStaking staking; - event Ghosted(bytes32 indexed receiver, uint256 indexed amount); + event ThreadInserted(uint256 index, uint256 session); function setUp() public { vm.prank(ALICE, ALICE); @@ -247,7 +247,7 @@ contract GatekeeperTest is Test { bytes32 receiver = bytes32(abi.encodePacked(ALICE)); vm.expectEmit(true, true, true, false, address(gatekeeper)); - emit Ghosted(receiver, ghostAmount); + emit ThreadInserted(0, 0); vm.prank(ALICE); staking.runGhost(receiver, ghostAmount); } diff --git a/test/gatekeeper/GatekeeperWeaver.t.sol b/test/gatekeeper/GatekeeperWeaver.t.sol index e83c75f..279638c 100644 --- a/test/gatekeeper/GatekeeperWeaver.t.sol +++ b/test/gatekeeper/GatekeeperWeaver.t.sol @@ -1,6 +1,6 @@ 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 {Hashes} from "../../src/libraries/Hashes.sol"; @@ -21,9 +21,9 @@ contract MockStaking { governor = msg.sender; } - function ghost(bytes32 receiver, uint256 amount) external returns (uint256) { + function ghost(bytes32 receiver, uint256 amount) external { require(msg.sender == governor); - return gatekeeper.ghost(receiver, amount); + gatekeeper.ghost(receiver, amount); } function createNewGatekeeper() external { @@ -127,25 +127,25 @@ contract GatekeeperWeaverTest is Test { } function test_insertationWorksAsExpected() public { - uint256 currentWeavingSession = gatekeeper.currentWeavingSession(); uint256 maxCount = gatekeeper.ENTRIES(); + uint256 currentWeavingSession; + uint256 newWeavingSession; uint256 globalIndex; (bytes32[] memory whos, uint256[] memory amounts) = _prepareArrays(maxCount); + vm.recordLogs(); for (uint256 i = 0; i < maxCount; i++) { 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])); } - globalIndex = _insertWithAssert(currentWeavingSession, amounts[69], whos[69]); - uint256 newSession = gatekeeper.currentWeavingSession(); - - assertEq(currentWeavingSession + 1, newSession); + (globalIndex, newWeavingSession) = _insertWithAssert(amounts[69], whos[69]); + assertEq(currentWeavingSession + 1, newWeavingSession); 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(69, currentWeavingSession, block.number, amounts[69], whos[69])); 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(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(2047, currentWeavingSession, 100, amounts[2047], whos[2047])); @@ -174,7 +174,7 @@ contract GatekeeperWeaverTest is Test { 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(69, currentWeavingSession, block.number, amounts[69], whos[69])); 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(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(2047, currentWeavingSession, 100, amounts[2047], whos[2047])); @@ -212,41 +212,43 @@ contract GatekeeperWeaverTest is Test { } 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); + ) private returns (uint256 globalIndex, uint256 emittedSession) { + uint256 preSession = gatekeeper.currentWeavingSession(); + + uint256 targetSlot = gatekeeper.filledEntries(preSession); + bytes32 previousHash = gatekeeper.treeNodesLatest(preSession, targetSlot); + uint256 prevEntries = gatekeeper.filledEntries(preSession); + uint160 prevLength = gatekeeper.slotLengths(preSession, targetSlot); 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(gatekeeper.slotLengths(session + 1, 0), 1); + assertEq(gatekeeper.slotLengths(emittedSession, 0), 1); assertEq(prevEntries, gatekeeper.ENTRIES()); - assertEq(gatekeeper.filledEntries(session + 1), 1); - assertEq(session + 1, gatekeeper.currentWeavingSession()); + assertEq(gatekeeper.filledEntries(emittedSession), 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)); + assertEq(prevLength + 1, gatekeeper.slotLengths(emittedSession, targetSlot)); + 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); assertEq(preimage1, preimage2); } { - bytes32 nodeHash1 = gatekeeper.treeNodesLatest(session, globalIndex); + bytes32 nodeHash1 = gatekeeper.treeNodesLatest(emittedSession, globalIndex); bytes32 preimageHash = Hashes.efficientKeccak256( gatekeeper.computePreimage(globalIndex, amount, who) ); @@ -268,4 +270,17 @@ contract GatekeeperWeaverTest is Test { 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"); + } } diff --git a/test/staking/Staking.t.sol b/test/staking/Staking.t.sol index e3316d5..43d569b 100644 --- a/test/staking/Staking.t.sol +++ b/test/staking/Staking.t.sol @@ -86,7 +86,6 @@ contract StakingTest is Test { event DistributorSet(address distributor); event WarmupSet(uint256 warmup); - event Ghosted(bytes32 indexed receiver, uint256 indexed amount); function setUp() public { vm.startPrank(INITIALIZER); @@ -640,23 +639,6 @@ contract StakingTest is Test { 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 { uint256 initialIndex = staking.index(); bytes32 receiver = bytes32(abi.encodePacked(ALICE)); @@ -678,9 +660,6 @@ contract StakingTest is Test { uint256 range = (payout * 3) / 100; requestedPayout = (pseudoRandom % range) + 1; - vm.expectEmit(true, true, true, false, staking.gatekeeper()); - emit Ghosted(receiver, requestedPayout); - vm.prank(ALICE); staking.breakout(receiver, requestedPayout); expectedPayout += requestedPayout;