Розповідаємо про смарт-контракти Tezos: якою мовою програмування їх пишуть і як їх виконує віртуальна машина.
В уроці майже немає інформації про синтаксис: на цьому етапі важливіше зрозуміти, як працює віртуальна машина блокчейна.
Що таке смарт-контракти
Смарт-контракт (Smart contract) — набір функцій і даних, який зберігається в блокчейні. Смарт-контракти виконують різні завдання — від продажу токенів до управління децентралізованими організаціями. Наприклад:
- вестинг-контракти відправляють користувачам токени в зазначений час. Ранні інвестори Tezos отримують кошти з контракту KT1KCYaULopm8i2CBbGF5EHXeXLUr4R6r2dA;
- оракули опитують джерела даних і повертають усереднені ціни активів або іншу інформацію. Так працює оракул Harbringer, який отримує ціни криптовалют з торгових майданчиків;
- алгоритмічні стейблкоїни випускають токени і змінюють вартість їх випуску залежно від курсу нативного токена. Смарт-контракт Kolibri коригує ціну випуску kUSD залежно від ціни tez.
Смарт-контракти виконує віртуальна машина (Virtual Machine або VM). Вона використовує обчислювальну потужність блокчейна: смарт-контракти виконують усі вузли мережі, але тільки найшвидший записує результат у блок.
Часті виклики смарт-контрактів можуть паралізувати блокчейн. Щоб цього уникнути, розробники протоколів обмежують максимальний розмір смарт-контрактів за обсягом коду і розміром комісій.
Як працюють віртуальні машини блокчейнів
Віртуальні машини "розуміють" низькорівневі мови програмування — байт-код. Вони інтерпретують такий код швидше, ніж команди мовами на кшталт JavaScript або Python. Це можна показати на прикладі однієї фрази, записаної різними словами:
- низькорівнева мова — 2 + 2 = 4;
- високорівнева мова №1 — два плюс два дорівнює чотири;
- високорівнева мова №3 — Якщо Скласти два І два, Отримаємо чотири
- високорівнева мова №2 — у результаті підсумовування двійки і двійки виходить чотири;
Під час читання ми автоматично переводимо останні рядки в найзручніший для розуміння варіант — 2 + 2 = 4. У програмуванні так само: компілятор переводить високорівневу мову в байт-код.
Віртуальна машина Tezos працює з байт-кодом Michelson. Це низькорівнева мова програмування з прямим доступом до стека — структури даних зі швидким доступом до інформації. Код на Michelson має такий вигляд:
parameter unit ; одиниця зберігання ; код CAR ; PUSH int 3 ; PUSH int 3 ; IFCMPEQ DROP > DROP > ; UNIT; операція NIL;PAIR > //Функція if. Оператори PUSH двічі додають число 3 у стек. Оператор IFCMPEQ порівнює два перші елементи в стеку, а потім виконує команди < DROP >і виводить результат операції.
Досвідчені блокчейн-розробники найчастіше пишуть смарт-контракти на Michelson. Новачкам краще використовувати високорівневі мови програмування з набором бібліотек і людиночитабельним синтаксисом.
Мови програмування смарт-контрактів на Tezos: SmartPy, LIGO і Lorentz
Учасники екосистеми Tezos розробили кілька високорівневих мов програмування. Найпопулярніші з них:
- SmartPy — об’єктно-орієнтована мова на основі Python. Підтримує онлайн-компілятор SmartPy.io, в якому можна розробляти, тестувати і публікувати смарт-контракти.
- LIGO — імперативна мова програмування з простою системою типів та онлайн-компілятором ide.ligolang.org. Існує кілька діалектів із синтаксисом поширених мов: PascaLIGO (Pascal), CameLIGO (OCaml), ReasonLIGO (ReasonML) і jsLIGO (JavaScript).
- Lorentz — вбудована предметно-орієнтована мова на базі Haskell. За допомогою Lorentz розробники можуть безпосередньо працювати зі стеком Michelson.
У цьому курсі ми будемо використовувати мову з найпростішим синтаксисом — PasaLIGO.
Простий смарт-контракт з однією точкою входу
Основа смарт-контракту на LIGO — точка входу (entry point). Це головна функція смарт-контракту (main function), яка приймає вхідну транзакцію і викликає інші функції.
function ім'я_функції (const параметр_вхідної_транзакції : тип; const сховище : тип) : тип_результату is результат
Вхідні параметри main function:
- параметр вхідної транзакції . Функція використовує його під час виконання коду;
- сховище [storage] . Інформація, яку потрібно зберігати в блокчейні до наступного виконання смарт-контракту, наприклад кількість токенів або записи про користувачів. Розробник задає значення storage під час розгортання смарт-контракту. Надалі вміст сховища може змінювати тільки смарт-контракт.
Вихідні параметри main function:
- список виконаних операцій, наприклад деталі транзакцій, які відправив смарт-контракт;
- результати виконання функцій, які прописав розробник.
Приклад смарт-контракту з функцією-інкрементом, яка приймає число і збільшує його на 1:
function main (const number : int; const сховище : int) : список (операція) * int is ((нуль : список (операція)), number + 1)
- function main (const number : int; const storage : int) — оголошуємо main function із вхідними параметрами number і storage. Коли користувач надсилає контракту число, головна функція сприйме його як number і виконує код;
- : list (operation) * int — визначаємо тип результату, який повертає функція. У цьому випадку — пара зі списку виконаних операцій і число-результат;
- is ((nil : list (operation)), number + 1) — визначаємо результат виконання функції:
- nil : list (operation) — повертає порожній список;
- , — слугує роздільником для значень типу tuple;
- number + 1 — однорядкова функція, збільшує число на 1.
Смарт-контракт можна модифікувати: реалізувати декремент, піднесення до квадрата або іншу математичну операцію. У нього одна точка входу, яка активує єдиний ланцюжок функцій. Такий смарт-контракт не може виконати кілька завдань, наприклад прийняти депозит, повернути баланс і відправити транзакцію.
Смарт-контракт із кількома точками входу
Віртуальна машина починає виконання смарт-контракту з main function . Вона може виконати тільки ті функції, які викликає точка входу.
Розробники збільшують кількість операцій, створюючи псевдо-точки входу всередині головної функції. Для цього потрібно:
- Оголосити псевдо-точки входу і тип параметра, з яким вони працюватимуть.
- Описати функції, які викликатиме кожна псевдо-точка.
- Використовувати оператор case у головній функції. Він вкаже віртуальній машині, яку функцію викликати в разі звернення до псевдо-точки входу.
Приклад смарт-контракту з псевдо-точками — калькулятор, який приймає назву математичної операції та два числа, а повертає результат обчислень. Смарт-контракт не записує значення в storage, тому що нам для виконання математичних операцій не потрібно поміщати дані в блокчейн.
//оголошення типу numbers, який містить пару з двох чисел (tuple) type numbers is (int * int) //оголошення типів action, які містять пари чисел type action is | Addition of числа | Віднімання of числа | Множення of числа | Ділення of числа //оголошення типу даних у сховищі смарт-контракту type storage is int //оголошення математичних функцій. //(const a : int ; const b : int) - параметри функції //: int - тип результату функції //is a + b - результат виконання функції function add (const a : int ; const b : int) : int is a + b function відняти (const a : int ; const b : int) : int is a - b function множити (const a : int ; const b : int) : int is a * b function розділити (const a : int ; const b : int) : int is a / b //оголошення головної функції //призначаємо першому параметру тип action, параметру store - тип storage //функція повертає дані типів list(operation) і int - пару зі списку і числа //після is йде результат виконання функції: //1) порожній список nil : list(operation). //2) const result : int = - запис результату виконання функції в константу result. //2) case parameter of - результат виконання об'єкта типу action, //чия назва збігається з параметром вхідної транзакції. function main (const параметр : action ; const сховище : storage) : (список(операція) * int) is блок const результат : int = case параметр of | Додавання(n1, n2) -> add(n1, n2) | Віднімання(n1, n2) -> відняти(n1, n2) | Множення(n1, n2) -> множити(n1, n2) | Ділення(n1, n2) -> розділити(n1, n2) end; //виведення результату виконання головної функції: порожній список операцій і значення result > with ((нуль : список(операція)), result)
Якщо надіслати смарт-контракту запит із параметром Multiplication(3, 9) , він поверне число 27. Віртуальна машина виконає контракт так:
- Зіставить параметр Multiplication з варіантами псевдо-точок під оператором case .
- Перейде до функції multiply , яку розробник описав на початку контракту.
- Підставить замість констант a і b параметри запиту — 3 і 9.
- Виконає функцію multiply і запише її в store .
- Завершить виконання головної функції — поверне порожній список і нове значення store .
Для перевірки смарт-контракту скопіюйте код калькулятора в редактор на ide.ligolang.org. Виберіть значення Dry Run зі списку, що випадає, на панелі Configure . У полі Parameters введіть назву математичної операції та два параметри, а потім натисніть кнопку Run .
Підбиваємо підсумки
Смарт-контракт — це код у блокчейні. Віртуальна машина виконує його, коли отримує транзакцію з потрібними параметрами.
Розробники пишуть смарт-контракти високорівневими мовами програмування із синтаксисом Python, Pascal, JS або Haskel. Досвідчені розробники часто пишуть байт-код на Michelson.
Смарт-контракти мовою PascalLIGO складаються зі змінних і функцій. Віртуальна машина виконує контракт починаючи з точки входу — головної функції main. У неї можна вставити псевдо-точки входу — додаткові функції.
Смарт-контракт завжди повертає результат виконання: список операцій і значення сховища storage .
- Автор — Павло Скоропляс
- Продюсер — Світлана Коваль
- Стилі — Дмитро Бойко
- Ілюстрації — Кшиштоф Шпак
- Верстка — Зара Аракелян
- Розробка — Олександр Пупко
- Керівник — Влад Ліхута