Files
evm/params/extras/network_upgrades.go
T
zeekay 162e4f6668 refactor: remove all SubnetEVM references and backward compatibility
- Rename plugin/evm/upgrade/subnetevm package to feewindow
- Rename SubnetEVMTimestamp to EVMTimestamp (JSON: evmTimestamp)
- Rename IsSubnetEVM method to IsEVM
- Rename SubnetEVMDefaultChainConfig to EVMDefaultChainConfig
- Rename TestSubnetEVMChainConfig to TestEVMChainConfig
- Rename TestPreSubnetEVMChainConfig to TestPreEVMChainConfig
- Rename ethclient/subnetevmclient to ethclient/evmclient
- Update VM factory ID from "subnetevm" to "evm"
- Remove all backward compatibility aliases
- Update all documentation and comments

Breaking change: This removes all SubnetEVM naming - only "EVM" is supported.
No backward compatibility maintained as per user requirement.
2025-12-21 10:26:52 -08:00

347 lines
13 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extras
import (
"fmt"
"reflect"
"time"
"github.com/luxfi/evm/utils"
ethparams "github.com/luxfi/geth/params"
"github.com/luxfi/upgrade"
)
var errCannotBeNil = fmt.Errorf("timestamp cannot be nil")
// newTimestampCompatError creates a ConfigCompatError for timestamp mismatches
func newTimestampCompatError(what string, storedtime, newtime *uint64) *ethparams.ConfigCompatError {
var rew *uint64
switch {
case storedtime == nil:
rew = newtime
case newtime == nil || *storedtime < *newtime:
rew = newtime
default:
rew = storedtime
}
err := &ethparams.ConfigCompatError{
What: what,
StoredTime: storedtime,
NewTime: newtime,
RewindToTime: 0,
}
if rew != nil && *rew > 0 {
err.RewindToTime = *rew - 1
}
return err
}
// NetworkUpgrades contains timestamps that enable network upgrades.
// Lux specific network upgrades are also included here.
// (nil = no fork, 0 = already activated)
type NetworkUpgrades struct {
// EVMTimestamp is a placeholder that activates Lux Upgrades prior to ApricotPhase6
EVMTimestamp *uint64 `json:"evmTimestamp,omitempty"`
// Durango activates the Shanghai Execution Spec Upgrade from Ethereum (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/shanghai.md#included-eips)
// and Lux Warp Messaging.
// Note: EIP-4895 is excluded since withdrawals are not relevant to the Lux C-Chain or Subnets running the EVM.
DurangoTimestamp *uint64 `json:"durangoTimestamp,omitempty"`
// Placeholder for EtnaTimestamp
EtnaTimestamp *uint64 `json:"etnaTimestamp,omitempty"`
// Fortuna has no effect on EVM by itself, but is included for completeness.
FortunaTimestamp *uint64 `json:"fortunaTimestamp,omitempty"`
// Granite is a placeholder for the next upgrade.
GraniteTimestamp *uint64 `json:"graniteTimestamp,omitempty"`
}
func (n *NetworkUpgrades) Equal(other *NetworkUpgrades) bool {
return reflect.DeepEqual(n, other)
}
func (n *NetworkUpgrades) checkNetworkUpgradesCompatible(newcfg *NetworkUpgrades, time uint64) *ethparams.ConfigCompatError {
if isForkTimestampIncompatible(n.EVMTimestamp, newcfg.EVMTimestamp, time) {
return newTimestampCompatError("EVM fork block timestamp", n.EVMTimestamp, newcfg.EVMTimestamp)
}
if isForkTimestampIncompatible(n.DurangoTimestamp, newcfg.DurangoTimestamp, time) {
return newTimestampCompatError("Durango fork block timestamp", n.DurangoTimestamp, newcfg.DurangoTimestamp)
}
if isForkTimestampIncompatible(n.EtnaTimestamp, newcfg.EtnaTimestamp, time) {
return newTimestampCompatError("Etna fork block timestamp", n.EtnaTimestamp, newcfg.EtnaTimestamp)
}
// Fortuna is optional, so allow nil values even after the fork time
// Only check incompatibility if both values are non-nil
if n.FortunaTimestamp != nil && newcfg.FortunaTimestamp != nil {
if isForkTimestampIncompatible(n.FortunaTimestamp, newcfg.FortunaTimestamp, time) {
return newTimestampCompatError("Fortuna fork block timestamp", n.FortunaTimestamp, newcfg.FortunaTimestamp)
}
}
if isForkTimestampIncompatible(n.GraniteTimestamp, newcfg.GraniteTimestamp, time) {
return newTimestampCompatError("Granite fork block timestamp", n.GraniteTimestamp, newcfg.GraniteTimestamp)
}
return nil
}
func (n *NetworkUpgrades) forkOrder() []fork {
return []fork{
{name: "evmTimestamp", timestamp: n.EVMTimestamp},
{name: "durangoTimestamp", timestamp: n.DurangoTimestamp},
{name: "etnaTimestamp", timestamp: n.EtnaTimestamp},
{name: "fortunaTimestamp", timestamp: n.FortunaTimestamp, optional: true},
{name: "graniteTimestamp", timestamp: n.GraniteTimestamp},
}
}
// SetDefaults sets the default values for the network upgrades.
// This overrides deactivating the network upgrade by providing a timestamp of nil value.
func (n *NetworkUpgrades) SetDefaults(agoUpgrades upgrade.Config) {
defaults := GetNetworkUpgrades(agoUpgrades)
// If the network upgrade is not set, set it to the default value.
// If the network upgrade is set to 0, we also treat it as nil and set it default.
// This is because in prior versions, upgrades were not modifiable and were directly set to their default values.
// Most of the tools and configurations just provide these as 0, so it is safer to treat 0 as nil and set to default
// to prevent premature activations of the network upgrades for live networks.
if n.EVMTimestamp == nil || *n.EVMTimestamp == 0 {
n.EVMTimestamp = defaults.EVMTimestamp
}
if n.DurangoTimestamp == nil || *n.DurangoTimestamp == 0 {
n.DurangoTimestamp = defaults.DurangoTimestamp
}
if n.EtnaTimestamp == nil || *n.EtnaTimestamp == 0 {
n.EtnaTimestamp = defaults.EtnaTimestamp
}
if n.FortunaTimestamp == nil || *n.FortunaTimestamp == 0 {
n.FortunaTimestamp = defaults.FortunaTimestamp
}
}
// verifyNetworkUpgrades checks that the network upgrades are well formed.
func (n *NetworkUpgrades) verifyNetworkUpgrades(agoUpgrades upgrade.Config) error {
defaults := GetNetworkUpgrades(agoUpgrades)
maxTimestamp := uint64(time.Unix(1<<63-1, 0).Unix())
// EVMTimestamp must not be nil
if n.EVMTimestamp == nil {
return fmt.Errorf("EVM fork block timestamp is invalid: %w", errCannotBeNil)
}
// EVMTimestamp must be 0 (activated at genesis)
if *n.EVMTimestamp != 0 {
return fmt.Errorf("EVM fork block timestamp is invalid: must be 0 (activated at genesis), got %d", *n.EVMTimestamp)
}
if err := verifyWithDefault(n.EVMTimestamp, defaults.EVMTimestamp); err != nil {
return fmt.Errorf("EVM fork block timestamp is invalid: %w", err)
}
// DurangoTimestamp must not be nil
if n.DurangoTimestamp == nil {
return fmt.Errorf("Durango fork block timestamp is invalid: %w", errCannotBeNil)
}
// Verify Durango timestamp against default
if defaults.DurangoTimestamp != nil {
if *defaults.DurangoTimestamp == 0 && *n.DurangoTimestamp != 0 {
// Durango is already activated at genesis in default, must be 0
return fmt.Errorf("Durango fork block timestamp is invalid: cannot be changed from genesis activation (0) to %d", *n.DurangoTimestamp)
}
if err := verifyWithDefault(n.DurangoTimestamp, defaults.DurangoTimestamp); err != nil {
return fmt.Errorf("Durango fork block timestamp is invalid: %w", err)
}
}
// EtnaTimestamp - allow any value if unscheduled (at max time)
if defaults.EtnaTimestamp != nil && *defaults.EtnaTimestamp < maxTimestamp {
// EtnaTimestamp is scheduled, must not be nil
if n.EtnaTimestamp == nil {
return fmt.Errorf("Etna fork block timestamp is invalid: %w", errCannotBeNil)
}
if err := verifyWithDefault(n.EtnaTimestamp, defaults.EtnaTimestamp); err != nil {
return fmt.Errorf("Etna fork block timestamp is invalid: %w", err)
}
}
// If unscheduled, allow any value including nil
// FortunaTimestamp is optional, allow nil even if default is set
if n.FortunaTimestamp != nil && defaults.FortunaTimestamp != nil && *defaults.FortunaTimestamp < maxTimestamp {
if err := verifyWithDefault(n.FortunaTimestamp, defaults.FortunaTimestamp); err != nil {
return fmt.Errorf("Fortuna fork block timestamp is invalid: %w", err)
}
}
// GraniteTimestamp is optional
if n.GraniteTimestamp != nil && defaults.GraniteTimestamp != nil && *defaults.GraniteTimestamp < maxTimestamp {
if err := verifyWithDefault(n.GraniteTimestamp, defaults.GraniteTimestamp); err != nil {
return fmt.Errorf("Granite fork block timestamp is invalid: %w", err)
}
}
// Check that forks are enabled in order
if err := checkForks(n.forkOrder(), false); err != nil {
return err
}
return nil
}
func (n *NetworkUpgrades) Override(o *NetworkUpgrades) {
if o.EVMTimestamp != nil {
n.EVMTimestamp = o.EVMTimestamp
}
if o.DurangoTimestamp != nil {
n.DurangoTimestamp = o.DurangoTimestamp
}
if o.EtnaTimestamp != nil {
n.EtnaTimestamp = o.EtnaTimestamp
}
if o.FortunaTimestamp != nil {
n.FortunaTimestamp = o.FortunaTimestamp
}
if o.GraniteTimestamp != nil {
n.GraniteTimestamp = o.GraniteTimestamp
}
}
// IsEVM returns whether [time] represents a block
// with a timestamp after the EVM upgrade time.
func (n NetworkUpgrades) IsEVM(time uint64) bool {
return isTimestampForked(n.EVMTimestamp, time)
}
// IsDurango returns whether [time] represents a block
// with a timestamp after the Durango upgrade time.
func (n NetworkUpgrades) IsDurango(time uint64) bool {
return isTimestampForked(n.DurangoTimestamp, time)
}
// IsEtna returns whether [time] represents a block
// with a timestamp after the Etna upgrade time.
func (n NetworkUpgrades) IsEtna(time uint64) bool {
return isTimestampForked(n.EtnaTimestamp, time)
}
// IsFortuna returns whether [time] represents a block
// with a timestamp after the Fortuna upgrade time.
func (n *NetworkUpgrades) IsFortuna(time uint64) bool {
return isTimestampForked(n.FortunaTimestamp, time)
}
// IsGranite returns whether [time] represents a block
// with a timestamp after the Granite upgrade time.
func (n *NetworkUpgrades) IsGranite(time uint64) bool {
return isTimestampForked(n.GraniteTimestamp, time)
}
func (n *NetworkUpgrades) Description() string {
var banner string
banner += fmt.Sprintf(" - EVM Timestamp: @%-10v (https://github.com/luxfi/node/releases/tag/v1.10.0)\n", ptrToString(n.EVMTimestamp))
banner += fmt.Sprintf(" - Durango Timestamp: @%-10v (https://github.com/luxfi/node/releases/tag/v1.11.0)\n", ptrToString(n.DurangoTimestamp))
banner += fmt.Sprintf(" - Etna Timestamp: @%-10v (https://github.com/luxfi/node/releases/tag/v1.12.0)\n", ptrToString(n.EtnaTimestamp))
banner += fmt.Sprintf(" - Fortuna Timestamp: @%-10v (https://github.com/luxfi/node/releases/tag/v1.13.0)\n", ptrToString(n.FortunaTimestamp))
banner += fmt.Sprintf(" - Granite Timestamp: @%-10v (https://github.com/luxfi/node/releases/tag/v1.14.0)\n", ptrToString(n.GraniteTimestamp))
return banner
}
type LuxRules struct {
IsEVM bool
IsDurango bool
IsEtna bool
IsFortuna bool
IsGranite bool
}
func (n *NetworkUpgrades) GetLuxRules(time uint64) LuxRules {
return LuxRules{
IsEVM: n.IsEVM(time),
IsDurango: n.IsDurango(time),
IsEtna: n.IsEtna(time),
IsFortuna: n.IsFortuna(time),
IsGranite: n.IsGranite(time),
}
}
// GetNetworkUpgrades returns the network upgrades for the specified luxd upgrades.
// Nil values are used to indicate optional upgrades.
// The function respects the upgrade times from the config - if an upgrade is scheduled
// at or after UnscheduledActivationTime, it is considered not scheduled.
// InitiallyActiveTime is treated as "activated at genesis (timestamp 0)".
func GetNetworkUpgrades(agoUpgrade upgrade.Config) NetworkUpgrades {
// EVM is always activated at genesis (0)
result := NetworkUpgrades{
EVMTimestamp: utils.NewUint64(0),
}
// Use upgrade.UnscheduledActivationTime as the threshold for "unscheduled"
// UnscheduledActivationTime is time.Date(9999, time.December, 1, 0, 0, 0, 0, time.UTC)
unscheduledTime := uint64(upgrade.UnscheduledActivationTime.Unix())
// InitiallyActiveTime means "always active from genesis" for EVM purposes
// We convert it to 0 (genesis timestamp) for EVM network upgrades
initiallyActiveTime := upgrade.InitiallyActiveTime
// Helper to convert upgrade time to EVM timestamp
// Returns nil if unscheduled or zero time, 0 if initially active, or the actual timestamp
toEVMTimestamp := func(t time.Time) *uint64 {
// Zero time (not set) is treated as unscheduled
if t.IsZero() {
return nil
}
// Check if scheduled at unscheduled time (far future)
if t.Unix() >= int64(unscheduledTime) {
return nil // Unscheduled
}
// InitiallyActiveTime means "already active from genesis"
if t.Equal(initiallyActiveTime) {
return utils.NewUint64(0) // Genesis activation
}
return utils.NewUint64(uint64(t.Unix()))
}
// Check DurangoTime
result.DurangoTimestamp = toEVMTimestamp(agoUpgrade.DurangoTime)
// Check EtnaTime
result.EtnaTimestamp = toEVMTimestamp(agoUpgrade.EtnaTime)
// Check FortunaTime
result.FortunaTimestamp = toEVMTimestamp(agoUpgrade.FortunaTime)
// Check GraniteTime
result.GraniteTimestamp = toEVMTimestamp(agoUpgrade.GraniteTime)
return result
}
// GetDefaultNetworkUpgrades returns default network upgrades
func GetDefaultNetworkUpgrades() NetworkUpgrades {
return NetworkUpgrades{
EVMTimestamp: utils.NewUint64(0),
DurangoTimestamp: utils.NewUint64(0), // Already activated
EtnaTimestamp: nil, // Not scheduled
FortunaTimestamp: nil, // Not scheduled
GraniteTimestamp: nil, // Not scheduled
}
}
// verifyWithDefault checks that the provided timestamp is greater than or equal to the default timestamp.
func verifyWithDefault(configTimestamp *uint64, defaultTimestamp *uint64) error {
if defaultTimestamp == nil {
return nil
}
if configTimestamp == nil {
return errCannotBeNil
}
if *configTimestamp < *defaultTimestamp {
return fmt.Errorf("provided timestamp (%d) must be greater than or equal to the default timestamp (%d)", *configTimestamp, *defaultTimestamp)
}
return nil
}
func ptrToString(val *uint64) string {
if val == nil {
return "nil"
}
return fmt.Sprintf("%d", *val)
}