March 8

Починаємо кодити з 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;

INFO

Цілі числа з більшою довжиною біта створюють більше обмежень у схемі, що може сповільнити час обчислення.

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 має бути оголошено наступне:

  • constants
  • mappings
  • record types
  • struct types
  • transition functions
  • helper functions
  • finalize functions

Наступне має бути оголошено за межами області дії програми у файлі Leo:

  • imports

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

Структура відображення дозволяє програмісту застосовувати оновлення до структури даних відображення програми, викликаючи одну з наведених нижче функцій.

INFO

Операції зіставлення дозволені лише у функції фіналізації.

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

Операція

Операнди

Підтримувані типи

додаток

+ += .add()

field group scalar i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

додавання обгортки

.add_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

заперечення (унарний)

- .neg()

field group i8 i16 i32 i64 i128

віднімання (двійковий)

- -= .sub()

field group i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

обтікання віднімання (двійковий)

.sub_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

множення

* *= .mul()

field group scalar i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

обгортання множення

.mul_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

поділ

/ /= .div()

field i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

поділ обгортки

.div_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

залишок

% %= .rem()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

обгортання залишку

.rem_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

піднесення до степеня

** **= .pow()

field i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

згортання піднесення до степеня

.pow_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

зрушення вліво

<< <<= .shl()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

обгортання лівого зсуву

.shl_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

правий зсув

>> >>= .shr()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

обтікання правою зміною

.shr_wrapped()

i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

абсолютне значення

.abs()

i8 i16 i32 i64 i128

обертання абсолютного значення

.abs_wrapped()

i8 i16 i32 i64 i128

подвоєння

.double()

field group

квадратура

.square()

field

квадратний корінь

.square_root()

field

зворотний

.square_root()

field

Logical Operators

Операція

Операнди

Підтримувані типи

NOT

! .not()

bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

AND

& &= .and()

bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

OR

| |= .or()

bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

XOR

^ ^= .xor()

bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128

NAND

.nand()

bool

NOR

.nor()

bool

умовне AND

&&

bool

умовне OR

||

bool

Relational Operators

Оператори відношення завжди перетворюватимуться на логічне bool значення.

Операція

Операнди

Підтримувані типи

рівні

== .eq()

bool, group, field, integers, addresses, structs, records

не-рівно

!= .neq()

bool, group, field, integers, addresses, structs, records

менше ніж

< .lt()

field, scalar, integers

менше або дорівнює

<= .lte()

field, scalar, integers

більш чим

> .gt()

field, scalar, integers

більше або дорівнює

>= .gte()

field, scalar, integers

Operator Precedence

Оператори визначать пріоритетність оцінювання відповідно до:

Оператор

Асоціативність

! -(унарний)

**

справа наліво

* /

зліва направо

+ -(двійковий)

зліва направо

<< >>

зліва направо

&

зліва направо

|

зліва направо

^

зліва направо

< > <= >=

== !=

зліва направо

&&

зліва направо

||

зліва направо

= += -= *= /= %= **= <<= >>= &= |= ^=

Parentheses

Щоб визначити пріоритет іншого обчислення, використовуйте дужки () навколо виразу.

let result = (a + 1u8) * 2u8;

(a + 1u8) буде обчислено перед множенням на два * 2u8.

Commands

Leo підтримує кілька команд, які можна використовувати для отримання інформації про блокчейн Aleo та поточну транзакцію.

self.caller

NOTE

self.caller наразі реалізовано якself.signer в інструкціях Aleo. T. Це буде виправлено в наступному випуску.

Повертає адресу облікового запису, який викликає функцію програми.

program test.aleo {    transition matches(addr: address) -> bool {        return self.caller == addr;    }}

block.height

Повертає висоту поточного блоку.

INFO

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);

See all hash functions

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);

See all commitment functions

Random

Leo підтримує алгоритм генерації випадкових чиселChaCha.
Тип виведення випадкової функції вказується в назві функції. напр. rand_group поверне тип group .

INFO

Випадкові функції дозволені лише у функції фіналізації.

let a: group = ChaCha::rand_group();let b: u32 = ChaCha::rand_u32();

See all random functions

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);    }}