Releases: ProvableHQ/leo
v1.9.2
commit example files
v1.9.1
chore(leo): bump version for new release
v1.9.0
chore(leo): bump version for new release
v1.8.3
chore(leo): bump version for new release
v1.8.2
chore(leo): bump version for new release
v1.8.1
chore(leo): bump version for new release
v1.8.0
chore(leo): bump version for new release
v1.7.2
chore(leo): bump version for new release
v1.7.1
Record refactors
Records in Aleo no longer require the gates field.
“gates” is allowed as a standard field of a record to ensure backwards compatibility.
- Remove gates from tests
- Remove gates from examples
- Remove gates from compiler
Expanded programmability
/// The maximum number of mappings in a program.
const MAX_MAPPINGS: usize = 31;
/// The maximum number of functions in a program.
const MAX_FUNCTIONS: usize = 31;
/// The maximum number of operands in an instruction.
const MAX_OPERANDS: usize = Self::MAX_INPUTS;
/// The maximum number of instructions in a closure or function.
const MAX_INSTRUCTIONS: usize = u16::MAX as usize;
/// The maximum number of commands in finalize.
const MAX_COMMANDS: usize = u16::MAX as usize;
/// The maximum number of inputs per transition.
const MAX_INPUTS: usize = 16;
/// The maximum number of outputs per transition.
const MAX_OUTPUTS: usize = 16;
v1.7.0
Changes
New Syntax - Mapping and finalize
The syntax around mappings and finalize has been updated to support get
, get_or_init
, set
functions.
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_init(counter, addr, 0u64);
Mapping::set(counter, addr, current_value + 1u64);
current_value = Mapping::get(counter, addr);
Mapping::set(counter, addr, current_value + 1u64);
}
}
Mapping
The mapping struct allows the programmer to apply updates to a program mapping data structure by calling one of the following functions.
get
A get command, e.g. current_value = Mapping::get(counter, addr);
Gets the value stored at addr
in counter
and stores the result in current_value
If the value at addr
does not exist, then the program will fail to execute.
get_or_init
A get command that initializes the mapping in case of failure, e.g.
let current_value: u64 = Mapping::get_or_init(counter, addr, 0u64);
Gets the value stored at addr
in counter
and stores the result in current_value
.
If the key is not present, 0u64
is stored in counter
and stored in current_value
.
set
A set command, e.g. Mapping::set(counter, addr, current_value + 1u64);
Sets the addr
entry as current_value + 1u64
in counter
.
Deprecated Syntax
increment
The increment function has been removed from Leo syntax.
Developers can increment values in a mapping using set
decrement
The decrement function has been removed from Leo syntax.
Developers can decrement values in a mapping using set
New Syntax - Inlined Functions
Users can specify inlined functions in the following way:
inline foo(a: u8, b: u8) -> u8 { ... }
With this new addition, the rules for functions (in the traditional sense) are as follows:
- There are three variants of functions: transition, function, inline.
- transitions can only call functions and inlines.
- functions can only call inlines.
- inlines can only call inlines.
- Direct/indirect recursive calls are not allowed
New Optimization - Dead Code Elimination
Dead Code Elimination (DCE) - The Leo compiler will identify and remove unused code blocks in programs - simplifying the generated zk circuit and saving the programmer credits for every execution.
Lets look at DCE in action. Consider the following Leo program and its generated bytecode.
program test.aleo {
record dummy {
owner: address,
gates: u64,
data: u8,
inline inline_and_eliminate(a: u8, b: u8) -> u8 {
return a * b;
}
transition foo(a: u8, b: u8) -> u8 {
let c: u8 = a + b;
let d: u8 = 0u8;
if (a == b) {
d = inline_and_eliminate(a, b);
}
let e: dummy = dummy {
owner: self.caller,
gates: 0u64,
data: d,
};
return a + b;
}
}
Initial Leo program above.
program test.aleo;
record dummy:
owner as address.private;
gates as u64.private;
data as u8.private;
function foo:
input r0 as u8.private;
input r1 as u8.private;
add r0 r1 into r2;
output r2 as u8.private;
Compiled bytecode above.
Observation One
Observe that the inline function inline_and_eliminate
is not produced in the compiled bytecode. Under the hood, Leo substitutes the code directly from inline_and_eliminate
into the transition function foo
where it is used. After the code is substituted, the inline function can be removed from the program tree.
Observation Two
Observe that the variables d
, e
, and the Leo code computing their values are not produced in the compiled bytecode. Leo’s DCE optimization has been hard at work recognizing that the if conditional, the inline function and the record creation are not output by the transition function foo. The last line of the initial program return a + b
is the only computation executed by foo
. All other code is unnecessary computation in the program and marked as “dead”. Leo will remove the dead code from the program tree before the resulting bytecode is generated.