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

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

Определение и общее описание работы смарт-контрактов блокчейн-платформы 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. Наличие аргументов или их отсутствие зависит от логики вашего конструктора.

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

crates/cargo-we

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

crates/cdk

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

crates/codegen

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

crates/proc-macro

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

examples

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

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