Skip to content

Commit

Permalink
contracts: Make L2 genesis script configurable (#10764)
Browse files Browse the repository at this point in the history
* contracts: Make L2 genesis script configurable

* contracts: Read latest fork from deploy config in L2 genesis script

* contracts: add README entry for new L2 genesis script env vars

* address review
  • Loading branch information
sebastianst authored Jun 11, 2024
1 parent 8e9baf5 commit 4826337
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 59 deletions.
12 changes: 6 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,25 +243,25 @@ jobs:
- "packages/contracts-bedrock/tsconfig.tsbuildinfo"
- "packages/contracts-bedrock/tsconfig.build.tsbuildinfo"
- ".devnet/allocs-l1.json"
- ".devnet/allocs-l2.json"
- ".devnet/allocs-l2-delta.json"
- ".devnet/allocs-l2-ecotone.json"
- ".devnet/allocs-l2-fjord.json"
- ".devnet/addresses.json"
- ".devnet-l2oo/allocs-l1.json"
- ".devnet-l2oo/addresses.json"
- ".devnet-l2oo/allocs-l2.json"
- ".devnet-l2oo/allocs-l2-delta.json"
- ".devnet-l2oo/allocs-l2-ecotone.json"
- ".devnet-l2oo/allocs-l2-fjord.json"
- ".devnet-plasma/allocs-l1.json"
- ".devnet-plasma/addresses.json"
- ".devnet-plasma/allocs-l2.json"
- ".devnet-plasma/allocs-l2-delta.json"
- ".devnet-plasma/allocs-l2-ecotone.json"
- ".devnet-plasma/allocs-l2-fjord.json"
- ".devnet-plasma-generic/allocs-l1.json"
- ".devnet-plasma-generic/addresses.json"
- ".devnet-plasma-generic/allocs-l2.json"
- ".devnet-plasma-generic/allocs-l2-delta.json"
- ".devnet-plasma-generic/allocs-l2-ecotone.json"
- ".devnet-plasma-generic/allocs-l2-fjord.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json"
- "packages/contracts-bedrock/deployments/devnetL1"
- notify-failures-on-develop
Expand Down Expand Up @@ -928,9 +928,9 @@ jobs:
name: Load devnet-allocs
command: |
mkdir -p .devnet
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2.json .devnet/allocs-l2.json
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2-delta.json .devnet/allocs-l2-delta.json
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2-ecotone.json .devnet/allocs-l2-ecotone.json
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2-fjord.json .devnet/allocs-l2-fjord.json
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l1.json .devnet/allocs-l1.json
cp /tmp/workspace/.devnet<<parameters.variant>>/addresses.json .devnet/addresses.json
cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json
Expand Down Expand Up @@ -1113,9 +1113,9 @@ jobs:
- persist_to_workspace:
root: .
paths:
- ".devnet/allocs-l2.json"
- ".devnet/allocs-l2-delta.json"
- ".devnet/allocs-l2-ecotone.json"
- ".devnet/allocs-l2-fjord.json"
- ".devnet/allocs-l1.json"
- ".devnet/addresses.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json"
Expand Down
11 changes: 7 additions & 4 deletions bedrock-devnet/devnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

log = logging.getLogger()

# Global constants
FORKS = ["delta", "ecotone", "fjord"]

# Global environment variables
DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true"
DEVNET_L2OO = os.getenv('DEVNET_L2OO') == "true"
Expand Down Expand Up @@ -171,9 +174,9 @@ def devnet_l2_allocs(paths):

# For the previous forks, and the latest fork (default, thus empty prefix),
# move the forge-dumps into place as .devnet allocs.
for suffix in ["-delta", "-ecotone", ""]:
input_path = pjoin(paths.contracts_bedrock_dir, f"state-dump-901{suffix}.json")
output_path = pjoin(paths.devnet_dir, f'allocs-l2{suffix}.json')
for fork in FORKS:
input_path = pjoin(paths.contracts_bedrock_dir, f"state-dump-901-{fork}.json")
output_path = pjoin(paths.devnet_dir, f'allocs-l2-{fork}.json')
shutil.move(src=input_path, dst=output_path)
log.info("Generated L2 allocs: "+output_path)

Expand Down Expand Up @@ -218,7 +221,7 @@ def devnet_deploy(paths):
log.info('L2 genesis and rollup configs already generated.')
else:
log.info('Generating L2 genesis and rollup configs.')
l2_allocs_path = pjoin(paths.devnet_dir, 'allocs-l2.json')
l2_allocs_path = pjoin(paths.devnet_dir, f'allocs-l2-{FORKS[-1]}.json')
if os.path.exists(l2_allocs_path) == False or DEVNET_L2OO == True:
# Also regenerate if L2OO.
# The L2OO flag may affect the L1 deployments addresses, which may affect the L2 genesis.
Expand Down
2 changes: 1 addition & 1 deletion op-chain-ops/genesis/layer_two.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type L2AllocsMode string
const (
L2AllocsDelta L2AllocsMode = "delta"
L2AllocsEcotone L2AllocsMode = "ecotone"
L2AllocsFjord L2AllocsMode = "" // the default in solidity scripting / testing
L2AllocsFjord L2AllocsMode = "fjord"
)

var (
Expand Down
5 changes: 1 addition & 4 deletions op-e2e/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,7 @@ func init() {
}
l2Allocs = make(map[genesis.L2AllocsMode]*genesis.ForgeAllocs)
mustL2Allocs := func(mode genesis.L2AllocsMode) {
name := "allocs-l2"
if mode != "" {
name += "-" + string(mode)
}
name := "allocs-l2-" + string(mode)
allocs, err := genesis.LoadForgeAllocs(filepath.Join(l2AllocsDir, name+".json"))
if err != nil {
panic(err)
Expand Down
12 changes: 12 additions & 0 deletions packages/contracts-bedrock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,18 @@ STATE_DUMP_PATH=<PATH_TO_WRITE_L2_ALLOCS> \
Create or modify a file `<network-name>.json` inside of the [`deploy-config`](./deploy-config/) folder.
Use the env var `DEPLOY_CONFIG_PATH` to use a particular deploy config file at runtime.

The script will read the latest active fork from the deploy config and the L2 genesis allocs generated will be
compatible with this fork. The automatically detected fork can be overwritten by setting the environment variable
`FORK` either to the lower-case fork name (currently `delta`, `ecotone`, or `fjord`) or to `latest`, which will select
the latest fork available (currently `fjord`).

By default, the script will dump the L2 genesis allocs of the detected or selected fork only, to the file at `STATE_DUMP_PATH`.
The optional environment variable `OUTPUT_MODE` allows to modify this behavior by setting it to one of the following values:
* `latest` (default) - only dump the selected fork's allocs.
* `all` - also dump all intermediary fork's allocs. This only works if `STATE_DUMP_PATH` is _not_ set. In this case, all allocs
will be written to files `/state-dump-<fork>.json`. Another path cannot currently be specified for this use case.
* `none` - won't dump any allocs. Only makes sense for internal test usage.

#### Custom Gas Token

The Custom Gas Token feature is a Beta feature of the MIT licensed OP Stack.
Expand Down
90 changes: 90 additions & 0 deletions packages/contracts-bedrock/scripts/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,56 @@ pragma solidity ^0.8.0;

import { Vm, VmSafe } from "forge-std/Vm.sol";

/// @notice Enum representing different ways of outputting genesis allocs.
/// @custom:value NONE No output, used in internal tests.
/// @custom:value LATEST Output allocs only for latest fork.
/// @custom:value ALL Output allocs for all intermediary forks.
enum OutputMode {
NONE,
LATEST,
ALL
}

library OutputModeUtils {
function toString(OutputMode _mode) internal pure returns (string memory) {
if (_mode == OutputMode.NONE) {
return "none";
} else if (_mode == OutputMode.LATEST) {
return "latest";
} else if (_mode == OutputMode.ALL) {
return "all";
} else {
return "unknown";
}
}
}

/// @notice Enum of forks available for selection when generating genesis allocs.
enum Fork {
NONE,
DELTA,
ECOTONE,
FJORD
}

Fork constant LATEST_FORK = Fork.FJORD;

library ForkUtils {
function toString(Fork _fork) internal pure returns (string memory) {
if (_fork == Fork.NONE) {
return "none";
} else if (_fork == Fork.DELTA) {
return "delta";
} else if (_fork == Fork.ECOTONE) {
return "ecotone";
} else if (_fork == Fork.FJORD) {
return "fjord";
} else {
return "unknown";
}
}
}

/// @title Config
/// @notice Contains all env var based config. Add any new env var parsing to this file
/// to ensure that all config is in a single place.
Expand Down Expand Up @@ -67,4 +117,44 @@ library Config {
function drippieOwnerPrivateKey() internal view returns (uint256 _env) {
_env = vm.envUint("DRIPPIE_OWNER_PRIVATE_KEY");
}

/// @notice Returns the OutputMode for genesis allocs generation.
/// It reads the mode from the environment variable OUTPUT_MODE.
/// If it is unset, OutputMode.ALL is returned.
function outputMode() internal view returns (OutputMode) {
string memory modeStr = vm.envOr("OUTPUT_MODE", string("latest"));
bytes32 modeHash = keccak256(bytes(modeStr));
if (modeHash == keccak256(bytes("none"))) {
return OutputMode.NONE;
} else if (modeHash == keccak256(bytes("latest"))) {
return OutputMode.LATEST;
} else if (modeHash == keccak256(bytes("all"))) {
return OutputMode.ALL;
} else {
revert(string.concat("Config: unknown output mode: ", modeStr));
}
}

/// @notice Returns the latest fork to use for genesis allocs generation.
/// It reads the fork from the environment variable FORK. If it is
/// unset, NONE is returned.
/// If set to the special value "latest", the latest fork is returned.
function fork() internal view returns (Fork) {
string memory forkStr = vm.envOr("FORK", string(""));
if (bytes(forkStr).length == 0) {
return Fork.NONE;
}
bytes32 forkHash = keccak256(bytes(forkStr));
if (forkHash == keccak256(bytes("latest"))) {
return LATEST_FORK;
} else if (forkHash == keccak256(bytes("delta"))) {
return Fork.DELTA;
} else if (forkHash == keccak256(bytes("ecotone"))) {
return Fork.ECOTONE;
} else if (forkHash == keccak256(bytes("fjord"))) {
return Fork.FJORD;
} else {
revert(string.concat("Config: unknown fork: ", forkStr));
}
}
}
51 changes: 47 additions & 4 deletions packages/contracts-bedrock/scripts/DeployConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,29 @@ import { stdJson } from "forge-std/StdJson.sol";
import { Executables } from "scripts/Executables.sol";
import { Process } from "scripts/libraries/Process.sol";
import { Chains } from "scripts/Chains.sol";
import { Config, Fork, ForkUtils } from "scripts/Config.sol";

/// @title DeployConfig
/// @notice Represents the configuration required to deploy the system. It is expected
/// to read the file from JSON. A future improvement would be to have fallback
/// values if they are not defined in the JSON themselves.
contract DeployConfig is Script {
using stdJson for string;
using ForkUtils for Fork;

/// @notice Represents an unset offset value, as opposed to 0, which denotes no-offset.
uint256 constant NULL_OFFSET = type(uint256).max;

string internal _json;

address public finalSystemOwner;
address public superchainConfigGuardian;
uint256 public l1ChainID;
uint256 public l2ChainID;
uint256 public l2BlockTime;
uint256 public l2GenesisDeltaTimeOffset;
uint256 public l2GenesisEcotoneTimeOffset;
uint256 public l2GenesisFjordTimeOffset;
uint256 public maxSequencerDrift;
uint256 public sequencerWindowSize;
uint256 public channelTimeout;
Expand Down Expand Up @@ -94,6 +104,11 @@ contract DeployConfig is Script {
l1ChainID = stdJson.readUint(_json, "$.l1ChainID");
l2ChainID = stdJson.readUint(_json, "$.l2ChainID");
l2BlockTime = stdJson.readUint(_json, "$.l2BlockTime");

l2GenesisDeltaTimeOffset = _readOr(_json, "$.l2GenesisDeltaTimeOffset", NULL_OFFSET);
l2GenesisEcotoneTimeOffset = _readOr(_json, "$.l2GenesisEcotoneTimeOffset", NULL_OFFSET);
l2GenesisFjordTimeOffset = _readOr(_json, "$.l2GenesisFjordTimeOffset", NULL_OFFSET);

maxSequencerDrift = stdJson.readUint(_json, "$.maxSequencerDrift");
sequencerWindowSize = stdJson.readUint(_json, "$.sequencerWindowSize");
channelTimeout = stdJson.readUint(_json, "$.channelTimeout");
Expand Down Expand Up @@ -161,6 +176,18 @@ contract DeployConfig is Script {
useInterop = _readOr(_json, "$.useInterop", false);
}

function fork() public view returns (Fork fork_) {
// let env var take precedence
fork_ = Config.fork();
if (fork_ == Fork.NONE) {
// Will revert if no deploy config can be found either.
fork_ = latestGenesisFork();
console.log("DeployConfig: using deploy config fork: %s", fork_.toString());
} else {
console.log("DeployConfig: using env var fork: %s", fork_.toString());
}
}

function l1StartingBlockTag() public returns (bytes32) {
try vm.parseJsonBytes32(_json, "$.l1StartingBlockTag") returns (bytes32 tag) {
return tag;
Expand Down Expand Up @@ -215,6 +242,17 @@ contract DeployConfig is Script {
customGasTokenAddress = _token;
}

function latestGenesisFork() internal view returns (Fork) {
if (l2GenesisFjordTimeOffset == 0) {
return Fork.FJORD;
} else if (l2GenesisEcotoneTimeOffset == 0) {
return Fork.ECOTONE;
} else if (l2GenesisDeltaTimeOffset == 0) {
return Fork.DELTA;
}
revert("DeployConfig: no supported fork active at genesis");
}

function _getBlockByTag(string memory _tag) internal returns (bytes32) {
string[] memory cmd = new string[](3);
cmd[0] = Executables.bash;
Expand All @@ -225,15 +263,20 @@ contract DeployConfig is Script {
}

function _readOr(string memory json, string memory key, bool defaultValue) internal view returns (bool) {
return vm.keyExists(json, key) ? stdJson.readBool(json, key) : defaultValue;
return vm.keyExistsJson(json, key) ? json.readBool(key) : defaultValue;
}

function _readOr(string memory json, string memory key, uint256 defaultValue) internal view returns (uint256) {
return vm.keyExists(json, key) ? stdJson.readUint(json, key) : defaultValue;
return (vm.keyExistsJson(json, key) && !_isNull(json, key)) ? json.readUint(key) : defaultValue;
}

function _readOr(string memory json, string memory key, address defaultValue) internal view returns (address) {
return vm.keyExists(json, key) ? stdJson.readAddress(json, key) : defaultValue;
return vm.keyExistsJson(json, key) ? json.readAddress(key) : defaultValue;
}

function _isNull(string memory json, string memory key) internal pure returns (bool) {
string memory value = json.readString(key);
return (keccak256(bytes(value)) == keccak256(bytes("null")));
}

function _readOr(
Expand All @@ -245,6 +288,6 @@ contract DeployConfig is Script {
view
returns (string memory)
{
return vm.keyExists(json, key) ? stdJson.readString(json, key) : defaultValue;
return vm.keyExists(json, key) ? json.readString(key) : defaultValue;
}
}
Loading

0 comments on commit 4826337

Please sign in to comment.