Skip to content

Commit 284913b

Browse files
authored
Config: Add support for Isthmus (ethereum-optimism#12847)
* Config: Enable Isthmus * Contracts: Add isthmus * set the isthmus time offset in genesis * rollup.Config implements types.BlockType interface --------- Co-authored-by: Vinod Damle <[email protected]>
1 parent 49fcee0 commit 284913b

File tree

10 files changed

+166
-7
lines changed

10 files changed

+166
-7
lines changed

bedrock-devnet/devnet/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
log = logging.getLogger()
2525

2626
# Global constants
27-
FORKS = ["delta", "ecotone", "fjord", "granite", "holocene"]
27+
FORKS = ["delta", "ecotone", "fjord", "granite", "holocene", "isthmus"]
2828

2929
# Global environment variables
3030
DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true"

op-chain-ops/genesis/config.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,9 @@ type UpgradeScheduleDeployConfig struct {
345345
// L2GenesisHoloceneTimeOffset is the number of seconds after genesis block that the Holocene hard fork activates.
346346
// Set it to 0 to activate at genesis. Nil to disable Holocene.
347347
L2GenesisHoloceneTimeOffset *hexutil.Uint64 `json:"l2GenesisHoloceneTimeOffset,omitempty"`
348+
// L2GenesisIsthmusTimeOffset is the number of seconds after genesis block that the Isthmus hard fork activates.
349+
// Set it to 0 to activate at genesis. Nil to disable Isthmus.
350+
L2GenesisIsthmusTimeOffset *hexutil.Uint64 `json:"l2GenesisIsthmusTimeOffset,omitempty"`
348351
// L2GenesisInteropTimeOffset is the number of seconds after genesis block that the Interop hard fork activates.
349352
// Set it to 0 to activate at genesis. Nil to disable Interop.
350353
L2GenesisInteropTimeOffset *hexutil.Uint64 `json:"l2GenesisInteropTimeOffset,omitempty"`
@@ -385,6 +388,8 @@ func (d *UpgradeScheduleDeployConfig) ForkTimeOffset(fork rollup.ForkName) *uint
385388
return (*uint64)(d.L2GenesisGraniteTimeOffset)
386389
case rollup.Holocene:
387390
return (*uint64)(d.L2GenesisHoloceneTimeOffset)
391+
case rollup.Isthmus:
392+
return (*uint64)(d.L2GenesisIsthmusTimeOffset)
388393
case rollup.Interop:
389394
return (*uint64)(d.L2GenesisInteropTimeOffset)
390395
default:
@@ -408,6 +413,8 @@ func (d *UpgradeScheduleDeployConfig) SetForkTimeOffset(fork rollup.ForkName, of
408413
d.L2GenesisGraniteTimeOffset = (*hexutil.Uint64)(offset)
409414
case rollup.Holocene:
410415
d.L2GenesisHoloceneTimeOffset = (*hexutil.Uint64)(offset)
416+
case rollup.Isthmus:
417+
d.L2GenesisIsthmusTimeOffset = (*hexutil.Uint64)(offset)
411418
case rollup.Interop:
412419
d.L2GenesisInteropTimeOffset = (*hexutil.Uint64)(offset)
413420
default:
@@ -472,6 +479,10 @@ func (d *UpgradeScheduleDeployConfig) HoloceneTime(genesisTime uint64) *uint64 {
472479
return offsetToUpgradeTime(d.L2GenesisHoloceneTimeOffset, genesisTime)
473480
}
474481

482+
func (d *UpgradeScheduleDeployConfig) IsthmusTime(genesisTime uint64) *uint64 {
483+
return offsetToUpgradeTime(d.L2GenesisIsthmusTimeOffset, genesisTime)
484+
}
485+
475486
func (d *UpgradeScheduleDeployConfig) InteropTime(genesisTime uint64) *uint64 {
476487
return offsetToUpgradeTime(d.L2GenesisInteropTimeOffset, genesisTime)
477488
}
@@ -504,6 +515,7 @@ func (d *UpgradeScheduleDeployConfig) forks() []Fork {
504515
{L2GenesisTimeOffset: d.L2GenesisFjordTimeOffset, Name: string(L2AllocsFjord)},
505516
{L2GenesisTimeOffset: d.L2GenesisGraniteTimeOffset, Name: string(L2AllocsGranite)},
506517
{L2GenesisTimeOffset: d.L2GenesisHoloceneTimeOffset, Name: string(L2AllocsHolocene)},
518+
{L2GenesisTimeOffset: d.L2GenesisIsthmusTimeOffset, Name: string(L2AllocsIsthmus)},
507519
}
508520
}
509521

@@ -1008,6 +1020,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Header, l2GenesisBlockHa
10081020
FjordTime: d.FjordTime(l1StartTime),
10091021
GraniteTime: d.GraniteTime(l1StartTime),
10101022
HoloceneTime: d.HoloceneTime(l1StartTime),
1023+
IsthmusTime: d.IsthmusTime(l1StartTime),
10111024
InteropTime: d.InteropTime(l1StartTime),
10121025
ProtocolVersionsAddress: d.ProtocolVersionsProxy,
10131026
AltDAConfig: altDA,

op-chain-ops/genesis/genesis.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func NewL2Genesis(config *DeployConfig, l1StartHeader *types.Header) (*core.Gene
7070
FjordTime: config.FjordTime(l1StartTime),
7171
GraniteTime: config.GraniteTime(l1StartTime),
7272
HoloceneTime: config.HoloceneTime(l1StartTime),
73+
IsthmusTime: config.IsthmusTime(l1StartTime),
7374
InteropTime: config.InteropTime(l1StartTime),
7475
Optimism: &params.OptimismConfig{
7576
EIP1559Denominator: eip1559Denom,

op-chain-ops/genesis/layer_two.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
L2AllocsFjord L2AllocsMode = "fjord"
2828
L2AllocsGranite L2AllocsMode = "granite"
2929
L2AllocsHolocene L2AllocsMode = "holocene"
30+
L2AllocsIsthmus L2AllocsMode = "isthmus"
3031
)
3132

3233
var (

op-node/rollup/chain_spec.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const (
4141
Fjord ForkName = "fjord"
4242
Granite ForkName = "granite"
4343
Holocene ForkName = "holocene"
44+
Isthmus ForkName = "isthmus"
4445
Interop ForkName = "interop"
4546
// ADD NEW FORKS TO AllForks BELOW!
4647
None ForkName = "none"
@@ -55,6 +56,7 @@ var AllForks = []ForkName{
5556
Fjord,
5657
Granite,
5758
Holocene,
59+
Isthmus,
5860
Interop,
5961
// ADD NEW FORKS HERE!
6062
}
@@ -114,6 +116,11 @@ func (s *ChainSpec) IsHolocene(t uint64) bool {
114116
return s.config.IsHolocene(t)
115117
}
116118

119+
// IsIsthmus returns true if t >= isthmus_time
120+
func (s *ChainSpec) IsIsthmus(t uint64) bool {
121+
return s.config.IsIsthmus(t)
122+
}
123+
117124
// MaxChannelBankSize returns the maximum number of bytes the can allocated inside the channel bank
118125
// before pruning occurs at the given timestamp.
119126
func (s *ChainSpec) MaxChannelBankSize(t uint64) uint64 {
@@ -185,6 +192,9 @@ func (s *ChainSpec) CheckForkActivation(log log.Logger, block eth.L2BlockRef) {
185192
if s.config.IsHolocene(block.Time) {
186193
s.currentFork = Holocene
187194
}
195+
if s.config.IsIsthmus(block.Time) {
196+
s.currentFork = Isthmus
197+
}
188198
if s.config.IsInterop(block.Time) {
189199
s.currentFork = Interop
190200
}
@@ -209,6 +219,8 @@ func (s *ChainSpec) CheckForkActivation(log log.Logger, block eth.L2BlockRef) {
209219
foundActivationBlock = s.config.IsGraniteActivationBlock(block.Time)
210220
case Holocene:
211221
foundActivationBlock = s.config.IsHoloceneActivationBlock(block.Time)
222+
case Isthmus:
223+
foundActivationBlock = s.config.IsIsthmusActivationBlock(block.Time)
212224
case Interop:
213225
foundActivationBlock = s.config.IsInteropActivationBlock(block.Time)
214226
}

op-node/rollup/chain_spec_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ var testConfig = Config{
4545
EcotoneTime: u64ptr(40),
4646
FjordTime: u64ptr(50),
4747
GraniteTime: u64ptr(60),
48+
HoloceneTime: u64ptr(70),
49+
IsthmusTime: u64ptr(80),
4850
InteropTime: nil,
4951
BatchInboxAddress: common.HexToAddress("0xff00000000000000000000000000000000000010"),
5052
DepositContractAddress: common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"),
@@ -173,14 +175,26 @@ func TestCheckForkActivation(t *testing.T) {
173175
},
174176
{
175177
name: "Granite activation",
176-
block: eth.L2BlockRef{Time: 60, Number: 8, Hash: common.Hash{0x7}},
178+
block: eth.L2BlockRef{Time: 60, Number: 8, Hash: common.Hash{0x8}},
177179
expectedCurrentFork: Granite,
178180
expectedLog: "Detected hardfork activation block",
179181
},
182+
{
183+
name: "Holocene activation",
184+
block: eth.L2BlockRef{Time: 70, Number: 9, Hash: common.Hash{0x9}},
185+
expectedCurrentFork: Holocene,
186+
expectedLog: "Detected hardfork activation block",
187+
},
188+
{
189+
name: "Isthmus activation",
190+
block: eth.L2BlockRef{Time: 80, Number: 10, Hash: common.Hash{0xa}},
191+
expectedCurrentFork: Isthmus,
192+
expectedLog: "Detected hardfork activation block",
193+
},
180194
{
181195
name: "No more hardforks",
182-
block: eth.L2BlockRef{Time: 700, Number: 9, Hash: common.Hash{0x8}},
183-
expectedCurrentFork: Granite,
196+
block: eth.L2BlockRef{Time: 700, Number: 11, Hash: common.Hash{0xb}},
197+
expectedCurrentFork: Isthmus,
184198
expectedLog: "",
185199
},
186200
}

op-node/rollup/types.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ type Config struct {
120120
// Active if HoloceneTime != nil && L2 block timestamp >= *HoloceneTime, inactive otherwise.
121121
HoloceneTime *uint64 `json:"holocene_time,omitempty"`
122122

123+
// IsthmusTime sets the activation time of the Isthmus network upgrade.
124+
// Active if IsthmusTime != nil && L2 block timestamp >= *IsthmusTime, inactive otherwise.
125+
IsthmusTime *uint64 `json:"isthmus_time,omitempty"`
126+
123127
// InteropTime sets the activation time for an experimental feature-set, activated like a hardfork.
124128
// Active if InteropTime != nil && L2 block timestamp >= *InteropTime, inactive otherwise.
125129
InteropTime *uint64 `json:"interop_time,omitempty"`
@@ -332,6 +336,10 @@ func (cfg *Config) Check() error {
332336
return nil
333337
}
334338

339+
func (cfg *Config) HasOptimismWithdrawalsRoot(timestamp uint64) bool {
340+
return cfg.IsIsthmus(timestamp)
341+
}
342+
335343
// validateAltDAConfig checks the two approaches to configuring alt-da mode.
336344
// If the legacy values are set, they are copied to the new location. If both are set, they are check for consistency.
337345
func validateAltDAConfig(cfg *Config) error {
@@ -404,6 +412,11 @@ func (c *Config) IsHolocene(timestamp uint64) bool {
404412
return c.HoloceneTime != nil && timestamp >= *c.HoloceneTime
405413
}
406414

415+
// IsIsthmus returns true if the Isthmus hardfork is active at or past the given timestamp.
416+
func (c *Config) IsIsthmus(timestamp uint64) bool {
417+
return c.IsthmusTime != nil && timestamp >= *c.IsthmusTime
418+
}
419+
407420
// IsInterop returns true if the Interop hardfork is active at or past the given timestamp.
408421
func (c *Config) IsInterop(timestamp uint64) bool {
409422
return c.InteropTime != nil && timestamp >= *c.InteropTime
@@ -459,6 +472,14 @@ func (c *Config) IsHoloceneActivationBlock(l2BlockTime uint64) bool {
459472
!c.IsHolocene(l2BlockTime-c.BlockTime)
460473
}
461474

475+
// IsIsthmusActivationBlock returns whether the specified block is the first block subject to the
476+
// Isthmus upgrade.
477+
func (c *Config) IsIsthmusActivationBlock(l2BlockTime uint64) bool {
478+
return c.IsIsthmus(l2BlockTime) &&
479+
l2BlockTime >= c.BlockTime &&
480+
!c.IsIsthmus(l2BlockTime-c.BlockTime)
481+
}
482+
462483
func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool {
463484
return c.IsInterop(l2BlockTime) &&
464485
l2BlockTime >= c.BlockTime &&
@@ -482,6 +503,9 @@ func (c *Config) ActivateAtGenesis(hardfork ForkName) {
482503
case Interop:
483504
c.InteropTime = new(uint64)
484505
fallthrough
506+
case Isthmus:
507+
c.IsthmusTime = new(uint64)
508+
fallthrough
485509
case Holocene:
486510
c.HoloceneTime = new(uint64)
487511
fallthrough
@@ -621,6 +645,7 @@ func (c *Config) Description(l2Chains map[string]string) string {
621645
banner += fmt.Sprintf(" - Fjord: %s\n", fmtForkTimeOrUnset(c.FjordTime))
622646
banner += fmt.Sprintf(" - Granite: %s\n", fmtForkTimeOrUnset(c.GraniteTime))
623647
banner += fmt.Sprintf(" - Holocene: %s\n", fmtForkTimeOrUnset(c.HoloceneTime))
648+
banner += fmt.Sprintf(" - Isthmus: %s\n", fmtForkTimeOrUnset(c.IsthmusTime))
624649
banner += fmt.Sprintf(" - Interop: %s\n", fmtForkTimeOrUnset(c.InteropTime))
625650
// Report the protocol version
626651
banner += fmt.Sprintf("Node supports up to OP-Stack Protocol Version: %s\n", OPStackSupport)
@@ -657,6 +682,7 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) {
657682
"fjord_time", fmtForkTimeOrUnset(c.FjordTime),
658683
"granite_time", fmtForkTimeOrUnset(c.GraniteTime),
659684
"holocene_time", fmtForkTimeOrUnset(c.HoloceneTime),
685+
"isthmus_time", fmtForkTimeOrUnset(c.IsthmusTime),
660686
"interop_time", fmtForkTimeOrUnset(c.InteropTime),
661687
"alt_da", c.AltDAConfig != nil,
662688
)

op-node/rollup/types_test.go

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,38 @@ func TestRandomConfigDescription(t *testing.T) {
169169
out := config.Description(nil)
170170
require.Contains(t, out, "Regolith: @ genesis")
171171
})
172-
t.Run("regolith date", func(t *testing.T) {
172+
t.Run("optimism forks check, date", func(t *testing.T) {
173+
config := randConfig()
174+
r := uint64(1677119335)
175+
config.RegolithTime = &r
176+
c := uint64(1677119336)
177+
config.CanyonTime = &c
178+
d := uint64(1677119337)
179+
config.DeltaTime = &d
180+
e := uint64(1677119338)
181+
config.EcotoneTime = &e
182+
f := uint64(1677119339)
183+
config.FjordTime = &f
184+
h := uint64(1677119340)
185+
config.HoloceneTime = &h
186+
i := uint64(1677119341)
187+
config.IsthmusTime = &i
188+
it := uint64(1677119342)
189+
config.InteropTime = &it
190+
191+
out := config.Description(nil)
192+
// Don't check human-readable part of the date, it's timezone-dependent.
193+
// Don't make this test fail only in Australia :')
194+
require.Contains(t, out, fmt.Sprintf("Regolith: @ %d ~ ", r))
195+
require.Contains(t, out, fmt.Sprintf("Canyon: @ %d ~ ", c))
196+
require.Contains(t, out, fmt.Sprintf("Delta: @ %d ~ ", d))
197+
require.Contains(t, out, fmt.Sprintf("Ecotone: @ %d ~ ", e))
198+
require.Contains(t, out, fmt.Sprintf("Fjord: @ %d ~ ", f))
199+
require.Contains(t, out, fmt.Sprintf("Holocene: @ %d ~ ", h))
200+
require.Contains(t, out, fmt.Sprintf("Isthmus: @ %d ~ ", i))
201+
require.Contains(t, out, fmt.Sprintf("Interop: @ %d ~ ", it))
202+
})
203+
t.Run("holocene & isthmus date", func(t *testing.T) {
173204
config := randConfig()
174205
x := uint64(1677119335)
175206
config.RegolithTime = &x
@@ -250,6 +281,15 @@ func TestActivations(t *testing.T) {
250281
return c.IsHolocene(t)
251282
},
252283
},
284+
{
285+
name: "Isthmus",
286+
setUpgradeTime: func(t *uint64, c *Config) {
287+
c.IsthmusTime = t
288+
},
289+
checkEnabled: func(t uint64, c *Config) bool {
290+
return c.IsIsthmus(t)
291+
},
292+
},
253293
{
254294
name: "Interop",
255295
setUpgradeTime: func(t *uint64, c *Config) {
@@ -518,10 +558,20 @@ func TestConfig_Check(t *testing.T) {
518558
canyonTime := uint64(2)
519559
deltaTime := uint64(3)
520560
ecotoneTime := uint64(4)
561+
fjordTime := uint64(5)
562+
graniteTime := uint64(6)
563+
holoceneTime := uint64(7)
564+
isthmusTime := uint64(8)
565+
interopTime := uint64(9)
521566
cfg.RegolithTime = &regolithTime
522567
cfg.CanyonTime = &canyonTime
523568
cfg.DeltaTime = &deltaTime
524569
cfg.EcotoneTime = &ecotoneTime
570+
cfg.FjordTime = &fjordTime
571+
cfg.GraniteTime = &graniteTime
572+
cfg.HoloceneTime = &holoceneTime
573+
cfg.IsthmusTime = &isthmusTime
574+
cfg.InteropTime = &interopTime
525575
},
526576
expectedErr: nil,
527577
},
@@ -717,3 +767,37 @@ func TestConfig_IsActivationBlock(t *testing.T) {
717767
require.Zero(t, cfg.IsActivationBlock(ts, ts+1))
718768
}
719769
}
770+
771+
func TestConfigImplementsBlockType(t *testing.T) {
772+
config := randConfig()
773+
isthmusTime := uint64(100)
774+
config.IsthmusTime = &isthmusTime
775+
tests := []struct {
776+
name string
777+
blockTime uint64
778+
hasOptimismWithdrawalsRoot bool
779+
}{
780+
{
781+
name: "BeforeIsthmus",
782+
blockTime: uint64(99),
783+
hasOptimismWithdrawalsRoot: false,
784+
},
785+
{
786+
name: "AtIsthmus",
787+
blockTime: uint64(100),
788+
hasOptimismWithdrawalsRoot: true,
789+
},
790+
{
791+
name: "AfterIsthmus",
792+
blockTime: uint64(200),
793+
hasOptimismWithdrawalsRoot: true,
794+
},
795+
}
796+
797+
for _, test := range tests {
798+
test := test
799+
t.Run(fmt.Sprintf("TestHasOptimismWithdrawalsRoot_%s", test.name), func(t *testing.T) {
800+
assert.Equal(t, config.HasOptimismWithdrawalsRoot(test.blockTime), test.hasOptimismWithdrawalsRoot)
801+
})
802+
}
803+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ contract L2Genesis is Deployer {
182182
if (writeForkGenesisAllocs(_fork, Fork.HOLOCENE, _mode)) {
183183
return;
184184
}
185+
if (writeForkGenesisAllocs(_fork, Fork.ISTHMUS, _mode)) {
186+
return;
187+
}
185188
}
186189

187190
function writeForkGenesisAllocs(Fork _latest, Fork _current, OutputMode _mode) internal returns (bool isLatest_) {

packages/contracts-bedrock/scripts/libraries/Config.sol

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ enum Fork {
3434
ECOTONE,
3535
FJORD,
3636
GRANITE,
37-
HOLOCENE
37+
HOLOCENE,
38+
ISTHMUS
3839
}
3940

40-
Fork constant LATEST_FORK = Fork.HOLOCENE;
41+
Fork constant LATEST_FORK = Fork.ISTHMUS;
4142

4243
library ForkUtils {
4344
function toString(Fork _fork) internal pure returns (string memory) {
@@ -53,6 +54,8 @@ library ForkUtils {
5354
return "granite";
5455
} else if (_fork == Fork.HOLOCENE) {
5556
return "holocene";
57+
} else if (_fork == Fork.ISTHMUS) {
58+
return "isthmus";
5659
} else {
5760
return "unknown";
5861
}
@@ -168,6 +171,8 @@ library Config {
168171
return Fork.GRANITE;
169172
} else if (forkHash == keccak256(bytes("holocene"))) {
170173
return Fork.HOLOCENE;
174+
} else if (forkHash == keccak256(bytes("isthmus"))) {
175+
return Fork.ISTHMUS;
171176
} else {
172177
revert(string.concat("Config: unknown fork: ", forkStr));
173178
}

0 commit comments

Comments
 (0)