Bitcoin Forum
November 09, 2024, 07:47:51 AM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Откуда берётся среднее время генерации?  (Read 1599 times)
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 07, 2013, 11:41:27 AM
 #1

Вопрос у меня такой: Когда после очередного изменения difficulty (сложности) сгенерировано ровно 2016 блоков, следующий блок должен генерироваться уже с новой сложностью. Принцип её расчёта понятен: Исходя из того, что 2016 блоков в идеале должны генерироваться за две недели (т.е. за 1209600 секунд), мы смотрим, какая была скорость генерации в предшествующий период, и в соответствующее количество раз изменяем сложность. Вопрос, как водится, заключается в деталях. Сложность определяется с точностью не менее 6-ти десятичных знаков, т.е. если я в этот самый момент хочу рассчитать новую сложность, я  должен получить значение ни на миллионную долю не меньше и не больше, чем стандартный алгоритм. Вот я беру последние 2016 блоков, смотрю записанное в их заголовках время генерации (а оно указывается с точностью до секунды), беру разность между временем генерации последнего и первого блоков, рассчитываю отсюда новую сложность и ... получаю совпадение с установленным новым значением сложности не более, чем в четырёх знаках. Huh Что здесь не так? Может алгоритм иначе оценивает время генерации 2016-ти блоков? Например, считает не с точностью до секунды, а округляет до минут? Где можно узнать точный алгоритм оценки среднего времени генерации блока, используемый при расчёте нового значения сложности?
awoland
Legendary
*
Offline Offline

Activity: 1498
Merit: 1021

Was mich nicht umbringt macht mich stärker [F.N.]


View Profile WWW
November 07, 2013, 11:52:45 AM
 #2

Точный алгоритм есть в исходниках bitcoin-qt. Это и есть эталонная реализация алгоритма.

Was mich nicht umbringt macht mich stärker [F.N.]
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 07, 2013, 11:59:01 AM
 #3

Точный алгоритм есть в исходниках bitcoin-qt. Это и есть эталонная реализация алгоритма.
Может Вы поможете мне в них разобраться? Я уже лазил в какие-то исходники bitcoin на GitHub, в основных принципах разобрался, но вот откуда именно берётся оценка времени генерации - "ни асилил"...
bee7
Hero Member
*****
Offline Offline

Activity: 574
Merit: 523


View Profile
November 07, 2013, 12:02:24 PM
 #4

Все определяется в функции GetNextWorkRequired (https://github.com/bitcoin/bitcoin/blob/master/src/main.cpp). Учитывается не среднее время на блок, а время, потребовавшееся для генерации 2016ти блоков (так ошибка вычислений меньше). Новый target (а не сложность) вычисляется исходя из cотношения LastTarged*ActualTimespan/TargetTimeSpan, где LastTarget - то значение, которое использовалось на момент пересчета, ActualTimespan - фактическое время, потребовавшееся на расчет 2016ти блоков, TargetTimeSpan - численно равен 2 неделям в секундах.
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 07, 2013, 12:49:19 PM
 #5

Это понятно. Время, как я понял, извлекается в этой строчке кода:
Code:
1170   int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
Но не сходится у меня. Roll Eyes

Вот, например, рассмотрим блок 266111 (см. на http://blockexplorer.com), у него время 2013-10-26 02:23:14 и это последний блок со сложностью 19100ab6. Раньше него на 2016 - это блок 264095, его время: 2013-10-16 12:15:18. Разница в секундах: 828476. Отношение к количеству секунд в двух неделях: 0.684917328. Умножаем 0x100ab6 на этот коэффициент, получаем 0x0afcс1. Но у следующего блока сложность 190afc85, а не 190afcс1. Близко, но не точно...
bee7
Hero Member
*****
Offline Offline

Activity: 574
Merit: 523


View Profile
November 07, 2013, 01:28:50 PM
 #6

Ну вы 0x100ab6 сначала отмаcштабируйте на 2^((0x19-3)*8 ), а потом умножайте/делите. После чего результат разделите на 2^((0x19-3)*8 ) . См bignum.h
И вообще, применять плавающую арифметику для проверки целочисленной несовсем корректно, IMHO. Т.е. результат вы получите близкий, но в силу ошибок округления не точный.
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 07, 2013, 01:53:46 PM
 #7

Ну вы 0x100ab6 сначала отмаcштабируйте на 2^((0x19-3)*8 ), а потом умножайте/делите. После чего результат разделите на 2^((0x19-3)*8 ) . См bignum.h
И вообще, применять плавающую арифметику для проверки целочисленной несовсем корректно, IMHO. Т.е. результат вы получите близкий, но в силу ошибок округления не точный.
Нет, это не ошибка округления. Масштабирование/размасштабирование на 2^((0x19-3)*8 )  - это лишнее, ибо в данном случае это сводится к дописыванию справа 44-х нулей, а потом к удалению этих же самых нулей из результата.

Я, кажется, разобрался. Если из времени последнего блока вычесть время блока, который раньше на 2015 (а не на 2016!!), то всё сходится (проверил на нескольких примерах). Получается, что в стандартном алгоритме ошибка? Ха, ха...
fsb4000
Legendary
*
Offline Offline

Activity: 1400
Merit: 1000



View Profile
November 07, 2013, 02:01:35 PM
 #8

у тебя ошибка. ты сначала считал 2017 блоков. 2016 блоков они все блоки с одной сложностью.
Между  266111  и 264095 2017 блоков. (266111 - 264095 +1 = 2017)
Нужно считать между 266111 264096 (266111 - 264095 +1 = 2016)

P.S. +1 нужно добавлять, потому что иначе мы не учитываем первый блок. Вот пример, показывающий почему нужно добавлять 1, если бы блоки были от 1 до 10, то количество блоков : 10 - 1 +1 = 10
bee7
Hero Member
*****
Offline Offline

Activity: 574
Merit: 523


View Profile
November 07, 2013, 02:03:08 PM
 #9

Нет, это не ошибка округления. Масштабирование/размасштабирование на 2^((0x19-3)*8 )  - это лишнее, ибо в данном случае это сводится к дописыванию справа 44-х нулей, а потом к удалению этих же самых нулей из результата.
Вы это серьёзно? Ну-ну.


Я, кажется, разобрался. Если из времени последнего блока вычесть время блока, который раньше на 2015 (а не на 2016!!), то всё сходится (проверил на нескольких примерах). Получается, что в стандартном алгоритме ошибка? Ха, ха...

Алгоритм берет последние 2016 блоков (см исходники). Если вы видите там ошибку - обоснуйте и обратитесь к девелоперам. Я продолжение этой дискуссии для себя считаю пустой тратой времени.
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 07, 2013, 02:09:57 PM
Last edit: November 07, 2013, 02:26:54 PM by epros
 #10

у тебя ошибка. ты сначала считал 2017 блоков. 2016 блоков они все блоки с одной сложностью.
Между  266111  и 264095 2017 блоков. (266111 - 264095 +1 = 2017)
Нужно считать между 266111 264096 (266111 - 264095 +1 = 2016)

P.S. +1 нужно добавлять, потому что иначе мы не учитываем первый блок. Вот пример, показывающий почему нужно добавлять 1, если бы блоки были от 1 до 10, то количество блоков : 10 - 1 +1 = 10
Ой, не путайте. Никакого "+1" не надо. (Время 266111-ого) - (Время 264095-ого) - это время генерации 2016-ти блоков: Начиная с 264096-ого (264095-ой не входит) и по 266111-ый включительно.

Нет, это не ошибка округления. Масштабирование/размасштабирование на 2^((0x19-3)*8 )  - это лишнее, ибо в данном случае это сводится к дописыванию справа 44-х нулей, а потом к удалению этих же самых нулей из результата.
Вы это серьёзно? Ну-ну.
Абсолютно. Разве что могу уточнить, что удалять из результата нужно будет 44 цифры (16-ти-ричных), а не обязательно нулей.


Алгоритм берет последние 2016 блоков (см исходники). Если вы видите там ошибку - обоснуйте и обратитесь к девелоперам. Я продолжение этой дискуссии для себя считаю пустой тратой времени.
Да начхать мне на ошибки. Тем более, что ошибка не критичная и теперь исправлять её уже точно никто не будет (иначе старые клиенты с исправленными друг друга не поймут). Просто надо будет иметь это в виду при собственных расчётах.
yurm
Full Member
***
Offline Offline

Activity: 216
Merit: 100


View Profile
November 07, 2013, 11:57:53 PM
 #11

Это понятно. Время, как я понял, извлекается в этой строчке кода:
Code:
1170   int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
Но не сходится у меня. Roll Eyes
Так там же пятью строчками выше ясно написано:
Code:
1165    for (int i = 0; pindexFirst && i < nInterval-1; i++)
1166        pindexFirst = pindexFirst->pprev;
Обратите внимание на -1. Берётся разница между временами первого и последнего блока, сгенерированных при одной и той же сложности. Соответственно, время, прошедшее между генерацией "пограничных" блоков (с разной сложностью), как бы выпадает (не оказывает влияния на сложность). Не знаю точно причин, но подозреваю, что это by design так.

BTC donation:1DPUVJWeN2CNgJvRx5MtbsYWnFsKHxXWrc
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 08, 2013, 06:43:03 AM
 #12

Обратите внимание на -1. Берётся разница между временами первого и последнего блока, сгенерированных при одной и той же сложности. Соответственно, время, прошедшее между генерацией "пограничных" блоков (с разной сложностью), как бы выпадает (не оказывает влияния на сложность). Не знаю точно причин, но подозреваю, что это by design так.
Да, спасибо, я уже понял. Вряд ли в этом "-1" есть какой-то практический смысл, скорее, это просто мелкая ошибка разработчика, которая теперь навечно войдёт в историю.  Wink
К сожалению, мне теперь в собственной программе придётся учитывать, что при смене сложности из расчёта выпадает время генерации одного блока. Т.е., скажем, в базе с историческими данными майнинга придётся хранить не по одному параметру DATETIME на каждый момент смены сложности, а по два.
bee7
Hero Member
*****
Offline Offline

Activity: 574
Merit: 523


View Profile
November 08, 2013, 08:25:22 AM
 #13

Обратите внимание на -1. Берётся разница между временами первого и последнего блока, сгенерированных при одной и той же сложности. Соответственно, время, прошедшее между генерацией "пограничных" блоков (с разной сложностью), как бы выпадает (не оказывает влияния на сложность). Не знаю точно причин, но подозреваю, что это by design так.
Да, спасибо, я уже понял. Вряд ли в этом "-1" есть какой-то практический смысл, скорее, это просто мелкая ошибка разработчика, которая теперь навечно войдёт в историю.  Wink
К сожалению, мне теперь в собственной программе придётся учитывать, что при смене сложности из расчёта выпадает время генерации одного блока. Т.е., скажем, в базе с историческими данными майнинга придётся хранить не по одному параметру DATETIME на каждый момент смены сложности, а по два.

Эта "ошибка" позволяет сделать самый первый пересчет сложности в блокчейне. Для биткоина это не актуально было, думаю, что у Сатоши пересчет был "отключен", потому что намайнил он за месяц работы в январе 2009го дофига и небыло ни одного пересчета, но актуально для форков (NMC и пр). Проблема в том, что генезис блок содержит таймстамп, не имеющий отношения к рассчтету блоков никакого, т.е. период на генерацию самого первого блока  посчитать таким образом невозможно.

Насчет нужно ли брать TargetTimeSpan на 1 блок меньше в таком случае, нужно подумать (с математической точки зрения - да, нужно, может есть какой-то другой смысл в этом)
epros (OP)
Newbie
*
Offline Offline

Activity: 45
Merit: 0


View Profile
November 08, 2013, 05:25:27 PM
 #14

Эта "ошибка" позволяет сделать самый первый пересчет сложности в блокчейне.
Для первого пересчёта можно было сделать отдельное исключение в коде.

Для биткоина это не актуально было
Наверное не так уж принципиально, что алгоритм подгоняет сложность под эталон "2015 блоков за две недели" вместо задуманного "2016 блоков за две недели". Правда тот факт, что начало периода оценки не совпадает с моментом предыдущего переключения сложности, создаёт некоторые неудобства. Об этом, вероятно, просто не подумали. Но теперь уж придётся принимать то, что есть.
bee7
Hero Member
*****
Offline Offline

Activity: 574
Merit: 523


View Profile
November 08, 2013, 06:46:19 PM
 #15

Эта "ошибка" позволяет сделать самый первый пересчет сложности в блокчейне.
Для первого пересчёта можно было сделать отдельное исключение в коде.

Для биткоина это не актуально было
Наверное не так уж принципиально, что алгоритм подгоняет сложность под эталон "2015 блоков за две недели" вместо задуманного "2016 блоков за две недели". Правда тот факт, что начало периода оценки не совпадает с моментом предыдущего переключения сложности, создаёт некоторые неудобства. Об этом, вероятно, просто не подумали. Но теперь уж придётся принимать то, что есть.

Да, теперь это не исправить: GetNextWorkRequired вызывается из AcceptBlock, которая, в свою очередь, используется для любого внесения блоков в чейн, в том числе, при синхронизации и работе с сетью.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!