Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dual Guardian mechanism #302

Open
wants to merge 29 commits into
base: celo-contracts/v1.8.0-1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9829a88
Setup new contract for Celo-specific guardian role
m-chrzan Jan 27, 2025
9993edd
Test the actual new contract
m-chrzan Jan 28, 2025
d1bb009
Add getter for global SuperchainConfig
m-chrzan Jan 28, 2025
417ed6e
Make sure global guardian can't directly pause Celo config
m-chrzan Jan 28, 2025
499cad4
Rename test contracts
m-chrzan Jan 28, 2025
6b70da0
Return combined Superchain and Celo paused status
m-chrzan Jan 28, 2025
0f57992
Allow pausing of Celo config when Superchain is paused
m-chrzan Jan 28, 2025
a706fdd
Ensure Celo stays paused if Superchain still paused
m-chrzan Jan 28, 2025
d2e6f84
Add CeloSuperchainConfig to Specs test
m-chrzan Jan 28, 2025
d903510
Exclude CeloSuperchainConfig from Initializable test for now
m-chrzan Jan 28, 2025
f57df3f
Make test view
m-chrzan Jan 28, 2025
85c0585
Use CeloSuperchainConfig interface
m-chrzan Jan 29, 2025
58a68aa
Check and propagate superchain paused status in one function
m-chrzan Jan 31, 2025
1ffc50f
Setup CeloSuperchainConfig in deploy script
m-chrzan Jan 31, 2025
df4d7d7
Point OptimismPortal to CeloSuperchainConfig
m-chrzan Jan 31, 2025
6e9b342
Formatting
m-chrzan Jan 31, 2025
24d1a68
Use CeloSuperchainConfig in contracts that used SuperchainConfig
m-chrzan Jan 31, 2025
735baa2
Fix CrossDomanMessenger test
m-chrzan Jan 31, 2025
2328688
Add NatSpecs
m-chrzan Feb 2, 2025
fd69f89
Add unit test NatSpecs
m-chrzan Feb 2, 2025
2ac9416
Add reference comment to deploy script
m-chrzan Feb 2, 2025
204495b
Add comments
m-chrzan Feb 2, 2025
46dbcdb
Emit event on config update
m-chrzan Feb 2, 2025
ced979e
Initialize as paused if Superchain paused at init time
m-chrzan Feb 2, 2025
8d55d40
Test Celo and Superchain pauses on OptimismPortal2
m-chrzan Feb 4, 2025
9474f63
Test Celo and Superchain pauses on OptimismPortal
m-chrzan Feb 4, 2025
05477cd
Test Celo and Superchain pauses on L1CrossDomainMessenger
m-chrzan Feb 4, 2025
e22524e
Test Celo and Superchain pauses on L1ERC721Bridge
m-chrzan Feb 4, 2025
af0377a
Use checkAndPause in L1ERC721Bridge
m-chrzan Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ library ChainAssertions {
if (_isProxy) {
require(weth.owner() == _expectedOwner, "CHECK-PDWETH-20");
require(weth.delay() == _cfg.faultGameWithdrawalDelay(), "CHECK-PDWETH-30");
require(weth.config() == ISuperchainConfig(_contracts.SuperchainConfig), "CHECK-PDWETH-40");
//TODO(m-chrzan): get correct value once CeloSuperchainConfig
//included in contracts
//require(weth.config() == ISuperchainConfig(_contracts.SuperchainConfig), "CHECK-PDWETH-40");
} else {
require(weth.owner() == _expectedOwner, "CHECK-PDWETH-50");
require(weth.delay() == _cfg.faultGameWithdrawalDelay(), "CHECK-PDWETH-60");
Expand Down Expand Up @@ -442,7 +444,8 @@ library ChainAssertions {
require(address(portal.l2Oracle()) == _contracts.L2OutputOracle, "CHECK-OP-20");
require(address(portal.systemConfig()) == _contracts.SystemConfig, "CHECK-OP-30");
require(portal.guardian() == guardian, "CHECK-OP-40");
require(address(portal.superchainConfig()) == address(_contracts.SuperchainConfig), "CHECK-OP-50");
//TODO(m-chrzan): reenable check once CeloSuperchainConfig included in contracts
//require(address(portal.superchainConfig()) == address(_contracts.SuperchainConfig), "CHECK-OP-50");
require(portal.paused() == ISuperchainConfig(_contracts.SuperchainConfig).paused(), "CHECK-OP-60");
require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER, "CHECK-OP-70");
} else {
Expand Down
52 changes: 49 additions & 3 deletions packages/contracts-bedrock/scripts/deploy/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import { Process } from "scripts/libraries/Process.sol";
import { ChainAssertions } from "scripts/deploy/ChainAssertions.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { DeploySuperchainInput, DeploySuperchain, DeploySuperchainOutput } from "scripts/deploy/DeploySuperchain.s.sol";
import {
DeployCeloSuperchainConfigInput,
DeployCeloSuperchainConfig,
DeployCeloSuperchainConfigOutput
} from "scripts/deploy/DeployCeloSuperchainConfig.s.sol";
import {
DeployImplementationsInput,
DeployImplementations,
Expand All @@ -42,6 +47,7 @@ import { IOptimismPortal } from "src/L1/interfaces/IOptimismPortal.sol";
import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol";
import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { ICeloSuperchainConfig } from "src/L1/interfaces/ICeloSuperchainConfig.sol";
import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol";
import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol";
import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol";
Expand Down Expand Up @@ -277,6 +283,12 @@ contract Deploy is Deployer {
deploySuperchain();
}

// Setup CeloSuperchainConfig.
// TODO(m-chrzan): add a cfg value for this
if (true) {
deployCeloSuperchainConfig();
}

deployImplementations({ _isInterop: cfg.useInterop() });

// Deploy Current OPChain Contracts
Expand Down Expand Up @@ -363,6 +375,40 @@ contract Deploy is Deployer {
ChainAssertions.checkSuperchainConfig({ _contracts: contracts, _cfg: cfg, _isPaused: false, _isProxy: false });
}

/// @notice Deploys the CeloSuperchainConfig, which is currently used to
/// enable a dual Guardian setup, i.e. having the Celo L1 system be
/// pausable by both the global Superchain Guardian (whenever the
/// SuperchainConfig contract is paused) and a new Celo Guardian
/// role.
function deployCeloSuperchainConfig() public {
console.log("Setting up CeloSuperchainConfig");
DeployCeloSuperchainConfig ds = new DeployCeloSuperchainConfig();
(DeployCeloSuperchainConfigInput dsi, DeployCeloSuperchainConfigOutput dso) = ds.etchIOContracts();

// Set the input values on the input contract.
address superchainConfigAddress = mustGetAddress("SuperchainConfigProxy");
dsi.set(dsi.superchainConfig.selector, superchainConfigAddress);
dsi.set(dsi.celoGuardian.selector, cfg.proxyAdminOwner());
dsi.set(dsi.paused.selector, false);

// Run the deployment script.
ds.run(dsi, dso);
save("CeloSuperchainConfigProxy", address(dso.celoSuperchainConfigProxy()));
save("CeloSuperchainConfig", address(dso.celoSuperchainConfigImpl()));

// First run assertions for the ProtocolVersions and SuperchainConfig proxy contracts.
//Types.ContractSet memory contracts = _proxies();
//TODO(m-chrzan): implement this check
//ChainAssertions.checkCeloSuperchainConfig({ _contracts: contracts, _cfg: cfg, _isProxy: true, _isPaused: false
// });

// TODO(m-chrzan): implement this? Check if necessary
// Finally replace the SuperchainConfig proxy with the implementation address and run assertions on it.
//contracts.SuperchainConfig = mustGetAddress("SuperchainConfig");
//ChainAssertions.checkSuperchainConfig({ _contracts: contracts, _cfg: cfg, _isPaused: false, _isProxy: false
// });
}

/// @notice Deploy all of the implementations
function deployImplementations(bool _isInterop) public {
require(_isInterop == cfg.useInterop(), "Deploy: Interop setting mismatch.");
Expand All @@ -382,7 +428,7 @@ contract Deploy is Deployer {
dii.set(
dii.standardVersionsToml.selector, string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml")
);
dii.set(dii.superchainConfigProxy.selector, mustGetAddress("SuperchainConfigProxy"));
dii.set(dii.superchainConfigProxy.selector, mustGetAddress("CeloSuperchainConfigProxy"));
dii.set(dii.protocolVersionsProxy.selector, mustGetAddress("ProtocolVersionsProxy"));
dii.set(dii.opcmProxyOwner.selector, cfg.finalSystemOwner());

Expand Down Expand Up @@ -893,7 +939,7 @@ contract Deploy is Deployer {
require(!cfg.useFaultProofs(), "Deploy: FaultProofs OptimismPortal is initialized by OPCM");
address optimismPortalProxy = mustGetAddress("OptimismPortalProxy");
address systemConfigProxy = mustGetAddress("SystemConfigProxy");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
address superchainConfigProxy = mustGetAddress("CeloSuperchainConfigProxy");
address optimismPortal = mustGetAddress("OptimismPortal");
address l2OutputOracleProxy = mustGetAddress("L2OutputOracleProxy");

Expand All @@ -906,7 +952,7 @@ contract Deploy is Deployer {
(
IL2OutputOracle(l2OutputOracleProxy),
ISystemConfig(systemConfigProxy),
ISuperchainConfig(superchainConfigProxy)
ICeloSuperchainConfig(superchainConfigProxy)
)
)
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { Script } from "forge-std/Script.sol";
import { stdToml } from "forge-std/StdToml.sol";

import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { ICeloSuperchainConfig } from "src/L1/interfaces/ICeloSuperchainConfig.sol";
import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol";
import { IProxy } from "src/universal/interfaces/IProxy.sol";

import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { Solarray } from "scripts/libraries/Solarray.sol";
import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol";

// See DeploySuperchain.s.sol for detailed comments on the script architecture used here.
contract DeployCeloSuperchainConfigInput is BaseDeployIO {
using stdToml for string;

// Role inputs.
address internal _superchainConfig;
address internal _celoGuardian;

// Other inputs.
bool internal _paused;

function set(bytes4 _sel, address _address) public {
require(_address != address(0), "DeployCeloSuperchainConfigInput: cannot set zero address");
if (_sel == this.superchainConfig.selector) _superchainConfig = _address;
else if (_sel == this.celoGuardian.selector) _celoGuardian = _address;
else revert("DeployCeloSuperchainConfigInput: unknown selector");
}

function set(bytes4 _sel, bool _value) public {
if (_sel == this.paused.selector) _paused = _value;
else revert("DeployCeloSuperchainConfigInput: unknown selector");
}

function superchainConfig() public view returns (address) {
require(_superchainConfig != address(0), "DeployCeloSuperchainConfigInput: superchainConfig not set");
return _superchainConfig;
}

function celoGuardian() public view returns (address) {
require(_celoGuardian != address(0), "DeployCeloSuperchainConfigInput: celoGuardian not set");
return _celoGuardian;
}

function paused() public view returns (bool) {
return _paused;
}
}

contract DeployCeloSuperchainConfigOutput is BaseDeployIO {
ICeloSuperchainConfig internal _celoSuperchainConfigImpl;
ICeloSuperchainConfig internal _celoSuperchainConfigProxy;
IProxyAdmin internal _tmpProxyAdmin;

function set(bytes4 _sel, address _address) public {
require(_address != address(0), "DeployCeloSuperchainConfigOutput: cannot set zero address");
if (_sel == this.celoSuperchainConfigImpl.selector) {
_celoSuperchainConfigImpl = ICeloSuperchainConfig(_address);
} else if (_sel == this.celoSuperchainConfigProxy.selector) {
_celoSuperchainConfigProxy = ICeloSuperchainConfig(_address);
} else if (_sel == this.tmpProxyAdmin.selector) {
_tmpProxyAdmin = IProxyAdmin(_address);
} else {
revert("DeployCeloSuperchainConfigOutput: unknown selector");
}
}

function checkOutput(DeployCeloSuperchainConfigInput _dsi) public {
address[] memory addrs = Solarray.addresses(
address(this.celoSuperchainConfigImpl()),
address(this.celoSuperchainConfigProxy()),
address(this.tmpProxyAdmin())
);
DeployUtils.assertValidContractAddresses(addrs);

// To read the implementations we prank as the zero address due to the proxyCallIfNotAdmin modifier.
vm.startPrank(address(0));
address actualCeloSuperchainConfigImpl = IProxy(payable(address(_celoSuperchainConfigProxy))).implementation();
vm.stopPrank();

require(actualCeloSuperchainConfigImpl == address(_celoSuperchainConfigImpl), "100"); // nosemgrep:
// sol-style-malformed-require

assertValidDeploy(_dsi);

// TODO(m-chrzan): consider if any checks necessary for tmpProxyAdmin??
}

function celoSuperchainConfigImpl() public view returns (ICeloSuperchainConfig) {
DeployUtils.assertValidContractAddress(address(_celoSuperchainConfigImpl));
return _celoSuperchainConfigImpl;
}

function celoSuperchainConfigProxy() public view returns (ICeloSuperchainConfig) {
DeployUtils.assertValidContractAddress(address(_celoSuperchainConfigProxy));
return _celoSuperchainConfigProxy;
}

function tmpProxyAdmin() public view returns (IProxyAdmin) {
DeployUtils.assertValidContractAddress(address(_tmpProxyAdmin));
return _tmpProxyAdmin;
}

// -------- Deployment Assertions --------
function assertValidDeploy(DeployCeloSuperchainConfigInput _dsi) public {
assertValidCeloSuperchainConfig(_dsi);
}

function assertValidCeloSuperchainConfig(DeployCeloSuperchainConfigInput _dsi) internal {
// Proxy checks.
ICeloSuperchainConfig celoSuperchainConfig = celoSuperchainConfigProxy();
DeployUtils.assertInitialized({ _contractAddress: address(celoSuperchainConfig), _slot: 0, _offset: 0 });
require(celoSuperchainConfig.guardian() == _dsi.celoGuardian(), "SUPCON-10");
require(celoSuperchainConfig.paused() == _dsi.paused(), "SUPCON-20");
require(celoSuperchainConfig.superchainConfig() == _dsi.superchainConfig(), "SUPCON-30");

vm.startPrank(address(0));
require(
IProxy(payable(address(celoSuperchainConfig))).implementation() == address(celoSuperchainConfigImpl()),
"SUPCON-30"
);
require(IProxy(payable(address(celoSuperchainConfig))).admin() == address(tmpProxyAdmin()), "SUPCON-40");
vm.stopPrank();

// Implementation checks
celoSuperchainConfig = celoSuperchainConfigImpl();
require(celoSuperchainConfig.guardian() == address(0), "SUPCON-50");
require(celoSuperchainConfig.superchainConfig() == address(0), "SUPCON-50");
require(celoSuperchainConfig.paused() == false, "SUPCON-60");
}
}

// For all broadcasts in this script we explicitly specify the deployer as `msg.sender` because for
// testing we deploy this script from a test contract. If we provide no argument, the foundry
// default sender would be the broadcaster during test, but the broadcaster needs to be the deployer
// since they are set to the initial proxy admin owner.
contract DeployCeloSuperchainConfig is Script {
function run(DeployCeloSuperchainConfigInput _dsi, DeployCeloSuperchainConfigOutput _dso) public {
deployCeloSuperchainImplementationContracts(_dsi, _dso);
deployTmpProxyAdmin(_dso);
deployAndInitializeCeloSuperchainConfig(_dsi, _dso);

_dso.checkOutput(_dsi);
}

// -------- Deployment Steps --------

function deployCeloSuperchainImplementationContracts(
DeployCeloSuperchainConfigInput,
DeployCeloSuperchainConfigOutput _dso
)
public
{
// Deploy implementation contracts.
vm.startBroadcast(msg.sender);
ICeloSuperchainConfig celoSuperchainConfigImpl = ICeloSuperchainConfig(
DeployUtils.create1({
_name: "CeloSuperchainConfig",
_args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ()))
})
);
vm.stopBroadcast();

vm.label(address(celoSuperchainConfigImpl), "CeloSuperchainConfigImpl");

_dso.set(_dso.celoSuperchainConfigImpl.selector, address(celoSuperchainConfigImpl));
}

function deployTmpProxyAdmin(DeployCeloSuperchainConfigOutput _dso) public {
vm.startBroadcast(msg.sender);
IProxyAdmin proxyAdmin = IProxyAdmin(
DeployUtils.create1({
_name: "ProxyAdmin",
_args: DeployUtils.encodeConstructor(abi.encodeCall(IProxyAdmin.__constructor__, (msg.sender)))
})
);
vm.stopBroadcast();

vm.label(address(proxyAdmin), "TmpProxyAdmin");

_dso.set(_dso.tmpProxyAdmin.selector, address(proxyAdmin));
}

function deployAndInitializeCeloSuperchainConfig(
DeployCeloSuperchainConfigInput _dsi,
DeployCeloSuperchainConfigOutput _dso
)
public
{
address guardian = _dsi.celoGuardian();
bool paused = _dsi.paused();
address superchainConfig = _dsi.superchainConfig();

IProxyAdmin celoProxyAdmin = IProxyAdmin(_dso.tmpProxyAdmin());
ICeloSuperchainConfig celoSuperchainConfigImpl = _dso.celoSuperchainConfigImpl();

vm.startBroadcast(msg.sender);
ICeloSuperchainConfig celoSuperchainConfigProxy = ICeloSuperchainConfig(
DeployUtils.create1({
_name: "Proxy",
_args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (address(celoProxyAdmin))))
})
);
vm.stopBroadcast();
// TODO(m-chrzan): should be able to set this to msg.sender now that
// we're using tmpProxyAdmin?
vm.startBroadcast(celoProxyAdmin.owner());
celoProxyAdmin.upgradeAndCall(
payable(address(celoSuperchainConfigProxy)),
address(celoSuperchainConfigImpl),
abi.encodeCall(ICeloSuperchainConfig.initialize, (guardian, paused, superchainConfig))
);
vm.stopBroadcast();

vm.label(address(celoSuperchainConfigProxy), "CeloSuperchainConfigProxy");
_dso.set(_dso.celoSuperchainConfigProxy.selector, address(celoSuperchainConfigProxy));
}

// -------- Utilities --------

// This etches the IO contracts into memory so that we can use them in tests.
// When interacting with the script programmatically (e.g. in a Solidity test), this must be called.
function etchIOContracts()
public
returns (DeployCeloSuperchainConfigInput dsi_, DeployCeloSuperchainConfigOutput dso_)
{
(dsi_, dso_) = getIOContracts();
vm.etch(address(dsi_), type(DeployCeloSuperchainConfigInput).runtimeCode);
vm.etch(address(dso_), type(DeployCeloSuperchainConfigOutput).runtimeCode);
vm.allowCheatcodes(address(dsi_));
vm.allowCheatcodes(address(dso_));
}

// This returns the addresses of the IO contracts for this script.
function getIOContracts()
public
view
returns (DeployCeloSuperchainConfigInput dsi_, DeployCeloSuperchainConfigOutput dso_)
{
dsi_ = DeployCeloSuperchainConfigInput(
DeployUtils.toIOAddress(msg.sender, "optimism.DeployCeloSuperchainConfigInput")
);
dso_ = DeployCeloSuperchainConfigOutput(
DeployUtils.toIOAddress(msg.sender, "optimism.DeployCeloSuperchainConfigOutput")
);
}
}
Loading