// (c) 2025 Lux Industries, Inc. All rights reserved. // See the file LICENSE for licensing terms. package extras import ( "encoding/json" "fmt" "math/big" "github.com/luxfi/evm/commontype" "github.com/luxfi/evm/utils" "github.com/luxfi/geth/common" gethparams "github.com/luxfi/geth/params" upgrade "github.com/luxfi/node/v2/upgrade" "github.com/luxfi/node/v2/quasar" constants "github.com/luxfi/node/v2/utils/constants" ) var ( DefaultFeeConfig = commontype.FeeConfig{ GasLimit: big.NewInt(8_000_000), TargetBlockRate: 2, // in seconds MinBaseFee: big.NewInt(25_000_000_000), TargetGas: big.NewInt(15_000_000), BaseFeeChangeDenominator: big.NewInt(36), MinBlockGasCost: big.NewInt(0), MaxBlockGasCost: big.NewInt(1_000_000), BlockGasCostStep: big.NewInt(200_000), } EVMDefaultChainConfig = &ChainConfig{ ChainConfig: &gethparams.ChainConfig{}, FeeConfig: DefaultFeeConfig, NetworkUpgrades: getDefaultNetworkUpgrades(upgrade.GetConfig(constants.MainnetID)), GenesisPrecompiles: Precompiles{}, } TestChainConfig = &ChainConfig{ ChainConfig: &gethparams.ChainConfig{}, LuxContext: LuxContext{ConsensusCtx: utils.TestConsensusContext()}, FeeConfig: DefaultFeeConfig, NetworkUpgrades: getDefaultNetworkUpgrades(upgrade.GetConfig(constants.UnitTestID)), // This can be changed to correct network (local, test) via VM. GenesisPrecompiles: Precompiles{}, } // All test configs now start with all current upgrades enabled // Only future upgrades (Fortuna, Granite) remain unscheduled TestAllEnabledChainConfig = TestChainConfig // All current upgrades enabled by default ) // GetTestChainConfig returns a copy of the test chain config func GetTestChainConfig() *ChainConfig { copy := *TestChainConfig return © } func copyAndSet(c *ChainConfig, set func(*ChainConfig)) *ChainConfig { newConfig := *c set(&newConfig) return &newConfig } // UpgradeConfig includes the following configs that may be specified in upgradeBytes: // - Timestamps that enable lux network upgrades, // - Enabling or disabling precompiles as network upgrades. type UpgradeConfig struct { // Config for timestamps that enable network upgrades. NetworkUpgradeOverrides *NetworkUpgrades `json:"networkUpgradeOverrides,omitempty"` // Config for modifying state as a network interfaces. StateUpgrades []StateUpgrade `json:"stateUpgrades,omitempty"` // Config for enabling and disabling precompiles as network upgrades. PrecompileUpgrades []PrecompileUpgrade `json:"precompileUpgrades,omitempty"` } // LuxContext provides Lux specific context directly into the EVM. type LuxContext struct { ConsensusCtx *quasar.Context } type ChainConfig struct { *gethparams.ChainConfig // Embedded go-ethereum ChainConfig NetworkUpgrades // Config for timestamps that enable network upgrades. LuxContext `json:"-"` // Lux specific context set during VM initialization. Not serialized. FeeConfig commontype.FeeConfig `json:"feeConfig"` // Set the configuration for the dynamic fee algorithm AllowFeeRecipients bool `json:"allowFeeRecipients,omitempty"` // Allows fees to be collected by block builders. GenesisPrecompiles Precompiles `json:"-"` // Config for enabling precompiles from genesis. JSON encode/decode will be handled by the custom marshaler/unmarshaler. UpgradeConfig `json:"-"` // Config specified in upgradeBytes (lux network upgrades or enable/disabling precompiles). Not serialized. } // AsGeth returns the embedded *params.ChainConfig func (c *ChainConfig) AsGeth() *gethparams.ChainConfig { return c.ChainConfig } func (c *ChainConfig) CheckConfigCompatible(newConfig *ChainConfig, headNumber *big.Int, headTimestamp uint64) *gethparams.ConfigCompatError { if c == nil { return nil } return c.checkConfigCompatible(newConfig, headNumber, headTimestamp) } func (c *ChainConfig) checkConfigCompatible(newcfg *ChainConfig, headNumber *big.Int, headTimestamp uint64) *gethparams.ConfigCompatError { if err := c.checkNetworkUpgradesCompatible(&newcfg.NetworkUpgrades, headTimestamp); err != nil { return err } // Check that the precompiles on the new config are compatible with the existing precompile interfaces. if err := c.checkPrecompilesCompatible(newcfg.PrecompileUpgrades, headTimestamp); err != nil { return err } // Check that the state upgrades on the new config are compatible with the existing state upgrade interfaces. if err := c.checkStateUpgradesCompatible(newcfg.StateUpgrades, headTimestamp); err != nil { return err } return nil } func (c *ChainConfig) Description() string { if c == nil { return "" } var banner string banner += "Lux Upgrades (timestamp based):\n" banner += c.NetworkUpgrades.Description() banner += "\n" upgradeConfigBytes, err := json.Marshal(c.UpgradeConfig) if err != nil { upgradeConfigBytes = []byte("cannot marshal UpgradeConfig") } banner += fmt.Sprintf("Upgrade Config: %s", string(upgradeConfigBytes)) banner += "\n" feeBytes, err := json.Marshal(c.FeeConfig) if err != nil { feeBytes = []byte("cannot marshal FeeConfig") } banner += fmt.Sprintf("Fee Config: %s\n", string(feeBytes)) banner += fmt.Sprintf("Allow Fee Recipients: %v\n", c.AllowFeeRecipients) return banner } // isForkTimestampIncompatible returns true if a fork scheduled at timestamp s1 // cannot be rescheduled to timestamp s2 because head is already past the fork. func isForkTimestampIncompatible(s1, s2 *uint64, head uint64) bool { return (isTimestampForked(s1, head) || isTimestampForked(s2, head)) && !configTimestampEqual(s1, s2) } // isTimestampForked returns whether a fork scheduled at timestamp s is active // at the given head timestamp. func isTimestampForked(s *uint64, head uint64) bool { if s == nil { return false } return *s <= head } func configTimestampEqual(x, y *uint64) bool { if x == nil { return y == nil } if y == nil { return x == nil } return *x == *y } // UnmarshalJSON parses the JSON-encoded data and stores the result in the // object pointed to by c. // This is a custom unmarshaler to handle the Precompiles field. // Precompiles was presented as an inline object in the JSON. // This custom unmarshaler ensures backwards compatibility with the old format. func (c *ChainConfig) UnmarshalJSON(data []byte) error { // Alias ChainConfigExtra to avoid recursion type _ChainConfigExtra ChainConfig tmp := _ChainConfigExtra{} if err := json.Unmarshal(data, &tmp); err != nil { return err } // At this point we have populated all fields except PrecompileUpgrade *c = ChainConfig(tmp) // Unmarshal inlined PrecompileUpgrade return json.Unmarshal(data, &c.GenesisPrecompiles) } // MarshalJSON returns the JSON encoding of c. // This is a custom marshaler to handle the Precompiles field. func (c *ChainConfig) MarshalJSON() ([]byte, error) { // Alias ChainConfigExtra to avoid recursion type _ChainConfigExtra ChainConfig tmp, err := json.Marshal(_ChainConfigExtra(*c)) if err != nil { return nil, err } // To include PrecompileUpgrades, we unmarshal the json representing c // then directly add the corresponding keys to the interfaces. raw := make(map[string]json.RawMessage) if err := json.Unmarshal(tmp, &raw); err != nil { return nil, err } for key, value := range c.GenesisPrecompiles { conf, err := json.Marshal(value) if err != nil { return nil, err } raw[key] = conf } return json.Marshal(raw) } type fork struct { name string block *big.Int // some go-ethereum forks use block numbers timestamp *uint64 // Lux forks use timestamps optional bool // if true, the fork may be nil and next fork is still allowed } func (c *ChainConfig) CheckConfigForkOrder() error { if c == nil { return nil } // Note: In Lux, upgrades must take place via block timestamps instead // of block numbers since blocks are produced asynchronously. Therefore, we do // not check block timestamp forks in the same way as block number forks since // it would not be a meaningful comparison. Instead, we only check that the // Lux upgrades are enabled in order. // Note: we do not add the precompile configs here because they are optional // and independent, i.e. the order in which they are enabled does not impact // the correctness of the chain interfaces. return checkForks(c.forkOrder(), false) } // checkForks checks that forks are enabled in order and returns an error if not. // `blockFork` is true if the fork is a block number fork, false if it is a timestamp fork func checkForks(forks []fork, blockFork bool) error { lastFork := fork{} for _, cur := range forks { if lastFork.name != "" { switch { // Non-optional forks must all be present in the chain config up to the last defined fork case lastFork.block == nil && lastFork.timestamp == nil && (cur.block != nil || cur.timestamp != nil): if cur.block != nil { return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at block %v", lastFork.name, cur.name, cur.block) } else { return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at timestamp %v", lastFork.name, cur.name, cur.timestamp) } // Fork (whether defined by block or timestamp) must follow the fork definition sequence case (lastFork.block != nil && cur.block != nil) || (lastFork.timestamp != nil && cur.timestamp != nil): if lastFork.block != nil && lastFork.block.Cmp(cur.block) > 0 { return fmt.Errorf("unsupported fork ordering: %v enabled at block %v, but %v enabled at block %v", lastFork.name, lastFork.block, cur.name, cur.block) } else if lastFork.timestamp != nil && *lastFork.timestamp > *cur.timestamp { return fmt.Errorf("unsupported fork ordering: %v enabled at timestamp %v, but %v enabled at timestamp %v", lastFork.name, lastFork.timestamp, cur.name, cur.timestamp) } // Timestamp based forks can follow block based ones, but not the other way around if lastFork.timestamp != nil && cur.block != nil { return fmt.Errorf("unsupported fork ordering: %v used timestamp ordering, but %v reverted to block ordering", lastFork.name, cur.name) } } } // If it was optional and not set, then ignore it if !cur.optional || (cur.block != nil || cur.timestamp != nil) { lastFork = cur } } return nil } // Verify verifies chain interfaces. func (c *ChainConfig) Verify() error { if err := c.FeeConfig.Verify(); err != nil { return fmt.Errorf("invalid fee config: %w", err) } // Verify the precompile upgrades are internally consistent given the existing chainConfig. if err := c.verifyPrecompileUpgrades(); err != nil { return fmt.Errorf("invalid precompile upgrades: %w", err) } // Verify the state upgrades are internally consistent given the existing chainConfig. if err := c.verifyStateUpgrades(); err != nil { return fmt.Errorf("invalid state upgrades: %w", err) } // Verify the network upgrades are internally consistent given the existing chainConfig. // First check if NetworkUpgrades itself is valid if err := c.NetworkUpgrades.verifyNetworkUpgrades(upgrade.Config{}); err != nil { return fmt.Errorf("invalid network upgrades: %w", err) } // Then verify against actual upgrade config if available if c.ConsensusCtx != nil { if upgrades, ok := c.ConsensusCtx.NetworkUpgrades.(*upgrade.Config); ok { if err := c.verifyNetworkUpgrades(*upgrades); err != nil { return fmt.Errorf("invalid network upgrades: %w", err) } } } return nil } // IsPrecompileEnabled returns whether precompile with `address` is enabled at `timestamp`. func (c *ChainConfig) IsPrecompileEnabled(address common.Address, timestamp uint64) bool { config := c.GetActivePrecompileConfig(address, timestamp) return config != nil && !config.IsDisabled() } // GetFeeConfig returns the original FeeConfig contained in the genesis ChainConfig. // Implements precompile.ChainConfig interface. func (c *ChainConfig) GetFeeConfig() commontype.FeeConfig { return c.FeeConfig } // AllowedFeeRecipients returns the original AllowedFeeRecipients parameter contained in the genesis ChainConfig. // Implements precompile.ChainConfig interface. func (c *ChainConfig) AllowedFeeRecipients() bool { return c.AllowFeeRecipients } // IsDurango returns whether [time] represents a block with a timestamp after the Durango upgrade time. // For v2.0.0, all upgrades are active at genesis, so this always returns true. // Implements precompileconfig.ChainConfig interface. func (c *ChainConfig) IsDurango(time uint64) bool { return true } // IsEVM returns whether [time] represents a block with a timestamp after the EVM upgrade time. // For v2.0.0, all upgrades are active at genesis, so this always returns true. func (c *ChainConfig) IsEVM(time uint64) bool { return true } // IsFortuna returns whether [time] represents a block with a timestamp after the Fortuna upgrade time. // For v2.0.0, all upgrades are active at genesis, so this always returns true. func (c *ChainConfig) IsFortuna(time uint64) bool { return true } // GetRules returns the rules for the given timestamp. // For v2.0.0, all upgrades are always active. func (c *ChainConfig) GetRules(time uint64) GenesisRules { return GenesisRules{ IsGenesis: true, // All features active from genesis in v2.0.0 } } // IsForkTransition returns true if `fork` activates during the transition from // `parent` to `current`. // Taking `parent` as a pointer allows for us to pass nil when checking forks // that activate during genesis. // Note: `parent` and `current` can be either both timestamp values, or both // block number values, since this function works for both block number and // timestamp activated forks. func IsForkTransition(fork *uint64, parent *uint64, current uint64) bool { var parentForked bool if parent != nil { parentForked = isTimestampForked(fork, *parent) } currentForked := isTimestampForked(fork, current) return !parentForked && currentForked }