Частные 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 приложения.