Делая некоторые тесты с использованием нашего моделирования тестовой сети в btcd, Джош Rickmar, и я заметил, что Bitcoin ядро неправильно вычисляет сложность перенастройки из-за переполнения, который происходит на regtest сети. Это другое поведение из всех версий Bitcoin Ядра до совершения https://github.com/bitcoin/bitcoin/commit/df9eb5e14fa8072bc8a82b59e712c2ba36f13f4c.
Вопрос заключается в том, что ранее расчеты использовали класс CBigNum, который, в свою очередь, используется произвольное точность OpenSSL bignums, теперь ограничены 256-битовых целых числа. Для справки, вот расчеты трудности корректив в pow.cpp:
Код:
Const arith_uint256 bnPowLimit = UintToArith256 (params.powLimit);
arith_uint256 bnNew;
arith_uint256 bnOld;
bnNew.SetCompact (pindexLast->Nbits);
bnOld = bnNew;
bnNew * = nActualTimespan;
bnNew / = params.nPowTargetTimespan;
если (bnNew > bnPowLimit)
bnNew = bnPowLimit;
arith_uint256 bnNew;
arith_uint256 bnOld;
bnNew.SetCompact (pindexLast->Nbits);
bnOld = bnNew;
bnNew * = nActualTimespan;
bnNew / = params.nPowTargetTimespan;
если (bnNew > bnPowLimit)
bnNew = bnPowLimit;
Обратите внимание, что корректировка вычисляется путем умножения старого трудности (в пересчете от его компактного представления к uint256) на промежуток времени, скорректированного (которая ограничена макс целевой промежуток времени * 4 и мин целевого промежутка времени / 4). Результат затем делится на промежуток времени цели и преобразован обратно в компактную форму. К сожалению, это означает, что с помощью новых uint256s, введенных с вышеупомянутой фиксации вызывает результат `bnNew * = nActualTimespan;` линия переполнения, тогда как ранее это было с произвольной точностью и не будет.
Используя реальные цифры для regtest, то разветвление состояние становится ясно.
Во-первых, давайте делать математику, используя старую арифметику на основе произвольной точности. Для простоты и иллюстрирующая проблемы, мы будем считать, что первые блоки 2016 найдены достаточно быстро, так что минимальный возможный случай умножения получает удар.
Код:
Компактные Насадки для regtest блока генеза: 0x207fffff
Конвертируется в uint256: 0x7fffff0000000000000000000000000000000000000000000000000000000000
nTargetTimespan: 14 * 24 * 60 * 60 = 1209600 = 0x127500
nActualTimespan = nTargetTimespan / 4 = 302400 = 0x49d40 (минимально возможный множитель для простоты)
Конвертируется в uint256: 0x7fffff0000000000000000000000000000000000000000000000000000000000
nTargetTimespan: 14 * 24 * 60 * 60 = 1209600 = 0x127500
nActualTimespan = nTargetTimespan / 4 = 302400 = 0x49d40 (минимально возможный множитель для простоты)
Код:
bnNew.SetCompact (pindexLast->Nbits) -> 0x7fffff0000000000000000000000000000000000000000000000000000000000
bnNew * = nActualTimespan -> 0x7fffff0000000000000000000000000000000000000000000000000000000000 * 0x49d40 = 0x24e9ffb62c00000000000000000000000000000000000000000000000000000000000
*** Обратите внимание на то, как предыдущая строка переполняет макс uint256, но это хорошо, потому что в произвольной точности арифметики OpenSSL ***
. BnNew / = Params () TargetTimespan () -> 0x24e9ffb62c00000000000000000000000000000000000000000000000000000000000 / 0x127500 = 0x1fffffc000000000000000000000000000000000000000000000000000000000
bnNew * = nActualTimespan -> 0x7fffff0000000000000000000000000000000000000000000000000000000000 * 0x49d40 = 0x24e9ffb62c00000000000000000000000000000000000000000000000000000000000
*** Обратите внимание на то, как предыдущая строка переполняет макс uint256, но это хорошо, потому что в произвольной точности арифметики OpenSSL ***
. BnNew / = Params () TargetTimespan () -> 0x24e9ffb62c00000000000000000000000000000000000000000000000000000000000 / 0x127500 = 0x1fffffc000000000000000000000000000000000000000000000000000000000
Старые Ожидаемые сложности бит: 0x201fffff
Теперь, давайте повторим математику с помощью текущий код в Bitcoin Ядра:
Код:
bnNew.SetCompact (pindexLast->Nbits) -> 0x7fffff0000000000000000000000000000000000000000000000000000000000
bnNew * = nActualTimespan -> 0x7fffff0000000000000000000000000000000000000000000000000000000000 * 0x49d40 = 0xfb62c00000000000000000000000000000000000000000000000000000000000
*** Обратите внимание на то, как предыдущая строка переполняет макс uint256, но теперь 2s дополнение завернуто и, таким образом, не то же самое, что и выше ***
. BnNew / = Params () TargetTimespan () -> 0x7fffff0000000000000000000000000000000000000000000000000000000000 / 0x127500 = 0xd9ebbc99a778556334111eefccdaab889667445223000ddebbc99a77855
bnNew * = nActualTimespan -> 0x7fffff0000000000000000000000000000000000000000000000000000000000 * 0x49d40 = 0xfb62c00000000000000000000000000000000000000000000000000000000000
*** Обратите внимание на то, как предыдущая строка переполняет макс uint256, но теперь 2s дополнение завернуто и, таким образом, не то же самое, что и выше ***
. BnNew / = Params () TargetTimespan () -> 0x7fffff0000000000000000000000000000000000000000000000000000000000 / 0x127500 = 0xd9ebbc99a778556334111eefccdaab889667445223000ddebbc99a77855
Новые Ожидаемые сложности биты: 0x1e0d9ebb
Обратите внимание на то, как расчетная величина значительно ниже, чем она должна быть, по сравнению с предыдущим поведением и, следовательно, приводит к требуемому доказательству работы будучи ~ 154000 раз сложнее, чем это должно быть.
Чуть больше математики показывает, что, так как максимальный множитель nTargetTimespan * 4 = 4838400 = 0x49d400, это условие переполнения может произойти до тех пор, трудность >= 0x377aef2669de1558cd0447bbf336aae22599d11488c00377aef2669de15 (компактные биты: 0x1e0377ae).
Простое исправление с помощью более высокой точности арифметических операций для промежуточных результатов. Джош Rickmar работает над патчем, который делает это.