История разработки блокчейна Minter

Сайт: https://minter.network

Github: http://github.com/MinterTeam

Всем привет! На связи Даниил Лашин, ведущий разработчик команды Minter. В 2017 году у нас, команды из DeCenter, появилась идея создать блокчейн, который позволил бы всем пользователям создавать абсолютно ликвидные монеты в 2 клика. Такие монеты позволят пользователю моментально продать или купить любой объем монет по рыночной цене, избегая бирж и обменников.

Больше полутора лет команда из 7 разработчиков трудилась над созданием экосистемы Minter. Сейчас подходит время запуска Mainnet. В этой статье я кратко опишу общую архитектуру и логику работы сети, а также расскажу причины принятия тех или иных решений.

Выбор фреймворка

Создание блокчейна с нуля — очень сложная и долгая работа. На нее уходят годы усилий больших команд разработчиков. Приходится заново разрабатывать системы p2p коммуникаций, алгоритм консенсуса и много других, стандартных для всех блокчейнов, модулей. На тот момент у нас не было таких возможностей, поэтому мы решили взять уже готовое решение и начать с него.

В качестве основы мы рассматривали следующие проекты:

Ethereum

Первым блокчейном, на который упал наш взгляд, был Ethereum. Основная нода написана при помощи Go, который считается одним из самых быстрых языков с низким порогом вхождения. Сообществу легко аудировать код, а команде — находить новых разработчиков.

Плюсы:

  • Развитая экосистема, много модулей переписаны под разные языки

Минусы:

  • Проблемы с безопасностью, много багов в EVM

Graphene 2.0

Graphene — это технология, лежащая в основе множества различных блокчейнов (Steem, Bitshares, Golos и другие). Graphene 1.0 с его способностью проводить сотни тысяч транзакций в секунду был настоящим прорывом.

Плюсы:

  • Высокая скорость транзакций

Минусы:

  • Репозиторий долго не обновлялся. Было похоже, что разработчики забросили свой проект

Tendermint

И, наконец, наш выбор — фреймворк от команды Cosmos — Tendermint.

Image for post
Image for post

Разработанный на основе протокола DLS, он не требует никакого «активного» майнинга, как в Proof-of-Work, и может обеспечить безопасную работу сети при наличии как минимум 2/3+ (строго больше чем две трети) «честных» участников сети.

Плюсы:

  • Проект изначально создавался как фреймворк для блокчейн-проектов

Минусы:

  • В момент начала разработки Minter фреймворк был еще далек от релиза, поэтому существовали риски того, что проект так и не запустится

Hello world

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

В качестве основы для адресов, криптографии и сохранения данных мы взяли разработки Ethereum. Это было обусловлено тем, что существует множество библиотек для разных языков программирования (Golang, Swift, Android, Javascript, PHP) и платформ (Web, iOS, Android), что позволило нам не заниматься написанием их вручную и как можно скорее выпустить приложения и кошельки.

Tendermint оказался на удивление простым в использовании, поэтому мы смогли за считанные недели создать proof-of-concept и провести нашу первую транзакцию в сети Minter. Она выполняла очень простую функцию — отправляла монеты с одного аккаунта на другой.

Image for post
Image for post
Первая транзакция блокчейна Minter

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

  • Разработка механизма создания монет пользователей

Общие параметры сети

Путем экспериментов и расчетов мы пришли к таким параметрам нашего блокчейна:

  • Скорость создания блока — 5 секунд

Создание монет

Чтобы создать монету в Minter, нужно отправить всего одну транзакцию— CreateCoin.

CreateCoinData {
Name
Symbol
InitialAmount
InitialReserve
ConstantReserveRatio
}
  • Name — имя создаваемой монеты. До 64 символов.

Стоимость создания монет зависит от количества символов в тикере:

  • 3 буквы — 1 000 000 BIP

Также создателю нужно иметь на счету первоначальное обеспечение монеты, которое не может быть ниже 1000 BIP.

Чтобы не создавать в сети много «мертвых монет», мы решили ввести механизм очистки. Монета удаляется, если соблюдается одно из этих условий:

  • Резерв монеты меньше чем 100 BIP

В этом случае все монеты будут удалены, а пользователям пропорционально вернется их резерв.

Image for post
Image for post
Пример создания монеты в Minter Console

Ликвидность монет

Cоздать новую монету можно только с резервированием за счет BIP, нашей базовой валюты. Такой подход с резервированием позволил создать то, что мы называем «сетью ликвидности» (network of liquidity), потому что любые монеты можно обменять на другие мгновенно и без ограничений. Подобные операции проводятся с использованием математических формул, разработанных экономистом Джоном Кейнсом и любезно доработанных сетью Bancor.

Чтобы создать монету, пользователю нужно определиться с ее параметрами. Возьмем, например, такие:

  • CRR — 40%

CRR, или Constant Reserve Ratio, отвечает за коэффициент постоянного резервирования монетами BIP в составе выпущенной новой. Чем выше коэффициент, тем больше обеспечение, а значит, меньше волатильность. И наоборот.

Запускаем создание, и вот мы уже имеем совершенно новую монету, назовем ее, к примеру, NewsCoin.

В нашем примере после этой транзакции у основателей на руках оказываются 2500 оплаченных NewsCoin, за которые они в среднем заплатили 100/2500=0.04 BIP, но при этом уже по формуле из-за резерва лишь на 40% цена вырастает до 0.1 NewsCoin/BIP. Да, за счет неполного резервирования стоимость с каждой новой выпущенной монетой становится чуть выше предыдущей, и это одна из наших фундаментальных идей.

Image for post
Image for post

Если в этот момент вам кажется, что на этом можно заканчивать ICO, продав монеты обратно за BIP и выручив в разы больше, то разочарую — формулы одинаково работают в обе стороны. Так, сжигая (продавая в систему) 2500 NewsCoin, вы получите те самые 100 BIP. Посмотрите на график, все достаточно понятно.

Попробовать механику монет можно в калькуляторе, который позволяет симулировать операции покупки и продажи: https://calculator.beta.minter.network/

Формулы были переписаны на язык Go, вы можете изучить их в нашем репозитории: https://github.com/MinterTeam/minter-go-node/blob/master/formula/formula.go

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

Все балансы, которые вы видите в кошельках, — результат их деления для удобного отображения. В блокчейне Minter 1 монета (bip) = 1,000,000,000,000,000,000 базовых единиц (pip).

Поэтому самая большая проблема, с которой мы столкнулись, заключалась в сложности этих формул для детерминированного (одинакового для всех устройств) вычисления. Так как формулы используют возведение в дробную степень, нам пришлось пойти на хитрость. Использование библиотеки big.Math с конвертацией из big.Float в big.Int и обратно от разработчиков Golang позволило производить подобные вычисления достаточно однозначно.

Экономика сети и награда за блоки

Мы придерживаемся мнения, что большинство монет в блокчейне должно быть «намайнено», а не продано на ICO. Поэтому 98% эмиссии пользователи получат в качестве награды за блоки.

Награды за блоки стартуют с 333 bip и постепенно, за 7 лет, уменьшаются. Формулу расчета наград вы можете посмотреть в исходном коде: https://github.com/MinterTeam/minter-go-node/blob/master/core/rewards/rewards.go

Image for post
Image for post
Распределение монет

Типы транзакций и комиссии

В сети Minter существует несколько типов транзакций:

TypeSend                = 0x01
TypeSellCoin = 0x02
TypeSellAllCoin = 0x03
TypeBuyCoin = 0x04
TypeCreateCoin = 0x05
TypeDeclareCandidacy = 0x06
TypeDelegate = 0x07
TypeUnbond = 0x08
TypeRedeemCheck = 0x09
TypeSetCandidateOnline = 0x0A
TypeSetCandidateOffline = 0x0B
TypeCreateMultisig = 0x0C
TypeMultisend = 0x0D
TypeEditCandidate = 0x0E

У каждой транзакции своя комиссия за отправку. Отличительной особенностью Minter является то, что комиссию за транзакцию можно платить абсолютно в любой монете, даже в той, которую создал пользователь. Это возможно благодаря тому, что в Minter у каждой монеты есть рыночная стоимость и есть возможность моментально конвертировать пользовательскую монету в базовую.

Общая структура транзакции выглядит следующим образом:

Transaction {
Nonce
ChainID
GasPrice
GasCoin
Type
Data
Payload
ServiceData
SignatureType
SignatureData
}
  • Nonce —уникальный номер исходящей транзакции. Используется для того, чтобы предотвратить ее повторное исполнение.

Теперь рассмотрим каждый тип транзакции в отдельности.

Send позволяет отправить монеты любому пользователю в блокчейне. Стоимость этой транзакции составляет 0.01 BIP.

SendData {
Coin // Монета для отправки
To // Адрес получателя
Value // Количество монет для отправки
}

С помощью SellCoin/BuyCoin можно продать или купить определенное количество монет. Так как цена на монету в блокчейне постоянно меняется сложно предугадать какой она будет в момент исполнения транзакции. Поэтому нам пришлось разделить эти две транзакции. В определенном случае пользователь хочет купить фиксированное количество монет, а в других случаях продать фиксированное количество монет. Стоимость транзакций конвертации монет составляет 0.1 BIP.

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

В транзакциях SellCoin/BuyCoin/SellAllCoin отправитель так же должен указать сколько максимально он хочет потратить монет или сколько минимально хочет получить. Это сделано для того, чтобы злонамеренные пользователи или даже валидаторы не смогли при виде транзакции покупки монет купить себе такие же и тем самым обогатиться засчет оригинального отправителя транзакции.

BuyCoinData {
CoinToBuy // Монета для покупки
ValueToBuy // Количество монет для покупки
CoinToSell // Монета для продажи
MaximumValueToSell // Максимальное количество монет для продажи
}
SellAllCoinData {
CoinToSell // Монета для продажи
CoinToBuy // Монета для покупки
MinimumValueToBuy // Минимальное количество монет для покупки
}
SellCoinData {
CoinToSell // Монета для продажи
ValueToSell // Количество монет для продажи
CoinToBuy // Монета для покупки
MinimumValueToBuy // Минимальное количество монет для покупки
}

С помощью CreateCoin можно создать свою монету всего за 1 транзакцию.

CreateCoinData {
Name // Название монеты
Symbol // Тикер монеты
InitialAmount // Начальная эмиссия
InitialReserve // Начальный резерв
ConstantReserveRatio // CRR монеты
}

DeclareCandidacy дает возможность “задекларировать” валидатора в сети, чтобы начать валидировать блокчейн. Стоимость транзакции — 10 BIP.

DeclareCandidacyData {
Address // Адрес для управления валидатором и получения монет
PubKey // Публичный ключ валидатора
Commission // Комиссия
Coin // Монета, которую кладем в стейк
Stake // Количество монет
}

Delegate и Unbond позволяют делегировать и отзывать средства из мастерноды валидатора. Стоимость транзакции — 0.2 BIP.

DelegateData {
PubKey // Публичный ключ валидатора
Coin // Тикер монеты
Value // Количество монет
}
UnbondData {
PubKey // Публичный ключ валидатора
Coin // Тикер монеты
Value // Количество монет
}

RedeemCheck используется для того чтобы обналичить чек. Стоимость транзакции — 0.03 BIP. Особенность транзаций в том, что комиссия будет списана с баланса создателя чека, а не с отправителя транзакции. Это позволяет обналичивать чеки на только что созданных аккаунтах. Средства из чека будут зачислены на адрес отправителя транзакции.

RedeemCheckData {
RawCheck // Чек
Proof // Подписанное доказательство от получателя
}

SetCandidateOnline/SetCandidateOffline включают или выключают мастерноду. Стоимость транзакции — 0.1 BIP.

SetCandidateOnData {
PubKey // Публичный ключ валидатора
}
SetCandidateOffData {
PubKey // Публичный ключ валидатора
}

CreateMultisig позволяет создать аккаунт, который будут контролировать несколько приватных ключей. Стоимость транзакции — 0.1 BIP.

CreateMultisigData {
Threshold // Порог для отправки транзакции с адреса
Weights // Список "весов"
Addresses // Список адресов
}

С помощью Multisend можно отправить монеты сразу нескольким получателям. За первый адрес берется комиссия в 0.01 BIP, за последующие 0.005 BIP.

MultisendData {
List // Список SendData
}

EditCandidate позволяет изменить данные валидатора: адрес для получения наград и адрес для управления.

EditCandidateData {
PubKey // Публичный ключ валидатора
RewardAddress // Адрес для получения наград
OwnerAddress // Адрес для управления валидатором
}

Так же в каждой транзакции можно отправить до 1024 байт полезных данных. За каждый байт предусмотренна комиссия в 0.002 BIP.

Чеки

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

Пример чека:

Mcf89b01830f423f8a4d4e5400000000000000843b9aca00b8419b3beac2c6ad88a8bd54d24912754bb820e58345731cb1b9bc0885ee74f9e50a58a80aa990a29c98b05541b266af99d3825bb1e5ed4e540c6e2f7c9b40af9ecc011ca0387fd67ec41be0f1cf92c7d0181368b4c67ab07df2d2384192520d74ff77ace6a04ba0e7ad7b34c64223fe59584bc464d53fcdc7091faaee5df0451254062cfb37

Minter Check — это подписанное сообщение, которое гласит: предъявителю этого чека и пароля я разрешаю получить с моего счета некоторое количество монет.

В чеке указывается следующая информация:

  • Nonce —уникальный id чека

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

Image for post
Image for post
Создание чека в Minter Console

Консенсус: Валидаторы и делегаторы

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

Придумали следующую схему:

  • Валидатором может стать любой держатель монет, нужно лишь установить мастерноду и отправить транзакцию на включение в список валидаторов.

Однако была проблема, которая возникла при делегировании пользовательских монет. Предположим, что есть монета А. Продажа 100 единиц приносит 10 BIP, но продажа 200 единиц принесет всего 15 BIP. Это происходит из-за плавающей цены каждой проданной монеты. Представим сценарий:

  • Пользователь создает 2 кошелька

Реальная стоимость 200 монет — 15 BIP. Однако стейки считаются отдельно, поэтому реальное количество делегированных монет будет 100 монет + 100 монет = 20 BIP. Выходит, что таким образом можно обмануть систему и делегировать больше монет, чем есть у пользователя.

Чтобы решить эту проблему, нам пришлось ввести следующий механизм:

  • Суммируем все делегированные монеты одного типа

Но одним из самых крутых решений для валидации стало то, что мы начисляем награды за каждый блок ВСЕМ активным валидаторам пропорционально их стейку. Делегаторам и валидаторам не нужно ждать, пока их нода «намайнит» новый блок, чтобы получить награду, как это происходит в традиционных сетях. Поэтому в блокчейне Minter все валидаторы и делегаторы получают свои награды раз в 5 секунд, а на счет они зачисляются раз в 12 блоков (1 минута).

Для защиты от long-range атак мы блокируем делегированные на ноду средства в течение 30 дней после их «отзыва».

Штрафы для валидаторов и делегаторов

Надежная работа валидаторов очень важна для блокчейна. Во-первых, чем стабильнее работают валидаторы, тем больше пропускная способность сети. Во-вторых, консенсус Tendermint позволяет генерировать блоки только если 2/3+ мощности сети онлайн. Получается, что если по каким-либо причинам 1/3 мощности сети уходит в оффлайн, то блокчейн останавливается. Чтобы этого избежать, мы разработали систему экономического мотивирования валидаторов.

Валидаторы могут получить штраф в двух случаях:

  1. Нода не подписала 12 из последних 24 блоков. В данном случае нода будет автоматически исключена из списка валидаторов, и на ее стейк будет наложен штраф в размере 1%. Нужно заметить, что штраф накладывается не только на собственный стейк ноды, но и на стейки делегаторов. Поэтому делегаторам очень важно выбирать стабильные ноды для вложения своих средств.

Разработка кошельков, приложений и API блокчейна

Нашей главной задачей было сделать блокчейн максимально понятным для пользователей. Поэтому, еще до запуска, было принято решение разработать все необходимые пользователям сервисы и кошельки. Так началась разработка кошельков для Web, Telegram, iOS, Android, а также для сервисов Explorer, Status и Gate.

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

Все кошельки нужно было привести к одному стандарту, поэтому мы пришли к следующему решению:

  • Кошельки общаются с блокчейном через наши сервисы Gate и Explorer, чтобы можно было быстро масштабировать систему при нагрузке

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

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

Какие у нас в итоге получились кошельки, вы можете посмотреть по ссылкам:

Minter Console

https://testnet.console.minter.network/

Один из важнейших веб-сервисов, с помощью которого пользователи могут взаимодействовать с блокчейном Minter.
Console работает в двух режимах:

  • Авторизация по логину-паролю и хранением приватного ключа на сервере

Основные функции — это отправка всех видов транзакций: пересылка, конвертация и создание монет, регистрация и редактирование мастерноды, делегирование и анбондинг монет в чужие мастерноды, а так же создание и обналичивание чеков. Тут же можно посмотреть свой баланс и последние транзакции.

Разработка сервисов: Explorer, Status, мониторинги мастернод

Финальный результат: https://testnet.explorer.minter.network

Чтобы пользователи могли наблюдать процессы происходящие в блокчейне, мы разработали несколько сервисов.

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

Мы так же, как и большинство блокчейн-сетей, нуждались в подобном сервисе. Не всегда эти данные можно получить из ноды, да и не ее это забота, ведь лишние задачи могут привести к замедлению. Было принято решение разработать Minter Explorer, но вскоре требования к проекту увеличились, и проводник перерос в шлюз для общения блокчейн-сети с нашими приложениями.

Подробнее о его разработке вы можете прочитать в статье «Разработка Explorer для блокчейн-сети Minter».

Image for post
Image for post
Minter Testnet Explorer

Достижение максимальной скорости обработки транзакций и стабильности сети

Плавающая комиссия за транзакцию

Чтобы защитить сеть от спама, мы ввели систему плавающей комиссии за транзакции. Цена за нее зависит от количества транзакций в мемпуле (список неподтвержденных транзакций) нод. Сейчас она выглядит таким образом:

if mempoolSize > 5000 {
return 50
}

if mempoolSize > 1000 {
return 10
}

if mempoolSize > 500 {
return 5
}

if mempoolSize > 100 {
return 2
}

return 1

Говоря человеческим языком, если в мемпуле больше 100 транзакций, то комиссия увеличивается в 2 раза. Если больше 500, то в 5.

Даже при самой большой комиссии стоимость отправки монет на аккаунт будет составлять всего 0.01$.

Плавающая мощность сети

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

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

Механизм восстановления после сбоев

Мы потратили много времени на разработку и тестирование нашего блокчейна. Были запущены десятки тестнетов, обработаны десятки миллионов транзакций, запущен открытый bug bounty. Однако многие модули нашего блокчейна являются новинками на рынке и мы не исключаем, что могут возникнуть проблемы.

Есть 4 основные причины, по которым Minter Mainnet может перестать генерировать блоки. Далее мы разберем каждую из этих причин и способы восстановления сети при подобных сбоях.

  1. Единовременное отключение ⅓+ мощности валидаторов

1. Единовременное отключение ⅓+ мощности валидаторов

Типичный сценарий для тестнета, который использовали, чтобы остановить старую версию сети и запустить новую. Алгоритм консенсуса Tendermint устроен таким образом, чтобы останавливать свою работу, если за блок не проголосовало ⅔+ мощности сети. Подобная остановка не считается ошибкой.

Способ решения: валидаторы должны как можно быстрее вернуть в онлайн свои ноды. Если по каким-либо причинам это невозможно, то потребуется ручной форк кода и обновление остальных нод.

2. Срабатывание защиты

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

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

3. Ошибка в коде

Ошибка может возникнуть как в коде, за который ответственна команда Minter, так и в сторонних библиотеках. Самыми критичными считаются ошибки в движке Tendermint, которые могут привести к полной остановке сети.

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

4. Непринятие обновления ⅔+ мощности сети

Если было выпущено обновление (форк) и его приняло больше ⅓, но меньше ⅔ мощности сети, то произойдет ситуация, схожая с первым пунктом (единовременное отключение ⅓+ мощности валидаторов).

Способ решения: валидаторам необходимо как можно быстрее обновить свои ноды. Если по каким-либо причинам это невозможно, то потребуется ручной форк кода и обновление большинства.

Критическая ситуация, невозможность хардфорка

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

Данные, которые подлежат выгрузке:

  • Балансы аккаунтов

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

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store