Генерация Genesis Block

В ходе исследований Blockchain технологии за последние пол года наша команда сгенерировала уже более 20 раз различные вариации форков. Но вспоминаем зачастую первый Genesis Block.

Пытаясь создать свой первый форк используя доступные в англоязычном интернете тематические статьи, мы не смогли четко следуя любой из них создать свою криптовалюту. Поэтому с первых же дней начала исследований мы закопались уже в тонкостях архитектуры Blockchain. Самое долгое было разобраться с генерацией Genesis block. Рассмотрим по шагово сам процесс:

Исходные данные для рассмотрения примера

Подготовленная виртуальная машина перед началом генерации Genesis Block.
Подготовленная виртуальная машина перед началом генерации Genesis Block.

 

  • Примеры рассматриваются на исходном коде электронной валюты Litecoin версии 0.8.7.4
  • В работе использовалась система с ОС Ubuntu 14.04 LTC
  • Перед началом рассмотрения примера система должна быть подготовлена к компиляции и запуску кошелька Litecoin.
  • Должна быть проведена успешно компиляция исходного кода Litecoin на данной системе.

 

 

Компиляция  1:

Самое первое, что необходимо изменить — это константу “pszTimestamp”.  Данная переменная находится в файле ./src/main.cpp в строчке 2782. Сама строчка кода ниже:

const char* pszTimestamp = «NY Times 05/Oct/2011 Steve Jobs, Apple’s Visionary, Dies at 56″;

Изменив данную строчку и скомпилировав кошелек заново вы уже не сможете участвовать в действующей сети. Именно с этого момента начинается создание собственной сети электронной валюты данного вида.

Меняем pszTimestamp при создании Genesis block.  Создание собственного форка Bitcoin.
Меняем pszTimestamp при создании Genesis block. Создание собственного форка Bitcoin.

“Почему в данной строчке записан заголовок новости?”

Самая достоверная информация на наш взгляд говорит, что записав какую то свежую новость при создании Genesis block’а вы получаете неопровержимое доказательство для любого о дате начала работы сети.

Меняем значение константы на недавнюю международную новость, сохраняем изменение и затем компилируем заново код.

Запускаем из консоли кошелек и получаем ошибку.

Ошибка при запуске скомпилированного кошелька с измененным pszTimestamp.  Создание собственного форка Bitcoin.
Ошибка при запуске скомпилированного кошелька с измененным pszTimestamp. Создание собственного форка Bitcoin.

Компиляция 2:

После того, как вы скомпилировали кошелек с измененной константой “pszTimestamp” и запустили его, произошел сбой и выдалась ошибка, при этом в  лог файл кошелька с именем “debug.log” записалась информация об ошибке. В этой информации содержится вторая константа, которую необходимо поменять.

Итак, после неудачного запуска в логе будет следующая ошибка:

Эта ошибка содержит хэш, являющийся корнем дерева Меркле для Genesis block. В логе вы увидите всего 3 варианта данного хэша, верный - последний.
Эта ошибка содержит хэш, являющийся корнем дерева Меркле для Genesis block. В логе вы увидите всего 3 варианта данного хэша, верный — последний.

Эта ошибка содержит хэш, являющийся корнем дерева Меркле для Genesis block.

В логе вы увидите всего 3 варианта данного хэша, верный — последний. Берем его и вставляем в строчку 2809 из того же файла ./src/main.cpp после “ …uint256(«0x ”

assert(block.hashMerkleRoot == uint256(«0x97ddfbbae6be97fd6cdf3e7ca13232a3afff2353e29badfab7f73011edd4ced9″));

Замена HashMerkleRoot при генерации Genesis Block.
Замена HashMerkleRoot при генерации Genesis Block.

 

Сохраняем изменения и после этого необходимо дописать код, который будет запускать поиск хэша Genesis block’а при не совпадении результатов проверки при запуске.

Ниже пример кода, его необходимо вставить в файле ./src/main.cpp между строчками 2809 и 2810 содержащими следующий код:

Исходный код:

assert(block.hashMerkleRoot == uint256(«0x97ddfbbae6be97fd6cdf3e7ca13232a3afff2353e29badfab7f73011edd4ced9″));

block.print();

 

Код для вставки:

//=====

// If genesis block hash does not match, then generate new genesis hash.

if (true && block.GetHash() != hashGenesisBlock)

{

printf(«Searching for genesis block…\n»);

// This will figure out a valid hash and Nonce if you’re

// creating a different genesis block:

uint256 hashTarget = CBigNum().SetCompact(block.nBits).getuint256();

uint256 thash;

char scratchpad[SCRYPT_SCRATCHPAD_SIZE];

 

loop

{

#if defined(USE_SSE2)

// Detection would work, but in cases where we KNOW it always has SSE2,

// it is faster to use directly than to use a function pointer or conditional.

#if defined(_M_X64) || defined(__x86_64__) || defined(_M_AMD64) || (defined(MAC_OSX) && defined(__i386__))

// Always SSE2: x86_64 or Intel MacOS X

scrypt_1024_1_1_256_sp_sse2(BEGIN(block.nVersion), BEGIN(thash), scratchpad);

#else

// Detect SSE2: 32bit x86 Linux or Windows

scrypt_1024_1_1_256_sp(BEGIN(block.nVersion), BEGIN(thash), scratchpad);

#endif

#else

// Generic scrypt

scrypt_1024_1_1_256_sp_generic(BEGIN(block.nVersion), BEGIN(thash), scratchpad);

#endif

if (thash <= hashTarget)

break;

if ((block.nNonce & 0xFFF) == 0)

{

printf(«nonce %08X: hash = %s (target = %s)\n», block.nNonce, thash.ToString().c_str(), hashTarget.ToString().c_str());

}

++block.nNonce;

if (block.nNonce == 0)

{

printf(«NONCE WRAPPED, incrementing time\n»);

++block.nTime;

}

}

printf(«block.nTime = %u \n», block.nTime);

printf(«block.nNonce = %u \n», block.nNonce);

printf(«block.GetHash = %s\n», block.GetHash().ToString().c_str());

}

 

//=====

Вставка кода для генерации хэша Genesis block при создании собственного форка.
Вставка кода для генерации хэша Genesis block при создании собственного форка.

Сохраняем изменения и заново компилируем кошелек.

 

Компиляция 3.

Итак вы скомпилировали теперь кошелек с измененными параметрами “pszTimestamp”, “hashMerkleRoot”, а также внесенными изменениями в код для создания хэша Genesis block’a.

Теперь запускаем из консоли кошелек и замечаем, что ошибок теперь консоль не выдает и замечаем, что наш процессор загрузился — это значит начался подбор хэша.

Для того, чтобы быть полностью уверенным в том, что поиск хэша начался открываем debug.log и видим, что туда начали записываться варианты подбора хэша.

Все варианты подбора хэша для Genesis block записываются в лог файл. Видя, что файл постоянно добавляется новыми вариантами хэша вы будете уверены, что процесс поиска идет.
Все варианты подбора хэша для Genesis block записываются в лог файл. Видя, что файл постоянно добавляется новыми вариантами хэша вы будете уверены, что процесс поиска идет.

 

Этот процесс может занять примерно от 1 до 20 минут, в зависимости от мощности вашего ПК и удачи :)

Обратите внимание! После того, как блок будет найдет в консоли ничего не появится. Просто процессор разгрузится и в debug.log появятся данные genesis блока. Теперь заглядываем в debug.log и находим следующую строчку.

После нахождения хэша появятся данные genesis блока в debug.log
После нахождения хэша появятся данные genesis блока в debug.log

 

Нам необходимо взять хэш блока и вставить его взамен неверного.  Меняем его в файле ./src/main.cpp в строчке 38 после : “… hashGenesisBlock(«0x”

uint256 hashGenesisBlock(«0x12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2″);

Да, и еще nBits, nTime сверяем.

Вставляем верный хэш Genesis block'a
Вставляем верный хэш Genesis block’a
Да, и еще nBits, nTime сверяем
Да, и еще nBits, nTime сверяем

 

Автор статьи: Сергей Лоншаков, Team lead Bitfork Develop, lonshakov@bitfork-develop.com.