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

Add template to set a dispute game implementation #354

Closed
wants to merge 4 commits into from

Conversation

ajsutton
Copy link
Contributor

Description

Adds a template to the fp-recovery directory to set the implementation for a particular game type. Most inputs are loaded from the superchain-registry to make it as easy as possible to adapt for other chains.

@ajsutton ajsutton requested review from a team as code owners October 28, 2024 05:13
@ajsutton ajsutton requested a review from maurelian October 28, 2024 05:13
Copy link
Contributor

@smartcontracts smartcontracts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me, would also like to get review from someone more familiar with writing superchain-ops tasks

tasks/sep/fp-recovery/005-set-game-implementation/.env Outdated Show resolved Hide resolved
GnosisSafe securityCouncilSafe = GnosisSafe(payable(vm.envAddress("COUNCIL_SAFE")));
GnosisSafe fndSafe = GnosisSafe(payable(vm.envAddress("FOUNDATION_SAFE")));
GnosisSafe ownerSafe = GnosisSafe(payable(vm.envAddress("OWNER_SAFE")));
address livenessGuard = 0xc26977310bC89DAee5823C2e2a73195E85382cC7;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Safe's store the guard address in slot 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8 (source), so we can read the guard address from the Safe instead of hardcoding it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea how to do that from within a solidity script. The getGuard method would be ideal but it's internal only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I wish the guard method was public. Form solidity this can be done with vm.load(address target, bytes32 slot): https://github.com/foundry-rs/forge-std/blob/1eea5bae12ae557d589f9f0f0edae2faa47cb262/src/Vm.sol#L588-L589

tasks/sep/fp-recovery/005-set-game-implementation/justfile Outdated Show resolved Hide resolved
SET_GAME_TYPE_SIG="setImplementation(uint32, address)"
ENCODED_CALL=$(cast calldata "${SET_GAME_TYPE_SIG}" "{{gameType}}" "{{implAddr}}")

REGISTRY_DIR="../../../../lib/superchain-registry"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just read the latest registry values directly with curl instead of requiring a local checkout? This way there's no risk the user has an outdated local version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd need to know where to find the SystemConfig or similar starting point to read the values from on-chain though and that would still come from the superchain-registry. It may still be better though since the superchain-registry version here may not have been updated since they deployed fault proofs so wouldn't have the DisputeGameFactory address but would have SystemConfig that we could get the DGF address from.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea perhaps we just make an HTTP request with curl or similar to fetch the system config address, then we can fetch the DGF and other addresses we need from that one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to load the SystemConfig address via a curl to the superchain-registry and the DisputeGameFactory from there.

I could also load the OWNER_SAFE from the superchain-registry ProxyAdminOwner field. From there I can call getOwners() get the address of the foundation and council safes, but I don't know which is which. The NestedSignFromJson.sol file can be updated to not need them, but the parent nested.just needs the env vars set for foundation and council safes so it knows which to use.

I've left it with NestedSignFromJson.sol just generically handling all the nested safe owners so it doesn't need to know the specific foundation and council addresses, and the OWNER_SAFE env var is populated from the superchain-registry. We still need the COUNCIL_SAFE and FOUNDATION_SAFE env vars to make nested.just happy though. Not sure if we should just keep NestedSignFromJson.sol simpler until nested.just doesn't need the council and foundation safe addresses - the new code might be overkill (and it allows foundation safe owners to have no code which wasn't previously allowed).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I don't know which is which

The only way I can think of to infer this is with a check based the threshold and/or number of owners: The foundation is a 5 of 7, and the SC is a 10 of 13.

nested.just may always need some way to know which safe is foundation and which is the council, because signers need to specify which safe they are signing for. Though now that I think of it, in theory we could take the signer address as input, determine which safe has that signer (assuming no duplicate signers across safes), and figure out which safe they need to sign with

I think what you have is ok, and perhaps we just add an assertion that ownerSafe has 2 (different) signers, and that each signer has code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I think this is bit overkill to try to remove the:
COUNCIL_SAFE
FOUNDATION_SAFE
Even if this unlikely that the 1 signer is owner on both safe (for privacy) as the security council member are public.
This will leak the identity of the safe owner.
However, I do think keeping them here for the signers is with clear name like this are explicit and good to keep imo.

ajsutton and others added 3 commits October 30, 2024 09:36
…uperchain-registry.

Dynamically load the safe owners.
Add revert reasons to pre checks.
Comment on lines +28 to +32
// Known EOAs to exclude from safety checks.
address systemConfigOwner; // In registry addresses.
address batchSenderAddress; // In registry genesis-system-configs
address p2pSequencerAddress; // cast call $SystemConfig "unsafeBlockSigner()(address)"
address batchInboxAddress; // In registry yaml.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the slots holding these are never touched and we can delete all these

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed!

_precheckDisputeGameImplementation();
}

function getCodeExceptions() internal view override returns (address[] memory) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering of the relevance of this method getCodeException.
@mds1 do you think this is usually necessary? Because for me, the getAllowedStorageAccess is enough to check if the storage access is modified. Even if the function getCodeException is not specifically for storage access, I think this is adding overhead to the code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The historical context behind this is https://www.notion.so/oplabs/PM-46-Testnet-Contract-Address-in-Mainnet-MCP-L1-Upgrade-Playbook-4ba491167af24959be654d66bbe78d8c?pvs=4

The summary is that we had the Sepolia SuperchainConfig address being written to storage in a mainnet playbook. getCodeExceptions was our solution—most addresses we write to storage are addresses of other contracts in the system, so if an EOA is in storage that is a signal that something might be wrong. However there are a few addresses that we do see in storage with no code, so getCodeExceptions lets us allowlist those

Comment on lines +126 to +128
if (address(currentImpl) == address(0)) {
return;
}
Copy link
Contributor

@Ethnical Ethnical Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Adding a comment above this line. To explain why we return early when the current impl == 0.
Even if this indicating in the doc:
No checks are performed if there is no prior implementation, in which case it is recommended to implement custom checks.


dgfProxy = DisputeGameFactory(stdJson.readAddress(inputJson, "$.transactions[0].to"));
targetGameType = GameType.wrap(uint32(stdJson.readUint(inputJson, "$.transactions[0].contractInputsValues._gameType")));
faultDisputeGame = FaultDisputeGame(stdJson.readAddress(inputJson, "$.transactions[0].contractInputsValues._impl"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Should we rename the faultDisputeGame to TargetFaultDisputeGame to make it clear that it is a target contract?


dgfProxy = DisputeGameFactory(stdJson.readAddress(inputJson, "$.transactions[0].to"));
targetGameType = GameType.wrap(uint32(stdJson.readUint(inputJson, "$.transactions[0].contractInputsValues._gameType")));
faultDisputeGame = FaultDisputeGame(stdJson.readAddress(inputJson, "$.transactions[0].contractInputsValues._impl"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Should we rename the faultDisputeGame to TargetFaultDisputeGame to make it clear that it is a target new impl?

SET_GAME_TYPE_SIG="setImplementation(uint32, address)"
ENCODED_CALL=$(cast calldata "${SET_GAME_TYPE_SIG}" "{{gameType}}" "{{implAddr}}")

REGISTRY_DIR="../../../../lib/superchain-registry"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I think this is bit overkill to try to remove the:
COUNCIL_SAFE
FOUNDATION_SAFE
Even if this unlikely that the 1 signer is owner on both safe (for privacy) as the security council member are public.
This will leak the identity of the safe owner.
However, I do think keeping them here for the signers is with clear name like this are explicit and good to keep imo.

Copy link
Contributor

@Ethnical Ethnical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good to me!
Great works, thanks!
There is few nits that can be discussed but overall looks great!

PROXY_ADMIN_OWNER_ADDR=$(echo "${CONFIG_TOML}" | yq -p toml .addresses.ProxyAdminOwner)

DGF_ADDR=$(cast call "${SC_CONFIG_ADDR}" "disputeGameFactory()(address)")
echo "OWNER_SAFE=${PROXY_ADMIN_OWNER_ADDR}" >> .env
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: When we launch multiples time the script this will append into the .env the OWNER_SAFE.
This shouldn't have any impact, I was thinking of a case when we run the script for the first time and wants to know how it works.
By adding multiples times this parameters this shouldn't not have impact, should we still fix this by checking if the OWNER_SAFE is already written into the .env or this is overkill in your opinion?
here ->

echo "OWNER_SAFE=${PROXY_ADMIN_OWNER_ADDR}" >> .env

CleanShot 2024-11-01 at 14 43 33@2x

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured it wasn't worth it given the last value is the one that gets used anyway. The smarter approach to this in #363 makes it much less likely this will happen as there's a separate prep task and then multiple set-implementation calls can be added to the batch.

@mds1
Copy link
Contributor

mds1 commented Nov 4, 2024

Closing this since #363 says it will replace this PR, but please reopen if we still need this for any reason

@mds1 mds1 closed this Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants