Частные PoA сети на базе Parity Ethereum
Константин Нархов
Об авторе
Основная сфера интересов — программирование для встроенных систем, IoT и разработка веб-интерфейсов для автоматизации доступа к распределенным системам. В настоящий момент занимаюсь исследованием способов интеграции децентрализованной сети IoT устройств в частную PoA сеть на blockchain Ethereum. Являюсь разработчиком фреймворка Pheix, предназначенного для создания систем управления контентом с поддержкой хранения данных в blockchain.
Введение в Parity
Введение в Parity
Ethereum Virtial Machine
В сети Ethereum на каждом узле выполняется EVM. Для взаимодействия с EVM используется ethereum-клиент (коротко CLI).
Cтрого говоря, CLI запускает EVM и обеспечивает взаимодействие с ней.
Клиенты Ethereum
CLI является базовым ПО, обеспечивающим работоспособность узла в сети ethereum. Проще говоря, для запуска узла ethereum вам потребуется запустить CLI.
Parity — это мульти функциональный в смысле развертывания узла клиент, позволяющий запустить облегченный (light), архивный (archive) и полный (full) узел.
Алгоритмы консенсуса PoA
PoA — оптимальный вариант для использования в частных сетях: в сети назначаются узлы, которым делегируются права валидации блока.
В CLI Parity используется PoA алгоритм Aura, в официальном CLI geth — Clique.
Установка, настройка, тестирование
Установка, настройка и
тестирование частной PoA сети
Docker: предустановленное ПО
Наиболее простой и быстрый способ запуска частной сети ethereum - это использование docker. На узлах д.б. запущен docker-контейнер с предустановленным для выполнения CLI ПО: Go, Rust, Go-ethereum, Z3 prover, Solidity и Parity.
Docker: Do it yourself
Вы можете скачать и запустить готовый контейнер, собрать собственный контейнер, используя мой Dockerfile, или на основании инструкций из моего Dockerfile собрать все ПО вручную для своего дистрибутива linux без docker.
Адрес репозитория: https://gitlab.com/pheix-pool/docker-ethereum/container_registry
Настройка Parity узлов
- Создание файла первичной спецификации частной сети (genesis);
- Создание файлов первичной конфигурации узлов (путь к genesis, путь к каталогу хранения данных blockchain, номера портов, настройка сети, etc…);
- Запуск узлов с заданными конфигурационными файлами;
- Создание аккаунтов (через REST-запросы к запущенным на предыдущем шаге узлам);
- Дополнение спецификации частной сети данными об аккаунтах;
- Дополнение файлов конфигурации узлов данными об аккаунтах.
Настройка Parity узлов: особенности
- У узла parity нет этапа инициализации: путь к genesis указывается в конфигурационном файле узла и инициализация выполняется при запуске автоматически;
- Parity умеет создавать аккаунты, адреса которых однозначно определяются некоторой фразой, т.е. при создании топологии сети можно назначить символьные имена узлам, и для этих имен parity выполнит генерацию постоянных адресов;
- узел parity может быть запущен с единственным параметром командной строки: файлом конфигурации.
Запуск PoA сети
- Запуск узла выполняется командой parity --config <file>, при запуск узлов на удаленных серверах сопровождается некоторыми особенностями в части сетевых настроек, например, указанием IP вашего маршрутизатора;
- Связывание узлов через REST-запрос, JS консоль или конфигруационный файл;
- Подключение к узлу командой geth attach http://addr:port
Запуск PoA сети: helper скрипты
В репозитории https://gitlab.com/pheix-research/ethereum-local-network размещены скрипты, автоматизирующие процесс запуска частной сети:
- patchaccounts.sh - скрипт, выполняющий все этапы настройки частной сети;
- start.node* - скрипты запуска узлов частной сети;
- bindpeers.sh - скрипт, выполняющий связывание узлов;
- attach.node* - скрипты подключения JS-консоли geth;
- reinit.sh - скрипт удаления blockchain данных из локальной файловой системы;
- trace_tx.sh - скрипт отладки транзакции через REST-запрос.
CRUD смарт-контракт
CRUD смарт-контракт
Методы смарт-контракта: ядро
- new_table(string tabname) - создание пустой таблицы;
- drop_table(string tabname) - удаление таблицы;
- set(string memory tabname, uint rowid, string memory rowdata) - модификация данных по идентификатору;
- insert(string memory tabname, string memory rowdata, uint id) - вставка данных в таблицу;
- select(string memory tabname, uint rowid) - чтение данных по идентификатору;
- remove(string memory tabname, uint rowid) - удаление записи по идентификатору.
Методы смарт-контракта: утилиты
- table_exists(string tabname) - проверка существования таблицы;
- id_exists(string tabname, uint rowid) - проверка существования записи;
- count_tables() - подсчет количества таблиц;
- count_rows(string tabname) - подсчет количества записей в таблице;
- get_tabname_byindex(uint index) - получение имени таблицы по индексу;
- get_id_byindex(string tabname, uint index) - получение идентификатора записи по индексу;
- table_index(string tabname) - получение индекса таблицы;
- get_max_id(string tabname) - получение максимального идентификатора для таблицы;
- init() - инициализация тестовой базы данных.
Select All?!
Метод select_all умышленно не реализован в смарт-контракте, так как возвращаемым значение этого метода является динамический массив (действительно, изначально нам неизвестно сколько записей вернет метод). Solidity не позволяет возвращать динамические массивы по-умолчанию. Для этого нужно либо использовать pragma experimental ABIEncoderV2, либо выполнять сериализацию строк. Поэтому select_all выносится на уровень выше - CRUD-приложение верхнего уровня (node.js) реализует этот метод, используя count_tables(), get_tabname_byindex(), count_rows() и get_id_byindex().
Репозиторий смарт-контракта
Репозиторий с рассматриваемым смарт-контрактом является публичным и доступен по адресу https://gitlab.com/pheix-research/smart-contracts
Кроме исходного текста собственно смарт-контракта в него входит набор unit-тестов и скрипт, автоматизирующий процесс компиляции смарт-контракта, а также скрипт генерирующий JS-сценарий для быстрого развертывания смарт-контракта в частной сети.
Запуск за 6 шагов
- Клонировать репозиторий смарта-контракта в файловую систему контейнера, например в каталог /sc;
- Сгенерировать JS-сценарий для быстрого развертывания смарт-контракта с помощью команды cd /sc/t; ./compile-contract.sh pheix_database;
- Открыть geth консоль любого из узлов сети;
- Выполнить развертывание смарт-контракта командой loadScript("/sс/t/pheix_database/pheix_database.js");
- После выполнения команды (код возврата true) в geth консоли становится доступен объект storage;
- Для инициализации тестовой БД следует выполнить команду storage.init.sendTransaction({from:eth.accounts[0],gas:4700000}).
Отладка и оценка производительности смарт-контракта
Отладка и оценка производительности смарт-контракта
Модульные тесты
Для смарт-контракта CRUD приложения был разработан набор тестов, полностью покрывающий исходный код смарта-контракта в части функций (методов). В тестах отражена специфика данных, характерных для веб-приложений. Набор тестов может быть представлен в виде двух сущностей: собственно набора модульных тестов для множества методов смарт-контракта CRUD приложения и нагрузочного теста, выполняющего сохранение в blockchain текстовых данных различного объема.
Модульные тесты
- pheix_db_init_drop.js - тестирование init() и drop();
- pheix_db_insert_select_set.js - тестирование new_table(), insert(), select() и set();
- pheix_db_remove.js - тестирование remove();
- pheix_db_select_all.js - тест реализует выборку всех данных из таблицы;
- pheix_db_heavy_test.js - итеративный тест, в котором на каждой итерации выполняется создание нескольких таблицы, заполнение таблиц данными, удаление данных и проверка оставшихся в таблице данных.
Нагрузочный тест
Файлы данных и сценарии нагрузочного теста располагаются в каталоге t/pheix_database/txt. В нагрузочный тест входят:
- convert-to-js.sh - bash сценарий, генерирующий общий js-файл (datasets.js) со всеми текстовыми данными из каталогов t/pheix_database/set_0* в виде массива строк;
- lzw.js - реализация алгоритма Лемпеля-Зива-Велча (Lempel-Ziv-Welch) для сжатия текстовых данных перед сохранением в blockchain;
- dataset-deploy.js - собственно сценарий нагрузочного теста.
Конфигурирование нагрузочного теста
Нагрузочный тест может быть сконфигурирован с помощью глобальных переменных:
- _TAB_S - номер таблицы, с которой следует начать тестирование;
- _TAB_E - номер таблицы, на которое следует закончить тестирование;
- _GAS_CAP - увеличение газа на транзакцию в процентах от величины, измеренной функцией estimateGas().
Сжатие
Для оптимизации объемов blockchain хранилища выполняется предварительное сжатие текстовых данных с помощью алгоритма LZW (Лемпеля-Зива-Велча). Предварительное сжатие экономит до 40% объема хранилища на текстовых данных более 10кБ.
Запуск тестов
- Сгенерировать с помощью bash сценария convert-to-js.sh файл datasets.js;
- Установить конфигурационные переменные (по-умолчанию тест выполняется для всех таблиц, газ увеличивается на 10%: var _GAS_CAP=10;)
- Выполнить загрузку текстовых данных loadScript("/sс/t/pheix_database/txt/datasets.js");
- Инициализировать объект LZW, командой loadScript("/sс/t/pheix_database/txt/lzw.js"); (если нет необходимости в сжатии данных, то это шаг следует пропустить);
- Выполнить нагрузочный текст с помощью команды loadScript("/sс/t/pheix_database/txt/dataset-deploy.js");
konstantin@narkhov.pro
https://narkhov.pro
https://gitlab.com/pheix
https://www.linkedin.com/in/knarkhov/