|
|
|
|
|
|
|
|
|
Blockchain на Go. Часть 3: Постоянная память и интерфейс командной строки. Blockchain habrahabrBitcoin Cash. А был ли форк? / ХабрСегодня тема Bitcoin и Blockchain привлекает невероятное количество внимания. К сожалению, новости не позволяют понять весь масштаб происходящего, так как авторы путаются в терминологии и в спешке выпускают неподготовленные статьи. Что опять же уводит волну обсуждения от технической стороны вопроса к вопросу спекуляции на тему курса Bitcoin. Попробуем отодвинуть эмоции и вместе изучить, что же происходит и каковы последствия. Краткое содержание
Bitcoin — общество любителей математикиМногие люди представляют bitcoin крайне упрощенной системой. Иногда это удобно, а иногда это приводит к невозможности объяснить текущую проблему. К примеру, Bitcoin представляют как огромную бухгалтерскую книгу, где майнеры каждые 5 минут пытаются написать очередную страницу в бухгалтерской книге, где будет записано, от кого и кому перешли средства. Это аналогия удобна и в 99% так оно и есть, только сложно понять, как можно попасть в эту бухгалтерскую книгу, почему можно создать адрес и счет в offline, даже никогда не контактируя с интернетом и другие вопросы.У системы выявился существенный изъян, ее необходимо было поддерживать online, чтобы математики в любой момент могли спокойно обмениваться решениями и выкладывать новые задачи. Естественно математики решили привлечь других «математиков» (программистов), чтобы те решали более простые задачи, а за это, кто быстрее решит, они получали 50 монет и возможность опубликовать свою задачу. Чтобы компьютеры (программисты запрограммировали решених простых задач) не решали задачи слишком быстро и не получали много монет, сложность задач варьируется динамически, через каждые N блоков. С тех пор все стали жить дружно или почти дружно. Переведем эту историю на понятный язык Blockchain: 1. Каждая опубликованная задача — это часть транзакции, которая содержит ответ на поставленную ранее задачу (предыдущую транзакцию и scriptSig / signature) и новые задачи (грубое приближение список output), а также распределение input coins к output coins. 2. Каждая простая задача — это задача mining блока, а именно нахождения ключа для хеша с заданными свойствами. Она адаптируется, чтобы решение находилось приблизительно каждые 10 минут. 3. Каждый блок включает в себя ответ на решение задачи mining и список транзакций, которые по своему желанию включил майнер (важно!) — Насколько такая аналогия близка к реальному состоянию дел в Bitcoin? — Достаточно сильно близка, вот некоторые суждения:
В этой довольно простой разнице и зародился конфликт Bitcoin Core группы со всеми остальными. Грубо говоря то, что для вас раньше было сообществом математиков, сначала стало финтехом, потом диктатом майнеров, а потом диктатом банкиров и спекулянтов, которых больше всего волнует курс BTC/USD. Что такое форк? Почему не так страшна атака 51%.Даже сегодня была статья, что атака 51% страшна и более 80% майнинговых мощностей находится в Китае и нам уже пора бояться. Опять же если мы представим аналогию, что есть книга записей и у кого-то 51% или больше и он начинает в нее писать, что захочет. То, да, атака 51% уже становится просто неотразимой. На практике, все не так.Вернемся к аналогии сообщества с математиками, предположим майнеры захватили 100% мощностей и публикуют задачи с неправильными ответами в различных научных журналах с бешеной скоростью. Естественно сообщество математиков впадет в уныние, но они никогда не признают неправильные ответы и не запишут в свой личный журнал. В blockchain каждый (!) хранит свой журнал. Как бы повело себя адекватное научное сообщество? Оно бы составило список хороших научных журналов или установило бы другой канал связи и начало бы с момента последней «валидной» транзакции. Да это бы отняло некоторое количество сил и времени, но эта атака никак бы не повлияла на «истинность» журнала. Это явление называется hard fork. Появилось 2 цепочки и каждый со своей правотой. Между майнерами и математиками (пользователями), сложились уникальные отношения, одних не бывает без других и наоборот. И этот баланс соблюдается. Как и любая система, bitcoin требует улучшений. Bitcoin Core — группа людей, которая пользовалась и разрабатывала bitcoin практически с самого начала. И с самого начала они столкнулись с первой трудностью разработки. Как выпускать обновления? Если даже провести атаку 100% и обновить всех майнеров, можно получить сломанный blockchain и вероятность hard fork (если клиенты не обновятся), что абсолютно нежелательно. Поэтому была разработана концепция soft fork: все майнеры обновляются, но клиентам продолжают присылать валидные (backward compatibility) блоки и транзакции. Клиенты обновляются по мере необходимости.Некоторые изменения было достаточно просто осуществить. Например, в языке Bitcoin были NO_OP1-10 операции, которые ничего не значили, их начали использовать для новой валидации. Любой процесс обновления сложная процедура и спустя некоторое время был выделен формальный процесс обновления через soft fork (используя следующие договоренности): 1. Все майнеры которые смайнили блок, включают в блок информацию, что они готовы обновиться и включить некоторые функции. 2. Спустя некоторый промежуток времени, когда блоков становится 95% подряд (договорились на этом числе, хотя и не обязательно), майнеры начинают осуществлять атаку большинства. Т.е. отказываются принимать блоки, которые не поддерживают новую функцию (BIP — bitcoin internal proposal). Это является сигналом, что пора обновляться всем майнерам, иначе им не видать их mining fee. 3. Активация фичи может происходит с запозданием, т.е. NO_OP1 -> DIG_SIG_OP может начать работать через 100 блоков, что дает время обновиться клиентам, которые уже знают, когда фича будет включена по маркерам в блоках. P.S. Все это должно работать в обратно совместимом режиме, т.е. те кто не обновился продолжат получать обновления и работать правильно. Алгоритм обновления — это договоренности, а не формальный алгоритм! А договоренности дают сбои. Самый известный случай произошел с обновлением block_version=3, когда прошел 95% сигнал о включении атаки большинства, но AntPool продолжил майнить блоки с версией (3) и к сожалению между ними по-прежнему включал блоки от других майнеров с версией 2. Это был самый настоящий hard fork, потому что AntPool с другими майнерами обладал большой мощностью и смог выстраивать самую длинную цепочку (по умолчанию клиенты bitcoin берут самую длинную цепочку). Проблема была с другими майнерами, которые не могли провалидировать эту цепочку (так как были блоки и 2 и 3) и отказывались ее принимать. После 6 часов форка с администраторами AntPool связались и они отказались от своих блоков. Заметьте hard fork происходил по тем же транзакциям, т.е. транзакции попадали сразу в 2 fork, правда в разных блоках. Как видите механизм обновления придумали не математики, а программисты-политики, поэтому он иногда дает сбои (шутка). Segwit. С чего все началосьМнениеРазработчики Bitcoin Core были и есть романтики. Они развивают сложную математическую систему, которая не учитывает, что большинство пользователей и даже майнеров не понимают и не используют сложность этой системы. Bitcoin используется только на 1% от своей сложности, в то же время Bitcoin Core группа двигалась достаточно быстро и предлагала все новые технологии. Некоторые разработчики это понимали и создавали altcoin, хотя большинство соглашалось, что неплохо бы иметь все в Bitcoin. Все это развивалось относительно хорошо, пока курс Bitcoin не начал взлетать. Каждое изменение (soft fork) обходилось все дороже и требовало все большей координации. Bitcoin буквально перестал использоваться для smart contract, fee стали увеличиваться и все это вызывало дебаты между майнерами, которые зависят скачков курса, и разработчиками. Пользователи оставались в стороне до тех пор, пока fee не выросли настолько и пока транзакций стало так много, что приходилось ждать часами. Тогда майнеры решили отстранить разработчиков и скооперироваться с большими пользователями (владельцами бирж, сайтов — coinbase, blockchain, etc). По сути разработчики остались в стороне, но запас той работы, которую они уже проделали и использовали майнеры, пользователи для новых обновлений. Segwit крайне интересное обновление для Bitcoin, которое позволяет сделать Transaction ID стабильными для изменений со стороны майнеров. Сегодня существуют методики, когда майнер, или владелец может поменять внутренность транзакции, что суть останется той же, но id изменится. Про Segwit стоит рассказать отдельно и подробно, но главное отличие Segwit обновления от других обновлений, что оно крайне объемное.Для Segwit обновления, обновленные системы должны держать аж два blockchain (!), один который они будут показывать всем нодам до SegWit, а другим нодам после SegWit. Суть транзакций и output, конечно, же будут совпадать, но input немного отличаются. Дело в том, что поменялся механизм расчет transactionId, а это необходимый параметр для input. Изменение segwit блокчейна необратимо, так как новые блоки не могут перемешиваться со старыми и должно пройти строго по договоренностям, что как раз и вызвало большую задержку и множество переговоров. 1 августа произошел locked-in для Segwit, т.е. блоки которые не заявляют поддержку segwit не включаются в блокчейн. 22 августа произойдет окончательная активация Segwit и можно будет хранить witness data в транзакциях. 1 байт Witness data считается как 0.25 байт, а 1 байт остальной data остался одним 1 байтом. Размер блока без witness data не увеличился и по прежнему составляет 1 МБ, зато с segwit может быть и 4 MB! Segwit является soft fork и не может увеличить размер блока иначе предыдущие версии не смогут провалидировать новые блоки. Ирония состоит в том, что майнеры адаптируют новые блоки для старых клиентов, чтобы они могли быть провалидированы. Новый формат транзакций также отличается и также требует адаптирования между версиями. Возможно найдется еще какой-то способ увеличить размер блока, сохраняя backward compatibility. К примеру, уменьшить сложность mining задачи и генерировать блоки чаще. Bitcoin Cash — как заработать и как дать другим заработатьЧто ждали 1 августа? Что произойдет hard fork и мы увидим несогласных. А что произошло? Произошло нечто странное… Так как во всех планах hard fork ожидался 1 августа, то все сайты написали 2 августа, что биткоин разделился и эту идею подхватили все комментаторы и ситуация стала запутанной.Рассмотрим + и — является ли Bitcoin Cash форком или нет: — Bitcoin Cash действительно взял всю историю Bitcoin до 1-го августа как есть (+ hard fork) — Bitcoin Cash мгновенно прекратил обрабатывать валидные транзакции распространявшиеся по сети Bitcoin (- не fork) — Bitcoin Cash отключился от сети Bitcoin? ( -нетипичный форк) — Bitcoin Cash стал отклонять новые Bitcoin блоки (+ hard fork, новые блоки не совместимы по валидации) — Bitcoin Cash провел несовместимое изменение с Bitcoin — увеличил блок до 8 Мб (+ hard fork) — Bitcoin Cash отказался включать SegWit (+ hard fork) В принципе, можно при оговорках назвать, что Bitcoin Cash — это форк. Но 2 фактора все-таки говорят, что это bitcoin history fork, а не bitcoin fork. — прекратил обрабатывать валидные транзакции (требует проверки) — выглядело крайне спланированной акцией со стороны организаторов BCH, а не атакой или защитой своих интересов в Bitcoin (появился сразу же новый кошелек, новые mining тулы, новые blockchain explorer ..) Кому это выгодно? — В принципе, всем, у кого были биткоины, теперь их стало на 20-30% больше. — У нас появилась новая уже популярная блокчейн-структура с 8МБ на транзакцию, что в 8 раз больше, а соответственно и дешевле. — Тем, кто до сплита закупил очень много Bitcoin, а сразу после сплита их продал. Fee от продаж составил 0.2-0.5% от X, а доход за 10 минут 30% от X. Если положить X = 10 000 000, то дальше посчитайте сами. Перспективы и что дальшеСамое интересное, что биткоин провел только soft fork и только готовится к hard fork в 20 ноября. Уже в ноябре произойдет настоящий hard fork и он будет неминуем (наверное). Майнеры приняли решение увеличить блок до 2МБ, это автоматически сделает несовместимыми старые версии Bitcoin wallet, что приведет, к тому, что люди или обновятся и примут 2 МБ или продолжать мусорить сеть альтернативной версией blockchain, а возможно кто-то продолжит майнить блоки по 1 МБ. Очень надеюсь, что этого не произойдет.После segwit2x возможно bitcoin core группа снова соберется и вернется к разработке новых BIP, а также обновит официальный клиент bitcoin core для поддержки 2MB block. По крайней мере, все существующие BIP были разработаны с ее поддержкой. P.S. Допускаю большое количество технических неточностей, пожалуйста, комментируйте — будем исправлять. P.P.S Что не удалось рассказать, но возможно удастся в следующий раз, отпишитесь если интересно
habr.com Пишем собственный платежный шлюз Bitcoin / ХабрUPDATE. Заопенсорсил платежный шлюз: github.com/Overtorment/Cashier-BTCПо разным причинам существующие платежные шлюзы (такие как Bitpay) вас могут не устраивать. В этой статье мы рассмотрим создание собственного Bitcoin шлюза с нуля. Предполагается что читатель знаком с устройством сети биткоин. Если нет, то рекомендую эти статьи: “Как на самом деле работает протокол Биткоин” и “Биткойн: введение для разработчиков” Условно, нашу предполагаемую систему я бы разделил на 4 части:
Работа с адресамиВ общем, с этим может справится любая криптографическая библиотека поддерживающая эллиптическую криптографию.Еще подойдут обычные биткоин библиотеки для работы с Bitcoin: Получение информации из биткоин сетиСамый “тяжеловесный” пункт. Классическим решением является поднятие собственного эталонного полного узла Биткоин, он же — каноничный bitcoind. Это позволит нам общаться с ним по JSON-RPC. С ним мы сможем как получать информацию из сети, так и пушить транзакции. На что стоит обратить внимание:
Сейчас есть: Лично я рекомендую подключить несколько решений с фейловером. Создание и подпись транзакцийВ зависимости от того, какую основную библиотеку мы выбрали, эта библиотека умеет или не умеет создавать и подписывать транзакции. Можно написать самому. Смотрите раздел “Работа с адресами”.Трансляция транзакцийРезультатом создания и подписи транзакций являются двоичные данные (hex), готовые к пушу в сеть. Пока сеть не увидит транзакцию, считайте, нет никакой транзакции. Когда сеть увидела транзакцию, она считается неподтвержденной. Транзакцию достаточно передать одному узлу биткоин, после чего за считаные секунды транзакцию увидит большая часть Биткоин сети. Транслировать транзакции умеют некоторые клиентские либы из раздела “Работа с адресами” (через какието свои захардкодженые ендпоинты), или любой полный узел. Транслировать транзакцию можно даже руками, зайдя на специальную страничку Биткоин API провайдера и вбив транзакцию в специальную форму. Канонично, подтвержденной транзакцией является транзакция, включенная в 6 и больше последовательных блоков (или в 1-3. Неканонично, но быстрее). Транзакции с нулевой (или недостаточной) комиссией могут оставаться неподтвержденными долгое время (до месяца, в моей практике). Такие транзакции желательно периодически ретранслировать.Общие принципы работы платежного шлюзаВариант 1Предположим, у нас есть уникальный счет (invoice, order), представленый к оплате клиенту, и платить клиент будет в биткоинах. Начнем с того, что надо сконвертировать валюту оригинального счета (USD например) в BTC. Задача это тривиальная и рассматривать мы ее не будем. Далее. Стандартом де факто является генерация нового уникального адреса биткоин под каждый заказ (он же счет, он же invoice, он же ордер). Ожидается, что средства на этот счет переведет только наш клиент, только 1 раз, и только строго указанную сумму (можно больше, никто не обидится, но никак не меньше). Т.о. при поступлении средств на указанный биткоин адрес в нужном количестве, заказ считается оплаченным.Вкратце, цепочка такая:
При поступлении биткоинов на адрес у вас есть варианты засчитать неподтвержденный или подтвержденный баланс. Есть небольшой шанс что транзакция откатится, причем это может быть как по вине плательщика (который на самом деле злоумышленник), так и по независящим от него обстоятельствам. Если вы имеете возможность “отобрать” предоставленный товар или услугу у клиента в случае выявленного факта отмены транзакции, я рекомендую засчитывать неподтвержденный баланс. Это будет означать почти мгновенный процесс оплаты для клиента (в противовес часа ожидания, например). А если какие то транзакции выявятся откаченными в итоге, запросить клиента о повторном платеже, угрожая отобрать услугу/товар. Не ожидайте что подобный фрод вас тут же массово настигнет, откат транзакций это очень большая редкость, а “вручную” стимулировать подобный откат (на который кстати у злоумышленника нет никаких гарантий успеха) технически неподкованным клиентам нереально (в противовес чарджбекам по кредитным картам). Еще один пример когда можно засчитывать неподтвержденный баланс — если на подготовку заказа клиента уходит больше одного часа (например обрабатывается корзина покупателя, готовится к отправке курьерской службой). Тут куча времени перепроверить баланс перед самой отправкой товаров. Для остальных случаев можно ввести некий порог, выше которого обязательно ожидать подтвержденного баланса (например 0.25 BTC). Для максимальной надежности сделать его нулевым. После закрытия ордера вы можете оставить биткоины на этом адресе до востребования, или для удобства перевести на единый “агрегационный” кошелек мерчанта. Будьте осторожны, в последнем случае вы можете скомпрометировать такой коммерческий показатель как “оборот”, т.к. транзакцию оплаты может отследить каждый платящий клиент. Для переводов вам понадобится создавать, подписывать и транслировать транзакции, используя приватные ключи от адресов. Пару слов о времени жизни ордера. Если ваш товар или услуга жестко привязаны к эквиваленту в фиатной валюте (например USD), то типичный срок жизни ордера составляет 7-15 минут из-за волатильности курса. Вариант 2Подходит когда вы не выставляете счета на оплату, а аккаунт юзера содержит некий единый баланс, который он пополняет и с которого тратит. Тут понадобится сгенерить биткоин адрес на пользователя, и показывать ему, с просьбой пополнить на любую сумму. В данном случае надо мониторить адрес на входящие транзакции, пополнять юзеру внутренний баланс при наличии оных. В данном случае я рекомендую засчитывать только подтвержденные транзакции (от 3х блоков и выше).
Несколько слов о безопасностиВ случае взлома злоумышленника будет интересовать только одно — приватные ключи от сгенерированых вами адресов, ведь они позволяют перевести средства с этих адресов куда угодно. Я рекомендую хранить их изолировано от основной системы в некоем безопасном хранилище. В конечном итоге они вам понадобятся только когда вы сами захотите воспользоваться вырученными биткоинами (потратить, сконвертировать в нал etc).Что дальше?При необходимости вырученые биткоины можно автоматически заводить на биржу и по API биржи продавать. Для этого нам понадобится создавать, подписывать и транслировать транзакции, используя приватные ключи от адресов.Вот и все, надеюсь окажется полезным тем у кого появилась похожая задача. Исправления неточностей и ошибок приветствуются в личку. habr.com Как выбрать по-настоящему защищенный мессенджер и при чем здесь блокчейн / ХабрВ магазинах приложений сегодня можно встретить десятки, если не сотни различных мессенджеров. Некоторые из них называют себя защищенными, но на практике это оказывается далеко не так. Сегодня мы поговорим о том, как выбрать по-настоящему защищенный мессенджер, и при чем здесь технология блокчейна, на которой построен проект ADAMANT. Децентрализация очень важнаСовременные мессенджеры чаще всего централизованы или построены на технологии P2P. В обоих случаях данные могут быть перехвачены и украдены. В случае централизованной системы злоумышленникам нужно лишь взломать центральный сервер, а при P2P-схеме информация хранится на устройствах, участвующих в переписке, так что если стоит задача атаки конкретного пользователя, то достаточно скомпрометировать его устройство или гаджет того, с кем он общается.Даже самые защищенные мессенджеры обладают теми или иными проблемами безопасности. Недавний случай — программная ошибка привела к тому, что удаляющиеся сообщения, отправленные с Mac-клиента мессенджера Signal, на самом деле не удалялись полностью. Кроме того, даже P2P-мессенджеры зависят от работоспособности своих серверов: Блокчейн позволяет избавиться от подобных проблем. Построенный на ней мессенджер ADAMANT хранит сообщения в распределенном блокчейне. В результате, на самом устройстве информация не хранится, а узлы блокчейна хранят зашифрованные сообщения в свободном доступе. Оконечное шифрование делает невозможным получение доступа к содержанию переписки любой третьей стороны. Открытый кодИсходный код почти всех популярных мессенджеров полностью или частично закрыт. Это логично, ведь почти все они управляются крупными корпорациями, которым чужда концепция прозрачности. Это касается Telegram, который откроет исходный код «когда-нибудь». Никто не хочет помогать независимым исследователям информационной безопасности, которые ищут уязвимости в их продуктах и рассказывают о них публике. Крайне редко происходят случаи, когда какая-то часть кода открывается — как в случае мессенджера Wikr в прошлом году — но это единичные акции.Команда проекта ADAMANT напротив, полностью открыла процесс развития продукта. Репозитории с полным кодом мессенджера доступны на GitHub. Открытый код — не основное преимущество АДАМАНТа. Команда проекта настроена, чтобы в конечном итоге и инфраструктура и сами приложения мессенджера поддерживались сообществом, как это сейчас происходит с биткоином. Фокус на безопасности личных данныхЧтобы воспользоваться популярными мессенджерами нужно будет предоставить им массу личной информации, начиная от номеров телефона, списка контактов, заканчивая данными об использовании приложения. Но чем больше данных приложение собирает, тем выше вероятность того, что какие-то из них «утекут». Это может случиться даже с самым защищенным мессенджером, как показывает пример Signal (который именно так себя и позиционирует).Обязательная идентификация пользователей по номеру телефона почти у всех мессенджеров (включая Signal и Telegram) вкупе с прекращением свободной продажи SIM-карт и отключения уже проданных и вовсе поднимает вопрос — а не является ли все это спланированной кампанией государств для создания иллюзии безопасной переписки. Кроме того, личные данные могут оказаться в руках третьих лиц или других компаний не в результате утечки, а за деньги. Компаниям, создающим мессенджеры, нужно как-то зарабатывать, а учитывая, что большинство таких приложений бесплатны для пользователей, то товаром становятся они сами. В результате их информация используется для рекламы и продается на сторону. От бесплатных мессенджеров ничего другого ждать нельзя (будем говорить прямо — ничего бесплатного не бывает). Поэтому ADAMANT использует другой подход к монетизации — пользователи этого приложения платят за доставку своих сообщений небольшую сумму в криптовалюте, которая используется для поддержания работы сети — мотивация для подключения “делегатов” к блокчейну! Для того, чтобы им воспользоваться не нужно вообще ничего, даже номера телефона или предоставления списка контактов. Чтобы попробовать АДАМАНТ в работе, перейдите в веб-приложение msg.adamant.im, и получите тестовые токены для переписки. Сравнение защищенности мессенджеровМногих пользователей смущает тот факт, что в блокчейне сообщения хранятся вечно. Ведь это означает, что даже если сейчас они защищены криптографией, кто гарантирует, что в будущем существующие алгоритмы шифрования нельзя будет взломать (например, с появлением квантовых компьютеров)?По ряду причин эти риски нельзя назвать слишком существенными. Во-первых, блокчейн позволяет создавать чаты, которые можно удалять (используя подход вторичных цепочек — side chains) Во-вторых, полностью расшифровать данные в блокчейне будет сложно в любом случае — ведь у каждого из участников переписки свой ключ. Это означает, что даже если способ расшифровки появится в будущем, его применение все равно будет требовать ресурсов и времени. Инвестировать их для взлома всех сообщений всех пользователей конкретного мессенджера нет смысла. При этом, в интернете все так или иначе «под колпаком» спецслужб (вспомним программу PRISM или закон Яровой). Ну и стоит ли говорить, что появление квантовых компьютеров, способных расшифровать вообще все, что зашифровано сегодня, станет мощнейшим потрясением для всей ИТ-индустрии, это будет совершенно другая реальность. Поэтому фантазировать об этом в данный момент нет никакого практического смысла. А вот в сравнении текущего уровня защищенности данных пользователей различных мессенджеров он есть. Вот, как ситуация с этим обстоит сегодня (по клику картинка откроется в полном размере): В случае мессенджера ADAMANT связать историю сообщений с конкретным пользователем будет проблематично. Есть вопросы о схеме работы приложения или хотите его протестировать? Пишите в комментариях, и члены команды проекта объяснят все подробнее.habr.com Переписываем приложение под Blockchain / ХабрОтмечу сразу, что данная статья не о том как писать код на Solidity, а как существующую классическую архитектуру вашего приложения можно перевести на рельсы blockchain и думать в ключе децентрализации. Пару лет назад я работал над одним интересным веб приложением сервиса p2p доставки посылок. По определенным причинам разработку пришлось заморозить на этапе прототипа, так что я просто выложил исходный код на GitHub и забыл про него. В последнее время по роду деятельности мне довелось поработать с несколькими проектами связанными с криптовалютой и blockchain-технологиями. Познакомившись ближе с Ethereum и его идеологией децентрализованных приложений (ĐApp) я просто заболел этой идеей: никакой цензуры, никто не может прикрыть ваш бизнес, никто не может конфисковать ваши средства, невозможно просто взять и выключить сервер на котором работает ваше приложение. В определенный момент я пришёл к выводу, что именно в такой среде мой проект может иметь шансы на жизнь. Итак, взглянем на фронт работ. Фронт работИзначальная идея проекта была в том, что люди которые часто путешествуют, могли бы перевозить что-нибудь в своих чемоданах или автомобилях. Этакий Uber для доставки. Пользователи делились на путешественников (осликов) и клиентов. Если вы собираетесь ехать, допустим, из Москвы в Минск на эти выходные, вы можете добавить в сервисе новую поездку (трип) с описанием того, что вы можете перевезти и когда. Клиенты, подписанные на данное направление, получали уведомление о новом трипе и могли добавлять запросы на доставку, например лекарства какие-нибудь или iPad для мамы. Стороны договаривались об оплате (самописный escrow на PayPal) и условиях доставки. После исполнения заказа стороны могли оставлять рейтинги и комментарии друг о друге. В профиле каждого пользователя имелась небольшая статистика. Со временем произошло переосмысление некоторых пунктов в алгоритме работы сервиса. Стало ясно, что будет правильно если бы сначало клиенты добавляли свои заказы на перевозку, а ослики уже выбирали бы кому оказывать услугу. Далее вы увидите, как такая концепция обеспечивает гибкость и масштабируемость. По сути нам надо переписать весь back-end на смарт контракты и задеплоиться на блокчейне. Приложение будет абсолютно открытым, неизменяемым (immutable), децентрализованным и, следовательно, свободным от регуляции даже самим разработчиком. Таким образом, мы получим децентрализованную платформу на базе которой любой человек сможет реализовать своё front-end, мобильное или серверное приложение. Протокол работы подобного сервиса в краткой форме будет выглядеть следующим образом:
ПриступимДанный протокол будет реализован смарт контрактом на Solidity, который назовём Osliki. Для начала заведём переменные для хранения заказов, предложений, счетов и статистики: Order[] public orders; // заказы Offer[] public offers; // предложения (тут же будут храниться и ответы от клиентов) Invoice[] public invoices; // счета mapping (address => Stat) internal stats; // маппинг адреса пользователя к его статистикеОписание всех структур: struct Order { address customer; // ethereum-адрес клиента string from; // гео координаты в формате "lat,lon" или ethereum-адрес "0x..." (например адрес принадлежащий магазину, ресторану, ферме или частному лицу) или просто пустое значение (например если важно что привезут, а не откуда) string to; // тоже самое что и предыдущее string params; // параметры посылки в формате "вес(кг),длина(м),ширина(м),высота(м)" uint expires; // дата истечения срока действия, Unix-время в секундах string message; // просто текст в свободной форме uint[] offerIds; // массив id предложений address carrier; // ethereum-адрес выбранного ослика для исполнения заказа uint invoiceId; // прикрепленный оплаченный счёт EnumOrderStatus status; // статус заказа uint createdAt; // дата создания uint updatedAt; // дата изменения } struct Offer { address carrier; // ethereum-адрес ослика uint orderId; // id заказа string message; // текст предложения в свободной форме string respond; // текст ответа от клиента в свободной форме. uint createdAt; uint updatedAt; } struct Invoice { address sender; // ethereum-адрес ослика uint orderId; // id заказа uint prepayment; // сумма для предоплаты (может быть 0) uint deposit; // сумма для депозита (может быть 0) uint expires; // срок годности инвойса EnumCurrency currency; // валюта в которой выставлен счёт bytes32 depositHash; // хеш Ethereum-SHA-3 (Keccak-256) депозитного ключа (устанавливается клиентом в момент оплаты счета) EnumInvoiceStatus status; // статус счёта uint createdAt; uint updatedAt; } struct Stat { uint[] orders; // массив id заказов в которых участвовал юзер в качестве клиента или ослика uint rateSum; // сумма оценок uint rateCount; // количество оценок, средняя оценка averageRate = rateSum / rateCount mapping (uint => Review) reviews; // маппинг id заказа к отзыву и оценке этого заказа } struct Review { uint8 rate; // оценка между 1 и 5 string text; // текст отзыва uint createdAt; }Статусы: enum EnumOrderStatus { New, Process, Fulfilled } enum EnumInvoiceStatus { New, Settled, Closed, Refund } enum EnumCurrency { ETH, OSLIK } // оплата счетов в эфире (комиссия 1%) или в своих внутренних токенах OSLIK (без комиссии)Далее не буду приводить тела функций, иначе займет слишком много места. Ниже будет ссылка на исходный код на GitHub. Клиент добавляет Заказ: function addOrder( string from, // "" || "lat,lon" || ethereum-адрес "0x..." string to, // "" || "lat,lon" || ethereum-адрес "0x..." string params, // "weight(kg),length(m),width(m),height(m)" uint expires, string message ) public;Ослик добавляет Предложение: function addOffer( uint orderId, string message ) public;Клиент отправляет Ответ: function respond( uint offerId, string message ) public; // только один ответ на предложениеОслик отправляет Счёт: function addInvoice( uint orderId, uint prepayment, uint deposit, EnumCurrency currency, // ETH || OSLIKI uint expires ) public;Клиент оплачивает Счёт: function pay( uint invoiceId, bytes32 depositHash // клиент отправляет хеш keccak256 ключа для доступа к депозиту, может быть пустым если нет депозита и все средства оплачены вперед ) public payable; // после оплаты, счёт прикрепляется к заказу и отправитель счёта считается выбранным в качестве исполнителя заказаОслик выполняет Заказ. К примеру при доставке Ослик сканирует QR-код у Клиента, в котором вшит Ключ от Депозита: function fulfill( uint orderId, string depositKey // ключ который хешируется и сравнивается с depositHash, может быть пустым если депозита не было, в этом случае просто поменяется статус заказа на ‘Выполнен’ ) public;Ослик возвращает средства Клиенту если тот не доволен или что-то пошло не так в процессе: function refund( uint invoiceId ) public payable;Стороны могут добавлять отзывы друг о друге с оценкой впечатления от Заказа: function addReview( uint orderId, uint8 rate, string text ) public; // тут же обновляем статистикуПлюс еще пачка функций для доступа к данным. В итоге у нас вышло 2 контракта:
ПримерыПо сути, контракт Osliki является открытой базой данных заказов на доставку, которую может использовать любое частное лицо, организация или какой-нибудь дрон по типу Amazon Prime Air. Представьте себе предпринимателя с парком грузовых дронов, базирующихся на крыше какого-нибудь здания в вашем городе. Дроны мониторят базу данных в блокчейне и если заказ соответствует каким-то определенным параметрам (например приемлемый радиус действия и габариты груза), автоматически отправляют предложения и счета, а потом летят исполнять заказы. Представьте себе Мистера X с Крэйглиста, который выращивает у себя на даче особо душистый сорт канабиса (конечно же в странах где это легально). Он кидает вам ссылку, по которой вы можете добавить заказ в Osliki с указанием ethereum-адреса Мистера X напрямую, чтобы другие ослики не спамили предложениями. Далее счёт, оплата и вот посылка у вас уже на руках. И даже в случае блокировки аккаунта Мистера X на Крэйглисте, поклонники его садоводческого таланта будут всегда помнить куда слать заказы. Можно представить себе маркетплейс, где фермеры продают свои биологические овощи без ГМО “только что с грядки”, свежее молоко и мясо. Доставка может осуществляться, например, водителями, курсирующими из пригорода в город. Фермеры таким образом получат неограниченный доступ к розничным клиентам в обход супермаркетов. Ну и изначальная идея с путешественниками, перевозящими посылки между городами и странами, также остается актуальной. ПланыКак часть платформы, сюда хорошо вписался бы такой же децентрализованный маркетплейс (Osliki Marketplace) или доска объявлений (Osliki Classifieds). Или, возможно, использовать уже готовые решения. С использованием методов BigData и AI можно более глубоко анализировать поведение и статистику каждого пользователя и выдавать результаты о его благонадежности. Например можно выявлять пользователей, которые накрутили себе рейтинг. На данный момент пока стоит задача реализовать front-end приложение osliki-js (как один из вариантов реализации) на каком-нибудь GitHub Pages, чтобы можно было привычным способом в браузере работать с контрактами. Плюс набор виджетов для встраивания на сайты. Если тема вам показалась интересной, присоединяйтесь к разработке. СсылкиСсылки на исходные коды на GitHub: Контракты на данный момент задеплоины в тестовой сети Ethereum Ropsten. Тестовый эфир, чтобы поиграться, можно получить тут. Адреса контрактов: habr.com Постоянная память и интерфейс командной строки / ХабрСодержание
ВступлениеВ предыдущей части мы построили блокчейн с PoW системой и возможностью майнинга. Наша реализация всё ближе к полностью функциональному блокчейну, но ей все ещё не хватает некоторых важных функций. Сегодня мы начнем хранить блокчейн в базе данных, после этого сделаем интерфейс командной строки для операций с блокчейном. По сути, блокчейн — это распределенная база данных. Мы пока опустим «распределенная» и сосредоточимся на «база данных».Выбор базы данныхПока что, у нас нет базы данных в реализации, мы просто создаем блоки при запуске программы и храним их в памяти. Мы не можем повторно использовать или поделиться с другими нашим блокчейном, поэтому нам нужно сохранить его на диске.Какая база данных нам нужна? На самом деле, подойдет любая. В Биткоин Paper ничего не сказано про конкретную базу данных, так что выбор остается за разработчиком. Bitcoin Core , который был первоначально опубликован Сатоши Накамото и который в настоящее время является эталонной реализацией Bitcoin, использует LevelDB (хотя он был представлен клиенту только в 2012 году). А мы будем использовать… BoltDBПотому что:
Так как Bolt предназначен для использования в качестве такого низкоуровневого элемента функциональности, простота является ключевой. API будет небольшим и ориентироваться только на получение значений и установке значений. Это всё! Звучит идеально для наших нужд! Потратим минутку на обзор базы.BoltDB — это хранилище «ключ-значение», что значит, что нет таблиц, как в реляционных СУБД ( MySQL, PostgreSQL и тд), нет рядов и столбцов. Вместо этого, данные хранятся в парах «ключ-значение»( как в Golang map). Пары хранятся в «корзинах», которые предназначены для группировки похожих пар (подобно таблицах в реляционных СУБД). Таким образом, чтобы получить значение, надо знать корзину и ключ. Важной вещью про BoltDB является то, что здесь нет типов данных: ключи и значения — это байтовые массивы. Так как мы храним Go структуры ( в частности Block), то мы должны сериализовать их, то есть реализовать механизм по переводу структуры в байтовый массив и восстановлению её назад из массива. Мы будем использовать encoding/gob для этого, хотя JSON, XML, Protocol Buffers тоже подходят. Мы используем encoding/gob, потому что это просто и это часть стандартной библиотеки Go. Структура базы данныхДо того, как мы начнем реализовывать персистентную логику, мы должны решить, как будем хранить наши данные в базе. И для этого мы будем использовать способ, который используем Bitcoin Core.Если по-простому, то Bitcoin Core использует две «корзины» для хранения данных.
В blocks пары key->value это:
Так как у нас пока что нет транзакций, то мы сделаем только корзину blocks. Кроме того, как было сказано выше, мы будем хранить всю базу данных в одном файле, без хранения блоков в отдельных файлах. Поэтому нам не нужно ничего, связанное с файловыми номерами. Поэтому пары key->value, которые мы будем использовать, это:
СериализацияКак сказано ранее, в BoltDB значения могут быть лишь []byte типа, и мы хотим хранить структуру Block в базе. Мы будем использовать encoding/gob для сериализации структур.Давайте реализуем метод Serialize для Block (обработка ошибок для краткости опущена) func (b *Block) Serialize() []byte { var result bytes.Buffer encoder := gob.NewEncoder(&result) err := encoder.Encode(b) return result.Bytes() } Здесь всё просто: в начале, мы объявляем буфер, где будут храниться сериализованные данные, затем инициализируем gob кодировщик и кодируем блок, результат возвращаем как массив байтов.Теперь нам нужна функция десериализации, которая получает на вход массив байтов и возвращает Block. Это будет не метод, а независимая функция: func DeserializeBlock(d []byte) *Block { var block Block decoder := gob.NewDecoder(bytes.NewReader(d)) err := decoder.Decode(&block) return &block } Вот и всё, что нам надо для сериализации.ПерсистентностьНачнем с функции NewBlockchain. Сейчас она создает новый экземпляр Blockchain и добавляет к нему генезис-блок. Мы хотим сделать следующее:
Также заметьте новый способ создания Blockchain: bc := Blockchain{tip, db} Мы не храним все блоки, вместо этого мы храним только кончик цепи. Также мы храним соединение с БД, потому что мы хотим открыть его один раз и держать его открытым во время работы программы. Вот так структура Blockchain выглядит сейчас:type Blockchain struct { tip []byte db *bolt.DB } Следующее, что мы хотим изменить — это метод AddBlock: добавление блоков в цепь теперь не такое простое, как добавление элемента в массив. С этого момента мы будем хранить блоки в БД:func (bc *Blockchain) AddBlock(data string) { var lastHash []byte err := bc.db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) lastHash = b.Get([]byte("l")) return nil }) newBlock := NewBlock(data, lastHash) err = bc.db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) err := b.Put(newBlock.Hash, newBlock.Serialize()) err = b.Put([]byte("l"), newBlock.Hash) bc.tip = newBlock.Hash return nil }) } Рассмотрим код по кусочкам:err := bc.db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) lastHash = b.Get([]byte("l")) return nil }) Это другой (read-only) тип транзакций BoltDB. Здесь мы получаем хэш последнего блока из БД, чтобы использовать его для майнинга хэша нового блока. newBlock := NewBlock(data, lastHash) b := tx.Bucket([]byte(blocksBucket)) err := b.Put(newBlock.Hash, newBlock.Serialize()) err = b.Put([]byte("l"), newBlock.Hash) bc.tip = newBlock.Hash После майнинга нового блока мы сохраняем сериализованное представление в БД и обновляем ключ l, который теперь сохраняет хэш нового блока.Готово! Это было не сложно, не так ли? Проверяя блокчейнВсе новые блоки теперь хранятся в базе данных, поэтому мы можем переоткрыть блокчейн и добавить в него новый блок. Но после реализации этого мы теряем одну полезную особенность: мы не можем напечатать блоки, потому что больше не храним их в массиве. Давайте это исправим.BoltDB позволяет пройтись по всем ключам в корзине, но все ключи хранятся в порядке сортировки по байтам, а мы хотим, чтобы блоки печатались в порядке, в котором они помещены в блокчейн. Также, так как мы не хотим грузить все блоки в память( наш блокчейн может быть очень огромным), то мы будем их читать один за одним. Для этой цели нам нужен итератор по блокчейну: type BlockchainIterator struct { currentHash []byte db *bolt.DB } Итератор будет создаваться каждый раз, как мы хотим перебирать блоки в блокчейне и он будет хранить хеш блока текущей итерации и соединение с БД. Из-за последнего итератор логически привязан к блокчейну (это экземпляр Blockchain, который хранит соединение с БД) и, таким образом, создается в методе Blockchain:func (bc *Blockchain) Iterator() *BlockchainIterator { bci := &BlockchainIterator{bc.tip, bc.db} return bci } Обратите внимание, что итератор сначала указывает на кончик блокчейна, поэтому блоки будут получены сверху донизу, от самого нового до самого старого. По факту, выбор кончика означает «голосование» за блокчейн. У блокчейна может быть несколько ветвей и самая длинная из них считается основной. После получения кончика ( это может быть любой блок в блокчейне) мы можем воссоздать весь блокчейн и найти его длину, и работу, необходимую для её построения. Этот факт также означает, что кончик является своего рода идентификатором блокчейна.BlockchainIterator делает лишь одну вещь: возвращает следующий блок из блокчейна. func (i *BlockchainIterator) Next() *Block { var block *Block err := i.db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) encodedBlock := b.Get(i.currentHash) block = DeserializeBlock(encodedBlock) return nil }) i.currentHash = block.PrevBlockHash return block } Вот и все про БД!Интерфейс командной строки (CLI)Пока что наша реализация не предоставляет нам никакого интерфейса для взаимодействия с программой: мы просто выполняли NewBlockchain, bc.AddBlock в main. Пора улучшить это! Мы хотим иметь такие команды:blockchain_go addblock "Pay 0.031337 for a coffee" blockchain_go printchain Все, связанные с командной строкой, операции будут обработаны структурой CLItype CLI struct { bc *Blockchain } «Входная точка» структуры — это функция Runfunc (cli *CLI) Run() { cli.validateArgs() addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) addBlockData := addBlockCmd.String("data", "", "Block data") switch os.Args[1] { case "addblock": err := addBlockCmd.Parse(os.Args[2:]) case "printchain": err := printChainCmd.Parse(os.Args[2:]) default: cli.printUsage() os.Exit(1) } if addBlockCmd.Parsed() { if *addBlockData == "" { addBlockCmd.Usage() os.Exit(1) } cli.addBlock(*addBlockData) } if printChainCmd.Parsed() { cli.printChain() } } Мы используем стандартный пакет flag для парсинга аргументов командной строки. addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError) printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) addBlockData := addBlockCmd.String("data", "", "Block data") Для начала, мы создаем две подкоманды addblock и printchain, затем добавим флаг -data к первому. printchain не требует никаких флагов.switch os.Args[1] { case "addblock": err := addBlockCmd.Parse(os.Args[2:]) case "printchain": err := printChainCmd.Parse(os.Args[2:]) default: cli.printUsage() os.Exit(1) } Затем мы проверим команду, указанную пользователем, и распарсим связанную подкоманду.if addBlockCmd.Parsed() { if *addBlockData == "" { addBlockCmd.Usage() os.Exit(1) } cli.addBlock(*addBlockData) } if printChainCmd.Parsed() { cli.printChain() } Дальше мы проверяем, какую подкоманду мы распарсили, и запускаем связанную функцию. func (cli *CLI) addBlock(data string) { cli.bc.AddBlock(data) fmt.Println("Success!") } func (cli *CLI) printChain() { bci := cli.bc.Iterator() for { block := bci.Next() fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash) fmt.Printf("Data: %s\n", block.Data) fmt.Printf("Hash: %x\n", block.Hash) pow := NewProofOfWork(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) fmt.Println() if len(block.PrevBlockHash) == 0 { break } } } Этот код похож на тот, что был раньше. Разница лишь в том, что сейчас мы используем BlockchainIterator чтобы итерировать по блокам в блокчейне.Также не забудем изменить функцию main соответственно: func main() { bc := NewBlockchain() defer bc.db.Close() cli := CLI{bc} cli.Run() } Заметим, что новый Blockchain создается независимо от того, какие были переданы аргументы командной строки.Вот и всё! Проверим, что всё работает так, как мы ожидаем: $ blockchain_go printchain No existing blockchain found. Creating a new one... Mining the block containing "Genesis Block" 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b Prev. hash: Data: Genesis Block Hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b PoW: true $ blockchain_go addblock -data "Send 1 BTC to Ivan" Mining the block containing "Send 1 BTC to Ivan" 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13 Success! $ blockchain_go addblock -data "Pay 0.31337 BTC for a coffee" Mining the block containing "Pay 0.31337 BTC for a coffee" 000000aa0748da7367dec6b9de5027f4fae0963df89ff39d8f20fd7299307148 Success! $ blockchain_go printchain Prev. hash: 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13 Data: Pay 0.31337 BTC for a coffee Hash: 000000aa0748da7367dec6b9de5027f4fae0963df89ff39d8f20fd7299307148 PoW: true Prev. hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b Data: Send 1 BTC to Ivan Hash: 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13 PoW: true Prev. hash: Data: Genesis Block Hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b PoW: true (звук открывания пивной банки)СсылкиОригинальная статьяПервая часть цикла статейИсходникиBitcoin Core Data StorageBoltDBencoding/gobflaghabr.com Blockchain на Go. Часть 2: Proof-of-Work / ХабрПривет, Хабр! Представляю вашему вниманию перевод статьи "Building Blockchain in Go. Part 2: Proof-of-Work".Содержание
ВступлениеВ предыдущей статье мы построили очень простую структуру данных, которая является основой для базы данных блокчейна. Также мы сделали добавление в нее блоков с цепной связью между ними: каждый блок связан с предыдущим. Увы, наша реализация блокчейна имеет один существенный недостаток: добавление блоков в цепочку слишком простое и дешевое.Одним из краеугольных камней Биткоина и блокчейна является то, что добавление новых блоков должно быть достаточно сложной работой. И сейчас мы собираемся исправить этот недостаток. Proof-of-Work(PoW)Ключевая идея блокчейна заключается в том, что для добавления нового блока необходимо проделать некоторую сложную работу. Именно эта сложная работа делает блокчейн надежным и целостным. Кроме того, за эту сложную работу выплачивается вознаграждение (вот так люди получают монеты за майнинг).Этот механизм похож на реальную жизнь: надо упорно работать, чтобы получать вознаграждения и обеспечивать себе жизнь. В блокчейне некоторые участники (майнеры) сети работают над поддержанием сети, добавлением в блокчейн новых блоков и получают вознаграждение за свою работу. В результате их работы блок встраивается в блокчейн надежным способом, что обеспечивает стабильность всей базы данных блокчейна. Стоит отметить, что тот, кто выполнил работу, должен также доказать её выполнение. Этот весь «сделай сложную работу и докажи её»-механизм называется Proof-of-Work (доказательство работы). Он сложен, потому что требует больших вычислительных мощностей: даже высокопроизводительные компьютеры не могут его быстро выполнить. Более того, сложность данной работы постепенно возрастает, для того чтобы в среднем создавалось около 6 блоков в час. В Биткоине цель такой работы — это нахождение хеша блока, который удовлетворяет определенным требованиям. Данный хеш и служит доказательством. Таким образом, поиск доказательства и есть фактическая работа. Необходимо заметить одну вещь: Proof-of-Work алгоритмы должны соответствовать следующему требованию: выполнение работы должно быть сложным, но проверка доказательства должна быть простой. Проверка доказательства обычно передается кому-то стороннему, поэтому у них данная проверка не должна занимать много времени. ХешированиеДанная часть посвящена хешированию. Те, кто знаком с этой концепцией, может данную часть пропустить.Хеширование — это процесс получения хеша для некоторых данных. Хеш — это уникальное представление для данных, для которых он был высчитан. Хеш-функция — это функция, которая для данных произвольного размера получает хеш конкретного размера. Некоторые ключевые особенности хеширования:
Функции хеширования широко применяются для проверки целостности данных. Многие поставщики софта публикуют вместе с софтом его контрольные суммы. После скачивания файла, его надо скормить хеш-функции, а затем сравнить полученный хеш с тем, что опубликовал разработчик софта. В блокчейне хеш используется, чтобы гарантировать целостность блока. Входные данные для хеширующего алгоритма содержат хеш предыдущего блока, что делает невозможным (или, по крайней мере, очень сложным) изменение блока в цепи: придется пересчитывать хеш самого блока, а также хеши всех следующих за ним блоков. HashcashБиткоин использует Hashcash, Proof-of-Work алгоритм, который был разработан для защиты от почтового спама. Алгоритм может быть разделен на следующие шаги:
Теперь рассмотрим требования, которым должен удовлетворять хеш. В оригинальной Hashcash реализации требование звучит как «первые 20 бит хеша должны быть нулевыми». В Биткоине требование время от времени корректируется, потому что по замыслу блок должен генерироваться каждые 10 минут, несмотря на то, что мощность вычислений растет со временем и все больше и больше майнеров присоединяются к сети. Для демонстрации алгоритма, возьмем предыдущий пример («I like donuts») и найдем хеш, который начинается с трех нулевых байтов. ca07ca — это шестнадцатеричное представления счетчика, что соответствует числу 13240266 в десятичной системе счисления. РеализацияИтак, с теорией покончено, приступим к коду. Для начала определим сложность майнинга:const targetBits = 24 В Биткоине, «target bits» — это поле заголовка блока, которое хранит сложность, на которой блок был добыт. Мы не будем строить корректирующийся алгоритм, поэтому определим сложность, как глобальную константу.24 — это произвольное число, наша цель — это иметь сложность, которая занимает менее 256 бит в памяти. И мы хотим, чтобы разница была достаточно значительной, но не слишком большой, потому что, чем больше разница, тем труднее найти правильный хеш. type ProofOfWork struct { block *Block target *big.Int } func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) target.Lsh(target, uint(256-targetBits)) pow := &ProofOfWork{b, target} return pow } Здесь мы создаем создаем ProofOfWork, которая содержит указатель на указатель на блок и указатель на цель. «Цель» — это другое имя для требований, описанных в предыдущей части. Мы используем big integer из-за способа сравнения хеша с целью: мы ковертируем хеш в big integer и проверить, меньше ли оно, чем цель. В функции NewProofOfWork мы проинициализируем big.Int значением 1, а потом сдвинуть на 256-targetBits битов. 256 — это длина SHA-256 хеша в битах, и данный алгоритм хеширования мы будем использовать. 16-ричное представление target: 0x10000000000000000000000000000000000000000000000000000000000 И оно занимает 29 байтов в памяти. А здесь визуальное сравнение с хешами из предыдущих примеров: 0fac49161af82ed938add1d8725835cc123a1a87b1b196488360e58d4bfb51e3 0000010000000000000000000000000000000000000000000000000000000000 0000008b0f41ec78bab747864db66bcb9fb89920ee75f43fdaaeb5544f7f76ca Первый хеш( подсчитан для «I like donuts») больше, чем цель, так что это неверное доказательство работы. Второй хеш ( подсчитан для «I like donutsca07ca») меньше цели, так что это верное доказательство.Можно считать цель как верхнюю границу диапазона: если число ( хеш) меньше, чем граница, то оно подходит, и наоборот. Понижение границы приведет к уменьшению количества подходящих чисел, тем самым повышая сложность поиска подходящего. Теперь нам нужны данные для хеширования. Давайте подготовим их: func (pow *ProofOfWork) prepareData(nonce int) []byte { data := bytes.Join( [][]byte{ pow.block.PrevBlockHash, pow.block.Data, IntToHex(pow.block.Timestamp), IntToHex(int64(targetBits)), IntToHex(int64(nonce)), }, []byte{}, ) return data } Этот кусок кода достаточно простой. Мы просто объединяем поля блока с целью и «nonce». nonce — это счетчик из описания Hashcash, это такой криптографический термин.Так, все приготовления выполнены. Теперь реализуем ядро Proof-of-Work алгоритма: func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0 fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data) for nonce < maxNonce { data := pow.prepareData(nonce) hash = sha256.Sum256(data) fmt.Printf("\r%x", hash) hashInt.SetBytes(hash[:]) if hashInt.Cmp(pow.target) == -1 { break } else { nonce++ } } fmt.Print("\n\n") return nonce, hash[:] } Сначала мы инициализируем переменные. hashInt — это целочисленное представление для hash. nonce — это счетчик. Затем мы запускаем «бесконечный» цикл: он ограничен константой maxNonce, значение которой равно math.MaxInt64. Это сделано, чтобы избежать возможное переполнение nonce. Хотя сложность нашей PoW реализации слишком мала для переполнения счетчика, на всякий случай лучше иметь такую проверку.В цикле мы делаем следующее:
Осталось еще кое-что сделать: давайте сделаем возможной проверку доказательств работы: func (pow *ProofOfWork) Validate() bool { var hashInt big.Int data := pow.prepareData(pow.block.Nonce) hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) isValid := hashInt.Cmp(pow.target) == -1 return isValid } Именно здесь нам понадобится сохраненная nonce.Проверим, что все в порядке: func main() { ... for _, block := range bc.blocks { ... pow := NewProofOfWork(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) fmt.Println() } } Output: ... Prev. hash: Data: Genesis Block Hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038 PoW: true Prev. hash: 00000093253acb814afb942e652a84a8f245069a67b5eaa709df8ac612075038 Data: Send 1 BTC to Ivan Hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062b PoW: true Prev. hash: 0000003eeb3743ee42020e4a15262fd110a72823d804ce8e49643b5fd9d1062b Data: Send 2 more BTC to Ivan Hash: 000000e42afddf57a3daa11b43b2e0923f23e894f96d1f24bfd9b8d2d494c57a PoW: trueЗаключениеНаш блокчейн еще на шаг ближе к актуальной архитектуре: добавление блоков требует вычислительной работы, поэтому возможен майнинг. Но в нем по-прежнему отсутствуют некоторые важные функции: база данных блокчейна не является постоянной, нет кошельков, адресов, транзакций и нет механизма консесуса. Все эти вещи мы рассмотрим вследующих статьях.СсылкиПервая частьОригинальная статьяИсходные коды для статьиАлгоритм хеширования блокчейнаProof of WorkHashcashhabr.com Как правильно готовить блокчейн / ХабрЗа последние несколько месяцев мы в BitClave услышали о большом количестве новых блокчейн-проектов, применяющих блокчейн во всех мыслимых и немыслимых сферах. Надеюсь вы понимаете, что один лишь факт использования блокчейна в проекте не придает ему никаких суперспособностей. Мы проанализировали ситуацию и рады представить вам наше текущее видение того, как использовать блокчейн правильным образом. Эта статья поможем вам определить заслуживает ли проект вашего времени, денег и сил. Также в статье имеется упрощенный пример того, как блокчейн может быть внедрен в рынок интернет-рекламы.Предметная область в которой вы собираетесь применить блокчейн должна иметь проблему потенциальной нечестности. Необходимо проанализировать потребности и сценарии взаимодействия участников, чтобы понять экономические выгоды участников от следования этим сценариям. Это позволит понять места, на которых участники готовы срезать ради своей выгоды. Давайте рассмотрим участников рынка интернет-рекламы: Рекламодатели, Агентства, Рекламные сети, Издатели, Пользователи.На представленной схеме вы можете заметить как именно действует каждый из участников. Рекламодатели платят Агентствам за управление их рекламными кампаниями и создание таргетированной рекламы. Агентства платят Рекламным сетям за действия пользователей (обычно клики или установки). Рекламные сети платят Издателям за показы и собирают информацию о Пользователях для оптимизации числа кликов к числу показов. Не редка ситуация когда Агентства и рекламные сети образуют довольно длинные цепочки, каждый из участников цепи съедает свою долю рекламного бюджета Рекламодателя. Рекламные сети могут нечестно устанавливать цену для Агентств и Рекламодателей за действие (CPA = cost per action) и вообще цепочка голодных посредников явно скажется на бюджете Рекламодателя не лучшим образом. В дополнение, неявный сбор информации о Пользователях даст не просто неполную, a также некорректную информацию, которая в конечном итоге приведет к удорожанию CPA. Все эти проблемы мы и попробуем решить в этом примере путем добавления блокчейна. Пожалуйста, перестаньте рассматривать блокчейн как децентрализованное синхронизируемоме надежное хранилище — все эти атрибуты вторичны. В первую очередь блокчейн – активный участник отношений. Он действует честно настолько, что ни у кого не остается сомнений в его беспристрастности. Например, честность блокчейна на консенсусе доказательства работы (Proof-of-Work) обеспечивается большим числом майнеров, чьё основное желание – зарабатывать деньги. Давайте посмотрим на самый первый и самый известный из современных блокчейнов – Биткоин. Вы можете отправить запрос блокчейну Биткоина (точнее сети Биткоин) на перевод BTC с вашего адреса, на любой другой. Блокчейн Биткоина (точнее майнеры сети Биткоин) проверят два конкретных условия: подпись транзакции с целью определить являетесь ли вы владельцем адреса и условие достаточности баланса вашего адреса для проведения этого перевода. Если одна из этих проверок провалится, блокчейн откажет вам в проведении транзакции. Блокчейн Ethereum способен выполнять произвольные проверки, который смогут предусмотреть разработчики смарт-контрактов – таким образом он является более продвинутым доверенным лицом, обладающим к тому же полнотой по Тьюрингу.Добавление нового действующего лица в какую-либо область ведет к полному пересмотру сценариев взаимодействия всех участников. Пользователи будут заполнять свои профили в Блокчейне, а также верифицировать часть информации через региональные сервисы посредством цифровых подписей. Часть Рекламодателей начнут создавать объявления и пополнять балансы рекламных кампаний прямо в Блокчейне. Рекламные сети получат возможность брать рекламные объявления из Блокчейна и показывать их Пользователям через площадки Издателей и позже просить Блокчейн произвести выплаты за пользовательские действия (клики или инсталлы). Блокчейн будет платить за действия не только Рекламным сетям, но и самим Пользователям, любезно согласившимся предоставить о себе информацию для таргетинга. После добавления блокчейна как нового участника в некую систему необходимо заново проработать сценарии работы всех участников, не забыв при этом подкрепить эти сценарии реальными экономическими мотивами участников. Также необходимо предусмотреть защиту системы от неподобающего поведения участников, чтобы даже экономически невыгодные действия некоторых из них не могли помешать взаимодействовать остальным участникам.Что насчёт фрода в системе? Каждое действие (клик/инсталл) пользователя будет обрабатываться разлитыми событиями от Рекламодателя: переход_пользователя, регистрация_пользователя, покупка_пользователем. Платформа предполагает оценку Рекламных сетей, вынуждая их соревноваться друг с другом за улучшение статистических показателей. Любые кликеры при отсутствии борьбы с ними со стороны Рекламной сети будут портить рейтинг Рекламной сети, поскольку к желаемым событиям Рекламодателя в итоге не приведут, в итоге рекламодатель больше этой Рекламной сети бюджета не выделит. Да, рекламные кампании будут позволять Рекламодателям/Агентствам настраивать пропорции бюджета по Рекламным сетям. Резюмируя: система проектируется таким образом чтобы заставить Рекламные сети бороться с фордом и конкурировать друг с другом на этой основе.Зачем этот ваш блокчейн нужен самим рекламным сетям? Рекламные сети получат возможность индексировать и ранжировать для себя объявления из Блокчейна чтобы сравнивать их со своими объявлениями. В случае, если конкретному пользователю окажется выгоднее показать объявление из блокчейна, чем то которое получено из другого источника – Рекламная сеть сможет заработать больше устроив показ объявления из Блокчейна. Со временем объявления в блокчейне вытеснят остальные объявления за счет чистого таргентинга, который выйдет просто дешевле. Зачем пользователи будут у вас регистрироваться? Это может быть частью рефератной программы веб-сайтов, поисковиков, браузеров – убедить пользователей и провести их сквозь регистрацию. Также участие в системе позволит пользователям явно выбирать какую информацию раскрывать Рекламным сетям, что приведет к интересным объявлениям и вознаграждениям за предоставление информации. Неявный сбор информации о пользователях будет все жестче пресекаться на уровне браузеров и операционных систем. В этой статье мы пытались резюмировать наше текущее видение о правильном использовании блокчейна, чтобы помочь вам отличить проекты, на которые стоит тратить своё время, деньги и силы. Предметная область должна содержать проблему потенциальной нечестности и добавление блокчейна как активного и абсолютно честного участника взаимоотношений должно решать эту проблему. Необходимо предусмотреть экономическую выгоду для каждого участника взаимоотношения для каждого из сценариев его работы и учесть возможные варианты атак на систему, с целью помешать её работе.P.S. Я там говорю об абстракции Биткоина, не вдаваясь в подробности реализации UTXO модели. Абстракция Биткоина хранит балансы кошельков и позволяет их изменять путем проведения транзакций, реализация Биткоина чуть более упоротая, но целью статьи не было раскрытие деталей реализации Биткоина :) habr.com
|