Skip to content

consensus/etc/engine.go — Version Comparison

v1.17.3 v1.17.4
Branch etc/v1.17.3-full-node etc/v1.17.4-full-node
Delta +771 -0 +633 -0

Diff between branches

diff --git a/consensus/etc/engine.go b/consensus/etc/engine.go
index 128b87bda..3f150f64c 100644
--- a/consensus/etc/engine.go
+++ b/consensus/etc/engine.go
@@ -14,16 +14,21 @@
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

-// Package etc implements the Ethereum Classic consensus engine.
-// It wraps the standard ethash engine with ETC-specific rules:
-// - ECIP-1017: Era-based monetary policy (20% reduction every era)
-// - ECIP-1010/1041: Difficulty bomb handling (DieHard pause, Gotham delay, disposal)
-// - ECIP-1099: Etchash (60k block epochs instead of 30k)
-// - London fork without EIP-1559 (Mystique)
+// Package etc implements the perpetual proof-of-work consensus engine used by
+// Ethereum Classic. It wraps the standard ethash engine, with every ETC rule
+// activated per-feature from its own chain config key rather than from a chain
+// identity check:
+//   - ECIP-1017: Era-based monetary policy (via ECIP1017EraRounds, in ethash.Finalize)
+//   - ECIP-1010/1041: Difficulty bomb handling (via ECIP1010Transition/ECIP1041Block;
+//     without those keys the standard ethash bomb schedule applies)
+//   - ECIP-1099: Etchash, 60k block epochs instead of 30k (via ECIP1099Block)
+//   - London fork without EIP-1559 (Mystique) — the current rule for perpetual
+//     PoW chains (see params.ChainConfig.IsEIP1559)
+//
+// A config with no ECIP keys behaves exactly like standard pre-merge ethash.
 package etc

 import (
-   "context"
    "errors"
    "fmt"
    "math/big"
@@ -35,16 +40,12 @@ import (
    "github.com/ethereum/go-ethereum/consensus"
    "github.com/ethereum/go-ethereum/consensus/ethash"
    "github.com/ethereum/go-ethereum/consensus/misc"
-   "github.com/ethereum/go-ethereum/core/state"
-   "github.com/ethereum/go-ethereum/core/tracing"
    "github.com/ethereum/go-ethereum/core/types"
+   "github.com/ethereum/go-ethereum/core/types/bal"
    "github.com/ethereum/go-ethereum/core/vm"
-   "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/params"
    "github.com/ethereum/go-ethereum/rlp"
    "github.com/ethereum/go-ethereum/rpc"
-   "github.com/ethereum/go-ethereum/trie"
-   "github.com/holiman/uint256"
    "golang.org/x/crypto/sha3"
 )

@@ -362,6 +363,10 @@ func (e *ETCEngine) verifyUncleHeader(chain consensus.ChainHeaderReader, header,
    if header.ParentBeaconRoot != nil {
        return fmt.Errorf("invalid parentBeaconRoot: have %x, expected nil", header.ParentBeaconRoot)
    }
+   // Verify the PoW seal
+   if err := e.inner.VerifySeal(header); err != nil {
+       return err
+   }
    return nil
 }

@@ -375,26 +380,12 @@ func (e *ETCEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Hea
    return nil
 }

-// Finalize implements consensus.Engine, accumulating block and uncle rewards
-// according to ECIP-1017 era-based monetary policy.
-func (e *ETCEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, stateDB vm.StateDB, body *types.Body) {
-   accumulateRewardsETC(e.config, stateDB, header, body.Uncles)
-}
-
-// FinalizeAndAssemble implements consensus.Engine, running post-transaction
-// state modifications and assembling the final block.
-func (e *ETCEngine) FinalizeAndAssemble(_ context.Context, chain consensus.ChainHeaderReader, header *types.Header, stateDB *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
-   if len(body.Withdrawals) > 0 {
-       return nil, errors.New("ETC does not support withdrawals")
-   }
-   // Finalize block
-   e.Finalize(chain, header, stateDB, body)
-
-   // Assign the final state root to header
-   header.Root = stateDB.IntermediateRoot(e.config.IsEIP158(header.Number))
-
-   // Assemble and return the final block
-   return types.NewBlock(header, &types.Body{Transactions: body.Transactions, Uncles: body.Uncles}, receipts, trie.NewStackTrie(nil)), nil
+// Finalize implements consensus.Engine, delegating to the inner ethash engine,
+// whose reward accumulation is itself per-feature: ECIP-1017 era-based monetary
+// policy when ECIP1017EraRounds is configured, the standard Frontier/Byzantium/
+// Constantinople schedule otherwise.
+func (e *ETCEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, stateDB vm.StateDB, body *types.Body, blockAccessIndex uint32, blockAccessList *bal.ConstructionBlockAccessList) {
+   e.inner.Finalize(chain, header, stateDB, body, blockAccessIndex, blockAccessList)
 }

 // Seal generates a new sealing request for the given input block.
@@ -480,7 +471,14 @@ var (
 // - ECIP-1010: DieHard bomb pause at block 3M
 // - Gotham: Bomb delay after pause
 // - ECIP-1041: Bomb disposal at block 5.9M
+// The ECIP bomb keys take precedence over the Ethereum bomb-delay forks: a
+// config that carries neither key has not opted into ETC bomb handling and is
+// delegated to the standard ethash schedule (Byzantium/Muir/Arrow/Gray/London
+// delays per its own fork blocks).
 func CalcDifficultyETC(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
+   if config.ECIP1010Transition == nil && config.ECIP1041Block == nil {
+       return ethash.CalcDifficulty(config, time, parent)
+   }
    next := new(big.Int).Add(parent.Number, big1)

    // Select the appropriate difficulty calculator
@@ -633,139 +631,3 @@ func calcBombComponentETC(config *params.ChainConfig, parentNumber *big.Int) *bi
    }
    return big.NewInt(0)
 }
-
-// ECIP-1017 block rewards
-var (
-   // Era 0 (blocks 0 - 4,999,999): 5 ETC
-   era0BlockReward = uint256.NewInt(5e+18)
-   // Reduction factor: 0.8 (20% reduction per era)
-   // Era 1: 4 ETC, Era 2: 3.2 ETC, Era 3: 2.56 ETC, etc.
-)
-
-// accumulateRewardsETC credits the coinbase with ECIP-1017 era-based rewards.
-func accumulateRewardsETC(config *params.ChainConfig, stateDB vm.StateDB, header *types.Header, uncles []*types.Header) {
-   // Get current era
-   era := getBlockEra(header.Number, config.ECIP1017EraRounds)
-
-   // Calculate block reward for this era
-   blockReward := getBlockWinnerRewardByEra(era)
-
-   // Accumulate the rewards for the miner
-   reward := new(uint256.Int).Set(blockReward)
-
-   // Uncle rewards
-   r := new(uint256.Int)
-   hNum, _ := uint256.FromBig(header.Number)
-   for _, uncle := range uncles {
-       uNum, _ := uint256.FromBig(uncle.Number)
-
-       if era.Sign() == 0 {
-           // Era 0: Standard Ethereum formula (8 - distance) / 8 * blockReward
-           r.AddUint64(uNum, 8)
-           r.Sub(r, hNum)
-           r.Mul(r, blockReward)
-           r.Rsh(r, 3) // Divide by 8
-       } else {
-           // Era 1+: Fixed 1/32 of block reward (ECIP-1017)
-           r.Rsh(blockReward, 5) // Divide by 32
-       }
-       stateDB.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle)
-
-       // Miner gets 1/32 of block reward per uncle (same for all eras)
-       r.Rsh(blockReward, 5) // Divide by 32
-       reward.Add(reward, r)
-   }
-   stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock)
-}
-
-// getBlockEra returns the era number for a given block number.
-// Era 0: blocks 0 to eraRounds-1
-// Era 1: blocks eraRounds to 2*eraRounds-1
-// etc.
-func getBlockEra(blockNum *big.Int, eraRounds *big.Int) *big.Int {
-   if eraRounds == nil || eraRounds.Sign() <= 0 {
-       return big.NewInt(0)
-   }
-   if blockNum.Sign() <= 0 {
-       return big.NewInt(0)
-   }
-   // era = (blockNum - 1) / eraRounds
-   // We subtract 1 so that era changes happen AT the boundary, not after
-   era := new(big.Int).Sub(blockNum, big1)
-   era.Div(era, eraRounds)
-   return era
-}
-
-// getBlockWinnerRewardByEra calculates the block reward for a given era.
-// ECIP-1017: reward = 5 ETC * (4/5)^era = 5 ETC * 0.8^era
-func getBlockWinnerRewardByEra(era *big.Int) *uint256.Int {
-   if era.Sign() <= 0 {
-       return new(uint256.Int).Set(era0BlockReward)
-   }
-
-   // For efficiency, handle small era numbers directly
-   eraU64 := era.Uint64()
-   if era.IsUint64() && eraU64 < 50 { // Reasonable upper bound
-       reward := new(uint256.Int).Set(era0BlockReward)
-       for i := uint64(0); i < eraU64; i++ {
-           // Multiply by 4, divide by 5 (= multiply by 0.8)
-           reward.Mul(reward, uint256.NewInt(4))
-           reward.Div(reward, uint256.NewInt(5))
-       }
-       return reward
-   }
-
-   // For very large era numbers (theoretical), use big.Int arithmetic
-   // This is unlikely to be reached in practice
-   reward := new(big.Int).Set(era0BlockReward.ToBig())
-   four := big.NewInt(4)
-   five := big.NewInt(5)
-   for i := big.NewInt(0); i.Cmp(era) < 0; i.Add(i, big1) {
-       reward.Mul(reward, four)
-       reward.Div(reward, five)
-   }
-   result, _ := uint256.FromBig(reward)
-   return result
-}
-
-// ECIP-1099 epoch length constants
-const (
-   EpochLengthDefault  = 30000
-   EpochLengthECIP1099 = 60000
-)
-
-// GetEpochLength returns the DAG epoch length for a given block number.
-func GetEpochLength(config *params.ChainConfig, blockNumber uint64) uint64 {
-   if config.ECIP1099Block != nil && blockNumber >= config.ECIP1099Block.Uint64() {
-       return EpochLengthECIP1099
-   }
-   return EpochLengthDefault
-}
-
-// CalcEpoch returns the DAG epoch number for a given block number.
-func CalcEpoch(config *params.ChainConfig, blockNumber uint64) uint64 {
-   return blockNumber / GetEpochLength(config, blockNumber)
-}
-
-// CalcSeedEpoch returns the epoch used for seed hash calculation.
-// Post-ECIP1099: seedEpoch = dagEpoch * 2 (maintains seed hash continuity).
-func CalcSeedEpoch(config *params.ChainConfig, blockNumber uint64) uint64 {
-   if config.ECIP1099Block == nil || blockNumber < config.ECIP1099Block.Uint64() {
-       return blockNumber / EpochLengthDefault
-   }
-   return (blockNumber / EpochLengthECIP1099) * 2
-}
-
-// SeedHash calculates the seed hash for a given block number.
-func SeedHash(config *params.ChainConfig, blockNumber uint64) []byte {
-   seed := make([]byte, 32)
-   for i := uint64(0); i < CalcSeedEpoch(config, blockNumber); i++ {
-       seed = crypto.Keccak256(seed)
-   }
-   return seed
-}
-
-// CacheKey returns a unique key for the LRU cache.
-func CacheKey(config *params.ChainConfig, blockNumber uint64) uint64 {
-   return (GetEpochLength(config, blockNumber) << 32) | CalcEpoch(config, blockNumber)
-}
← Back to Version Comparison