From 9cfaad5a999e1724d5b2241a2d99b51417d98940 Mon Sep 17 00:00:00 2001 From: Alejandro Ranchal-Pedrosa Date: Wed, 15 Jan 2025 14:13:36 +0100 Subject: [PATCH 1/3] Create and initialize SystemSignerRegistry keeping track of signers --- scripts/foundry/InitializeL1ScrollOwner.s.sol | 26 +++++++- src/L1/L1ScrollMessenger.sol | 2 +- .../system-contract/SystemSignerRegistry.sol | 65 +++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/L1/system-contract/SystemSignerRegistry.sol diff --git a/scripts/foundry/InitializeL1ScrollOwner.s.sol b/scripts/foundry/InitializeL1ScrollOwner.s.sol index b2af8e9d..b6322b29 100644 --- a/scripts/foundry/InitializeL1ScrollOwner.s.sol +++ b/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -21,6 +21,9 @@ import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; +import {SystemSignerRegistry} from "../../src/L1/system-contract/SystemSignerRegistry.sol"; + + // solhint-disable max-states-count // solhint-disable state-visibility // solhint-disable var-name-mixedcase @@ -63,6 +66,9 @@ contract InitializeL1ScrollOwner is Script { address L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ENFORCED_TX_GATEWAY_PROXY_ADDR"); address L1_WHITELIST_ADDR = vm.envAddress("L1_WHITELIST_ADDR"); + address SYSTEM_CONTRACT_ADDR = vm.envAddress("SYSTEM_CONTRACT_ADDR"); + + ScrollOwner owner; function run() external { @@ -81,7 +87,8 @@ contract InitializeL1ScrollOwner is Script { configL1GatewayRouter(); configL1CustomERC20Gateway(); configL1ERC721Gateway(); - configL1ERC1155Gateway(); + configL1ERC1155Gateway(); + configSystemContract(); configL1USDCGateway(); configEnforcedTxGateway(); @@ -272,4 +279,21 @@ contract InitializeL1ScrollOwner is Script { owner.updateAccess(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); owner.updateAccess(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, _selectors, EMERGENCY_MULTISIG_NO_DELAY_ROLE, true); } + + function configSystemContract() internal { + // If we already have deployed it, just do: + SystemSignerRegistry sys = SystemSignerRegistry(SYSTEM_CONTRACT_ADDR); + + // sys has a normal constructor that set the "owner" to `ScrollOwner`. + // Now we want to let the Security Council call `addSigner(...)` with no delay. + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = sys.addSigner.selector; + + owner.updateAccess( + SYSTEM_CONTRACT_ADDR, // the system contract + selectors, // array of function selectors (onlyOwner) + SECURITY_COUNCIL_NO_DELAY_ROLE, + true + ); + } } diff --git a/src/L1/L1ScrollMessenger.sol b/src/L1/L1ScrollMessenger.sol index c70719ff..fd37ecf9 100644 --- a/src/L1/L1ScrollMessenger.sol +++ b/src/L1/L1ScrollMessenger.sol @@ -1,5 +1,4 @@ // SPDX-License-Identifier: MIT - pragma solidity =0.8.24; import {IScrollChain} from "./rollup/IScrollChain.sol"; @@ -10,6 +9,7 @@ import {IScrollMessenger} from "../libraries/IScrollMessenger.sol"; import {ScrollMessengerBase} from "../libraries/ScrollMessengerBase.sol"; import {WithdrawTrieVerifier} from "../libraries/verifier/WithdrawTrieVerifier.sol"; + import {IMessageDropCallback} from "../libraries/callbacks/IMessageDropCallback.sol"; // solhint-disable avoid-low-level-calls diff --git a/src/L1/system-contract/SystemSignerRegistry.sol b/src/L1/system-contract/SystemSignerRegistry.sol new file mode 100644 index 00000000..1ddba319 --- /dev/null +++ b/src/L1/system-contract/SystemSignerRegistry.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @dev Example "SystemSignerRegistry" storing `(startBlock, signer)` pairs. + * The getSigners() function returns parallel arrays for block numbers and addresses, + * + */ +contract SystemSignerRegistry is Ownable { + struct Signer { + uint64 startBlock; + address signer; + } + + Signer[] private signers; + + constructor(address _owner) { + _transferOwnership(_owner); + } + + /** + * @dev Add a (startBlock, signer) pair. Only the owner can do this. + */ + function addSigner(uint64 _startBlock, address _signer) external onlyOwner { + require(_signer != address(0), "Zero address not allowed"); + signers.push(Signer({startBlock: _startBlock, signer: _signer})); + } + + /** + * @dev Remove a signer by matching both startBlock & signer, or just signer (you choose). + * + */ + function removeSigner(uint64 _startBlock, address _signer) external onlyOwner { + uint256 length = signers.length; + for (uint256 i = 0; i < length; i++) { + // If you only want to match signer, ignore _startBlock + if (signers[i].startBlock == _startBlock && signers[i].signer == _signer) { + if (i < length - 1) { + signers[i] = signers[length - 1]; + } + signers.pop(); + return; + } + } + revert("Signer not found"); + } + + /** + * @dev Return two parallel arrays: blockNumbers and signers. + * + */ + function getSigners() external view returns (uint64[] memory, address[] memory) { + uint256 len = signers.length; + uint64[] memory blocks = new uint64[](len); + address[] memory addrs = new address[](len); + + for (uint256 i = 0; i < len; i++) { + blocks[i] = signers[i].startBlock; + addrs[i] = signers[i].signer; + } + return (blocks, addrs); + } +} \ No newline at end of file From 03dc4043e35c98814a49de536920f5dcd163cb7b Mon Sep 17 00:00:00 2001 From: Alejandro Ranchal-Pedrosa Date: Thu, 16 Jan 2025 13:58:33 +0100 Subject: [PATCH 2/3] Keep only curent signer --- scripts/foundry/InitializeL1ScrollOwner.s.sol | 6 +- src/L1/system-contract/SystemConfig.sol | 30 +++++++++ .../system-contract/SystemSignerRegistry.sol | 65 ------------------- 3 files changed, 33 insertions(+), 68 deletions(-) create mode 100644 src/L1/system-contract/SystemConfig.sol delete mode 100644 src/L1/system-contract/SystemSignerRegistry.sol diff --git a/scripts/foundry/InitializeL1ScrollOwner.s.sol b/scripts/foundry/InitializeL1ScrollOwner.s.sol index b6322b29..943f169e 100644 --- a/scripts/foundry/InitializeL1ScrollOwner.s.sol +++ b/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -21,7 +21,7 @@ import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; -import {SystemSignerRegistry} from "../../src/L1/system-contract/SystemSignerRegistry.sol"; +import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; // solhint-disable max-states-count @@ -282,12 +282,12 @@ contract InitializeL1ScrollOwner is Script { function configSystemContract() internal { // If we already have deployed it, just do: - SystemSignerRegistry sys = SystemSignerRegistry(SYSTEM_CONTRACT_ADDR); + SystemConfig sys = SystemConfig(SYSTEM_CONTRACT_ADDR); // sys has a normal constructor that set the "owner" to `ScrollOwner`. // Now we want to let the Security Council call `addSigner(...)` with no delay. bytes4[] memory selectors = new bytes4[](1); - selectors[0] = sys.addSigner.selector; + selectors[0] = sys.updateSigner.selector; owner.updateAccess( SYSTEM_CONTRACT_ADDR, // the system contract diff --git a/src/L1/system-contract/SystemConfig.sol b/src/L1/system-contract/SystemConfig.sol new file mode 100644 index 00000000..a1d995bb --- /dev/null +++ b/src/L1/system-contract/SystemConfig.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract SystemConfig is Ownable { + + address public currentSigner; + + constructor(address _owner) { + _transferOwnership(_owner); + } + + /** + * @dev Update the current signer. + * Only the owner can call this function. + * @param _newSigner The address of the new authorized signer. + */ + function updateSigner(address _newSigner) external onlyOwner { + currentSigner = _newSigner; + } + + /** + * @dev Return the current authorized signer. + * @return The authorized signer address. + */ + function getSigner() external view returns (address) { + return currentSigner; + } +} \ No newline at end of file diff --git a/src/L1/system-contract/SystemSignerRegistry.sol b/src/L1/system-contract/SystemSignerRegistry.sol deleted file mode 100644 index 1ddba319..00000000 --- a/src/L1/system-contract/SystemSignerRegistry.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity =0.8.24; - -import "@openzeppelin/contracts/access/Ownable.sol"; - -/** - * @dev Example "SystemSignerRegistry" storing `(startBlock, signer)` pairs. - * The getSigners() function returns parallel arrays for block numbers and addresses, - * - */ -contract SystemSignerRegistry is Ownable { - struct Signer { - uint64 startBlock; - address signer; - } - - Signer[] private signers; - - constructor(address _owner) { - _transferOwnership(_owner); - } - - /** - * @dev Add a (startBlock, signer) pair. Only the owner can do this. - */ - function addSigner(uint64 _startBlock, address _signer) external onlyOwner { - require(_signer != address(0), "Zero address not allowed"); - signers.push(Signer({startBlock: _startBlock, signer: _signer})); - } - - /** - * @dev Remove a signer by matching both startBlock & signer, or just signer (you choose). - * - */ - function removeSigner(uint64 _startBlock, address _signer) external onlyOwner { - uint256 length = signers.length; - for (uint256 i = 0; i < length; i++) { - // If you only want to match signer, ignore _startBlock - if (signers[i].startBlock == _startBlock && signers[i].signer == _signer) { - if (i < length - 1) { - signers[i] = signers[length - 1]; - } - signers.pop(); - return; - } - } - revert("Signer not found"); - } - - /** - * @dev Return two parallel arrays: blockNumbers and signers. - * - */ - function getSigners() external view returns (uint64[] memory, address[] memory) { - uint256 len = signers.length; - uint64[] memory blocks = new uint64[](len); - address[] memory addrs = new address[](len); - - for (uint256 i = 0; i < len; i++) { - blocks[i] = signers[i].startBlock; - addrs[i] = signers[i].signer; - } - return (blocks, addrs); - } -} \ No newline at end of file From 77c6e586c4e811e13b26e32806375b3bcb5f1f58 Mon Sep 17 00:00:00 2001 From: Alejandro Ranchal-Pedrosa Date: Mon, 20 Jan 2025 19:33:15 +0100 Subject: [PATCH 3/3] Deploy and Initialize L1 System Config --- scripts/foundry/DeployL1SystemConfig.s.sol | 24 ++++++++++ scripts/foundry/InitializeL1ScrollOwner.s.sol | 22 +-------- scripts/foundry/InitializeL1SystemConfig.sol | 47 +++++++++++++++++++ 3 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 scripts/foundry/DeployL1SystemConfig.s.sol create mode 100644 scripts/foundry/InitializeL1SystemConfig.sol diff --git a/scripts/foundry/DeployL1SystemConfig.s.sol b/scripts/foundry/DeployL1SystemConfig.s.sol new file mode 100644 index 00000000..21f96d69 --- /dev/null +++ b/scripts/foundry/DeployL1SystemConfig.s.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.24; + +import {Script} from "forge-std/Script.sol"; +import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; // adjust the relative path as necessary +import {console} from "forge-std/console.sol"; + +contract DeployL1SystemConfig is Script { + function run() external { + // Retrieve the deployer private key from environment variables + uint256 deployerKey = vm.envUint("L1_DEPLOYER_PRIVATE_KEY"); + // Read the intended owner from an environment variable (for example, L1_SCROLL_OWNER_ADDR) + address ownerAddr = vm.envAddress("L1_SCROLL_OWNER_ADDR"); + + vm.startBroadcast(deployerKey); + + // Deploy the SystemConfig contract with the specified owner. + SystemConfig sysConfig = new SystemConfig(ownerAddr); + + console.log("Deployed SystemConfig at address:", address(sysConfig)); + + vm.stopBroadcast(); + } +} \ No newline at end of file diff --git a/scripts/foundry/InitializeL1ScrollOwner.s.sol b/scripts/foundry/InitializeL1ScrollOwner.s.sol index 943f169e..2f060477 100644 --- a/scripts/foundry/InitializeL1ScrollOwner.s.sol +++ b/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -21,8 +21,6 @@ import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; -import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; - // solhint-disable max-states-count // solhint-disable state-visibility @@ -88,8 +86,7 @@ contract InitializeL1ScrollOwner is Script { configL1CustomERC20Gateway(); configL1ERC721Gateway(); configL1ERC1155Gateway(); - configSystemContract(); - + configL1USDCGateway(); configEnforcedTxGateway(); @@ -279,21 +276,4 @@ contract InitializeL1ScrollOwner is Script { owner.updateAccess(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); owner.updateAccess(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, _selectors, EMERGENCY_MULTISIG_NO_DELAY_ROLE, true); } - - function configSystemContract() internal { - // If we already have deployed it, just do: - SystemConfig sys = SystemConfig(SYSTEM_CONTRACT_ADDR); - - // sys has a normal constructor that set the "owner" to `ScrollOwner`. - // Now we want to let the Security Council call `addSigner(...)` with no delay. - bytes4[] memory selectors = new bytes4[](1); - selectors[0] = sys.updateSigner.selector; - - owner.updateAccess( - SYSTEM_CONTRACT_ADDR, // the system contract - selectors, // array of function selectors (onlyOwner) - SECURITY_COUNCIL_NO_DELAY_ROLE, - true - ); - } } diff --git a/scripts/foundry/InitializeL1SystemConfig.sol b/scripts/foundry/InitializeL1SystemConfig.sol new file mode 100644 index 00000000..c6e9c121 --- /dev/null +++ b/scripts/foundry/InitializeL1SystemConfig.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.24; + +import { Script } from "forge-std/Script.sol"; +import { SystemConfig } from "../../src/L1/system-contract/SystemConfig.sol"; +import { ScrollOwner } from "../../src/misc/ScrollOwner.sol"; // Adjust this path as needed + +/** + * @title InitializeL1SystemConfig + * @notice Configures the deployed SystemConfig contract. + * This script grants the Security Council (as defined by L1_SECURITY_COUNCIL_ADDR) + * access to call updateSigner() on the SystemConfig contract with no delay. + */ +contract InitializeL1SystemConfig is Script { + function run() external { + // Retrieve required environment variables. + uint256 deployerKey = vm.envUint("L1_DEPLOYER_PRIVATE_KEY"); + address systemConfigAddr = vm.envAddress("SYSTEM_CONTRACT_ADDR"); + address securityCouncilAddr = vm.envAddress("L1_SECURITY_COUNCIL_ADDR"); + address scrollOwnerAddr = vm.envAddress("L1_SCROLL_OWNER_ADDR"); + + // Compute the role hash for the Security Council with no delay. + bytes32 SECURITY_COUNCIL_NO_DELAY_ROLE = keccak256("SECURITY_COUNCIL_NO_DELAY_ROLE"); + + vm.startBroadcast(deployerKey); + + // Instantiate the ScrollOwner contract instance which manages access control. + ScrollOwner owner = ScrollOwner(payable(scrollOwnerAddr)); + // Instantiate the already-deployed SystemConfig contract. + SystemConfig sys = SystemConfig(systemConfigAddr); + + // Prepare a single-element array containing the function selector for updateSigner. + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = sys.updateSigner.selector; + + // Grant the SECURITY_COUNCIL_NO_DELAY_ROLE permission on SystemConfig, + // so that the Security Council address can call updateSigner() with no delay. + owner.updateAccess( + systemConfigAddr, // Address of the SystemConfig contract. + selectors, // The function selectors (only updateSigner here). + SECURITY_COUNCIL_NO_DELAY_ROLE, + true // Grant access. + ); + + vm.stopBroadcast(); + } +} \ No newline at end of file