ECIP 1099: Calibrate Epoch Duration Source

AuthorLuke Williams
Discussions-Tohttps://github.com/ethereumclassic/ECIPs/issues/368
StatusAccepted
TypeStandards Track
CategoryCore
Created2020-09-10
Replaces 1043

Abstract

The purpose of this ECIP is to calibrate the epoch length used in DAG calculations.

Motivation

The original intent of the DAG was to provide ASIC resistance to the mining protocol in order to prevent centralization of mining distributions and thereby provide for an objectively fair distribution of tokens. As evident by ASICs being developed that are capable of matching current GPU miners while being more energy efficient, the DAG has succeeded in leveling the playing field between GPU incumbents and ASIC challengers, avoiding ASIC takeover and domination, and leading to a competitive and large mining ecosystem. However the original parameters are proving too aggressive, resulting in obsoleting support for GPUs still in wide use today by miners.

Calibrating these parameters to better reflect todays hardware markets and mining ecosystem will bring the DAG growth back in sync with commonly used GPUs. Allowing the DAG system to continue to serve it’s purpose far into the future.

Parameters

Ethash transitions to a modified Dagger Hashimoto algorithm, referred to hereby as Etchash, on block ETCHASH_FORK_BLOCK:

  • For the Ethereum Classic main network: ETCHASH_FORK_BLOCK := 11_700_000 (Epoch 390)
  • For the Mordor Classic test network: ETCHASH_FORK_BLOCK := 2_520_000 (Epoch 82)
  • For the Kotti Classic test network, no upgrade is required.

Specification

On ETCHASH_FORK_BLOCK, activate the following Etchash logic:

const oldEpochLength = 30000
const newEpochLength = 60000
const activationBlock = ETCHASH_FORK_BLOCK

// calcEpochLength returns the epoch length for a given block number.
func calcEpochLength(blockNum uint64) uint64 {
  if(blockNum < activationBlock) {
    return oldEpochLength
  }
  return newEpochLength
}

// calcEpoch returns the epoch for a given block number
func calcEpoch(block uint64) int {
  epochLength := calcEpochLength(block)
  epoch := int(block / epochLength)
  return epoch
}

// seedHash is the seed to use for generating a verification cache and the
// mining dataset.
func seedHash(block uint64) []byte {
  seed := make([]byte, 32)
  if block < oldEpochLength {
    return seed
  }
  // keep using oldEpochLength here so seeds don't overlap
  keccak256 := makeHasher(sha3.NewLegacyKeccak256())
  for i := 0; i < int(block/oldEpochLength); i++ {
    keccak256(seed, seed)
  }
  return seed
}

The oldEpochLength (30000) changes to newEpochLength (60000) at a given ETCHASH_FORK_BLOCK (hardfork required).

This will result in the epoch (blockNumber/[old/new]EpochLength) halving upon activation. As the DAG cache and dataset sizes are calculated from epoch, the growth in which they have experienced too will half upon activation. DAG caches and dataset will then continue to grow as intended, however at half of the pre-activation rate. To avoid re-use of seeds oldEpochLength will continue to be used within the seedHash function.

Rationale

At current epoch (372) the DAG size is 3.91 GB. 4GB GPUs are getting obsoleted while they still make up a significant amount of ethash hashrate. Activating this change at epoch 376 (for example), would reduce the DAG size from 3.94GB to 2.47GB. With the reduced growth rate, 4GB GPUs will remain supported for an additional 3+ years.

Implementation

Hard fork required for activation. Mining software and pool implementations required.

For the smoothest possible transition activation should occur on a block in which an epoch transition to an even epoch number is occurring.

  • Epoch 388/2 = 194 (good) = block 11_640_000
  • Epoch 389/2 = 194.5 (bad) -
  • Epoch 390/2 = 195 (good) = block 11_700_000

Clients

  • core-geth - https://github.com/etclabscore/core-geth/pull/186
  • besu

Pools

  • open-ethereum-pool

Mining Software

  • ethminer

References

This work is licensed under Apache License, Version 2.0.