Частные PoA сети на базе Parity Ethereum — часть 3: смарт-контракт CRUD приложения
06 июня, 2019
Предыдущие части
Частные PoA сети на базе Parity Ethereum — часть 2: настройка и запуск
Частные PoA сети на базе Parity Ethereum — часть 1: общие тезисы и особенности установки
CRUD - приложение, предоставляющее базовые функции взаимодействия с базой данных (создание/create, чтение/read, обновление/update, удаление/delete) и обеспечивающее доступ к хранилищу данных или СУБД. В терминах blockchain технологии CRUD обеспечивает доступ к данным, хранящимся в цепочке блоков.
В blockchain Ethereum CRUD приложением нижнего уровня является смарт-контракт, а CRUD приложением высокого - geth консоль или, например, веб-приложение на node.js или php.
Коснувшись вскользь использования веб-приложений, следует ненадолго на этом остановиться. В web важность децентрализованных приложений (и как следствие хранилищ данных) особенно актуальна. При этом blockchain предоставляет дополнительные средства, качественно увеличивающие функциональную ценность веб-приложения: полная история изменений хранилища данных, история доступа к данным на уровне узлов, ограничение доступа к методам смарт-контракта на уровне узлов - очень полезное свойство для ограничения доступа к rw-методам контракта, т.е. некоторый доверенный узел (т.н. owner смарт-контракта) может модифицировать хранилище данных в полном объеме, а остальным узлам предоставлен только доступ на чтение. Это схема, при которой работоспособность административной панели некоторого сайта переносится на защищенный узел.
Архитектура базы данных представлена на рисунке. Особенностью смарт-контракта является использование специального типа данных mapping, который обеспечивает доступ к данным по ключу. Ключи хранятся в динамических массивах: table_names[] и indexes[].

Для управления хранилищем данных (базой данных) смарт-контракт предоставляет следующие программисту базовые для 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)— вставка данных в таблицу, если таблицы с заданным именем не существует она создается, если в качестве идентификатора записи передан 0, то новой записи будет присвоен(max(ids[])+1)идентификатор для заданной таблицы;select(string memory tabname, uint rowid)— чтение данных по идентификатору записи для таблицы с заданным именем;remove(string memory tabname, uint rowid)— удаление записи по идентификатору для таблицы с заданным именем.
Помимо указанные методов в смарт-контракте реализованы утилитарные методы, обеспечивающие работу CRUD методов:
table_exists(string tabname)— проверка существования таблицы с заданным именем;id_exists(string tabname, uint rowid)— проверка существования записи с заданным идентификатором в таблице с заданным именем;count_tables()— подсчет количества таблиц в хранилище;count_rows(string tabname)— подсчет количества записей в таблице с заданным именем;get_tabname_byindex(uint index)— получение имени таблицы по индексу в массиве table_names[];get_id_byindex(string tabname, uint index)— получение идентификатора записи по индексу в массиве indexes[];table_index(string tabname)— получение индекса таблицы в массиве table_names[] по имени;get_max_id(string tabname)— получение максимального идентификатора для таблицы с заданным именем;init()— инициализация тестовой базы данных (три таблицы с несколькими записями в каждой).
Метод select_all() умышленно не реализован в смарт-контракте, так как возвращаемым значение этого метода является динамический массив (действительно, изначально нам неизвестно сколько записей вернет метод). Загвоздка в том, что solidity не позволяет возвращать динамические массивы по-умолчанию. Для этого нужно либо использовать pragma experimental ABIEncoderV2; (но стоит ли применять экспериментальный функционал в production?!), либо выполнять сериализацию строк. Поэтому select_all() выносится на уровень выше — CRUD-приложение верхнего уровня (например, node.js) реализует этот метод, используя count_tables(), get_tabname_byindex(), count_rows() и get_id_byindex().
Отметим, что для компиляции смарт-контракта требует компилятор solidity версии не ниже 0.5.1.
Репозиторий с рассматриваемым смарт-контрактом является публичным и доступен всем желающим. Кроме исходного текста собственно смарт-контракта в него входит набор unit-тестов и скрипт, автоматизирующий процесс компиляции смарт-контракта, а также скрипт генерирующий JS-сценарий для быстрого развертывания смарт-контракта в частной сети.
Для развертывания смарт-контракта в локальной частной сети (предполагаем, что сеть успешно настроена и запущена в docker контейнере) нужно:
- Клонировать репозиторий смарта-контракта в файловую систему контейнера, например в каталог
/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}).
Читать далее: Частные PoA сети на базе Parity Ethereum — часть 4: тестирование смарт-контракта CRUD приложения.
narkhov.pro