Files

384 lines
14 KiB
Go

// (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 &copy
}
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
}