13.02.2018
Майнинг в блокчейн-сетях: как он работает

В начале января правительство Китая приказало «армии биткойн-майнеров» страны, которые генерируют 3/4 всей мировой криптовалюты, прекратить работу. Одной из главных причин такого решения стало слишком высокое количество электроэнергии, которое потребляют майнинговые фермы — сейчас на обеспечение безопасности биткойн-блокчейна и добычу монет уходит порядка 0,2% всего мирового энергопотребления.

Сегодня поговорим, почему майнинг потребляет такое количество энергии, зачем он нужен и как реализуется. Мы расскажем об алгоритме хеширования (SHA-256) и о том, почему майнеры «обращаются за помощью» к пулам.

/ изображение Cindy Shebley CC

Для чего нужен майнинг

Майнинг часто считают способом создания новых биткойнов, однако это не совсем верно. Главная задача майнинга — обеспечить достижение консенсуса о том, какие транзакции считать валидными, чтобы не позволить кому-то из участников сети потратить уже использованные в другой транзакции монеты.

Для этого майнеры решают сложные математические задачи на своем оборудовании и расходуют электроэнергию, а взамен получают вознаграждение в виде биткойнов (и комиссии за обработку транзакций), что является стимулом защищать блокчейн дальше (сейчас за каждый блок сеть выдает 12,5 биткойнов).

В чем состоит работа майнеров

Майнеры занимаются тем, что подбирают значение хеша, которое бы подошло к транзакциям в блоке и позволило получить секретный ключ. Искомый хеш формируется на основании хеша предыдущего блока, случайного числа (nonce) и суммы хешей транзакций за прошедшие 10 минут. При этом он должен удовлетворять условиям системы: соответствовать заявленной сложности майнинга (Difficulty) и быть меньше целевой сложности (Target) — она определяет количество нулевых битов в начале искомого хеша.

Для вычисления сложности хеша можно использовать алгоритм с разложением в модифицированный ряд Тейлора, который приводится на страничке bitcoinwiki:

#include <iostream>
#include <cmath>
 
inline float fast_log(float val)
{
   int * const exp_ptr = reinterpret_cast <int *>(&val);
   int x = *exp_ptr;
   const int log_2 = ((x >> 23) & 255) - 128;
   x &= ~(255 << 23);
   x += 127 << 23;
   *exp_ptr = x;
 
   val = ((-1.0f/3) * val + 2) * val - 2.0f/3;
   return ((val + log_2) * 0.69314718f);
} 
 
float difficulty(unsigned int bits)
{
    static double max_body = fast_log(0x00ffff), scaland = fast_log(256);
    return exp(max_body - fast_log(bits & 0x00ffffff) + scaland * (0x1d - ((bits & 0xff000000) >> 24)));
}
 
int main()
{
    std::cout << difficulty(0x1b0404cb) << std::endl;
    return 0;
}

На момент написания статьи сложность блокчейн-сети составляет: 2874674234415.941, однако этот параметр пересчитывается через каждые 2016 блоков. Он увеличивается или уменьшается, чтобы поддержать среднюю скорость создания блоков (примерно 6 штук в час).

SHA-256

В качестве инструмента для хеширования в биткойн-блокчейне выбран алгоритм SHA-256. Далее, мы рассмотрим один из его раундов.

/ Раунд SHA-256 для восьми входных слов / Wikimedia / kockmeyer / CC

Предположим, что на вход алгоритму подали восемь слов, обозначим их A, B, C… H. Функция Ma выполняет побитовые операции со словами A, B и C — если большинство полученных значений нули, она также вернет ноль, иначе — единицу.

Блок Σ0 трижды сдвигает слово A: на 2, 13 и 22 бита, а сформированные значения побитно складываются операцией xor. Блок Σ1 работает аналогично — сдвиги выполняются на 6, 11 и 25 бит.

Блок Ch — это функция выбора результирующего бита на основании битов в E. Если входное значение единица, то на выход поступит соответствующий бит слова F, иначе — бит слова G.

Красные квадраты на схеме — это блоки 32-битного сложения, которые генерируют новые значения для A и E. Весь цикл повторяется 64 раза, после чего информация оказывается надежно зашифрованной.

Для иллюстрации процесса вычисления хеша, приведем код на Python, который формирует хеш слов «Bl0Ckchain» и «blockchain»:

import itertools
from hashlib import sha256

# Представляем входные данные в порядке от младшего к старшему
to_long = lambda x: sum(ord(b) << (8*i) for i, b in enumerate(x))

# На практике обычно используется двоичное представление nonce фиксированной длины;
# строки здесь - для большей наглядности. При использовании строк
# разделитель (":") обязателен, иначе PoW можно использовать
# повторно.
combine = lambda nonce, msg: str(nonce) + ":" + msg

def verify_pow(msg, nonce, difficulty):
    h = sha256(combine(nonce, msg)).digest()
    return to_long(h) % (1 << difficulty) == 0

def create_pow(msg, difficulty):
    for nonce in itertools.count(0):
        if verify_pow(msg, nonce, difficulty): return nonce

def print_pow(msg, nonce):
    print combine(nonce, msg), sha256(combine(nonce, msg)).hexdigest()
	
#----------------------------------------------------

msg = "Bl0Ckchain"
nonce = create_pow(msg, 16)
print_pow(msg, nonce)

# 6571:Bl0Ckchain 0000d087d242930aaf6ac5a790ae8d8ece6b502cdb70ba07c1168738b253d279

assert verify_pow(msg, nonce, 16)

msg = "blockchain"
nonce = create_pow(msg, 16)
print_pow(msg, nonce)

# 43952:blockchain 000027b5022f88d2da21bd2802268966050f5a0b031058ce4562939c13727303

assert verify_pow(msg, nonce, 16)
# Количество проделанной работы лишь статически близко к (1 << difficulty),
# для конкретных сообщений возможны отклонения
msg = "Bl0Ckchain"
nonce = create_pow(msg, 16)
print_pow(msg, nonce)
# 6571:Bl0Ckchain 0000d087d242930aaf6ac5a790ae8d8ece6b502cdb70ba07c1168738b253d279
assert verify_pow(msg, nonce, 16)

А вот проверка созданных хешей выполняется очень быстро, даже если на их создание ушло множество ресурсов:

msg = "blockchain"
nonce = 5263268363
print_pow(msg, nonce)
# 5263268363:blockchain 000000007cf39dfc8fccae534b39b5f362c16891abca02d0e7b1dbd5a129ee17
assert verify_pow(msg, nonce, 32)

Результат применения функции SHA-256 считается необратимым, поэтому подбор майнерами «целевого» хеша выполняется с помощью грубого перебора.

Так как биткойн-протокол использует двойное хеширование, то задачей майнеров становится нахождение второго прообраза хеша x’ (имея первый прообраз x хеша y, где y=H(x)), который бы удовлетворял условию y=H(x’). Однако с целью упрощения задачи, майнеры ищут лишь частичный прообраз — H(x)/2^(n-k)=0. Здесь n — «размер» результата (n=256 бит для SHA256), а k — это фактор, отвечающий за количество нулей в результирующем хеше. Например, k=20 потребует проведения порядка одного миллиона попыток.

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

Майнинг — совместные усилия участников сети

Сложность решения блока чрезвычайно велика. Сейчас майнеры в биткойн-сети выполняют порядка 20 млн терахешей в секунду, и эта цифра растет. Для сравнения, одна видеокарта выдает порядка 30 мегахешей в секунду. Также в своем блоге инженер Кен Ширриф (Ken Shirriff) попробовал решить блок с помощью ручки и бумаги — его достижение было еще скромнее (0,67 хешей в день).

/ Количество терахешей за секунду, по данным blockchain.info

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

Чтобы оценить процент проделанной работы, пул просит майнеров присылать информацию о нахождении частичных решений. Например, если биткойн-блокчейн требует, чтобы хеш блока имел 15 нулей, пул может просить присылать ему результаты с 10 нулями в качестве доказательства работы. Это в миллионы раз проще и такое решение майнер будет получать несколько раз в час.

За организацию взаимодействия пула и майнеров отвечают специальные протоколы. Например, протокол Stratum (еще есть Getwork, Getblocktemplate и другие), который используется большинством пулов. В своем блоге Кен Ширриф приводит код, пересылаемый пулом при выдаче задания, который содержит всю необходимую информацию для начала работы над блоком.

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

По своей сути майнинг биткойнов напоминает своеобразную «гонку вооружений». В самом начале люди майнили криптовалюту с помощью CPU, затем мощности центрального процессора стало недостаточно, и индустрия перешла на GPU и специализированное оборудование ASIC.

Отметим, что повышение сложности вычислений, помимо регулирования количества выпускаемых биткойнов, выступает в качестве защиты от атак на блокчейн. Если майнинг с использованием персональных компьютеров в масштабной сети окажется выгодным, злоумышленникам будет проще найти оборудование для атаки. Необходимость вкладываться в «железо» выступает ограничивающим фактором и дополнительным защитным механизмом.

Но подобная привязка к технике и «реальному миру» имеет свои недостатки. Как уже было отмечено, майнинг — достаточно «затратная» с точки зрения потребления энергии деятельность. На сегодняшний день биткойн-блокчейн расходует 48 ТВт/час в год, причем 30–40% энергии идет на охлаждение чипов.

Однако есть мнение, что объемы потребляемой энергии начнут постепенно снижаться после 2020 года, когда произойдет следующее уменьшение награды за блок, так как майнинг станет менее выгодным. В этом случае действенным способом замотивировать майнеров продолжать защищать информацию в блокчейне остается увеличение дохода с комиссий за обработку транзакций.

А для этого важно продолжать повышать интерес общества к экосистеме блокчейн-технологий. Это позволит в будущем использовать биткойн-блокчейн в качестве своеобразного «арбитра» для permissioned-блокчейнов и сайдчейнов, что позволит создать финансовую систему нового поколения.


Оригинал статьи

Майнинг в блокчейн-сетях: как он работает

В начале января правительство Китая приказало «армии биткойн-майнеров» страны, которые генерируют 3/4 всей мировой криптовалюты, прекратить работу. Одной из главных причин такого решения стало слишком высокое количество электроэнергии, которое потребляют майнинговые фермы — сейчас на обеспечение безопасности биткойн-блокчейна и добычу монет уходит порядка 0,2% всего мирового энергопотребления.

Сегодня поговорим, почему майнинг потребляет такое количество энергии, зачем он нужен и как реализуется. Мы расскажем об алгоритме хеширования (SHA-256) и о том, почему майнеры «обращаются за помощью» к пулам.

/ изображение Cindy Shebley CC

Для чего нужен майнинг

Майнинг часто считают способом создания новых биткойнов, однако это не совсем верно. Главная задача майнинга — обеспечить достижение консенсуса о том, какие транзакции считать валидными, чтобы не позволить кому-то из участников сети потратить уже использованные в другой транзакции монеты.

Для этого майнеры решают сложные математические задачи на своем оборудовании и расходуют электроэнергию, а взамен получают вознаграждение в виде биткойнов (и комиссии за обработку транзакций), что является стимулом защищать блокчейн дальше (сейчас за каждый блок сеть выдает 12,5 биткойнов).

В чем состоит работа майнеров

Майнеры занимаются тем, что подбирают значение хеша, которое бы подошло к транзакциям в блоке и позволило получить секретный ключ. Искомый хеш формируется на основании хеша предыдущего блока, случайного числа (nonce) и суммы хешей транзакций за прошедшие 10 минут. При этом он должен удовлетворять условиям системы: соответствовать заявленной сложности майнинга (Difficulty) и быть меньше целевой сложности (Target) — она определяет количество нулевых битов в начале искомого хеша.

Для вычисления сложности хеша можно использовать алгоритм с разложением в модифицированный ряд Тейлора, который приводится на страничке bitcoinwiki:

#include <iostream>
#include <cmath>
 
inline float fast_log(float val)
{
   int * const exp_ptr = reinterpret_cast <int *>(&val);
   int x = *exp_ptr;
   const int log_2 = ((x >> 23) & 255) - 128;
   x &= ~(255 << 23);
   x += 127 << 23;
   *exp_ptr = x;
 
   val = ((-1.0f/3) * val + 2) * val - 2.0f/3;
   return ((val + log_2) * 0.69314718f);
} 
 
float difficulty(unsigned int bits)
{
    static double max_body = fast_log(0x00ffff), scaland = fast_log(256);
    return exp(max_body - fast_log(bits & 0x00ffffff) + scaland * (0x1d - ((bits & 0xff000000) >> 24)));
}
 
int main()
{
    std::cout << difficulty(0x1b0404cb) << std::endl;
    return 0;
}

На момент написания статьи сложность блокчейн-сети составляет: 2874674234415.941, однако этот параметр пересчитывается через каждые 2016 блоков. Он увеличивается или уменьшается, чтобы поддержать среднюю скорость создания блоков (примерно 6 штук в час).

SHA-256

В качестве инструмента для хеширования в биткойн-блокчейне выбран алгоритм SHA-256. Далее, мы рассмотрим один из его раундов.

/ Раунд SHA-256 для восьми входных слов / Wikimedia / kockmeyer / CC

Предположим, что на вход алгоритму подали восемь слов, обозначим их A, B, C… H. Функция Ma выполняет побитовые операции со словами A, B и C — если большинство полученных значений нули, она также вернет ноль, иначе — единицу.

Блок Σ0 трижды сдвигает слово A: на 2, 13 и 22 бита, а сформированные значения побитно складываются операцией xor. Блок Σ1 работает аналогично — сдвиги выполняются на 6, 11 и 25 бит.

Блок Ch — это функция выбора результирующего бита на основании битов в E. Если входное значение единица, то на выход поступит соответствующий бит слова F, иначе — бит слова G.

Красные квадраты на схеме — это блоки 32-битного сложения, которые генерируют новые значения для A и E. Весь цикл повторяется 64 раза, после чего информация оказывается надежно зашифрованной.

Для иллюстрации процесса вычисления хеша, приведем код на Python, который формирует хеш слов «Bl0Ckchain» и «blockchain»:

import itertools
from hashlib import sha256

# Представляем входные данные в порядке от младшего к старшему
to_long = lambda x: sum(ord(b) << (8*i) for i, b in enumerate(x))

# На практике обычно используется двоичное представление nonce фиксированной длины;
# строки здесь - для большей наглядности. При использовании строк
# разделитель (":") обязателен, иначе PoW можно использовать
# повторно.
combine = lambda nonce, msg: str(nonce) + ":" + msg

def verify_pow(msg, nonce, difficulty):
    h = sha256(combine(nonce, msg)).digest()
    return to_long(h) % (1 << difficulty) == 0

def create_pow(msg, difficulty):
    for nonce in itertools.count(0):
        if verify_pow(msg, nonce, difficulty): return nonce

def print_pow(msg, nonce):
    print combine(nonce, msg), sha256(combine(nonce, msg)).hexdigest()
	
#----------------------------------------------------

msg = "Bl0Ckchain"
nonce = create_pow(msg, 16)
print_pow(msg, nonce)

# 6571:Bl0Ckchain 0000d087d242930aaf6ac5a790ae8d8ece6b502cdb70ba07c1168738b253d279

assert verify_pow(msg, nonce, 16)

msg = "blockchain"
nonce = create_pow(msg, 16)
print_pow(msg, nonce)

# 43952:blockchain 000027b5022f88d2da21bd2802268966050f5a0b031058ce4562939c13727303

assert verify_pow(msg, nonce, 16)
# Количество проделанной работы лишь статически близко к (1 << difficulty),
# для конкретных сообщений возможны отклонения
msg = "Bl0Ckchain"
nonce = create_pow(msg, 16)
print_pow(msg, nonce)
# 6571:Bl0Ckchain 0000d087d242930aaf6ac5a790ae8d8ece6b502cdb70ba07c1168738b253d279
assert verify_pow(msg, nonce, 16)

А вот проверка созданных хешей выполняется очень быстро, даже если на их создание ушло множество ресурсов:

msg = "blockchain"
nonce = 5263268363
print_pow(msg, nonce)
# 5263268363:blockchain 000000007cf39dfc8fccae534b39b5f362c16891abca02d0e7b1dbd5a129ee17
assert verify_pow(msg, nonce, 32)

Результат применения функции SHA-256 считается необратимым, поэтому подбор майнерами «целевого» хеша выполняется с помощью грубого перебора.

Так как биткойн-протокол использует двойное хеширование, то задачей майнеров становится нахождение второго прообраза хеша x’ (имея первый прообраз x хеша y, где y=H(x)), который бы удовлетворял условию y=H(x’). Однако с целью упрощения задачи, майнеры ищут лишь частичный прообраз — H(x)/2^(n-k)=0. Здесь n — «размер» результата (n=256 бит для SHA256), а k — это фактор, отвечающий за количество нулей в результирующем хеше. Например, k=20 потребует проведения порядка одного миллиона попыток.

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

Майнинг — совместные усилия участников сети

Сложность решения блока чрезвычайно велика. Сейчас майнеры в биткойн-сети выполняют порядка 20 млн терахешей в секунду, и эта цифра растет. Для сравнения, одна видеокарта выдает порядка 30 мегахешей в секунду. Также в своем блоге инженер Кен Ширриф (Ken Shirriff) попробовал решить блок с помощью ручки и бумаги — его достижение было еще скромнее (0,67 хешей в день).

/ Количество терахешей за секунду, по данным blockchain.info

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

Чтобы оценить процент проделанной работы, пул просит майнеров присылать информацию о нахождении частичных решений. Например, если биткойн-блокчейн требует, чтобы хеш блока имел 15 нулей, пул может просить присылать ему результаты с 10 нулями в качестве доказательства работы. Это в миллионы раз проще и такое решение майнер будет получать несколько раз в час.

За организацию взаимодействия пула и майнеров отвечают специальные протоколы. Например, протокол Stratum (еще есть Getwork, Getblocktemplate и другие), который используется большинством пулов. В своем блоге Кен Ширриф приводит код, пересылаемый пулом при выдаче задания, который содержит всю необходимую информацию для начала работы над блоком.

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

По своей сути майнинг биткойнов напоминает своеобразную «гонку вооружений». В самом начале люди майнили криптовалюту с помощью CPU, затем мощности центрального процессора стало недостаточно, и индустрия перешла на GPU и специализированное оборудование ASIC.

Отметим, что повышение сложности вычислений, помимо регулирования количества выпускаемых биткойнов, выступает в качестве защиты от атак на блокчейн. Если майнинг с использованием персональных компьютеров в масштабной сети окажется выгодным, злоумышленникам будет проще найти оборудование для атаки. Необходимость вкладываться в «железо» выступает ограничивающим фактором и дополнительным защитным механизмом.

Но подобная привязка к технике и «реальному миру» имеет свои недостатки. Как уже было отмечено, майнинг — достаточно «затратная» с точки зрения потребления энергии деятельность. На сегодняшний день биткойн-блокчейн расходует 48 ТВт/час в год, причем 30–40% энергии идет на охлаждение чипов.

Однако есть мнение, что объемы потребляемой энергии начнут постепенно снижаться после 2020 года, когда произойдет следующее уменьшение награды за блок, так как майнинг станет менее выгодным. В этом случае действенным способом замотивировать майнеров продолжать защищать информацию в блокчейне остается увеличение дохода с комиссий за обработку транзакций.

А для этого важно продолжать повышать интерес общества к экосистеме блокчейн-технологий. Это позволит в будущем использовать биткойн-блокчейн в качестве своеобразного «арбитра» для permissioned-блокчейнов и сайдчейнов, что позволит создать финансовую систему нового поколения.


Оригинал статьи