From 0a140b39cdcb806435ee53655aa6d98809d68346 Mon Sep 17 00:00:00 2001 From: josojo Date: Thu, 28 Sep 2023 09:21:36 +0200 Subject: [PATCH] Forkaware structure (#74) On L2, I think many contracts need to wait after a fork for the first message that will come from the bridge. E.g. our adjudication framework would have to wait for a message which court is removed before it starts to forward any new information of the oracles/courts. Then I think the following contract will be quite helpful. --- .solhint.json | 4 +- .../contracts/mixin/InitializeChain.sol | 23 +++++++ test/InitializeChain.sol | 64 +++++++++++++++++++ test/testcontract/InitializeChainWrapper.sol | 15 +++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 development/contracts/mixin/InitializeChain.sol create mode 100644 test/InitializeChain.sol create mode 100644 test/testcontract/InitializeChainWrapper.sol diff --git a/.solhint.json b/.solhint.json index 8f59ba96..a21db1e1 100644 --- a/.solhint.json +++ b/.solhint.json @@ -3,6 +3,8 @@ "plugins": ["prettier"], "rules": { "compiler-version": "off", - "max-states-count": "off" + "max-states-count": "off", + "no-empty-blocks": "off", + "func-visibility": ["warn",{"ignoreConstructors":true}] } } \ No newline at end of file diff --git a/development/contracts/mixin/InitializeChain.sol b/development/contracts/mixin/InitializeChain.sol new file mode 100644 index 00000000..e782dd45 --- /dev/null +++ b/development/contracts/mixin/InitializeChain.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.8.20; + +/** This contract can be inherited form any other contrac that needs to be aware of Forks. +This contract uses the fact that after a fork the chainId changes and thereby detects forks*/ + +contract InitializeChain { + uint256 public chainId; + + modifier onlyChainUninitialized() { + require(chainId != block.chainid, "Not on new fork"); + _; + chainId = block.chainid; + } + + modifier onlyChainInitialized() { + require(chainId == block.chainid, "On new fork"); + _; + } + + constructor() { + chainId = block.chainid; + } +} diff --git a/test/InitializeChain.sol b/test/InitializeChain.sol new file mode 100644 index 00000000..cfc45bd0 --- /dev/null +++ b/test/InitializeChain.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.17; + +import {Test} from "forge-std/Test.sol"; +import {InitializeChainWrapper} from "./testcontract/InitializeChainWrapper.sol"; + +contract InitializeChainWrapperTest is Test { + InitializeChainWrapper public wrapper; + + function setUp() public { + wrapper = new InitializeChainWrapper(); + } + + function testSetChainId() public { + assertEq(wrapper.chainId(), block.chainid); + } + + function testonlyChainUninitialized() public { + // Simulate a chain fork by setting block.chainid to a new value + vm.chainId(2); + wrapper.onlyChainUninitializedWrapper(); // This should pass since it's the first TX after fork + + // Subsequent calls should fail + try wrapper.onlyChainUninitializedWrapper() { + fail( + "Should have reverted because it's not the first TX after fork." + ); + } catch Error(string memory reason) { + assertEq(reason, "Not on new fork"); + } + } + + function testonlyChainInitialized() public { + // On the original chain, this call should succeed + wrapper.onlyChainInitializedWrapper(); + + // Simulate a chain fork by setting block.chainid to a new value + vm.chainId(2); + + // This call should fail since it's the first TX after fork + try wrapper.onlyChainInitializedWrapper() { + fail("Should have reverted because it's the first TX after fork."); + } catch Error(string memory reason) { + assertEq(reason, "On new fork"); + } + + // Call a function with onlyChainUninitialized to update chainId + wrapper.onlyChainUninitializedWrapper(); + + // Now, this call should succeed + wrapper.onlyChainInitializedWrapper(); + } + + function testUpdateChainId() public { + // Initially, chainId is 1 + assertFalse(wrapper.chainId() == 1); + + // Simulate a chain fork by setting block.chainid to a new value + vm.chainId(2); + wrapper.onlyChainUninitializedWrapper(); // This should update the chainId + + assertEq(wrapper.chainId(), 2); + } +} diff --git a/test/testcontract/InitializeChainWrapper.sol b/test/testcontract/InitializeChainWrapper.sol new file mode 100644 index 00000000..ec6a3c47 --- /dev/null +++ b/test/testcontract/InitializeChainWrapper.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.17; + +import {InitializeChain} from "../../development/contracts/mixin/InitializeChain.sol"; + +contract InitializeChainWrapper is InitializeChain { + constructor() InitializeChain() {} + + function onlyChainUninitializedWrapper() public onlyChainUninitialized {} + + function onlyChainInitializedWrapper() + public + onlyChainInitialized + {} +}