Cмарт-контракты Docker

Платформа Waves Enterprise предоставляет возможность разработки и использования Тьюринг-полных смарт-контрактов.

Смарт-контракты на платформе Waves Enterprise

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

Когда смарт-контракт запускается в блокчейн сети, его код нельзя произвольно изменить, заменить или запретить его выполнение без вмешательства в работу всей сети. Такое свойство делает смарт-контракты практически незаменимым инструментов в блокчейн сети.

Для хранения смарт-контрактов используется Docker Registry с доступом на чтение образов (Docker images) контрактов для машин с нодами. Компания Waves Enterprise предоставляет открытый репозиторий для Docker смарт-контрактов, куда любой разработчик может добавить свой смарт-контракт. Открытый репозиторий находится по адресу registry.wavesenterprise.com/waves-enterprise-public. Для добавления своего смарт-контракта в открытый репозиторий необходимо написать заявку в нашу техническую поддержку. После одобрения заявки смарт-контракт будет добавлен в открытый репозиторий, и вы сможете вызывать его из клиента или REST API ноды.

Если вы используете приватную блокчейн сеть, то вам необходимо иметь свой собственный Docker-репозиторий для публикования и вызова смарт-контрактов.

Доступ к состоянию ноды может выполняться через REST API ноды или через gRPC.

../../_images/docker-1.png

Смарт-контракт может создавать и вызывать любой участник сети независимо от того, есть у него нода-майнер или нет. Достаточно зарегистрироваться в сети Mainnet через клиентский интерфейс.

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

Создание смарт-контракта начинается с подготовки Docker-образа, который состоит из программного кода контракта, необходимого окружения и из специального сценарного файла Dockerfile. Подготовленный Docker-образ собирается (build) и отправляется в Docker Registry. Для отправки нового смарт-контракта создайте заявку на портале технической поддержки. После проверки смарт-контракта сотрудники технической поддержки размещают его в открытом Docker репозитории. В конфигурационном файле ноды по умолчанию уже присутствуют настройки секции docker-engine для работы с открытым Docker репозиторием, а также по умолчанию установлены рекомендованные значения параметров для оптимальной работы смарт-контрактов в блокчейн сети Mainnet.

Пример Dockerfile при использовании REST API:

FROM python:alpine3.8
ADD contract.py /
ADD run.sh /
RUN chmod +x run.sh
CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

Пример Dockerfile при использовании gRPC:

FROM python:3.9-rc-buster
RUN pip3 install grpcio-tools
ADD src/contract.py /
ADD src/protobuf/common_pb2.py /protobuf/
ADD src/protobuf/contract_pb2.py /protobuf/
ADD src/protobuf/contract_pb2_grpc.py /protobuf/
ADD run.sh /
RUN chmod +x run.sh
ENTRYPOINT ["/run.sh"]

Установка контракта реализуется через публикацию специальной (CreateContractTransaction) транзакции, содержащей ссылку на образ в Docker Registry. Для использования REST API или gRPC необходимо указать версию транзакции 103. После получения транзакции нода скачивает образ по указанной в поле «image» ссылке, образ проверяется и запускается в виде Docker-контейнера.

Исполнение контракта

Исполнение смарт-контрактов инициируется специальной (CallContractTransaction) транзакцией, в которой содержится идентификатор контракта и параметры для его вызова. По идентификатору транзакции определяется Docker-контейнер. Контейнер запускается, если не был запущен ранее. В контейнер передаются параметры запуска контракта. Смарт-контракты изменяют своё состояние через обновление пар ключ - значение.

Параллельное исполнение контрактов

Платформа Waves Enterprise позволяет запускать несколько Docker контрактов одновременно. Такая опция поддерживается только gRPC-контрактами. Как это работает:

  1. Разработчик смарт-контракта указывает параметр async-factor в коде контракта (подробнее см. Создание смарт-контракта). Данный параметр определяет допустимое количество одновременно выполняемых транзакций по смарт-контракту.

  2. При старте контракт передает в ноду значение параметра async-factor.

  3. Когда запускается исполнение контрактов, буфер для контрактов начинает наполняться. Из UTX пула берутся необработанные транзакции с контрактами, пока буфер не заполнится.

  4. Далее набранные транзакции разбиваются на группы по идентификаторам контрактов. В один момент времени допускается выполнение только одной группы, при этом внутри группы параллельная обработка контрактов определяется параметром async-factor.

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

  6. На параллельное исполнение контрактов влияет значение параметра pulling-buffer-size. Данный параметр настраивается в секции docker-engine конфигурационного файла ноды и указывает на величину буфера для обработки транзакций с контрактами.

На схеме ниже показан примерный принцип параллельной обработки смарт-контрактов.

../../_images/docker-parallel.png

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

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

Подробнее о создании смарт-контракта читайте на страничке Создание смарт контракта.

Изменение контракта

Изменять Docker смарт-контракт может только его разработчик, который создал транзакцию 103 и сохранил свою роль contract_developer в момент изменения смарт-контракта. Изменение смарт-контракта выполняется при помощи транзакции 107. Необходимо, чтобы смарт-контракт был активным.

После включения 107 транзакции в блок ноды-майнеры скачивают образ контракта и запускают его для проверки корректности исполнения. Далее выпускается 105 транзакция с включением в неё 107 транзакции.

Запрет вызова контракта

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

Описание транзакций

Для реализации взаимодействия между блокчейном и Docker контрактом реализованы следующие транзакции:

Код

Тип транзакции

Назначение

103

CreateContractTransaction

Инициализация контракта. Подписание транзакции производится пользователем с ролью «contract_developer»

104

CallContractTransaction

Вызов контракта. Подписание транзакции производится инициатором исполнения контракта

105

ExecutedContractTransaction

Запись результата исполнения контракта на стейт контракта. |br| Подписание транзакции производится нодой, формирующей блок

106

DisableContractTransaction

Запрет вызова контракта. |br| Подписание транзакции производится пользователем с ролью «contract_developer»

107

UpdateContractTransaction

Обновление кода контракта. |br| Подписание транзакции производится пользователем с ролью «contract_developer» |br| Изменять контракт может только его разработчик и инициатор 103 транзакции

Конфигурация ноды

Скачивание и исполнение Docker-контрактов, инициированных транзакциями с кодами 103 - 107 выполняется на нодах с включенной опцией docker-engine.enable = yes (подробнее в разделе «Установка и настройка» > «Запуск Docker-контрактов»).

REST API

Описание методов REST API, которые может использовать Docker-контракт, приведено в разделе Методы API, доступные смарт-контракту.

gRPC

Описание методов gRPC, которые может использовать Docker-контракт, приведено в разделе Сервисы gRPC, используемые смарт-контрактом.

Примеры реализации