![](https://hackmd.io/_uploads/ryxHM1GA2.png) **目录** [toc] :::info :information_source: 由于单篇长度的限制,第二部分内容在:https://hackmd.io/@maxsayss/B1nBeHtin ::: :::success 欢迎对Aleo技术感兴趣的小伙伴加入社群! 注意哈,社群只讨论技术,所以非技术类小伙伴谨慎加入哦! 再次非常感谢大家的支持,谢谢! TG: https://t.me/+VATAn9z4x_9iOTc1 ::: # 介绍 ## 入门 欢迎来到 Aleo 社区。我们共同致力于构建一个高性能、安全且私密的 Web 应用程序平台。Aleo 引入了一个应用程序运行时环境,允许您的软件自主编译、执行和最终确定其状态。 与 Leo 一起在 Aleo 上进行开发 **1.搭建** **1.1 安装Git** ```php= bit.ly/start-git ``` **1.2 安装Rust** ```php= bit.ly/start-rust ``` **1.3 安装Leo** ```php= curl -L https://raw.githubusercontent.com/AleoHQ/workshop/master/install.sh | sh ``` 学习 Leo 是在 Aleo 上开发私人应用程序的最快方法。 你好 Leo - 运行你的第一个 Leo 程序。 Leo Docs - 了解如何使用最新的 Leo 语言功能。 Leo 示例 - 使用官方 Aleo 示例快速启动您的应用程序开发。 **阅读协议** [Aleo Concepts](https://hackmd.io/@maxsayss/B1nBeHtin#概念) - Aleo 是一个全新的 Layer1 区块链。了解 Aleo 和其他区块链之间的异同。 **演讲和视频** 来自 Aleo 社区的演讲和视频。 Coinbase BUIDL -[学习 LEO | 使用 ZK 密码学对私有应用程序进行编码](https://youtu.be/LJXjtthDl6I) DEVCON - [Aleo 开发者研讨会:Leo 编码和示例](https://www.youtube.com/watch?v=ABPCr2TwrgE&feature=youtu.be) DEVCON - [ZK 证明有什么用?匿名身份、女巫预防和审核的应用](https://www.youtube.com/watch?v=d2n0Al0P2Jc&feature=youtu.be) DEVCON - [zkSNARK 证明者的高效私人委托](https://www.youtube.com/watch?v=mFzwp8gGn-E&feature=youtu.be) ICC - [TLV Howard Wu Aleo 开发人员的 Web 3.0](https://www.youtube.com/watch?v=-FrrylHITvg&feature=youtu.be) ZK8 - [揭秘零知识编程 – Howard Wu - Aleo](https://www.youtube.com/watch?v=e-1mPNEINRI&feature=youtu.be) ETHCC - [ZK 漫游指南:Aleo 开发者研讨会](https://www.youtube.com/watch?v=fjfj5kFcQe4&feature=youtu.be) CoinGecko - [Alex Pruden 零知识证明的重要性](https://www.youtube.com/watch?v=NvYddvVBjXY&feature=youtu.be) 思考加密货币 - [Alex Pruden 访谈 - Aleo 的隐私解决方案 - 零知识证明 - a16z - 比特币和加密货币](https://www.youtube.com/watch?v=1y4jvyy8Nsk) Interop - [与 Aleo 的 Alex Pruden 合作的零知识智能合约](https://www.youtube.com/watch?v=6BwefrwgN3w) Axelar - [与 Aleo 进行技术会谈](https://www.youtube.com/watch?v=P7G2DKWZbVM&feature=youtu.be) **更多Aleo资源** 如果您有兴趣了解有关 Aleo 的更多信息: 🐦 | 推特 ~ @AleoHQ ~ https://twitter.com/AleoHQ 🤝 | 社区 Twitter ~ @aleocommunity ~ https://twitter.com/aleocommunity 🐙 | GitHub ~ https://github.com/AleoHQ 📄 | 开发者文档 ~ https://developer.aleo.org/ 🎮 | Leo游乐场 ~ https://play.leo-lang.org/ 🔎 | Aleo 区块浏览器 ~ https://www.aleo.network/ ✍️ | 社区博客 ~ https://medium.com/@AleoHQ [正在寻找开发者资源](https://hackmd.io/@maxsayss/BJ9kjZIjh#开发者资源) # Leo ## 概述 Leo 编程语言 欢迎使用 Leo 编程语言。Leo 是一种函数式静态类型编程语言,专为编写私有应用程序而构建。Leo 是一种高级编程语言,可编译为低级 Aleo 指令。 :::info :information_source: Leo 语言目前正在积极开发中。请监控GitHub上的存储库是否有可能发生重大更改。 ::: **安装Leo** 前往[安装](https://hackmd.io/@maxsayss/BJ9kjZIjh#安装)以获取有关如何安装 Leo 的信息。 查看[Hello Leo](https://hackmd.io/@maxsayss/BJ9kjZIjh#你好Leo)教程,了解 Leo 和 Leo CLI 的简要演练。 **语言指南** 要了解 Leo 支持的类型,请从[数据类型和值](https://hackmd.io/@maxsayss/BJ9kjZIjh#语言)开始。 要学习 Leo 的核心概念和语法,请从[Leo 程序的布局](https://hackmd.io/@maxsayss/BJ9kjZIjh#语言)开始。 要查看 Leo 支持的运算符的完整列表,请参阅[运算符](https://hackmd.io/@maxsayss/BJ9kjZIjh#运算符)。 有关 Leo 语法的快速参考,请参阅Leo Cheat Sheet。 **正式语言文档** Leo 语法的正式 ABNF 语法规范可以在[此处](https://github.com/AleoHQ/grammars/blob/master/leo.abnf)查看。 命令行界面文档 Leo CLI 提供了一套命令,使 Leo 中的编程变得简单。 * leo new * leo build (deprecated) * leo run * leo execute * leo clean * leo update **附加资料** 为您最喜欢的代码[编辑器](https://hackmd.io/@maxsayss/BJ9kjZIjh#工具)安装 Leo 。 有关示例和社区项目等其他开发人员资源,请参阅开发人员资源。 您的 Leo 代码格式正确吗?查看样式指南和常见模式以获取官方指南。 发现错误?对某个功能有想法吗?在[Leo GitHub](https://github.com/AleoHQ/leo/issues/new/choose)上提交问题。 有关更多信息,请参阅贡献指南。 **案例:** * [Private Auction](https://hackmd.io/@maxsayss/BJ9kjZIjh#拍卖) ## 安装 下载 使用适合您平台的预构建安装程序下载最新的 Leo 版本,并立即开始开发。 **苹果系统** * [安装适用于 MacOS M1 的 Leo](https://github.com/AleoHQ/leo/releases/download/v1.9.0/leo-v1.9.0-aarch64-apple-darwin.zip) * [为 MacOS Intel 安装 Leo](https://github.com/AleoHQ/leo/releases/download/v1.9.0/leo-v1.9.0-x86_64-apple-darwin.zip) **Windows系统** * [安装 Windows 版 Leo](https://github.com/AleoHQ/leo/releases/download/v1.9.0/leo-v1.9.0-x86_64-pc-windows-msvc.zip) **乌班图系统** * [为 Ubuntu 安装 Leo](https://github.com/AleoHQ/leo/releases/download/v1.9.0/leo-v1.9.0-x86_64-unknown-linux-gnu.zip) [在此浏览所有 Leo 版本](https://github.com/AleoHQ/leo/releases) **从源安装** 要使用最新的 Leo 功能,请从 GitHub 安装 Leo 源代码。 **1. 安装前提** **1.1 安装Git** ```php= bit.ly/start-git ``` **1.2 安装Rust** ```php= bit.ly/start-rust ``` **1.3 检查** ```php= git --version cargo --version ``` **2.从源代码构建** 您可以从源代码构建并安装 Leo,如下所示: ```python= # Download the source code git clone https://github.com/AleoHQ/leo cd leo # Build and install cargo install --path . ``` 这将生成可执行文件~/.cargo/bin/leo。 现在要使用 Leo,请在终端中运行: ```php= leo ``` **3. IDE语法** Aleo 维护跨不同平台的语法突出显示实现。 如果您在此列表中没有看到您最喜欢的编辑器,请访问[GitHub](https://github.com/AleoHQ/welcome/issues/new)。 * [Visual Studio Code](https://hackmd.io/@maxsayss/BJ9kjZIjh#工具) * [Sublime Text](https://hackmd.io/@maxsayss/BJ9kjZIjh#工具) * [Intellij](https://hackmd.io/@maxsayss/BJ9kjZIjh#工具) ## 你好Leo 使用 Leo 命令行界面 (CLI) 创建新项目。在您的终端中,运行: ```python= leo new hello cd hello ``` 这将创建一个具有以下结构的目录: ```python= hello/ ├── .env # Your program environment ├── program.json # Your program manifest ├── README.md # Your program description ├── build/ ├── inputs/ │ ├── hello.in # Your program inputs └── src/ └── main.leo # Your program file ``` 让我们运行该项目。 **一行零知识证明** 该leo run命令将编译并运行该程序。在您的终端中,运行: ```python= leo run main ``` 控制台输出: ```python= Leo Compiled 'main.leo' into Aleo instructions ⛓ Constraints • 'hello.aleo/main' - 35 constraints (called 1 time) ➡️ Output • 3u32 Leo ✅ Finished 'hello.aleo/main' (in "/hello/build") ``` 恭喜!您刚刚运行了您的第一个 Leo 程序。 让我们看一下刚刚执行的程序的文件语法。 program.json是配置我们的包的 Leo 清单文件。 ```python= { "program": "hello.aleo", "version": "0.0.0", "description": "", "license": "MIT" } ``` 其中的程序 IDprogram是您发布程序后其他开发人员可以查找的正式名称。 ```python= "program": "hello.aleo", ``` 当前包中的所有文件都将使用指定的 Leo 进行编译version。 ```python= "version": "0.0.0", ``` **电路语法** 打开src/main.leo。main.leo文件是 Leo 项目的入口点。它通常包含一个名为 的函数main。让我们分解一下 Leo 文件的结构。 ```python= // The 'hello' program. program hello.aleo { transition main(public a: u32, b: u32) -> u32 { let c: u32 = a + b; return c; } } ``` program hello.aleo {定义Leo 文件内的程序名称。程序 ID 必须与program.json清单文件匹配。该关键字transition表示Leo 中的转换函数定义。我们的hello main函数接受一个a具有类型u32和public可见性的输入,以及一个b具有类型u32和private可见性的输入(默认情况下)。该程序返回一个类型为 的结果u32。转换函数体用花括号括起来{ }。Leo 中的一种常见约定是将左大括号与函数定义放在同一行,并在中间添加一个空格。 ```python= transition main(public a: u32, b: u32) -> u32 { ``` 在函数内部main,我们声明一个类型c为变量,并将其设置为等于变量和u32的加法。Leo 的编译器将检查 和的类型是否相等,并且加法的结果是 type 。ababu32 ```python= let c: u32 = a + b; ``` :::info :information_source: 我们设计 Leo 时坚信开发人员也是人,也会犯错误。尝试更改任何变量的类型,并查看 Leo 的建议和有用的错误消息。 ::: 最后,我们返回变量c。Leo 将检查其c类型与函数返回类型是否匹配u32。 ```python= return c; ``` **接线程序输入** Leo 的编译器将从main.leo程序构建一个电路。打开input/hello.in。以.in结尾的文件为程序提供输入。您还可以通过命令行指定程序参数。 输入/hello.in ```python= // The program input for hello/src/main.leo [main] public a: u32 = 1u32; b: u32 = 2u32; ``` 输入文件以方括号括起来的部分开始[ ]。方括号内main表示我们正在定义转换函数的输入main。您只能定义转换函数的输入。 ```python= [main] ``` 输入赋值与普通.leo文件中的显式变量赋值共享语法。1这里我们将type 的值分配u32给public名为 的输入a。2我们还将type 的值分配u32给名为 的(默认为私有)输入b。Leo 的编译器将获取这些值,并在证明时将它们作为电路的输入提供。 ```python= public a: u32 = 1u32; b: u32 = 2u32; ``` 现在让我们使用 Leo CLI 看看我们可以在程序上运行哪些其他命令。 之前我们使用 运行该程序leo run。该命令本地构建并运行该程序。 ```python= leo run ``` **按步骤:** 1. 清理 首先,使用以下命令删除所有构建文件: ```python= leo clean ``` 控制台输出: ```python= Leo cleaned the build directory (in "/build/") ``` 2. 更新 该leo update命令将 Leo 编译器更新到最新版本。 ```python= leo update ``` 控制台输出: ```python= Leo ✅ Updated to version 1.9.0 ``` 3. 执行 命令leo execute执行Leo程序并输出交易对象 ```python= leo execute main ``` 控制台输出: ```python= Leo ✅ Compiled 'main.leo' into Aleo instructions ⛓ Constraints • 'hello.aleo/main' - 35 constraints (called 1 time) ➡️ Output • 3u32 {"type":"execute","id":"at1 ... (transaction object truncated for brevity) Leo ✅ Executed 'hello.aleo/main' (in "/hello/build") ``` 仅当前面的所有步骤成功完成时,该leo execute命令才会尝试验证证明。在幕后,Leo CLI将在运行每个命令之前检查构建.prover目录中现有的、.verifier和.avm文件。这确保我们不会运行不必要的命令。 **下一步** 要了解有关 Leo 语言及其语法的更多信息,请从[这里](https://hackmd.io/@maxsayss/BJ9kjZIjh#语言)开始。 要了解有关如何使用 Leo CLI 的更多信息,请从[此处](https://hackmd.io/@maxsayss/BJ9kjZIjh#命令行)开始。 ## 语言 Leo语言指南 **静态类型** Leo 是一种静态类型语言,这意味着我们在执行电路之前必须知道每个变量的类型。 **显式类型定义** Leo没有undefined任何价值。null分配新变量时,必须显式声明值的类型。 **值传递** Leo 中的表达式始终按值传递,这意味着当它们用作函数输入或赋值右侧时,它们的值始终会被复制。 **数据类型和值** **布尔值** Leo 支持传统true或false布尔值。bool语句中布尔值的显式类型是必需的。 ```python= let b: bool = false; ``` **整数** Leo 支持有符号整数类型i8, i16, i32, i64,i128 和无符号整数类型u8, u16, u32, u64, u128。 ```python= let b: u8 = 1u8; ``` :::info :information_source: 较高位长度的整数会在电路中产生更多约束,这会减慢计算时间。 ::: 关于Leo整数的提示 Leo 不会默认使用整数类型。整数的定义必须包含显式类型。 从 Leo v1.8.2 开始支持类型转换 ```python= let a: u8 = 2u8; // explicit type let b: u16 = a as u16; // type casting let b: u8 = 2; // implicit type -- not supported ``` **域(Field Elements)** Leo 支持field椭圆曲线基域元素的类型。这些是低于基字段模数的无符号整数。 ```python= let a: field = 1field; let b: field = 21888242871839275222246405745257275088548364400416034343698204186575808495617field; ``` **组(Group Elements)** 传递到 Leo 编译器的椭圆曲线上的仿射点集形成一个组。Leo 支持由生成点生成的该组的子组作为原始数据类型。组元素很特殊,因为它们的值可以从坐标对的 x 坐标定义,例如 1group。group指定组值时必须使用组类型关键字。 ```python= let b: group = 0group; // the point with 0 x-coordinate let a: group = 1group; // the point with 1 x-coordinate let c: group = 2group; // the point with 2 x-coordinate ``` 上述生成点可以通过与类型相关的常数来获得group。 ```python= let g: group = group::GEN; // the group generator ``` **标量(Scalar Elements)** Leo 支持scalar椭圆曲线子群的标量场元素的类型。这些是低于标量场模的无符号整数。 ```python= let a: scalar = 1scalar; ``` **地址(Addresses)** 定义地址是为了启用编译器优化的例程来解析和操作地址。这些语义将在未来的冲刺中伴随一个标准库。 ```python= let receiver: address = aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4; ``` **Leo程序的整体结构** Leo 程序包含程序作用域、导入 、转换函数、辅助函数、结构 、记录、 映射和Finalize 函数的声明。声明可以在程序文件中本地访问。如果您需要来自另一个 Leo 文件的声明,则必须导入它。 **程序范围** Leo 意义上的程序范围是驻留在 Aleo 区块链上的程序 ID中的代码(其功能)和数据(其类型)的集合。 ```python= import foo.leo; program hello.aleo { mapping balances: address => u64; record token { owner: address, microcredits: u64, amount: u64, } struct message { sender: address, object: u64, } transition mint_public( public receiver: address, public amount: u64, ) -> token { return token { owner: receiver, microcredits: 0u64, 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 文件的程序范围内声明以下内容: * mappings * record types * struct types * transition functions * helper functions * finalize functions 必须在 Leo 文件中的程序范围之外声明以下内容: * imports **程序ID** 程序 ID 声明为{name}.{network}. a 的第一个字符name必须是小写。 name可以包含小写字母、数字和下划线。目前,aleo是唯一受支持的network域。 ```python= program hello.aleo; // valid program Foo.aleo; // invalid program baR.aleo; // invalid program 0foo.aleo; // invalid program 0_foo.aleo; // invalid program _foo.aleo; // invalid ``` **导入(Import)** 您可以导入下载到该imports目录的依赖项。导入被声明为import {filename}.leo; 这将查找imports/{filename}.leo所有声明并将其带入当前文件范围。如果声明有重复的名称,Leo 将无法编译。导入强制排序,必须位于文件顶部。 :::warning :warning: Leo 导入不稳定,目前仅提供最少的功能。他们的语法预计会改变。 ::: ```python=9 import foo.leo; // Import all `foo.leo` declarations into the `hello.aleo` program. program hello.aleo { } ``` **结构体(Struct)** 结构体数据类型声明为struct {name} {}. 结构体包含组件声明{name}: {type},。 ```python= struct array3 { a0: u32, a1: u32, a2: u32, } ``` **记录(Record)** 记录数据类型声明为record {name} {}. 记录包含组件声明{visibility} {name}: {type},。 可见性可以是constant、public或private。用户也可以忽略可见性,在这种情况下,Leo 将默认为private。 记录数据结构必须包含owner和microcredits组件,如下所示。当将记录作为输入传递给程序函数时,该_nonce: group组件也是必需的(但不需要在 Leo 程序中声明)。 ```python= record token { // The token owner. owner: address, // The Aleo balance (in microcredits). microcredits: u64, // The token amount. amount: u64, } ``` **过度函数(Transition Function)** Leo 中的过度函数声明为transition {name}() {}。运行 Leo 程序时可以直接调用过度函数(通过leo run)。过度函数包含可以计算值的表达式和语句。过度函数必须位于要调用的程序的当前作用域内。 ```python= program hello.aleo { transition foo( public a: field, b: field, ) -> field { return a + b; } } ``` 函数入参 函数输入声明为{visibility} {name}: {type}, 函数输入必须在函数名称声明之后的括号中声明。 ```python= // The transition function `foo` takes a single input `a` with type `field` and visibility `public`. transition foo(public a: field) { } ``` 函数出参 函数输出计算为return {expression};。返回输出结束函数的执行。函数声明的返回类型必须与返回的类型匹配{expression}。 ```python= 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}像过度函数这样的修饰符,因为它们仅在内部使用,而不是作为程序外部接口的一部分。 ```python= function foo( a: field, b: field, ) -> field { return a + b; } ``` **内联函数(Inline Function)** 内联函数被声明为inline {name}() {}. 内联函数包含可以计算值的表达式和语句。内联函数不能直接从外部执行,而是 Leo 编译器在每个调用点内联函数体。 内联函数的输入不能具有{visibility}像转换函数这样的修饰符,因为它们仅在内部使用,而不是作为程序外部接口的一部分。 ```python= inline foo( a: field, b: field, ) -> field { return a + b; } ``` 函数的规则(传统意义上)如下: * 函数有三种变体:transition, function, inline. * transition只能调用function和inline。 * function只能调用inline。 * inlines只能调用inlines。 * 不允许直接/间接递归调用 **最终化函数(Finalize Function)** Finalize函数声明为finalize {name}:. Finalize 函数必须紧跟在过度函数之后,并且必须具有相同的名称;与过度函数关联并在关联转换执行的零知识证明经过验证后在链上执行;Finalize 函数最终确定链上的过度函数。一旦finalize函数成功,程序逻辑就会被执行。一旦 Finalize 函数失败,程序逻辑就会恢复。 ```python= 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, microcredits: 0u64, 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}. 映射包含键值对。映射存储在链上。 ```python= // 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; ``` 映射操作 映射结构允许程序员通过调用以下函数之一来对程序映射数据结构应用更新。 :::info :information_source: 映射操作只允许在Finalize 函数中进行。 ::: ```python= 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** get命令,例如获取存储在in中current_value = Mapping::get(counter, addr); 的值并将结果存储在中。 如果at的值不存在,则程序将无法执行。addrcountercurrent_valueaddr **get_or_use** get 命令在失败时使用提供的默认值, 例如获取存储在in 中let current_value: u64 = Mapping::get_or_use(counter, addr, 0u64); 的值并将结果存储在 中。如果密钥不存在,则存储在并存储在 中。addrcountercurrent_value0u64countercurrent_value **set** 设置命令,例如Mapping::set(counter, addr, current_value + 1u64); 设置addr条目,如current_value + 1u64中所示counter。 **contains** 包含命令,例如,如果中存在则let contains: bool = Mapping::contains(counter, addr); 返回,否则返回。trueaddrcounterfalse **remove** 删除命令,例如删除inMapping::remove(counter, addr); 中的条目。addrcounter **For循环** For循环声明为for {variable: type} in {lower bound}..{upper bound}. 建议将无符号整数类型u8、u16、 和作为循环变量类型。u32下限必须小于上限。支持嵌套循环。 ```php= let count: u32 = 0u32; for i: u32 in 0u32..5u32 { count += 1u32; } return count; // returns 5u32 ``` **运算符** Leo 中的运算符根据一个或多个表达式计算一个值。Leo 会尽快尝试检测算术运算错误。如果在编译时可以识别出整数溢出或被零除,Leo 会很快告诉程序员。否则,在处理转换函数输入时,将在证明时捕获错误。 例如,加法first与相加second,将结果存储在 中destination。对于整数类型,添加约束来检查溢出。对于整数类型需要包装语义的情况,请参阅Add Wrapped运算符。 ```php= let a: u8 = 1u8 + 1u8; // a is equal to 2 a += 1u8; // a is now equal to 3 a = a.add(1u8); // a is now equal to 4 ``` 算术运算符 ![](https://hackmd.io/_uploads/ByLrY4Ii2.jpg) 逻辑运算符 ![](https://hackmd.io/_uploads/rJCFKELjh.jpg) 关系运算符 关系运算符将始终解析为布尔bool值。 ![](https://hackmd.io/_uploads/By7hYEUi2.jpg) 运算符优先级 ![](https://hackmd.io/_uploads/BJCAKEUj3.jpg) **括号** 要确定不同计算的优先级,请()在表达式两侧使用括号。 ```php= let result = (a + 1u8) * 2u8; ``` (a + 1u8)将在乘以 2 之前进行评估* 2u8。 **命令** Leo 支持多种命令,可用于引用有关 Aleo 区块链和当前交易的信息。 self.caller 返回调用程序函数的帐户的地址。 ```php= program test.aleo { transition matches(addr: address) -> bool { return self.caller == addr; } } ``` block.height 返回当前块的高度。 :::info :information_source: block.height只允许在Finalize 函数中使用。 ::: ```php= program test.aleo { transition matches(height: u32) { return then finalize(height); } finalize matches(height: u32) { assert_eq(height, block.height); } } ``` **核心函数** 核心函数是 Leo 语言中内置的函数。它们用于执行加密操作,例如哈希、承诺和随机数生成。 **Assert and AssertEq** assert和assert_eq用于验证条件是否为真。如果条件不成立,程序就会失败。 ```php= program test.aleo { transition matches() { assert(true); assert_eq(1u8, 1u8); } } ``` **Hash** Leo 支持以下哈希算法:BHP256, BHP512, BHP768, BHP1024, Pedersen64, Pedersen128, Poseidon2, Poseidon4,Poseidon8 承诺函数的输出类型在函数名称中指定。例如hash_to_group将返回一个group类型。哈希函数接受任何类型作为参数。 ```php= let a: scalar = BHP256::hash_to_scalar(1u8); let b: address = Pedersen64::hash_to_address(1u128); let c: group = Posidon2::hash_to_group(1field); ``` [查看所有哈希函数](https://hackmd.io/@maxsayss/BJ9kjZIjh#运算符) **Commit** Leo 支持以下承诺算法:BHP256, BHP512, BHP768, BHP1024, Pedersen64,Pedersen128 承诺函数的输出类型在函数名称中指定。例如commit_to_group将返回一个group类型。 第一个参数可以是任何类型。第二个参数必须是field类型并用作致盲因素。 ```php= let a: group = BHP256::commit_to_group(1u8, 2field); let b: address = Pedersen64::commit_to_address(1u128, 2field); ``` [查看所有Commit函数](https://hackmd.io/@maxsayss/BJ9kjZIjh#运算符) **Random** Leo支持ChaCha随机数生成算法。 随机函数的输出类型在函数名称中指定。例如rand_group将返回一个group类型。 :::info :information_source: 随机函数只允许在Finalize 函数中使用。 ::: ```php= let a: group = ChaCha::rand_group(); let b: u32 = ChaCha::rand_u32(); ``` [查看所有随机函数](https://hackmd.io/@maxsayss/BJ9kjZIjh#运算符) **已弃用的语法** Increment and Decrement increment()decrement()从 Leo v1.7.0 开始,函数已被弃用。请改用该Mapping::set()函数。 ```php= 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); } } ``` ## 运算符 以下列表显示了 Leo 支持的标准和加密运算符。Leo 运算符编译为可由 Aleo 虚拟机 (AVM) 执行的Aleo 指令操作码。 标准运算符 | 名称 | 中文描述 | 英文描述 | | ----------- | --------------- | ----------------------------------- | | abs | 绝对值运算 | Absolute value operation | | abs_wrapped | 包装绝对值运算 | Wrapping absolute value operation | | add | 加法运算 | Addition operation | | add_wrapped | 包装加法运算 | Wrapping addition operation | | and | 与运算 | AND operation | | assert | 断言布尔值 true | Assert boolean true | | assert_eq | 断言相等 | Assert equality | | assert_neq | 断言不相等 | Assert non-equality | | div | 除法运算 | Division operation | | div_wrapped | 包装除法运算 | Wrapping division operation | | double | double运算 | Double operation | | group::GEN | 组生成器 | Group generator | | gt | 大于 | Greater than comparison | | gte | 大于或者等于 | Greater than or equal to comparison | | inv | 乘法逆运算 | Multiplicative inverse operation | | eq | 等于 | Equality comparison | | neq | 不等于 | Not equal comparison | | lt | 小于 | Less than comparison | | lte | 小于或者等于 | Less than or equal to comparison | | mod | 模运算 | Arithmetic modulo operation | | mul | 乘法运算 | Multiplication operation | | mul_wrapped | 包装乘法运算 | Wrapping multiplication operation | | nand | Boolean与非运算 | Boolean NAND operation | | neg | 加法逆运算 | Additive inverse operation | | nor | Boolean或非运算 | Boolean NOR operation | | not | 非运算 | NOT operation | | or | 或运算 | OR Operation | | pow | 幂运算 | Exponentiation operation | | pow_wrapped | 包装幂运算 | Wrapping exponentiation operation | | rem | 余数运算 | Remainder operation | | rem_wrapped | 包装余数运算 | Wrapping remainder operation | | shl | 左移操作 | Shift left operation | | shl_wrapped | 包装左移操作 | Wrapping shift left operation | | shr | 右移操作 | Shift right operation | | shr_wrapped | 包装右移操作 | Wrapping shift right operation | | square_root | 平方根运算 | Square root operation | | square | 平方运算 | Square operation| | sub | 减法运算 | Subtraction operation| | sub_wrapped | 包装减法运算 | Wrapping subtraction operation| | ternary | 三元选择运算 | Ternary select operation| | xor | 异或运算 | XOR operation| 加密运算 ![](https://hackmd.io/_uploads/rkI_SvDih.jpg) 规范 以下是 Leo 编译器中每个运算符的规范。 **abs** ```php= let a: i8 = -1i8; let b: i8 = a.abs(); // 1i8 ``` 计算输入的绝对值,检查溢出,将结果存储在目标中。 对于整数类型,添加约束来检查下溢。对于需要包装语义的情况,请参阅abs_wrapped指令。当输入是有符号整数类型的最小值时,会发生这种下溢。例如,abs -128i8会导致下溢,因为128不能表示为i8. 支持的类型 ![](https://hackmd.io/_uploads/SyomUwDj3.jpg) **abs_wrapped** ```php= let a: i8 = -128i8; let b: i8 = a.abs_wrapped(); // -128i8 ``` 计算输入的绝对值,在类型边界处环绕,并将结果存储在目标中。 支持的类型 ![](https://hackmd.io/_uploads/BJtDUvDj3.jpg) **add** ```php= let a: u8 = 1u8; let b: u8 = a + 1u8; // 2u8 let c: u8 = b.add(1u8); // 3u8 ``` first与相加second,将结果存储在destination 。 对于整数类型,添加约束来检查溢出。对于整数类型需要包装语义的情况,请参阅add_wrapped指令。 支持的类型 ![](https://hackmd.io/_uploads/rkPnIwwon.jpg) **add_wrapped** ```php= let a: u8 = 255u8; let b: u8 = a.add_wrapped(1u8); // 0u8 ``` 将first与second相加,在类型的边界处环绕,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/rkPnIwwon.jpg) **and** ```php= let a: i8 = 1i8 & 1i8; // 1i8 let b: i8 = 1i8.and(2i8); // 0i8 ``` first对整数(按位)或布尔值and执行 AND 运算second,并将结果存储在 中destination。 支持的类型 ![](https://hackmd.io/_uploads/rykl_DDo2.jpg) **assert** ```php= let a: bool = true; let b: bool = false; assert(a); // will not halt assert(b); // program halts ``` 检查表达式的计算结果是否为true布尔值,如果计算结果为false则停止。 支持的类型 | 表达式 | | -------- | | 布尔 | **assert_eq** ```php= let a: u8 = 1u8; let b: u8 = 2u8; assert_eq(a, a); // will not halt assert_eq(a, b); // program halts ``` 检查First和Second是否相等,如果不相等则停止。 支持的类型 ![](https://hackmd.io/_uploads/S1BcODwjh.jpg) **assert_neq** ```php= let a: u8 = 1u8; let b: u8 = 2u8; assert_neq(a, b); // will not halt assert_neq(a, a); // program halts ``` 检查First和Second是否不相等,如果相等则停止。 支持的类型 ![](https://hackmd.io/_uploads/r1aCuwPjn.jpg) **block.height** ```php= transition matches(height: u32) { return then finalize(height); } finalize matches(height: u32) { assert_eq(height, block.height); } ``` 该block.height运算符用于在 Leo 程序中获取最新的块高度。它代表链中区块的数量。在上面的示例中,在上下文block.height中使用finalize来获取程序中的最新块高度。 注意 * 该block.height运算符只能在finalize上下文中使用。在上下文之外使用它finalize会导致编译错误。 * 该block.height运算符不带任何参数。 **div** ```php= let a: u8 = 4u8; let b: u8 = a / 2u8; // 2u8 let c: u8 = b.div(2u8); // 1u8 ``` 将第一个操作数除以第二个操作数,并将结果存储在目标中。如果尝试除以零,则操作将停止。 对于整数类型,此运算执行截断除法。无论操作数的符号如何,截断除法都会向零舍入。这意味着它会截掉小数点后的所有数字,留下小于或等于结果的最大整数。 例如: 1. 7 / 3 yields 2, not 2.3333. 2. -7 / 3 yields -2, not -2.3333. 如果出现下溢,操作将停止。将有符号整数类型的最小值除以 -1 时会发生下溢。例如,-128i8 / -1i8会导致下溢,因为 128 不能表示为i8. 对于字段类型,任何字段值的划分a / b都是明确定义的a,b但当 时除外b = 0field。 对于整数类型需要包装语义的情况,请参阅div_wrapped指令。 支持的类型 ![](https://hackmd.io/_uploads/SkZttDvin.jpg) **div_wrapped** ```php= let a: i8 = -128i8; let b: i8 = a.div_wrapped(-1i8); // -128i8 ``` 将First除以Second,在类型的边界处环绕,并将结果存储在目标中。 支持的类型 ![](https://hackmd.io/_uploads/rkIRYPvo3.jpg) **double** ```php= let a: group = (0, 4)group; let b: group = a.double(); ``` Doubles输入,将结果存储在destination中。 支持的类型 | Input | Destination | | ----- | ----------- | | Field | Field | | Group | Group | **gt** ```php= let a: bool = 2u8 > 1u8; // true let b: bool = 1u8.gt(1u8); // false ``` 检查first是否大于second,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/SJlx3wvo2.png) **gte** ```php= let a: bool = 2u8 >= 1u8; // true let b: bool = 1u8.gte(1u8); // true ``` 检查first是否大于或者等于second,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/SJlx3wvo2.png) **inv** ```php= let a: field = 1field.inv(); ``` 计算输入的乘法逆元,将结果存储在destination中。 支持的类型 | Input | Destination | | -------- | -------- | | Field | Field | **eq** ```php= let a: bool = 1u8 == 1u8; // true let b: bool = 1u8.eq(2u8); // false ``` 比较first和second,将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/BkRi3Pwi3.png) **neq** ```php= let a: bool = 1u8 != 1u8; // false let b: bool = 1u8.neq(2u8); // true ``` first如果不等于second则返回true ,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/H1DlpDDsh.png) **lt** ```php= let a: bool = 1u8 < 2u8; // true let b: bool = 1u8.lt(1u8); // false ``` 检查是否first小于second,将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/B1UB6vPi2.png) **lte** ```php= let a: bool = 1u8 <= 2u8; // true let b: bool = 1u8.lte(1u8); // true ``` 检查是否first小于或等于second,将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/B1UB6vPi2.png) **mod** ```php= let a: u8 = 3u8.mod(2u8); // 1u8 ``` 取first相对于second的模数,并将结果存储在中destination中。如果second为零,则停止。 此操作的语义与模操作的数学定义一致。 支持的类型 ![](https://hackmd.io/_uploads/SJA0aDPjn.png) **mul** ```php= let a: u8 = 2u8 * 2u8; // 4u8 let b: u8 = a.mul(2u8); // 8u8 ``` first与相乘second,将结果存储在 中destination。 对于整数类型,添加约束来检查上溢/下溢。对于整数类型需要包装语义的情况,请参阅mul_wrapped指令。 支持的类型 ![](https://hackmd.io/_uploads/Hyc-AvPoh.png) **mul_wrapped** ```php= let a: u8 = 128u8.mul_wrapped(2u8); // 0u8 ``` first与相乘second,在类型边界处环绕,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/r1DtCwDih.png) **nand** ```php= let a: bool = true.nand(false); // true ``` first仅当和second为true时才返回false ,并将结果存储在destination。 支持的类型 | First | Second | Destination | | -------- | -------- | -------- | | Boolean | Boolean | Boolean | **neg** ```php= let a: i8 = -1i8.neg(); // 1i8 ``` 求反first,将结果存储在destination. 对于有符号整数类型,调用neg最小值是无效操作。例如,输入-128i8无效,因为128无法表示为i8. 支持的类型 ![](https://hackmd.io/_uploads/SJc71dvih.png) **nor** ```php= let a: bool = false.nor(false); // true ``` 当first和second都不为真时,返回true,并将结果存储在destination中。 支持的类型 | First | Second | Destination | | -------- | -------- | -------- | | Boolean | Boolean | Boolean | **not** ```php= let a: bool = true.not(); // false ``` 对整数(按位)或布尔输入执行 NOT 运算,将结果存储在destination. 支持的类型 ![](https://hackmd.io/_uploads/SJBIfuPsh.png) **or** ```php= let a: bool = true || false; // true let b: bool = false.or(false); // false ``` 对整数(位运算)或布尔值的first和second执行 OR 运算,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/Sk6KXdwsh.png) **pow** ```php= let a: u8 = 2u8 ** 2u8; // 4u8 let b: u8 = a.pow(2u8); // 16u8 ``` first求second的次方,将结果存储在destination。 对于整数类型,添加约束来检查上溢/下溢。对于整数类型需要包装语义的情况,请参阅pow_wrapped指令。 支持的类型 ![](https://hackmd.io/_uploads/BkvfVdPih.png) **pow_wrapped** ```php= let a: u8 = 16u8.pow_wrapped(2u8); // 0u8 ``` first求second的次方,在类型边界处环绕,将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/BkvfVdPih.png) **rem** ```php= let a: u8 = 3u8 % 2u8; // 1u8 let b: u8 = 4u8.rem(2u8); // 0u8 ``` 计算first除以second的截断余数,并将结果存储在destination。除以零时停止。 添加约束来检查下溢。当关联的除法运算div下溢时,就会发生这种下溢。 对于整数类型需要包装语义的情况,请参阅rem_wrapped指令。 支持的类型 ![](https://hackmd.io/_uploads/Syl24_Pon.png) **rem_wrapped** ```php= let a: i8 = -128i8; let b: i8 = a.rem_wrapped(-1i8); // 0i8 ``` 计算first除以second的截断余数,在类型边界处环绕,并将结果存储在目标中。 支持的类型 ![](https://hackmd.io/_uploads/Syl24_Pon.png) **shl** ```php= let a: u8 = 1u8 << 1u8; // 2u8 let b: u8 = a.shl(1u8); // 4u8 ``` 将first向左移动second位,并将结果存储在中destination。 支持的类型 ![](https://hackmd.io/_uploads/Bk3UHOvo2.png) **shl_wrapped** ```php= let a: u8 = 128u8.shl_wrapped(1u8); // 0u8 ``` 将first向左移动second位,在类型边界处环绕,将结果存储在destination. 支持的类型 ![](https://hackmd.io/_uploads/Bk3UHOvo2.png) **shr** ```php= let a: u8 = 4u8 >> 1u8; // 2u8 let b: u8 = a.shr(1u8); // 1u8 ``` 将first向右移动second位,并将结果存储在中destination。 支持的类型 ![](https://hackmd.io/_uploads/Bk3UHOvo2.png) **shr_wrapped** ```php= let a: u8 = 128u8.shr_wrapped(7u8); // 1u8 ``` 将first向右移动second位,在类型边界处环绕,将结果存储在destination. 支持的类型 ![](https://hackmd.io/_uploads/Bk3UHOvo2.png) **square** ```php= let a: field = 1field.square(); // 1field ``` 对输入求平方,将结果存储在destination。 支持的类型 | Input | Destination | | -------- | -------- | | Field | Field | **square_root** ```php= let a: field = 1field.square_root(); // 1field ``` 计算输入的平方根,将结果存储在destination。 支持的类型 | Input | Destination | | -------- | -------- | | Field | Field | **sub** ```php= let a: u8 = 2u8 - 1u8; // 1u8 let b: u8 = a.sub(1u8); // 0u8 ``` 计算first - second,将结果存储在destination. 支持的类型 ![](https://hackmd.io/_uploads/BkqTUdPj3.png) **sub_wrapped** ```php= let a: u8 = 0u8.sub_wrapped(1u8); // 255u8 ``` 计算first - second,在类型边界处环绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/BkqTUdPj3.png) **ternary** ```php= let a: u8 = true ? 1u8 : 2u8; // 1u8 ``` 如果条件为真,则选择first,否则选择second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/B14BDOPsh.png) **xor** ```php= let a: bool = true.xor(false); // true ``` 对整数(位运算)或布尔值的first和second执行 XOR 运算,并将结果存储在destination中。 支持的类型 ![](https://hackmd.io/_uploads/ryrYPdws3.png) **group::GEN** ```php= let g: group = group::GEN; // the group generator ``` 返回该group类型所组成的代数群的生成器。 Leo 的编译基于一条椭圆曲线,其点形成一个群,并基于该曲线上的指定点生成一个子群,其元素形成类型group。 这是一个常数,而不是一个函数。因此,它不需要输入,只返回输出。 它是一个关联常量,其名称为GEN,关联类型为group。 支持的类型 | Destination| | -------- | | Group | **ChaCha::rand_DESTINATION** ```php= let result: address = ChaCha::rand_address(); let result: bool = ChaCha::rand_bool(); let result: field = ChaCha::rand_field(); let result: group = ChaCha::rand_group(); let result: i8 = ChaCha::rand_i8(); let result: i16 = ChaCha::rand_i16(); let result: i32 = ChaCha::rand_i32(); let result: i64 = ChaCha::rand_i64(); let result: i128 = ChaCha::rand_i128(); let result: u8 = ChaCha::rand_u8(); let result: u16 = ChaCha::rand_u16(); let result: u32 = ChaCha::rand_u32(); let result: u64 = ChaCha::rand_u64(); let result: u128 = ChaCha::rand_u128(); let result: scalar = ChaCha::rand_scalar(); ``` 返回目标类型的随机值。必须在finalize上下文中使用 支持的类型 ![](https://hackmd.io/_uploads/rJS0I9vj2.png) **BHP256::commit_to_DESTINATION** ```php= let salt: scalar = ChaCha::rand_scalar(); let a: address = BHP256::commit_to_address(1u8, salt); let b: field = BHP256::commit_to_field(2i64, salt); let c: group = BHP256::commit_to_group(1field, salt); ``` 计算 Bowe-Hopwood-Pedersen 承诺,first输入 256 位数据块,second输入一些随机值,并将承诺存储在destination。随机性应始终是Scalar,生成的承诺可以是Address、Field或Group。 如果给定的输入值小于 129 位,指令将停止运行。 支持的类型 ![](https://hackmd.io/_uploads/BJE5wcPin.png) **BHP512::commit_to_DESTINATION** ```php= let salt: scalar = ChaCha::rand_scalar(); let a: address = BHP512::commit_to_address(1u8, salt); let b: field = BHP512::commit_to_field(2i64, salt); let c: group = BHP512::commit_to_group(1field, salt); ``` 计算 Bowe-Hopwood-Pedersen 承诺,first输入 512 位数据块,second输入一些随机值,并将承诺存储在destination。随机性应始终是Scalar,生成的承诺可以是Group。 如果给定输入小于 171 位,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/HkAxd9Psn.png) **BHP768::commit_to_DESTINATION** ```php= let salt: scalar = ChaCha::rand_scalar(); let a: address = BHP768::commit_to_address(1u8, salt); let b: field = BHP768::commit_to_field(2i64, salt); let c: group = BHP768::commit_to_group(1field, salt); ``` 计算 Bowe-Hopwood-Pedersen 承诺,first输入 768 位数据块,second输入一些随机值,并将承诺存储在destination。随机性应始终是Scalar,生成的承诺可以是Group。 如果给定输入小于 129 位,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/H13lY9Doh.png) **BHP1024::commit_to_DESTINATION** ```php= let salt: scalar = ChaCha::rand_scalar(); let a: address = BHP1024::commit_to_address(1u8, salt); let b: field = BHP1024::commit_to_field(2i64, salt); let c: group = BHP1024::commit_to_group(1field, salt); ``` 计算 Bowe-Hopwood-Pedersen 承诺,first输入 1024 位数据块,second输入一些随机值,并将承诺存储在destination。随机性应始终是Scalar,生成的承诺可以是Group。 如果给定输入小于 171 位,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/S1tHt9vo2.png) **Pedersen64::commit_to_DESTINATION** ```php= let salt: scalar = ChaCha::rand_scalar(); let a: address = Pedersen64::commit_to_address(1u8, salt); let b: field = Pedersen64::commit_to_field(2i64, salt); let c: group = Pedersen64::commit_to_group(1field, salt); ``` 计算 Pedersen 承诺,first是 64 位输入值,second是一些随机值,并将承诺值存储在目destination。随机性应始终是Scalar,生成的承诺可以是Group。 如果给定的 Struct 值超过 64 位限制,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/rk1ljqwo2.png) **Pedersen128::commit_to_DESTINATION** ```php= let salt: scalar = ChaCha::rand_scalar(); let a: address = Pedersen64::commit_to_address(1u8, salt); let b: field = Pedersen64::commit_to_field(2i64, salt); let c: group = Pedersen64::commit_to_group(1field, salt); ``` 计算 Pedersen 承诺,first是 128 位输入值,second是一些随机值,并将承诺值存储在目destination。随机性应始终是Scalar,生成的承诺可以是Group。 如果给定的 Struct 值超过 128 位限制,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/Hy0Xo5Din.png) **BHP256::hash_to_DESTINATION** ```php= let result: address = BHP256::hash_to_address(1u8); let result: field = BHP256::hash_to_field(2i64); let result: group = BHP256::hash_to_group(1field); let result: scalar = BHP256::hash_to_scalar(1field); let result: i8 = BHP256::hash_to_i8(1field); let result: i16 = BHP256::hash_to_i16(1field); let result: i32 = BHP256::hash_to_i32(1field); let result: i64 = BHP256::hash_to_i64(1field); let result: i128 = BHP256::hash_to_i128(1field); let result: u8 = BHP256::hash_to_u8(1field); let result: u16 = BHP256::hash_to_u16(1field); let result: u32 = BHP256::hash_to_u32(1field); let result: u64 = BHP256::hash_to_u64(1field); let result: u128 = BHP256::hash_to_u128(1field); ``` 根据first中输入的 256 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 如果给定的输入值小于 129 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/BJRynqPi3.png) **BHP512::hash_to_DESTINATION** ```php= let result: address = BHP512::hash_to_address(1u8); let result: field = BHP512::hash_to_field(2i64); let result: group = BHP512::hash_to_group(1field); let result: scalar = BHP512::hash_to_scalar(1field); let result: i8 = BHP512::hash_to_i8(1field); let result: i16 = BHP512::hash_to_i16(1field); let result: i32 = BHP512::hash_to_i32(1field); let result: i64 = BHP512::hash_to_i64(1field); let result: i128 = BHP512::hash_to_i128(1field); let result: u8 = BHP512::hash_to_u8(1field); let result: u16 = BHP512::hash_to_u16(1field); let result: u32 = BHP512::hash_to_u32(1field); let result: u64 = BHP512::hash_to_u64(1field); let result: u128 = BHP512::hash_to_u128(1field); ``` 根据first中输入的 512 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 如果给定的输入值小于 171 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/B1wShqwo2.png) **BHP768::hash_to_DESTINATION** ```php= let result: address = BHP768::hash_to_address(1u8); let result: field = BHP768::hash_to_field(2i64); let result: group = BHP768::hash_to_group(1field); let result: scalar = BHP768::hash_to_scalar(1field); let result: i8 = BHP768::hash_to_i8(1field); let result: i16 = BHP768::hash_to_i16(1field); let result: i32 = BHP768::hash_to_i32(1field); let result: i64 = BHP768::hash_to_i64(1field); let result: i128 = BHP768::hash_to_i128(1field); let result: u8 = BHP768::hash_to_u8(1field); let result: u16 = BHP768::hash_to_u16(1field); let result: u32 = BHP768::hash_to_u32(1field); let result: u64 = BHP768::hash_to_u64(1field); let result: u128 = BHP768::hash_to_u128(1field); ``` 根据first中输入的 768 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 如果给定的输入值小于 129 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/B1Atn5wo2.png) **BHP1024::hash_to_DESTINATION** ```php= let result: address = BHP1024::hash_to_address(1u8); let result: field = BHP1024::hash_to_field(2i64); let result: group = BHP1024::hash_to_group(1field); let result: scalar = BHP1024::hash_to_scalar(1field); let result: i8 = BHP1024::hash_to_i8(1field); let result: i16 = BHP1024::hash_to_i16(1field); let result: i32 = BHP1024::hash_to_i32(1field); let result: i64 = BHP1024::hash_to_i64(1field); let result: i128 = BHP1024::hash_to_i128(1field); let result: u8 = BHP1024::hash_to_u8(1field); let result: u16 = BHP1024::hash_to_u16(1field); let result: u32 = BHP1024::hash_to_u32(1field); let result: u64 = BHP1024::hash_to_u64(1field); let result: u128 = BHP1024::hash_to_u128(1field); ``` 根据first中输入的 1024 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 如果给定的输入值小于 171 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/rJsJT5wo3.png) **Pedersen64::hash_to_DESTINATION** ```php= let result: address = Pedersen64::hash_to_address(1u8); let result: field = Pedersen64::hash_to_field(2i64); let result: group = Pedersen64::hash_to_group(1field); let result: scalar = Pedersen64::hash_to_scalar(1field); let result: i8 = Pedersen64::hash_to_i8(1field); let result: i16 = Pedersen64::hash_to_i16(1field); let result: i32 = Pedersen64::hash_to_i32(1field); let result: i64 = Pedersen64::hash_to_i64(1field); let result: i128 = Pedersen64::hash_to_i128(1field); let result: u8 = Pedersen64::hash_to_u8(1field); let result: u16 = Pedersen64::hash_to_u16(1field); let result: u32 = Pedersen64::hash_to_u32(1field); let result: u64 = Pedersen64::hash_to_u64(1field); let result: u128 = Pedersen64::hash_to_u128(1field); ``` 根据 first 中的 64 位输入值计算 Pedersen 哈希值,并将哈希值存储在目标值中。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field, Group, or Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 如果给定的 Struct 值超过 64 位限制,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/rJLjpqDi2.png) **Pedersen128::hash_to_DESTINATION** ```php= let result: address = Pedersen128::hash_to_address(1u8); let result: field = Pedersen128::hash_to_field(2i64); let result: group = Pedersen128::hash_to_group(1field); let result: scalar = Pedersen128::hash_to_scalar(1field); let result: i8 = Pedersen128::hash_to_i8(1field); let result: i16 = Pedersen128::hash_to_i16(1field); let result: i32 = Pedersen128::hash_to_i32(1field); let result: i64 = Pedersen128::hash_to_i64(1field); let result: i128 = Pedersen128::hash_to_i128(1field); let result: u8 = Pedersen128::hash_to_u8(1field); let result: u16 = Pedersen128::hash_to_u16(1field); let result: u32 = Pedersen128::hash_to_u32(1field); let result: u64 = Pedersen128::hash_to_u64(1field); let result: u128 = Pedersen128::hash_to_u128(1field); ``` 根据 first 中的 128 位输入值计算 Pedersen 哈希值,并将哈希值存储在目标值中。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field, Group, or Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 如果给定的 Struct 值超过 64 位限制,指令将停止执行。 ![](https://hackmd.io/_uploads/SyO-A5vo2.png) ==原文写的是64,不知道是不是写错了?== 支持的类型 ![](https://hackmd.io/_uploads/HkxI0qDsn.png) **Poseidon2::hash_to_DESTINATION** ```php= let result: address = Poseidon2::hash_to_address(1u8); let result: field = Poseidon2::hash_to_field(2i64); let result: group = Poseidon2::hash_to_group(1field); let result: scalar = Poseidon2::hash_to_scalar(1field); let result: i8 = Poseidon2::hash_to_i8(1field); let result: i16 = Poseidon2::hash_to_i16(1field); let result: i32 = Poseidon2::hash_to_i32(1field); let result: i64 = Poseidon2::hash_to_i64(1field); let result: i128 = Poseidon2::hash_to_i128(1field); let result: u8 = Poseidon2::hash_to_u8(1field); let result: u16 = Poseidon2::hash_to_u16(1field); let result: u32 = Poseidon2::hash_to_u32(1field); let result: u64 = Poseidon2::hash_to_u64(1field); let result: u128 = Poseidon2::hash_to_u128(1field); ``` 根据 first 中的输入,以 2 的输入率计算 Pedersen 哈希值,并将哈希值存储在destination中。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field, Group, or Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 支持的类型 ![](https://hackmd.io/_uploads/SkkRR9Dj3.png) **Poseidon4::hash_to_DESTINATION** ```php= let result: address = Poseidon4::hash_to_address(1u8); let result: field = Poseidon4::hash_to_field(2i64); let result: group = Poseidon4::hash_to_group(1field); let result: scalar = Poseidon4::hash_to_scalar(1field); let result: i8 = Poseidon4::hash_to_i8(1field); let result: i16 = Poseidon4::hash_to_i16(1field); let result: i32 = Poseidon4::hash_to_i32(1field); let result: i64 = Poseidon4::hash_to_i64(1field); let result: i128 = Poseidon4::hash_to_i128(1field); let result: u8 = Poseidon4::hash_to_u8(1field); let result: u16 = Poseidon4::hash_to_u16(1field); let result: u32 = Poseidon4::hash_to_u32(1field); let result: u64 = Poseidon4::hash_to_u64(1field); let result: u128 = Poseidon4::hash_to_u128(1field); ``` 根据 first 中的输入,以 4 的输入率计算 Pedersen 哈希值,并将哈希值存储在destination中。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field, Group, or Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 支持的类型 ![](https://hackmd.io/_uploads/HyiWksDih.png) **Poseidon8::hash_to_DESTINATION** ```php= let result: address = Poseidon8::hash_to_address(1u8); let result: field = Poseidon8::hash_to_field(2i64); let result: group = Poseidon8::hash_to_group(1field); let result: scalar = Poseidon8::hash_to_scalar(1field); let result: i8 = Poseidon8::hash_to_i8(1field); let result: i16 = Poseidon8::hash_to_i16(1field); let result: i32 = Poseidon8::hash_to_i32(1field); let result: i64 = Poseidon8::hash_to_i64(1field); let result: i128 = Poseidon8::hash_to_i128(1field); let result: u8 = Poseidon8::hash_to_u8(1field); let result: u16 = Poseidon8::hash_to_u16(1field); let result: u32 = Poseidon8::hash_to_u32(1field); let result: u64 = Poseidon8::hash_to_u64(1field); let result: u128 = Poseidon8::hash_to_u128(1field); ``` 根据 first 中的输入,以 8 的输入率计算 Pedersen 哈希值,并将哈希值存储在destination中。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field, Group, or Scalar)或Address,由函数末尾的 hash_too_DESTINATION 指定。 支持的类型 ![](https://hackmd.io/_uploads/H17S1jDin.png) ## 命令行 Leo 命令行界面 Leo CLI 是一个命令行界面工具,配备了 Leo 编译器。 安装 [安装Leo](https://hackmd.io/@maxsayss/BJ9kjZIjh#安装) * -d, --debug - Enables debugging mode * -h, --help - Prints help information * -V, --version - Prints version information 命令列表 :::info :information_source: 您可以通过运行打印命令列表leo --help ::: * new- 在新目录中创建新的 Leo 包。 * build- 将当前包编译为程序。 * run- 使用输入变量运行程序。 * execute- 使用输入变量执行程序。 * clean- 清理输出目录。 * update- 更新到最新版本的 Leo。 **leo new** 要创建新包,请运行: ```php= leo new {$NAME} ``` 有效的包名称为snake_case:小写字母和数字,下划线分隔。此命令将使用给定的包名称创建一个新目录。新包的目录结构如下: ```php= package/ ├── .env # Your program environment ├── program.json # Your program manifest ├── README.md # Your program description ├── build/ ├── inputs/ │ ├── hello.in # Your program inputs └── src/ └── main.leo # Your program file ``` **leo build** :::info :information_source: 从 Leo 开始,此命令已被弃用v1.9.0。它将在未来版本中删除。 ::: 要将程序编译为 Aleo 指令并验证其是否正确构建,请运行: ```php= leo build ``` 这将使用 Aleo 指令文件填充目录build/(如果不存在则创建它).aleo。 控制台输出: ```php= Leo ✅ Compiled 'main.leo' into Aleo instructions ``` **leo run** :::info :information_source: 在执行程序之前使用此命令来运行程序。 ::: 使用程序输入文件中的输入运行 Leo 转换函数.in。 ```php= leo run {$TRANSITION} ``` 使用命令行输入运行 Leo 转换函数。 {$INPUTS}应该是程序输入的列表,以空格分隔。 ```php= leo run {$TRANSITION} {$INPUTS} ``` 该命令不综合程序电路或生成证明和验证密钥。 控制台输出: ```php= Leo ✅ Compiled 'main.leo' into Aleo instructions ⛓ Constraints • 'hello.aleo/main' - 35 constraints (called 1 time) ➡️ Output • 3u32 Leo ✅ Finished 'hello.aleo/main' (in "/hello/build") ``` **leo execute** :::info :information_source: 使用此命令执行您的程序并生成交易对象。 ::: 使用程序输入文件中的输入执行 Leo 转换函数 .in。 ```php= leo execute {$TRANSITION} ``` 使用命令行输入执行 Leo 转换函数。 {$INPUTS}应该是程序输入的列表,以空格分隔。 ```php= leo execute {$TRANSITION} {$INPUTS} ``` 该命令综合程序电路并生成证明和验证密钥。 控制台输出: ```php= Leo ✅ Compiled 'main.leo' into Aleo instructions ⛓ Constraints • 'hello.aleo/main' - 35 constraints (called 1 time) ➡️ Output • 3u32 {"type":"execute","id":"at1 ... (transaction object truncated for brevity) Leo ✅ Executed 'hello.aleo/main' (in "/hello/build") ``` **leo clean** 要清理构建目录,请运行: ```php= leo clean ``` 控制台输出: ```php= Leo cleaned the build directory (in "/build/") ``` **leo update** 要下载并安装最新的 Leo 版本,请运行: ```php= leo update ``` 控制台输出: ```php= Checking target-arch... x86_64-apple-darwin Checking current version... v1.8.3 Checking latest released version... v1.8.3 Updating Leo is on the latest version 1.9.0 ``` ## 工具 Leo 的工具 Aleo 维护跨不同平台的语法突出显示实现。 如果您在此列表中没有看到您最喜欢的编辑器,请访问GitHub。 * Sublime Text * Visual Studio Code * Intellij **Sublime Text** 在此处下载编辑器: https: //www.sublimetext.com/download。Leo 对 Sublime 的 LSP 插件的支持是通过语言服务器提供的。 安装 * 从 Package Control安装LSP和LSP-Leo 。 * 重新启动 Sublime。 使用 按照以下步骤切换Leo语法突出显示、悬停和标记。 * 打开Sublime Text。 * 从首选项 > 选择配色方案... > LSP-aleo-developer **VS Code** 在此处下载编辑器: https: //code.visualstudio.com/download。 安装 * 从 VS Code 市场安装Leo for VS Code 。 * 正确的扩展 ID 是aleohq.leo-extension. 使用 * 打开VS Code。 * 从首选项 > 颜色主题... > Aleo 主题 **Intellij** 在此处下载编辑器: https: //www.jetbrains.com/idea/download/。 安装 从 JetBrains 市场安装Leo for Intellij 。 ## 开发者资源 1. Aleo Workshop仓库 在 Aleo 上构建应用程序的入门指南 https://github.com/AleoHQ/workshop 2. The Awesome Aleo仓库 Aleo & Leo 代码和资源的精选列表 https://github.com/howardwu/awesome-aleo 3. Discord社区 在 #aleo-language 频道中分享您正在构建的内容 https://discord.gg/aleohq **Style指南** 本指南旨在为开发人员在编写 Leo 代码时指明正确的方向。有许多 Leo 语言及其生成的电路所特有的约定。 本指南是一份动态文件。随着新的 Leo 编程约定的出现和旧的约定的过时,本指南应该反映这些变化。欢迎在此添加您的意见和建议。 **代码格式** **缩进** 每级缩进 4 个空格。 **空行** 单个空行应分隔作用域中的顶级声明program,即transition、function、struct、record和mapping声明。多个导入可以选择用一个空行分隔;文件顶部的最后一个导入后面应该有一个空行。 **Yes:** ```php= import std.io.Write; import std.math.Add; program prog.aleo { struct A { // ... } function foo() { // ... } } ``` **==No:==** ```php= import std.io.Write; import std.math.Add; program prog.aleo { struct A { // ... } function foo() { // ... } } ``` **命名约定** ![](https://hackmd.io/_uploads/HkePnpY_s2.jpg) ```php= ### Layout Leo file elements should be ordered: 1. Imports 2. Program declaration 3. Mappings 4. Records + Structs 5. Functions + Transitions ### Braces Opening braces always go on the same line. ```leo struct A { // ... } transition main() { // ... } let a: A = A { }; ``` **分号** 包括语句在内的每个语句return都应以分号结尾。 ```php= let a: u32 = 1u32; let b: u32 = a + 5u32; b *= 2u32; return b ``` **逗号** 每当结束分隔符出现在单独的行上时,都应包含尾随逗号。 ```php= let a: A = A { x: 0, y: 1 }; let a: A = A { x: 0, y: 1, }; ``` **常见模式** 根据Style指南,这里列出了 Leo 开发人员可能遇到的常见模式以及推荐的代码解决方案。 **条件分支** Leo 中的条件if else最好使用三元? :表达式。 示例: ```php= if (condition) { return a } else { return b } ``` 倾向于: ```php= return condition ? a : b ``` **==为什么?==** 三元表达式是最便宜的条件形式。我们可以在评估条件之前解析第一个表达式和第二个表达式的值。这很容易转换成电路,因为我们知道每个表达式不依赖于后面语句中的信息。 在原始版本中Example,我们无法在评估条件之前解析返回语句。作为解决方案,Leo 在电路中创建分支,以便可以评估两条路径。 branch 1, condition = true ```php= return a ``` branch 2, condition = false ```php= return b ``` condition当在证明时获取输入值时,我们选择电路的一个分支进行评估。请注意,该语句return a在两个分支中都重复出现。条件中每次计算的成本都会加倍。这大大增加了约束数量并减慢了电路速度。 **贡献** 感谢您帮助 Leo 变得更好! 在贡献之前,请查看[贡献者行为准则](https://github.com/AleoHQ/leo/blob/testnet3/CONTRIBUTING.md)。通过参与此项目(在问题、Pull请求或 Gitter 频道中),您同意遵守这些条款。 **报告问题** 要报告问题,请使用[GitHub 问题跟踪器](https://github.com/AleoHQ/leo/issues)。报告问题时,请提及以下详细信息: * 您使用的是哪个版本的 Leo。 * 源代码是什么(如果适用)。 * 您在哪个平台上运行。 * 如何重现该问题。 * 问题的结果是什么。 * 预期的行为是什么。 将导致问题的源代码减少到最低限度总是非常有帮助的,有时还可以澄清误解 **Pull请求** 首先分叉分支来testnet3进行更改。提交消息应清楚地解释您更改的原因和内容。 如果您在创建 fork 后需要从分支中提取任何更改testnet3(例如,为了解决潜在的合并冲突),请避免使用 git merge,而是使用 git rebase 您的分支。变基将帮助我们轻松审查您的更改。 **所需工具** 要从源代码构建 Leo,您将需要以下工具: * 最新的 Rust 稳定版本和夜间版本。 建议使用 rustup 安装多个版本。 * Cargo Rusty Hook install via <kbd>cargo install rusty-hook</kbd>. * Clippy 通过 rustup,如果您没有执行默认的 rustup install <kbd>rustup componenet add clippy</kbd>。 **格式化** 在创建 PR 之前,请执行以下操作。 * <kbd>cargo +nightly fmt --all</kbd> 将格式化您的所有代码。 * <kbd>cargo clippy --all-features --examples --all --benches</kbd> **测试** 如果您的代码添加了新功能,请编写测试以确认新功能按预期运行。有关如何编写测试的示例,请参阅现有测试。请阅读解析器测试部分。要运行测试,请使用以下命令:<kbd>cargo test --all --features ci_skip --no-fail-fast</kbd> 解析器测试 在仓库的根目录中,有一个“tests”目录。要添加解析器测试,请查看解析器子目录中的示例 Leo 文件。然后,在运行测试命令时,确保将环境变量<kbd>CLEAR_LEO_TEST_EXPECTATIONS</kbd>设置为 true。例如,在 UNIX 环境中,您可以运行以下命令<kbd>CLEAR_LEO_TEST_EXPECTATIONS=true cargo test --all --features ci_skip --no-fail-fast</kbd> **语法** 语法仓库中有一个<kbd>leo.abnf</kbd>文件,其中包含 ABNF 格式的Leo语法规则。如果您的更改会影响语法规则,我们可能会要求您修改该<kbd>.abnf</kbd>文件中的语法规则。 我们感谢您的辛勤工作! ## 语法备忘单 **1. 文件导入** ```php= import foo.leo; ``` **2. 程序** ```php= program hello.aleo { // code } ``` **3. 数据类型** ```php= let b: bool = false; // boolean let i: u8 = 1u8; // unsigned integer let a: field = 1field; // field element let g: group = 0group; // group element let s: scalar = 1scalar; // scalar element let receiver: address = aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4; // address ``` 类型转换 ```php= let a: u8 = 1u8; let b: u32 = a as u32; // cast 1u8 to 1u32 ``` 基本类型有:address, bool, field, group, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, scalar。我们可以在所有这些类型之间进行转换。 **4. 记录(Records)** ```php= record token { owner: address, microcredits: u64, amount: u64, } ``` **5. 结构体(Structs)** ```php= struct message { sender: address, object: u64, } ``` **6. 过渡(Transitions)** ```php= transition mint_public( public receiver: address, public amount: u64, ) -> token { /* Your code here */ } ``` **7. 函数(Functions)** ```php= function compute(a: u64, b: u64) -> u64 { return a + b; } ``` **8. 内联函数(Inline Functions)** ```php= inline foo( a: field, b: field, ) -> field { return a + b; } ``` :::info 函数的规则(传统意义上)如下: 函数有三种变体:transition, function, inline。 * 过渡(transition)只能调用函数(function)和内联(inline)。 * 函数(function)只能调用内联(inline)。 * 内联(inlines)只能调用内联(inlines)。 * 不允许直接/间接递归调用。 ::: **9. 循环** ```php= let count: u32 = 0u32; for i: u32 in 0u32..5u32 { count += 1u32; } ``` **10. 映射(Mappings)** ```php= mapping balances: address => u64; let contains_bal: bool = Mapping::contains(balances, receiver); let get_bal: u64 = Mapping::get(balances, receiver); let get_or_use_bal: u64 = Mapping::get_or_use(balances, receiver, 0u64); let set_bal: () = Mapping::set(balances, receiver, 100u64); let remove_bal: () = Mapping::remove(balances, receiver); ``` **11. 命令(Commands)** ```php= transition matches(height: u32) { return then finalize(height); } finalize matches(height: u32) { assert_eq(height, block.height); // block.height returns latest block height } let g: group = group::GEN; // the group generator let result: u32 = ChaCha::rand_u32(); // generate a random type `ChaCha::rand_<type>()` let owner: address = self.caller; // address of the program function caller let hash: field = BHP256::hash_to_field(1u32); // hash any type to any type let commit: group = Pedersen64::commit_to_group(1u64, 1scalar); // commit any type to a field, group, or address, using a scalar as blinding factor let a: bool = true; assert(a); // assert the value of a is true let a: u8 = 1u8; let b: u8 = 2u8; assert_eq(a, a); // assert a and b are equal assert_neq(a, b); // assert a and b are not equal ``` **12. 运算符** ```php= let sum: u64 = a + b; // arithmetic addition let diff: u64 = a - b; // arithmetic subtraction let prod: u64 = a * b; // arithmetic multiplication let quot: u64 = a / b; // arithmetic division let remainder: u64 = a % b; // arithmetic remainder let neg: u64 = -a; // negation let bitwise_and: u64 = a & b; // bitwise AND let bitwise_or: u64 = a | b; // bitwise OR let bitwise_xor: u64 = a ^ b; // bitwise XOR let bitwise_not: u64 = !a; // bitwise NOT let logical_and: bool = a && b; // logical AND let logical_or: bool = a || b; // logical OR let eq: bool = a == b; // equality let neq: bool = a != b; // non-Equality let lt: bool = a < b; // less than let lte: bool = a <= b; // less than or equal let gt: bool = a > b; // greater than let gte: bool = a >= b; // greater than or equal ``` # Leo例子 ## 拍卖 Aleo 的私下拍卖 **总结** 最高价格密封投标拍卖(或盲目拍卖)是一种拍卖类型,其中每个参与者在不知道其他参与者的出价的情况下提交出价。出价最高的投标人赢得拍卖。 在这个模型中,有两方:拍卖师和投标人。 * 投标人:拍卖的参与者。 * 拍卖师:负责进行拍卖的一方。 我们对此次拍卖做出以下假设: * 拍卖师很诚实。也就是说,拍卖师将按照收到的顺序解决所有出价。拍卖师不得篡改出价。 * 投标数量没有限制。 * 拍卖师知道所有竞拍者的身份,但竞拍者不一定知道其他竞拍者的身份。 在这个模型下,我们要求: * 投标人不会了解有关其他投标价值的任何信息。 **拍卖流程** 拍卖分一系列阶段进行。 * 投标:在投标阶段,投标人向拍卖师提交投标书。他们通过调用该place_bid函数来做到这一点。 * 决议:在决议阶段,拍卖师按照收到的顺序解决出价。拍卖师通过调用该resolve函数来做到这一点。解决过程产生一个单一的中标。 * 结束:在此阶段,拍卖师通过调用该finish函数来结束拍卖。此函数将中标结果返回给投标人,然后投标人可以用它来领取该物品。 **语言特点和概念** * record declarations * assert_eq * record ownership **1. 安装Leo** 遵循[Leo 安装说明](https://hackmd.io/@maxsayss/BJ9kjZIjh#安装) **2. 下载拍卖示例代码** 从[Github](https://github.com/AleoHQ/workshop/tree/master/auction)克隆拍卖示例的源代码。 **如何运行** **生成账户** 该program.json文件包含私钥和地址。这是将用于签署交易并检查记录所有权的帐户。当作为不同方执行程序时,请务必将private_key和address字段设置program.json为适当的值。请./run.sh参阅 示例,了解如何以不同方身份运行该程序。 前往[aleo.tools](https://aleo.tools/)生成新帐户。 Web 界面由[Aleo SDK](https://github.com/AleoHQ/sdk)提供支持,可以在本地运行。 使用输入文件 1. 使用所需的输入进行修改<kbd>inputs/auction.in</kbd> 2. 运行 ```php= leo run <function_name> ``` * Step 0: 初始化拍卖(Initializing the Auction) * Step 1: 第一次出价(The First Bid) * Step 2: 第二次出价(The Second Bid) * Step 3: 选择获胜者(Select the Winner) **第0步:初始化拍卖** 您可以使用提供的帐户或生成您自己的帐户 ```php= Bidder 1: Private Key APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK Address aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke Bidder 2: Private Key APrivateKey1zkpAFshdsj2EqQzXh5zHceDapFWVCwR6wMCJFfkLYRKupug Address aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 Auctioneer: Private Key APrivateKey1zkp5wvamYgK3WCAdpBQxZqQX8XnuN2u11Y6QprZTriVwZVc Address aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh ``` 将第一个投标人的私钥和地址交换到program.json。 ```php= { "program": "auction.aleo", "version": "0.0.0", "description": "A sealed bid auction", "development": { "private_key": "APrivateKey1zkpG9Af9z5Ha4ejVyMCqVFXRKknSm8L1ELEwcc4htk9YhVK", "address": "aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke" }, "license": "MIT" } ``` **第 1 步:第一次出价** 让第一个出价者出价 10。 ```php= // Returns a new bid. // - `bidder` : The address of the account that placed the bid. // - `amount` : The amount of the bid. // Requires that `bidder` matches the function caller. // The owner of the record is set to the entity responsible for running the auction (auction runner). // The address of the auction runner is aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh. transition place_bid(bidder: address, amount: u64) -> Bid { // Ensure the caller is the auction bidder. console.assert_eq(self.caller, bidder); // Return a new 'Bid' record for the auction bidder. return Bid { owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh, microcredits: 0u64, bidder: bidder, amount: amount, is_winner: false, }; } ``` 使用bidder 1 和 10u64 参数调用 place_bid 程序函数。 ```php= leo run place_bid aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke 10u64 ``` **第 2 步:第二次出价** 让第二个投标人出价 90。 将第二个投标人的私钥和地址交换到program.json。 使用bidder 2 和 90u64 参数调用 place_bid 程序函数。 ```php= leo run place_bid aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4 90u64 ``` **第 3 步:选择获胜者** 让拍卖师选出中标者。 ```php= // Returns the winning bid. // - `first` : The first bid. // - `second` : The second bid. // Requires that the function caller is the auction runner. // Assumes that the function is invoked only after the bidding period has ended. // In the event of a tie, the first bid is selected. transition resolve(first: Bid, second: Bid) -> Bid { // Ensure the caller is the auctioneer. assert_eq(self.caller, aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh); // Resolve the winner of the auction. if (first.amount >= second.amount) { return first; } else { return second; } } ``` 将拍卖师的私钥和地址交换到program.json。 提供两条Bid记录作为resolve过度函数的输入。 ```php= leo run resolve "{ owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, microcredits: 0u64.private, bidder: aleo1yzlta2q5h8t0fqe0v6dyh9mtv4aggd53fgzr068jvplqhvqsnvzq7pj2ke.private, amount: 10u64.private, is_winner: false.private, _nonce: 4668394794828730542675887906815309351994017139223602571716627453741502624516group.public }" "{ owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, microcredits: 0u64.private, bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, amount: 90u64.private, is_winner: false.private, _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public }" ``` **第4步:完成拍卖** 让拍卖师完成拍卖。 ```php= // Returns ownership of the bid to bidder. // - `bid` : The winning bid. // Requires that the function caller is the auction runner. // Assumes that the function is invoked only after all bids have been resolved. transition finish(bid: Bid) -> Bid { // Ensure the caller is the auctioneer. console.assert_eq(self.caller, aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh); // Return 'is_winner' as 'true' in the winning 'Bid'. return Bid { owner: bid.bidder, microcredits: bid.microcredits, bidder: bid.bidder, amount: bid.amount, is_winner: true, }; } ``` 使用Bid记录调用finish过度函数。 ```php= leo run finish "{ owner: aleo1fxs9s0w97lmkwlcmgn0z3nuxufdee5yck9wqrs0umevp7qs0sg9q5xxxzh.private, microcredits: 0u64.private, bidder: aleo1esqchvevwn7n5p84e735w4dtwt2hdtu4dpguwgwy94tsxm2p7qpqmlrta4.private, amount: 90u64.private, is_winner: false.private, _nonce: 5952811863753971450641238938606857357746712138665944763541786901326522216736group.public }" ``` ## [基本银行](https://github.com/AleoHQ/workshop/tree/master/basic_bank) ## [投票](https://github.com/AleoHQ/workshop/tree/master/vote) ## [代币](https://github.com/AleoHQ/workshop/tree/master/token) ## [Tic-Tac-Toe游戏](https://github.com/AleoHQ/workshop/tree/master/tictactoe) ## [战舰](https://github.com/AleoHQ/workshop/tree/master/battleship) # Aleo指令 ## 概述 Aleo 指令和 SnarkVM 欢迎来到 Aleo 说明指南。Aleo 指令是 Aleo 程序的中间表示。所有 Leo 程序都编译为 Aleo 指令,再编译为字节码。如果您的目标是细粒度电路设计,或者您正在实现一个以 Leo 以外的高级语言读取的编译器,并且希望您的程序在 Aleo 上运行,我们建议您学习和使用 Aleo 指令。 Aleo 程序是带有.aleo扩展名的文件。Aleo 程序包含 Aleo 指令 - 一种类似汇编的编程语言。Aleo 指令被编译为可由 Aleo 虚拟机执行的 AVM 操作码。 安装 SnarkVM 来编译和执行 Aleo 指令。 :::info :information_source: SnarkVM 目前正在积极开发中。请监控[GitHub](https://github.com/AleoHQ/snarkVM)上的代码库是否有可能发生重大更改。 ::: **安装SnarkVM** 继续安装以获取有关如何安装 SnarkVM 的信息。 **你好Aleo指令** 开发您的第一个<kbd>Hello Aleo</kbd> Aleo指令程序。 **Aleo指令指南** 了解Aleo 指令的核心概念和语法。 阅读受支持的AVM 操作码的完整列表。 **正式语言文档** 根据Aleo 指令语法检查您的程序或编译器实现。 研究正式ABNF 语法规范,以获得 Aleo 指令的完整正式语法。 **命令行界面文档** SnarkVM CLI 提供了一套命令,使 Aleo 指令中的编程变得简单。 * snarkvm new * snarkvm build (deprecated) * snarkvm run * snarkvm clean * snarkvm update 附加材料 为您最喜欢的代码编辑器安装Aleo指令。 ## 安装 **1. 安装前提** **1.1 安装Git** ```php= bit.ly/start-git ``` **1.2 安装Rust** ```php= bit.ly/start-rust ``` **1.3 检查** ```php= git --version cargo --version ``` **2.从源代码构建** 您可以从源代码构建并安装 SnarkVM,如下所示: ```python= # Download the source code git clone https://github.com/AleoHQ/snarkVM cd snarkvm # Build in release mode cargo install --path . ``` 这将生成可执行文件~/.cargo/bin/snarkvm。 现在要使用 SnarkVM CLI,请在终端中运行: ```php= snarkvm ``` :::info :information_source: 使用Hello Aleo深入研究一些代码。 ::: ## 你好Aleo **你好Aleo指令** **1. 创建并构建一个新项目** 要创建一个新项目,我们将使用该new命令。我们的项目 ```php= snarkvm new foo ``` 这将创建foo目录和具有项目基本结构的文件: * README.md具有 README 的框架,其中包含有关如何编译的说明。 * main.aleo源代码的主文件。 * program.json包含 JSON 格式的项目标识。特别是该程序的开发地址及其私钥。 main.aleo 文件应包含如下内容: ```php= // The 'foo.aleo' program. program foo.aleo; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; output r2 as u32.private; ``` 您可以使用以下命令运行程序snarkvm run,后跟要运行的函数名称及其输入参数: ```php= snarkvm run hello 2u32 3u32 ``` 您将看到如下输出: ```php= • Loaded universal setup (in 1478 ms) ⛓ Constraints • 'foo.aleo/hello' - 35 constraints (called 1 time) ➡️ Output • 5u32 ✅ Finished 'foo.aleo/hello' (in "/Users/collin/code/snarkVM/foo") ``` 可以看到,输出为 5u32 值,代表输入的总和。 **2. 执行程序** 您可以使用以下命令执行程序snarkvm execute,后跟要执行的函数名称及其输入参数: ```php= snarkvm execute hello 2u32 3u32 ``` 执行完成后,您应该看到以下输出: ```php= • Loaded universal setup (in 1478 ms) ⛓ Constraints • 'foo.aleo/hello' - 35 constraints (called 1 time) ➡️ Output • 5u32 {"type":"execute","id":"at1 ... (transaction object truncated for brevity) ✅ Executed 'foo.aleo/hello' (in "/Users/collin/code/snarkVM/foo") ``` 可以看到,输出为 5u32 值,代表输入的总和。 “通用设置”已加载到您的环境中。您可以在[Marlin 论文](https://eprint.iacr.org/2019/1047.pdf)中阅读更多相关内容。 通用设置准备就绪后,main.aleo文件中的每个函数都会构建,并在输出文件夹中生成: * hello.prover —— hello函数的证明者。 * hello.verifier —— hello函数的验证者。 * main.avm —— 将由虚拟机运行的 aleo 程序的字节码。 正如你已经猜到的,整个程序只有一个 .avm 文件,但每个函数都有一个证明者和验证者。 **3. 程序概述** 让我们检查一下 main.aleo 文件中的 foo 程序: ```php= // The 'foo.aleo' program. program foo.aleo; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; output r2 as u32.private; ``` 首先,我们需要声明程序如下: ```php= program foo.aleo; ``` 之后,我们可以开始编写它的函数(或其他 Aleo 结构,例如结构体[structs]、记录[records]、闭包[closures],我们稍后会看到)。 对于函数来说,我们很容易做到: ```php= function [function_name]: ``` 函数由三个主要部分组成: * 输入部分 这里我们声明它的输入参数: ```php= input r0 as u32.public; input r1 as u32.private; ``` Aleo 指令中的所有内容都声明/存储在一个寄存器中,寄存器具有类型(i8、字段、布尔等)和可见性选项(公共或私有),寄存器命名为 r0、r1、......、rn。 在本例中,我们使用 r0 和 r1 将按顺序传递给程序的输入存储为 u32 值,我们可以在其中存储 32 位无符号整数来执行求和运算。 * 指令部分 下一部分是函数的核心部分:在这里,我们调用所需的 Aleo 指令,使程序按照我们的要求运行。例如,执行加法运算 ```php= add r0 r1 into r2; ``` 每条 Aleo 指令后面都有特定类型的输入参数,其结果存储在 into 后指定的寄存器中。 您可以在这里找到所有可用的 Aleo 指令操作码。 * 输出部分 与输入部分类似,输出部分也用于程序的输出。它是函数的返回值。 ```php= output r2 as u32.private; ``` **4. 类型** Aleo 使用强类型语法。该语言支持 16 种基本类型,并允许用户定义自定义类型。 Aleo 原始类型包括: ```php= address boolean field group i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 scalar ``` 用户可以使用struct或record关键字定义自定义类型。我们将在接下来的几节中探讨这些内容。 4.1 寄存器(Registers) 寄存器是存储数据以便可以修改数据的地方。 4.2 结构体(Structs) 结构体是用户定义的数据结构。它们与传统编程语言中的传统结构体非常相似。您可以将结构体存储到寄存器中,就像存储其他 Aleo 数据类型一样。 例如,让我们建立一个结构体,代表一个由 3 个元素组成的固定大小数组。将其添加到 main.aleo 文件的底部: ```php= struct array3: a0 as u32; a1 as u32; a2 as u32; ``` 现在,让我们来编写一个函数,在寄存器中存储了一个数据类型为 array3 的寄存器的每个元素上加 1。 ```php= 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; ``` 如图所示,我们可以在寄存器 r0 中输入一个结构体,并使用<kbd> .</kbd>语法访问结构体元素。我们对每个元素执行 add 指令,将结果存储在寄存器 r1、r2 和 r3 中,最后使用 cast 指令在 r4 中创建一个新的 array3 结构。 现在,让我们运行它。在这种情况下,你唯一需要知道的是结构体是以如下格式传递给 cli 的: ```php= "{a0: 1u32, a1: 2u32, a2: 3u32}" ``` 现在我们可以执行 aleo run 命令了。我们将清理项目,以获取新代码: ```php= aleo clean && aleo run sum_one_to_array3 "{a0: 0u32, a1: 1u32, a2: 2u32}" ``` 输出结果是新的 array3 元素: ```php= ➡️ Output • { a0: 1u32, a1: 2u32, a2: 3u32 } ✅ Executed 'foo.aleo/sum_one_to_array3' (in "[...]/foo") ``` 4.3 记录(Records) 记录是一种基本数据结构,用于编码用户资产和应用程序状态。记录与结构体非常相似,但它们有两个非可选项: ```php= record token: owner as address.private microcredits as u64.private ``` owner是拥有该记录的 Aleo 地址,并且microcredits是该记录必须花费的积分数量。 记录很重要,因为它们代表了处理应用程序状态的基本 Aleo 结构。 运行 Aleo 函数时,只有属于应用程序地址的寄存器才能作为输入寄存器传递。否则,会引发错误并且应用程序不会运行。 您可以在文件中找到您的开发应用程序地址.env: ```php= { NETWORK=testnet3 PRIVATE_KEY=APrivateKey1zkpFsQNXJwdvjKs9bRsM91KcwJW1gW4CDtF3FJbgVBAvPds } ``` 4.4 Aleo状态 在 Aleo 中,应用程序的状态通过记录进行管理。Aleo 帐户可以创建交易来使用记录并在其位置生成新记录。Aleo 中的记录被加密到记录所有者地址,确保 Aleo 中的所有记录都是完全私密的。 **5. 你的第一个Aleo程序:转账** 考虑这个程序: ```php= // The 'foo.aleo' program. program foo.aleo; record token: owner as address.private; microcredits 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.microcredits 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; ``` 首先,我们定义自己的记录数据类型token,它具有必需的参数owner、可选的参数microcredits,以及一个用户定义参数名为amount,代表我们拥有的代币数量。 该transfer_amount函数接收 3 个输入参数(senderrecord、receiverrecord 和amount)并将它们存储在 3 个寄存器(r0、r1和r2)中。之后,它计算两者的最终余额并将其存储在r3和r4中(使用sub和add指令分别计算减法和加法)。使用这些最终金额,它为发送者和接收者创建输出记录,并将它们存储在r5和中r6。最后,两条记录都通过输出指令从函数中发送出去。 要运行此函数,第一个参数是程序的输入记录。该参数的格式与结构类型相同: ```php= { owner: aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09.private, microcredits: 0u64.private, amount: 50u64.private } ``` 在哪里: * owner:程序的公共地址,可在PRIVATE_KEY文件中找到.env。 * microcredits:记录拥有的积分数量。 * 其他参数:取决于程序本身(在本例中,我们使用的参数值为50)。 让我们运行该transfer_amount函数(如果您正在执行此操作,请记住使用在 program.json 中找到的地址作为所有者字段 ```php= aleo clean && aleo run transfer_amount "{ owner: aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09.private, microcredits: 0u64.private, amount: 50u64.private }" aleo1h3gu7fky36y8r7v2x9phc434fgf20g8qd7c7u45v269jfw6vmugqjegcvp 10u64 ``` 我们得到以下输出记录: ```php= 🚀 Executing 'foo.aleo/transfer_amount'... • Calling 'foo.aleo/transfer_amount'... • Executed 'transfer_amount' (in 3520 ms) ➡️ Outputs • { owner: aleo1x5nz5u4j50w482t5xtqc3jdwly9s8saaxlgjz0wvmuzmxv2l5q9qmypx09.private, microcredits: 0u64.private, amount: 40u64.private _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public } • { owner: aleo1h3gu7fky36y8r7v2x9phc434fgf20g8qd7c7u45v269jfw6vmugqjegcvp.private, microcredits: 0u64.private, amount: 10u64.private _nonce: 2323253577170856894742339369235137467208538700597121244293392765726742543235group.public } ✅ Executed 'foo.aleo/transfer_amount' (in "[...]/foo") ``` 就是这样。您已在 Aleo 中转移了您的第一个所有者定义的代币! **注意:_nonceAleo 说明书中没有写。编译器输出_nonce记录输出。用户在使用记录时需要将其作为输入提供。** ## 语言 Aleo 指令语言指南 **静态类型** Aleo指令是一种静态类型语言,这意味着我们在执行电路之前必须知道每个变量的类型。 **显式类型** Aleo 指令中没有undefined或null。分配新变量时,必须显式声明值的类型。 **值传递** Aleo 指令中的表达式始终按值传递,这意味着当它们用作函数输入或赋值右侧时,它们的值始终会被复制。 **寄存器** Aleo 指令中没有变量名称。所有变量都存储在表示的寄存器中,rX其中X是从 0 开始的非负整数r0, r1, r2, etc.。 **数据类型和值** **布尔值** Aleo 指令支持传统值true或false布尔值。boolean语句中布尔值的显式类型是必需的。 ```php= function main: input r0: boolean.private; ``` **整数** Aleo 指令支持有符号整数类型 i8、i16、i32、i64、i128 和无符号整数类型 u8、u16、u32、u64、u128。 ```php= function main: input r0: u8.public; ``` :::info :information_source: 较高位长度的整数会在电路中产生更多约束,这会减慢计算时间。 ::: **域(Field Elements)** Aleo 指令支持field椭圆曲线基域元素的类型。这些是基域模数以下的无符号整数。 ```php= function main: input r0: field.private; ``` **组(Group Elements)** 传入 Aleo 指令编译器的椭圆曲线上的仿射点集合构成一个组。Leo 支持由生成点生成的子群作为原始数据类型。组元素比较特殊,因为它们的值可以通过坐标对的 x 坐标来定义,例如 1group。在指定组值时,必须使用组类型关键字 group。 ```php= function main: input r0: group.private; ``` **标量(Scalar Elements)** Aleo 指令支持scalar椭圆曲线子组的标量场元素的类型。这些是标量域模数以下的无符号整数。 ```php= function main: input r0: scalar.private; ``` **地址(Addresses)** 定义地址是为了启用编译器优化的例程来解析和操作地址。 ```php= function main: input r0: address.private; ``` **Aleo程序的整体结构** Aleo 程序包含程序ID(Program ID)、导入(Imports)、函数(Functions)、闭包(Closures)、结构体(Struts)、记录(Records)、 映射(Mappings)和Finalize(最终化)的声明。仅对必须位于文件顶部的导入强制执行排序。声明可以在程序文件中本地访问。如果您需要来自另一个程序文件的声明,则必须导入它。 **程序ID(Program ID)** 程序 ID 声明为 {name}.{network}。name的第一个字符必须小写。name可以包含小写字母、数字和下划线。目前,==aleo是唯一支持<kbd>network</kbd>域==。 ```php= program hello.aleo; // valid program Foo.aleo; // invalid program baR.aleo; // invalid program 0foo.aleo; // invalid program 0_foo.aleo; // invalid program _foo.aleo; // invalid ``` **导入(Import)** 导入声明为import {ProgramID};. 导入通过程序 ID 获取其他声明并将它们带入当前文件范围。您可以导入下载到该imports目录的依赖项。 ```php= import foo.aleo; // Import the `foo.aleo` program into the `hello.aleo` program. program hello.aleo; ``` **函数(Function)** 函数输入声明为input {register} as {type}.{visibility};。 函数输入必须紧接在函数名称声明之后声明。 ```php= // The function `foo` takes a single input `r0` with type `field` and visibility `public`. function foo: input r0 as field.public; ``` 函数输入 函数输入声明为input {register} as {type}.{visibility};。 函数输入必须紧接在函数名称声明之后声明。 ```php= // The function `foo` takes a single input `r0` with type `field` and visibility `public`. function foo: input r0 as field.public; ``` 函数输出 函数输出被声明为output {register} as {type}.{visibility};. 函数输出必须在函数定义的末尾声明。 ```php= ... output r0 as field.public; ``` 调用函数 在 Aleo 协议中,调用函数会创建一个可以在链上消费和生成记录的transition(过度)。使用<kbd>aleo run</kbd>CLI 命令将输入传递给函数并执行程序。 在Testnet3中,程序函数不能调用其他内部程序函数。如果您想开发在程序内部调用的“辅助函数”,请尝试编写一个closure. 调用导入的函数 Aleo 程序可以使用该指令从外部调用其他 Aleo 程序<kbd>call {program}/{function} {register} into {register}</kbd> ```php= import foo.aleo; program bar.aleo; function call_external: input r0 as u64.private; call foo.aleo/baz r0 into r1; // Externally call function `baz` in foo.aleo with argument `r0` and store the result in `r1`. output r1; ``` **闭包(Closure)** 闭包被声明为closure {name}:. 闭包包含可以计算值的指令。闭包是无法直接执行的辅助函数。闭包可能会被其他函数调用。 ```php= closure foo: input r0 as field; input r1 as field; add r0 r1 into r2; output r2 as field; ``` 调用闭包 Aleo 程序可以使用该指令在内部调用其他 Aleo 闭包<kbd>call {name} {register} into {register}</kbd> ```php= program bar.aleo; function call_internal: input r0 as u64.private; call foo r0 into r1; // Internally call closure `foo` with argument `r0` and store the result in `r1`. output r1; ``` **结构体(Struct)** 结构体是一种声明为 的数据类型struct {name}:。 结构体包含组件声明{name} as {type}。 ```php= struct array3: a0 as u32; a1 as u32; a2 as u32; ``` 要在程序中对结构体进行实例化,请使用<kbd>cast</kbd>指令。 ```php= function new_array3: input r0 as u32.private; input r1 as u32.private; input r2 as u32.private; cast r0 r1 r2 into r3 as array3; output r3; ``` **记录(Record)** 记录类型声明为<kbd>record {name}:</kbd> 记录包含组件声明<kbd>{name} as {type}.{visibility};</kbd> 记录数据结构必须包含owner如下所示的声明。 当将记录作为输入传递给程序函数时,<kbd>_nonce as group.{visibility}</kbd>需要声明。 ```php= record token: // The token owner. owner as address.private; // The Aleo balance (in microcredits). microcredits as u64.private; // The token amount. amount as u64.private; ``` 要在程序中对记录进行实例化,请使用<kbd>cast</kbd>指令。 ```php= function new_token: input r0 as address.private; input r1 as u64.private; input r2 as u64.private; cast r0 r1 r2 into r3 as token.record; output r3; ``` **映射(Mapping)** 映射被声明为mapping {name}:. 映射包含键值对。映射必须在程序范围内定义。映射公开存储在链上。不可能在映射中私有存储数据。 ```php= // On-chain storage of an `account` map, with `owner` as the key, // and `amount` as the value. mapping account: // The token owner. key owner as address.public; // The token amount. value amount as u64.public; ``` Contains命令 包含命令用于检查映射中是否存在某个键,例如: contains accounts[r0] into r1; Get命令 从映射中获取值的 get 命令,例如:get accounts[r0] into r1; Get or Use 一条Get命令,在失败时使用所提供的默认值,例如:get.or_use accounts[r0] r1 into r2; ```php= finalize transfer_public: // Input the sender. input r0 as address.public; // Input the receiver. input r1 as address.public; // Input the amount. input r2 as u64.public; // Decrements `account[r0]` by `r2`. // If `account[r0]` does not exist, 0u64 is used. // If `account[r0] - r2` underflows, `transfer_public` is reverted. get.or_use account[r0] 0u64 into r3; sub r3 r2 into r4; set r4 into account[r0]; // Increments `account[r1]` by `r2`. // If `account[r1]` does not exist, 0u64 is used. // If `account[r1] + r2` overflows, `transfer_public` is reverted. get.or_use account[r1] 0u64 into r5; add r5 r2 into r6; set r6 into account[r1]; ``` Set命令 Set命令,用于设置映射中的某个值,例如 set r0 into accounts[r0]; Remove命令 删除命令,用于从映射中删除键值对,例如 remove accounts[r0]; ```php= ### Finalize A finalize is declared as `finalize {name}:`. A finalize must immediately follow a [function](#function), and must have the same name; it is associated with the function and is executed on chain, after the zero-knowledge proof of the execution of the associated function is verified; a finalize *finalizes* a function on chain. Upon success of the finalize function, the program logic is executed. Upon failure of the finalize function, the program logic is reverted. ```aleo showLineNumbers // The `transfer_public_to_private` function turns a specified amount // from the mapping `account` into a record for the specified receiver. // // This function preserves privacy for the receiver's record, however // it publicly reveals the sender and the specified amount. function transfer_public_to_private: // Input the receiver. input r0 as address.public; // Input the amount. input r1 as u64.public; // Construct a record for the receiver. cast r0 r1 into r2 as credits.record; // Output the record of the receiver. output r2 as credits.record; // Decrement the balance of the sender publicly. finalize self.caller r1; finalize transfer_public_to_private: // Input the sender. input r0 as address.public; // Input the amount. input r1 as u64.public; // Retrieve the balance of the sender. // If `account[r0]` does not exist, 0u64 is used. get.or_use account[r0] 0u64 into r2; // Decrements `account[r0]` by `r1`. // If `r2 - r1` underflows, `trasfer_public_to_private` is reverted. sub r2 r1 into r3; // Updates the balance of the sender. set r3 into account[r0]; ``` **命令** Aleo 指令支持以下命令以提供附加程序功能。 **self.caller** self.caller 命令会返回程序调用者的地址,这对于管理程序的访问控制非常有用。在上例中,transfer_public_to_private 函数使用 self.caller 公开递减发送者的余额。 **block.height** block.height 命令返回执行程序的块的高度(最新块高度+1)。这对于管理对程序的基于时间的访问控制非常有用。该block.height命令必须在 Finalize 块内调用。 ```php= assert.eq block.height 100u64; ``` **rand.chacha** rand.chacha 命令返回由 ChaCha20 算法生成的随机数。 该命令支持对随机的 address、boolean、field、group、i8、i16、i32、i64、i128、u8、u16、u32、u64、u128 和scalar进行采样。 rand.chacha 命令最多可提供两个附加种子。 目前仅支持 ChaCha20,但将来可能支持其他随机数生成器。 ```php= rand.chacha into r0 as field; rand.chacha r0 into r1 as field; rand.chacha r0 r1 into r2 as field; ``` **Hash** Aleo指令支持以下语法来哈希为标准类型。 ```php= hash.bhp256 r0 into r1 as address; hash.bhp256 r0 into r1 as field; hash.bhp256 r0 into r1 as group; hash.bhp256 r0 into r1 as i8; hash.bhp256 r0 into r1 as i16; hash.bhp256 r0 into r1 as i32; hash.bhp256 r0 into r1 as i64; hash.bhp256 r0 into r1 as i128; hash.bhp256 r0 into r1 as u8; hash.bhp256 r0 into r1 as u16; hash.bhp256 r0 into r1 as u32; hash.bhp256 r0 into r1 as u64; hash.bhp256 r0 into r1 as u128; hash.bhp256 r0 into r1 as scalar; hash.bhp512 ...; hash.bhp768 ...; hash.bhp1024 ...; hash.ped64 ...; hash.ped128 ...; hash.psd2 ...; hash.psd4 ...; hash.psd8 ...; ``` 查看Aleo指令操作码以获取支持的哈希算法的完整列表。 **Commit** Aleo指令支持以下语法来commi为标准类型。 请注意,commit命令的第一个参数为任意类型,第二个参数为scalar。 ```php= commit.bhp256 r0 r1 into r2 as address; commit.bhp256 r0 r1 into r2 as field; commit.bhp256 r0 r1 into r2 as group; commit.bhp512 ...; commit.bhp768 ...; commit.bhp1024 ...; commit.ped64 ...; commit.ped128 ...; ``` 查看Aleo指令操作码以获取支持的承诺算法的完整列表。 **position, branch.eq, branch.neq** * position 命令,例如 position exit,指示执行分支到的某个位置。 * branch.eq 命令,例如 branch.eq r0 r1 to exit,如果 r0 和 r1 相等,则执行分支到 exit 所指示的位置。 * branch.neq 命令,例如 branch.neq r0 r1 to exit,如果 r0 和 r1 不相等,则执行分支到 exit 所指示的位置。 示例 如果输入值为 0u8,则 finalize 程序块成功退出,否则执行失败。 ```php= program test_branch.aleo; function run_test: input r0 as u8.public; finalize r0; finalize run_test: input r0 as u8.public; branch.eq r0 0u8 to exit; assert.eq true false; position exit; ``` ## 操作码 Aleo 操作码参考 以下列表显示了 Aleo 指令支持的标准和加密操作码。 **标准操作码** | 名字 | 中文描述 | 英文描述 | | ------------ | ---------------------------------- | ----------------------------------------------------- | | abs | 绝对值操作 | Absolute value operation | | abs.w | 包装绝对值操作 | Wrapping absolute value operation | | add | 加法操作 | Addition operation | | add.w | 包装加法操作 | Wrapping addition operation | | and | 与运算 | AND operation | | assert.eq | 断言相等 | Assert equality | | assert.neq | 断言不相等 | Assert non-equality | | block.height | 返回最终确定范围内区块的高度 | Returns height of the block within the finalize scope | | branch.eq | 如果参数相等,则分支到某个位置。 | Branches to a position if the arguments are equal | | branch.neq | 如果参数不相等,则分支到某个位置。 | Branches to a position if the arguments are not equal | | cast | 字面量之间转换 | Cast between literals | | cast.lossy | 采用有损截断方式在字面量之间转换 | Cast between literals with lossy truncation | | div | 除法操作 | Division operation | | div.w | 包装除法操作 | Wrapping division operation | | double | double操作 | Double operation | | gt | 大于 | Greater than comparison | | gte | 大于等于 | Greater than or equal to comparison | | inv | 乘法逆操作 | Multiplicative inverse operation | | is.eq | 相等 | Equality comparison | | is.neq | 不相等 | Not equal comparison | | lt | 小于 | Less than comparison | | lte | 小于等于 | Less than or equal to comparison | | mod | 模操作 | Arithmetic modulo operation | | mul | 乘法操作 | Multiplication operation | | mul.w | 包装乘法操作 | Wrapping multiplication operation | | nand | Boolean与非操作 | Boolean NAND operation | | neg | 加法逆运算 | Additive inverse operation | | nor | Boolean或非操作 | Boolean NOR operation | | not | 非操作 | NOT operation | | or | 或操作 | OR Operation | | position | 位置命令 | The position command | | pow | 幂操作 | Exponentiation operation | | pow.w | 包装幂操作 | Wrapping exponentiation operation | | rand.chacha | 在 finalize 范围内生成一个随机值。 | Generates a random value within the finalize scope. | | rem | 余数操作 | Remainder operation | | rem.w | 包装余数操作 | Wrapping remainder operation | | shl | 左移操作 | Shift left operation | | shl.w | 包装左移操作 | Wrapping shift left operation | | shr | 右移操作 | Shift right operation | | shr.w | 包装右移操作 | Wrapping shift right operation | | sqrt | 平方根操作 | Square root operation | | square | 平方操作 | Square operation | | sub | 减法操作 | Subtraction operation | | sub.w | 包装减法操作 | Wrapping subtraction operation | | ternary | 三元选择操作 | Ternary select operation | | xor | 异或操作 | XOR operation | **加密操作码** ![](https://hackmd.io/_uploads/B1hkY0Yjn.png) **规范** 以下是 Aleo 虚拟机 (AVM) 中每个操作码的规范。 **abs** 计算输入的绝对值,检查是否有溢出,并将结果存入目的寄存器。 对于整数类型,会添加一个约束条件来检查是否有下溢。对于需要包装语义的情况,请参阅 abs.w 指令。当输入是有符号整数类型的最小值时,就会出现下溢。例如,abs -128i8 将导致下溢,因为 128 不能表示为 i8。 支持的类型 ![](https://hackmd.io/_uploads/BJ_PKRKih.png) **abs.w** 计算输入的绝对值,在类型边界处绕,并将结果存储在目的寄存器中。 支持的类型 ![](https://hackmd.io/_uploads/SJN3tRYjh.png) **add** 将first与second相加,并将结果存储在destination。 对于整数类型,会添加一个约束来检查是否有溢出。如果整数类型需要包装语义,请参阅 add.w 指令。 支持的类型 ![](https://hackmd.io/_uploads/SyEg9AFih.png) **add.w** 将first与second相加,在类型的边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/BySv90ti2.png) **and** 对first和second的整数(位运算)或布尔值执行与运算,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rkf29Ati3.png) **assert.eq** 检查first和second是否相等,如果不相等则停止。 支持的类型 ![](https://hackmd.io/_uploads/rytli0tj3.png) **assert.neq** 检查first和second是否不相等,如果相等则停止。 支持的类型 ![](https://hackmd.io/_uploads/rytli0tj3.png) **block.height** 返回块在最终确定范围内的高度。目前,块高度是唯一受支持的属性。 > assert.eq block.height 1337; **branch.eq** branch.eq 命令,例如: ``` branch.eq <first> <second> to <destination> ``` 如果first和second相等,则将执行分支到目的地所指示的position。 ![](https://hackmd.io/_uploads/SJLjkyqs2.png) **branch.neq** branch.neq 命令,例如: ``` branch.neq <first> <second> to <destination> ``` 如果first和second不相等,则将执行分支到目的地所指示的position。 ![](https://hackmd.io/_uploads/SJLjkyqs2.png) **cast** ```php= input r0 as field.private; cast r0 into r1 as group; cast r0 into r2 as u8; ``` 实现不同字面量之间的转换。 支持的类型 ![](https://hackmd.io/_uploads/Bki5Hy5o2.jpg) **cast.lossy** ```php= input r0 as field.private; cast r0 into r1 as group; cast r0 into r2 as u8; cast.lossy r0 into r3 as u8; // The bottom 8 bits of the r0 are extracted into a u8 and placed into r3 ``` 使用有损截断功能进行转换。 支持的类型 ![](https://hackmd.io/_uploads/H1IYcy5sh.png) **commit.bhp256** 计算 Bowe-Hopwood-Pedersen 承诺值,first输入 256 位数据块,second输入随机值,并将承诺值存储在destination。随机值应始终是Scalar,生成的承诺值应始终是Address、Field或Group,在指令末尾通过 as 指定。 如果给定输入值小于 129 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/S1U4jkcjh.png) **commit.bhp512** 计算 Bowe-Hopwood-Pedersen 承诺值,first输入 512 位数据块,second输入随机值,并将承诺值存储在destination。随机值应始终是Scalar,生成的承诺值应始终是Address、Field或Group,在指令末尾通过 as 指定。 如果给定输入值小于 171 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/S1U4jkcjh.png) **commit.bhp768** 计算 Bowe-Hopwood-Pedersen 承诺值,first输入 768 位数据块,second输入随机值,并将承诺值存储在destination。随机值应始终是Scalar,生成的承诺值应始终是Address、Field或Group,在指令末尾通过 as 指定。 如果给定输入值小于 129 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/S1U4jkcjh.png) **commit.bhp1024** 计算 Bowe-Hopwood-Pedersen 承诺值,first输入 1024 位数据块,second输入随机值,并将承诺值存储在destination。随机值应始终是Scalar,生成的承诺值应始终是Address、Field或Group,在指令末尾通过 as 指定。 如果给定输入值小于 171 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/S1U4jkcjh.png) **commit.ped64** 计算 Pedersen 承诺,first是 64 位输入值,second是一些随机值,并将承诺值存储在destination。随机值应始终是Scalar,生成的承诺值是Address、Field或Group,在指令末尾通过 as 指定。 如果给定的 Struct 值超过 64 位限制,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/H1fUnkqin.png) **commit.ped128** 计算 Pedersen 承诺,first是 128 位输入值,second是一些随机值,并将承诺值存储在destination。随机值应始终是Scalar,生成的承诺值是Address、Field或Group,在指令末尾通过 as 指定。 如果给定的 Struct 值超过 128 位限制,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/BJBC21cjn.png) **div** 将first除以second,并将结果存储在destination。除以 0 时停止。 对于整数类型,该操作执行截断除法。此外,还添加了一个约束条件,以检查是否出现下溢。当有符号整数类型的最小值除以-1 时,就会出现下溢。例如,除以 -128i8 -1i8 将导致下溢,因为 128 不能表示为 i8。 如果需要对整数类型进行包装语义处理,请参阅 div.w 指令。 支持的类型 ![](https://hackmd.io/_uploads/BkBf6y9jn.png) **div.w** 将first除以second,在类型边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/r1Wwpk9jh.png) **double** Doubles输入,并将结果存储在destination。 支持的类型 | Input | Destination | | ----- | ----------- | | Field |Field | | Group | Group | **gt** 检查first是否大于second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/SJcW0k5on.png) **gte** 检查first是否大于或等于second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/S1ZvCJ9s2.png) **hash.bhp256** 对first输入的 256 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 如果给定的输入值小于 129 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/rkm7kx9j2.png) **hash.bhp512** 对first输入的 512 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 如果给定的输入值小于 171 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/rkm7kx9j2.png) **hash.bhp768** 对first输入的 768 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 如果给定的输入值小于 129 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/rkm7kx9j2.png) **hash.bhp1024** 对first输入的 1024 位数据块计算 Bowe-Hopwood-Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 如果给定的输入值小于 171 位,指令将停止执行。 支持的类型 ![](https://hackmd.io/_uploads/rkm7kx9j2.png) **hash.ped64** 对first输入的 64 位输入值计算 Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 如果给定的 Struct 值超过 64 位限制,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/SJiLxeqoh.png) **hash.ped128** 对first输入的 128 位输入值计算 Pedersen 哈希值,然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 如果给定的 Struct 值超过 128 位限制,指令将停止。 支持的类型 ![](https://hackmd.io/_uploads/H1LYll9jh.png) **hash.psd2** 对first输入值计算Poseidon哈希值(输入率为 2),然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 支持的类型 ![](https://hackmd.io/_uploads/rkEeWxqih.png) **hash.psd4** 对first输入值计算Poseidon哈希值(输入率为 4),然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 支持的类型 ![](https://hackmd.io/_uploads/rkEeWxqih.png) **hash.psd8** 对first输入值计算Poseidon哈希值(输入率为 8),然后将哈希值存储在destination。生成的哈希值始终是算术值(U8、U16、U32、U64、U128、I8、I16、I32、I64、I128、Field、Group或Scalar)或Address,通过指令末尾的 as 指定。 支持的类型 ![](https://hackmd.io/_uploads/rkEeWxqih.png) **inv** 计算输入的乘法逆向,并将结果存储在destination。 支持的类型 | Input | Destination | | -------- | -------- | | Field | Field | **is.eq** 比较first和second,将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/B1UA-x5ih.png) **is.neq** 如果first不等于second,则返回 true,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/B1UA-x5ih.png) **lt** 检查first是否小于second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rkdEzxcoh.png) **lte** 检查first是否小于等于second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rkdEzxcoh.png) **mod** 取first相对于second的模数,并将结果存储在destination。如果second为零,则停止。 此操作的语义与模操作的数学定义一致。 支持的类型 ![](https://hackmd.io/_uploads/BkY5fe9s3.png) **mul** 将first与second相乘,并将结果存储在destination。 对于整数类型,会添加一个约束条件来检查是否有溢出/下溢。如果整数类型需要包装语义,请参阅 mul.w 指令。 支持的类型 ![](https://hackmd.io/_uploads/rJspzg9i3.png) **mul.w** 将first与second相乘,在类型边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/H1lMQgqin.png) **nand** 只有当first和second为真时才返回假,并将结果存储在destination。 支持的类型 | First | Second | Destination | | -------- | -------- | -------- | | Boolean | Boolean | Boolean | **neg** 取反first,将结果存储在destination。 对于有符号整数类型,在最小值上调用 neg 是无效操作。例如,输入 -128i8 将无效,因为 128 不能表示为 i8。 支持的类型 ![](https://hackmd.io/_uploads/Syaamg5oh.png) **nor** 当first和second都不为真时,返回 true,并将结果存储在destination。 支持的类型 | First | Second | Destination | | -------- | -------- | -------- | | Boolean | Boolean | Boolean | **not** 对整数(位运算)或布尔输入执行非运算,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/HyT6Vx9on.png) **or** 对整数(位操作)或布尔值的first和second执行或运算,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/BkcGHg9s2.png) **position** 位置声明,例如 ```php= position <name> ``` 表示程序中要分支执行的位置name。位置必须是小写字母数字字符串。 **pow** 将first提升到second的幂级数,并将结果存储在destination。 对于整数类型,会添加一个约束条件来检查是否有溢出/下溢。如果整数类型需要包装语义,请参阅 pow.w 指令。 支持的类型 ![](https://hackmd.io/_uploads/Hkv0Ie5s3.png) **pow.w** 将first提升到second的幂级数,在类型的边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/Hy0Gve9o3.png) **rand.chacha** rand.chacha 操作码用于在 finalize 范围内生成随机值。它支持多种随机值类型。 ```php= rand.chacha into r0 as field; rand.chacha r0 into r1 as field; rand.chacha r0 r1 into r2 as field; rand.chacha 1u8 2i16 into r27 as u32; ``` 支持的类型 可以是以下任意类型 Address、Boolean、Field、Group、I8、I16、I32、I64、I128、U8、U16、U32、U64、U128 或Scalar。不允许使用结构体和映射等复合数据类型。 | First | Second | Destination | | -------- | -------- | -------- | | Single | Single | Single | **rem** 计算first除以second的截断余数,并将结果存储在destination。除以 0 时停止。 增加了一个限制条件,以检查是否有下溢。当相关的除法运算 div 出现下溢时,就会出现下溢。 对于整数类型需要包装语义的情况,请参阅 rem.w 指令。 支持的类型 ![](https://hackmd.io/_uploads/rJpgdg5j2.png) **rem.w** 计算first除以second的截断余数,在类型边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/H1x5NOg9i2.png) **shl** 将first向左移动second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rJu_dg5i3.png) **shl.w** 将first向左移动second,在类型边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rJu_dg5i3.png) **shr** 将first向右移动second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rJu_dg5i3.png) **shr.w** 将first向右移动second,在类型边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/rJu_dg5i3.png) **square** 对输入进行平方运算,并将运算结果存入destination。 支持的类型 | Input | Destination | | -------- | -------- | | Field | Field | **sqrt** 计算输入值的平方根,并将结果存储在destination。 支持的类型 | Input | Destination | | -------- | -------- | | Field | Field | **sub** 计算first减去second,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/r1IdFl5on.png) **sub.w** 计算first减去second,在类型边界处绕,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/r1IdFl5on.png) **ternary** 如果条件为真,则选择first,否则选择second,并将结果存储到destination。 示例:ternary r0 r1 r2 into r3,其中 r0 是条件,r1 是第一选择,r2 是第二选择,r3 是目的地。 支持的类型 ![](https://hackmd.io/_uploads/B1Xkqgqon.png) **xor** 对整数(位运算)或布尔值的first和second执行异或运算,并将结果存储在destination。 支持的类型 ![](https://hackmd.io/_uploads/SknX5l5j2.png) :::info :information_source: 由于单篇长度的限制,第二部分内容在:https://hackmd.io/@maxsayss/B1nBeHtin ::: :::success 欢迎对Aleo技术感兴趣的小伙伴加入社群! 注意哈,社群只讨论技术,所以非技术类小伙伴谨慎加入哦! 再次非常感谢大家的支持,谢谢! TG: https://t.me/+VATAn9z4x_9iOTc1 :::