Как строить, развертывать и тестировать Waves RIDE dApp
Здравствуйте! В статье я покажу, как написать и запустить на ноде Waves обычный dApp. Рассмотрим необходимые инструменты, методы и пример разработки.
Схема разработки dApps и обычных приложений почти не отличается:
Пишем код
Пишем автоматизированное тестирование
Запускаем приложение
Тестируем
Инструменты
1. docker для запуска ноды и Waves Explorer
Если не хотите запускать ноду, можете пропустить этот шаг. Ведь есть тестовая и экспериментальная сеть. Но без развертывания своей ноды процесс тестирования может затянуться.
Вам постоянно будут нужны новые аккаунты с тестовыми токенами. Кран тестовой сети переводит по 10 WAVES каждые 10 минут.
Среднее время блоков в тестовой сети – 1 минута, в ноде – 15 секунд. Это особенно ощутимо, когда транзакция требует нескольких подтверждений.
В общедоступных тестовых нодах возможно агрессивное кэширование.
Еще они могут быть временно недоступны из-за технического обслуживания.
Далее буду считать, что вы работаете со своей нодой.
Установите Surfboard – инструмент, который позволит запустить тесты на существующей ноде.
npm install -g @waves/surfboard
3. Плагин Visual Studio Code
Шаг необязателен, если вы – не фанат IDE и предпочитаете текстовые редакторы. Все необходимые инструменты – это утилиты командной строки. Если используете vim, обратите внимание на плагин vim-ride.
Скачайте и установите Visual Studio Code: https://code.visualstudio.com/
docker run -d -e API_NODE_URL=http://localhost:6869 -e NODE_LIST=http://localhost:6869 -p 3000:8080 wavesplatform/explorer
Откройте браузер и перейдите в http://localhost:3000. Увидите, как быстро строится пустая цепь локальной ноды.
Waves Explorer отображает экземпляр локальной ноды
Структура RIDE и инструмент Surfboard
Создайте пустую директорию и выполните в ней команду
surfboard init
Команда инициализирует директорию со структурой проекта, приложения типа «hello world» и тесты. Если откроете эту папку с VS Code, то увидите:
Surfboard.config.json
Под папкой ./ride/ найдете одиночный файл wallet.ride – директорию, где расположен код dApp. Мы кратко проанализируем dApp в следующем блоке.
Под папкой ./test/ найдете файл *.js. Здесь хранятся тесты.
./surfboard.config.json – конфигурационный файл для запуска тестов.
Envs – важная секция. Каждая среда настраивается так:
Конечная точка REST API ноды, которая будет использоваться для запуска dApp и CHAIN_ID сети.
Секретная фраза для аккаунта с токенами, которые будут источниками токенов вашего теста.
Как видите, surfboard.config.json по умолчанию поддерживает несколько сред. По умолчанию выставлена локальная среда (ключ defaultEnv – изменяемый параметр).
Приложение Wallet-demo
Эта секция – не справочник по языку RIDE. Скорее, взгляд на приложение, которые разворачиваем и тестируем, чтобы лучше понять, что происходит в блокчейне.
Рассмотрим простое приложение Wallet-demo. Каждый может отправить на адрес dApp токены. Вывести можно только свои WAVES. Две @Callable функции доступны через InvokeScriptTransaction:
deposit(), которое требует прикрепленного платежа в WAVES
withdraw(amount: Int), который возвращает токены
На протяжении всего жизненного цикла dApp, структура (address → amount) будет поддерживаться:
Action
Resulting state
initial
empty
Alice deposits 5 WAVES
alice-address → 500000000
Bob deposits 2 WAVES
alice-address → 500000000
bob-address → 200000000
Bob withdraws 7 WAVES
DENIED!
Alice withdraws 4 WAVES
alice-address → 100000000
bob-address → 200000000
Вот код для полного понимания ситуации:
# In this example multiple accounts can deposit their funds and safely take them back. No one can interfere with this.
# An inner state is maintained as mapping `address=>waves`.
{-# STDLIB_VERSION 3 #-}
{-# CONTENT_TYPE DAPP #-}
{-# SCRIPT_TYPE ACCOUNT #-}
@Callable(i)
func deposit() = {
let pmt = extract(i.payment)
if (isDefined(pmt.assetId))
then throw("works with waves only")
else {
let currentKey = toBase58String(i.caller.bytes)
let currentAmount = match getInteger(this, currentKey) {
case a:Int => a
case _ => 0
}
let newAmount = currentAmount + pmt.amount
WriteSet([DataEntry(currentKey, newAmount)])
}
}
@Callable(i)
func withdraw(amount: Int) = {
let currentKey = toBase58String(i.caller.bytes)
let currentAmount = match getInteger(this, currentKey) {
case a:Int => a
case _ => 0
}
let newAmount = currentAmount - amount
if (amount < 0)
then throw("Can't withdraw negative amount")
else if (newAmount < 0)
then throw("Not enough balance")
else ScriptResult(
WriteSet([DataEntry(currentKey, newAmount)]),
TransferSet([ScriptTransfer(i.caller, amount, unit)])
)
}
@Verifier(tx)
func verify() = false
Плагин VSCode поддерживает непрерывную компиляцию при редактировании файла. Поэтому вы всегда можете следить за ошибками во вкладке PROBLEMS.
Если хотите использовать другой текстовый редактор при компиляции файла, используйте
surfboard compile ride/wallet.ride
Это выведет ряд base64 скомпилированного кода RIDE.
Тестовый сценарий для ‘wallet.ride’
Посмотрим на тестовый файл. Работает на фреймворке JavaScript’s Mocha. Есть функция «Before» и три теста:
«Before» финансирует несколько аккаунтов через MassTransferTransaction, компилирует скрипт и развертывает его в блокчейне.
«Can deposit» отправляет в сеть InvokeScriptTransaction, активируя функцию deposit() для каждого из двух аккаунтов.
«Can’t withdraw more than was deposited» тестирует, что никто не сможет украсть чужие токены.
«Can deposit» проверяет, что выводы обрабатываются корректно.
Запуск тестов с Surfboard и анализ результатов в Waves Explorer
Для запуска теста выполните
surfboard test
Если у вас есть несколько сценариев (например, нужен отдельный скрипт развертывания), можете запустить
surfboard test my-scenario.js
Surfboard соберет тестовые файлы в папке ./test/ и запустит сценарий в ноде, которая настроена в surfboard.config.json. Спустя несколько секунд вы будете наблюдать что-то подобное:
wallet test suite
Generating accounts with nonce: ce8d86ee
Account generated: foofoofoofoofoofoofoofoofoofoofoo#ce8d86ee - 3M763WgwDhmry95XzafZedf7WoBf5ixMwhX
Account generated: barbarbarbarbarbarbarbarbarbar#ce8d86ee - 3MAi9KhwnaAk5HSHmYPjLRdpCAnsSFpoY2v
Account generated: wallet#ce8d86ee - 3M5r6XYMZPUsRhxbwYf1ypaTB6MNs2Yo1Gb
Accounts successfully funded
Script has been set
√ Can deposit (4385ms)
√ Cannot withdraw more than was deposited
√ Can withdraw (108ms)
3 passing (15s)
Ура! Тесты пройдены. Теперь взглянем на то, что происходит при использовании Waves Explorer: просмотрим блоки или вставим один из указанных выше адресов в поиск(например, соответствующий wallet#. Там можно найти историю транзакций, состояние dApp, декомпилированный двоичный файл.
Waves Explorer. Приложение, которое только что развернули.
Несколько советов по Surfboard:
1. Чтобы тестировать в среде testnet, используйте: