Разработка блокчейна с помощью Python и Tendermint
Часть 1 - создание кошелька

Это статья – первая часть из серии о разработке собственного блокчейна на Tendermint. Она будет интересна всем тем, кто задается вопросом создания блокчейн сети и выбирает среди множества вариантов. Объективно, на данный момент у Tendermint существует немало конкурентов: Exonum от команды BitFury, HyperLedger Fabric от Linux Foundation и многие другие. Плюсы и минусы каждого инструмента мы рассмотрим в отдельной статье, а здесь лишь зададим основной тезис – Tendermint фантастически прост в использовании. Данный фреймворк обеспечивает, наверное, максимально комфортный старт в разработке корпоративных блокчейнов, особенно если речь идет о каких-то MVP.

Мы используем Tendermint уже более года, разработали блокчейны для четырех разных проектов и получаем искреннее удовольствие от этого инструмента. Эта серия статей будет представлять из себя описание общей философии при работе с Tendermint (эта часть будет интересна не-техническим людям) и непосредственно tutorial по разработке.

Результатом этого цикла станет полностью работающая криптовалюта. Вы можете сразу изучать, что получилось, в нашем репозитории – https://github.com/SoftblocksCo/Simple_coin.

Введение

Сам по себе Tendermint можно описать как некоторое ядро, в котором реализован самый необходимый функционал, характерный для любого блокчейна. Можно сказать, что основная задача Tendermint – снять с разработчиков совсем уж рутинные задачи. Простой пример – любая блокчейн сеть децентрализована по определению. А значит, когда вы поднимаете ноду Bitcoin, Ethereum и т.д., ваша нода первым делом начинает искать других участников сети. Не будем заострять внимание на том, какой механизм лежит под капотом – главное то, что этот функционал “поиска таких же как я” должен быть реализован в ПО любого блокчейн проекта.

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

Если вы решите использовать Tendermint – не придется. Этот проект дает возможность тратить время разработчиков именно на описание бизнес-логики вашего блокчейна, а не на какие-то рутинные вещи. Вот примерный список того, что вы получаете из коробки:

  • Алгоритм консенсуса (легко кастомизируется, но об этом в другой раз)
  • P2P networking
  • Memory pool
  • API (очень удобно, для создания кошельков и т.д.)

В двух словах об архитектуре Tendermint. По сути она состоит из двух главных компонент: Tendermint core и Application. Что такое Tendermint core я уже рассказал выше. Application – это реализация вашей бизнес логики. Для простоты, рассмотрим пример. Пусть у нас есть два участника: нода Alice и нода Bob. Каждая нода (с точки зрения программного обеспечения) представляет собой пару из Tendermint core и Application. Когда Alice создает транзакцию (например с помощью какого-то кошелька), она обращается к своему Tendermint core – “Вот новая транзакция, распространи ее по сети”.

Как мы уже говорили ранее, Tendermint core берет на себя все заботы по тому, что касается поиска других участников сети и коммуникации с ними. Для нас важно то, что Tendermint core из ноды Боба, спустя какое-то время получил эту транзакцию. Теперь встает вопрос – а валидна ли эта транзакция? Может быть случилась ошибка и Алиса средств больше, чем имеет? Здесь в игру вступает ваш Application.

По сути, Tendermint core не имеет ни малейшего представления о вашем блокчейне – его задачах, ограничениях, протоколе и так далее. Любая транзакция для него это просто набор байт. Он может их передать другим нодам, запомнить, что такая транзакция уже когда-то была. А вот вопросы по логике его совершенно не интересуют. Вместо этого, получив транзакцию, он обратиться к вашему Application и спросит: “Вот транзакция 0x…. Это валидная транзакция?” А вот внутри вашего Application вы должны будете произвести все проверки и “ответить” core – да или нет.

Как вы видите, еще один плюс Tendermint – это удивительная модульность. Вы реализуете одну компоненту, которая должна уметь “отвечать на ряд вопросов” от Tendermint core – и все! Еще один плюс – взаимодействие между Tendermint core и Application происходит в виде вызовов через протокол ABCI. А значит вам ничто не мешает написать свой  использовать любой язык программирования для написания Application, лишь бы вы умели работать с этим протоколом. И в целом нет никаких ограничений на используемый стек разработки.

Наверное самый популярный вопрос у наших клиентов касается производительности Tendermint. В последнее время разговоры о 100 / 1000 / 5000 транзакциях в секунду уже не кажутся безумством, поэтому проясним и этот момент. Лучше всего будет процитировать документацию самого Tendermint:

“Tendermint blocks can commit to finality in the order of 1 second. Tendermint can handle transaction volume at the rate of 10,000 transactions per second for 250byte transactions. The bottleneck is in the application.”

На пальцах это означает, что сам Tendermint core обладает пропускной способностью до 10.000 транзакций в секунду. И, вероятнее всего, пропускная способность вашего блокчейна упрется в то, как вы реализовали Application.

Установка

Описание Tendermint, доступное широкой публике закончилось, теперь можно поговорить именно о разработке на Tendermint. Для разработки и деплоя мы использовали Ubuntu 16.04. В качестве языка для написания своего Application, мы выбрали Python. Рекомендуется использовать Go (Tendermint core написан именно на нем), но с Python знакомо больше людей, поэтому для своего tutorial мы остановились на нем.

Для работы с Tendermint core мы будем использовать docker-контейнеры. Данные, записанные в блокчейн, будут потеряны, если контейнер будет удален, поэтому желательно сохранять данные на host машине (флаг -v):

sudo docker run --rm -v /your/tendermint/folder:/tendermint tendermint/tendermint:0.24.0 

Настройка Python окружения

Как это часто бывает в мире Python разработки, вне зависимости от свежести какой-либо технологии, для нее уже существует несколько библиотек. Мы воспользуемся tm-abci – эта обертка предоставляет базовый класс `BaseApplication`, который умеет обрабатывать запросы от Tendermint core и вызывать реализованные вами методы. Данный проект подерживается командой Softblocks – если вы столкнулись с какими-то трудностями или хотите внести свои изменения – не стесняйтесь писать в Issues или создавать Pull requests в нашем Github репозитории.

cd Simple_coin
virtualenv --python python3 --no-site-packages venv/
source venv/bin/activate
pip install -r requirements.txt

Функционал

В качестве примера, мы будем реализовывать самую простую криптовалюту. Под простотой я имею в виду полное отсутствие различных фишек вроде смарт контрактов, оракулов, zero-knowledge и прочего. Весь функционал монетки можно описать следующим списком:

–  Пользователь может завести себе кошелек (на выходе пользователь получит приватный и публичный ключ)
– Пользователь может отправить другому пользователю монеты (для этого ему нужен публичный ключ получателя)
– Пользователь может записать какую-то текстовую строку в транзакцию (своего рода хранение данных)

Кошелек

В первую очередь давайте определимся с протоколом и используемой криптографией. А так как у нас нет цели создать что-то, способное занять верхние позиции на coinmarketcap.com, то мы можем позволить себе некоторые упрощения. Я предлагаю взять так называемую state модель, используемую в Ethereum, а сами транзакции будут реализованы в виде обычного JSON.

Транзакцией будем называть следующую структуру данных:

{
     "sender" : ,
     "receiver" : ,
     "amount" : 100,
     "data" : "big espresso",
     "timestamp" : 1517395993,
     "signature" :
}

Как видите, это совершенно привычный нам JSON, где в качестве адресов отправителя и получателя используются их публичные ключи. Поле amount показывает сумму транзакции, в data хранится какие-то дополнительные данные, а timestamp используется для сохранения временной метки транзакции. В signature будет вставлена подпись транзакции приватным ключом отправителя, но об этом чуть ниже.

Открытие счета

Как вы уже поняли, для работы с нашей монетой нужно иметь приватный и публичный ключ.  В качестве используемого стандарта криптографических подписей мы выбрали Ed25519. В целом, можно было взять любой другой стандарт – хоть тот же secp256k1, используемый в Bitcoin и его форках. Но, во-первых, сам Tendermint под капотом использует Ed25519. Во-вторых, Ed25519 все-таки обладает некоторыми преимуществами по сравнению с Bitcoin. Детальное сравнение этих двух стандартов вы можете найти в блоге Exonum.

Для тех, кто совсем не знаком с такого рода криптографией, дам краткий экскурс. Прежде всего, пользователю нужен приватный ключ – это просто 32 случайных байта = 256 случайных бит. Для работы с криптографией я воспользуюсь пакетом ed25519. В качестве кошелька давайте использовать файлы с расширением .sc: в первой строчке записн приватный ключ, во второй публичный.

signing_key, verifying_key = ed25519.create_keypair(entropy=urandom)
with open(options.wallet, "w") as ff:
   ff.write(signing_key.to_ascii(encoding="base64").decode())
   ff.write("\n")
   ff.write(verifying_key.to_ascii(encoding="base64").decode())
   ff.write("\n")

Для удобного создания нового адреса, в wallet.py мы реализуем новый флаг -n / –new

$ python wallet.py --new --wallet my.sc

New keypair saved into the my.sc

$ cat my.sc
GQsF8XoX8tT5Fwh9H8WLROhbfVr9AJjBL0F9XX1rugY
oBgi6dPXyH1bGi/8SeSA+LNHbwNEg3ZbPNxMLmZcJpQ

Подпись сообщения

Данный функционал не является чем-то необходимым, но мы решили его встроить, чтобы продемонстрировать на пальцах работу электронной подписи. С помощью флага –sign мы можем подписать любое сообщение, например “Hello, world”:

 

$ python wallet.py --message "Hello world" --sign
The signature is: JHCfU2rJ4sm9GGfXRo9tBWoVJ5/MGW+B4MhFzapIR6bS46saPLNQVz00drmR52ZwgdzjDX0647HYXCFMpFgpCA

Проверка подписи сообщения

Теперь убедимся в том, что наша подпись работает корректно. Проверить это можно с помощью флага check_sign (публичный ключ взят из файла my.sc):

$ python wallet.py --check_sign JHCfU2rJ4sm9GGfXRo9tBWoVJ5/MGW+B4MhFzapIR6bS46saPLNQVz00drmR52ZwgdzjDX0647HYXCFMpFgpCA --message "Hello world" --pub_key 0ADti7wLQBWj0SlPTkwmegrydF1LtuSTYUw1d00bpt4
Valid signature!

Отлично, подпись прошла проверку! Теперь попробуем изменить первый символ подписи J -> I:

$ python wallet.py --check_sign IHCfU2rJ4sm9GGfXRo9tBWoVJ5/MGW+B4MhFzapIR6bS46saPLNQVz00drmR52ZwgdzjDX0647HYXCFMpFgpCA --message "Hello world" --pub_key 0ADti7wLQBWj0SlPTkwmegrydF1LtuSTYUw1d00bpt4
Invalid signature!

Подпись не подошла, как и следовало ожидать. Отлично, теперь у нас есть рабочий инструмент для подписывания транзакций.

Создание транзакции

В первую очередь создадим получателя для нашей транзакции (будем называть его Bob):

$ python wallet.py --new --wallet bob.sc
New keypair saved into the bob.sc
$ cat bob.sc
NkjHzUjBoH0xNvGdeJg2ga2qkuFDO3n3J8IO8x21YJo
nSQkpxrkWfdjFKVzvbLkbJd6oTIWXPmdudHYJMjVcqo

Теперь давайте создадим транзакцию. Мы хотим отправить Бобу 10 монет и указать в сообщении “Thanks for latte”:

$ python wallet.py --transaction --amount 10 --receiver nSQkpxrkWfdjFKVzvbLkbJd6oTIWXPmdudHYJMjVcqo --data "Thanks for latte" --wallet my.sc
Your txn is printed bellow. Copy as it is and send with the ABCI query
0x7b2273656e646572223a2022304144746937774c5142576a30536c50546b776d656772796446314c747553545955773164303062707434222c20227265636569766572223a20226e53516b7078726b5766646a464b567a76624c6b624a64366f54495758506d64756448594a4d6a5663716f222c2022616d6f756e74223a2031302c202264617461223a20225468616e6b7320666f72206c61747465222c202274696d657374616d70223a20313533373237363334332c20227369676e6174757265223a202262525753326b456c636c6268586736686e533834317433666f6d77506d346441415951673749693179537748397046316258673873486d716a4b6e4b433851386b424a7534634f6c7678656553306158684f7a624251227d

Подпись транзакции

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

       txn = {
           "sender" : verifying_key.to_ascii(encoding="base64").decode(),
           "receiver" : options.receiver,
           "amount" : options.amount,
           "data" : options.data,
           "timestamp" : int(time())
       }

Для проверки соответствия подписи сообщению, мы должны быть уверены, что проверяющая сторона получит точно такое же сообщение. Словари в Python не гарантируют упорядоченность ключей, поэтому подписывать мы будем строку, состоящую из полей транзакции, разделенных через `;`.

       keys_sequence = sorted(txn.keys())
       msg_to_sign = ";".join([str(txn[k]) for k in keys_sequence])
       txn["signature"] = signing_key.sign(msg_to_sign.encode(), encoding="base64").decode("ascii")

Распостранение транзакции

Для того, чтобы оправить свою транзакцию в сеть, можно использовать стандартный API, встроенный в Tendermint. Работать с Tendermint core мы будем в следующей статье, поэтому здесь просто приведу пример того, как выглядит взаимодействие с API:

        r = requests.get("http://localhost:26657/broadcast_tx_async?tx={}".format(options.broadcast))


       if r.status_code == 200:
           txn_hash = r.json()['result']['hash']
           exit("Txn broadcasted, txn hash: {txn_hash}".format(txn_hash))
       else:
           err_log = r.json()['result']['log']
           exit("Can't broadcast your txn: {err_log}".format(err_log))

Распостранение транзакции

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

Итог

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

Если у вас остались какие-то вопросы – не стесняйтесь задавать их в нашем Twitter или отправлять на почту sergey@softblocks.co!

Close Menu