From acd944617ef9cb73d9a479d538c034c78e50f7ad Mon Sep 17 00:00:00 2001 From: ericlee Date: Tue, 5 Mar 2024 19:48:03 +0800 Subject: [PATCH] feat(seqset): add finalizedBlock --- contracts/SequencerSet.sol | 56 +++++++++++++++++++++++++++---------- ts-src/test/SequencerSet.ts | 39 ++++++++++++++++++-------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/contracts/SequencerSet.sol b/contracts/SequencerSet.sol index f7e3037..9fa3869 100644 --- a/contracts/SequencerSet.sol +++ b/contracts/SequencerSet.sol @@ -66,6 +66,14 @@ contract MetisSequencerSet is OwnableUpgradeable { // initial epoch uint256 epochId = 0; + // We do not check if the length between the start and end block matches the epoch length + // the epoch length can only be used for next epoch + require( + _firstStartBlock > block.number && + _firstEndBlock > _firstStartBlock, + "Invalid block range" + ); + // initial epoch item epochs[epochId] = Epoch({ number: epochId, @@ -99,9 +107,9 @@ contract MetisSequencerSet is OwnableUpgradeable { // get epoch number by block // It returns Max(uint256) if the height doesn't exist in any epochs function getEpochByBlock(uint256 _number) public view returns (uint256) { - uint256 lastIndex = currentEpochId; - for (uint256 i = lastIndex; i >= 0; i--) { - Epoch memory epoch = epochs[i]; + uint256 lastIndex = currentEpochId + 1; + for (uint256 i = lastIndex; i > 0; i--) { + Epoch memory epoch = epochs[i - 1]; if (epoch.startBlock <= _number && _number <= epoch.endBlock) { return epoch.number; } @@ -110,11 +118,6 @@ contract MetisSequencerSet is OwnableUpgradeable { if (i == lastIndex && _number > epoch.endBlock) { return type(uint256).max; } - - // not in the first epoch - if (i == 0 && _number < epoch.startBlock) { - return type(uint256).max; - } } // return default if not found any thing @@ -132,12 +135,32 @@ contract MetisSequencerSet is OwnableUpgradeable { // finalizedEpoch returns the finalized epoch // the epoch won't be recommitted by `recommitEpoch` - // It will returns zero-value epoch if the epoch doesn't exist - function finalizedEpoch() public view returns (Epoch memory epoch) { - uint256 curEpochId = currentEpochId; - if (curEpochId > 2) { - epoch = epochs[curEpochId - 2]; + // if there is no such epoch, the second return value will be false + function finalizedEpoch() + public + view + returns (Epoch memory epoch, bool exist) + { + uint256 lastIndex = currentEpochId + 1; + for (uint256 i = lastIndex; i > 0; i--) { + epoch = epochs[i - 1]; + if (block.number > epoch.endBlock) { + return (epoch, true); + } + } + // It's epoch 0 but not exist + return (epoch, false); + } + + // finalizedBlock returns the finalized block number + function finalizedBlock() public view returns (uint256) { + (Epoch memory epoch, bool exist) = finalizedEpoch(); + if (exist) { + return epoch.endBlock; } + // the last block of epoch 0 + // since the startBlock must not be 0, so it's safe to minus 1 + return epoch.startBlock - 1; } // get metis sequencer by block number @@ -210,7 +233,7 @@ contract MetisSequencerSet is OwnableUpgradeable { require(_startBlock == block.number, "Invalid start block"); require(_newSigner != address(0), "Invalid signer"); - // Note: We don't check if the startBlock and endBlock match with epochLength + // Note: We do not check if the length between the start and end block matches the epoch length uint256 curEpochId = currentEpochId; // recommitEpoch occurs in the latest epoch @@ -236,6 +259,11 @@ contract MetisSequencerSet is OwnableUpgradeable { // recommitEpoch occurs in last but one epoch else if (_oldEpochId + 1 == curEpochId) { Epoch storage epoch = epochs[_oldEpochId]; + // ensure that finilized epoch can't be changed + require( + epoch.endBlock > block.number, + "The last epoch is finished" + ); epoch.endBlock = block.number - 1; // update latest epoch diff --git a/ts-src/test/SequencerSet.ts b/ts-src/test/SequencerSet.ts index 8df5bf0..d2b6268 100644 --- a/ts-src/test/SequencerSet.ts +++ b/ts-src/test/SequencerSet.ts @@ -5,7 +5,7 @@ import { mineUpTo, } from "@nomicfoundation/hardhat-toolbox/network-helpers"; -const initStartBlock = 1n; +const initStartBlock = 400n; const initEndBlock = 599n; const initEpochLen = 200n; @@ -102,38 +102,51 @@ describe("MetisSequencerSet", async () => { await seqset.connect(mpc).commitEpoch(nextEpochNumber, 600, 799, seq1); + // epoch 0 but not exists { - const { number, startBlock, endBlock, signer } = + const [{ number, startBlock, endBlock, signer }, valid] = await seqset.finalizedEpoch(); expect(number).eq(0); - expect(startBlock).eq(0); - expect(endBlock).eq(0); - expect(signer).eq(ethers.ZeroAddress); + expect(startBlock).eq(initStartBlock); + expect(endBlock).eq(initEndBlock); + expect(signer).eq(seq0.address); + expect(valid).eq(false); + + expect(await seqset.finalizedBlock(), "finalizedBlock-0").eq( + initStartBlock - 1n, + ); } await mineUpTo(700); nextEpochNumber++; await seqset.connect(mpc).commitEpoch(nextEpochNumber, 800, 999, seq0); + // epoch 0 and exists { - const { number, startBlock, endBlock, signer } = + const [{ number, startBlock, endBlock, signer }, valid] = await seqset.finalizedEpoch(); expect(number).eq(0); - expect(startBlock).eq(0); - expect(endBlock).eq(0); - expect(signer).eq(ethers.ZeroAddress); + expect(startBlock).eq(initStartBlock); + expect(endBlock).eq(initEndBlock); + expect(signer).eq(seq0.address); + expect(valid).eq(true); + expect(await seqset.finalizedBlock(), "finalizedBlock-1").eq( + initEndBlock, + ); } await mineUpTo(900); nextEpochNumber++; await seqset.connect(mpc).commitEpoch(nextEpochNumber, 1000, 1199, seq1); { - const { number, startBlock, endBlock, signer } = + const [{ number, startBlock, endBlock, signer }, valid] = await seqset.finalizedEpoch(); expect(number).eq(nextEpochNumber - 2n); expect(startBlock).eq(600); expect(endBlock).eq(799); expect(signer).eq(seq1.address); + expect(valid).eq(true); + expect(await seqset.finalizedBlock(), "finalizedBlock-2").eq(799); } }); @@ -326,7 +339,11 @@ describe("MetisSequencerSet", async () => { await mineUpTo(1200); // block 1201, add epoch 5, 1300-1499 await seqset.connect(mpc).commitEpoch(5, 1300, 1499, seq1); - await mineUpTo(1320); + await mineUpTo(1319); + // block 1320 the epoch 4 has been finished + await expect( + seqset.connect(mpc).recommitEpoch(4, 5, 1321, 1700, seq1), + ).to.be.revertedWith("The last epoch is finished"); // block 1321, add epoch 5, 1500-1699 await seqset.connect(mpc).commitEpoch(6, 1500, 1699, seq1);