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

Config: Add support for Isthmus #12847

Merged
merged 4 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion bedrock-devnet/devnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
log = logging.getLogger()

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

# Global environment variables
DEVNET_NO_BUILD = os.getenv('DEVNET_NO_BUILD') == "true"
Expand Down
13 changes: 13 additions & 0 deletions op-chain-ops/genesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ type UpgradeScheduleDeployConfig struct {
// L2GenesisHoloceneTimeOffset is the number of seconds after genesis block that the Holocene hard fork activates.
// Set it to 0 to activate at genesis. Nil to disable Holocene.
L2GenesisHoloceneTimeOffset *hexutil.Uint64 `json:"l2GenesisHoloceneTimeOffset,omitempty"`
// L2GenesisIsthmusTimeOffset is the number of seconds after genesis block that the Isthmus hard fork activates.
// Set it to 0 to activate at genesis. Nil to disable Isthmus.
L2GenesisIsthmusTimeOffset *hexutil.Uint64 `json:"l2GenesisIsthmusTimeOffset,omitempty"`
// L2GenesisInteropTimeOffset is the number of seconds after genesis block that the Interop hard fork activates.
// Set it to 0 to activate at genesis. Nil to disable Interop.
L2GenesisInteropTimeOffset *hexutil.Uint64 `json:"l2GenesisInteropTimeOffset,omitempty"`
Expand Down Expand Up @@ -385,6 +388,8 @@ func (d *UpgradeScheduleDeployConfig) ForkTimeOffset(fork rollup.ForkName) *uint
return (*uint64)(d.L2GenesisGraniteTimeOffset)
case rollup.Holocene:
return (*uint64)(d.L2GenesisHoloceneTimeOffset)
case rollup.Isthmus:
return (*uint64)(d.L2GenesisIsthmusTimeOffset)
case rollup.Interop:
return (*uint64)(d.L2GenesisInteropTimeOffset)
default:
Expand All @@ -408,6 +413,8 @@ func (d *UpgradeScheduleDeployConfig) SetForkTimeOffset(fork rollup.ForkName, of
d.L2GenesisGraniteTimeOffset = (*hexutil.Uint64)(offset)
case rollup.Holocene:
d.L2GenesisHoloceneTimeOffset = (*hexutil.Uint64)(offset)
case rollup.Isthmus:
d.L2GenesisIsthmusTimeOffset = (*hexutil.Uint64)(offset)
case rollup.Interop:
d.L2GenesisInteropTimeOffset = (*hexutil.Uint64)(offset)
default:
Expand Down Expand Up @@ -472,6 +479,10 @@ func (d *UpgradeScheduleDeployConfig) HoloceneTime(genesisTime uint64) *uint64 {
return offsetToUpgradeTime(d.L2GenesisHoloceneTimeOffset, genesisTime)
}

func (d *UpgradeScheduleDeployConfig) IsthmusTime(genesisTime uint64) *uint64 {
return offsetToUpgradeTime(d.L2GenesisIsthmusTimeOffset, genesisTime)
}

func (d *UpgradeScheduleDeployConfig) InteropTime(genesisTime uint64) *uint64 {
return offsetToUpgradeTime(d.L2GenesisInteropTimeOffset, genesisTime)
}
Expand Down Expand Up @@ -504,6 +515,7 @@ func (d *UpgradeScheduleDeployConfig) forks() []Fork {
{L2GenesisTimeOffset: d.L2GenesisFjordTimeOffset, Name: string(L2AllocsFjord)},
{L2GenesisTimeOffset: d.L2GenesisGraniteTimeOffset, Name: string(L2AllocsGranite)},
{L2GenesisTimeOffset: d.L2GenesisHoloceneTimeOffset, Name: string(L2AllocsHolocene)},
{L2GenesisTimeOffset: d.L2GenesisIsthmusTimeOffset, Name: string(L2AllocsIsthmus)},
}
}

Expand Down Expand Up @@ -1008,6 +1020,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Header, l2GenesisBlockHa
FjordTime: d.FjordTime(l1StartTime),
GraniteTime: d.GraniteTime(l1StartTime),
HoloceneTime: d.HoloceneTime(l1StartTime),
IsthmusTime: d.IsthmusTime(l1StartTime),
InteropTime: d.InteropTime(l1StartTime),
ProtocolVersionsAddress: d.ProtocolVersionsProxy,
AltDAConfig: altDA,
Expand Down
1 change: 1 addition & 0 deletions op-chain-ops/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func NewL2Genesis(config *DeployConfig, l1StartHeader *types.Header) (*core.Gene
FjordTime: config.FjordTime(l1StartTime),
GraniteTime: config.GraniteTime(l1StartTime),
HoloceneTime: config.HoloceneTime(l1StartTime),
IsthmusTime: config.IsthmusTime(l1StartTime),
InteropTime: config.InteropTime(l1StartTime),
Optimism: &params.OptimismConfig{
EIP1559Denominator: eip1559Denom,
Expand Down
1 change: 1 addition & 0 deletions op-chain-ops/genesis/layer_two.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
L2AllocsFjord L2AllocsMode = "fjord"
L2AllocsGranite L2AllocsMode = "granite"
L2AllocsHolocene L2AllocsMode = "holocene"
L2AllocsIsthmus L2AllocsMode = "isthmus"
)

var (
Expand Down
12 changes: 12 additions & 0 deletions op-node/rollup/chain_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
Fjord ForkName = "fjord"
Granite ForkName = "granite"
Holocene ForkName = "holocene"
Isthmus ForkName = "isthmus"
Interop ForkName = "interop"
// ADD NEW FORKS TO AllForks BELOW!
None ForkName = "none"
Expand All @@ -55,6 +56,7 @@ var AllForks = []ForkName{
Fjord,
Granite,
Holocene,
Isthmus,
Interop,
// ADD NEW FORKS HERE!
}
Expand Down Expand Up @@ -114,6 +116,11 @@ func (s *ChainSpec) IsHolocene(t uint64) bool {
return s.config.IsHolocene(t)
}

// IsIsthmus returns true if t >= isthmus_time
func (s *ChainSpec) IsIsthmus(t uint64) bool {
return s.config.IsIsthmus(t)
}

// MaxChannelBankSize returns the maximum number of bytes the can allocated inside the channel bank
// before pruning occurs at the given timestamp.
func (s *ChainSpec) MaxChannelBankSize(t uint64) uint64 {
Expand Down Expand Up @@ -185,6 +192,9 @@ func (s *ChainSpec) CheckForkActivation(log log.Logger, block eth.L2BlockRef) {
if s.config.IsHolocene(block.Time) {
s.currentFork = Holocene
}
if s.config.IsIsthmus(block.Time) {
s.currentFork = Isthmus
}
if s.config.IsInterop(block.Time) {
s.currentFork = Interop
}
Expand All @@ -209,6 +219,8 @@ func (s *ChainSpec) CheckForkActivation(log log.Logger, block eth.L2BlockRef) {
foundActivationBlock = s.config.IsGraniteActivationBlock(block.Time)
case Holocene:
foundActivationBlock = s.config.IsHoloceneActivationBlock(block.Time)
case Isthmus:
foundActivationBlock = s.config.IsIsthmusActivationBlock(block.Time)
case Interop:
foundActivationBlock = s.config.IsInteropActivationBlock(block.Time)
}
Expand Down
20 changes: 17 additions & 3 deletions op-node/rollup/chain_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ var testConfig = Config{
EcotoneTime: u64ptr(40),
FjordTime: u64ptr(50),
GraniteTime: u64ptr(60),
HoloceneTime: u64ptr(70),
IsthmusTime: u64ptr(80),
InteropTime: nil,
BatchInboxAddress: common.HexToAddress("0xff00000000000000000000000000000000000010"),
DepositContractAddress: common.HexToAddress("0xbEb5Fc579115071764c7423A4f12eDde41f106Ed"),
Expand Down Expand Up @@ -173,14 +175,26 @@ func TestCheckForkActivation(t *testing.T) {
},
{
name: "Granite activation",
block: eth.L2BlockRef{Time: 60, Number: 8, Hash: common.Hash{0x7}},
block: eth.L2BlockRef{Time: 60, Number: 8, Hash: common.Hash{0x8}},
expectedCurrentFork: Granite,
expectedLog: "Detected hardfork activation block",
},
{
name: "Holocene activation",
block: eth.L2BlockRef{Time: 70, Number: 9, Hash: common.Hash{0x9}},
expectedCurrentFork: Holocene,
expectedLog: "Detected hardfork activation block",
},
{
name: "Isthmus activation",
block: eth.L2BlockRef{Time: 80, Number: 10, Hash: common.Hash{0xa}},
expectedCurrentFork: Isthmus,
expectedLog: "Detected hardfork activation block",
},
{
name: "No more hardforks",
block: eth.L2BlockRef{Time: 700, Number: 9, Hash: common.Hash{0x8}},
expectedCurrentFork: Granite,
block: eth.L2BlockRef{Time: 700, Number: 11, Hash: common.Hash{0xb}},
expectedCurrentFork: Isthmus,
expectedLog: "",
},
}
Expand Down
26 changes: 26 additions & 0 deletions op-node/rollup/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ type Config struct {
// Active if HoloceneTime != nil && L2 block timestamp >= *HoloceneTime, inactive otherwise.
HoloceneTime *uint64 `json:"holocene_time,omitempty"`

// IsthmusTime sets the activation time of the Isthmus network upgrade.
// Active if IsthmusTime != nil && L2 block timestamp >= *IsthmusTime, inactive otherwise.
IsthmusTime *uint64 `json:"isthmus_time,omitempty"`

// InteropTime sets the activation time for an experimental feature-set, activated like a hardfork.
// Active if InteropTime != nil && L2 block timestamp >= *InteropTime, inactive otherwise.
InteropTime *uint64 `json:"interop_time,omitempty"`
Expand Down Expand Up @@ -332,6 +336,10 @@ func (cfg *Config) Check() error {
return nil
}

func (cfg *Config) HasOptimismWithdrawalsRoot(timestamp uint64) bool {
return cfg.IsIsthmus(timestamp)
}

// validateAltDAConfig checks the two approaches to configuring alt-da mode.
// If the legacy values are set, they are copied to the new location. If both are set, they are check for consistency.
func validateAltDAConfig(cfg *Config) error {
Expand Down Expand Up @@ -404,6 +412,11 @@ func (c *Config) IsHolocene(timestamp uint64) bool {
return c.HoloceneTime != nil && timestamp >= *c.HoloceneTime
}

// IsIsthmus returns true if the Isthmus hardfork is active at or past the given timestamp.
func (c *Config) IsIsthmus(timestamp uint64) bool {
return c.IsthmusTime != nil && timestamp >= *c.IsthmusTime
}

// IsInterop returns true if the Interop hardfork is active at or past the given timestamp.
func (c *Config) IsInterop(timestamp uint64) bool {
return c.InteropTime != nil && timestamp >= *c.InteropTime
Expand Down Expand Up @@ -459,6 +472,14 @@ func (c *Config) IsHoloceneActivationBlock(l2BlockTime uint64) bool {
!c.IsHolocene(l2BlockTime-c.BlockTime)
}

// IsIsthmusActivationBlock returns whether the specified block is the first block subject to the
// Isthmus upgrade.
func (c *Config) IsIsthmusActivationBlock(l2BlockTime uint64) bool {
return c.IsIsthmus(l2BlockTime) &&
l2BlockTime >= c.BlockTime &&
!c.IsIsthmus(l2BlockTime-c.BlockTime)
}

func (c *Config) IsInteropActivationBlock(l2BlockTime uint64) bool {
return c.IsInterop(l2BlockTime) &&
l2BlockTime >= c.BlockTime &&
Expand All @@ -482,6 +503,9 @@ func (c *Config) ActivateAtGenesis(hardfork ForkName) {
case Interop:
c.InteropTime = new(uint64)
fallthrough
case Isthmus:
c.IsthmusTime = new(uint64)
fallthrough
case Holocene:
c.HoloceneTime = new(uint64)
fallthrough
Expand Down Expand Up @@ -621,6 +645,7 @@ func (c *Config) Description(l2Chains map[string]string) string {
banner += fmt.Sprintf(" - Fjord: %s\n", fmtForkTimeOrUnset(c.FjordTime))
banner += fmt.Sprintf(" - Granite: %s\n", fmtForkTimeOrUnset(c.GraniteTime))
banner += fmt.Sprintf(" - Holocene: %s\n", fmtForkTimeOrUnset(c.HoloceneTime))
banner += fmt.Sprintf(" - Isthmus: %s\n", fmtForkTimeOrUnset(c.IsthmusTime))
banner += fmt.Sprintf(" - Interop: %s\n", fmtForkTimeOrUnset(c.InteropTime))
// Report the protocol version
banner += fmt.Sprintf("Node supports up to OP-Stack Protocol Version: %s\n", OPStackSupport)
Expand Down Expand Up @@ -657,6 +682,7 @@ func (c *Config) LogDescription(log log.Logger, l2Chains map[string]string) {
"fjord_time", fmtForkTimeOrUnset(c.FjordTime),
"granite_time", fmtForkTimeOrUnset(c.GraniteTime),
"holocene_time", fmtForkTimeOrUnset(c.HoloceneTime),
"isthmus_time", fmtForkTimeOrUnset(c.IsthmusTime),
"interop_time", fmtForkTimeOrUnset(c.InteropTime),
"alt_da", c.AltDAConfig != nil,
)
Expand Down
86 changes: 85 additions & 1 deletion op-node/rollup/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,38 @@ func TestRandomConfigDescription(t *testing.T) {
out := config.Description(nil)
require.Contains(t, out, "Regolith: @ genesis")
})
t.Run("regolith date", func(t *testing.T) {
t.Run("optimism forks check, date", func(t *testing.T) {
config := randConfig()
r := uint64(1677119335)
config.RegolithTime = &r
c := uint64(1677119336)
config.CanyonTime = &c
d := uint64(1677119337)
config.DeltaTime = &d
e := uint64(1677119338)
config.EcotoneTime = &e
f := uint64(1677119339)
config.FjordTime = &f
h := uint64(1677119340)
config.HoloceneTime = &h
i := uint64(1677119341)
config.IsthmusTime = &i
it := uint64(1677119342)
config.InteropTime = &it

out := config.Description(nil)
// Don't check human-readable part of the date, it's timezone-dependent.
// Don't make this test fail only in Australia :')
require.Contains(t, out, fmt.Sprintf("Regolith: @ %d ~ ", r))
require.Contains(t, out, fmt.Sprintf("Canyon: @ %d ~ ", c))
require.Contains(t, out, fmt.Sprintf("Delta: @ %d ~ ", d))
require.Contains(t, out, fmt.Sprintf("Ecotone: @ %d ~ ", e))
require.Contains(t, out, fmt.Sprintf("Fjord: @ %d ~ ", f))
require.Contains(t, out, fmt.Sprintf("Holocene: @ %d ~ ", h))
require.Contains(t, out, fmt.Sprintf("Isthmus: @ %d ~ ", i))
require.Contains(t, out, fmt.Sprintf("Interop: @ %d ~ ", it))
})
t.Run("holocene & isthmus date", func(t *testing.T) {
config := randConfig()
x := uint64(1677119335)
config.RegolithTime = &x
Expand Down Expand Up @@ -250,6 +281,15 @@ func TestActivations(t *testing.T) {
return c.IsHolocene(t)
},
},
{
name: "Isthmus",
setUpgradeTime: func(t *uint64, c *Config) {
c.IsthmusTime = t
},
checkEnabled: func(t uint64, c *Config) bool {
return c.IsIsthmus(t)
},
},
{
name: "Interop",
setUpgradeTime: func(t *uint64, c *Config) {
Expand Down Expand Up @@ -518,10 +558,20 @@ func TestConfig_Check(t *testing.T) {
canyonTime := uint64(2)
deltaTime := uint64(3)
ecotoneTime := uint64(4)
fjordTime := uint64(5)
graniteTime := uint64(6)
holoceneTime := uint64(7)
isthmusTime := uint64(8)
interopTime := uint64(9)
cfg.RegolithTime = &regolithTime
cfg.CanyonTime = &canyonTime
cfg.DeltaTime = &deltaTime
cfg.EcotoneTime = &ecotoneTime
cfg.FjordTime = &fjordTime
cfg.GraniteTime = &graniteTime
cfg.HoloceneTime = &holoceneTime
cfg.IsthmusTime = &isthmusTime
cfg.InteropTime = &interopTime
},
expectedErr: nil,
},
Expand Down Expand Up @@ -717,3 +767,37 @@ func TestConfig_IsActivationBlock(t *testing.T) {
require.Zero(t, cfg.IsActivationBlock(ts, ts+1))
}
}

func TestConfigImplementsBlockType(t *testing.T) {
config := randConfig()
isthmusTime := uint64(100)
config.IsthmusTime = &isthmusTime
tests := []struct {
name string
blockTime uint64
hasOptimismWithdrawalsRoot bool
}{
{
name: "BeforeIsthmus",
blockTime: uint64(99),
hasOptimismWithdrawalsRoot: false,
},
{
name: "AtIsthmus",
blockTime: uint64(100),
hasOptimismWithdrawalsRoot: true,
},
{
name: "AfterIsthmus",
blockTime: uint64(200),
hasOptimismWithdrawalsRoot: true,
},
}

for _, test := range tests {
test := test
t.Run(fmt.Sprintf("TestHasOptimismWithdrawalsRoot_%s", test.name), func(t *testing.T) {
assert.Equal(t, config.HasOptimismWithdrawalsRoot(test.blockTime), test.hasOptimismWithdrawalsRoot)
})
}
}
3 changes: 3 additions & 0 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ contract L2Genesis is Deployer {
if (writeForkGenesisAllocs(_fork, Fork.HOLOCENE, _mode)) {
return;
}
if (writeForkGenesisAllocs(_fork, Fork.ISTHMUS, _mode)) {
return;
}
}

function writeForkGenesisAllocs(Fork _latest, Fork _current, OutputMode _mode) internal returns (bool isLatest_) {
Expand Down
9 changes: 7 additions & 2 deletions packages/contracts-bedrock/scripts/libraries/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ enum Fork {
ECOTONE,
FJORD,
GRANITE,
HOLOCENE
HOLOCENE,
ISTHMUS
vdamle marked this conversation as resolved.
Show resolved Hide resolved
}

Fork constant LATEST_FORK = Fork.HOLOCENE;
Fork constant LATEST_FORK = Fork.ISTHMUS;

library ForkUtils {
function toString(Fork _fork) internal pure returns (string memory) {
Expand All @@ -53,6 +54,8 @@ library ForkUtils {
return "granite";
} else if (_fork == Fork.HOLOCENE) {
return "holocene";
} else if (_fork == Fork.ISTHMUS) {
return "isthmus";
} else {
return "unknown";
}
Expand Down Expand Up @@ -168,6 +171,8 @@ library Config {
return Fork.GRANITE;
} else if (forkHash == keccak256(bytes("holocene"))) {
return Fork.HOLOCENE;
} else if (forkHash == keccak256(bytes("isthmus"))) {
return Fork.ISTHMUS;
} else {
revert(string.concat("Config: unknown fork: ", forkStr));
}
Expand Down