Skip to content

Commit 51a62a3

Browse files
sebastianstrdovgan
authored andcommitted
contracts: Make L2 genesis script configurable (ethereum-optimism#10764)
* 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
1 parent affa52c commit 51a62a3

File tree

11 files changed

+207
-59
lines changed

11 files changed

+207
-59
lines changed

.circleci/config.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -243,25 +243,25 @@ jobs:
243243
- "packages/contracts-bedrock/tsconfig.tsbuildinfo"
244244
- "packages/contracts-bedrock/tsconfig.build.tsbuildinfo"
245245
- ".devnet/allocs-l1.json"
246-
- ".devnet/allocs-l2.json"
247246
- ".devnet/allocs-l2-delta.json"
248247
- ".devnet/allocs-l2-ecotone.json"
248+
- ".devnet/allocs-l2-fjord.json"
249249
- ".devnet/addresses.json"
250250
- ".devnet-l2oo/allocs-l1.json"
251251
- ".devnet-l2oo/addresses.json"
252-
- ".devnet-l2oo/allocs-l2.json"
253252
- ".devnet-l2oo/allocs-l2-delta.json"
254253
- ".devnet-l2oo/allocs-l2-ecotone.json"
254+
- ".devnet-l2oo/allocs-l2-fjord.json"
255255
- ".devnet-plasma/allocs-l1.json"
256256
- ".devnet-plasma/addresses.json"
257-
- ".devnet-plasma/allocs-l2.json"
258257
- ".devnet-plasma/allocs-l2-delta.json"
259258
- ".devnet-plasma/allocs-l2-ecotone.json"
259+
- ".devnet-plasma/allocs-l2-fjord.json"
260260
- ".devnet-plasma-generic/allocs-l1.json"
261261
- ".devnet-plasma-generic/addresses.json"
262-
- ".devnet-plasma-generic/allocs-l2.json"
263262
- ".devnet-plasma-generic/allocs-l2-delta.json"
264263
- ".devnet-plasma-generic/allocs-l2-ecotone.json"
264+
- ".devnet-plasma-generic/allocs-l2-fjord.json"
265265
- "packages/contracts-bedrock/deploy-config/devnetL1.json"
266266
- "packages/contracts-bedrock/deployments/devnetL1"
267267
- notify-failures-on-develop
@@ -928,9 +928,9 @@ jobs:
928928
name: Load devnet-allocs
929929
command: |
930930
mkdir -p .devnet
931-
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2.json .devnet/allocs-l2.json
932931
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2-delta.json .devnet/allocs-l2-delta.json
933932
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2-ecotone.json .devnet/allocs-l2-ecotone.json
933+
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l2-fjord.json .devnet/allocs-l2-fjord.json
934934
cp /tmp/workspace/.devnet<<parameters.variant>>/allocs-l1.json .devnet/allocs-l1.json
935935
cp /tmp/workspace/.devnet<<parameters.variant>>/addresses.json .devnet/addresses.json
936936
cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json
@@ -1113,9 +1113,9 @@ jobs:
11131113
- persist_to_workspace:
11141114
root: .
11151115
paths:
1116-
- ".devnet/allocs-l2.json"
11171116
- ".devnet/allocs-l2-delta.json"
11181117
- ".devnet/allocs-l2-ecotone.json"
1118+
- ".devnet/allocs-l2-fjord.json"
11191119
- ".devnet/allocs-l1.json"
11201120
- ".devnet/addresses.json"
11211121
- "packages/contracts-bedrock/deploy-config/devnetL1.json"

bedrock-devnet/devnet/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
log = logging.getLogger()
2828

29+
# Global constants
30+
FORKS = ["delta", "ecotone", "fjord"]
31+
2932
# Global environment variables
3033
DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true"
3134
DEVNET_L2OO = os.getenv('DEVNET_L2OO') == "true"
@@ -171,9 +174,9 @@ def devnet_l2_allocs(paths):
171174

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

@@ -218,7 +221,7 @@ def devnet_deploy(paths):
218221
log.info('L2 genesis and rollup configs already generated.')
219222
else:
220223
log.info('Generating L2 genesis and rollup configs.')
221-
l2_allocs_path = pjoin(paths.devnet_dir, 'allocs-l2.json')
224+
l2_allocs_path = pjoin(paths.devnet_dir, f'allocs-l2-{FORKS[-1]}.json')
222225
if os.path.exists(l2_allocs_path) == False or DEVNET_L2OO == True:
223226
# Also regenerate if L2OO.
224227
# The L2OO flag may affect the L1 deployments addresses, which may affect the L2 genesis.

op-chain-ops/genesis/layer_two.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type L2AllocsMode string
2424
const (
2525
L2AllocsDelta L2AllocsMode = "delta"
2626
L2AllocsEcotone L2AllocsMode = "ecotone"
27-
L2AllocsFjord L2AllocsMode = "" // the default in solidity scripting / testing
27+
L2AllocsFjord L2AllocsMode = "fjord"
2828
)
2929

3030
var (

op-e2e/config/init.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,7 @@ func init() {
113113
}
114114
l2Allocs = make(map[genesis.L2AllocsMode]*genesis.ForgeAllocs)
115115
mustL2Allocs := func(mode genesis.L2AllocsMode) {
116-
name := "allocs-l2"
117-
if mode != "" {
118-
name += "-" + string(mode)
119-
}
116+
name := "allocs-l2-" + string(mode)
120117
allocs, err := genesis.LoadForgeAllocs(filepath.Join(l2AllocsDir, name+".json"))
121118
if err != nil {
122119
panic(err)

packages/contracts-bedrock/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,18 @@ STATE_DUMP_PATH=<PATH_TO_WRITE_L2_ALLOCS> \
319319
Create or modify a file `<network-name>.json` inside of the [`deploy-config`](./deploy-config/) folder.
320320
Use the env var `DEPLOY_CONFIG_PATH` to use a particular deploy config file at runtime.
321321

322+
The script will read the latest active fork from the deploy config and the L2 genesis allocs generated will be
323+
compatible with this fork. The automatically detected fork can be overwritten by setting the environment variable
324+
`FORK` either to the lower-case fork name (currently `delta`, `ecotone`, or `fjord`) or to `latest`, which will select
325+
the latest fork available (currently `fjord`).
326+
327+
By default, the script will dump the L2 genesis allocs of the detected or selected fork only, to the file at `STATE_DUMP_PATH`.
328+
The optional environment variable `OUTPUT_MODE` allows to modify this behavior by setting it to one of the following values:
329+
* `latest` (default) - only dump the selected fork's allocs.
330+
* `all` - also dump all intermediary fork's allocs. This only works if `STATE_DUMP_PATH` is _not_ set. In this case, all allocs
331+
will be written to files `/state-dump-<fork>.json`. Another path cannot currently be specified for this use case.
332+
* `none` - won't dump any allocs. Only makes sense for internal test usage.
333+
322334
#### Custom Gas Token
323335

324336
The Custom Gas Token feature is a Beta feature of the MIT licensed OP Stack.

packages/contracts-bedrock/scripts/Config.sol

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,56 @@ pragma solidity ^0.8.0;
33

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

6+
/// @notice Enum representing different ways of outputting genesis allocs.
7+
/// @custom:value NONE No output, used in internal tests.
8+
/// @custom:value LATEST Output allocs only for latest fork.
9+
/// @custom:value ALL Output allocs for all intermediary forks.
10+
enum OutputMode {
11+
NONE,
12+
LATEST,
13+
ALL
14+
}
15+
16+
library OutputModeUtils {
17+
function toString(OutputMode _mode) internal pure returns (string memory) {
18+
if (_mode == OutputMode.NONE) {
19+
return "none";
20+
} else if (_mode == OutputMode.LATEST) {
21+
return "latest";
22+
} else if (_mode == OutputMode.ALL) {
23+
return "all";
24+
} else {
25+
return "unknown";
26+
}
27+
}
28+
}
29+
30+
/// @notice Enum of forks available for selection when generating genesis allocs.
31+
enum Fork {
32+
NONE,
33+
DELTA,
34+
ECOTONE,
35+
FJORD
36+
}
37+
38+
Fork constant LATEST_FORK = Fork.FJORD;
39+
40+
library ForkUtils {
41+
function toString(Fork _fork) internal pure returns (string memory) {
42+
if (_fork == Fork.NONE) {
43+
return "none";
44+
} else if (_fork == Fork.DELTA) {
45+
return "delta";
46+
} else if (_fork == Fork.ECOTONE) {
47+
return "ecotone";
48+
} else if (_fork == Fork.FJORD) {
49+
return "fjord";
50+
} else {
51+
return "unknown";
52+
}
53+
}
54+
}
55+
656
/// @title Config
757
/// @notice Contains all env var based config. Add any new env var parsing to this file
858
/// to ensure that all config is in a single place.
@@ -67,4 +117,44 @@ library Config {
67117
function drippieOwnerPrivateKey() internal view returns (uint256 _env) {
68118
_env = vm.envUint("DRIPPIE_OWNER_PRIVATE_KEY");
69119
}
120+
121+
/// @notice Returns the OutputMode for genesis allocs generation.
122+
/// It reads the mode from the environment variable OUTPUT_MODE.
123+
/// If it is unset, OutputMode.ALL is returned.
124+
function outputMode() internal view returns (OutputMode) {
125+
string memory modeStr = vm.envOr("OUTPUT_MODE", string("latest"));
126+
bytes32 modeHash = keccak256(bytes(modeStr));
127+
if (modeHash == keccak256(bytes("none"))) {
128+
return OutputMode.NONE;
129+
} else if (modeHash == keccak256(bytes("latest"))) {
130+
return OutputMode.LATEST;
131+
} else if (modeHash == keccak256(bytes("all"))) {
132+
return OutputMode.ALL;
133+
} else {
134+
revert(string.concat("Config: unknown output mode: ", modeStr));
135+
}
136+
}
137+
138+
/// @notice Returns the latest fork to use for genesis allocs generation.
139+
/// It reads the fork from the environment variable FORK. If it is
140+
/// unset, NONE is returned.
141+
/// If set to the special value "latest", the latest fork is returned.
142+
function fork() internal view returns (Fork) {
143+
string memory forkStr = vm.envOr("FORK", string(""));
144+
if (bytes(forkStr).length == 0) {
145+
return Fork.NONE;
146+
}
147+
bytes32 forkHash = keccak256(bytes(forkStr));
148+
if (forkHash == keccak256(bytes("latest"))) {
149+
return LATEST_FORK;
150+
} else if (forkHash == keccak256(bytes("delta"))) {
151+
return Fork.DELTA;
152+
} else if (forkHash == keccak256(bytes("ecotone"))) {
153+
return Fork.ECOTONE;
154+
} else if (forkHash == keccak256(bytes("fjord"))) {
155+
return Fork.FJORD;
156+
} else {
157+
revert(string.concat("Config: unknown fork: ", forkStr));
158+
}
159+
}
70160
}

packages/contracts-bedrock/scripts/DeployConfig.s.sol

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,29 @@ import { stdJson } from "forge-std/StdJson.sol";
77
import { Executables } from "scripts/Executables.sol";
88
import { Process } from "scripts/libraries/Process.sol";
99
import { Chains } from "scripts/Chains.sol";
10+
import { Config, Fork, ForkUtils } from "scripts/Config.sol";
1011

1112
/// @title DeployConfig
1213
/// @notice Represents the configuration required to deploy the system. It is expected
1314
/// to read the file from JSON. A future improvement would be to have fallback
1415
/// values if they are not defined in the JSON themselves.
1516
contract DeployConfig is Script {
17+
using stdJson for string;
18+
using ForkUtils for Fork;
19+
20+
/// @notice Represents an unset offset value, as opposed to 0, which denotes no-offset.
21+
uint256 constant NULL_OFFSET = type(uint256).max;
22+
1623
string internal _json;
1724

1825
address public finalSystemOwner;
1926
address public superchainConfigGuardian;
2027
uint256 public l1ChainID;
2128
uint256 public l2ChainID;
2229
uint256 public l2BlockTime;
30+
uint256 public l2GenesisDeltaTimeOffset;
31+
uint256 public l2GenesisEcotoneTimeOffset;
32+
uint256 public l2GenesisFjordTimeOffset;
2333
uint256 public maxSequencerDrift;
2434
uint256 public sequencerWindowSize;
2535
uint256 public channelTimeout;
@@ -94,6 +104,11 @@ contract DeployConfig is Script {
94104
l1ChainID = stdJson.readUint(_json, "$.l1ChainID");
95105
l2ChainID = stdJson.readUint(_json, "$.l2ChainID");
96106
l2BlockTime = stdJson.readUint(_json, "$.l2BlockTime");
107+
108+
l2GenesisDeltaTimeOffset = _readOr(_json, "$.l2GenesisDeltaTimeOffset", NULL_OFFSET);
109+
l2GenesisEcotoneTimeOffset = _readOr(_json, "$.l2GenesisEcotoneTimeOffset", NULL_OFFSET);
110+
l2GenesisFjordTimeOffset = _readOr(_json, "$.l2GenesisFjordTimeOffset", NULL_OFFSET);
111+
97112
maxSequencerDrift = stdJson.readUint(_json, "$.maxSequencerDrift");
98113
sequencerWindowSize = stdJson.readUint(_json, "$.sequencerWindowSize");
99114
channelTimeout = stdJson.readUint(_json, "$.channelTimeout");
@@ -161,6 +176,18 @@ contract DeployConfig is Script {
161176
useInterop = _readOr(_json, "$.useInterop", false);
162177
}
163178

179+
function fork() public view returns (Fork fork_) {
180+
// let env var take precedence
181+
fork_ = Config.fork();
182+
if (fork_ == Fork.NONE) {
183+
// Will revert if no deploy config can be found either.
184+
fork_ = latestGenesisFork();
185+
console.log("DeployConfig: using deploy config fork: %s", fork_.toString());
186+
} else {
187+
console.log("DeployConfig: using env var fork: %s", fork_.toString());
188+
}
189+
}
190+
164191
function l1StartingBlockTag() public returns (bytes32) {
165192
try vm.parseJsonBytes32(_json, "$.l1StartingBlockTag") returns (bytes32 tag) {
166193
return tag;
@@ -215,6 +242,17 @@ contract DeployConfig is Script {
215242
customGasTokenAddress = _token;
216243
}
217244

245+
function latestGenesisFork() internal view returns (Fork) {
246+
if (l2GenesisFjordTimeOffset == 0) {
247+
return Fork.FJORD;
248+
} else if (l2GenesisEcotoneTimeOffset == 0) {
249+
return Fork.ECOTONE;
250+
} else if (l2GenesisDeltaTimeOffset == 0) {
251+
return Fork.DELTA;
252+
}
253+
revert("DeployConfig: no supported fork active at genesis");
254+
}
255+
218256
function _getBlockByTag(string memory _tag) internal returns (bytes32) {
219257
string[] memory cmd = new string[](3);
220258
cmd[0] = Executables.bash;
@@ -225,15 +263,20 @@ contract DeployConfig is Script {
225263
}
226264

227265
function _readOr(string memory json, string memory key, bool defaultValue) internal view returns (bool) {
228-
return vm.keyExists(json, key) ? stdJson.readBool(json, key) : defaultValue;
266+
return vm.keyExistsJson(json, key) ? json.readBool(key) : defaultValue;
229267
}
230268

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

235273
function _readOr(string memory json, string memory key, address defaultValue) internal view returns (address) {
236-
return vm.keyExists(json, key) ? stdJson.readAddress(json, key) : defaultValue;
274+
return vm.keyExistsJson(json, key) ? json.readAddress(key) : defaultValue;
275+
}
276+
277+
function _isNull(string memory json, string memory key) internal pure returns (bool) {
278+
string memory value = json.readString(key);
279+
return (keccak256(bytes(value)) == keccak256(bytes("null")));
237280
}
238281

239282
function _readOr(
@@ -245,6 +288,6 @@ contract DeployConfig is Script {
245288
view
246289
returns (string memory)
247290
{
248-
return vm.keyExists(json, key) ? stdJson.readString(json, key) : defaultValue;
291+
return vm.keyExists(json, key) ? json.readString(key) : defaultValue;
249292
}
250293
}

0 commit comments

Comments
 (0)