Установка и использование платформы

Разработка и применение смарт-контрактов

Определение и общее описание работы смарт-контрактов блокчейн-платформы Waves Enterprise приведено в статье Смарт-контракты.

Ниже приведены примеры разработки Docker смарт-контрактов и WASM смарт-контрактов.

Разработка и применение Docker смарт-контрактов

Подготовка к работе

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

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

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

Также вы можете загрузить Docker-образ контракта из репозитория, не указанного в конфигурационном файле ноды, при помощи транзакции 103 CreateContract, инициирующей создание смарт-контракта. Подробнее см. раздел Создание и установка смарт-контракта, а также описание транзакции 103. CreateContract.

При работе в Mainnet в конфигурационном файле предустановлен открытый репозиторий Waves Enterprise.

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

Смарт-контракты блокчейн-платформы Waves Enterprise могут разрабатываться на любом предпочтительном вам языке программирования и реализовывать любые алгоритмы. Готовый код смарт-контракта упаковывается в Docker-образ с используемыми protobuf-файлами.

Примеры кода смарт-контрактов на Python с применением gRPC API-методов для обмена данными с нодой, а также пошаговое руководство по созданию соответствующих Docker-образов приведены в следующих статьях:

Для разработки, тестирования и развертывания смарт-контрактов в публичных блокчейн сетях Waves Enterprise вы можете использовать инструментарии JS Сontract SDK Toolkit или Java/Kotlin Сontract SDK Toolkit. Они описаны в следующих разделах:

Загрузка смарт-контракта в репозиторий

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

При работе в частной сети, загрузите Docker-образ смарт-контракта в собственный репозиторий Docker registry как описано ниже.

Загрузка Docker-образа смарт-контракта в репозиторий при работе в частной сети

1. Запустите ваш репозиторий в контейнере:

docker run -d -p 5000:5000 --name my-registry-container my-registry:2

2. Перейдите в директорию, содержащую файлы смарт-контракта и сценарный файл Dockerfile с командами для сборки образа.

3. Соберите образ вашего смарт-контракта:

docker build -t my-contract .

4. Укажите имя образа и адрес его размещения в репозитории:

docker image tag my-contract my-registry:5000/my-contract

5. Запустите созданный вами контейнер репозитория:

docker start my-registry-container

6. Загрузите ваш смарт-контракт в репозиторий:

docker push my-registry:5000/my-contract

7. Получите информацию о смарт-контракте. Для этого выведите информацию о контейнере:

docker image ls|grep 'my-node:5000/my-contract'

Таким образом вы получите идентификатор контейнера. Выведите информацию о нем при помощи команды docker inspect:

docker inspect my-contract-id

Пример ответа:

{
"Id": "sha256:57c2c2d2643da042ef8dd80010632ffdd11e3d2e3f85c20c31dce838073614dd",
"RepoTags": [
    "wenode:latest"
],
"RepoDigests": [],
"Parent": "sha256:d91d2307057bf3bb5bd9d364f16cd3d7eda3b58edf2686e1944bcc7133f07913",
"Comment": "",
"Created": "2019-10-25T14:15:03.856072509Z",
"Container": "",
"ContainerConfig": {
    "Hostname": "",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,

Поле Id – это идентификатор Docker-образа смарт-контракта, который вводится в поле ImageHash транзакции 103 при создании смарт-контракта.

Размещение смарт-контракта в блокчейне

После загрузки смарт-контракта в репозиторий опубликуйте его в сети при помощи транзакции 103. CreateContract.

Для этого подпишите транзакцию посредством клиента блокчейн-платформы, метода sign REST API или метода JavaScript SDK.

Данные, возвращенные в ответе метода, подаются на вход при публикации транзакции 103.

Ниже приведены примеры подписания и отправки транзакции при помощи методов sign и broadcast. В примерах транзакции подписываются ключом, сохраненным в keystore ноды.

Curl-запрос на подписание транзакции 103:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '    { \
      "fee": 100000000, \
      "image": "my-contract:latest", \
      "imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
      "contractName": "my-contract", \
      "sender": "3PudkbvjV1nPj1TkuuRahh4sGdgfr4YAUV2", \
      "password": "", \
      "params": [], \
      "type": 103, \
      "version": 1 \
  }' 'http://my-node:6862/transactions/sign'

Ответ метода sign, который передается методу broadcast:

{
  "type": 103,
  "id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky",
  "sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew",
  "senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M",
  "fee": 100000000,
  "timestamp": 1550591678479,
  "proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ],
  "version": 1,
  "image": "my-contract:latest",
  "imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65",
  "contractName": "my-contract",
  "params": [],
  "height": 1619
  }

Curl-запрос на отправку транзакции 103:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
{
    "type": 103, \
    "id": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky", \
    "sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew", \
    "senderPublicKey": "3kW7vy6nPC59BXM67n5N56rhhAv38Dws5skqDsjMVT2M", \
    "fee": 500000, \
    "timestamp": 1550591678479, \
    "proofs": [ "yecRFZm9iBLyDy93bDVaNo1PR5Qkkic7196GAgUt9TNH1cnQphq4yGQQ8Fxj4BYA4TaqYVw5qxtWzGMPQyVeKYv" ], \
    "version": 1, \
    "image": "my-contract:latest", \
    "imageHash": "7d3b915c82930dd79591aab040657338f64e5d8b842abe2d73d5c8f828584b65", \
    "contractName": "my-contract", \
    "params": [], \
    "height": 1619 \
}
}' 'http://my-node:6862/transactions/broadcast'

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

Примечание

Если в дальнейшем код смарт-контракта будет обновлён, то контракт необходимо будет опубликовать заново. Для этого используйте транзакцию 107. UpdateContract Transaction.

Важно

Смарт-контракт не помещается в блокчейн; в блокчейн попадает транзакция, в теле которой зафиксирован хэш Docker-образа, в который упакован код смарт-контракта. Таким образом хэш Docker образа смарт-контракта оказывается на всех нодах блокчейна, но сам смарт-контракт находится в репозитории Docker registry вне блокчейн сети.

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

После размещения смарт-контракта в блокчейне он может быть вызван при помощи транзакции 104 CallContract Transaction.

Эта транзакция также может быть подписана и отправлена в блокчейн посредством клиента блокчейн-платформы, метода sign REST API или метода JavaScript SDK. При подписании транзакции 104 в поле contractId укажите идентификатор транзакции 103 для вызываемого смарт-контракта (поле id ответа метода sign).

Примеры подписания и отправки транзакции при помощи методов sign и broadcast с использованием ключа, сохраненного в keystore ноды:

Curl-запрос на подписание транзакции 104:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"contractId": "ULcq9R7PvUB2yPMrmBdxoTi3bcRmQPT3JDLLLZVj4Ky", \
"fee": 10, \
"sender": "3N3YTj1tNwn8XUJ8ptGKbPuEFNa9GFnhqew", \
"password": "", \
"type": 104, \
"version": 1, \
"params": [ \
    { \
        "type": "integer", \
        "key": "a", \
        "value": 1 \
    } \
] \
}' 'http://my-node:6862/transactions/sign'

Ответ метода sign, который передается методу broadcast:

{
"type": 104,
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP",
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58",
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq",
"fee": 10,
"timestamp": 1549365736923,
"proofs": [
    "2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v"
],
"version": 1,
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2",
"params": [
    {
    "key": "a",
    "type": "integer",
    "value": 1
    }
    ]
  }

Curl-запрос на отправку транзакции 104:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'X-Contract-Api-Token' -d '{ \
"type": 104, \
"id": "9fBrL2n5TN473g1gNfoZqaAqAsAJCuHRHYxZpLexL3VP", \
"sender": "3PKyW5FSn4fmdrLcUnDMRHVyoDBxybRgP58", \
"senderPublicKey": "2YvzcVLrqLCqouVrFZynjfotEuPNV9GrdauNpgdWXLsq", \
"fee": 10, \
"timestamp": 1549365736923, \
"proofs": [ \
    "2q4cTBhDkEDkFxr7iYaHPAv1dzaKo5rDaTxPF5VHryyYTXxTPvN9Wb3YrsDYixKiUPXBnAyXzEcnKPFRCW9xVp4v" \
], \
"version": 1, \
"contractId": "2sqPS2VAKmK77FoNakw1VtDTCbDSa7nqh5wTXvJeYGo2", \
"params": [ \
    { \
    "key": "a", \
    "type": "integer", \
    "value": 1 \
    } \
] \
}' 'http://my-node:6862/transactions/broadcast'

Разработка и применение WASM смарт-контрактов

В этом разделе приведен пример разработки WASM смарт-контракта при помощи Rust CDK. Rust CDK – это набор библиотек и утилит, которые представляют собой eDSL для написания смарт-контрактов на языке Rust.

Подготовка к работе

Для начала работы необходимо, чтобы в вашей системе были установлены Rust и Cargo.

Установка cargo-we

Чтобы установить cargo-we, выполните команду:

cargo install --git https://github.com/waves-enterprise/we-cdk.git --force

Используйте –force для установки последней версии утилиты.

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

Для создания проекта используйте команду cargo we new <NAME>, например:

cargo we new flipper

Эта команда создаст папку flipper в вашей рабочей директории. В папке будут созданы файлы:

  • Cargo.toml – файл, содержащий метаданные проекта, необходимые для сборки;

  • lib.rs – файл исходного кода контракта;

  • .gitignore – файл игнорирования файлов для git.

В файле lib.rs будет создан пример контракта – Flipper.

Сборка проекта

Чтобы собрать проект, выполните команду:

cargo we build

Пример WASM смарт-контракта – Flipper

Flipper – это простой смарт-контракт, содержащий только одно значение bool. Контракт предоставляет метод, изменяющий его значение с true на false и наоборот. Ниже приведён код контракта с использованием CDK.

use we_cdk::*;

// Объявление функции, доступной для вызова.
// Для этого используется ключевое слово - #[action].
// _constructor - обязательный метод, который вызывается при CreateContract Transaction.
#[action]
fn _constructor(init_value: Boolean) {
    // Данная функция устанавливает значение, полученное аргументом функции, по ключу "value".
    set_storage!(boolean :: "value" => init_value);
}

#[action]
fn flip() {
    // Читаем значение по ключу.
    let value: Boolean = get_storage!(boolean :: "value");
    // Записываем значение обратное полученному.
    set_storage!(boolean :: "value" => !value);
}

Основы CDK

Типы

В CDK используются типы, аналогичные типам, доступным для хранения в состоянии контракта:

  • Integer

  • Boolean

  • Binary

  • String

Вызываемые функции

Для того чтобы сделать функцию доступной для вызова извне, необходимо указать атрибут action:

#[action]
fn flip() {
...

Вызываемые функции не должны возвращать значений. По умолчанию все функции не доступны извне.

Конструктор контракта

Любой контракт должен иметь функцию-конструктор контракта. Данная функция вызывается в CreateContract Transaction. Функция должна иметь имя _constructor.

#[action]
fn _constructor() {
...

Данный метод используется для инициализации контракта при его размещении в сети. Чаще всего – для установки стартовых значений, ролей и так далее.

Функция также должна быть отмечена атрибутом action. Наличие аргументов или их отсутствие зависит от логики вашего конструктора.

Доступные функции

Наиболее актуальный список доступных функций вы найдёте по адресу https://docs.rs/we-cdk/latest/we_cdk/.

Основные компоненты we-cdk

crates/cargo-we

Утилита предназначена для работы со смарт-контрактами: создание и сборка проекта, утилиты для WASM и WAT.

crates/cdk

Rust библиотека для написания WASM смарт-контрактов.

crates/codegen

WEVM bindings и алгоритмы для промежуточного представления.

crates/proc-macro

Процедурные макросы для генерации кода для контрактов WASM.

examples

Примеры контрактов.

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