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

На минулому уроці ми запустили простий смарт-контракт в онлайн-компіляторі LIGO. У ньому не вийде створити великі проекти: там не можна зберегти код, додати сторонню бібліотеку або викликати інший смарт-контракт.

Програмісти пишуть код в середовищах розробки — текстових редакторах з підтримкою плагінів і бібліотек. На цьому уроці ми встановимо середу розробки VS Code і розповімо про базові типи даних LIGO.

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

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

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

  • Завантажте редактор з офіційного сайту Visual Studio Code.
  • Встановіть програму та завантажте плагін pascaligo-vscode.
  • Встановіть Docker. Він потрібен для запуску бібліотеки LIGO у віртуальній машині. Так комп'ютер зможе компілювати і виконувати код на PascalLIGO.
  • Запустіть Docker і дочекайтеся, поки в лівому нижньому кутку додатка з'явиться зелена смужка.

1

Під час установки можуть виникнути такі проблеми:

  • при запуску Docker видає помилку «This computer does not have VT-X/AMD-v enabled». Завантажте BIOS. Увімкніть віртуалізацію VT-X або AMD-V;
  • після запуску Docker комп'ютер повільно працює. Відкрийте настройки програми, перейдіть у вкладку Resources і зменшіть ресурси комп'ютера, виділені для Docker. Натисніть кнопку Apply and restart в нижньому правому куті, щоб застосувати зміни.

Встановіть контейнер 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 cubes, яка містить два значення: назва куба і значення довжини його граней
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
// ми хочемо, щоб контракт повертав рядок (string), тому в типі 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 — тип даних без значення.
  • Автор — Павло Скоропляс
  • Продюсер — Світлана Коваль
  • Стилі — Дмитро Бойко
  • Ілюстрації — Кшиштоф Шпак
  • Верстка — Зара Аракелян
  • Розробка — Олександр Пупко
  • Керівник — Влад Ліхута