diff --git a/contracts/ChainIdManager.sol b/contracts/ChainIdManager.sol index 7602387d..03e71c40 100644 --- a/contracts/ChainIdManager.sol +++ b/contracts/ChainIdManager.sol @@ -4,30 +4,31 @@ pragma solidity ^0.8.20; import {Owned} from "./mixin/Owned.sol"; contract ChainIdManager is Owned { - // Counter for the number of used Chain IDs - uint256 public usedChainIdCounter = 0; // Counter for the number of Chain IDs - uint256 public chainIdCounter = 0; - mapping(uint256 => uint64) public usableChainIds; + uint64 public chainIdCounter = 0; + // contains a list of denied Chain IDs that should not be used as chainIds + // they are governed by a owner of the contract + mapping(uint64 => bool) public deniedChainIds; + // Fee to use up a Chain ID + uint256 public immutable gasBurnAmount = 1000000; - constructor() Owned() {} + constructor(uint64 _chainIdCounter) Owned() {chainIdCounter = _chainIdCounter;} /** - * @dev Adds a Chain ID + * @dev Adds a Chain ID to the deny list, this can be done if the chainId is used by another project * @param chainId The Chain ID to add */ - function addChainId(uint64 chainId) public onlyOwner { - usableChainIds[chainIdCounter] = chainId; - chainIdCounter++; + function denyListChainId(uint64 chainId) public onlyOwner { + deniedChainIds[chainId] = true; } /** - * @dev Adds multiple Chain IDs + * @dev Adds multiple Chain IDs to the deny list * @param chainIds The Chain IDs to add */ - function addChainIds(uint64[] memory chainIds) public onlyOwner { + function denyListChainIds(uint64[] memory chainIds) public onlyOwner { for (uint256 i = 0; i < chainIds.length; i++) { - addChainId(chainIds[i]); + denyListChainId(chainIds[i]); } } @@ -36,10 +37,20 @@ contract ChainIdManager is Owned { * @return chainId The next usable Chain ID */ function getNextUsableChainId() public returns (uint64 chainId) { - // Todo: Add a ddos protection: Probably charging gas. But for now, - // the owner can counter ddos attacks by readding unused chainIds - chainId = usableChainIds[usedChainIdCounter]; - require(chainId != 0, "No usable Chain ID available"); - usedChainIdCounter++; + // The burnGas function introduces a cost to use up chainIds. + // There are uint64(2**63=9.223372e+18) chainIds minus the publicly used chainIds available. + // Using all of the chainIds would cost 9.223372e+18 * gasBurnAmount = 9.223372e+24 gas = 6.1489147e+17 blocks = 237226647377 years + burnGas(); + while (deniedChainIds[chainIdCounter]) { + chainIdCounter++; + } + chainId = chainIdCounter; + chainIdCounter++; + } + + function burnGas() public view { + uint256 counter = 0; + uint256 _lowestLimit = gasleft() - gasBurnAmount; + while(gasleft() > _lowestLimit) counter++; } } diff --git a/test/ChainIdManager.t.sol b/test/ChainIdManager.t.sol index eee191ef..19771f01 100644 --- a/test/ChainIdManager.t.sol +++ b/test/ChainIdManager.t.sol @@ -9,90 +9,79 @@ contract ChainIdManagerTest is Test { address public owner = address(0xabc); address public nonOwner = address(0xdef); + uint64 public initialChainId = 10; function setUp() public { - chainIdManager = new ChainIdManager(); + chainIdManager = new ChainIdManager(initialChainId); chainIdManager.transferOwnership(owner); } function testAddChainId() public { vm.prank(owner); - uint64 newChainId = 1; - chainIdManager.addChainId(newChainId); + uint64 newChainId = 10; + chainIdManager.denyListChainId(newChainId); assertEq( - chainIdManager.chainIdCounter(), - 1, - "Chain ID counter did not increment" - ); - assertEq( - chainIdManager.usableChainIds(0), - newChainId, + chainIdManager.getNextUsableChainId(), + newChainId + 1, "Chain ID not correctly added" ); // Attempt to add a ChainId by a non-owner, expect a revert vm.prank(nonOwner); vm.expectRevert(bytes("Caller is not the owner")); // Expect a revert with a specific revert message - chainIdManager.addChainId(2); + chainIdManager.denyListChainId(2); } function testAddChainIds() public { vm.prank(owner); uint64[] memory newChainIds = new uint64[](2); - newChainIds[0] = 1; - newChainIds[1] = 2; - chainIdManager.addChainIds(newChainIds); + newChainIds[0] = 10; + newChainIds[1] = 11; + chainIdManager.denyListChainIds(newChainIds); assertEq( chainIdManager.chainIdCounter(), - 2, + initialChainId, "Chain ID counter did not increment correctly" ); assertEq( - chainIdManager.usableChainIds(0), - newChainIds[0], + chainIdManager.getNextUsableChainId(), + newChainIds[1] + 1, "First Chain ID not correctly added" ); - assertEq( - chainIdManager.usableChainIds(1), - newChainIds[1], - "Second Chain ID not correctly added" - ); } function testGetNextUsableChainId() public { + uint64 firstDeniedChainId = 10; vm.prank(owner); - chainIdManager.addChainId(1); + chainIdManager.denyListChainId(firstDeniedChainId); + uint64 secondDeniedChainId = 11; vm.prank(owner); - chainIdManager.addChainId(2); + chainIdManager.denyListChainId(secondDeniedChainId); uint64 nextChainId = chainIdManager.getNextUsableChainId(); assertEq( nextChainId, - 1, + secondDeniedChainId + 1, "Did not get the correct next usable Chain ID" ); - assertEq( - chainIdManager.usedChainIdCounter(), - 1, - "Used Chain ID counter did not increment" - ); nextChainId = chainIdManager.getNextUsableChainId(); assertEq( nextChainId, - 2, + secondDeniedChainId + 2, "Did not get the correct next usable Chain ID" ); - assertEq( - chainIdManager.usedChainIdCounter(), - 2, - "Used Chain ID counter did not increment" - ); + } - // Assuming that there's no Chain ID left, expect a revert or a return of a default value depending on the contract's behavior - vm.expectRevert(bytes("No usable Chain ID available")); // Or check for a specific return value - chainIdManager.getNextUsableChainId(); + function testCheckGasBurn() public { + uint256 initialGasLeft = gasleft(); + chainIdManager.burnGas(); + uint256 finalGasLeft = gasleft(); + assert( + initialGasLeft - finalGasLeft >= + chainIdManager.gasBurnAmount() + ); } } diff --git a/test/ForkingManager.t.sol b/test/ForkingManager.t.sol index 3b51917a..c68da20e 100644 --- a/test/ForkingManager.t.sol +++ b/test/ForkingManager.t.sol @@ -58,6 +58,7 @@ contract ForkingManagerTest is Test { uint256 public arbitrationFee = 1020; bytes32[32] public depositTree; address public admin = address(0xad); + uint64 public initialChainId = 1; uint64 public firstChainId = 1; uint64 public secondChainId = 2; @@ -131,10 +132,7 @@ contract ForkingManagerTest is Test { ) ); - chainIdManager = address(new ChainIdManager()); - - ChainIdManager(chainIdManager).addChainId(firstChainId); - ChainIdManager(chainIdManager).addChainId(secondChainId); + chainIdManager = address(new ChainIdManager(initialChainId)); globalExitRoot.initialize( address(forkmanager), address(0x0),