В ходе исследований Blockchain технологии за последние полгода наша команда сгенерировала уже более 20 раз различные вариации форков. Но вспоминаем зачастую первый Genesis Block.
Пытаясь создать свой первый форк, используя доступные в англоязычном интернете тематические статьи, мы не смогли создать свою криптовалюту, четко следуя любой из них. Поэтому с первых же дней начала исследований мы закопались уже в тонкостях архитектуры Blockchain. Самое долгое было разобраться с генерацией 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»;
Изменив данную строчку и скомпилировав кошелек заново, вы уже не сможете участвовать в действующей сети. Именно с этого момента начинается создание собственной сети электронной валюты данного вида.
“Почему в данной строчке записан заголовок новости?”
Самая достоверная информация, на наш взгляд, говорит, что, записав какую-то свежую новость при создании Genesis block’а, вы получаете неопровержимое доказательство для любого о дате начала работы сети.
Меняем значение константы на недавнюю международную новость, сохраняем изменение и затем компилируем заново код.
Запускаем из консоли кошелек и получаем ошибку.
Компиляция 2:
После того, как вы скомпилировали кошелек с измененной константой “pszTimestamp” и запустили его, произошел сбой и выдалась ошибка. При этом в лог файл кошелька с именем “debug.log” записалась информация об ошибке. В этой информации содержится вторая константа, которую необходимо поменять.
Итак, после неудачного запуска в логе будет следующая ошибка:
Эта ошибка содержит хэш, являющийся корнем дерева Меркле для Genesis block.
В логе вы увидите всего 3 варианта данного хэша, верный — последний. Берем его и вставляем в строчку 2809 из того же файла ./src/main.cpp после “ …uint256(«0x ”
Сохраняем изменения, и после этого необходимо дописать код, который будет запускать поиск хэша 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());
}
//=====
Сохраняем изменения и заново компилируем кошелек.
Компиляция 3.
Итак, вы скомпилировали теперь кошелек с измененными параметрами “pszTimestamp”, “hashMerkleRoot”, а также внесенными изменениями в код для создания хэша Genesis block’a.
Теперь запускаем из консоли кошелек и замечаем, что ошибок теперь консоль не выдает, также замечаем, что наш процессор загрузился — это значит начался подбор хэша.
Для того, чтобы быть полностью уверенным в том, что поиск хэша начался, открываем debug.log и видим, что туда начали записываться варианты подбора хэша.
Этот процесс может занять примерно от 1 до 20 минут, в зависимости от мощности вашего ПК. Удачи 🙂
Обратите внимание! После того, как блок будет найдет, в консоли ничего не появится. Просто процессор разгрузится и в debug.log, появятся данные genesis блока. Теперь заглядываем в debug.log и находим следующую строчку.
Нам необходимо взять хэш блока и вставить его взамен неверного. Меняем его в файле ./src/main.cpp в строчке 38 после : “… hashGenesisBlock(«0x”
uint256 hashGenesisBlock(«0x12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2»);
Да, и еще nBits, nTime сверяем.
Автор статьи: Сергей Лоншаков, Team lead Bitfork Develop, lonshakov@bitfork-develop.com.