3.7 Operators

In this chapter, we'll explore operators and expressions in the Leo programming language. Leo offers a long list of operators for performing arithmetic operations, logical comparisons, bitwise manipulations, and cryptographic functions.

Basic Concepts

Operators in Leo compute a value based on one or more expressions. Leo defaults to checked arithmetic. This means that operations like addition, subtraction, and multiplication will throw an error if an overflow or division by zero is detected.

Let's look at a simple example:

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

This example demonstrates three different ways to perform addition in Leo:

  • Using the + operator

  • Using the += compound assignment operator

  • Using the .add() method

Operator Precedence

When an expression contains multiple operators, Leo follows a strict order of evaluation based on operator precedence. Here's the complete precedence table, from highest (evaluated first) to lowest:

Operator
Associativity

! -(unary)

**

right to left

* /

left to right

+ -(binary)

left to right

<< >>

left to right

&

left to right

|

left to right

^

left to right

< > <= >=

== !=

left to right

&&

left to right

||

left to right

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

Using Parentheses for Explicit Ordering

To override the default precedence, you can use parentheses to explicitly control the order of evaluation:

In this example, (a + 1u8) will be evaluated first, then the result will be multiplied by 2u8.

Context-dependent Expressions

Leo supports special expressions that provide information about the Aleo blockchain and the current transaction.

self.caller

The self.caller expression returns the address of the account or program that invoked the current transition:

self.signer

The self.signer expression returns the address of the account that invoked the top-level transition - the account that signed the transaction:

block.height

The block.height expression returns the current block height. Note that this can only be used in an async function:

Core Functions

Leo provides several built-in functions for assertions and cryptographic operations.

Assertions

The assert and assert_eq functions verify conditions and halt program execution if they fail:

Hash Functions

Leo supports multiple hashing algorithms, each with different input sizes and output types:

Available hashing algorithms include:

  • BHP256, BHP512, BHP768, BHP1024

  • Pedersen64, Pedersen128

  • Poseidon2, Poseidon4, Poseidon8

  • Keccak256, Keccak384, Keccak512

  • SHA3_256, SHA3_384, SHA3_512

Commitment Functions

Commitment schemes allow you to commit to a value while keeping it hidden, then reveal it later:

Random Number Generation

The ChaCha algorithm provides cryptographically secure random number generation. These functions can only be used in async functions:

Standard Operators

Let's explore the standard operators available in Leo in more detail.

Arithmetic Operators

Addition (+, add, add_wrapped)

Subtraction (-, sub, sub_wrapped)

Multiplication (*, mul, mul_wrapped)

Division (/, div, div_wrapped)

Exponentiation (**, pow, pow_wrapped)

Comparison Operators

Method syntax is also available for these comparisons:

Logical Operators

Method syntax:

Bitwise Operators

Method syntax:

Wrapped versions are also available for shift operations:

Other Mathematical Operators

Ternary Operator

The ternary (conditional) operator allows for concise conditional expressions:

Signature Verification

Special Operators and Constants

Group Generator

The group::GEN constant provides access to the generator point of the elliptic curve group:

Double Operation

The double method performs doubling operation on field and group elements:

Inverse Operations

The multiplicative inverse can be computed for field elements:

Best Practices for Operators

When working with operators in Leo, keep these best practices in mind:

  1. Use checked operations by default for better safety, unless you specifically need wrapping behavior.

  2. Be careful with type conversions when working with different numeric types.

  3. Consider overflow/underflow risks when performing operations near the bounds of a type's range.

  4. Use parentheses liberally to make your code's precedence explicit, especially in complex expressions.

  5. Take advantage of method syntax when it makes your code more readable.

  6. Be mindful of the differences between rem and mod operations, especially when working with negative numbers.

  7. Use cryptographic operations appropriately based on your security and performance requirements.

Common Patterns and Examples

Safely Incrementing a Counter

Computing an Average

Checking Permissions

Creating a Simple Hash-based Commitment

Type-Specific Operator Behavior

Different types in Leo may have different behaviors with the same operators. Here's a quick summary:

Integer Types (i8, i16, i32, i64, i128, u8, u16, u32, u64, u128)

  • Support full arithmetic operations

  • Checked operations will halt on overflow/underflow

  • Wrapping operations available for all arithmetic operations

  • Support all bitwise operations

Field Type

  • Supports addition, subtraction, multiplication, division, square, square_root, inverse

  • Does not support bitwise operations

  • No overflow concerns (the field is very large)

Group Type

  • Supports addition and scalar multiplication (for elliptic curve points)

  • Supports negation and doubling

  • Special operation: scalar multiplication with a scalar type

Boolean Type

  • Supports logical operations: and, or, not, xor, nand, nor

  • Can be used in ternary operations as the condition

Last updated