В 2019 году к нам обратился «Ростелеком» с предложением принять участие в разработке бэкенда федеральной системы дистанционного электронного голосования (ДЭГ) на основе блокчейна. По сравнению с обычным голосованием ДЭГ на блокчейне явно дешевле и быстрее для подсчета голосов; оно также может обеспечить большую явку. Но при этом для большинства людей блокчейн-голосование — это черный ящик, а в голосованиях такого уровня ящик все-таки должен быть прозрачным. О том, как мы добились этого и выполнили другие требования заказчика, я расскажу далее в посте.

Требования к системе ДЭГ формировала Центральная избирательная комиссия. Вот их исходный список на 2019 год.

  • Запуск более 1000 голосований одновременно. Забегая вперед, скажу, что в 2021 году, пока что самом нагруженном для системы, мы одновременно запустили около 1700 голосований.

  • Обработка 10 млн голосов за 12 часов, то есть примерно 230 tps (транзакций в секунду). Впоследствии минимально допустимая скорость выросла в два раза, поскольку, на каждый голос пришлось отправлять дополнительную транзакцию — подтверждающую факт выдачи слепой подписи, то есть выдачи бюллетеня.

  • Гарантированный учет всех голосов.

  • Защита тайны голосования.

  • Защита промежуточных результатов голосования.

  • Защита итогов голосования.

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

К началу проекта у нас уже был готовый криптографический протокол и собственная блокчейн-платформа Waves Enterprise. Через смарт-контракты в ней можно было сразу развернуть всю необходимую логику и защитить данные в соответствии с требованиями. Но были нюансы.

  • Производительность (пропускная способность). Мы всегда уделяли ей внимание, но к 500 tps с ходу на смарт-контрактах были не готовы.

  • Гарантия исполнения транзакций. По умолчанию блокчейн очень старается исполнять все транзакции, но не гарантирует этого — принцип best effort basis. Для голосования так не пойдет, нужны железные гарантии. 

Далее я в общем виде опишу, как работает наша блокчейн платформа WE.Vote для дистанционных голосований, а затем то, как мы ее изменили для ДЭГ, чтобы соответствовать требованиям к федеральной системе.

Исходная архитектура WE.Vote

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

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

Третьим идет классический клиент-серверный слой — веб-интерфейсы администратора и участника голосования, а также некоторые вспомогательные сервисы. Администратор управляет запуском/остановкой голосований через взаимодействие с криптосервисами. Участник отправляет бюллетень напрямую в ноду.

Изменения для федерального ДЭГ

Исходная архитектура нашей платформы голосования соответствовала требованиям ЦИК частично. Вот что мы сделали, чтобы закрыть оставшиеся вопросы.

В нашей платформе создание ключа голосования было полностью автоматизировано. В ЦИК нас попросили сделать его более нагл��дным и отделенным от ядра системы — чтобы члены избирательных комиссий непосредственно участвовали в запуске и проведении голосований, создавая таким образом дополнительный барьер для злоумышленников.

В рамках федерального ДЭГ создание ключей частично вынесено в офлайн. У избирательной комиссии есть ноутбук со специальной утилитой, которая создает ключ. Затем по схеме разделения секрета Шамира ключ разбивают на несколько частей и на съемных носителях раздают держателям ключа. После завершения голосования ключ снова собирают. Имея ключ системы и собранный ключ комиссии, можно расшифровать итоги голосования. Благодаря распределенному хранению, злоумышленник не сможет манипулировать ключами, даже попав в контур голосования.

Следующий шаг — интеграция с компонентами, разработанными «Ростелекомом». Здесь мы создали очередь сообщений. В этой части системы нет ни особых нагрузок, ни секретных данных, сделано это только для упрощения интеграции.

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

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

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

Оптимизация пропускной способности

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

К началу проекта ДЭГ собственная платформа, мейннет Waves Enterprise, обеспечивал всего 10–20 транзакций в секунду. Предстояла большая работа, и мы начали исследование. 

Число транзакций в секунду обратно пропорционально размеру транзакции. На размер транзакции мы повлиять не можем, так как бюллетени предоставляет заказчик. Поэтому мы взяли за стандарт бюллетень с выбором одной партии из восьми и сосредоточились на оптимизации. Перевели транзакции в бинарный вид, научили ноды сериализовывать Protobuf и перекодировали криптографические константы в HEX. Ноды получили дополнительную нагрузку в связи с введением сериализации/десериализации, но в итоге пропускная способность выросла примерно в 10 раз.

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

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

Попробовали подойти к вопросу через железо. В итоге на самых мощных стендах добивались 500–600 tps. В целом влияние процессора и памяти оказалось не таким значительным, как мы предполагали, так что развивать эту тему мы не стали. Однако с помощью инструментов профилирования мы также нашли пару багов, и после фикса ноды стали работать быстрее.

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

Я писал выше, что блокчейн сам по себе не гарантирует, что все транзакции будут проведены. Нашим спасением здесь стала Kafka, с помощью которой мы создали очередь на обработку голосов. Другой сценарий для Kafka здесь — убежище при перезагрузке системы для данных, которые нода не приняла в блокчейн. По умолчанию данные хранятся только в оперативной памяти ноды и при перезагрузке уничтожаются. В этой роли Kafka, к счастью, нам не пригодилась, но перестраховаться стоило. Третий сценарий Kafka — это троттлинг нагрузки для пиковых часов. 40–50% избирателей приходят голосовать в первые часы работы ДЭГ, и Kafka позволяет эту нагрузку сглаживать.

Также ДЭГ стала первым проектом, где мы развернули свою версию CFT-консенсуса, который не приводит к роллбэкам. Когда мы начинали с PoS, роллбэки усложняли взаимодействие компонентов и нам приходилось объяснять, что это не мы выкинули голоса из системы, а потом что-то обратно записали, а это технологическая особенность. Если интересно узнать о нашем CFT-консенсусе подробнее, напишите в комментариях.

На самом масштабном голосовании на данный момент, в 2021 году, мы не потеряли ни одну из 1,5 млн транзакций. Но один голосов пришлось все-таки отклонить — он не прошел проверку ZKP.

Мониторинг всей системы ДЭГ

Фронтенд системы, как я уже писал, был в зоне ответственности «Ростелекома». Нам осталось протестировать только бэкенд — по сути, большую трубу, которая должна обеспечить на выходе столько же голосов, сколько и на входе.

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

Если у нас увеличивается число голосов со статусом pending — «было отправлено на ноду, но не обработано» — мы понимаем, что нода не справляется и есть какие-то проблемы. Аналогично мы можем отслеживать и другие этапы. Grafana может мониторить и Kafka: сколько голосов через нее было отправлено, какой лаг вычитки.

Помимо подсчета голосов, мы должны распределить ресурсы по компонентам «трубы» так, чтобы не возникало ботлнеков. Поэтому у каждого компонента предусмотрен мониторинг ресурсов. Удивительно, но инструмент мониторинга местами сам требовал больше ресурсов, чем отдельные компоненты системы, обрабатывающие голоса. Так что счетчики мы повесили и на мониторинг. 

Мониторинг ��локчейн-сети 

Через Grafana мы развернули мониторинг майнинга. Если высота блокчейна на графике равномерно растет, значит, ноды майнят и все работает как надо:

Если ноды обрабатывают блоки неравномерно, то сеть начинает разъезжаться и надо принимать меры:

Здесь мы столкнулись только с одним интересным эпизодом, на тестовом прогоне. На скорости 200 tps безопасники по ошибке отключили доступ по сети. После восстановления и перезапуска метода отправки все ноды в течение 5–10 минут собрались, синхронизировались и обработали все транзакции из UTX-пула — а их накопилось около полутора тысяч. В итоге не потеряли ни одной.

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

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

Если говорить о боевых метриках, то в этом году голосование было менее масштабным по сравнению с прошлым годом, когда показатели доходили до 120 tps. И у нас всегда имелся большой запас по производительности. 

Прозрачность системы

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

В прошлом году за нашей системой ДЭГ следили всего пять наблюдателей, а в этом их их стало уже 109. Для них был разработан портал наблюдения, который подключен к очереди обратной связи и получает данные одновременно с остальными компонентами системы. Выгрузки с этого портала наблюдатели могли загружать в дополнительно разработанную нами open-source утилиту. Сравнивая данные, прозрачно обработанные этой утилитой, и данные блокчейна голосования, наблюдатели могли убедиться, что всё обрабатывается одинаково.

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

Выводы

На основе проекта федеральной системы ДЭГ можно сделать несколько выводов с точки зрения технологии.

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

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

  • Эффективность блокчейна зависит от его места в общей архитектуре. Он помогает обеспечить прозрачность, но производительность его ограничена. Поэтому есть смысл фиксировать в блокчейне только ключевые точки бизнес-процессов. Здесь важно уметь положить сквозной процесс на блокчейн, но при этом не положить сам блокчейн.

Для дальнейшего погружения советую свой предыдущий пост о том, как работает дистанционное голосование на блокчейне. Также в сети выходили отчеты независимых наблюдателей о нашей с «Ростелекомом» системе по итогам голосования 2021 и 2022 гг. На Youtube можно посмотреть видеоверсию этого поста.