Починаємо кодити з Aleo (Мова)
Leo Мовний посібник
Statically Typed
Leo є статично типізованою мовою, що означає, що ми повинні знати тип кожної змінної перед виконанням схеми.
Explicit Types Required
У Leo немає значення undefined або null. При призначенні нової змінної тип значення має бути вказано явно.
Pass by Value
Вирази в Leo завжди передаються за значенням, що означає, що їхні значення завжди копіюються, коли вони використовуються як вхідні дані функції або в правих частинах призначень.
Data Types and Values
Booleans
Leo підтримує традиційні булеві значення true або false. Потрібен явний тип bool для логічних значень у операторах.
let b: bool = false;
Integers
Leo підтримує знакові цілі типи i8, i16, i32, i64, i128 і беззнакові цілі типи u8, u16, u32, u64, u128.
let b: u8 = 1u8;
Підкреслення _ можна використовувати для розділення цифр у цілих літералах.
let b: u8 = 1_000_000u64;
Цілі числа з більшою довжиною біта створюють більше обмежень у схемі, що може сповільнити час обчислення.
A Note on Leo Integers
Leo за замовчуванням не використовуватиме цілочисельний тип. Визначення цілого числа має містити явний тип.
Type casting is supported as of Leo v1.8.2
let a: u8 = 2u8; // explicit typelet b: u16 = a as u16; // type castinglet b: u8 = 2; // implicit type -- not supported
Field Elements
Leo підтримує field
тип для елементів базового поля еліптичної кривої. Це цілі числа без знаку, менші за модуль основного поля. Нижче наведені найменший і найбільший елементи поля.
let a: field = 0field;let b: field = 8444461749428370424248824938781546531375899335154063827935233455917409239040field;
Group Elements
Множина афінних точок на еліптичній кривій утворює групу. Крива є скрученою кривою Едвардса з a = -1 і d = 3021. Leo підтримує підгрупу групи, створену генераторною точкою, як примітивний тип даних. Елемент групи позначається х-координатою його точки; наприклад, 2group означає точку(2,5553594316923449299484601589326170487897520766531075014687114064346375156608)
.
let a: group = 0group; // the point with 0 x-coordinate, (0, 1)let b: group = 1540945439182663264862696551825005342995406165131907382295858612069623286213group; // the generator point
Вищезазначену генераторну точку можна отримати через константу, пов’язану з типом group
.
let g: group = group::GEN; // the group generator
Scalar Elements
Leo підтримує scalar
тип для елементів скалярного поля, визначеного підгрупою еліптичної кривої. Це цілі числа без знака, менші за модуль скалярного поля. Показ найменшого та найбільшого скалярів.
let a: scalar = 0scalar;let b: scalar = 2111115437357092606062206234695386632838870926408408195193685246394721360382scalar;
Addresses
Адреси визначаються, щоб увімкнути оптимізовані компілятором підпрограми для аналізу та роботи над адресами. Ця семантика буде супроводжуватися стандартною бібліотекою в майбутньому спринті.
let receiver: address = aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4;
Signatures
Aleo використовує схему підписів Schnorr для підпису повідомлень закритим ключем Aleo. Підписи в Leo мають власний підпис типу та можуть бути оголошені як літерали sign069ju4e8s66unu25celqycvsv3k9chdyz4n4sy62tx6wxj0u25vqp58hgu9hwyqc63qzxvjwesf2wz0krcvvw9kd9x0rsk4lwqn2acqhp9v0pdkhx6gvkanuuwratqmxa3du7l43c05253hhed9eg6ppzzfnjt06fpzp6msekdjxd36smjltndmxjndvv9x2uecsgngcwsc2qkns4afd
.
Підписи можна перевірити в Leo за допомогою операторів signature::verify
or s.verify
.
program test.aleo { struct foo { a: u8, b: scalar } transition verify_field(s: signature, a: address, v: field) { let first: bool = signature::verify(s, a, v); let second: bool = s.verify(a, v); assert_eq(first, second); } transition verify_foo(s: signature, a: address, v: foo) { let first: bool = signature::verify(s, a, v); let second: bool = s.verify(a, v); assert_eq(first, second); }}
Layout of a Leo Program
Програма Leo містить Program Scope, Constants, Imports , Transition Functions, Helper Functions, Structs , Records, Mappings, та Finalize Functions.Оголошення доступні локально в програмному файлі. Якщо вам потрібна декларація з іншого файлу Leo, ви повинні її імпортувати.
Program Scope
Область дії програми в сенсі Leo — це набір коду (його функцій) і даних (його типів), які знаходяться в program ID в Aleo blockchain.
import foo.leo;program hello.aleo { mapping balances: address => u64; record token { owner: address, amount: u64, } struct message { sender: address, object: u64, } transition mint_public( public receiver: address, public amount: u64, ) -> token { return token { owner: receiver, amount, } then finalize(receiver, amount); } finalize mint_public( public receiver: address, public amount: u64, ) { let current_amount: u64 = Mapping::get_or_use(account, receiver, 0u64); Mapping::set(account, receiver, current_amount + amount); } function compute(a: u64, b: u64) -> u64 { return a + b; }}
У межах програми у файлі Leo має бути оголошено наступне:
Наступне має бути оголошено за межами області дії програми у файлі Leo:
Program ID
ID програми оголошено як {name}.{network}
. Перший символ name
має бути малим. name
може містити малі літери, цифри та підкреслення. Наразі aleo є єдиним підтримуваним доменом мережі.
program hello.aleo; // validprogram Foo.aleo; // invalidprogram baR.aleo; // invalidprogram 0foo.aleo; // invalidprogram 0_foo.aleo; // invalidprogram _foo.aleo; // invalid
Constant
Константа оголошується як const {name}: {type} = {expression};
.
Константи є незмінними і їм потрібно присвоїти значення під час оголошення. Константи можуть бути оголошені в глобальній області програми або в локальній області видимості функції.
program foo.aleo { const FOO: u8 = 1u8; function bar() -> u8 { const BAR: u8 = 2u8; return FOO + BAR; }}
Import
Ви можете імпортувати залежності, завантажені до каталогуimports
.Імпорт оголошується як import {filename}.leo;
Цу буде виглядати як mports/{filename}.leo
та перенесено всі оголошення в поточну область файлу. Якщо є повторювані імена для декларацій, Leo не вдасться скомпілювати. Порядок застосовується для імпорту, який має бути у верхній частині файлу.
CAUTION
Імпорт Leo нестабільний і наразі забезпечує лише мінімальну функціональність. Очікується, що їхній синтаксис зміниться.
import foo.leo; // Import all `foo.leo` declarations into the `hello.aleo` program.program hello.aleo { }
Struct
Тип даних struct оголошується як struct {name} {}
. Тип даних struct оголошується як {name}: {type},
.
struct array3 { a0: u32, a1: u32, a2: u32,}
Record
Тип даних запису оголошено як запис {name} {}
. Записи містять оголошення компонентів {visibility} {name}: {type},
.
Видимість може бути constant
, public
, or private
. Користувачі також можуть опускати видимість, і в цьому випадку Leo за умовчанням буде private
.
Структури даних запису повинні містити компонентowner
як показано нижче. При передачі запису як вхідних даних у програмну функцію також потрібен компонент _nonce: group
(але його не потрібно оголошувати в програмі Leo).
record token { // The token owner. owner: address, // The token amount. amount: u64,}
Array
Leo підтримує статичні масиви. Масиви оголошуються як [type; length]
і може бути вкладеним. Масиви не можуть бути порожніми або зміненими.
Масиви підтримують лише постійні доступи (індекс має бути постійним значенням). Значення засобу доступу має бути постійним цілим числом.
Масиви можуть містити примітивні типи даних, структури або масиви. Структури та записи також можуть містити масиви.
Масиви можна повторювати за допомогою циклу for.
// Initalize a boolean array of length 4let arr: [bool; 4] = [true, false, true, false];// Nested Arraylet nested: [[bool; 2]; 2] = [[true, false], [true, false]];// Array of Structsstruct bar { data: u8,}let arr_of_structs: [bar; 2] = [bar { data: 1u8 }, bar { data: 2u8 }];// Access the field of a struct within an arraytransition foo(a: [bar; 8]) -> u8 { return a[0u8].data;}// Struct that contains an arraystruct bat { data: [u8; 8],}// Record that contains an arrayrecord floo { owner: address, data: [u8; 8],}// Declare a mapping that contains an array valuemapping data: address => [bool; 8];// Iterate over an array using a for loop and sum the values withintransition sum_with_loop(a: [u64; 4]) -> u64 { let sum: u64 = 0u64; for i: u8 in 0u8..4u8 { sum += a[i]; } return sum;}
Tuple
Лев підтримує кортежі. Кортежі оголошуються як (type1, type2, ...)
і можуть бути вкладеними. Кортежі не можуть бути порожніми або зміненими.
Кортежі підтримують лише постійний доступ із крапкою . і постійне ціле число.
Кортежі можуть містити примітивні типи даних, структури або масиви. Структури та записи також можуть містити кортежі.
program test.aleo { transition baz(foo: u8, bar: u8) -> u8 { let a: (u8, u8) = (foo, bar); let result: u8 = a.0 + a.1; return result; }}
Transition Function
Функції переходу в Leo оголошуються як transition {name}() {}
. Функції переходу можна викликати безпосередньо під час виконання програми Leo (через leo run
). Функції переходу містять вирази та оператори, які можуть обчислювати значення. Для виклику функції переходу мають бути в поточній області програми.
program hello.aleo { transition foo( public a: field, b: field, ) -> field { return a + b; }}
Function Inputs
Вхідні дані функції оголошуються як {visibility} {name}: {type},
Вхідні дані функції мають бути оголошені відразу після оголошення назви функції в дужках.
// The transition function `foo` takes a single input `a` with type `field` and visibility `public`.transition foo(public a: field) { }
Function Outputs
Вихід функції обчислюється як return {expression};
. Повернення виведення завершує виконання функції. Тип повернення оголошення функції має відповідати типу повернутого {expression}
.
transition foo(public a: field) -> field { // Returns the addition of the public input a and the value `1field`. return a + 1field;}
Helper Function
Допоміжна функція оголошується як function {name}() {}
. Допоміжні функції містять вирази та оператори, які можуть обчислювати значення, однак допоміжні функції не можуть створюватиrecords
.
Допоміжні функції не можна викликати безпосередньо ззовні. Замість цього вони повинні викликатися іншими функціями. Вхідні дані допоміжних функцій не можуть мати модифікатори {visibility}
Допоміжні функції не можна викликати безпосередньо ззовні. Замість цього вони повинні викликатися іншими функціями. Вхідні дані допоміжних функцій не можуть мати модифікатори.
function foo( a: field, b: field,) -> field { return a + b;}
Inline Function
Вбудована функція оголошується як inline {name}() {}
. Вбудовані функції містять вирази та оператори, які можуть обчислювати значення. Вбудовані функції не можуть бути виконані безпосередньо ззовні, натомість компілятор Leo вбудовує тіло функції в кожне місце виклику.
Вхідні дані вбудованих функцій не можуть мати модифікатори {visibility}
як функції переходу, оскільки вони використовуються лише внутрішньо, а не як частина зовнішнього інтерфейсу програми.
inline foo( a: field, b: field,) -> field { return a + b;}
Правила для функцій (у традиційному розумінні) такі:
- Є три варіанти функцій: перехідна, функціональна, вбудована.
- переходи можуть викликати лише функції та вбудовані рядки.
- функції можуть викликати лише inlines.
- функції можуть викликати лише inlines.
- Прямі/непрямі рекурсивні виклики заборонені
Finalize Function
Функція finalize оголошується як finalize {name}:
і використовується для виконання обчислень у ланцюжку. Однією з його основних цілей є ініціювання або зміна публічного стану в ланцюжку в межах зіставлення. Функція завершення повинна слідувати безпосередньо за функцією переходу та мати таке саме ім’я; він пов’язаний із функцією переходу та виконується в ланцюжку після перевірки підтвердження виконання пов’язаного переходу з нульовими знаннями; функція finalize завершує функцію переходу в ланцюжку. Після успішного завершення функції завершується логіка програми. У разі збою функції фіналізації логіка програми повертається назад.
Отже, вузли в мережі Aleo виконують код функції finalize. Лише код у блоках фіналізації, який виконується вузлами в мережі Aleo, оновлює відображення програм. Лише програма може писати у власне відображення, але всі вузли в мережі Aleo можуть читати публічний стан.
Прикладом мутації стану в ланцюжку є перехід transfer_public_to_private у прикладі finalize, який оновлює відображення публічного облікового запису (і, отже, баланс користувача) під час виклику.
program transfer.aleo { // The function `transfer_public_to_private` turns a specified token amount // from `account` into a token record for the specified receiver. // // This function preserves privacy for the receiver's record, however // it publicly reveals the caller and the specified token amount. transition transfer_public_to_private( public receiver: address, public amount: u64 ) -> token { // Produce a token record for the token receiver. let new: token = token { owner: receiver, amount, }; // Return the receiver's record, then decrement the token amount of the caller publicly. return new then finalize(self.caller, amount); } finalize transfer_public_to_private( public sender: address, public amount: u64 ) { // Decrements `account[sender]` by `amount`. // If `account[sender]` does not exist, it will be created. // If `account[sender] - amount` underflows, `transfer_public_to_private` is reverted. let current_amount: u64 = Mapping::get_or_use(account, sender, 0u64); Mapping::set(account, sender, current_amount - amount); }}
Якщо немає потреби створювати або змінювати загальнодоступний стан у ланцюжку, завершальні функції не потрібні.
Mapping
Відображення оголошується як mapping {name}: {key-type} => {value-type}
. Відображення містять пари ключ-значення. Відображення зберігаються в ланцюжку.
// On-chain storage of an `account` mapping,// with `address` as the type of keys,// and `u64` as the type of values.mapping account: address => u64;
Mapping Operations
Структура відображення дозволяє програмісту застосовувати оновлення до структури даних відображення програми, викликаючи одну з наведених нижче функцій.
Операції зіставлення дозволені лише у функції фіналізації.
program test.aleo { mapping counter: address => u64; transition dubble() { return then finalize(self.caller); } finalize dubble(addr: address) { let current_value: u64 = Mapping::get_or_use(counter, addr, 0u64); Mapping::set(counter, addr, current_value + 1u64); current_value = Mapping::get(counter, addr); Mapping::set(counter, addr, current_value + 1u64); }}
get
Команда current_value = Mapping::get(counter, addr);
Отримує значення, збережене в addr
і counter
і зберігає результат у current_value
Якщо значення в addr
не існує, програма не зможе виконатися.
get_or_use
Команда get, яка використовує надане значення за замовчуванням у разі помилки, напр let current_value: u64 = Mapping::get_or_use(counter, addr, 0u64);
Отримує значення, збережене в addr
в counter
і зберігає результат уcurrent_value
. Якщо ключ відсутній, 0u64
зберігається в counter
зберігається в current_value
.
set
Встановлена команда, напр. Mapping::set(counter, addr, current_value + 1u64);
Встановлює addr
як current_value + 1u64
вcounter
.
contains
A містить команду, напр. let contains: bool = Mapping::contains(counter, addr);
Повертаєtrue
якщо addr
присутній у лічильнику counter
, false
в іншому випадку.
remove
Команда видалення, напр. Mapping::remove(counter, addr);
Команда видалення, напр. addr
в counter
.
For Loops
Цикли For оголошуються як for {variable: type} in {lower bound}..{upper bound}
. Беззнакові цілі типи u8
, u16
, and u32
рекомендуються для типів змінних циклу. Нижня межа має бути меншою за верхню. Підтримуються вкладені цикли.
Example
let count: u32 = 0u32; for i: u32 in 0u32..5u32 { count += 1u32; } return count; // returns 5u32
Operators
Оператори в Leo обчислюють значення на основі одного або кількох виразів. Лео намагатиметься якнайшвидше виявити помилки арифметичних операцій. Якщо під час компіляції можна визначити цілочисельне переповнення або ділення на нуль, Лео швидко повідомить програмісту. В іншому випадку помилка буде виявлена під час перевірки, коли обробляються вхідні дані функції переходу.
Наприклад, додавання додає first
з second
, зберігаючи результат у destination
.Для цілочисельних типів додається обмеження для перевірки переповнення. Інформацію про випадки, коли для цілочисельних типів потрібна семантика обгортання, див. операторAdd Wrapped
.
let a: u8 = 1u8 + 1u8;// a is equal to 2a += 1u8;// a is now equal to 3a = a.add(1u8);// a is now equal to 4
Arithmetic Operators
field
group
scalar
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
field
group
i8
i16
i32
i64
i128
field
group
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
обтікання віднімання (двійковий)
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
field
group
scalar
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
field
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
field
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
згортання піднесення до степеня
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
обертання абсолютного значення
Logical Operators
bool
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
bool
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
bool
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
bool
i8
i16
i32
i64
i128
u8
u16
u32
u64
u128
Relational Operators
Оператори відношення завжди перетворюватимуться на логічне bool
значення.
bool
, group
, field
, integers, addresses, structs, records
bool
, group
, field
, integers, addresses, structs, records
Operator Precedence
Оператори визначать пріоритетність оцінювання відповідно до:
=
+=
-=
*=
/=
%=
**=
<<=
>>=
&=
|=
^=
Parentheses
Щоб визначити пріоритет іншого обчислення, використовуйте дужки ()
навколо виразу.
let result = (a + 1u8) * 2u8;
(a + 1u8)
буде обчислено перед множенням на два * 2u8
.
Commands
Leo підтримує кілька команд, які можна використовувати для отримання інформації про блокчейн Aleo та поточну транзакцію.
self.caller
self.caller
наразі реалізовано якself.signer
в інструкціях Aleo. T. Це буде виправлено в наступному випуску.
Повертає адресу облікового запису, який викликає функцію програми.
program test.aleo { transition matches(addr: address) -> bool { return self.caller == addr; }}
block.height
Повертає висоту поточного блоку.
block.height
дозволений лише у функції finalize function.
program test.aleo { transition matches(height: u32) { return then finalize(height); } finalize matches(height: u32) { assert_eq(height, block.height); }}
Core Functions
Основні функції — це функції, вбудовані в мову Leo. Вони використовуються для виконання криптографічних операцій, таких як хешування, зобов’язання та генерація випадкових чисел.
Assert and AssertEq
assert
і assert_eq
використовуються для перевірки істинності умови. Якщо умова хибна, програма не працюватиме.
program test.aleo { transition matches() { assert(true); assert_eq(1u8, 1u8); }}
Hash
Leo підтримує наступні алгоритми хешування:BHP256
, BHP512
, BHP768
, BHP1024
, Pedersen64
, Pedersen128
, Poseidon2
, Poseidon4
, Poseidon8
, Keccak256
, Keccak384
, Keccak512
, SHA3_256
, SHA3_384
, SHA3_512
.
Тип виведення функції зобов’язання вказується в назві функції. напр.hash_to_group
поверне тип group
. Хеш-функції приймають будь-який тип як аргумент.
let a: scalar = BHP256::hash_to_scalar(1u8);let b: address = Pedersen64::hash_to_address(1u128);let c: group = Poseidon2::hash_to_group(1field);
Commit
Leo підтримує наступні алгоритми зобов'язань:BHP256
, BHP512
, BHP768
, BHP1024
, Pedersen64
, Pedersen128
Тип виведення функції зобов’язання вказується в назві функції. напр. commit_to_group
поверне типgroup
.
Перший аргумент може мати будь-який тип. Другий аргумент має бути типом поля та використовується як фактор засліплення.
let a: group = BHP256::commit_to_group(1u8, 2field);let b: address = Pedersen64::commit_to_address(1u128, 2field);
Random
Leo підтримує алгоритм генерації випадкових чиселChaCha
.
Тип виведення випадкової функції вказується в назві функції. напр. rand_group
поверне тип group
.
Випадкові функції дозволені лише у функції фіналізації.
let a: group = ChaCha::rand_group();let b: u32 = ChaCha::rand_u32();
Deprecated Syntax
Increment and Decrement
increment()
іdecrement()
застаріли з Leo v1.7.0. Натомість використовуйте функцію Mapping::set()
.
program transfer.aleo { // On-chain storage of an `account` map, // with `address` as the key, // and `u64` as the value. mapping account: address => u64; transition transfer_public(...) {...} finalize transfer_public( public sender: address, public receiver: address, public amount: u64 ) { // Decrements `account[sender]` by `amount`. // If `account[sender]` does not exist, it will be created. // If `account[sender] - amount` underflows, `transfer_public` is reverted. let sender_amount: u64 = Mapping::get_or_use(account, sender, 0u64); Mapping::set(account, sender, sender_amount - amount); // Increments `account[receiver]` by `amount`. // If `account[receiver]` does not exist, it will be created. // If `account[receiver] + amount` overflows, `transfer_public` is reverted. let receiver_amount: u64 = Mapping::get_or_use(account, receiver, 0u64); Mapping::set(account, receiver, receiver_amount + amount); }}