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 4This example demonstrates three different ways to perform addition in Leo:
Using the
+operatorUsing the
+=compound assignment operatorUsing 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:
! -(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:
Use checked operations by default for better safety, unless you specifically need wrapping behavior.
Be careful with type conversions when working with different numeric types.
Consider overflow/underflow risks when performing operations near the bounds of a type's range.
Use parentheses liberally to make your code's precedence explicit, especially in complex expressions.
Take advantage of method syntax when it makes your code more readable.
Be mindful of the differences between
remandmodoperations, especially when working with negative numbers.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