Как работает алгоритм регулировки сложности Ethereum Homestead?

Из From EIP 2 алгоритм настройки сложности Homestead таков:

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

где // — оператор целочисленного деления, например. 6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4.

Как это работает?

(Этот вопрос был вызван вопросом Какой был первый блок, добытый с помощью Homestead? )


Другие связанные вопросы и ответы:

Ответы (2)

Резюме

Если разница временных меток (block_timestamp - parent_timestamp):

  • < 10 секунд, сложность повышается с помощьюparent_diff // 2048 * 1
  • от 10 до 19 секунд, сложность остается неизменной
  • >= 20 секунд, сложность уменьшается пропорционально разнице временных меток, от parent_diff // 2048 * -1до максимальной регулировки внизparent_diff // 2048 * -99

Это согласуется с заявлением ethdocs.org — Ethereum Homestead — The Homestead Release :

EIP-2/4 устраняет избыточный стимул устанавливать разницу временных меток ровно 1, чтобы создать блок с немного более высокой сложностью, который, таким образом, гарантированно превзойдет все возможные форки. Это гарантирует сохранение времени блока в диапазоне 10-20 и, согласно моделированию, восстанавливает целевое время блока 15 секунд (вместо текущих эффективных 17 секунд).

А из Ethereum Network Status среднее время блока в настоящее время составляет 13,86 секунды.



Подробности

Формула корректировки сложности:

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

где // — оператор целочисленного деления, например. 6 // 2 = 3, 7 // 2 = 3, 8 // 2 = 4.

можно разбить на следующие части:


Подформула B — часть бомбы сложности, которая экспоненциально увеличивает сложность каждые 100 000 блоков.

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

Бомба сложности здесь обсуждаться не будет, так как она уже рассмотрена в следующих вопросах и ответах:


Подформула A — часть регулировки сложности, которая увеличивает или уменьшает сложность блока в зависимости от времени между отметкой времени текущего блока и отметкой времени родительского блока:

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

Подформула A1 - позволяет выделить часть подформулы A

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

и рассмотрим, каков эффект корректировки из-за разницы временных меток между текущим блоком и родительским блоком:

Когда (block_timestamp - parent_timestamp)_

  • 0, 1, 2, ..., 8, 9 секунд
    • A1 оцениваетmax(1 - 0, -99) = 1
    • А оценивает+parent_diff // 2048 * 1
  • 10, 11, 12, ..., 18, 19 секунд
    • A1 оцениваетmax(1 - 1, -99) = 0
    • А оценивает+parent_diff // 2048 * 0
  • 20, 21, 22, ..., 28, 29 секунд
    • A1 оцениваетmax(1 - 2, -99) = -1
    • А оценивает+parent_diff // 2048 * -1
  • 30, 31, 32, ..., 38, 39 секунд
    • A1 оцениваетmax(1 - 3, -99) = -2
    • А оценивает+parent_diff // 2048 * -2
  • 1000, 1001, 1002, ..., 1008, 1009 секунд
    • A1 оцениваетmax(1 - 100, -99) = -99
    • А оценивает+parent_diff // 2048 * -99
  • > 1009 секунд
    • A1 оцениваетmax(1 - {number greater than 100}, -99) = -99
    • А оценивает+parent_diff // 2048 * -99


Итак, если разница временных меток (block_timestamp - parent_timestamp):

  • < 10 секунд, сложность повышается с помощьюparent_diff // 2048 * 1
  • от 10 до 19 секунд, сложность остается неизменной
  • >= 20 секунд, сложность уменьшается пропорционально разнице временных меток, от parent_diff // 2048 * -1до максимальной регулировки внизparent_diff // 2048 * -99



Исходный код

Из Go Ethereum — core/block_validator.go, строки 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 Спасибо за подробный обзор. В вашем объяснении подформул A и A1 для 20-29секунд и 30-39секунд не является результатом A1 -1и -2соответственно?
Хорошо, это имеет смысл, по сути, вы смотрите на разницу временных меток из последних 2 блоков и извлекаете вторую значащую цифру. Эта цифра является определяющим фактором, являются ли блоки слишком медленными, слишком быстрыми или правильными. Можете ли вы объяснить 2048магическое число, хотя? В чем идея деления на 2048и почему это число?
@BokkyPooBah Мне нравится, что вы нашли время написать этот подробный отчет: я многому научился. Я хочу добавить следующее: github.com/ethereum/go-ethereum/blob/… Независимо от того, насколько плох ваш хешрейт, есть минимум, на который вы не можете пойти. github.com/ethereum/go-ethereum/blob/…
Диапазоны, используемые в сводке, не содержат (19,20)диапазона.
@greatwolf это делитель с ограниченной сложностью, будь он меньше, поощрения не сработали бы

Алгоритм Ethereum был бы лучше, если бы max() не использовался. Параметр parent_diff/2048*(1-t/10) можно было бы расширить, чтобы предотвратить появление нуля в результате целочисленного деления. Это привело бы к

diff = parent_diff + parent_diff/N - parent_diff*t/T/N

где t = родительское время решения T = целевое время решения N = коэффициент исчезновения, также известный как «среднее время жизни», также известный как количество блоков для «умерения» или «буферизации» размера ответа. Она не может быть слишком маленькой, иначе из-за длительного времени решения может возникнуть отрицательная сложность.

Это очень близко к теоретически лучшему алгоритму, который представляет собой экспоненциальную скользящую среднюю (EMA), которую исследовали я и другие. Это аппроксимация EMA разложением экспоненциальной функции в ряд Тейлора:

е ^ х = 1 + х + х ^ 2/2! + ...

Где вы используете приближение e^x = 1 + x в алгоритме EMA:

diff = parent_diff*( 1 - A + A*T/t )

где A = альфа = 1-e^(-t/T/N)

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

Этот алгоритм был открыт Джейкобом Элиософфом, который уже был хорошо знаком с EMA для цен на акции. Ему нужно было изменить его, чтобы он соответствовал сложности, и в результате получается известная версия, упомянутая в Википедии в отношении оценки производительности компьютера:

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

Я говорю, что это теоретически лучше, потому что вы можете уменьшить N до «1», а среднее и медианное время решения близки к ожидаемым T и ln(2)*T. Так что это лучшая оценка (из известных мне) угадывания текущего хэшрейта на основе только предыдущего блока.