Среда разработки и базовые типы данных LIGO

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

Программисты пишут код в средах разработки — текстовых редакторах с поддержкой плагинов и библиотек. На этом уроке мы установим среду разработки VS Code и расскажем о базовых типах данных LIGO.

Установка и настройка среды разработки

Мы будем использовать редактор Visual Studio Code (VS Code) с плагином pascaligo-vscode.

Порядок установки среды разработки:

  1. Скачайте редактор с официального сайта Visual Studio Code.
  2. Установите приложение и загрузите плагин pascaligo-vscode.
  3. Установите Docker. Он нужен для запуска библиотеки LIGO в виртуальной машине. Так компьютер сможет компилировать и исполнять код на PascalLIGO.
  4. Запустите Docker и дождитесь, пока в левом нижнем углу приложения появится зеленая полоска.

1

Во время установки могут возникнуть такие проблемы:

  • при запуске Docker выдает ошибку «This computer doesn’t have VT-X/AMD-v enabled». Загрузите BIOS. Включите виртуализацию VT-X или AMD-V;
  • после запуска Docker компьютер медленно работает. Откройте настройки программы, перейдите во вкладку Resources и уменьшите ресурсы компьютера, выделенные для Docker. Нажмите Apply and restart в нижнем правом углу, чтобы применить изменения.

2

Установите контейнер LIGO. Для этого откройте терминал и выполните команду:

  • для MacOS и Linux:
    docker run --rm -v "$PWD":"$PWD" -w "$PWD" ligolang/ligo:0.19.0
  • для Windows:
    docker run --rm -v "%CD%":/cd -w /cd ligolang/ligo:0.19.0

Docker загрузит контейнер и выдаст встроенную справку LIGO.

3

Проверьте, все ли работает. Выполните в терминале команду

ligo --version

Если терминал выдает ошибку «ligo: command not found», значит Docker преждевременно закрывает контейнер LIGO. Решение проблемы:

  1. Откройте VS Code и создайте файл с названием ligo.

    4

  2. Вставьте в него следующий код:

    #!/bin/sh
    docker run --user=`id -u` -v $PWD:$PWD -w $PWD ligolang/ligo:next "$@"

    5

  3. Сохраните документ и откройте терминал. С помощью команды cd перейдите в папку с файлом ligo:

    cd ~/Documents
  4. Если у вас Linux или Mac OS, включите режим суперпользователя

    sudo -s
  5. В терминале выполните команды:

    chmod +x ligo
    ./ligo

    Они привяжут запуск контейнера LIGO к команде ligo.

    6

  6. Сделайте файл исполняемым на уровне системы. В Linux и Mac OS откройте проводник, и перенесите файл ligo в папку /usr/local/bin на системном диске. В Windows откройте терминал и выполните команду:

    $env:path += ";c:\[путь к папке с файлом ligo]"
  7. Проверьте работу LIGO: снова введите в терминале команду ligo --version.

    На черном фоне — ошибка при выполнении команды ligo --version до переноса файла ligo в папку bin. На синем фоне — успешное выполнение команды после переноса файла.

    На черном фоне — ошибка при выполнении команды ligo --version до переноса файла ligo в папку bin. На синем фоне — успешное выполнение команды после переноса файла.

Компиляция пробного контракта

Откройте VS Code и создайте новый проект:

  1. Нажмите на кнопку «Open...».
  2. Выберите папку, в которой сохраните тестовый проект LIGO.
  3. Создайте в ней папку LIGO_test.
  4. Откройте новую папку.

Слева отображаются папки и файлы текущего проекта. Создайте в этом списке пустой файл с названием test.ligo. По расширению .ligo VS Code поймет, что нужно задействовать плагин для подсветки синтаксиса и использовать команды PascaLIGO.

8

Вставьте в файл test.ligo функцию из первого урока.

function main (const number : int; const storage : int) : list (operation) * int is ((nil : list (operation)), number + 1)

Попробуйте скомпилировать этот контракт. Откройте терминал VS Code.

9

Введите в нем следующую команду и нажмите Enter:

ligo compile-contract test.ligo main

Если у вас Linux или Mac OS, терминал может выдать ошибку «root/ligo». В таком случае сначала выполните команду sudo -s, чтобы дать терминалу право исполнить скрипт ligo в папке bin.

VS Code переведет код PascaLIGO из test.ligo в код Michelson и проверит, можно ли исполнить функцию main.

10

Теперь проверьте работу смарт-контракта с помощью команды dry-run (репетиции). Она выполнит контракт с заданными параметрами внутри виртуальной машины Docker.

Для «репетиции» тестового контракта введите в терминале команду:

ligo dry-run test.ligo main 2 0
  • main — название точки входа;
  • 2 — первый входящий параметр;
  • 0 — значение хранилища.

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

11

При исполнении dry-run компилятор выдаст результат исполнения функции main с заданными параметрами.

Константы, переменные и базовые типы данных LIGO

Для хранения информации LIGO использует:

  • const (константы). Смарт-контракт не может изменить значение константы во время исполнения функций. Чаще всего в них записывают параметры, которые поступают на вход главной функции;
  • var (переменные). Смарт-контракт может изменить значение переменной в любой момент. Обычно переменные используют в loop-функциях, которые контракт вызывает несколько раз.

Константам и переменным нужно присвоить тип данных, чтобы компилятор понимал, как с ними работать. LIGO использует такие базовые типы данных:

  • int — неограниченное по размеру целое число, может быть отрицательным;
  • nat — неограниченное по размеру натуральное число, не может быть отрицательным;
  • string — строка, содержит печатные символы: латинские буквы, цифры, знаки препинания и некоторые спецсимволы вроде | и ~;
  • bool — логическая переменная true или false.

Пример объявления констант и переменных:

// const [название] : [тип данных] = [значение]
// var [название] : [тип данных] := [значение]

const a : int = 50
const b : nat = 3n
var c : string := “Example”
var d : bool := True

Иногда константе или переменной нельзя присвоить значение до исполнения смарт-контракта. Пример: контракт получает входящий параметр, а затем использует его для расчетов. В таком случае нужно объявить пустую константу или переменную и присвоить ей значение позже.

В примере ниже мы объявили константу number типа int. Во время исполнения кода смарт-контракт присвоит ей значение, которое отправит пользователь.

function main (const number : int; const storage : int) : list (operation) * int is ((nil : list (operation)), number + 1)

Объявлять константы можно в любом месте смарт-контракта, а переменные — только внутри функций. Например, мы можем вынести число 1 из главной функции в примере выше. Для этого нужно объявить константу add в начале контракта, присвоить ей значение 1 и заменить единицу на add.

const add : int = 1

function main (const number : int; const storage : int) :
list (operation) * int is ((nil : list (operation)), number + add)

Но если сделать add переменной, при попытке исполнить код компилятор выдаст ошибку «ILL FORMED CONTRACT» — неправильно сформированный контракт.

12

Продвинутые типы и псевдонимы

Кроме базовых типов данных LIGO поддерживает несколько продвинутых типов:

  • структуры данных — tuple, record и map для передачи нескольких значений в одном параметре;
  • специальные типы — адреса, балансы tez, варианты, timestamp.

Разработчики создают такие типы с помощью псевдонимов (alias). Псевдонимы также упрощают чтение кода и позволяют сортировать параметры по способам применения.

Пример объявления псевдонимов:

//объявляем псевдоним типа int
type numbers is int
//объявляем константу типа numbers и присваиваем ей значение
const a : numbers = -5

//объявляем константу admin типа user_id
type user_id is nat
const admin : user_id = 1n

Часто используемые структуры данных

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

tuple или «кортеж» хранит два и более значений заданного типа. В примере ниже мы задаем тип cube, в который можно записать длину сторон куба.

//объявляем псевдоним cube, чтобы хранить в нем длину трех граней куба.
type storage is int

type cube is int * int * int

//объявляем в функции входящий параметр — константу side типа cube
function main (const side : cube ; const _store : storage) :
  (list(operation) * int) is block {

//объявляем константу result и присваиваем ей значение: умножение трех значений, которые хранятся в кортеже side
    const result : int = side.0 * side.1 * side.2
    } with ((nil : list(operation)), result)

record хранит несколько констант или переменных разных типов. Например, в константу типа user можно записать аргументы: id типа int, is_admin типа bool и name типа string:

//объявляем тип user
type user is
  record [
    id       : nat;
    is_admin : bool;
    name     : string
  ]

//объявляем константу alice и присваиваем ей значения типа user
const alice : user =
  record [
    id       = 1n;
    is_admin = True;
    name     = "Alice"
  ]

map связывает данные в набор пар «ключ–значение». Также в LIGO есть тип данных big_map, который оптимизирует загрузку большого количества «ключей-значений» и экономит газ, но не поддерживает итерацию.

//объявление псевдоним dims типа tuple, затем — cube_dimensions типа map
type dims is int * int * int
type cube_dimension is map (string, dims)

//объявление map cube, которая содержит два значения: название куба и значения длины его граней
const cubes : cube_dimension =
    map [
    "big cube" -> (123243, 1251, 823);
    "small cube" -> (3, 3, 7)
    ]
//записываем в константу big типа dims. Для этого получаем значения из map по обращению к ключу “big cube”
const big : option(dims) = cubes["big cube"]

unit — тип данных без значения. Он нужен, когда по правилам синтаксиса функция должна принять параметр, но этот параметр не важен для работы кода. Мы используем unit для объявления типов Labeouf, Nike и Yoda в примере ниже.

variant позволяет использовать переменные нескольких типов в зависимости от ситуации и логики смарт-контракта. В примере объявляем variant для создания псевдо-точек входа.

//объявляем псевдоним speach и записываем несколько вариантов. Для каждого варианта указываем тип параметра, который он будет принимать. В нашем случае главная функция ничего не делает с принятым параметром, поэтому для вариантов указываем пустой тип unit
type speach is
    Labeouf of unit
   |Nike of unit
   |Yoda of unit

//мы хотим, чтобы контракт возвращал строку, поэтому в типе return указываем, что он возвращает список операций и строку
type return is (list(operation) * string)

//объявляем, что функция main должна принять от пользователя параметр и записать его в константу word типа speach
function main (const word : speach; const _store : string) : return is
 ((nil : list (operation)),
//”case word of” сравнивает принятый параметр с вариантами типа speach. Если вариант совпадет, контракт вернет указанную строку
  case word of
    Labeouf (_n) -> "DO IT!"
  | Nike (_n) -> "Just do it"
  | Yoda (_n) -> "Do it you can"
  end)

Специальные типы данных

Кроме продвинутых структур данных, LIGO использует несколько «блокчейновых» типов данных и функцию failwith.

tez — обозначает количество токенов XTZ (tez). При объявлении, присвоении или оперировании типом tez нужно добавлять к числу суффикс «tz» или «tez».

В LIGO также используется суффикс «mutez» для обозначения одной миллионной части tez. Для удобства чтения можно разделять большие числа на порядки с помощью нижнего подчеркивания. Например, «1_000_000tez» виртуальная машина Tezos воспримет как 1 млн tez.

//1 000 000 mutez = 1 tez
//присваиваем t значение 1,5003 tez с помощью суффикса mutez
const t : tez = 1_500_300mutez

address — тип данных для хранения адресов Tezos.

const my_account : address =
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)

failwith — функция обработки ошибок и исключений. Например, мы можем применить failwith, чтобы калькулятор не делил числа на 7.

Division(n1, n2) -> if n2 = 7 then failwith("Error: you can not divide by 7!") else divide(n1, n2)

Больше примеров операций с разными типами данных — в документации LIGO.

Подводим итоги

В онлайн-среде разработки нельзя сохранять код, подгружать библиотеки и вызывать другие контракты, поэтому программисты используют Visual Studio Code и контейнер LIGO в Docker.

Базовые типы LIGO — int, nat, string и bool. Специальные структуры для хранения данных:

  • tuple — несколько значений в одной константе или переменной;
  • record — несколько констант с присвоенными значениями в одной константе или переменной;
  • map — несколько констант продвинутых типов с привязкой к константе-ключу;
  • tez — баланс XTZ;
  • address — адрес в блокчейне Tezos;
  • unit — тип данных без значения.
  • Автор — Павел Скоропляс
  • Продюсер — Светлана Коваль
  • Стили — Дмитрий Бойко
  • Иллюстрации — Кшиштоф Шпак
  • Верстка — Зара Аракелян
  • Разработка — Александр Пупко
  • Руководитель — Влад Лихута