¿Cómo funciona el algoritmo de ajuste de dificultad de Ethereum Homestead?

De From EIP 2 , el algoritmo de ajuste de dificultad de Homestead es:

    block_diff = parent_diff + parent_diff // 2048 * 
      max(1 - (block_timestamp - parent_timestamp) // 10, -99) + 
      int(2**((block.number // 100000) - 2))

donde // es el operador de división de enteros, p. 6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4.

¿Como funciona esto?

(Esta pregunta fue provocada por la pregunta ¿Cuál fue el primer bloque extraído con Homestead? )


Otras preguntas y respuestas relacionadas:

Respuestas (2)

Resumen

Si la diferencia de marca de tiempo (block_timestamp - parent_timestamp)es:

  • < 10 segundos, la dificultad se ajusta hacia arriba porparent_diff // 2048 * 1
  • 10 a 19 segundos, la dificultad no cambia
  • >= 20 segundos, la dificultad se ajusta hacia abajo proporcional a la diferencia de marca de tiempo, desde parent_diff // 2048 * -1un ajuste hacia abajo máximo deparent_diff // 2048 * -99

Esto es consistente con la declaración de ethdocs.org - Ethereum Homestead - The Homestead Release :

EIP-2/4 elimina el incentivo excesivo para establecer la diferencia de marca de tiempo exactamente en 1 para crear un bloque que tenga una dificultad ligeramente mayor y que, por lo tanto, garantice superar cualquier bifurcación posible. Esto garantiza mantener el tiempo de bloqueo en el rango de 10-20 y, según las simulaciones, restaura el tiempo de bloqueo objetivo de 15 segundos (en lugar de los 17 segundos efectivos actuales).

Y según Ethereum Network Status , el tiempo promedio de bloqueo actualmente es de 13,86 segundos.



Detalles

La fórmula de ajuste de dificultad:

    block_diff = parent_diff + parent_diff // 2048 * 
      max(1 - (block_timestamp - parent_timestamp) // 10, -99) + 
      int(2**((block.number // 100000) - 2))

donde // es el operador de división de enteros, p. 6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4.

se puede dividir en las siguientes partes:


Subfórmula B : la parte de la bomba de dificultad, que aumenta la dificultad exponencialmente cada 100,000 bloques.

+ int(2**((block.number // 100000) - 2))

La bomba de dificultad no se discutirá aquí, ya que ya se trata en las siguientes preguntas y respuestas:


Subfórmula A : la parte de ajuste de dificultad, que aumenta o disminuye la dificultad del bloque según el tiempo entre la marca de tiempo del bloque actual y la marca de tiempo del bloque principal:

+ parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)

Subfórmula A1 - Vamos a separar parte de la Subfórmula A

+ max(1 - (block_timestamp - parent_timestamp) // 10, -99)

y considere cuál es el efecto de ajuste debido a la diferencia de marca de tiempo entre el bloque actual y el bloque principal:

cuando (block_timestamp - parent_timestamp)es

  • 0, 1, 2, ..., 8, 9 segundos
    • A1 se evalúa comomax(1 - 0, -99) = 1
    • A evalúa a+parent_diff // 2048 * 1
  • 10, 11, 12, ..., 18, 19 segundos
    • A1 se evalúa comomax(1 - 1, -99) = 0
    • A evalúa a+parent_diff // 2048 * 0
  • 20, 21, 22, ..., 28, 29 segundos
    • A1 se evalúa comomax(1 - 2, -99) = -1
    • A evalúa a+parent_diff // 2048 * -1
  • 30, 31, 32, ..., 38, 39 segundos
    • A1 se evalúa comomax(1 - 3, -99) = -2
    • A evalúa a+parent_diff // 2048 * -2
  • 1000, 1001, 1002, ..., 1008, 1009 segundos
    • A1 se evalúa comomax(1 - 100, -99) = -99
    • A evalúa a+parent_diff // 2048 * -99
  • > 1009 segundos
    • A1 se evalúa comomax(1 - {number greater than 100}, -99) = -99
    • A evalúa a+parent_diff // 2048 * -99


Entonces, si la diferencia de marca de tiempo (block_timestamp - parent_timestamp)es:

  • < 10 segundos, la dificultad se ajusta hacia arriba porparent_diff // 2048 * 1
  • 10 a 19 segundos, la dificultad no cambia
  • >= 20 segundos, la dificultad se ajusta hacia abajo proporcional a la diferencia de marca de tiempo, desde parent_diff // 2048 * -1un ajuste hacia abajo máximo deparent_diff // 2048 * -99



El código fuente

De Go Ethereum - core/block_validator.go, líneas 264-311 :

func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
    // algorithm:
    // diff = (parent_diff +
    //         (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
    //        ) + 2^(periodCount - 2)

    bigTime := new(big.Int).SetUint64(time)
    bigParentTime := new(big.Int).SetUint64(parentTime)

    // holds intermediate values to make the algo easier to read & audit
    x := new(big.Int)
    y := new(big.Int)

    // 1 - (block_timestamp -parent_timestamp) // 10
    x.Sub(bigTime, bigParentTime)
    x.Div(x, big10)
    x.Sub(common.Big1, x)

    // max(1 - (block_timestamp - parent_timestamp) // 10, -99)))
    if x.Cmp(bigMinus99) < 0 {
        x.Set(bigMinus99)
    }

    // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
    y.Div(parentDiff, params.DifficultyBoundDivisor)
    x.Mul(y, x)
    x.Add(parentDiff, x)

    // minimum difficulty can ever be (before exponential factor)
    if x.Cmp(params.MinimumDifficulty) < 0 {
        x.Set(params.MinimumDifficulty)
    }

    // for the exponential factor
    periodCount := new(big.Int).Add(parentNumber, common.Big1)
    periodCount.Div(periodCount, ExpDiffPeriod)

    // the exponential factor, commonly referred to as "the bomb"
    // diff = diff + 2^(periodCount - 2)
    if periodCount.Cmp(common.Big1) > 0 {
        y.Sub(periodCount, common.Big2)
        y.Exp(common.Big2, y, nil)
        x.Add(x, y)
    }

    return x
}
@BookyPooBah Gracias por un recorrido detallado. En su explicación de la subfórmula A y A1, para 20-29segundos y 30-39segundos, ¿no es el resultado de A1 -1y -2respectivamente?
Bien, esto tiene sentido, esencialmente estás viendo la diferencia de marca de tiempo de los últimos 2 bloques y estás extrayendo el segundo dígito significativo. Ese dígito es el factor determinante si los bloques son demasiado lentos, demasiado rápidos o correctos. ¿Puedes explicar el 2048número mágico? ¿Cuál es la idea detrás de dividirlo y por 2048qué ese número?
@BokkyPooBah Me encanta que te hayas dado el tiempo para escribir este relato detallado: aprendí de él. Quiero agregar lo siguiente: github.com/ethereum/go-ethereum/blob/… No importa cuán pobre sea su tasa de hash, hay un mínimo por el que no puede pasar. github.com/ethereum/go-ethereum/blob/…
Los rangos utilizados en el resumen no contienen (19,20)rango.
@greatwolf es un divisor limitado por dificultad, si hubiera sido más pequeño, los incentivos no funcionarían

El algoritmo de Ethereum hubiera sido mejor si no se hubiera utilizado max(). parent_diff/2048*(1-t/10) podría haberse expandido para evitar el cero que resulta de la división de enteros. Esto habría resultado en

diferencia = diferencia_principal + diferencia_principal/N - diferencia_principal*t/T/N

donde t = tiempo de resolución principal T = tiempo de resolución objetivo N = coeficiente de extinción, también conocido como "vida útil media", también conocido como número de bloques para "temperar" o "amortiguar" el tamaño de la respuesta. No puede ser demasiado pequeño o una dificultad negativa puede resultar de largos tiempos de resolución.

Esto está muy cerca del mejor algoritmo teórico que es un promedio móvil exponencial (EMA) que yo y otros hemos investigado. Es una aproximación de la EMA por la expansión de la serie de Taylor de la función exponencial:

e^x = 1 + x + x^2/2! + ...

Donde usas la aproximación e^x = 1 + x en el algoritmo EMA:

diferencia = diferencia_principal*( 1 - A + A*T/t )

donde A = alfa = 1-e^(-t/T/N)

Ver https://github.com/zawy12/difficulty-algorithms/issues/17

Este algoritmo fue descubierto por Jacob Eliosoff, quien ya estaba muy familiarizado con los EMA para los precios de las acciones. Necesitaba modificarlo para adaptarlo a la dificultad, y el resultado resulta ser una versión conocida que se menciona en Wikipedia con respecto a la estimación del rendimiento de la computadora:

https://en.wikipedia.org/wiki/Moving_average#Application_to_measuring_computer_rendimiento

Digo que teóricamente es mejor porque puede reducir N hasta "1" y los tiempos de resolución medio y mediano están cerca de la esperada T y ln(2)*T. Por lo tanto, es el mejor estimador (que conozco) para adivinar el hashrate actual basado solo en el bloque anterior.