В наше время каждая бабушка слышала о криптовалютах, курсы майнинга проводят даже серьезные учебные заведения, а антивирусы все чаще кричат о заражении сайта или игрушки майнером. Пришло время на практике разобраться, что это такое, как работает, и написать свой криптомайнер.
В качестве криптовалюты возьмем Electroneum. Это довольно перспективная криптовалюта из семейства Monero. Как заверяют разработчики, она защищена от майнинга на специальном оборудовании, точнее, оборудование будет стоить больше, чем можно получить прибыли. Это дает примерно равные шансы всем майнерам. Так как в качестве основы была использована Monero, многое из написанного будет правдиво и для других криптовалют этого семейства.
Для начала разберемся, что же такое майнинг. По сути это проверка транзакций различных пользователей криптовалют. Нет никакого центрального органа, а подтвердить, что один участник сети не использовал свои деньги дважды или не попытался как-то еще обмануть систему, могут все остальные. За это майнеры получают награду в виде небольшого количества криптоденег. В эту сумму входит награда за создание нового блока и оплата за транзакции, которая взимается с пользователей, проводящих транзакцию, и уже включена в нее.
Создание нового блока представляет собой решение определенной математической задачи. Необходимо найти такой хеш блока, который был бы меньше значения, определяемого сетью. Это значение называется сложность (difficulty). Оно регулируется сетью, чтобы время создания блока было более-менее предсказуемо. Майнер, который первый решит задачу, получает всю награду. Награда за блок на сегодняшний день составляет 11 300,93 ETN, что примерно равно 146,2 доллара.
В блоке не обязательно должны быть транзакции других пользователей, может быть только одна транзакция создания новых денег. Зачем нужно просто раздавать деньги? Во-первых, это привлекает больше участников сети, во-вторых, снижает риск атаки на сеть, так как заработать легально получается проще.
Чтобы стать участником сети Electroneum, необходимо скачать пакет программ с официального сайта. Выбираем direct miner для своей платформы. После скачивания и распаковки нужно синхронизироваться с сетью — скачать все уже сгенерированные блоки. Для разработки и тестирования лучше пользоваться тестовой сетью с пониженной сложностью.
К сожалению, синхронизация «из коробки» может зависнуть на блоке 155750. Это связано с найденным критичным багом и кардинальными изменениями из-за этого в сети Electroneum (подробнее). Поэтому прежде чем запускать синхронизацию, нужно скачать файлик с правильной цепочкой блоков и положить его в папку
.electroneum/testnet/export/blockchain.raw. Затем выполнить импорт:
> ./electroneum—blockchain—import —testnet —verify 0 |
Теперь смело запускаем синхронизацию:
> ./electroneumd —testnet |
Далее создаем кошелек для начисления заработка:
> electoneum—wallet—cli —testnet |
Ответив на все вопросы, получаем публичный адрес в файлике
<название кошелька>.address.txt. Если лениво заморачиваться с развертыванием сервера Electroneum, можно воспользоваться онлайн-сервисом
nodes.hashvault.pro:26968.
Настало время запустить свой любимый редактор и приступать к кодированию. Для связи с сервисом Electroneum используется протокол
jsonrpc. Нам понадобится всего две команды: получить шаблон блока и отправить решение. Начнем с простого HTTP-клиента:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public String sendRpcCommand(String command) { // Определяем URL для связи с сервером. Для реальной, не тестовой сети порт будет 26968 URL url = new URL(«http://127.0.0.1:34568/json_rpc»); HttpURLConnection con = (HttpURLConnection) url.openConnection(); // Задаем параметры соединения. Разрешаем вывод, чтобы забрать ответ сервера con.setDoOutput(true); // Передавать будем данные в формате JSON con.setRequestProperty(«Content-Type», «application/json; charset=UTF-8»); con.setRequestProperty(«Accept», «application/json»); con.setRequestMethod(«POST»); // Отправляем команду OutputStream os = con.getOutputStream(); os.write(command.getBytes(«UTF-8»)); os.close(); StringBuilder sb = new StringBuilder(); int HttpResult = con.getResponseCode(); if (HttpResult == HttpURLConnection.HTTP_OK) { // Если соединение успешно, то забираем ответ сервера BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), «utf-8»)); String line; while ((line = br.readLine()) != null) { sb.append(line).append(«n»); } br.close(); return sb.toString(); } else { // Если соединение не удалось, то бросаем исключение с описанием проблемы throw new IOException(con.getResponseMessage()); } } |
Чтобы получить шаблон блока для вычисления, отправим команду
{ «jsonrpc»:«2.0», «id»:«0», «method»:«get_block_template», «params»:{ «wallet_address»:«44GBHzv6ZyQdJkjqZje6KLZ3xSyN1hBSFAnLP6EAqJtCRVzMzZmeXTC2AHKDS9aEDTRKmo6a6o9r9j86pYfhCWDkKjbtcns», «reserve_size»:8 } } |
В качестве параметра wallet_address указываем адрес из файла
<название кошелька>.address.txt. Адрес используется, чтобы сразу сгенерировать транзакцию получения награды за расчет блока. Параметр
reserve_size задает, сколько выделить зарезервированных байтов, которые потом можно использовать при майнинге. Максимальное число — 255 байт.
В результате получаем:
{ «id»: «0», «jsonrpc»: «2.0», «result»: { «blockhashing_blob»: «070784e5dbda054486739aac8830906e18272012b97b98993afccf89d0044241193d1788f760cb0000000057754af7e8324054869263b355ede600c2381cbdf6acf2dc8f2b26f4a9a82bae14», «blocktemplate_blob»: «070784e5dbda054486739aac8830906e18272012b97b98993afccf89d0044241193d1788f760cb000000000183d71401fff1d61401eff844020117b5d2cead5bd512ab4b0a2e73377049c49c69ffc916687e811bbb0f5f65322b01d67fec53c3f1cab976537a4ab4ebba03c89849d554963df6ed1a0023e6d5d9e90208000000000000000013d5c0b347172631f9b0175365936f98b00198d8caf3dbb77edc6c002dbb6c302776e7d543da92fdf5c30e91d4b21762eb6fe5daf8959b519f3de65a3cd80adda1e5674fedeb2a5038577ea2fe9eb6a3fd2162a3a09cbe6d3b62c9b04a29d47c5c14c119f0812448ab4e14a76f1c2ddc2ff6ac0b97f1fb9e4cabf0ef2adf79221a3e865b8d9252f41f31e110326b78b0c506e9f18eb094305b6216221c2bd3f9d996bedf54dbb4c0bfe4fea6f2240181c91789270a48cae44d7662e1a13aae45c3edc3247736879f6aa2670b8816e551856b912f11269979fac1c97203365247eaee476ed815e3fa597b5230db7e0162816b55b23d2bfb8b9506492e8359f8ba33807eab0972a7837893163cadf314888dbb64190fa00553156dc7b05574eacd3b9a268666201ab202b23ecf960565c01a6a61fe5f03ba5b6c22d7e6639e7708941c876ecdc191cec4c5797e520855d9cc34ef9c3866ded9a4722c6437363bb7a47c9dbd303c15a18dfb72028054cd438924978f5c5d32be3bcbc622e0fb4b9aef865fea52a09f518952ec0aa94bbfa969f192a93b80a50fe7af2728cbd76e739e9af80aee2644fb2bbe1c82724bdc4678a5a206a945a3e49dabcb10ae0f25d473aa76e0275c4f9fa1cffc3e1d8748278561b99953966606a5d891717b4fb0366a77e38db4c267c3724e994532ae97fc7b12842157d8a11bc97926eb9978c82a07afc573a04660247a94c5c4f14556fbcc9aa367b7bef4fdf18b626b4342d4e84850f133076dcd26c16d3efe9f85fa29c757acda5dff2fe26fbf87d937be455d4053e4246a3055ace5fcb6d6545aa3cd0b2e21ea3648f0dd6cde386933381b7116», «difficulty»: 237219196877, «expected_reward»: 1129583, «height»: 338801, «prev_hash»: «4486739aac8830906e18272012b97b98993afccf89d0044241193d1788f760cb», «reserved_offset»: 126, «status»: «OK» } } |
Рассмотрим подробнее первые два поля.
Electroneum предоставляет две возможности для майнинга. Можно использовать готовый для расчета хеша blockhashing_blob, подбирая четыре байта nonce. Из достоинств — не нужно рассчитывать самому корень Меркле для транзакций. Из недостатков — довольно скудный набор возможных значений, среди которых может и не найтись нужного.
Второй вариант — использовать сырой блок blocktemplate_blob. Тут уже можно перебирать как четыре байта nonce, так и значение блока дополнительных данных, что заметно расширяет вероятность нахождения нужного значения. Но приходится считать хеш первой транзакции и корень Меркле, а только потом рассчитывать хеш самого блока.
Для начала попробуем первый вариант. Напишем небольшой метод, который будет перебирать значения nonce.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static boolean findProperNonce(byte[] blockheader, int nonceByteIndex, long difficulty) { byte nonceByte = Byte.MIN_VALUE; while (nonceByte != Byte.MAX_VALUE) { blockheader[39 + nonceByteIndex] = nonceByte; if (nonceByteIndex < 3) { boolean found = findProperNonce(blockheader, nonceByteIndex + 1, difficulty); if (found) { return true; } } else { byte[] hash = calculateHash(blockheader); if (hasRequiredDifficulty(hash, difficulty)) return true; } nonceByte++; } return false; } |
Electroneum использует алгоритм хеширования CryptoNight. Описание алгоритма можно посмотреть тут. Хорошая новость — есть много готовых реализаций, плохая — практически все они написаны на С. К счастью, Java-машина прекрасно умеет запускать код на С. Поэтому, чтобы сократить время, возьмем готовую реализацию алгоритма и сделаем для нашего майнера подключаемую DLL’ку.
Для этого нам понадобится Cygwin. Это набор опенсорсных линуксовых утилит, которые можно запускать под виндой. При установке нужно выбрать пакеты
mingw64—x86_64—gcc—core и
mingw64—x86_64—gcc—g++.
Для загрузки библиотеки создадим класс CryptoNight в пакете
com.gogaworm.electroneumminer.
public class Cryptonight { // Загружаем библиотеку с именем minerhashing, расширение писать не нужно static { System.loadLibrary(«minerhashing»); } // Метод расчета хеша из библиотеки public native static void calculateHash(byte[] output, byte[] input); } |
Метод calculateHash объявлен как native, это означает, что он реализован на другом языке. Далее нужно сгенерировать файл заголовка:
> %JAVA_HOME%binjavah.exe —jni —v —d com/gogaworm/electroneumminer com.gogaworm.electroneumminer.Cryptonight |
В результате получим файл
com_gogaworm_electroneumminer_Cryptonight.h. В нем объявлен метод
Java_com_gogaworm_electroneumminer_Cryptonight_hash, который нужно реализовать на С. Для этого создадим файл с таким же именем, но расширением .c. Оба файла нужно перенести в папку с исходниками libcryptonight.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Обязательно нужно включить файл — заголовок для jni. Он находится в папке с установленной Java #include <jni.h> JNIEXPORT void JNICALL Java_com_gogaworm_electroneumminer_Cryptonight_calculateHash (JNIEnv *env, jclass clazz, jbyteArray output, jbyteArray input) { // Копируем массивы данных в новую область памяти. Работать с массивами напрямую в JavaHeap нельзя, так как сборщик мусора может перемещать их в памяти unsigned char* inputBuffer = (*env)—>GetByteArrayElements(env, input, NULL); unsigned char* outputBuffer = (*env)—>GetByteArrayElements(env, output, NULL); // Определяем размеры массивов jsize inputSize = (*env)—>GetArrayLength(env, input); jsize outputSize = (*env)—>GetArrayLength(env, output); // Рассчитываем хеш cryptonight_hash(outputBuffer, inputBuffer, inputSize); // Освобождаем область памяти, использованную для массивов (*env)—>ReleaseByteArrayElements(env, input, inputBuffer, JNI_ABORT); (*env)—>ReleaseByteArrayElements(env, output, outputBuffer, JNI_COMMIT); } |
Теперь запускаем Cygwin-консоль и собираем DLL:
> x86_64—w64—mingw32—gcc —I«$JAVA_HOME/include» —I«$JAVA_HOME/include/win32» —shared —o minerhashing.dll —g com_gogaworm_electroneumminer_Cryptonight.c cryptonight.c crypto/aesb.c crypto/c_blake256.c crypto/c_groestl.c crypto/c_jh.c crypto/c_keccak.c crypto/c_skein.c crypto/oaes_lib.c |
Чтобы наш майнер увидел библиотеку, необходимо определить системную переменную Java
java.library.path=<путь к библиотеке>.
Проверим, что библиотека работает правильно. В документе, описывающем алгоритм CryptoNight, есть два примера для проверки. Запустим один из них:
@Test public void testHashMethod() throws UnsupportedEncodingException { byte[] outputBuffer = new byte[32]; byte[] input = hexStringToByteArray(block); Cryptonight.calculateHash(outputBuffer, «This is a test».getBytes(«US-ASCII»)); assertEquals(«a084f01d1437a09c6985401b60d43554ae105802c5f5d8a9b3253649c0be6605», bytesToHex(outputBuffer).toLowerCase()); } |
Остался метод проверки, найдено ли нужно значение. Команда get_block_template вернула в результате параметр difficulty. Этот параметр показывает условный коэффициент сложности нахождения нужного хеша. По спецификации сложность = ( 2^265 — 1 ) / целевое значение (target). Для этой формулы хеш блока нужно перевести из больших индейцев в мелкие. Затем сравним с текущей сложностью, чтобы понять, найдено ли нужное значение:
public static boolean hasRequiredDifficulty(byte[] hash, BigInteger difficulty) { BigInteger diff1 = new BigInteger(«FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF», 16); BigInteger reversed = new BigInteger(bytesToHex(Arrays.reverse(hash)), 16); BigInteger hashdiff = diff1.divide(difficulty); if (hashdiff.compareTo(difficulty) >= 0) { return true; } return false; } |
Чтобы проверить, верно ли работает метод, испытаем его на уже готовом блоке из сети. Получить его можно командой getblock. Возьмем блок с высотой 338 401. Его хеш равен
13b3cf8b04b6bb78f0c7c1a50f7e8656963c1f48a56ba89999eddf0531750b15 |
а сложность —
252087628780. В результате вычислений получаем, что hashdiff больше difficulty.
Когда найдено нужное значение nonce, можно отправлять блок в сеть. Это делает команда
{ «jsonrpc»:«2.0», «id»:«0», «method»:«submitblock», «params»:{ «Block «:«blob template с нужным nonce» } } |
Осталось перенести методы работы с сервером и майнинга в отдельные потоки, и простой майнер готов.
Вместо заключения
Как заявляют разработчики криптовалюты Electroneum, ее можно майнить даже на смартфонах. Приложение для майнинга уже лежит в Google Play. Но на самом деле там только симуляция майнинга: вместо того чтобы решать сложную криптографическую задачу, измеряют доступную производительность CPU, которую теоретически можно было бы использовать для майнинга, и на основе этого значения начисляется заработок. Поэтому майнер для Андроида будет выглядеть несколько иначе.
Но это уже совсем другая история.
(2 оценок, среднее: 5,00 из 5)
Загрузка…
Реализация Java для пользователей и майнеров
Майнинг криптовалюты, вероятно, неправильное название и приводит к некоторому непониманию того, что делают майнеры. Майнинг обычно вызывает образ добычи чего-либо, но с майнингом криптовалюты майнеры создают блокчейн и получают за это деньги. Майнеры спешат собирать все новые транзакции, помещать их в блок и предоставлять какие-то искусственные доказательства работы.
Это третья и последняя часть моей серии о блокчейне, в которой я буду внедрять пользователей и майнеры в свою коллекцию Java-сервисов. Я уже выполнил часть работ по майнингу, потому что у вас не может быть блокчейна без майнера, чтобы собрать его. Но мне еще предстоит создать пользователя, который добавляет адреса, которые используются в блоках цепочки для идентификации сторон, участвующих в контракте. А майнер — это пользователь, так как ему нужен адрес для получения кредита за свою работу.
Конечным результатом этого проекта является ненадежный блокчейн: поскольку не происходит обмена конкретными ценностями, нам не нужно следить за тем, чтобы все складывалось. Единственное, что майнеры получают за свою работу по поддержанию блокчейна, — это хорошая карма.
С такой криптовалютой, как Биткойн, нам нужно найти все неизрасходованные выходные транзакции для адреса, чтобы убедиться, что их достаточно для покрытия суммы, обмениваемой во входной транзакции. Мы также платим майнерам комиссию и определенное количество биткойнов за каждый блок, который они могут добавить в цепочку блоков. Наша цепочка блоков — это, скорее, способ общения поддающимся проверке, хотя он имеет некоторые аспекты криптовалюты, такие как транзакции ввода и вывода.
Создание пользователей
Начнем с создания нового сервиса для пользователей и майнеров. Это будет копия двух других сервисов для блокчейна и узлов, но мы добавим новый контроллер, сервис и модели. Поскольку майнеры являются пользователями (т.е. им нужен адрес для включения в транзакции), мы начнем с пользователей. Давайте создадим конечную точку для добавления, составления списка и получения информации о пользователе. Я собираюсь использовать базовую аутентификацию для конечных точек list
и get
. Конечная точка add
будет открыта, так как любой сможет создать пользователя.
Хотя я хочу продемонстрировать некоторый уровень аутентификации, я не хочу увязываться в деталях настоящей системы аутентификации. Таким образом, для имитации проверки имя пользователя и пароль должны быть одинаковыми, а специального пользователя для перечисления всех пользователей необходимо назвать admin. Я также сразу создам пользователя с правами администратора, если он не существует при запуске.
Вот что у меня есть для контроллера:
Сервис довольно простой:
Используйте application.yaml
, чтобы установить параметры для MongoDB:
Теперь вы можете использовать файл docker-compose.yaml
, чтобы развернуть MongoDB и протестировать новые конечные точки. Вы должны иметь возможность добавлять и перечислять пользователей в систему. Только не забудьте использовать базовую аутентификацию. Важно помнить, что при базовой аутентификации используется имя пользователя, а конечная точка использует идентификатор для более эффективного поиска.
Что еще есть у пользователя, кроме идентификатора и имени? В системе криптовалюты мы думаем, что у пользователя есть кошелек. Кошелек — это адрес и пара закрытого / открытого ключей. В нашей системе мы собираемся сложить их вместе, чтобы у пользователя был только один «кошелек» (т. Е. Адрес и пара закрытый / открытый ключ). При этом пользователь может создавать транзакции. Мы не можем использовать идентификатор в качестве адреса, потому что каждый пользователь привязан к узлу, и идентификатор не является уникальным для всех узлов. Поэтому мы будем использовать UUID для адреса. Тогда остается только создать пару закрытый / открытый ключ. Вот обновленный UserService.addUser
:
Поскольку мы не хотим раскрывать privateKey
, мы добавим эту строку в любую цепочку в UserController
, которая будет возвращать пользователя или список пользователей:
.doOnNext(u -> u.setPrivateKey(null));
Теперь мы готовы создать транзакцию от имени пользователя.
У нас есть элементарная конечная точка создания транзакции в сервисе цепочки блоков, поэтому мы собираемся ее удалить. Мы собираемся сохранить конечную точку добавления транзакции, и мы можем вызывать ее из нашей новой конечной точки в пользовательской службе. Новая конечная точка в контроллере выглядит так:
Новый метод в классе UserService
выглядит так:
Как видите, мы берем закрытый ключ, закодированный в базе данных, и используем его для подписания входного контракта. Так же, как мы создали метод withHash
, мы создали метод withSignature
, потому что подпись должна быть вычислена после применения данных. Мы не утруждаем себя проверкой выходных адресов, поскольку для адресов нет единого источника достоверных данных. Существуют определенные форматы адресов, которые используются некоторыми криптовалютами, чтобы предотвратить передачу монет на несуществующие адреса, но нет никакого способа гарантировать, что вы не отправите свои монеты в пресловутую битовую корзину.
Создание майнеров
На самом деле это проще, чем создавать пользователей, поскольку майнер — это пользователь с возможностью создавать блоки. Создание блока уже выполняется службой цепочки блоков, и мы можем вызвать это косвенно. Единственное, что нам нужно, это адрес, который является частью пользователя. Итак, давайте добавим новую конечную точку в UserController
:
Поскольку майнер — это просто пользователь, мы можем использовать тот же код для его аутентификации. Нас не беспокоит авторизация. У всех одинаковый уровень авторизации. Затем нам нужно добавить метод createBlock
к UserService
:
Можно использовать существующую конечную точку для создания нового блока, и все готово.
Заключение
Уф. Это была большая работа, чтобы разобрать все части. В последних трех статьях мы создали базовую цепочку блоков, одноранговую сеть, которая поддерживает синхронизацию всех, и пользователей / майнеров для управления всем этим. Вероятно, потребуется хороший рефакторинг и несколько тестов интеграционного типа для запуска всех различных сценариев, но на данный момент я доволен наличием полной системы сервисов, которые работают вместе.
Вы можете найти весь код на моей странице GitHub:
А вот и другие статьи из этой серии:
Майнинг и как он работает: матчасть
Время на прочтение
6 мин
Количество просмотров 554K
Привет, %username%!
Я расскажу и покажу как работает основа генерации денег в криптовалютах — майнинг. Как создается первый блок, новые блоки и как появляются деньги из ниоткуда.
Чтобы было проще понять, мы напишем свой импровизированный майнер для импровизированной криптовалюты HabraCoin.
Сначала упрощенный ликбез, куда без него.
Кошельки
Каждый кошелек — это случайно сгенерированная пара ключей. Собственно, адрес кошелька — это хэш от публичного ключа. Так его можно однозначно идентифицировать.
Транзакция
Это запись о том, с какого кошелька на какой какая сумма переводятся. А так же, время и дата операции. Эта запись (её хэш) подписывается закрытым ключом отправителя и рассылается всем в округе в ожидании подтверждения.
Подтверждение
Чтобы о транзакции узнали и все себе её записали, необходимо её подтверждение, которое получается в результате создания нового блока.
Блок
Это служебные данные + список транзакций + номер кошелька майнящего + волшебное число.
Цепочка блоков
Последовательность, в которой каждый следующий блок включает в себя Id предыдущего.
Начало
Итак, есть некоторое количество народа, можно один. Назовём его Хаброша. Он решает запустить свою систему криптовалюты HabraCoin.
Поскольку выделенных серверов у этой валюты нет, то все её участники равноправны и должны как-то договариваться о валидности транзакций. То есть, нужен механизм, который обеспечит:
- Неотвратимость транзакций.
- Возможность любому проверить их валидность.
Для этого он формирует блок из существующих на данный момент неподтвержденных транзакций, номера предыдущего блока, номера своего кошелька и т.д. Для самого первого блока транзакций у нас нет, номера предыдущего блока у нас нет, ничего толком нет. Только адрес кошелька Хаброши да дата со временем.
Так же, в алгоритме HabraCoin указаны следующие вещи.
- Желательно, чтобы новые блоки создавались раз в 10 минут. Если через какое то время мы посмотрим, и окажется, что их больше чем надо было, то рассчитать новую сложность их генерации каждые 100 блоков
- За то, что кто-то создает валидный блок, ему полагается награда в 50 HabraCoins + комиссия
- «Побеждает» тот блок, в котором больше всех транзакций
Ограничение скорости
Как мы видим, сам факт создания блока говорит о том, что его создатель получает за это вознаграждение. И чтобы это вообще имело смысл, скорость и сложность создания блоков следует ограничить. Иначе сами понимаете, тонны блоков из ничего и никакого толку.
В криптовалютах используется способ ограничения сложности, который заключается в проблеме вычисления хэша заданного значения. Если быть точнее, то меньше определенного значения.
Если кто не в курсе, хэш, например f7c9f52d1ebf8c6aef8986fb127ba1bdeec58521f7eb46f026b708df26a40912 — это какое никакое, а число. В десятичной системе оно выглядит как 112078102004378042284884826242280406284022042488488848628408208468422468268028. То есть, хэши можно сравнивать, складывать вычитать и всё такое.
Так вот. Чтобы все признали блок валидным, его хэш должен быть меньше максимально возможного минус определеного всеми значения, называемого сложностью.
Например, хэш у нас 4 байта, максимально возможное значение его FFFFFFFF16. А сложность, допустим, 10010. Вычитаем одно из другого, получается, наш хэш должен быть меньше чем FFFFFF9B16
Как этого добиться?
Если помните, все блоки состоят из нескольких полей. Мы берем эти поля, конкатенируем, получаем из них массив байт. Это массив байт отдаем хэш функции, получаем результат и смотрим: меньше то, что получилось с учетом текущей сложности, или нет?
Если нет, то изменяем этот массив байт до тех пор, пока не получим нужное значение. А именно:
В каждом блоке есть поле, называемое nonce. Это число размером несколько байт, которое нужно увеличивать на единицу, дописывать к блоку и опять считать от него хэш. Поскольку хорошие хэш функции выдают более-менее равновероятностные значения, то мы не знаем заранее, сколько раз придется повторять процесс. Может 1-2 раза, а может миллиарды.
Тот, кому удалось получить хэш, удовлетворяющий условиям сложности, рассылает всем блок с включенными в него транзакциями. На случай, если несколько человек сделали это одновременно, вводится условие, что несколько цепочек блоков существовать вместе не могут, а побеждает самая длинная. Таким образом, если у нас есть две цепочки:
Block1->Block2->Block3A
Block1->Block2->Block3B
то победит та, для которой 4й блок найдут раньше времени. А меньшая цепочка выкидывается и её транзакции снова попадают в очередь на подтверждение.
Комиссия
Все у себя в кошельках видели поле «комиссия» при совершении транзакции. Эта комиссия идет людям, занимающимся генерацией блоков. То есть, они в первую очередь будут выбирать из всех транзакций, ожидающих подтверждения, те, которые содержат в себе комиссию. После формирования блока считается, что вся комиссия, содержащаяся в транзакциях, отходит к его (блока) создателю.
То есть, когда кончится вознаграждение за генерацию блока (если это прописано в алгоритме валюты), то майнерам останется лишь комиссия, а халявные транзакции могут быть никогда не обработаны.
Давайте смоделируем ситуацию и поможем Хаброше скрафтить какой нибудь блок.
Под спойлером программа (в виде Junit теста), которая ради приличия генерирует 2 случайных пары ключей, формирует какое то подобие транзакции (даже подписывает её, все по честному!), а потом ищет такую к ней добавку, чтобы первые 2 байта хэша были нулевыми. Типа сложность такая. Работает пару минут, а потом действительно выдает хэш, который можно быстро проверить, сконкатенировав байты транзакции и счетчика.
Код программы
package com.paranoim.money;
import java.math.BigInteger;
import java.util.Arrays;
import junit.framework.TestCase;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.util.Pack;
import org.bouncycastle.math.ec.ECPoint;
import com.paranoim.TestsAll;
import com.paranoim.crypto.assymetric.ECDSA;
import com.paranoim.crypto.digest.SHA3_512;
import com.paranoim.crypto.utils.ByteUtils;
public class MiningTest extends TestCase
{
private byte[] counter = new byte[4];
private byte[] getAddressFromPublicKey(ECPublicKeyParameters publicKey)
{
ECPoint q = publicKey.getQ();
byte[] encoded = q.getEncoded(true);
return SHA3_512.process(encoded); // reciever's address is it's pubkic key hash
}
public void testMining()
{
ECPublicKeyParameters fromKey = (ECPublicKeyParameters) TestsAll.ALICE.getPublic();
ECPublicKeyParameters toKey = (ECPublicKeyParameters) TestsAll.BOB.getPublic();
byte[] from = getAddressFromPublicKey(fromKey);
byte[] to = getAddressFromPublicKey(toKey);
int amount = 100; //100 HabraCoin
long now = System.currentTimeMillis();
//compose the message for signing
byte[] fromTo = ByteUtils.concat(from, to);
byte[] bAmount = Pack.intToBigEndian(amount);
byte[] bTime = Pack.longToBigEndian(now);
byte[] amountAndTime = ByteUtils.concat(bAmount, bTime);
byte[] msg = ByteUtils.concat(fromTo, amountAndTime);
BigInteger[] sigCoords = ECDSA.signDigest(TestsAll.ALICE.getPrivate(), SHA3_512.process(msg));
byte[] signature = ByteUtils.concat(sigCoords[0].toByteArray(), sigCoords[1].toByteArray());
// MSG contains from, to, amount, time and signature
msg = ByteUtils.concat(msg, signature);
ECPublicKeyParameters minersKey = (ECPublicKeyParameters) TestsAll.ALICE1.getPublic();
byte[] bminersKey = getAddressFromPublicKey(minersKey);
//msg = msg + miner's address
msg = ByteUtils.concat(msg, bminersKey);
byte[] hash = doTheMining(msg);
msg = ByteUtils.concat(msg, counter);
assertTrue(Arrays.equals(hash, SHA3_512.process(msg)));
}
private byte[] doTheMining(byte[] msg)
{
byte[] hash = SHA3_512.process(ByteUtils.concat(msg, counter));
while(hash[0] != 0 || hash[1] != 0 )
{
incrementCounter();
hash = SHA3_512.process(ByteUtils.concat(msg, counter));
}
return hash;
}
private void incrementCounter()
{
for (int i = 0; i < counter .length; i++)
{
counter[i]++;
if (counter[i] != 0)
break;
}
}
}
Пример получившегося блока:
1824B9ADF09908222CF65069FDE226D32F165B3CF71B7AA0039FDFEF75EAA61610909EBFFBAC023480FC87FCF640C4A
009B82C4A6D25A0F4B8A732AE54EF733E792681137BA378577DFDC2732D192DAF323966EAD4ADC9635D7A12EDD50E34
9F660622D186AF3C03BF7D265F2AA7EB125056F4BF45BE519E8B22B845B28065110000006400000142E5D667CB01CEE
EDD0AC15EC4C491819A99030BD5FEF7CD2B469F2B90BA13D7981EDCD0708353D13390B8564F496C44FAC2777B0AF79D
C94CBF36D0CC0F047E807889F34C4DC5FEB724699C257391F84F3DDD70B84F841D115F4EFEAF4E58779042F35257E5C
035046037DE740718D199A8F06AD7A58E37CCCD4CC5E95295DCC2C5F3C70847BD59FA57BCC5FF4B208F93948FCFD763
EC1E5C85B61C43EB64B77A9F53B28785D7DE2335333003260A0839D53927976751A8D8967B2BB325909D86E82BC4125
2A28ECF6F0E7476BB99B29585EB0E75410000
И хэш для него:
000008ACF935A8E3E453AC538706F560155943C6B0A77E5F5FCA7939D5FFE589676A6B3CD7AC78845786C50449D1A6F
91003EDCA7B5D8B12AC36CCA36A00844A
Вот мы и заработали пару хабракоинов для Хаброши. Статья конечно поверхностная, так что готов к вашим вопросам.
Автор оригинала: Kumar Chandrakant.
1. Обзор
В этом уроке мы познакомимся с основными концепциями технологии блокчейн. Мы также реализуем базовое приложение на Java, которое фокусируется на концепциях.
Далее мы обсудим некоторые передовые концепции и практическое применение этой технологии.
2. Что Такое Блокчейн?
Итак, давайте сначала разберемся, что же такое блокчейн…
Ну, он прослеживает свое происхождение до белой книги , опубликованной Сатоши Накамото на биткойне , еще в 2008 году.
Блокчейн-это децентрализованный реестр информации . Он состоит из блоков данных, соединенных с помощью криптографии. Он принадлежит к сети узлов, подключенных через общедоступную сеть. Мы поймем это лучше, когда позже попытаемся создать базовый учебник.
Есть некоторые важные атрибуты, которые мы должны понять, поэтому давайте рассмотрим их:
- Защита от несанкционированного доступа: В первую очередь, данные как часть блока защищены от несанкционированного доступа . На каждый блок ссылается криптографический дайджест, обычно известный как хэш, что делает блок защищенным от несанкционированного доступа.
- Децентрализованный: весь блокчейн полностью децентрализован по всей сети. Это означает, что нет главного узла, и каждый узел в сети имеет одну и ту же копию.
- Прозрачный: Каждый узел, участвующий в сети , проверяет и добавляет новый блок в свою цепочку через консенсус с другими узлами. Следовательно, каждый узел имеет полную видимость данных.
3. Как Работает Блокчейн?
Теперь давайте разберемся, как работает блокчейн.
Фундаментальными единицами блокчейна являются блоки . Один блок может инкапсулировать несколько транзакций или другие ценные данные:
3.1. Майнинг блока
Мы представляем блок хэш-значением. Генерация хэш-значения блока называется “майнингом” блока. Майнинг блока, как правило, требует больших вычислительных затрат, поскольку он служит “доказательством работы”.
Хэш блока обычно состоит из следующих данных:
- В первую очередь хэш блока состоит из транзакций, которые он инкапсулирует
- Хэш также состоит из временной метки создания блока
- Он также включает в себя nonce, произвольное число, используемое в криптографии
- Наконец, хэш текущего блока также включает в себя хэш предыдущего блока
Несколько узлов в сети могут конкурировать за добычу блока одновременно. Помимо генерации хэша, узлы также должны проверить, что транзакции, добавляемые в блок, являются законными. Первый, кто добудет блок, выиграет гонку!
3.2. Добавление блока в блокчейн
В то время как майнинг блока является вычислительно дорогостоящим, проверка того, что блок является законным, относительно намного проще . Все узлы в сети участвуют в проверке недавно добытого блока.
Таким образом, недавно добытый блок добавляется в блокчейн на основе консенсуса узлов.
Теперь существует несколько консенсусных протоколов, которые мы можем использовать для проверки. Узлы в сети используют один и тот же протокол для обнаружения вредоносной ветви цепочки. Следовательно, вредоносная ветвь, даже если она будет введена, вскоре будет отклонена большинством узлов.
Теперь у нас достаточно контекста, чтобы начать создавать базовое приложение на Java.
Наш простой пример здесь проиллюстрирует основные понятия мы только что видели. Приложение производственного уровня влечет за собой множество соображений, которые выходят за рамки этого руководства. Однако позже мы коснемся некоторых продвинутых тем.
4.1. Реализация блока
Во-первых, нам нужно определить простой POJO, который будет содержать данные для нашего блока:
public class Block { private String hash; private String previousHash; private String data; private long timeStamp; private int nonce; public Block(String data, String previousHash, long timeStamp) { this.data = data; this.previousHash = previousHash; this.timeStamp = timeStamp; this.hash = calculateBlockHash(); } // standard getters and setters }
Давайте разберемся, что мы здесь упаковали:
- Хэш предыдущего блока, важная часть для построения цепочки
- Фактические данные, любая информация, имеющая ценность, например контракт
- Временная метка создания этого блока
- Nonce, который является произвольным числом, используемым в криптографии
- Наконец, хэш этого блока, вычисленный на основе других данных
4.2. Вычисление хэша
Теперь, как мы вычисляем хэш блока? Мы использовали метод вычисления хэша блока , но еще не видели его реализации. Прежде чем мы реализуем этот метод, стоит потратить некоторое время, чтобы понять, что именно является хэшем.
Хэш-это результат чего-то, известного как хэш-функция. A хэш-функция сопоставляет входные данные произвольного размера с выходными данными фиксированного размера . Хэш довольно чувствителен к любому изменению входных данных, каким бы незначительным оно ни было.
Более того, невозможно получить входные данные обратно только из его хэша. Эти свойства делают хэш-функцию весьма полезной в криптографии.
Итак, давайте посмотрим, как мы можем сгенерировать хэш нашего блока в Java:
public String calculateBlockHash() { String dataToHash = previousHash + Long.toString(timeStamp) + Integer.toString(nonce) + data; MessageDigest digest = null; byte[] bytes = null; try { digest = MessageDigest.getInstance("SHA-256"); bytes = digest.digest(dataToHash.getBytes(UTF_8)); } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) { logger.log(Level.SEVERE, ex.getMessage()); } StringBuffer buffer = new StringBuffer(); for (byte b : bytes) { buffer.append(String.format("%02x", b)); } return buffer.toString(); }
Здесь происходит довольно много вещей, давайте разберемся в них подробнее:
- Во-первых, мы объединяем различные части блока, чтобы сгенерировать хэш из
- Затем мы получаем экземпляр хэш-функции SHA-256 из Дайджеста сообщений
- Затем мы генерируем хэш-значение наших входных данных, которое представляет собой массив байтов
- Наконец, мы преобразуем массив байтов в шестнадцатеричную строку, хэш обычно представляется в виде 32-значного шестнадцатеричного числа
4.3. Мы уже Добыли Блок?
Пока все звучит просто и элегантно, за исключением того факта, что мы еще не добыли блок. Так что же именно влечет за собой майнинг блока, который уже некоторое время захватил воображение разработчиков!
Ну, майнинг блока означает решение вычислительно сложной задачи для блока. В то время как вычисление хэша блока несколько тривиально, найти хэш, начинающийся с пяти нулей, нет. Еще сложнее было бы найти хэш, начинающийся с десяти нулей, и мы получим общее представление.
Итак, как именно мы можем это сделать? Честно говоря, решение гораздо менее причудливое! Именно с помощью грубой силы мы пытаемся достичь этой цели. Мы используем здесь nonce:
public String mineBlock(int prefix) { String prefixString = new String(new char[prefix]).replace('', '0'); while (!hash.substring(0, prefix).equals(prefixString)) { nonce++; hash = calculateBlockHash(); } return hash; }
Давайте посмотрим, что мы здесь пытаемся сделать.:
- Мы начинаем с определения префикса, который мы хотим найти
- Затем мы проверяем, нашли ли мы решение
- Если нет, мы увеличиваем nonce и вычисляем хэш в цикле
- Цикл продолжается до тех пор, пока мы не сорвем джекпот
Мы начинаем со значения по умолчанию nonce здесь и увеличиваем его на единицу. Но есть более сложные стратегии для запуска и увеличения nonce в реальных приложениях. Кроме того, мы не проверяем наши данные здесь, что обычно является важной частью.
4.4. Давайте рассмотрим пример
Теперь, когда мы определили наш блок вместе с его функциями, мы можем использовать его для создания простого блокчейна. Мы сохраним это в ArrayList :
List blockchain = new ArrayList<>(); int prefix = 4; String prefixString = new String(new char[prefix]).replace('', '0');
Кроме того, мы определили префикс из четырех, что фактически означает, что мы хотим, чтобы наш хэш начинался с четырех нулей.
Давайте посмотрим, как мы можем добавить блок здесь:
@Test public void givenBlockchain_whenNewBlockAdded_thenSuccess() { Block newBlock = new Block( "The is a New Block.", blockchain.get(blockchain.size() - 1).getHash(), new Date().getTime()); newBlock.mineBlock(prefix); assertTrue(newBlock.getHash().substring(0, prefix).equals(prefixString)); blockchain.add(newBlock); }
4.5. Верификация блокчейна
Как узел может подтвердить, что блокчейн действителен? Хотя это может быть довольно сложно, давайте подумаем о простой версии:
@Test public void givenBlockchain_whenValidated_thenSuccess() { boolean flag = true; for (int i = 0; i < blockchain.size(); i++) { String previousHash = i==0 ? "0" : blockchain.get(i - 1).getHash(); flag = blockchain.get(i).getHash().equals(blockchain.get(i).calculateBlockHash()) && previousHash.equals(blockchain.get(i).getPreviousHash()) && blockchain.get(i).getHash().substring(0, prefix).equals(prefixString); if (!flag) break; } assertTrue(flag); }
Итак, здесь мы делаем три конкретные проверки для каждого блока:
- Сохраненный хэш текущего блока на самом деле является тем, что он вычисляет
- Хэш предыдущего блока, хранящийся в текущем блоке, является хэшем предыдущего блока
- Текущий блок был добыт
5. Некоторые Передовые Концепции
Хотя наш базовый пример раскрывает основные концепции блокчейна, он, безусловно, не является полным. Чтобы применить эту технологию на практике, необходимо учесть несколько других соображений.
Хотя невозможно подробно описать все из них, давайте рассмотрим некоторые из важных:
5.1. Проверка транзакций
Вычисление хэша блока и поиск нужного хэша – это всего лишь одна часть майнинга. Блок состоит из данных, часто в виде нескольких транзакций. Они должны быть проверены, прежде чем их можно будет сделать частью блока и добыть.
Типичная реализация блокчейна устанавливает ограничение на то, сколько данных может быть частью блока . Он также устанавливает правила проверки транзакции . В процессе проверки участвуют несколько узлов сети.
5.2. Альтернативный Консенсусный протокол
Мы видели, что алгоритм консенсуса, такой как “Доказательство работы”, используется для добычи и проверки блока. Однако это не единственный алгоритм консенсуса, доступный для использования.
Существует несколько других алгоритмов консенсуса на выбор , таких как Доказательство ставки, Доказательство авторитета и Доказательство веса. Все это имеет свои плюсы и минусы. Какой из них использовать, зависит от типа приложения, которое мы намерены разработать.
5.3. Вознаграждение за добычу полезных ископаемых
Блокчейн-сеть обычно состоит из добровольных узлов. Итак, почему кто-то хочет внести свой вклад в этот сложный процесс и сохранить его законным и растущим?
Это происходит потому, что узлы вознаграждаются за проверку транзакций и майнинг блока . Эти награды, как правило, в виде монет, связанных с приложением. Но приложение может решить, что вознаграждение будет чем-то ценным.
5.4. Типы узлов
Блокчейн полностью полагается на свою сеть для работы. Теоретически сеть полностью децентрализована, и каждый узел равен. Однако на практике сеть состоит из нескольких типов узлов.
В то время как полный узел имеет полный список транзакций, легкий узел имеет только частичный список . Кроме того, не все узлы участвуют в проверке и валидации.
5.5. Безопасная связь
Одной из отличительных черт технологии блокчейн является ее открытость и анонимность. Но как он обеспечивает безопасность транзакций, осуществляемых внутри? Это основано на криптографии и инфраструктуре открытых ключей .
Инициатор транзакции использует свой закрытый ключ для его защиты и прикрепляет его к открытому ключу получателя. Узлы могут использовать открытые ключи участников для проверки транзакций.
6. Практическое применение блокчейна
Таким образом, блокчейн кажется захватывающей технологией, но она также должна оказаться полезной. Эта технология существует уже некоторое время, и – излишне говорить – она оказалась разрушительной во многих областях.
Его применение во многих других областях активно продолжается. Давайте разберемся в самых популярных приложениях:
- Валюта : Это, безусловно, самое старое и наиболее широко известное использование блокчейна, благодаря успеху Биткойна. Они обеспечивают безопасные и свободные от трений деньги людям по всему миру без какого-либо вмешательства центральной власти или правительства.
- Идентичность : Цифровая идентичность быстро становится нормой в современном мире. Однако это связано с проблемами безопасности и вмешательством. Блокчейн неизбежно революционизирует эту область с помощью полностью безопасных и защищенных от несанкционированного доступа идентификационных данных.
- Здравоохранение : Отрасль здравоохранения загружена данными, в основном обрабатываемыми центральными органами власти. Это снижает прозрачность, безопасность и эффективность обработки таких данных. Технология блокчейн может обеспечить систему без какой-либо третьей стороны, чтобы обеспечить столь необходимое доверие.
- Правительство : Это, возможно, область, которая хорошо открыта для разрушения технологией блокчейн. Правительство, как правило, находится в центре нескольких гражданских служб, которые часто обременены неэффективностью и коррупцией. Блокчейн может помочь установить гораздо лучшие отношения между правительством и гражданами.
7. Инструменты торговли
Хотя наша базовая реализация здесь полезна для выявления концепций, нецелесообразно разрабатывать продукт на блокчейне с нуля. К счастью, сейчас это пространство созрело, и у нас есть несколько весьма полезных инструментов для начала.
Давайте рассмотрим некоторые из популярных инструментов для работы в этом пространстве:
- Solidity : Solidity – это статически типизированный и объектно-ориентированный язык программирования, предназначенный для написания смарт-контрактов. Его можно использовать для написания смарт-контрактов на различных блокчейн-платформах, таких как Ethereum . Remix VIDEO : Remix-это мощный
- инструмент с открытым исходным кодом для написания смарт-контрактов в солидности. Это позволяет пользователю писать смарт – контракты прямо из браузера. Truffle Suite : Truffle предоставляет
- кучу инструментов для запуска разработчика в разработке распределенных приложений. Это включает в себя Трюфель, Ганаш и морось. Ethlint/Solium : Solium позволяет разработчикам гарантировать, что их
- смарт-контракты, написанные на Солидности, свободны от проблем стиля и безопасности . Solium также помогает в устранении этих проблем. Паритет : Паритет помогает в
- настройке среды разработки для смарт-контракта на Etherium. Он обеспечивает быстрый и безопасный способ взаимодействия с блокчейном.
8. Заключение
Подводя итог, в этом уроке мы рассмотрели основные концепции технологии блокчейн. Мы поняли, как сеть добывает и добавляет новый блок в блокчейн. Кроме того, мы реализовали основные концепции в Java. Мы также обсудили некоторые передовые концепции, связанные с этой технологией.
Наконец, мы закончили с некоторыми практическими приложениями блокчейна, а также доступными инструментами.
Как всегда, код можно найти на GitHub .
-
July 30 2018, 16:03
- Финансы
- Экономика
- IT
- Cancel
Пишем майнер на Java. Кодим добытчик криптовалюты Electroneum
[КАТ]
Для подписчиков
В наше время каждая бабушка слышала о криптовалютах, курсы майнинга проводят даже серьезные учебные заведения, а антивирусы все чаще кричат о заражении сайта или игрушки майнером. Пришло время на практике разобраться, что это такое, как работает, и написать свой криптомайнер.
https://goo.gl/i8QkLg