В этой статье объясняется, что такое Argon2id, как Blocknet превращает его в доказательство работы, почему он вознаграждает большой объём быстрой памяти и как seine выжимает максимум хешей из каждого CPU, на котором работает.
Что такое Argon2id?
Argon2id — это требовательная к памяти хеш-функция, описанная в RFC 9106 для хеширования паролей и доказательства работы. Она заполняет большую область памяти блоками по 1 KiB, строя каждый блок из предыдущего и более раннего блока, выбранного в другом месте памяти, поэтому для вычисления хеша нужна настоящая память, а не только процессорное время.
RFC 9106, опубликованный в 2021 году исследовательской группой Crypto Forum Research Group в IRTF, описывает «требовательную к памяти функцию Argon2 для хеширования паролей и приложений с доказательством работы». У Argon2 три варианта:
- Argon2d выбирает, какой более ранний блок читать, на основе уже вычисленных данных. RFC называет его «подходящим для криптовалют и приложений с доказательством работы».
- Argon2i выбирает блоки в порядке, который не зависит от данных, и это подходит для хеширования паролей.
- Argon2id «работает как Argon2i в первой половине первого прохода по памяти и как Argon2d в остальной части». Это основной вариант: по словам RFC, его обязана поддерживать каждая реализация.
Внутри Argon2 рассматривает память как длинную последовательность блоков по 1 024 байта. Каждый новый блок — это результат функции сжатия G, применённой к двум входам: блоку прямо перед ним и опорному блоку из более ранней части памяти. G построена на раундах перемешивания хеш-функции BLAKE2b, которые работают с 64-битными словами с помощью сложений, XOR и циклических сдвигов.
Как Blocknet использует Argon2id для доказательства работы?
Blocknet фиксирует каждый параметр Argon2id в коде консенсуса, поэтому каждый майнер и каждая нода вычисляют одну и ту же функцию:
| Параметр | Значение в Blocknet | Что это значит |
|---|---|---|
| Вариант | Argon2id, версия 1.3 | Основной вариант RFC 9106 |
| Память | 2 GiB (2 097 152 KiB) | 2 097 152 блока по 1 KiB заполняются на каждый хеш |
| Итерации | 1 | Один проход по памяти |
| Параллелизм | 1 | Одна полоса, поэтому один хеш выполняется в одном потоке |
| Выход | 32 байта | Значение, которое сравнивается с целью |
| Пароль | 8-байтовый nonce, little-endian | Число, которое майнер меняет при каждой попытке |
| Соль | Заголовок блока без nonce | Привязывает хеш к одному шаблону блока |
Майнинг — это поиск. Майнер берёт заголовок блока, который хочет опубликовать, выбирает nonce и вычисляет хеш Argon2id. Если 32-байтовый результат, прочитанный как число, не выше цели сети, блок действителен. Если нет, майнер пробует следующий nonce.
Цель напрямую следует из сложности: это (2^256 − 1), делённое на сложность. Поэтому сложность примерно равна количеству хешей, которое вся сеть в среднем должна вычислить, чтобы найти один блок. Blocknet рассчитан на один блок каждые пять минут, поэтому хешрейт сети примерно равен сложности, делённой на 300 секунд. Сама сложность следует за временем недавних блоков через взвешенное скользящее среднее по 60 блокам.
Пулы работают так же, но с более лёгкой целью. На bntpool.com seine отправляет шары, которые удовлетворяют более низкой сложности для каждого подключения, и любая шара, которая удовлетворяет и цели сети, — это найденный блок.
Почему требовательное к памяти доказательство работы подходит обычным компьютерам?
Параметры выше ставят память в центр каждого хеша. При одном проходе и одной полосе вторая половина прохода зависит от данных: позиция опорного блока для каждого блока берётся из первого 64-битного слова блока, вычисленного прямо перед ним. Майнер не может знать, какой ранний блок ему понадобится, до того момента, когда он понадобится, поэтому держит в памяти всю область в 2 GiB.
Майнер мог бы попробовать хранить меньше блоков и пересчитывать недостающие по требованию. RFC 9106 называет это компромиссами между временем и памятью: они экономят память «ценой большего числа вызовов внутренней функции сжатия». За каждый блок, выброшенный из области памяти, приходится платить заново дополнительными вычислениями.
Именно это делает конструкцию дружелюбной к обычному железу. Настольные ПК, ноутбуки и серверы уже поставляются с гигабайтами ОЗУ и контроллерами памяти, рассчитанными на быструю работу с ней. Чипу, спроектированному только для хеширования, всё равно понадобились бы гигабайты быстрой памяти рядом с хеширующими ядрами. Первая рекомендуемая настройка в RFC 9106 — Argon2id с одним проходом по 2 GiB памяти, то же число проходов и тот же объём памяти, что использует Blocknet, и по словам RFC, такая настройка «максимизирует затраты противника на специализированное оборудование для перебора». В коде демона Blocknet этот выбор описан как требовательное к памяти доказательство работы, чтобы «противостоять ASIC и обеспечить честный майнинг». Argon2id спроектирован так, чтобы противостоять ASIC, а настройка 2 GiB воплощает эту идею, и майнинг остаётся открытым для компьютеров, которые у людей уже есть. Статья Доказательство работы и доказательство доли (PoW и PoS) объясняет, чем это отличается от стейкинга.
Почему хешрейт определяют ОЗУ и пропускная способность памяти?
Каждый поток майнинга держит свою область памяти в 2 GiB, поэтому объём ОЗУ определяет, сколько потоков можно запустить. Каждый хеш также записывает 2 GiB и читает примерно столько же из разбросанных мест, поэтому скорость и задержка памяти определяют, как быстро эти потоки работают. Память важна не меньше, чем число ядер процессора.
ОЗУ определяет число потоков. Параллелизм 1 означает, что один хеш выполняется в одном потоке, и каждому потоку нужна собственная полная область в 2 GiB. Компьютер с 16 GB, оставив место для системы, может запустить примерно шесть-семь потоков. seine измеряет вашу память, оставляет 5% в резерве и сам выбирает число потоков.
Пропускная способность и задержка определяют скорость каждого потока. Хеш создаёт 2 097 152 новых блока, и каждый из них читает опорный блок размером 1 KiB откуда-то из области памяти. В итоге на каждый хеш записывается около 2 GiB и читается около 2 GiB. При 10 H/s машина прогоняет через память примерно 40 GiB данных в секунду. В зависящей от данных половине каждый адрес чтения становится известен, только когда предыдущий блок готов, поэтому время ответа памяти важно не меньше, чем чистая пропускная способность.
Поэтому на странице производительности seine сказано, что хешрейт «зависит от пропускной способности и задержки памяти не меньше, чем от числа ядер». Руководство по железу для майнинга Blocknet показывает лучший результат для каждого поколения железа.
По той же причине помогают huge pages. Область в 2 GiB занимает 524 288 обычных страниц по 4 KiB или 1 024 страницы по 2 MiB. Меньше страниц большего размера — меньше промахов трансляции адресов при этих разбросанных чтениях, что добавило около 4–6% на тестовой машине seine. Настройка описана в руководстве по huge pages.
Как вычислительные ядра seine выжимают максимум из Argon2id?
Вычислительное ядро (kernel) — это внутренний цикл, который вычисляет блоки. Сборки seine для x86 содержат скалярное ядро, ядра AVX2 и AVX-512, а также потоковый вариант, а сборки для Arm — NEON с быстрым путём SHA3. Каждое по-своему справляется с работой, упирающейся в память:
- AVX2 и AVX-512 выполняют раунды функции G в стиле BLAKE2b сразу над многими 64-битными словами в широких векторных регистрах. У AVX-512 также есть встроенные инструкции циклического сдвига, которые эти раунды используют постоянно.
- Упреждающая выборка (prefetch) запрашивает опорные блоки из памяти до того, как они понадобятся. В первой половине прохода адреса известны заранее, поэтому seine запрашивает блоки на несколько шагов вперёд. Во второй половине seine запрашивает следующий опорный блок, как только первое слово текущего блока готово, и остальное сжатие выполняется, пока данные идут.
- Потоковое ядро держит 64 последних блока в небольшом кешируемом кольце и записывает каждый готовый блок в область памяти в 2 GiB потоковыми записями, которые идут прямо в память и оставляют кеш свободным для блоков, которые ещё нужны хешу. seine автоматически сравнивает его на процессорах AMD EPYC Zen 5.
- NEON с SHA3 использует инструкцию XAR из расширения Arm SHA3, которая выполняет XOR и циклический сдвиг за один шаг, вместе с более широким охватом упреждающей выборки.
На протестированном нами железе правильное ядро давало большую разницу. Это парные прогоны на одной машине с одинаковым числом потоков со страницы производительности seine:
| Машина | Смена ядра | Прирост |
|---|---|---|
| AWS Graviton4 (Neoverse V2), 16 потоков | NEON на SHA3 с упреждающей выборкой | +56% (с 13,80 до 21,50 H/s) |
| AWS Graviton3 (Neoverse V1), 16 потоков | NEON на SHA3 с упреждающей выборкой | +45% (с 12,52 до 18,17 H/s) |
| AMD EPYC 9R45 (Zen 5), 24 потока | AVX2 на потоковое | +30% (с 34,47 до 44,74 H/s) |
| Intel Xeon 8375C (Ice Lake), 1–4 потока | AVX2 на AVX-512 | +21–29% |
Ничего из этого не нужно выбирать самому. seine сравнивает ядра, которые поддерживает ваш CPU, на реальной работе майнинга, переключается только после двух подтверждённых приростов и кеширует победителя для следующего запуска. Лучшее ядро зависит от числа потоков, размера страниц и размещения на CPU, поэтому seine измеряет на вашем компьютере, а не зашивает выбор в код.
Чтобы заставить свою память работать, скачайте seine, запустите его и вставьте свой адрес. Остальное описано в руководстве по майнингу.
Часто задаваемые вопросы
Какие параметры Argon2id использует Blocknet для доказательства работы?
Blocknet использует Argon2id версии 1.3 с 2 GiB памяти, одной итерацией, параллелизмом 1 и 32-байтовым выходом. 8-байтовый nonce служит паролем, а заголовок блока без nonce — солью. Хеш выигрывает блок, если он не выше цели, заданной сложностью сети.
Почему каждому потоку майнинга Blocknet нужно 2 GB ОЗУ?
Каждый хеш Argon2id заполняет область памяти в 2 GiB из блоков по 1 KiB, а во второй половине этого прохода каждый новый блок читает более ранний, выбранный по данным, которые майнер не может предсказать. Поток должен держать всю свою область в памяти, поэтому каждому запущенному потоку нужно около 2 GB ОЗУ.
Почему Argon2id спроектирован так, чтобы противостоять ASIC?
Argon2id делает память самой дорогой частью каждого хеша. Каждому хешу Blocknet нужно 2 GiB быстрой памяти, через которую проходят гигабайты данных, поэтому чипу, созданному только для хеширования, всё равно понадобится много ОЗУ на каждое ядро. В коде демона этот выбор описан как требовательное к памяти доказательство работы, чтобы противостоять ASIC и обеспечить честный майнинг.
Может ли видеокарта майнить Argon2id в Blocknet?
Да. В Windows и Linux seine майнит на всех видеокартах NVIDIA в компьютере одновременно с CPU, и хешрейт суммируется. Вычислительные ядра компилируются при старте, а при первом запуске seine настраивает каждую видеокарту и кеширует результат, так что перед началом ничего настраивать не нужно.