Как устроена платформа

Смарт-контракты

Смарт-контракт – это отдельное приложение, которое записывает в блокчейн свои входные данные и результаты исполнения заложенного алгоритма. Блокчейн-платформа Waves Enterprise поддерживает разработку и применение Тьюринг-полных смарт-контрактов для создания высокоуровневых бизнес-приложений.

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

Смарт-контракт может быть разработан на любом языке программирования и не имеет ограничений на реализацию заложенной логики.

Блокчейн-платформа Waves Enterprise реализует два типа смарт-контрактов:

Доступ смарт-контракта к стейту ноды для обмена данными осуществляется через API ноды. Docker смарт-контракты используют для этого gRPC API интерфейс. Доступ WASM смарт-контрактов к АПИ ноды предоставлен напрямую.

У каждого смарт-контракта есть собственный баланс, на котором могут храниться токены. Подробнее об управлении токенами из Docker смарт-контракта см. ниже. Управлении токенами из WASM смарт-контракта осуществляется аналогично.

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

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

Если вам необходимо ограничить доступ к смарт-контракту, создайте конфиденциальный смарт-контракт. Вызывать его и получать его результат смогут только ноды из списка, заданного вами при создании контракта. В дальнейшем вы сможете изменять этот список нод. Подробнее о конфиденциальных смарт-контрактах:

Важно

В релизах 1.14.0 и 1.15.0 WASM смарт-контракты не поддерживают атомарные транзакции и конфиденциальные смарт-контракты.

В ноду внедрен механизм MVCC (Multiversion concurrency control) – управление параллельным доступом к состоянию смарт-контрактов посредством многоверсионности. Благодаря этому нода позволяет параллельно выполнять несколько транзакций любых смарт-контрактов. При этом гарантируется согласованность данных. Механизм MVCC реализован одинаково для Docker и WASM смарт-контрактов.

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

Docker смарт-контракт исполняется в контейнере Docker. Благодаря этому запуск и исполнение смарт-контракта отделены от самой блокчейн-платформы.

Разработанный смарт-контракт упаковывается в Docker-образ, который хранится в открытом репозитории Waves Enterprise. Этот репозиторий основан на технологии Docker Registry, к нему имеет доступ любой разработчик смарт-контрактов.

Для добавления смарт-контракта в репозиторий свяжитесь со службой технической поддержки. После одобрения вашей заявки смарт-контракт будет загружен в репозиторий, и вы сможете вызвать его при помощи клиентского приложения или запроса по REST API к вашей ноде.

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

Общая схема работы Docker смарт-контракта

Ниже приведена общая схема работы Docker смарт-контракта:

../../_images/docker-11.png

Управление токенами из Docker смарт-контракта

Начиная с релиза 1.12 после активации функциональной возможности 1120 у смарт-контрактов блокчейн-платформы Waves Enterprise появляется собственный баланс, на котором могут храниться как системные токены WEST, так и любые другие токены. При этом для существовавших ранее смарт-контрактов баланс системных токенов WEST устанавливается равным нулю.

Также смарт-контрактам становятся доступны базовые функции работы с токенами:

  • выпуск токенов,

  • довыпуск токенов,

  • сжигание токенов, находящихся на балансе смарт-контракта,

  • перевод токенов с баланса смарт-контракта на баланс по адресу пользователя или пользователей.

Эти функции реализует метод CommitExecutionSuccess.

При помощи этой функциональности смарт-контракты имеют возможность изменять стейты ассетов и пользователей (их балансы). Пользователи также могут отправлять токены на баланс смарт-контракта.

Создание и установка Docker смарт-контракта

Практические указания по разработке логики смарт-контрактов, а также пример реализации на Python приведены в статье Разработка и применение смарт-контрактов Docker.

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

Создание смарт-контракта начинается с подготовки Docker-образа, который содержит готовый код смарт-контракта, сценарный файл Dockerfile, а также, в случае использования gRPC-интерфейса для обмена данных с нодой, необходимые protobuf-файлы.

Подготовленный образ собирается при помощи утилиты build, входящей в состав пакета Docker, после чего отправляется в репозиторий.

Для установки смарт-контракта и работы с ним необходима настройка секции docker-engine конфигурационного файла ноды. Если ваша нода работает в сети Waves Enterprise Mainnet, на ней по умолчанию настроены установка смарт-контрактов из открытого репозитория и установлены рекомендованные параметры для обеспечения оптимального исполнения смарт-контрактов.

Установка смарт-контракта в блокчейне выполняется посредством транзакции 103 CreateContract Transaction, в теле которой указывается ссылка на образ смарт-контракта в репозитории. При работе со смарт-контрактами рекомендуется отправлять транзакции последних версий.

При работе в частной сети транзакция 103 предусматривает загрузку Docker-образа контракта не только из репозиториев, указанных в секции docker-engine конфигурационного файла ноды. Если вам необходимо загрузить смарт-контракт из репозитория, не внесенного в конфигурационный файл, укажите в поле name транзакции 103 полный адрес смарт-контракта в созданном вами репозитории. Пример заполнения полей транзакции 103 приведен в ее описании.

После получения транзакции нода скачивает образ по ссылке, указанной в поле image. Затем скачанный образ проверяется нодой и запускается в Docker-контейнере.

Запуск Docker смарт-контракта и фиксация результатов исполнения

Запуск смарт-контракта инициируется участником сети при помощи транзакции 104 CallContract Transaction.

В этой транзакции передается id Docker-контейнера, в котором запускается смарт-контракт, а также его входные и выходные параметры в виде пар «ключ-значение».

Контейнер запускается, если не был запущен ранее.

Смарт-контракт выполняется и отправляет результат через gRPC API-интерфейс на ноду, которая инициировала запуск смарт-контракта. Нода, в свою очередь, генерирует транзакцию о результате выполнения смарт-контракта 105 ExecutedContract Transaction. Таким образом результат исполнения смарт-контракта фиксируется в его стейте при помощи транзакции 105 ExecutedContract.

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

Запрет запуска Docker смарт-контракта

Для того, чтобы отключить запуск смарт-контракта в блокчейне, отправьте транзакцию 106 DisableContract Transaction с указанием идентификатора контракта в блокчейн сети – contractId. Отправить эту транзакцию может только участник с ролью contract_developer, который создал этот контракт.

После отключения смарт-контракт становится недоступен для запуска. Информация об отключенном смарт-контракте продолжает храниться в блокчейне и доступна для gRPC или REST API-методов.

Обновление Docker смарт-контракта

Если вы изменили код вашего смарт-контракта, обновите его. Для этого заново загрузите смарт-контракт в репозиторий Waves Enterprise, отправив заявку на обновление смарт-контракта в службу технической поддержки.

Затем отправьте на ноду транзакцию 107 UpdateContract Transaction. Обновляемый смарт-контракт не должен быть отключен при помощи транзакции 106.

После обновления смарт-контракта ноды-майнеры блокчейна скачивают его и проверяют корректность исполнения. Затем информация об обновлении смарт-контракта вносится в его стейт при помощи транзакции 105, содержащей тело исполненной транзакции 107.

Подсказка

Изменять смарт-контракт может только участник с ролью contract_developer, создавший транзакцию 103 CreateContract Transaction для этого смарт-контракта.

Валидация Docker смарт-контрактов

Блокчейн-платформа поддерживает три варианта политик валидации смарт-контракта для обеспечения дополнительного контроля его целостности. Эта возможность доступна при выполнении следующих условий:

  • в сети активирована функциональная возможность 162;

  • в сети присутствует хотя бы один участник с активной ролью contract_validator;

  • для загрузки и обновления смарт-контрактов используются транзакции 103 и 107 версии 4 (и выше).

Политика валидации настраивается при помощи строкового поля validationPolicy.type соответствующей транзакции.

Доступные политики валидации:

  • any – сохраняется действующая в сети общая политика валидации: для майнинга обновляемого смарт-контракта майнер подписывает соответствующую транзакцию 105. Также этот параметр устанавливается, если в сети нет ни одного зарегистрированного валидатора.

  • majority – транзакция считается валидной, если она подтверждена большинством валидаторов: 2/3 от общего числа зарегистрированных адресов с ролью contract_validator.

  • majorityWithOneOf(List[Address]) – транзакция считается валидной, если собрано большинство валидаторов, среди которых присутствует хотя бы один из адресов, включенных в список параметра. Адреса, включаемые в список, должны иметь действующую роль contract_validator.

Предупреждение

При выборе политики валидации majorityWithOneOf(List[Address]), список адресов должен содержать хотя бы один адрес, передача пустого списка запрещена.

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

На платформе Waves Enterprise можно запускать несколько смарт-контрактов одновременно. Для этого на ноде реализован механизм MVCC (Multiversion concurrency control) – управление параллельным доступом посредством многоверсионности. Механизм позволяет параллельно выполнять несколько транзакций контейнеризированных смарт-контрактов и сохранять согласованность данных.

Все транзакции делятся на две группы:

  1. non-executable транзакции – атомарные контейнеры и все классические транзакции: transfer transaction, data transaction и т. п.;

  2. executable транзакции – транзакции всех контейнеризированных смарт-контрактов.

Транзакции первой группы всегда выполняются последовательно (уровень параллелизма равен единице). Для второй группы транзакций параллелизм исполнения определяется значением параметра node.docker-engine.contracts-parallelism в конфигурации ноды:

node.docker-engine.contracts-parallelism = 8

По умолчанию используется значение 8. Таким образом все смарт-контракты выполняются параллельно, независимо от Docker-образа.

Примечание

Между двумя группами транзакций присутствует конкуренция: если в UTX-пуле накапливаются разнородные транзакции, то параллельность может снижаться. Такое поведение можно сгладить, увеличив размер pulling буфера, но полностью исключить нельзя.

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

API-инструменты, доступные Docker смарт-контракту

Для обмена данными между смарт-контрактом и нодой предусмотрены методы gRPC API. При использовании этих методов вы можете осуществлять широкий спектр операций с блокчейном.

Подробнее:

WASM смарт-контракты

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

Важно

WASM смарт-контракты можно использовать на платформе Waves Enterprise начиная с релиза 1.14.0 после активации функциональной возможности 1140.

Основные преимущества WASM:

  • Высокая производительность и совместимость,

  • Малый размер WASM байт-кода,

  • Изолированное и платформо-независимое исполнение,

  • Расширенная языковая поддержка.

Важно

В релизах 1.14.0 и 1.15.0 WASM смарт-контракты не поддерживают атомарные транзакции и конфиденциальные смарт-контракты.

Общие настройки WASM смарт-контрактов задаются в секции node.wasm конфигурационного файла ноды.

Общая схема работы WASM смарт-контракта

WASM смарт-контракты представляют собой base-64 WASM байт-код, расположенный внутри ContractInfo.

Основным отличием реализации WASM смарт-контракта от Docker смарт-контракта является доступ к API ноды напрямую без использования gRPC API.

Ниже приведена общая схема работы WASM смарт-контракта:

../../_images/wasm_ru.png

При исполнении WASM смарт-контракта создается WASMService, который получает доступ к функционалу блокчейна (предоставляет интерфейс к инстансу DelegatedBlockchain, например, TransactionsAccumulator).

WASMContractExecutor вызывает executeTransaction. Внутри executeTransaction WASMExecutor вызывает исполнение байткода смарт-контракта (runContract) и его валидацию (validateBytecode).

  • В случае транзакции создания контракта CreateContractTransaction, вызывается метод runBytecode с переданной функцией _constructor.

  • В случае транзакции изменения контракта UpdateContractTransaction, метод runBytecode не вызывается; возвращается ContractExecutionSuccess с пустыми изменениями.

  • В случае транзакции вызова контракта CallContractTransaction, вызывается метод runBytecode с переданной в CallContractTransaction функцией callFunc. Функцией в CallContractTransaction не может быть _constructor.

При формировании очередного блока транзакция извлекается из UTX-пула, создаётся одноразовый инстанс класса WASMService и аккумулируется транзакция через WASMService.

Разработка WASM смарт-контракта

WASM смарт-контракт можно разработать на любом языке программирования, который поддерживает компиляцию исходного кода в WebAssembly байт-код. Виртуальная машина предоставляет весь необходимый набор функций для написания контрактов любой сложности. Перед созданием WASM контракта на платформе достаточно его скомпилировать.

Практические указания по разработке WASM смарт-контрактов приведены в статье Разработка и применение WASM смарт-контрактов.

Создание и вызов WASM смарт-контракта

Для создания и вызова WASM смарт-контрактов используются те же транзакции, что и для Docker контрактов.

Для создания контракта необходимо в транзакции 103 CreateContract версии 7 указать байт-код контракта в виде base64 строки и его хэш байт-кода.

Важно

У участника, который разрабатывает смарт-контракт, должна быть роль contract_developer в сети. Участник с этой ролью может вызывать смарт-контракты, а также запрещать их исполнение и обновлять их код.

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

Запуск WASM смарт-контракта инициируется участником сети при помощи транзакции 104 CallContract Transaction версии 7. Для вызова WASM контракта необходимо указать вызываемую функцию и список аргументов, необходимый для данной функции.

Фиксация результатов исполнения WASM смарт-контракта

Результатом выполнения контракта является число 0, если контракт выполнен успешно, либо код ошибки. В таблице ниже перечислены коды ошибок, которые возвращает WASM смарт-контракт.

Коды ошибок

Код ошибки

Название ошибки

Условие при котором возвращается ошибка

100

InvalidBytecode

Не удалось распознать и валидировать байт-код WASM

101

ConstructorNotFound

Не удалось найти конструктор контракта

102

MemoryError

Ошибка при работе с виртуальной или линейной памятью

103

MemoryLimits

Ограничение объема памяти ниже u32::MAX

104

LinkerError

Ошибка при работе с инстансами Linker

105

InstantiateFailed

Не удалось создать и запустить байт-код WASM

106

HeapBaseNotFound

Не удалось найти Global heap base

107

FuncNotFound

Не удалось найти функцию

108

InvalidNumArgs

Недопустимое количество аргументов

109

FailedParseFuncArgs

Не удалось распознать аргумент функции

110

FailedDeserializeDataEntry

Не удалось распознать аргументы DataEntry

111

FailedExec

Сбой во время выполнения

112

StackOverflow

Ошибка переполнения стека вызовов

304

no data for this key: <key>

Попытка обратиться к ключу, для которого не инициализирован storage

501

DataIsMissing

Не существует contractKey или contractBytecode

502

InvalidArgument

Передан некорректный аргумент из WEVM, например некорректный адрес получателя

503

InvalidTransfer

Ошибки переводов, например при попытке передать в Transfer значение меньше 0 или создать слишком много переводов на баланс контракта (поле payments)

Если контракт производит какие-либо изменения своего состояния, или совершает операцию над ассетами, то WASMService фиксирует такие изменения, вызывая внешние функции (Environment).

Смарт-контракт выполняется и отправляет результат через API на ноду, которая инициировала его запуск. Нода, в свою очередь, генерирует транзакцию 105 ExecutedContract Transaction о результате выполнения смарт-контракта. Таким образом результат исполнения смарт-контракта фиксируется в его стейте при помощи транзакции 105 ExecutedContract.

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

Запрет запуска WASM смарт-контракта

Запуск WASM смарт-контракта отключается так же как и запуск Docker смарт-контракта.

Обновление WASM смарт-контракта

Если вы изменили код вашего WASM смарт-контракта, обновите его с помощью транзакции 107 UpdateContract Transaction версии 7.

Важно

Обновляемый смарт-контракт не должен быть отключен при помощи транзакции 106 DisableContract Transaction.

Подсказка

Изменять смарт-контракт может только участник с ролью contract_developer, создавший транзакцию 103 CreateContract Transaction для этого смарт-контракта.

Валидация WASM смарт-контрактов

Для обеспечения дополнительного контроля целостности WASM смарт-контрактов блокчейн-платформа поддерживает те же политики валидации, что и для Docker смарт-контрактов.

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

На платформе Waves Enterprise можно запускать несколько смарт-контрактов одновременно с помощью реализованного на ноде механизма управления параллельным доступом к состоянию смарт-контрактов посредством многоверсионности (MVCC). Этот механизм позволяет параллельно выполнять несколько транзакций контейнеризированных смарт-контрактов и сохранять согласованность данных.

Механизм MVCC для WASM смарт-контрактов аналогичен механизму для Docker смарт-контрактов.

Смотрите также