DEV Community

EgorMajj
EgorMajj

Posted on

Инструкция по началу работы с Aleo

Это руководство представляет собой описание некоторых простых примеров, которые помогут вам в работе с инструкциями Aleo.
Инструкции это как сборка для программ Aleo.
Программирование более высокого уровня может быть выполнено с помощью Leo, о котором вы можете прочитать здесь.

Прежде чем мы начнем, убедитесь, что у вас установлен и запущен Rust. Вы можете найти несколько примеров в репозитории проекта Aleo

Установите Aleo

  • Сборка из исходного кода

Вы можете установить aleo, собрав его из исходного кода следующим образом (мы рекомендуем устанавливать Aleo именно таким образом):

# Download the source code
git clone https://github.com/AleoHQ/aleo && cd aleo

# Install Aleo
$ cargo install --path .
Enter fullscreen mode Exit fullscreen mode

Сборка из crates.io

(Эта опция устарела, мы рекомендуем собирать из исходников, чтобы использовать последнюю версию)

Также вы можете установить сборник aleo из репозитория crates.io. В терминале запустите:

cargo install aleo
Enter fullscreen mode Exit fullscreen mode

На этом этапе вы можете запустить команду aleo в терминале:

aleo
Enter fullscreen mode Exit fullscreen mode

Первые шаги

Создание нового проекта Aleo

Чтобы создать новый проект, мы воспользуемся командой new. Наш проект:

aleo new foo
Enter fullscreen mode Exit fullscreen mode

Это создаст папку foo и файлы с базовой структурой проекта:

  • README.md скелет README с инструкциями по сборке
  • main.aleo основной файл исходного кода.
  • program.json, содержащий идентификационные данные проекта в формате JSON. В частности, адрес разработчика и его закрытый ключ для программы.

Давайте откроем main.aleo и зададим функцию sum:

// The 'foo.aleo' program.
program foo.aleo;

function sum:
    input r0 as u32.public;
    input r1 as u32.private;
    add r0 r1 into r2;
    output r2 as u32.private;
Enter fullscreen mode Exit fullscreen mode

О том, что означает этот код, мы поговорим позже. Сначала мы построим нашу программу foo.

Сборка проекта Aleo

Чтобы собрать проект, запустите его в главной папке:

aleo build
Enter fullscreen mode Exit fullscreen mode

В результате вы увидите следующее:

 Compiling 'foo.aleo'...

  Loaded universal setup (in 1478 ms)
  Built 'sum' (in 6323 ms)

 Built 'foo.aleo' (in "[...]/foo")
Enter fullscreen mode Exit fullscreen mode

Сначала в вашу систему загружается "universal setup". Подробнее об этом можно прочитать здесь или в документе Marlin.

Как только universal setup будет готова, каждая функция в вашем файле main.aleo собирается, создавая это в папке output:

  • sum.prover проверяющая для функции sum.
  • sum.verifier верификатор для функции sum.
  • main.avm байткод вашей программы aleo для запуска виртуальной машиной.

Как вы уже догадались, у нас всего один .avm-файл для всей программы, но для каждой функции есть свой проверяющий и верификатор.

Запуск программы

Вы можете запустить программу с помощью команды aleo run, за которой следует имя функции, которую вы хотите выполнить, и ее входные параметры. Давайте запустим наши функции sum:

aleo run sum 2u32 3u32
Enter fullscreen mode Exit fullscreen mode

когда выполнение будет завершено, вы должны увидеть следующий результат:

🚀 Executing 'foo.aleo/sum'...

  Calling 'foo.aleo/sum'...
  Executed 'sum' (in 1170 ms)

➡️  Output

  5u32

 Executed 'foo.aleo/sum' (in "[...]/foo")
Enter fullscreen mode Exit fullscreen mode

Как вы можете видеть здесь, выполнение функции sum длилось 1170 мс, а выходному регистру было присвоено значение 5u32, представляющее sum входов.

Обзор программы

Давайте рассмотрим программу foo внутри файла main.aleo:

// The 'foo.aleo' program.
program foo.aleo;

function sum:
    input r0 as u32.public;
    input r1 as u32.private;
    add r0 r1 into r2;
    output r2 as u32.private;
Enter fullscreen mode Exit fullscreen mode

Во-первых, нам нужно заявить программу следующим образом:

program foo.aleo;
Enter fullscreen mode Exit fullscreen mode

Затем мы можем начать писать её функции (или другие структуры Aleo, такие как интерфейсы, записи, закрытия, как мы увидим позже). В случае с функциями все очень просто:

function [function_name]:
Enter fullscreen mode Exit fullscreen mode

Функции состоят из трех основных частей:

  • Раздел input "ввода". Здесь мы определяем его входные параметры:
    input r0 as u32.public;
    input r1 as u32.private;
Enter fullscreen mode Exit fullscreen mode

Все в инструкциях Aleo заявляются/хранятся внутри регистра с типом (i8,field,bool, и т.д.) и параметром видимости (public или private). Регистры имеют имена r0, r1, ..., rn. Более подробно о регистрах, типах и видимости мы поговорим позже.

Итак, в данном случае мы используем r0 и r1 для хранения входов, передаваемых в последовательном порядке в программу, в виде значений u32, где мы можем хранить 32-битные беззнаковые целые числа для выполнения нашей функции sum.

  • Раздел instructions "инструкций". Следующий раздел, заключающий в себе ядро нашей функции. Здесь мы указываем количество инструкций Aleo, необходимых для того, чтобы наша программа сделала то, что мы хотим. Например, выполнение функции add:
    add r0 r1 into r2;
Enter fullscreen mode Exit fullscreen mode

За каждой инструкцией Aleo следуют ее входные параметры с заданными типами, а результат сохраняется в регистре into. Вы можете найти все доступные инструкции Aleo здесь.

  • Раздел output "вывода". Подобно разделу ввода, раздел вывода делает то же самое для вывода программы. Это возвращаемое значение функции.
 output r2 as u32.private;
Enter fullscreen mode Exit fullscreen mode

Разбираемся в некоторых концепциях

Типы

Aleo является типизированным языком. В настоящее время доступны следующие типы данных:

Boolean
Field
Group
I8
I16
I32
I64
I128
U8
U16
U32
U64
U128
Scalar
Enter fullscreen mode Exit fullscreen mode

Определены пользователем:

Interface
Record
Enter fullscreen mode Exit fullscreen mode

Registers "Регистры"

Регистр - это место, где вы храните данные, чтобы затем иметь возможность их изменять.

Interfaces "Интерфейсы"

Interfaces - это определяемые пользователем структуры данных. Они очень похожи на традиционные структуры в обычных языках программирования. Интерфейсы можно хранить в регистрах, как и любые другие типы данных Aleo.

Например, давайте создадим интерфейс, представляющий массив фиксированного размера из 3 элементов. Добавьте это в нижнюю часть файла main.aleo:

interface array3:
    a0 as u32;
    a1 as u32;
    a2 as u32;
Enter fullscreen mode Exit fullscreen mode

Теперь, для примера, давайте закодируем функцию, которая добавляет единицу к каждому элементу регистра с хранящимся в нем типом данных array3.

function sum_one_to_array3:
    input r0 as array3.private;
    add r0.a0 1u32 into r1;
    add r0.a1 1u32 into r2;
    add r0.a2 1u32 into r3;
    cast r1 r2 r3 into r4 as array3;
    output r4 as array3.private;
Enter fullscreen mode Exit fullscreen mode

Как видите, мы можем ввести интерфейс в регистр r0 и получить доступ к элементам интерфейса с помощью синтаксиса . Мы выполняем инструкцию add над каждым элементом, сохраняя результаты в регистрах r1, r2 и r3, и, наконец, используем команду cast для создания нового интерфейса array3 в r4.

Теперь давайте запустим его. В этом случае единственное, что вам нужно знать, это то, что интерфейсы передаются в cli в следующем формате:

"{a0: 1u32, a1: 2u32, a2: 3u32}"
Enter fullscreen mode Exit fullscreen mode

Теперь мы можем выполнить команду aleo run. Мы очистим проект, чтобы подхватить новый код:

aleo clean && aleo run sum_one_to_array3 "{a0: 0u32, a1: 1u32, a2: 2u32}"
Enter fullscreen mode Exit fullscreen mode

И на выходе мы получаем новый элемент набора array3:

🚀 Executing 'foo.aleo/sum_one_to_array3'...

  Calling 'foo.aleo/sum_one_to_array3'...
  Executed 'sum_one_to_array3' (in 1331 ms)

➡️  Output

  {
  a0: 1u32,
  a1: 2u32,
  a2: 3u32
}

 Executed 'foo.aleo/sum_one_to_array3' (in "[...]/foo")
Enter fullscreen mode Exit fullscreen mode

Records "Записи"

Record - это фундаментальная структура данных для кодирования пользовательских активов и состояния приложения. Они очень похожи на интерфейсы, но у них есть два необязательных параметра:

record token:
    owner as address.private
    gates as u64.private
Enter fullscreen mode Exit fullscreen mode

owner означает адрес Aleo, которому принадлежит запись, а gates - количество кредитов, которые запись должна потратить.

Регистры важны, поскольку они представляют собой базовую структуру Aleo для обработки состояния в вашем приложении.

При выполнении функции Aleo в качестве входных регистров можно передавать только регистры, принадлежащие адресу приложения. В противном случае возникнет ошибка, и приложение не будет запущено.

Адрес приложения для разработки можно найти в файле program.json:

{
    "program": "foo.aleo",
    "version": "0.0.0",
    "description": "",
    "development": {
        "private_key": "APrivateKey1zkpFsQNXJwdvjKs9bRsM91KcwJW1gW4CDtF3FJbgVBAvPds",
        "address": "aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09"
    },
    "license": "MIT"
}
Enter fullscreen mode Exit fullscreen mode

Состояние Aleo в двух словах

В Aleo состояние приложения управляется с помощью записей. Учетная запись Aleo может создать транзакцию для потребления записи и создания новой записи на ее месте. Записи в Aleo шифруются по адресу владельца записи, что обеспечивает полную конфиденциальность всех записей в Aleo.

Ваша первая программа Aleo: Выполнение перевода

Рассмотрим эту программу:

// The 'foo.aleo' program.
program foo.aleo;

record token:
    owner as address.private;
    gates as u64.private;
    amount as u64.private;

function transfer_amount:
    //  sender token record
    input r0 as token.record;
    // receiver address
    input r1 as address.private;
    // amount to transfer
    input r2 as u64.private;

    // final balance of sender
    sub r0.amount r2 into r3;
    // final balance of receiver
    add 0u64 r2 into r4;

    // sender token record after the transfer
    cast r0.owner r0.gates r3 into r5 as token.record;
    // receiver token record after the transfer
    cast r1 0u64 r4 into r6 as token.record;

    // sender new token record
    output r5 as token.record;
    // receiver new token record
    output r6 as token.record;
Enter fullscreen mode Exit fullscreen mode

Во-первых, мы определили наш собственный тип данных записи под названием token, который имеет два необязательных параметра, owner и gates, и определяемый пользователем параметр под названием amount, представляющий количество имеющихся у нас токенов.

Функция transfer_amount получает 3 входных параметра (запись sender, запись receiver и amount) и сохраняет их в 3 регистрах (r0, r1 и r2). После этого она вычисляет окончательный баланс для обоих регистров и сохраняет его в r3 и r4 (используя инструкции sub и add для вычисления вычитания и сложения). С этими итоговыми суммами он создает выходные записи для отправителя и получателя, сохраняя их в r5 и r6. Наконец, обе записи отправляются из функции с помощью инструкции output.

Для запуска этой функции первым параметром является входная запись программы. Формат этого параметра такой же, как и для интерфейсных типов:

{
  owner: aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09.private,
  gates: 0u64.private,
  amount: 50u64.private
}
Enter fullscreen mode Exit fullscreen mode

где:

  • owner: публичный адрес программы, как указано в development.address файла build/program.json.
  • gates: параметры, которые есть у записи.
  • другие параметры: зависят от самой программы (в данном примере мы использовали параметр amount со значением 50).

Запустим функцию transfer_amount (если вы следуете за нами, не забудьте использовать адрес, найденный в program.json для поля owner):

aleo clean && aleo run transfer_amount "{
owner: aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09.private,
gates: 0u64.private,
amount: 50u64.private
}" aleo1h3gu7fky36y8r7v2x9phc434fgf20g8qd7c7u45v269jfw6vmugqjegcvp 10u64
Enter fullscreen mode Exit fullscreen mode

Мы получаем следующие результаты:

🚀 Executing 'foo.aleo/transfer_amount'...

  Calling 'foo.aleo/transfer_amount'...
  Executed 'transfer_amount' (in 3520 ms)

➡️  Outputs

  {
  owner: aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09.private,
  gates: 0u64.private,
  amount: 40u64.private
  _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public
}
  {
  owner: aleo1h3gu7fky36y8r7v2x9phc434fgf20g8qd7c7u45v269jfw6vmugqjegcvp.private,
  gates: 0u64.private,
  amount: 10u64.private
  _nonce: 2323253577170856894742339369235137467208538700597121244293392765726742543235group.public
}

 Executed 'foo.aleo/transfer_amount' (in "[...]/foo")
Enter fullscreen mode Exit fullscreen mode

Вот и все. Вы передали свои первые собственные определенные токены в Aleo!

Примечание: _nonce не записывается в инструкции Aleo. Сборка выводит _nonce в выводах записей. Пользователь должен указать его в качестве ввода при использовании записи.

Итоговые замечания

Программы Aleo могут создавать и исполнять криптографические схемы, написанные в инструкциях Aleo. Это вводит некоторые ограничения, которых нет в обычных языках программирования. Например, при использовании тернарного оператора для наличия ответвления в потоке программы необходимо заранее иметь оба результата (т.е. результат каждого ответвления должен быть вычислен и известен до выполнения тернарного оператора). Логическое сравнение должно быть между регистрами, а не между возвратными значениями вызываемых функций.

Источник - https://www.entropy1729.com/getting-started-aleo-instructions/

Перевел - egormajj#0340

Top comments (0)