Releases: ProvableHQ/leo
testnet-v3.5.1
testnet v3.5.1
canary-v3.5.0
What's Changed
- Update linux and macos release jobs by @mohammadfawaz in #29108
- feat(leo-fmt): implement statement and expression formatting by @JoshuaBatty in #29104
- Add signature literals and support optional signatures by @mohammadfawaz in #29102
- feat(cli): add
leo abicommand for generating ABI from .aleo files by @mitchmindtree in #29116 - refactor(leo-parser): change tests to use
Displayinstead ofSerializeby @mitchmindtree in #29117 - refactor(leo-fmt): switch from
leo-parser-losslesstoleo-parser-rowanby @JoshuaBatty in #29118 - Refactor the CLI testing framework +
leo devnodeenhancements by @mohammadfawaz in #29120 - Surface missing private_key error for leo devnode by @vicsn in #29110
- feat(cli): add leo fmt command by @JoshuaBatty in #29124
- fix(cli): remove duplicate
EnvOptionsflatten indevnodesubcommands by @mitchmindtree in #29126 - Use a prebuilt genesis block for execution tests by @mohammadfawaz in #29121
- [Fix]
--skipoption forleo upgradeand improve spacing for CLI output by @d0cd in #29130 - feat(leo-fmt): add line wrapping at 100-char width by @JoshuaBatty in #29132
- Traverse stubs when calling
fillin WriteTransforming by @mohammadfawaz in #29137 - Some fixes from master and move to canary 4.5.3 by @mohammadfawaz in #29141
- fix(leo-fmt): remaining formatter edge cases from #29127 by @JoshuaBatty in #29142
Full Changelog: testnet-v3.5.0...canary-v3.5.0
testnet-v3.5.0
What's Changed
- [Release] Leo v3.3.1 by @d0cd in #28965
- support CONSENSUS_VERSION_HEIGHTS env var in devnet command by @eranrund in #28968
- Don't delete local constants during const propagation and unroll by @IGI-111 in #28970
- Allow empty loops by @IGI-111 in #28975
- Add support for empty arrays by @IGI-111 in #28979
- perf: remove unnecessary clone when reconstructing functions by @Galoretka in #29000
- fix: remove shadowed predeclared code and redundant clear() in interpret() by @Snezhkko in #29003
- Structured code generation by @IGI-111 in #28999
- [Feat] Support
block.timestampby @raychu86 in #28988 - [Fix] Code generation for
*::commit_*core functions. by @d0cd in #28995 - Tidying up the repo a bit by @mohammadfawaz in #29015
- Allow
addressin optionals and storage by @mohammadfawaz in #29005 - Simplify conditional blocks by @IGI-111 in #29004
- A few fixes to options in type checking and option lowering by @mohammadfawaz in #29019
- [Release] Leo v3.4.0 by @d0cd in #29023
- Add CLI shortcuts for build, run, and test commands by @JoshuaBatty in #29020
- No ledger execution tests by @mohammadfawaz in #29022
- Explicit pass schedule in test framework by @IGI-111 in #29028
- feat: Add a
--disable-update-checkflag to leo by @mitchmindtree in #29026 - ci: Change integration test condition to allow testing on unix by @mitchmindtree in #29032
- Display compiled program size in
leo build,leo deploy, andleo upgradeoutput by @JoshuaBatty in #29033 - Add definition aware late const folding step by @IGI-111 in #29013
- Cache snarkOS binary in CI to avoid 5+ minute rebuild by @JoshuaBatty in #29031
- Enforce usage of latest program edition by @JoshuaBatty in #29027
- Unified intrinsics by @IGI-111 in #29034
- fix: tokenize group/signature/Future paths with regex by @phrwlk in #29046
- Upgrade Rust version and remove some dead files by @mohammadfawaz in #29042
- [Non-functional] Change the syntax tree converter to use a
ConversionContextby @mohammadfawaz in #29047 - Change "struct" to "composite" where appropriate by @mohammadfawaz in #29052
- Explicit intrinsic disambiguation by @IGI-111 in #29048
- perf: reduce cloning in await checker tree nodes by @SashaMalysehko in #29054
- fix: clarify import private key error message by @Fibonacci747 in #29055
- Add
--json-outputflag to save structured CLI output to disk by @JoshuaBatty in #29051 - Rework
Pathrepresentation and early compiler pipeline ordering by @mohammadfawaz in #29060 - Cleanup remnants from code generation refactor by @IGI-111 in #29065
- fix: avoid extra allocation when building record members by @Bashmunta in #29070
- Update all license headers to use 2026 by @mohammadfawaz in #29073
- Upgrade to canary-4.5.0 by @mohammadfawaz in #29069
- Rework program imports + external structs by @mohammadfawaz in #28986
- refactor: reduce redundant type cloning in reconstructor by @radik878 in #29076
- Cleanup Cargo.toml files by @mohammadfawaz in #29075
- Update leo banner by @raychu86 in #29081
- feat(leo-fmt): add formatter crate scaffolding by @JoshuaBatty in #29083
- feat(parser): Add
leo-parser-rowancrate and initial lexer implementation by @mitchmindtree in #29080 - feat: Add ABI Generation, including
leo-abi-typesandleo-abicrates by @mitchmindtree in #29064 - fix: drop
redundant filter_mapin remove_unreachable by @GarmashAlex in #29072 - perf: avoid Vec allocation in async finalize lookup by @phrwlk in #29091
- feat(leo-fmt): implement declaration formatting by @JoshuaBatty in #29094
- External storage accesses by @mohammadfawaz in #29092
- feat(parser-rowan): implement core parsing logic by @mitchmindtree in #29095
- [Feature] Leo Devnode CLI command by @Roee-87 in #29012
- fix: preserve and format HTTP response body in broadcast errors by @JoshuaBatty in #29100
- feat(parser-rowan): implement comprehensive error recovery by @mitchmindtree in #29103
- Deprecate
Locatorexpressions in favor ofPathexpressions by @mohammadfawaz in #29098 - Prepare for
testnet-v3.5.0tag by @mohammadfawaz in #29105
New Contributors
- @Galoretka made their first contribution in #29000
- @Snezhkko made their first contribution in #29003
- @mitchmindtree made their first contribution in #29026
- @phrwlk made their first contribution in #29046
- @SashaMalysehko made their first contribution in #29054
- @Fibonacci747 made their first contribution in #29055
- @Bashmunta made their first contribution in #29070
- @radik878 made their first contribution in #29076
- @Roee-87 made their first contribution in #29012
Full Changelog: v3.3.1...testnet-v3.5.0
v3.4.0
Release Notes
Empty arrays and empty loop
Arrays of size 0 and loops over empty ranges are now supported in Leo. While these constructs do not produce any instructions in the compiled Aleo bytecode, they enable more generic or pattern-based programming styles in Leo, especially when writing code that abstracts over sizes, iterates conditionally, or uses compile-time parameters. For example:
inline build_default::[N: u32]() -> [u8; N] {
let xs: [u8; N] = [0u8; N];
// When N = 0 this loop is simply skipped
for i:u32 in 0..N {
xs[i] = 1u8;
}
return xs;
}
let xs = build_default::[0](); // yields []Stability improvements
- Improved identifier validation in Leo, resulting in clearer and more precise error messages, especially when Leo identifiers conflict with reserved Aleo identifiers.
- Fixed an issue where local
constvalues failed to compile correctly when used in loop ranges. - Resolved a crash in the common subexpression elimination optimization pass that occurred for certain patterns of function calls.
Breaking Changes
- The
*hash_native*core functions have been renamed to*hash_to_bits*.
v3.3.1
Patch
- Fixes error in common subexpression elimination.
v3.3.0
Release Notes
Optional Types (T?)
Leo now supports first-class optional types using the T? syntax (e.g., u8?, Foo?, [u64?; 2]).
Optional values can be compared with none, assigned, passed into inline functions, and stored in arrays and structs.
program optionals.aleo {
struct Point { x: u32, y: u32 }
transition main() {
// Optional integers
let x: u8? = 42u8;
let y = x.unwrap(); // Returns 42u8
let z: u8? = none;
let a = z.unwrap_or(99u8); // Returns 99u8
// Array of optionals
let arr: [u16?; 2] = [1u16, none];
let first_val = arr[0].unwrap(); // Returns 1u16
let second_val = arr[1].unwrap_or(0u16); // Returns 0u16
// Optional struct
let p: Point? = none;
let p_val = p.unwrap_or(Point { x: 0u32, y: 0u32 }); // Returns default Point
}
}Type coercion from T to T? is supported in variable definitions, inline function calls, and intermediate expressions.
Explicit unwrapping is required to go from T? → T.
New Storage System
Leo now supports persistent storage variables and storage vectors using the storage keyword.
Storage variables and vectors are declared at program scope, similar to mappings.
program storage_ops.aleo {
struct Point { x: field, y: field }
storage counter: u32; // singleton storage variable
storage points: [Point]; // storage vector of `Point`s
transition main() -> Future {
return async {
counter = 5u32;
let old = counter.unwrap_or(0u32); // returns optional
points.push(Point { x: 1field, y: 2field });
let first = points.get(0u32).unwrap();
points.set(0u32, Point { x: 3field, y: 4field });
counter = none; // unset
}
}
}Storage vector supported operations:
vec.push(10u32); // Push 10u32 at the end of vector `vec`
let x = vec.pop(); // Pop and return the last element of `vec`
let y = vec.get(5); // Get element at index 5
vec.set(3, 5u32); // Set element at index 3 to `5u32`
let y = vec.len(); // Return the number of elements in `vec`
vec.swap_remove(3); // Remove element at index 3 and replace with lastInternally, the compiler rewrites these high-level constructs into mappings and mapping operations.
ECDSA Signature Verification
ECDSA signature verification is now supported with 20 variants covering different hash algorithms and address formats.
// Verify with digest (pre-hashed message)
let valid: bool = ECDSA::verify_digest(sig, addr, digest);
let valid: bool = ECDSA::verify_digest_eth(sig, eth_addr, digest);
// Verify with Keccak256 hashing
let valid: bool = ECDSA::verify_keccak256(sig, addr, msg);
let valid: bool = ECDSA::verify_keccak256_raw(sig, addr, msg);
let valid: bool = ECDSA::verify_keccak256_eth(sig, eth_addr, msg);Also available: keccak384, keccak512, sha3_256, sha3_384, sha3_512.
Parameters:
| Parameter | Type | Description |
|---|---|---|
sig |
[u8; 65] |
ECDSA signature (r, s, v) |
addr |
[u8; 33] or [u8; 20] |
Public key or Ethereum-style address |
digest |
[u8; 32] |
Pre-hashed message |
msg |
Any |
Message to hash and verify |
Raw Hash Operations
Leo now supports raw hash variants. These omit metadata and directly hash input bits, useful for interoperability with external (especially EVM) systems.
Inputs for raw variants of Keccak* and Sha3* must be byte-aligned (number of bits is a multiple of 8).
// Raw hash functions
let h: field = Keccak256::hash_to_field_raw(input);
let h: group = BHP256::hash_to_group_raw(input);
let h: address = Pedersen64::hash_to_address_raw(input);
// Native variants
let bits: [bool; 256] = Keccak256::hash_native(input);
let bits: [bool; 256] = Keccak256::hash_native_raw(input);Available for:
BHP256, BHP512, BHP768, BHP1024, Pedersen64, Pedersen128, Poseidon2, Poseidon4, Poseidon8, Keccak256, Keccak384, Keccak512, SHA3_256, SHA3_384, SHA3_512.
Serialization / Deserialization Operations
Leo now supports serialize and deserialize operations to and from bits, with both metadata-inclusive and raw variants.
The compiler checks that bit sizes match.
// Standard serialization (includes metadata)
let bits: [bool; 58] = Serialize::to_bits(value);
let value: u32 = Deserialize::from_bits::[u32](bits);
// Raw serialization (no metadata)
let bits: [bool; 32] = Serialize::to_bits_raw(value);
let value: u32 = Deserialize::from_bits_raw::[u32](bits);
// Arrays
let bits: [bool; 128] = Serialize::to_bits_raw([1u32, 2u32, 3u32, 4u32]);
let arr: [u32; 4] = Deserialize::from_bits_raw::[[u32; 4]](bits);Bit Lengths
| Type | Raw Bits | Notes |
|---|---|---|
u32 |
32 | |
field |
253 | |
scalar |
251 | |
address |
253 | |
| Non-raw | + metadata overhead |
leo synthesize Command
Generate proving and verifying keys for all transitions in a local or remote Leo program, along with circuit metadata:
leo synthesize credits.aleo --save keys \
--endpoint https://api.explorer.provable.com/v1 \
--network mainnetOutput includes:
- Public inputs
- Variables
- Constraints
- Non-zero entries in matrices
- Circuit ID
- Proving and verifying keys saved to disk
This enables better understanding of program size and key management.
Lossless Syntax Tree Parser
A new lossless syntax tree parser has been added.
While not user-facing yet, it lays the foundation for a future code formatter.
Common Subexpression Elimination (CSE)
New optimization pass reduces bytecode size by eliminating redundant expressions.
Enhanced Error Messages
Error messages now display multiple related source locations.
Example with duplicate struct members:
struct Person {
name: field,
age: u8,
name: field,
}Error:
Error [ETYC0372017]: the name `name` is defined multiple times in struct `Person`
--> src/main.leo:3:9
|
3 | name: field,
| ^^^^^^^^^^^ previous definition here
...
5 | name: field,
| ^^^^^^^^^^^ redefined here
Remaining Stability Improvements
- Various fixes to the interpreter related to hashing correctness
- Fixed broken
leo query committeeendpoint - Validates program names in
leo newagainst reserved SnarkVM keywords - Enforces test isolation for native tests
- Speeds up
leo testby running native tests in parallel - Supports
CheatCode::set_signer("APrivateKey1...")for test signer switching
v3.2.0
🦁 Leo 3.2.0 Release Notes
📦 Modules
Leo 3.2.0 introduces a module system.
Example: Defining a Module
Given a file other_module.leo containing struct, const, and inline definitions:
const X: u32 = 2u32;
struct S {
a: field
}
inline increment(x: field) -> field {
return 1field;
}You may refer to contents of the module as:
other_module::Xother_module::Sother_module::increment
📁 Example Directory Structure
src
├── common.leo
├── main.leo
├── outer.leo
└── outer
└── inner.leo
📦 Module Access Paths
Given the structure above, the following modules are defined:
-
common.leo→ defines modulecommon
Accessible frommain.leovia:common::<item>
-
outer.leo→ defines moduleouter
Accessible frommain.leovia:outer::<item>
-
outer/inner.leo→ defines submoduleouter::inner
Accessible from:-
main.leo:outer::inner::<item>
-
outer.leo:inner::<item>
-
📏 Module Layout Rules
New rules enforce consistency and prevent ambiguity:
-
Leaf modules (modules without submodules) must be defined in a single file:
foo.leo -
Modules with submodules must be defined by an optional top-level file and a subdirectory:
foo.leo # defines module `foo` (optional) foo/ └── bar.leo # defines submodule `foo::bar`
For example, items in outer.leo cannot be accessed from items in inner.leo. This limitation will be removed once absolute paths are implemented.
🔄 Leo Update Command
The leo update command now supports selecting a specific version:
leo update --name v3.0.0🔐 Other Updates
- Decrypt records via CLI: You can now decrypt records directly from the command line.
- Pass ciphertexts to
leo execute: Record ciphertexts may be passed as inputs to execution.
🐞 Bug Fixes & Improvements
- Fixed outputting code regarding parenthesizing operands of the ternary conditional.
- Removed redundant type errors in async function calls.
- Correctly handle shorthand struct initializers that are consts.
leo cleanno longer errors whenoutputs/orbuild/directory is missing.- Correctly type-check
returnstatements inside constructors. - Added a check for the snarkOS installation location.
- Ensure const arguments are evaluated before monomorphization.
- Improved error messages for unresolved generics.
v3.1.0
- Upgradability: See the SnarkOS notes for more about how upgradability works in general, but in Leo code you will now include a constructor to be run during upgrades. The constructor may use an annotation to indicate use of a standard constructor, such as the following to forbid upgrades:
@noupgrade
async constructor() {}
Or you may create a custom constructor:
@custom
async constructor() {
assert_eq(true, true);
}
- Async blocks allow you to elide defining separate async functions, and write your on-chain code in the same location as the rest of the transition:
async transition main(public a: u32, b: u32) -> Future {
let f1: Future = other.aleo/main_inner(0u32, 0u32);
let f: Future = async {
f1.await();
};
return f;
}
- You can use a
.aleofile as a local dependency (previously dependencies were programs already deployed to the network, or local Leo projects):
leo add --local this_dependency.aleo
- leo devnet allows you to conveniently start a local devnet, optionally installing snarkos if desired:
leo devnet --install --snarkos ~/bin/snarkos --snarkos-features test_network
-
Identifiers may no longer contain double underscores, so for instance
this__nameis now disallowed. -
Bug fix:: we no longer try to fetch the edition of
credits.aleo.
v3.0.0
- Array repeater syntax:
[0u8; 3]; - input files are gone;
- numerous small fixes.
v2.7.3
- Fix to type resolution in interpreter (allowing tests to work correctly)