Files
coreth/params/extras/precompile_upgrade.go
zeekay cdd1e95a26 Replace luxfi/geth imports with luxfi packages where available
- Replace github.com/luxfi/geth/metrics with github.com/luxfi/metrics
- Replace github.com/luxfi/geth/crypto with github.com/luxfi/crypto
- Keep github.com/luxfi/geth/log due to API incompatibility with luxfi/log v0.1.1
- Update eth-allowed-packages.txt to reflect new imports
- Update go.mod dependencies

This change externalizes metrics and crypto packages to use the shared luxfi
implementations across all projects. The log package remains from geth due to
API differences that would require more extensive refactoring.

Sync with latest ava-labs/coreth semantics and test patterns.
2025-08-02 22:33:17 +00:00

211 lines
8.1 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package extras
import (
"encoding/json"
"errors"
"fmt"
"github.com/luxfi/coreth/precompile/modules"
"github.com/luxfi/coreth/precompile/precompileconfig"
"github.com/luxfi/coreth/utils"
"github.com/luxfi/geth/common"
ethparams "github.com/luxfi/geth/params"
)
var errNoKey = errors.New("PrecompileUpgrade cannot be empty")
// PrecompileUpgrade is a helper struct embedded in UpgradeConfig.
// It is used to unmarshal the json into the correct precompile config type
// based on the key. Keys are defined in each precompile module, and registered in
// precompile/registry/registry.go.
type PrecompileUpgrade struct {
precompileconfig.Config
}
// UnmarshalJSON unmarshals the json into the correct precompile config type
// based on the key. Keys are defined in each precompile module, and registered in
// precompile/registry/registry.go.
// Ex: {"feeManagerConfig": {...}} where "feeManagerConfig" is the key
func (u *PrecompileUpgrade) UnmarshalJSON(data []byte) error {
raw := make(map[string]json.RawMessage)
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if len(raw) == 0 {
return errNoKey
}
if len(raw) > 1 {
return fmt.Errorf("PrecompileUpgrade must have exactly one key, got %d", len(raw))
}
for key, value := range raw {
module, ok := modules.GetPrecompileModule(key)
if !ok {
return fmt.Errorf("unknown precompile config: %s", key)
}
config := module.MakeConfig()
if err := json.Unmarshal(value, config); err != nil {
return err
}
u.Config = config
}
return nil
}
// MarshalJSON marshal the precompile config into json based on the precompile key.
// Ex: {"feeManagerConfig": {...}} where "feeManagerConfig" is the key
func (u *PrecompileUpgrade) MarshalJSON() ([]byte, error) {
res := make(map[string]precompileconfig.Config)
res[u.Key()] = u.Config
return json.Marshal(res)
}
// verifyPrecompileUpgrades checks [c.PrecompileUpgrades] is well formed:
// - [upgrades] must specify exactly one key per PrecompileUpgrade
// - the specified blockTimestamps must monotonically increase
// - the specified blockTimestamps must be compatible with those
// specified in the chainConfig by genesis.
// - check a precompile is disabled before it is re-enabled
func (c *ChainConfig) verifyPrecompileUpgrades() error {
// Store this struct to keep track of the last upgrade for each precompile key.
// Required for timestamp and disabled checks.
type lastUpgradeData struct {
blockTimestamp uint64
disabled bool
}
lastPrecompileUpgrades := make(map[string]lastUpgradeData)
// next range over upgrades to verify correct use of disabled and blockTimestamps.
// previousUpgradeTimestamp is used to verify monotonically increasing timestamps.
var previousUpgradeTimestamp *uint64
for i, upgrade := range c.PrecompileUpgrades {
key := upgrade.Key()
// lastUpgradeByKey is the previous processed upgrade for this precompile key.
lastUpgradeByKey, ok := lastPrecompileUpgrades[key]
var (
disabled bool
lastTimestamp *uint64
)
if !ok {
disabled = true
lastTimestamp = nil
} else {
disabled = lastUpgradeByKey.disabled
lastTimestamp = utils.NewUint64(lastUpgradeByKey.blockTimestamp)
}
upgradeTimestamp := upgrade.Timestamp()
if upgradeTimestamp == nil {
return fmt.Errorf("PrecompileUpgrade (%s) at [%d]: block timestamp cannot be nil ", key, i)
}
// Verify specified timestamps are monotonically increasing across all precompile keys.
// Note: It is OK for multiple configs of DIFFERENT keys to specify the same timestamp.
if previousUpgradeTimestamp != nil && *upgradeTimestamp < *previousUpgradeTimestamp {
return fmt.Errorf("PrecompileUpgrade (%s) at [%d]: config block timestamp (%v) < previous timestamp (%v)", key, i, *upgradeTimestamp, *previousUpgradeTimestamp)
}
if disabled == upgrade.IsDisabled() {
return fmt.Errorf("PrecompileUpgrade (%s) at [%d]: disable should be [%v]", key, i, !disabled)
}
// Verify specified timestamps are monotonically increasing across same precompile keys.
// Note: It is NOT OK for multiple configs of the SAME key to specify the same timestamp.
if lastTimestamp != nil && *upgradeTimestamp <= *lastTimestamp {
return fmt.Errorf("PrecompileUpgrade (%s) at [%d]: config block timestamp (%v) <= previous timestamp (%v) of same key", key, i, *upgradeTimestamp, *lastTimestamp)
}
if err := upgrade.Verify(c); err != nil {
return err
}
lastPrecompileUpgrades[key] = lastUpgradeData{
disabled: upgrade.IsDisabled(),
blockTimestamp: *upgradeTimestamp,
}
previousUpgradeTimestamp = upgradeTimestamp
}
return nil
}
// GetActivePrecompileConfig returns the most recent precompile config corresponding to [address].
// If none have occurred, returns nil.
func (c *ChainConfig) GetActivePrecompileConfig(address common.Address, timestamp uint64) precompileconfig.Config {
configs := c.GetActivatingPrecompileConfigs(address, nil, timestamp, c.PrecompileUpgrades)
if len(configs) == 0 {
return nil
}
return configs[len(configs)-1] // return the most recent config
}
// GetActivatingPrecompileConfigs returns all precompile upgrades configured to activate during the
// state transition from a block with timestamp [from] to a block with timestamp [to].
func (c *ChainConfig) GetActivatingPrecompileConfigs(address common.Address, from *uint64, to uint64, upgrades []PrecompileUpgrade) []precompileconfig.Config {
// Get key from address.
module, ok := modules.GetPrecompileModuleByAddress(address)
if !ok {
return nil
}
configs := make([]precompileconfig.Config, 0)
key := module.ConfigKey
// Loop over all upgrades checking for the requested precompile config.
for _, upgrade := range upgrades {
if upgrade.Key() == key {
// Check if the precompile activates in the specified range.
if IsForkTransition(upgrade.Timestamp(), from, to) {
configs = append(configs, upgrade.Config)
}
}
}
return configs
}
// checkPrecompilesCompatible checks if [precompileUpgrades] are compatible with [c] at [headTimestamp].
// Returns a ConfigCompatError if upgrades already activated at [headTimestamp] are missing from
// [precompileUpgrades]. Upgrades not already activated may be modified or absent from [precompileUpgrades].
// Returns nil if [precompileUpgrades] is compatible with [c].
// Assumes given timestamp is the last accepted block timestamp.
// This ensures that as long as the node has not accepted a block with a different rule set it will allow a
// new upgrade to be applied as long as it activates after the last accepted block.
//
//nolint:unused
func (c *ChainConfig) checkPrecompilesCompatible(precompileUpgrades []PrecompileUpgrade, time uint64) *ethparams.ConfigCompatError {
for _, module := range modules.RegisteredModules() {
if err := c.checkPrecompileCompatible(module.Address, precompileUpgrades, time); err != nil {
return err
}
}
return nil
}
// checkPrecompileCompatible verifies that the precompile specified by [address] is compatible between [c]
// and [precompileUpgrades] at [headTimestamp].
// Returns an error if upgrades already activated at [headTimestamp] are missing from [precompileUpgrades].
// Upgrades that have already gone into effect cannot be modified or absent from [precompileUpgrades].
//
//nolint:unused
func (c *ChainConfig) checkPrecompileCompatible(address common.Address, precompileUpgrades []PrecompileUpgrade, time uint64) *ethparams.ConfigCompatError {
// All upgrades are always activated from the beginning
// No compatibility checking needed for Lux infrastructure
// Skip checking activeUpgrades and newUpgrades since we don't need compatibility checks
return nil
}
// EnabledStatefulPrecompiles returns current stateful precompile configs that are enabled at [blockTimestamp].
func (c *ChainConfig) EnabledStatefulPrecompiles(blockTimestamp uint64) Precompiles {
statefulPrecompileConfigs := make(Precompiles)
for _, module := range modules.RegisteredModules() {
if config := c.GetActivePrecompileConfig(module.Address, blockTimestamp); config != nil && !config.IsDisabled() {
statefulPrecompileConfigs[module.ConfigKey] = config
}
}
return statefulPrecompileConfigs
}