Our team is happy to announce a new version of the Aleo Stack, which includes the node implementation snarkOS and the programming language Leo. These applications are built on top of the developer-friendly snarkVM and Aleo SDK libraries. Aleo is an open source cryptocurrency through which developers can deploy cryptographically secure dApps.
Most notably, this release unlocks native Keccak+ECDSA-based signature verification, allowing for messages from other popular cryptocurrency ecosystems like Ethereum to be verified in Aleo’s public smart contract scope - while still having access to native zero knowledge proofs verification too. Moreover, maximum array sizes have increased, node syncing is 5x faster, and both transaction and solution broadcasting has been greatly stabilized.
If you want to try it out, you can build from source today or download the mainnet release from github October 28th. Find the current release schedule at: https://provablehq.github.io/.
Please report any issues you might come across!
What's new in snarkOS v4.3.0
Consensus change: ConsensusVersion V11
A consensus change will occur at the following times:
Canary - ~9AM PT October 17th, 2025
Testnet - ~9AM PT October 24th, 2025
Mainnet - ~9AM PT November 4th, 2025
The exact block heights will be encoded in the snarkVM library at release.
ATTENTION: Validators that do not upgrade in time will be at risk of forking and require manual intervention. Clients that do not upgrade will be at risk of halting until an upgrade occurs.
Keccak+ECDSA signature verification opcodes
Aleo added native ECDSA signature verification to on-chain (finalize) execution to improve interoperability and broaden supported signature formats. With on-chain verification, programs can trust externally signed messages and approvals without off-chain helpers, enabling cross-chain messaging, asset flows, multisig/governance, etc. The initial scope focuses on verification with a simple canonical representation and deterministic, consensus-safe behavior, while leaving room to add other schemes and encodings over time.
The core design revolves around the new `ecdsa.verify.*` instructions that will be available to new programs. In order to support this, we require a lot of supporting features that enable the use of the new signature verification algorithm.
ECDSA
This PR introduces native ECDSA signature verification to on-chain (finalize) execution to improve interoperability and broaden supported signature formats. With on-chain verification, programs can trust externally signed messages and approvals without off-chain helpers, enabling cross-chain messaging, asset flows, multisig/governance, etc. The initial scope focuses on verification with a simple canonical representation and deterministic, consensus-safe behavior, while leaving room to add other schemes and encodings over time.
The list of new ecdsa.verify.<variant> instructions are as follows:
ecdsa.verify.keccak256
ecdsa.verify.keccak256.raw
ecdsa.verify.keccak256.eth
ecdsa.verify.keccak384
ecdsa.verify.keccak384.raw
ecdsa.verify.keccak384.eth
ecdsa.verify.keccak512
ecdsa.verify.keccak512.raw
ecdsa.verify.keccak512.eth
ecdsa.verify.sha3_256
ecdsa.verify.sha3_256.raw
ecdsa.verify.sha3_256.eth
ecdsa.verify.sha3_384
ecdsa.verify.sha3_384.raw
ecdsa.verify.sha3_384.eth
ecdsa.verify.sha3_512
ecdsa.verify.sha3_512.raw
ecdsa.verify.sha3_512.eth
The variant types allow the users to select the underlying hash function used, the serialization type (with .raw), and if the address being verified is a 20-byte Ethereum address. The .raw variants indicate if the message should be serialized in it's raw bit form or with Aleo specific variant bits; this is necessary to ensure compatibility with external signing libraries. By default the .eth variants will serialize the message in it's raw form, so there is no need for an additional .eth.raw suffixed variant. Both .raw and .eth variant messages must be "byte-aligned", meaning that the length of it's bit representation must be a multiple of 8.
Hash Functions
To further interoperability of AVM programs between EVM programs, this PR introduces .native variants of the hash.keccak* and hash.sha3* opcodes that allow the user to output the hash as a bit array. These opcodes do not implicitly perform the BHP hash on the result. The PR also introduces .raw variants use the raw serialization of the pre-image that does not contain any Aleo specific variant bits.
The list of new instructions are as follows:
hash.keccak256.native
hash.keccak384.native
hash.keccak512.native
hash.sha3_256.native
hash.sha3_384.native
hash.sha3_512.native
hash.keccak256.native.raw
hash.keccak384.native.raw
hash.keccak512.native.raw
hash.sha3_256.native.raw
hash.sha3_384.native.raw
hash.sha3_512.native.raw
hash.keccak256.raw
hash.keccak384.raw
hash.keccak512.raw
hash.sha3_256.raw
hash.sha3_384.raw
hash.sha3_512.raw
Note that all .raw variant inputs must be byte-aligned.
Serialization Operations
This PR also introduces serialize.* and deserialize.* opcodes which allow encoding and decoding AVM values to and from bits respectively.
The list of new instructions are as follows:
serialize.bits
serialize.bits.raw
deserialize.bits
deserialize.bits.raw
Increase Array Size Limit to 512
`N::MAX_ARRAY_ELEMENTS` is updated from 32 to 512. This provides more general flexibility for handling inputs to hash and signature verification functions without having to construct custom structs to get around the existing limitation.
Faster syncing for Aleo nodes
By combining faster deserialization and better pipelined fetching of blocks, syncing was sped up by up to 5 times! Syncing speed is continuously monitored in CI to prevent regressions.
Benchmark suite | Current: 50aa002 | Previous: 83805f6 | Ratio |
rest-get-block | 2.68524245510956 ops/s | 0.65 ops/s | 0.24 |
rest-block-height | 7863.4395405039495 ops/s | 185.18 ops/s | 0.023549491166830062 |
p2p-sync | 1.3 blocks/s | 0.25 blocks/s | 0.19 |
p2p-sync-speed-variance | 1.240252 blocks^2/s^2 | 0.202773 blocks^2/s^2 | 0.16 |
bft-sync | 1.16 blocks/s | 0.2 blocks/s | 0.17 |
cdn-sync | 1.23 blocks/s | 0.43 blocks/s | 0.35 |
More stable transaction and solution broadcast
Both transaction and solution broadcast now offer the same broadcast strategy to ensure even when clients are out of sync they may still propagate transactions and solutions.
Moreover, transactions which hit a "global state root not found" errors are still propagated. In all likelihood, these are caused by nodes temporarily being behind. This PR improves the network's UX by letting those transactions propagate anyway.
Improvements for node operators
Further improvements worth mentioning include:
A local peer cache was introduced for client, prover and validator nodes. The cache is stored in the ledger folder, and allows a node to be able to find its previous (high quality) peers again. Ultimately this paves the way for clients not having to manually specify other clients anymore, as after an initial connection via bootstrap peers, they will always be able to find each other again directly. Connections to new nodes are partially guided by whether they are ahead or not.
Nodes will now shut down when a panic occurs and log a clean error.
This PR implements snarkos account import to allow derivation of view key and address from a given private key, allowing easier testing without the use of the leo binary.
Logs were improved (PR)
This PR introduces a special bootstrap client, which can be enabled by running snarkos start with --bootstrap-client. This client is specialized in facilitating peer list gossip, and doesn’t participate in block syncing.
This PR documents a native backup utility. Node operators should be able to create fast local backups either:
using a JWT, which they can read from a file instead of stdout
explicitly passing in --nojwt, not having to authenticate if they already have a solid firewall and don't want to deal with the complexity of JWT creation and backup
Colored logging is currently disabled to protect against vulnerabilities.
What’s new in Leo v3.3.0
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 vectors supported core 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` from `vec` and returns
// it. The removed element is replaced by the last
// element of the vector.Internally, 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_512Parameters:
sig: [u8; 65] - ECDSA signature (r, s, v)
addr: [u8; 33] - Public key for standard variants, [u8; 20] for *_eth variants
digest: [u8; 32] - Pre-hashed message for digest variants
msg: Any byte-aligned type - Message to hash and verify
Raw Hash Operations
Leo now supports “raw” hash variants: raw hash variants omit metadata of a variable and directly hash the input bits. They are useful for interoperability with external, particularly EVM, systems. Note: inputs for raw variants of Keccak* and Sha3* must by byte-aligned (meaning the number of bits must be a multiple of 8).
// Raw hash functions return the same types as standard variants
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 return bit arrays instead of field elements
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_512Serialization/Deserialization Operation
Leo now supports “serialize” and “deserialize” data operations to and from bits. This supports both metadata-inclusive and raw variants. Note: the compiler will check that the bit sizes actually match.
// Standard serialization (includes type metadata)
let bits: [bool; 58] = Serialize::to_bits(value);
let value: u32 = Deserialize::from_bits::[u32](bits);
// Raw serialization (no metadata, just raw bits)
let bits: [bool; 32] = Serialize::to_bits_raw(value);
let value: u32 = Deserialize::from_bits_raw::[u32](bits);
// Works with arrays too
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
Raw: Exact type width
u32 = 32
field = 253
scalar = 251
address = 253
Non-raw: Type width + 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 this does not directly impact users 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 support displaying multiple related source locations, starting with duplicate struct and record member detection:
struct Person {
name: field,
age: u8,
name: field,
}Before, only the duplicate location was shown. Now both the original and duplicate locations are displayed:
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 hereRemaining stability improvements
Various fixes to the interpreter related to hashing correctness
Fixed broken
leo querycommittee endpointValidates program names in
leo newagainst reserved SnarkVM keywords
Library and tooling updates
snarkVM v4.3.0
snarkVM is a zkVM library. It powers the Aleo network and most of the software used to interact with it. The following contains some of the most relevant changes which were introduced:
The snarkVM CLI was removed - instead we encourage users to make use of the
snarkos developerorleocli tools.Several fixes for WASM-based builds were introduced, e.g. to allow loading the inclusion proving key from memory and supporting custom consensus heights for testing.
Closing notes
The full changelogs for the referenced releases can be found here:
https://github.com/ProvableHQ/snarkVM/compare/mainnet...testnet
https://github.com/ProvableHQ/snarkOS/compare/mainnet...testnet
https://github.com/ProvableHQ/snarkVM/compare/v4.2.2...testnet-v4.3.0
https://github.com/ProvableHQ/snarkOS/compare/v4.2.2…testnet-v4.3.0
If you want to try it out, you can build from source today or download the mainnet release from github October 28th. Find the current release schedule at: https://provablehq.github.io/.
Contributors
A big thank you to all the contributors on Github to this snarkOS, Leo, SDK and snarkVM release!
@antonio95
@copilot
@d0cd
@eranrund
@Forostovec
@henrikkv
@iamalwaysuncomfortable
@kaimast
@ljedrz
@meddle0x53
@mohammadfawaz
@mikebenfield
@niklaslong
@tenequm
@tetektoza
@raychu86
@Roee-87
@usagi32
@vicsn
