diff --git a/.env.template b/.env.template index 9d2fbda..96e6968 100644 --- a/.env.template +++ b/.env.template @@ -104,18 +104,20 @@ GOVERNOR_VOTING_PERIOD= GOVERNOR_PROPOSAL_THRESHOLD= GOVERNOR_QUORUM_FRACTION= -###################### Initial ghosted supply on gatekeeper ########################## -## ghostedSupply - supply that is currently locked inside the gatekeeper ## -## existential - minimum amount that could be reflected inside other chain ## -## ghostedIn - historical supply that was bridged in (max value is 2^104) ## -## ghostedOut - historical supply that was bridged out (max value is 2^104) ## -## deployedAt - time when gatekeepers was deployed, could be inherited if needed ## -###################################################################################### +###################### Initial ghosted supply on gatekeeper ########################### +## ghostedSupply - supply that is currently locked inside the gatekeeper ## +## existential - minimum amount that could be reflected inside other chain ## +## ghostedIn - historical supply that was bridged in (max value is 2^104) ## +## ghostedOut - historical supply that was bridged out (max value is 2^104) ## +## deployedAt - time when gatekeepers was deployed, could be inherited if needed ## +## previousWeaver - previous weaver address if any to make linked list of weavers ## +####################################################################################### INITIAL_GHOSTED_SUPPLY= INITIAL_EXISTENTIAL_DEPOSIT= INITIAL_GHOSTED_IN= INITIAL_GHOSTED_OUT= GATEKEEPER_DEPLOYED_AT= +PREVIOUS_WEAVER_ADDRESS= SEPOLIA_TEST_RPC_URL= SEPOLIA_TEST_API_KEY= diff --git a/src/Gatekeeper.sol b/src/Gatekeeper.sol index e9f264e..9bf00ab 100644 --- a/src/Gatekeeper.sol +++ b/src/Gatekeeper.sol @@ -19,10 +19,11 @@ contract Gatekeeper is IGatekeeper, Metadata, Weaver { address _staking, uint256 _ghostedSupply, uint256 _existential, + address _previousWeaver, uint48 _deployedAt, uint104 _amountIn, uint104 _amountOut - ) Metadata(_deployedAt, _amountIn, _amountOut) Weaver() { + ) Metadata(_deployedAt, _amountIn, _amountOut) Weaver(_previousWeaver) { existentialDeposit = _existential; ghostedSupply = _ghostedSupply; staking = _staking; diff --git a/src/interfaces/IWeaver.sol b/src/interfaces/IWeaver.sol index b8f3600..8682fb7 100644 --- a/src/interfaces/IWeaver.sol +++ b/src/interfaces/IWeaver.sol @@ -2,7 +2,9 @@ pragma solidity ^0.8.20; interface IWeaver { - function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory); - function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory); - function getRoot(uint256 session, uint256 atBlock) external returns (bytes32, uint256); + function currentSession() external view returns (uint256); + function startSession() external view returns (uint256); + function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory); + function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory); + function getRoot(uint256 session, uint256 atBlock) external view returns (bytes32, uint256); } diff --git a/src/mocks/WeaverMock.sol b/src/mocks/WeaverMock.sol index f8aead1..b060569 100644 --- a/src/mocks/WeaverMock.sol +++ b/src/mocks/WeaverMock.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import {Weaver} from "../types/Weaver.sol"; contract WeaverMock is Weaver { - constructor() Weaver() {} + constructor(address previousWeaver) Weaver(previousWeaver) {} function insertTreeNode(bytes32 receiver, uint256 amount) external returns (uint256) { return _insertTreeNode(receiver, amount); diff --git a/src/types/Weaver.sol b/src/types/Weaver.sol index 8443d7f..61dbe63 100644 --- a/src/types/Weaver.sol +++ b/src/types/Weaver.sol @@ -14,20 +14,33 @@ abstract contract Weaver is IWeaver { uint256 public constant SLOTS = 2 ** DEPTH; uint256 public constant ENTRIES = DEPTH * SLOTS; - uint256 public currentSession; + uint256 public override currentSession; + uint256 public override startSession; + address public previousWeaver; mapping(uint256 => mapping(uint256 => Checkpoints.Trace256)) internal _treeNodes; mapping(uint256 => mapping(uint256 => Checkpoints.Trace160)) internal _slotLenghts; mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues; mapping(uint256 => uint256) internal _filledEntries; - constructor() {} + constructor(address _previousWeaver) { + previousWeaver = _previousWeaver; + if (_previousWeaver != address(0)) { + uint256 newSession = IWeaver(_previousWeaver).currentSession() + 1; + currentSession = newSession; + startSession = newSession; + } + } function getSlotValues( uint256 globalIndex, uint256 session, uint256 atBlock - ) external view returns (bytes32[] memory) { + ) external override view returns (bytes32[] memory) { + if (session < startSession) { + return IWeaver(previousWeaver).getSlotValues(globalIndex, session, atBlock); + } + uint256 slotIndex = globalIndex % SLOTS; // forge-lint: disable-next-line(unsafe-typecast) uint256 length = _slotLenghts[session][slotIndex].upperLookupRecent(uint96(atBlock)); @@ -42,7 +55,11 @@ abstract contract Weaver is IWeaver { return values; } - function getRoot(uint256 session, uint256 atBlock) public view returns (bytes32, uint256) { + function getRoot(uint256 session, uint256 atBlock) public override view returns (bytes32, uint256) { + if (session < startSession) { + return IWeaver(previousWeaver).getRoot(session, atBlock); + } + uint256 currentLevelCount = SLOTS >> 1; bytes32[] memory currentLevel = new bytes32[](currentLevelCount); @@ -83,7 +100,11 @@ abstract contract Weaver is IWeaver { uint256 globalIndex, uint256 session, uint256 atBlock - ) external view returns (bytes32[] memory) { + ) external override view returns (bytes32[] memory) { + if (session < startSession) { + return IWeaver(previousWeaver).getProof(globalIndex, session, atBlock); + } + uint256 currentLevelCount = SLOTS; uint256 currentIndex = globalIndex % SLOTS; diff --git a/test/bonding/BondDepositorty.t.sol b/test/bonding/BondDepositorty.t.sol index 1ea0702..b38085d 100644 --- a/test/bonding/BondDepositorty.t.sol +++ b/test/bonding/BondDepositorty.t.sol @@ -324,7 +324,7 @@ contract GhostBondDepositoryTest is Test { uint256 amount = 10_000 * 1e18; // 10,000 vm.startPrank(GOVERNOR); - Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, 0, 0, 0, 0); + Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, 0, address(0), 0, 0, 0); staking.setGatekeeperAddress(address(gatekeeper)); staking.setWarmupPeriod(1); vm.stopPrank(); @@ -509,7 +509,7 @@ contract GhostBondDepositoryTest is Test { } vm.startPrank(GOVERNOR); - Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, 0, 0, 0, 0); + Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, 0, address(0), 0, 0, 0); staking.setGatekeeperAddress(address(gatekeeper)); staking.setWarmupPeriod(10); vm.stopPrank(); diff --git a/test/gatekeeper/Gatekeeper.t.sol b/test/gatekeeper/Gatekeeper.t.sol index cecc2c1..9c561d6 100644 --- a/test/gatekeeper/Gatekeeper.t.sol +++ b/test/gatekeeper/Gatekeeper.t.sol @@ -14,14 +14,14 @@ contract GatekeeperTest is Test { event Ghosted(bytes32 indexed receiver, uint256 indexed amount); function setUp() public { - gatekeeper = new Gatekeeper(ALICE, 0, EXISTENTIAL, 0, 0, 0); + gatekeeper = new Gatekeeper(ALICE, 0, EXISTENTIAL, address(0), 0, 0, 0); } function test_correctInitialization() public { assertEq(gatekeeper.staking(), ALICE); assertEq(gatekeeper.ghostedSupply(), 0); - Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, INIT_AMOUNT, EXISTENTIAL, 0, 0, 0); + Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, INIT_AMOUNT, EXISTENTIAL, address(0), 0, 0, 0); assertEq(anotherGatekeeper.staking(), BOB); assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT); assertEq(anotherGatekeeper.existentialDeposit(), EXISTENTIAL); diff --git a/test/gatekeeper/GatekeeperMetadata.t.sol b/test/gatekeeper/GatekeeperMetadata.t.sol index 8875978..de69c18 100644 --- a/test/gatekeeper/GatekeeperMetadata.t.sol +++ b/test/gatekeeper/GatekeeperMetadata.t.sol @@ -16,7 +16,7 @@ contract GatekeeperMetadataTest is Test { Gatekeeper gatekeeper; function setUp() public { - gatekeeper = new Gatekeeper(ALICE, INIT_GHOSTED, EXISTENTIAL, DEPLOYED_AT, AMOUNT_IN, AMOUNT_OUT); + gatekeeper = new Gatekeeper(ALICE, INIT_GHOSTED, EXISTENTIAL, address(0), DEPLOYED_AT, AMOUNT_IN, AMOUNT_OUT); } function test_correctMetadataInitialization() public view { diff --git a/test/gatekeeper/GatekeeperWeaver.t.sol b/test/gatekeeper/GatekeeperWeaver.t.sol index 44f26b3..0fd5943 100644 --- a/test/gatekeeper/GatekeeperWeaver.t.sol +++ b/test/gatekeeper/GatekeeperWeaver.t.sol @@ -14,6 +14,7 @@ contract GatekeeperVerification is Gatekeeper { address staking, uint256 ghostedSupply, uint256 existential, + address previousWeaver, uint48 deployedAt, uint104 amountIn, uint104 amountOut @@ -21,6 +22,7 @@ contract GatekeeperVerification is Gatekeeper { staking, ghostedSupply, existential, + previousWeaver, deployedAt, amountIn, amountOut @@ -102,7 +104,7 @@ contract GatekeeperWeaverTest is Test { GatekeeperVerification gatekeeper; function setUp() public { - gatekeeper = new GatekeeperVerification(ALICE, 0, EXISTENTIAL, 0, 0, 0); + gatekeeper = new GatekeeperVerification(ALICE, 0, EXISTENTIAL, address(0), 0, 0, 0); } function test_insertationWorksAsExpected() public { @@ -138,6 +140,36 @@ contract GatekeeperWeaverTest is Test { 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])); + + vm.roll(block.number + 420); + gatekeeper = new GatekeeperVerification(ALICE, 0, EXISTENTIAL, address(gatekeeper), 0, 0, 0); + uint256 finalSession = gatekeeper.currentSession(); + + for (uint256 i = 0; i < 69; i++) { + if (i % 2 == 0) { vm.roll(block.number + 1); } + vm.prank(ALICE); + gatekeeper.ghost(whos[i], amounts[i]); + } + + 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])); + + // TODO: this fails for some reason + assertTrue(_verifyProof(0, finalSession, block.number, amounts[0], whos[0])); + assertTrue(_verifyProof(34, finalSession, block.number, amounts[34], whos[34])); + assertTrue(_verifyProof(35, finalSession, block.number, amounts[35], whos[35])); } function _verifyProof( diff --git a/test/staking/Staking.t.sol b/test/staking/Staking.t.sol index 1d7ebbd..6c77139 100644 --- a/test/staking/Staking.t.sol +++ b/test/staking/Staking.t.sol @@ -110,7 +110,7 @@ contract StakingTest is Test { treasury = new GhostTreasury(address(ftso), 69, address(authority)); stnk.initialize(address(staking), address(treasury), address(ghst)); ghst.initialize(address(staking)); - gatekeeper = new Gatekeeper(address(staking), 0, 0, 0, 0, 0); + gatekeeper = new Gatekeeper(address(staking), 0, 0, address(0), 0, 0, 0); calculator = new GhostBondingCalculator(address(ftso), 1, 1); vm.stopPrank(); vm.roll(block.number + 1);