Skip to content

params/config_etc.go — Version Comparison

v1.16.8 v1.17.3
Branch etc/v1.16.8-full-node etc/v1.17.3-full-node
Delta +192 -0 +330 -0

Diff between branches

diff --git a/params/config_etc.go b/params/config_etc.go
index e04f0ab7a..21569677b 100644
--- a/params/config_etc.go
+++ b/params/config_etc.go
@@ -17,6 +17,7 @@
 package params

 import (
+   "fmt"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
@@ -54,7 +55,8 @@ var (
        CancunTime:              nil,
        PragueTime:              nil,
        OsakaTime:               nil,
-       VerkleTime:              nil,
+       AmsterdamTime:           nil,
+       UBTTime:                 nil,
        TerminalTotalDifficulty: nil, // ETC is perpetual PoW, no TTD
        // ETC-specific ECIPs
        ECIP1017Block:                big.NewInt(5_000_000),  // Gotham monetary policy
@@ -84,8 +86,8 @@ var (
        PetersburgBlock:     big.NewInt(301_243),   // Mordor Agharta
        IstanbulBlock:       big.NewInt(999_983),   // Mordor Phoenix
        MuirGlacierBlock:    nil,                   // No Muir Glacier
-       BerlinBlock:         big.NewInt(2_520_000), // Mordor Magneto
-       LondonBlock:         big.NewInt(3_985_893), // Mordor Mystique (WITHOUT EIP-1559)
+       BerlinBlock:         big.NewInt(3_985_893), // Mordor Magneto
+       LondonBlock:         big.NewInt(5_520_000), // Mordor Mystique (WITHOUT EIP-1559)
        ArrowGlacierBlock:   nil,
        GrayGlacierBlock:    nil,
        MergeNetsplitBlock:  nil,
@@ -94,11 +96,12 @@ var (
        CancunTime:              nil,
        PragueTime:              nil,
        OsakaTime:               nil,
-       VerkleTime:              nil,
+       AmsterdamTime:           nil,
+       UBTTime:                 nil,
        TerminalTotalDifficulty: nil, // Perpetual PoW
        // ETC-specific ECIPs
-       ECIP1017Block:      nil,                   // From genesis (not a fork point)
-       ECIP1041Block:      nil,                   // Mordor never had bomb
+       ECIP1017Block:      big.NewInt(0),         // Active from genesis
+       ECIP1041Block:      big.NewInt(0),         // Bomb disposed from genesis
        ECIP1099Block:      big.NewInt(2_520_000), // Etchash from Magneto
        SpiralBlock:        big.NewInt(9_957_000), // Mordor Spiral
        ECIP1017EraRounds:  big.NewInt(2_000_000), // Era length: 2M blocks
@@ -126,6 +129,55 @@ func (c *ChainConfig) IsPow() bool {
    return c.Ethash != nil && c.TerminalTotalDifficulty == nil
 }

+// describePoWForks returns the human-readable hard-fork section for a
+// perpetual PoW chain. Used by ChainConfig.Description for the --classic /
+// --mordor banner, where merge/post-merge sections do not apply and forks
+// carry their ETC names. Only non-nil fields are printed, so the helper
+// works for both ETC mainnet and Mordor (which omits a few ECIPs).
+func (c *ChainConfig) describePoWForks() string {
+   s := "Hard forks (block based):\n"
+   if c.HomesteadBlock != nil {
+       s += fmt.Sprintf(" - Homestead:                   #%-8v\n", c.HomesteadBlock)
+   }
+   if c.EIP150Block != nil {
+       s += fmt.Sprintf(" - Tangerine Whistle:           #%-8v\n", c.EIP150Block)
+   }
+   if c.ECIP1010Transition != nil {
+       s += fmt.Sprintf(" - DieHard:                     #%-8v\n", c.ECIP1010Transition)
+   }
+   if c.EIP155Block != nil {
+       s += fmt.Sprintf(" - Spurious Dragon:             #%-8v\n", c.EIP155Block)
+   }
+   if c.ECIP1017Block != nil {
+       s += fmt.Sprintf(" - Gotham:                      #%-8v\n", c.ECIP1017Block)
+   }
+   if c.ECIP1041Block != nil {
+       s += fmt.Sprintf(" - Bomb disposal:               #%-8v\n", c.ECIP1041Block)
+   }
+   if c.ByzantiumBlock != nil {
+       s += fmt.Sprintf(" - Atlantis:                    #%-8v\n", c.ByzantiumBlock)
+   }
+   if c.ConstantinopleBlock != nil {
+       s += fmt.Sprintf(" - Agharta:                     #%-8v\n", c.ConstantinopleBlock)
+   }
+   if c.IstanbulBlock != nil {
+       s += fmt.Sprintf(" - Phoenix:                     #%-8v\n", c.IstanbulBlock)
+   }
+   if c.ECIP1099Block != nil {
+       s += fmt.Sprintf(" - Etchash:                     #%-8v\n", c.ECIP1099Block)
+   }
+   if c.BerlinBlock != nil {
+       s += fmt.Sprintf(" - Magneto:                     #%-8v\n", c.BerlinBlock)
+   }
+   if c.LondonBlock != nil {
+       s += fmt.Sprintf(" - Mystique:                    #%-8v\n", c.LondonBlock)
+   }
+   if c.SpiralBlock != nil {
+       s += fmt.Sprintf(" - Spiral:                      #%-8v\n", c.SpiralBlock)
+   }
+   return s
+}
+
 // IsSpiral returns whether num is either equal to the Spiral fork block or greater.
 func (c *ChainConfig) IsSpiral(num *big.Int) bool {
    return isBlockForked(c.SpiralBlock, num)
@@ -185,6 +237,92 @@ func (c *ChainConfig) isEIP160(num *big.Int) bool {
    return false
 }

+// checkCompatibleETC guards the ETC-specific fork blocks against incompatible changes.
+// Upstream's checkCompatible enumerates its own forks but knows nothing about the forks
+// getc adds. Those *Block fields enter the fork ID via gatherForks' reflection, so a
+// changed Etchash/Spiral/rewards block on an already-advanced DB would otherwise be
+// accepted silently (SetupGenesisBlock rewrites the stored config when CheckCompatible
+// returns no error).
+//
+// Enumerated on purpose: go-ethereum has always kept checkCompatible enumerated (only the
+// fork ID was moved to reflection), so this mirrors upstream's style and stays rebase-
+// friendly. TestETCForkCompatibilityCoverage enforces that every fork-ID *Block is guarded
+// here, giving us the fork-ID↔compatibility symmetry core-geth gets from its reflective
+// confp system — without importing reflection into production code.
+//
+// MESS (ECBP1100Transition / ECBP1100DeactivateTransition) is intentionally excluded, just
+// like it is excluded from the fork ID: it is a fork-choice/reorg policy, not a block-
+// validity rule, so changing it cannot retroactively split consensus.
+func (c *ChainConfig) checkCompatibleETC(newcfg *ChainConfig, headNumber *big.Int) *ConfigCompatError {
+   if isForkBlockIncompatible(c.ECIP1017Block, newcfg.ECIP1017Block, headNumber) {
+       return newBlockCompatError("ECIP-1017 (Gotham) fork block", c.ECIP1017Block, newcfg.ECIP1017Block)
+   }
+   if isForkBlockIncompatible(c.ECIP1041Block, newcfg.ECIP1041Block, headNumber) {
+       return newBlockCompatError("ECIP-1041 (bomb disposal) fork block", c.ECIP1041Block, newcfg.ECIP1041Block)
+   }
+   if isForkBlockIncompatible(c.ECIP1099Block, newcfg.ECIP1099Block, headNumber) {
+       return newBlockCompatError("ECIP-1099 (Etchash) fork block", c.ECIP1099Block, newcfg.ECIP1099Block)
+   }
+   if isForkBlockIncompatible(c.SpiralBlock, newcfg.SpiralBlock, headNumber) {
+       return newBlockCompatError("Spiral fork block", c.SpiralBlock, newcfg.SpiralBlock)
+   }
+   if isForkBlockIncompatible(c.ECIP1010Transition, newcfg.ECIP1010Transition, headNumber) {
+       return newBlockCompatError("ECIP-1010 (DieHard) transition", c.ECIP1010Transition, newcfg.ECIP1010Transition)
+   }
+   // ECIP1017EraRounds / ECIP1010Length are parameters (era length, pause duration), not
+   // activation blocks: changing them rewrites the reward schedule / bomb pause retroactively.
+   // They don't enter the fork ID, so require equality while their fork is active.
+   if c.IsECIP1017(headNumber) && !configBlockEqual(c.ECIP1017EraRounds, newcfg.ECIP1017EraRounds) {
+       return newBlockCompatError("ECIP-1017 era rounds", c.ECIP1017EraRounds, newcfg.ECIP1017EraRounds)
+   }
+   if c.IsECIP1010(headNumber) && !configBlockEqual(c.ECIP1010Length, newcfg.ECIP1010Length) {
+       return newBlockCompatError("ECIP-1010 pause length", c.ECIP1010Length, newcfg.ECIP1010Length)
+   }
+   return nil
+}
+
+// checkConfigForkOrderETC checks the ordering of the ETC-specific fork blocks. Upstream's
+// CheckConfigForkOrder validates the Ethereum ordering of the shared forks, but knows nothing
+// about the ETC forks getc adds (Gotham/DieHard/Etchash/Spiral), nor about where they sit
+// relative to the shared ones. This validates the canonical ETC sequence so an out-of-order
+// config (e.g. Spiral before Mystique, or Etchash before Phoenix) is rejected.
+//
+// A few shared forks are included as anchors so the ETC forks are ordered relative to them.
+// Every entry is optional (skipped if nil): only ordering is enforced, not presence — Classic
+// and Mordor enable different subsets and place some forks at the same block (e.g. Mordor has
+// Magneto == Etchash), which equality tolerates.
+func (c *ChainConfig) checkConfigForkOrderETC() error {
+   type fork struct {
+       name  string
+       block *big.Int
+   }
+   var last fork
+   for _, cur := range []fork{
+       {"eip155Block", c.EIP155Block},
+       {"ecip1010Transition (DieHard)", c.ECIP1010Transition},
+       {"ecip1017Block (Gotham)", c.ECIP1017Block},
+       {"ecip1041Block (bomb disposal)", c.ECIP1041Block},
+       // ETC delayed EIP-158 to Atlantis (8.772M on Classic), unlike Ethereum's block 3M.
+       {"eip158Block (Atlantis)", c.EIP158Block},
+       {"byzantiumBlock (Atlantis)", c.ByzantiumBlock},
+       {"istanbulBlock (Phoenix)", c.IstanbulBlock},
+       {"ecip1099Block (Etchash)", c.ECIP1099Block},
+       {"berlinBlock (Magneto)", c.BerlinBlock},
+       {"londonBlock (Mystique)", c.LondonBlock},
+       {"spiralBlock (Spiral)", c.SpiralBlock},
+   } {
+       if cur.block == nil {
+           continue // optional: presence not required, only ordering
+       }
+       if last.block != nil && last.block.Cmp(cur.block) > 0 {
+           return fmt.Errorf("unsupported ETC fork ordering: %s enabled at block %v, but %s enabled at block %v",
+               last.name, last.block, cur.name, cur.block)
+       }
+       last = cur
+   }
+   return nil
+}
+
 func init() {
    // Register ETC networks in NetworkNames
    NetworkNames[ClassicChainConfig.ChainID.String()] = "classic"
← Back to Version Comparison