From 81b715969713e2bda3e93fa788901643f59d8763 Mon Sep 17 00:00:00 2001 From: ChainDev <76214877+ChainDev931105@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:03:34 -0500 Subject: [PATCH] Make epoch period flexible (#118) * epoch period flexible * test for epoch period flex * add simulation test for flex period * fix epoch period for prior blocks --- contracts/interfaces/ILagrangeCommittee.sol | 5 - contracts/protocol/LagrangeCommittee.sol | 132 +++++-------- script/update/Redeploy_For_UpdateEpoch.s.sol | 30 --- test/edge-fork/UpdateEpochPeriod.s.sol | 143 ++++++-------- test/foundry/CommitteeTree.t.sol | 10 +- test/foundry/EpochNumber.t.sol | 156 +++++++++++++++ test/foundry/LagrangeDeployer.t.sol | 4 +- test/foundry/UpdateChain.t.sol | 197 ------------------- 8 files changed, 270 insertions(+), 407 deletions(-) delete mode 100644 script/update/Redeploy_For_UpdateEpoch.s.sol create mode 100644 test/foundry/EpochNumber.t.sol delete mode 100644 test/foundry/UpdateChain.t.sol diff --git a/contracts/interfaces/ILagrangeCommittee.sol b/contracts/interfaces/ILagrangeCommittee.sol index 9e66ad8..b946f2c 100644 --- a/contracts/interfaces/ILagrangeCommittee.sol +++ b/contracts/interfaces/ILagrangeCommittee.sol @@ -117,11 +117,6 @@ interface ILagrangeCommittee { // Fired on successful rotation of committee event UpdateCommittee(uint256 indexed chainID, uint256 indexed epochNumber, bytes32 current); - // Event fired on updating epoch period - event EpochPeriodUpdated( - uint32 indexed chainID, uint32 indexed epochPeriodIndex, uint256 flagBlock, uint256 flagEpoch, uint256 duration - ); - // Event fired on updating sign address event SignAddressUpdated(address indexed operator, address indexed signAddress); } diff --git a/contracts/protocol/LagrangeCommittee.sol b/contracts/protocol/LagrangeCommittee.sol index 3b75a22..7d99a36 100644 --- a/contracts/protocol/LagrangeCommittee.sol +++ b/contracts/protocol/LagrangeCommittee.sol @@ -23,14 +23,6 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, ILagrangeCommit // ChainID => Committee mapping(uint32 => CommitteeDef) public committeeParams; - // committees is also used for external storage for epoch period modification - // committees[chainID][uint256.max].leafCount = current index of epoch period - // committees[chainID][uint256.max - 1] : 1-index - // updatedBlock: (flagBlock << 112) | flagEpoch - // leafCount: epochPeriod - // committees[chainID][uint256.max - 2] : 2-index - // committees[chainID][uint256.max - 3] : 3-index - // ... ... ... // ChainID => Epoch => CommitteeData mapping(uint32 => mapping(uint256 => CommitteeData)) public committees; @@ -67,16 +59,6 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, ILagrangeCommit _transferOwnership(initialOwner); } - // Initializes epoch period - // @dev This function can be called for the chainID registered in the previous version - function setFirstEpochPeriod(uint32 chainID) public onlyOwner { - uint32 _count = getEpochPeriodCount(chainID); - if (_count != 0) return; // already initialized - CommitteeDef memory _committeeParam = committeeParams[chainID]; - - _writeEpochPeriod(chainID, _committeeParam.startBlock, 0, _committeeParam.duration); - } - // Adds a new operator to the committee function addOperator(address operator, address signAddress, uint256[2][] calldata blsPubKeys) external @@ -366,7 +348,7 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, ILagrangeCommit epochNumber = _getEpochNumber(chainID, blockNumber); // All the prior blocks belong to epoch 1 - if (epochNumber == 0) epochNumber = 1; + if (epochNumber == 0 && blockNumber >= committeeParams[chainID].genesisBlock) epochNumber = 1; } // Get the operator's voting power for the given chainID @@ -424,8 +406,6 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, ILagrangeCommit chainIDs.push(_chainID); - setFirstEpochPeriod(_chainID); - emit InitCommittee(_chainID, _quorumNumber, _genesisBlock, _duration, _freezeDuration, _minWeight, _maxWeight); } @@ -441,12 +421,6 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, ILagrangeCommit uint96 _minWeight, uint96 _maxWeight ) internal { - if (committeeParams[_chainID].duration != _duration) { - uint256 _flagEpoch = _getEpochNumber(_chainID, block.number - 1) + 1; - (,, uint256 _endBlockPrv) = getEpochInterval(_chainID, _flagEpoch - 1); - _writeEpochPeriod(_chainID, _endBlockPrv, _flagEpoch, _duration); - } - committeeParams[_chainID] = CommitteeDef( _startBlock, _l1Bias, _genesisBlock, _duration, _freezeDuration, _quorumNumber, _minWeight, _maxWeight ); @@ -456,74 +430,68 @@ contract LagrangeCommittee is Initializable, OwnableUpgradeable, ILagrangeCommit ); } - // ----------------- Functions for Epoch Number ----------------- // - function getEpochPeriodCount(uint32 chainID) public view returns (uint32) { - return committees[chainID][type(uint256).max].leafCount; - } - - function getEpochPeriodByIndex(uint32 chainID, uint32 index) - public - view - returns (uint256 flagBlock, uint256 flagEpoch, uint256 duration) - { - CommitteeData memory _epochPeriodContext = committees[chainID][type(uint256).max - index]; - return ( - _epochPeriodContext.updatedBlock >> 112, - (_epochPeriodContext.updatedBlock << 112) >> 112, - _epochPeriodContext.leafCount - ); - } - - function getEpochInterval(uint32 _chainID, uint256 _epochNumber) + // Get epoch interval for a given chain + function getEpochInterval(uint32 chainID, uint256 epochNumber) public view - returns (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) + returns (uint256 startBlock, uint256 freezeBlock, uint256 endBlock) { - // epoch period would be updated rarely - uint32 _index = getEpochPeriodCount(_chainID); - while (_index > 0) { - (uint256 _flagBlock, uint256 _flagEpoch, uint256 _duration) = getEpochPeriodByIndex(_chainID, _index); - if (_epochNumber >= _flagEpoch) { - _startBlock = (_epochNumber - _flagEpoch) * _duration + _flagBlock; - _endBlock = _startBlock + _duration; - _freezeBlock = _endBlock - committeeParams[_chainID].freezeDuration; - break; - } - unchecked { - _index--; - } + CommitteeDef memory committeeParam = committeeParams[chainID]; + uint256 _lastEpoch = updatedEpoch[chainID]; + + if (epochNumber == 0) { + startBlock = committeeParam.startBlock; + endBlock = _lastEpoch == 0 + ? committeeParam.startBlock + committeeParam.duration + : committees[chainID][1].updatedBlock; + freezeBlock = endBlock - committeeParam.freezeDuration; + return (startBlock, freezeBlock, endBlock); } - } - function _writeEpochPeriod(uint32 _chainID, uint256 _flagBlock, uint256 _flagEpoch, uint256 _duration) internal { - uint32 _index = committees[_chainID][type(uint256).max].leafCount + 1; - committees[_chainID][type(uint256).max - _index] = CommitteeData( - 0, - (uint224(SafeCast.toUint112(_flagBlock)) << 112) + uint224(SafeCast.toUint112(_flagEpoch)), - SafeCast.toUint32(_duration) - ); - committees[_chainID][type(uint256).max].leafCount = _index; - emit EpochPeriodUpdated(_chainID, _index, _flagBlock, _flagEpoch, _duration); + if (epochNumber <= _lastEpoch) { + startBlock = committees[chainID][epochNumber].updatedBlock; + endBlock = _lastEpoch == epochNumber + ? startBlock + committeeParam.duration + : committees[chainID][epochNumber + 1].updatedBlock; + freezeBlock = endBlock - committeeParam.freezeDuration; + } else { + uint256 _lastEpochBlock = + _lastEpoch > 0 ? committees[chainID][_lastEpoch].updatedBlock : committeeParam.startBlock; + startBlock = _lastEpochBlock + (epochNumber - _lastEpoch) * committeeParam.duration; + endBlock = startBlock + committeeParam.duration; + freezeBlock = endBlock - committeeParam.freezeDuration; + } } function _getEpochNumber(uint32 _chainID, uint256 _blockNumber) internal view returns (uint256 _epochNumber) { - if (_blockNumber < committeeParams[_chainID].genesisBlock) { + CommitteeDef memory committeeParam = committeeParams[_chainID]; + if (_blockNumber < committeeParam.startBlock) { return 0; } - // epoch period would be updated rarely - uint32 _index = getEpochPeriodCount(_chainID); - while (_index > 0) { - (uint256 _flagBlock, uint256 _flagEpoch, uint256 _duration) = getEpochPeriodByIndex(_chainID, _index); - if (_blockNumber >= _flagBlock) { - _epochNumber = _flagEpoch + (_blockNumber - _flagBlock) / _duration; - break; - } - unchecked { - _index--; + + uint256 _lastEpoch = updatedEpoch[_chainID]; + uint256 _lastEpochBlock = + _lastEpoch > 0 ? committees[_chainID][_lastEpoch].updatedBlock : committeeParam.startBlock; + + if (_blockNumber >= _lastEpochBlock) { + _epochNumber = _lastEpoch + (_blockNumber - _lastEpochBlock) / committeeParam.duration; + } else if (_lastEpoch == 0) { + return 0; + } else { + // binary search + uint256 _low = 0; + uint256 _high = _lastEpoch; + while (_low < _high - 1) { + uint256 _mid = (_low + _high + 1) >> 1; + if (_blockNumber < committees[_chainID][_mid].updatedBlock) { + _high = _mid; + } else { + _low = _mid + 1; + } } + _epochNumber = _high - 1; } } - // ------------------------------------------------------------- // function _registerOperator(address _operator, address _signAddress, uint256[2][] memory _blsPubKeys) internal { delete operatorsStatus[_operator]; diff --git a/script/update/Redeploy_For_UpdateEpoch.s.sol b/script/update/Redeploy_For_UpdateEpoch.s.sol deleted file mode 100644 index 256b201..0000000 --- a/script/update/Redeploy_For_UpdateEpoch.s.sol +++ /dev/null @@ -1,30 +0,0 @@ -pragma solidity ^0.8.12; -pragma solidity ^0.8.12; - -import "./BaseScript.s.sol"; - -contract Redeploy_For_UpdateEpoch is BaseScript { - function run() public { - _readContracts(); - - vm.startBroadcast(msg.sender); - - // deploy lagrangeCommittee implementation - LagrangeCommittee lagrangeCommitteeImp = new LagrangeCommittee(lagrangeService, lagrangeCommittee.voteWeigher()); - // upgrade proxy contract - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(lagrangeCommittee))), address(lagrangeCommitteeImp) - ); - - uint256 chainCount = 3; // @note Please set this value exactly - - for (uint256 i = 0; i < chainCount; i++) { - // Get Chain ID - uint32 chainID = lagrangeCommittee.chainIDs(0); - // set first epoch period for CHAIN_ID - lagrangeCommittee.setFirstEpochPeriod(chainID); - } - - vm.stopBroadcast(); - } -} diff --git a/test/edge-fork/UpdateEpochPeriod.s.sol b/test/edge-fork/UpdateEpochPeriod.s.sol index 200cd3c..bf69f06 100644 --- a/test/edge-fork/UpdateEpochPeriod.s.sol +++ b/test/edge-fork/UpdateEpochPeriod.s.sol @@ -23,7 +23,7 @@ contract UpdateEpochPeriod is Script { struct TestCase { uint32 chainID; uint256 blockNumber; - uint256 epochPeriod; + uint256 epochNumber; } function run() public { @@ -34,36 +34,55 @@ contract UpdateEpochPeriod is Script { uint32 CHAIN_ID_BASE = lagrangeCommittee.chainIDs(0); uint32 CHAIN_ID_OP = lagrangeCommittee.chainIDs(1); + uint32 CHAIN_ID_ARB = lagrangeCommittee.chainIDs(2); address owner = lagrangeService.owner(); uint256 _blockNumber = block.number; // Make test cases - TestCase[] memory testCases = new TestCase[](10); + TestCase[] memory testCases = new TestCase[](15); { - (uint256 startBlock,,,,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID_BASE); - testCases[0].chainID = CHAIN_ID_BASE; - testCases[0].blockNumber = startBlock; - testCases[1].chainID = CHAIN_ID_BASE; - testCases[1].blockNumber = startBlock + 10000; - testCases[2].chainID = CHAIN_ID_BASE; - testCases[2].blockNumber = _blockNumber - 10000; - testCases[3].chainID = CHAIN_ID_BASE; - testCases[3].blockNumber = _blockNumber + 10000; - testCases[4].chainID = CHAIN_ID_BASE; - testCases[4].blockNumber = _blockNumber + 20000; - testCases[5].chainID = CHAIN_ID_OP; - testCases[5].blockNumber = startBlock; - testCases[6].chainID = CHAIN_ID_OP; - testCases[6].blockNumber = startBlock + 10000; - testCases[7].chainID = CHAIN_ID_OP; - testCases[7].blockNumber = _blockNumber - 10000; - testCases[8].chainID = CHAIN_ID_OP; - testCases[8].blockNumber = _blockNumber + 10000; - testCases[9].chainID = CHAIN_ID_OP; - testCases[9].blockNumber = _blockNumber + 20000; + { + (uint256 startBlock,,,,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID_BASE); + testCases[0].chainID = CHAIN_ID_BASE; + testCases[0].blockNumber = startBlock; + testCases[1].chainID = CHAIN_ID_BASE; + testCases[1].blockNumber = startBlock + 10000; + testCases[2].chainID = CHAIN_ID_BASE; + testCases[2].blockNumber = _blockNumber - 10000; + testCases[3].chainID = CHAIN_ID_BASE; + testCases[3].blockNumber = _blockNumber + 10000; + testCases[4].chainID = CHAIN_ID_BASE; + testCases[4].blockNumber = _blockNumber + 20000; + } + { + (uint256 startBlock,,,,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID_OP); + testCases[5].chainID = CHAIN_ID_OP; + testCases[5].blockNumber = startBlock; + testCases[6].chainID = CHAIN_ID_OP; + testCases[6].blockNumber = startBlock + 10000; + testCases[7].chainID = CHAIN_ID_OP; + testCases[7].blockNumber = _blockNumber - 10000; + testCases[8].chainID = CHAIN_ID_OP; + testCases[8].blockNumber = _blockNumber + 10000; + testCases[9].chainID = CHAIN_ID_OP; + testCases[9].blockNumber = _blockNumber + 20000; + } + { + (uint256 startBlock,,,,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID_ARB); + testCases[10].chainID = CHAIN_ID_ARB; + testCases[10].blockNumber = startBlock; + testCases[11].chainID = CHAIN_ID_ARB; + testCases[11].blockNumber = startBlock + 10000; + testCases[12].chainID = CHAIN_ID_ARB; + testCases[12].blockNumber = _blockNumber - 10000; + testCases[13].chainID = CHAIN_ID_ARB; + testCases[13].blockNumber = _blockNumber + 10000; + testCases[14].chainID = CHAIN_ID_ARB; + testCases[14].blockNumber = _blockNumber + 20000; + } for (uint256 i; i < testCases.length; i++) { - testCases[i].epochPeriod = lagrangeCommittee.getEpochNumber(testCases[i].chainID, testCases[i].blockNumber); + testCases[i].epochNumber = lagrangeCommittee.getEpochNumber(testCases[i].chainID, testCases[i].blockNumber); } } @@ -76,83 +95,33 @@ contract UpdateEpochPeriod is Script { TransparentUpgradeableProxy(payable(address(lagrangeCommittee))), address(lagrangeCommitteeImp) ); } - - // set first epoch period for CHAIN_ID_BASE - { - (uint256 startBlock,,, uint256 duration,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID_BASE); - (uint256[] memory _flagBlocks, uint256[] memory _flagEpoches, uint256[] memory _durations) = - _getAllEpochPeriods(CHAIN_ID_BASE); - - require(_flagBlocks.length == 0, "Expected zero length originally"); - lagrangeCommittee.setFirstEpochPeriod(CHAIN_ID_BASE); - (_flagBlocks, _flagEpoches, _durations) = _getAllEpochPeriods(CHAIN_ID_BASE); - require(_flagBlocks.length == 1, "First epoch period is not written."); - require(_flagBlocks[0] == startBlock, "First epoch period / flagBlock is incorrect."); - require(_flagEpoches[0] == 0, "First epoch period / flagEpoch is incorrect."); - require(_durations[0] == duration, "First epoch period / epochPeriod is incorrect."); - } - - // set first epoch period for CHAIN_ID_OP - { - (uint256 startBlock,,, uint256 duration,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID_OP); - (uint256[] memory _flagBlocks, uint256[] memory _flagEpoches, uint256[] memory _durations) = - _getAllEpochPeriods(CHAIN_ID_OP); - - require(_flagBlocks.length == 0, "Expected zero length originally"); - lagrangeCommittee.setFirstEpochPeriod(CHAIN_ID_OP); - (_flagBlocks, _flagEpoches, _durations) = _getAllEpochPeriods(CHAIN_ID_OP); - require(_flagBlocks.length == 1, "First epoch period is not written."); - require(_flagBlocks[0] == startBlock, "First epoch period / flagBlock is incorrect."); - require(_flagEpoches[0] == 0, "First epoch period / flagEpoch is incorrect."); - require(_durations[0] == duration, "First epoch period / epochPeriod is incorrect."); - } vm.stopPrank(); // check getEpochNumber through testcases { for (uint256 i; i < testCases.length; i++) { - require(testCases[i].epochPeriod == lagrangeCommittee.getEpochNumber(testCases[i].chainID, testCases[i].blockNumber), "Failed in test case"); + uint256 epochNumber = lagrangeCommittee.getEpochNumber(testCases[i].chainID, testCases[i].blockNumber); + if (testCases[i].epochNumber != epochNumber) { + (uint256 startBlock,,,uint256 duration,,,,) = lagrangeCommittee.committeeParams(testCases[i].chainID); + uint256 latestEpoch = lagrangeCommittee.updatedEpoch(testCases[i].chainID); + (bytes32 root, uint224 updatedBlock, uint32 leafCount) = + lagrangeCommittee.committees(testCases[i].chainID, latestEpoch); + if (updatedBlock <= testCases[i].blockNumber) { + uint256 expectedBlockNumber = (testCases[i].blockNumber - updatedBlock) / duration + latestEpoch; + require(epochNumber == expectedBlockNumber); + } + } } } uint256 _newBlockNumber = _blockNumber + 5000; vm.roll(_newBlockNumber); - // update epoch period for CHAIN_ID_BASE + // update epoch period { _updateEpochPeriod(CHAIN_ID_BASE, 3000); - (uint256[] memory _flagBlocks, uint256[] memory _flagEpoches, uint256[] memory _durations) - = _getAllEpochPeriods(CHAIN_ID_BASE); - uint256 epochNumber = lagrangeCommittee.getEpochNumber(CHAIN_ID_BASE, _newBlockNumber - 1) + 1; - require(_flagBlocks.length == 2, "Second epoch period is not written."); - require(_flagBlocks[1] >= _newBlockNumber, "Second epoch period / flagBlock is incorrect."); - require(_flagEpoches[1] == epochNumber, "Second epoch period / flagEpoch is incorrect."); - require(_durations[1] == 3000, "Second epoch period / epochPeriod is incorrect."); - } - - // update epoch period for CHAIN_ID_OP - { _updateEpochPeriod(CHAIN_ID_OP, 3000); - (uint256[] memory _flagBlocks, uint256[] memory _flagEpoches, uint256[] memory _durations) - = _getAllEpochPeriods(CHAIN_ID_BASE); - uint256 epochNumber = lagrangeCommittee.getEpochNumber(CHAIN_ID_BASE, _newBlockNumber - 1) + 1; - require(_flagBlocks.length == 2, "Second epoch period is not written."); - require(_flagBlocks[1] >= _newBlockNumber, "Second epoch period / flagBlock is incorrect."); - require(_flagEpoches[1] == epochNumber, "Second epoch period / flagEpoch is incorrect."); - require(_durations[1] == 3000, "Second epoch period / epochPeriod is incorrect."); - } - } - - function _getAllEpochPeriods(uint32 chainID) - internal - returns (uint256[] memory _flagBlocks, uint256[] memory _flagEpoches, uint256[] memory _durations) - { - uint32 _epochHistoryCount = lagrangeCommittee.getEpochPeriodCount(chainID); - _flagBlocks = new uint256[](_epochHistoryCount); - _flagEpoches = new uint256[](_epochHistoryCount); - _durations = new uint256[](_epochHistoryCount); - for (uint32 i = 0; i < _epochHistoryCount; i++) { - (_flagBlocks[i], _flagEpoches[i], _durations[i]) = lagrangeCommittee.getEpochPeriodByIndex(chainID, i + 1); + _updateEpochPeriod(CHAIN_ID_ARB, 3000); } } diff --git a/test/foundry/CommitteeTree.t.sol b/test/foundry/CommitteeTree.t.sol index 446eef6..5a48fbc 100644 --- a/test/foundry/CommitteeTree.t.sol +++ b/test/foundry/CommitteeTree.t.sol @@ -82,7 +82,8 @@ contract CommitteeTreeTest is LagrangeDeployer { } // update the tree - vm.roll(START_EPOCH + EPOCH_PERIOD - FREEZE_DURATION + 1); + uint256 updatingBlock1 = START_EPOCH + EPOCH_PERIOD - FREEZE_DURATION + 1; + vm.roll(updatingBlock1); lagrangeCommittee.update(CHAIN_ID, 1); ILagrangeCommittee.CommitteeData memory cur = @@ -93,14 +94,15 @@ contract CommitteeTreeTest is LagrangeDeployer { _deposit(operators[0], 1e15); - vm.roll(START_EPOCH + EPOCH_PERIOD * 2 - FREEZE_DURATION); + uint256 updatingBlock2 = updatingBlock1 + EPOCH_PERIOD - FREEZE_DURATION + 1; + vm.roll(updatingBlock2 - 1); vm.expectRevert("Block number is prior to committee freeze window."); lagrangeCommittee.update(CHAIN_ID, 2); - vm.roll(START_EPOCH + EPOCH_PERIOD * 2 - FREEZE_DURATION + 1); + vm.roll(updatingBlock2); lagrangeCommittee.update(CHAIN_ID, 2); - cur = lagrangeCommittee.getCommittee(CHAIN_ID, START_EPOCH + EPOCH_PERIOD * 2); + cur = lagrangeCommittee.getCommittee(CHAIN_ID, updatingBlock2 + 1); assertEq(cur.leafCount, 3); assertEq(cur.root, 0x02f507202eec14a32171bbca5d048778e4c67238b21037a90b90608c71b6276a); } diff --git a/test/foundry/EpochNumber.t.sol b/test/foundry/EpochNumber.t.sol new file mode 100644 index 0000000..fa0631a --- /dev/null +++ b/test/foundry/EpochNumber.t.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import "./LagrangeDeployer.t.sol"; + +contract UpdateChainTest is LagrangeDeployer { + function testEpochPeriodUpdate_failWithSmallValue() public { + ( + , // startBlock + int256 l1Bias, + uint256 genesisBlock, + uint256 duration, + uint256 freezeDuration, + uint8 quorumNumber, + uint96 minWeight, + uint96 maxWeight + ) = lagrangeCommittee.committeeParams(CHAIN_ID); + + uint256 newEpochPeriod = freezeDuration - 1; // set with smaller value than freezeDuration + + vm.roll(START_EPOCH + duration * 10); + vm.prank(lagrangeCommittee.owner()); + vm.expectRevert("Invalid freeze duration"); + lagrangeCommittee.updateChain( + CHAIN_ID, l1Bias, genesisBlock, newEpochPeriod, freezeDuration, quorumNumber, minWeight, maxWeight + ); + } + + function testEpochPeriodUpdate() public { + (uint256 startBlock,,uint256 genesisBlock, uint256 duration, uint256 freezeDuration,,,) = + lagrangeCommittee.committeeParams(CHAIN_ID); + + // Originally + { + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, genesisBlock - 1), 0); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, genesisBlock), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock + duration), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock + duration * 2 - 1), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock + duration * 2), 2); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock + duration * 3 - 1), 2); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock + duration * 3), 3); + + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 10); + assertEq(_startBlock, startBlock + duration * 10); + assertEq(_freezeBlock, startBlock + duration * 11 - freezeDuration); + assertEq(_endBlock, startBlock + duration * 11); + } + + // Update failed before lock period + vm.roll(startBlock + duration - freezeDuration); + vm.expectRevert("Block number is prior to committee freeze window."); + lagrangeCommittee.update(CHAIN_ID, 1); + + // Update in some block + uint256 updatedBlock1 = startBlock + duration - freezeDuration + 4; + vm.roll(updatedBlock1); + lagrangeCommittee.update(CHAIN_ID, 1); + + uint256 updatedBlock2 = updatedBlock1 + duration; + vm.roll(updatedBlock2); + lagrangeCommittee.update(CHAIN_ID, 2); + + uint256 updatedBlock3 = updatedBlock1 + duration * 100; + vm.roll(updatedBlock3); + lagrangeCommittee.update(CHAIN_ID, 3); + + // Test getEpochInterval + { + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, genesisBlock - 1), 0); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, genesisBlock), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, startBlock), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, updatedBlock1), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, updatedBlock2 - 1), 1); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, updatedBlock2), 2); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, updatedBlock3 - 1), 2); + assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, updatedBlock3), 3); + + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 1); + assertEq(_startBlock, updatedBlock1); + assertEq(_freezeBlock, updatedBlock2 - freezeDuration); + assertEq(_endBlock, updatedBlock2); + } + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 2); + assertEq(_startBlock, updatedBlock2); + assertEq(_freezeBlock, updatedBlock3 - freezeDuration); + assertEq(_endBlock, updatedBlock3); + } + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 3); + assertEq(_startBlock, updatedBlock3); + assertEq(_freezeBlock, updatedBlock3 + duration - freezeDuration); + assertEq(_endBlock, updatedBlock3 + duration); + } + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 10); + assertEq(_startBlock, updatedBlock3 + duration * 7); + assertEq(_freezeBlock, updatedBlock3 + duration * 8 - freezeDuration); + assertEq(_endBlock, updatedBlock3 + duration * 8); + } + } + + uint256 newEpochPeriod = 30; + + _updateEpochPeriod(CHAIN_ID, newEpochPeriod); + + // Test getEpochInterval + { + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 2); + assertEq(_startBlock, updatedBlock2); + assertEq(_freezeBlock, updatedBlock3 - freezeDuration); + assertEq(_endBlock, updatedBlock3); + } + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 3); + assertEq(_startBlock, updatedBlock3); + assertEq(_freezeBlock, updatedBlock3 + newEpochPeriod - freezeDuration); + assertEq(_endBlock, updatedBlock3 + newEpochPeriod); + } + { + (uint256 _startBlock, uint256 _freezeBlock, uint256 _endBlock) = + lagrangeCommittee.getEpochInterval(CHAIN_ID, 10); + assertEq(_startBlock, updatedBlock3 + newEpochPeriod * 7); + assertEq(_freezeBlock, updatedBlock3 + newEpochPeriod * 8 - freezeDuration); + assertEq(_endBlock, updatedBlock3 + newEpochPeriod * 8); + } + } + } + + function _updateEpochPeriod(uint32 chainID, uint256 newEpochPeriod) internal { + ( + , // startBlock + int256 l1Bias, + uint256 genesisBlock, + , + uint256 freezeDuration, + uint8 quorumNumber, + uint96 minWeight, + uint96 maxWeight + ) = lagrangeCommittee.committeeParams(chainID); + vm.prank(lagrangeCommittee.owner()); + lagrangeCommittee.updateChain( + chainID, l1Bias, genesisBlock, newEpochPeriod, freezeDuration, quorumNumber, minWeight, maxWeight + ); + } +} diff --git a/test/foundry/LagrangeDeployer.t.sol b/test/foundry/LagrangeDeployer.t.sol index 250ebe4..762b84b 100644 --- a/test/foundry/LagrangeDeployer.t.sol +++ b/test/foundry/LagrangeDeployer.t.sol @@ -119,7 +119,7 @@ contract LagrangeDeployer is Test { // register chains lagrangeCommittee.registerChain( CHAIN_ID, - 0, + 1, EPOCH_PERIOD, FREEZE_DURATION, 0, @@ -128,7 +128,7 @@ contract LagrangeDeployer is Test { ); lagrangeCommittee.registerChain( CHAIN_ID + 1, - 0, + 1, EPOCH_PERIOD * 2, FREEZE_DURATION * 2, 0, diff --git a/test/foundry/UpdateChain.t.sol b/test/foundry/UpdateChain.t.sol deleted file mode 100644 index d6ac4af..0000000 --- a/test/foundry/UpdateChain.t.sol +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.12; - -import "./LagrangeDeployer.t.sol"; - -contract UpdateChainTest is LagrangeDeployer { - function testGetAllEpochHistory() public { - (uint256 startBlock,,, uint256 duration,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID); - - (uint256[] memory flagBlocks, uint256[] memory flagEpoches, uint256[] memory durations) = - _getAllEpochHistory(CHAIN_ID); - - assertEq(flagBlocks.length, 1); - assertEq(flagEpoches.length, 1); - assertEq(durations.length, 1); - - assertEq(flagBlocks[0], startBlock); - assertEq(flagEpoches[0], 0); - assertEq(durations[0], duration); - } - - function testEpochPeriodUpdate_skipWithSameValue() public { - (,,, uint256 duration,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID); - - vm.roll(START_EPOCH + duration * 10); - _updateEpochPeriod(CHAIN_ID, duration); - - uint32 epochHistoryCount = lagrangeCommittee.getEpochPeriodCount(CHAIN_ID); - // epochHistoryCount should be still 1 - assertEq(epochHistoryCount, 1); - } - - function testEpochPeriodUpdate_failWithSmallValue() public { - ( - , // startBlock - int256 l1Bias, - uint256 genesisBlock, - uint256 duration, - uint256 freezeDuration, - uint8 quorumNumber, - uint96 minWeight, - uint96 maxWeight - ) = lagrangeCommittee.committeeParams(CHAIN_ID); - - uint256 newEpochPeriod = freezeDuration - 1; // set with smaller value than freezeDuration - - vm.roll(START_EPOCH + duration * 10); - vm.prank(lagrangeCommittee.owner()); - vm.expectRevert("Invalid freeze duration"); - lagrangeCommittee.updateChain( - CHAIN_ID, l1Bias, genesisBlock, newEpochPeriod, freezeDuration, quorumNumber, minWeight, maxWeight - ); - } - - function testEpochPeriodUpdate_single() public { - (uint256 startBlock,,, uint256 duration,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID); - - uint256 newEpochPeriod = 30; - - vm.roll(startBlock + duration * 10 + 1); - _updateEpochPeriod(CHAIN_ID, newEpochPeriod); - - (uint256[] memory flagBlocks, uint256[] memory flagEpoches, uint256[] memory durations) = - _getAllEpochHistory(CHAIN_ID); - - // 1. check epoch hisotry - { - uint256 expectedFlagEpoch = lagrangeCommittee.getEpochNumber(CHAIN_ID, block.number - 1) + 1; - uint256 expectedFlagBlock = startBlock + duration * expectedFlagEpoch; - assertEq(durations.length, 2); - - assertEq(flagBlocks[0], startBlock); - assertEq(flagEpoches[0], 0); - assertEq(durations[0], duration); - - assertEq(flagBlocks[1], expectedFlagBlock); - assertEq(flagEpoches[1], expectedFlagEpoch); - assertEq(durations[1], newEpochPeriod); - } - - // 2. check getEpochNumber with several cases - { - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[0] - 1), 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[0]), 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[0] + durations[0]), 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[0] + durations[0] * 2 - 1), 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[0] + durations[0] * 2), 2); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] - durations[0]), flagEpoches[1] - 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] - 1), flagEpoches[1] - 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1]), flagEpoches[1]); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] + 1), flagEpoches[1]); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] + durations[1]), flagEpoches[1] + 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] + durations[1] * 2), flagEpoches[1] + 2); - assertEq( - lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] + durations[1] * 100 - 1), flagEpoches[1] + 99 - ); - assertEq( - lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] + durations[1] * 100), flagEpoches[1] + 100 - ); - } - - // 3. check isLocked with several cases - { - bool locked; - uint256 blockNumber; - - vm.roll(flagBlocks[0]); - (locked, blockNumber) = lagrangeCommittee.isLocked(CHAIN_ID); - assertEq(locked, false); - - vm.roll(flagBlocks[1] - 1); - (locked, blockNumber) = lagrangeCommittee.isLocked(CHAIN_ID); - assertEq(locked, true); - assertEq(blockNumber, flagBlocks[1]); - - vm.roll(flagBlocks[1] + 1); - (locked, blockNumber) = lagrangeCommittee.isLocked(CHAIN_ID); - assertEq(locked, false); - - vm.roll(flagBlocks[1] + durations[1] - FREEZE_DURATION + 1); - (locked, blockNumber) = lagrangeCommittee.isLocked(CHAIN_ID); - assertEq(locked, true); - assertEq(blockNumber, flagBlocks[1] + durations[1]); - } - - // 4. check isUpdatable with several cases - { - vm.roll(flagBlocks[1] - FREEZE_DURATION); - assertEq(lagrangeCommittee.isUpdatable(CHAIN_ID, flagEpoches[1]), false); - - vm.roll(flagBlocks[1] - FREEZE_DURATION + 1); - assertEq(lagrangeCommittee.isUpdatable(CHAIN_ID, flagEpoches[1]), true); - - vm.roll(flagBlocks[1] + durations[1] * 100 - FREEZE_DURATION); - assertEq(lagrangeCommittee.isUpdatable(CHAIN_ID, flagEpoches[1] + 100), false); - - vm.roll(flagBlocks[1] + durations[1] * 100 - FREEZE_DURATION + 1); - assertEq(lagrangeCommittee.isUpdatable(CHAIN_ID, flagEpoches[1] + 100), true); - } - } - - function testEpochPeriodUpdate_multiple() public { - (uint256 startBlock,,, uint256 duration,,,,) = lagrangeCommittee.committeeParams(CHAIN_ID); - - vm.roll(startBlock + duration * 10 + 1); - _updateEpochPeriod(CHAIN_ID, duration * 2); - - vm.roll(startBlock + duration * 100); - _updateEpochPeriod(CHAIN_ID, duration * 5 / 2); - - vm.roll(startBlock + duration * 200); - _updateEpochPeriod(CHAIN_ID, duration); - - (uint256[] memory flagBlocks, uint256[] memory flagEpoches,) = _getAllEpochHistory(CHAIN_ID); - - assertEq(flagBlocks.length, 4); - { - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1] - 1), flagEpoches[1] - 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[1]), flagEpoches[1]); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[2] - 1), flagEpoches[2] - 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[2]), flagEpoches[2]); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[3] - 1), flagEpoches[3] - 1); - assertEq(lagrangeCommittee.getEpochNumber(CHAIN_ID, flagBlocks[3]), flagEpoches[3]); - } - } - - function _getAllEpochHistory(uint32 chainID) - internal - view - returns (uint256[] memory _flagBlocks, uint256[] memory _flagEpoches, uint256[] memory _durations) - { - uint32 _epochHistoryCount = lagrangeCommittee.getEpochPeriodCount(chainID); - _flagBlocks = new uint256[](_epochHistoryCount); - _flagEpoches = new uint256[](_epochHistoryCount); - _durations = new uint256[](_epochHistoryCount); - for (uint32 i = 0; i < _epochHistoryCount; i++) { - (_flagBlocks[i], _flagEpoches[i], _durations[i]) = lagrangeCommittee.getEpochPeriodByIndex(chainID, i + 1); - } - } - - function _updateEpochPeriod(uint32 chainID, uint256 newEpochPeriod) internal { - ( - , // startBlock - int256 l1Bias, - uint256 genesisBlock, - , - uint256 freezeDuration, - uint8 quorumNumber, - uint96 minWeight, - uint96 maxWeight - ) = lagrangeCommittee.committeeParams(chainID); - vm.prank(lagrangeCommittee.owner()); - lagrangeCommittee.updateChain( - chainID, l1Bias, genesisBlock, newEpochPeriod, freezeDuration, quorumNumber, minWeight, maxWeight - ); - } -}