Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
e649462
adapt old MTU-XMSS branch
TomWambsgans Apr 4, 2026
9e9e7cd
digests of 4
TomWambsgans Apr 5, 2026
c43ac8f
wip
TomWambsgans Apr 5, 2026
f76b4cb
wip
TomWambsgans Apr 5, 2026
964099f
w
TomWambsgans Apr 5, 2026
3a630cc
w
TomWambsgans Apr 5, 2026
ad7cda3
Merge branch 'main' into mtu-xmss2
TomWambsgans Apr 7, 2026
a76952c
wip
TomWambsgans Apr 7, 2026
5725b9a
wip
TomWambsgans Apr 8, 2026
5740a88
w
TomWambsgans Apr 9, 2026
ced35b0
Revert "wip"
TomWambsgans Apr 9, 2026
0677d7e
harcoded left input, first 4 FE, of poseidon, for tweaks
TomWambsgans Apr 9, 2026
16c79a3
tweak always at left
TomWambsgans Apr 9, 2026
53407a5
new convention index left
TomWambsgans Apr 9, 2026
cf35002
wip
TomWambsgans Apr 9, 2026
f574825
w
TomWambsgans Apr 9, 2026
4fe6770
w
TomWambsgans Apr 9, 2026
50986aa
w
TomWambsgans Apr 9, 2026
670d336
w
TomWambsgans Apr 9, 2026
00913e7
wip
TomWambsgans Apr 9, 2026
c590d9c
w
TomWambsgans Apr 9, 2026
1f0de06
w
TomWambsgans Apr 9, 2026
3558680
Merge branch 'main' into custom-poseidon-precompile
TomWambsgans Apr 9, 2026
1883d55
w
TomWambsgans Apr 9, 2026
1cda812
make 1 column virtual
TomWambsgans Apr 9, 2026
1cfd7e6
Merge branch 'main' into custom-poseidon-precompile
TomWambsgans Apr 9, 2026
4c7e974
wip
TomWambsgans Apr 9, 2026
2139ece
w
TomWambsgans Apr 9, 2026
ce09720
Merge branch 'custom-poseidon-precompile' into mtu-xmss2
TomWambsgans Apr 9, 2026
21265bb
w
TomWambsgans Apr 9, 2026
61ef5e3
remove V_grinding
TomWambsgans Apr 10, 2026
a25e199
MESSAGE_LEN_FE = 8
TomWambsgans Apr 10, 2026
4eb7fcc
w
TomWambsgans Apr 10, 2026
697b889
w
TomWambsgans Apr 10, 2026
49dab0e
w
TomWambsgans Apr 10, 2026
fbfe030
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 10, 2026
19c10c8
w
TomWambsgans Apr 10, 2026
88d5757
harcode PRIVATE_INPUT_START instead of hinting it
TomWambsgans Apr 10, 2026
b3e8a0d
tweaks table: 00 instead 000
TomWambsgans Apr 10, 2026
e5e472b
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 10, 2026
5fa85df
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 10, 2026
da939aa
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 11, 2026
8c7a5c1
wip
TomWambsgans Apr 11, 2026
2e05b6d
wip
TomWambsgans Apr 11, 2026
0da362d
w
TomWambsgans Apr 11, 2026
ef144d2
wip
TomWambsgans Apr 11, 2026
652ca2b
Merge remote-tracking branch 'origin/main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 11, 2026
bd81720
Merge remote-tracking branch 'origin/main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 11, 2026
c206683
add a section "Efficiently verrifying hash-based signatures"
TomWambsgans Apr 11, 2026
29eabc5
naming
TomWambsgans Apr 11, 2026
b5183ce
Merge remote-tracking branch 'origin/main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 13, 2026
0e4f2d7
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 13, 2026
5249624
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 14, 2026
1d65592
n_sigs != 0
TomWambsgans Apr 14, 2026
7228a57
skip the last 5 sumchecks of logup-GKR (send data in clear instead)
TomWambsgans Apr 15, 2026
ec9e2f7
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 15, 2026
06283e8
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 15, 2026
4196051
Merge remote-tracking branch 'origin/main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 19, 2026
abd1008
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 28, 2026
4424e97
bench numbers
TomWambsgans Apr 28, 2026
28ed9e5
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans Apr 30, 2026
4e58a23
latex
TomWambsgans Apr 30, 2026
a29ca49
small improvements
TomWambsgans Apr 30, 2026
9da912b
naming
TomWambsgans Apr 30, 2026
dc98f61
naming
TomWambsgans Apr 30, 2026
7f93044
missing constraint
TomWambsgans Apr 30, 2026
09fb510
w
TomWambsgans May 1, 2026
14cb0a9
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans May 1, 2026
3c828f6
pdf
TomWambsgans May 1, 2026
8f280c9
w
TomWambsgans May 1, 2026
ad48a8b
w
TomWambsgans May 1, 2026
69e47e8
w
TomWambsgans May 1, 2026
063c504
w
TomWambsgans May 1, 2026
43cf677
use 6 field elements for XMSS randomnes
TomWambsgans May 1, 2026
0efa54c
clippy
TomWambsgans May 1, 2026
8b4ed79
w
TomWambsgans May 1, 2026
65a25f4
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans May 1, 2026
dd84b5e
w
TomWambsgans May 1, 2026
8267435
naming
TomWambsgans May 1, 2026
20a6130
README
TomWambsgans May 1, 2026
c7ade99
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans May 1, 2026
911ba17
readme
TomWambsgans May 2, 2026
685ecf5
typos
TomWambsgans May 2, 2026
e4d1afb
Merge branch 'main' into xmss-sub-mtu-ipv6
TomWambsgans May 2, 2026
bf62851
XMSS high-level specification
TomWambsgans May 2, 2026
c5bd2ea
comment
TomWambsgans May 2, 2026
684d0b5
update benchmarks
TomWambsgans May 3, 2026
fbeaaf2
w
TomWambsgans May 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ Documentation: [PDF](minimal_zkVM.pdf)
The VM design is inspired by the famous [Cairo paper](https://eprint.iacr.org/2021/1063.pdf).


## Security

124 bits of provable security, given by Johnson bound + degree 5 extension of koala-bear. (128 bits would require hash digests of more than 8 field elements, todo?). In the benchmarks, we also display performance with conjectured security, even though leanVM targets the proven regime by default.

## Benchmarks

Machine: M4 Max 48GB (CPU only)
Expand All @@ -31,13 +27,14 @@ Machine: M4 Max 48GB (CPU only)
### XMSS aggregation

```bash
cargo run --release -- xmss --n-signatures 1400 --log-inv-rate 1
cargo run --release -- xmss --n-signatures 1500 --log-inv-rate 1
```

| WHIR rate | Proven Regime | Proximity Gaps Conjecture |
| --------- | --------------------- | ------------------------- |
| 1/2 | 1193 XMSS/s - 377 KiB | 1207 XMSS/s - 191 KiB |
| 1/4 | 863 XMSS/s - 243 KiB | 872 XMSS/s - 129 KiB |
| 1/2 | 1319 XMSS/s - 338 KiB | 1345 XMSS/s - 176 KiB |
| 1/4 | 961 XMSS/s - 228 KiB | 969 XMSS/s - 126 KiB |


(Proving throughput - proof size)

Expand All @@ -53,14 +50,15 @@ cargo run --release -- recursion --n 2 --log-inv-rate 2

| n | WHIR rate | Proven Regime | Proximity Gaps Conjecture |
| --- | --------- | --------------------------- | --------------------------- |
| 1 | 1/2 | 0.35s = 1 x 0.35s - 256 KiB | 0.24s = 1 x 0.24s - 146 KiB |
| 1 | 1/4 | 0.33s = 1 x 0.33s - 183 KiB | 0.26s = 1 x 0.26s - 98 KiB |
| 2 | 1/2 | 0.65s = 2 x 0.33s - 272 KiB | 0.43s = 2 x 0.21s - 157 KiB |
| 2 | 1/4 | 0.56s = 2 x 0.28s - 190 KiB | 0.41s = 2 x 0.21s - 101 KiB |
| 3 | 1/2 | 0.83s = 3 x 0.28s - 303 KiB | 0.62s = 3 x 0.21s - 150 KiB |
| 3 | 1/4 | 0.86s = 3 x 0.29s - 192 KiB | 0.71s = 3 x 0.24s - 107 KiB |
| 4 | 1/2 | 1.23s = 4 x 0.31s - 327 KiB | 0.76s = 4 x 0.19s - 166 KiB |
| 4 | 1/4 | 1.01s = 4 x 0.25s - 200 KiB | 0.76s = 4 x 0.19s - 106 KiB |
| 1 | 1/2 | 0.39s = 1 x 0.39s - 278 KiB | 0.24s = 1 x 0.24s - 147 KiB |
| 1 | 1/4 | 0.32s = 1 x 0.32s - 188 KiB | 0.27s = 1 x 0.27s - 100 KiB |
| 2 | 1/2 | 0.7s = 2 x 0.35s - 293 KiB | 0.43s = 2 x 0.21s - 157 KiB |
| 2 | 1/4 | 0.56s = 2 x 0.28s - 194 KiB | 0.43s = 2 x 0.22s - 102 KiB |
| 3 | 1/2 | 0.85s = 3 x 0.28s - 312 KiB | 0.63s = 3 x 0.21s - 150 KiB |
| 3 | 1/4 | 0.94s = 3 x 0.31s - 203 KiB | 0.73s = 3 x 0.24s - 108 KiB |
| 4 | 1/2 | 1.27s = 4 x 0.32s - 308 KiB | 0.78s = 4 x 0.2s - 166 KiB |
| 4 | 1/4 | 1.02s = 4 x 0.26s - 206 KiB | 0.79s = 4 x 0.2s - 108 KiB |



(time for n->1 recursive aggregation - proof size)
Expand All @@ -75,6 +73,20 @@ cargo run --release -- fancy-aggregation

(Proven regime)

## Security

### snark

≈ 124 bits of provable security, given by Johnson bound + degree 5 extension of koala-bear. (128 bits requires bigger hash digests (8 koalabears ≈ 248 bits) -> TODO). In the benchmarks, we also display performance with conjectured security, even though leanVM targets the proven regime by default.

### XMSS

Currently, we use an [XMSS](crates/xmss/xmss.md) with hash digests of 4 field elements ≈ 124 bits. Tweaks and public parameters ensure domain separation. An analysis in the ROM (resp. QROM), inspired by the section 3.1 of [Tight adaptive reprogramming in the QROM](https://arxiv.org/pdf/2010.15103) would lead to ≈ 124 (resp. 62) bits of classical (resp. quantum) security. Going to 128 / 64 bits of classical / quantum security, i.e. NIST level 1 (in the ROM/QROM), is an ongoing effort. It requires either:
- hash digests of 5 field elements (drawback: we need to double the hash chain length from 8 to 16 if we want to stay below one IPv6 MTU = 1280 bytes)
- a new prime, close to 32 bits (typically p = 125.2^25 + 1) or 64 bits ([goldilocks](https://2π.com/22/goldilocks/))

It's important to mention that a security analysis in the ROM / QROM is not the most conservative. In particular, [eprint 2025/055](https://eprint.iacr.org/2025/055.pdf)'s security proof holds in the standard model (at the cost of bigger hash digests): the implementation is available in the [leanSig](https://github.com/leanEthereum/leanSig) repository. A compatible version of leanMultisig can be found in the [devnet4](https://github.com/leanEthereum/leanMultisig/tree/devnet4) branch.

## Credits

- [Plonky3](https://github.com/Plonky3/Plonky3) for its various performant crates
Expand Down
2 changes: 1 addition & 1 deletion crates/backend/fiat-shamir/src/challenger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<F: PrimeField64, P: Compression<[F; WIDTH]>> Challenger<F, P> {
}

pub fn sample_many(&mut self, n: usize) -> Vec<[F; RATE]> {
let mut sampled = Vec::with_capacity(n);
let mut sampled = Vec::with_capacity(n + 1);
for i in 0..n + 1 {
let mut domain_sep = [F::ZERO; RATE];
domain_sep[0] = F::from_usize(i);
Expand Down
22 changes: 20 additions & 2 deletions crates/lean_compiler/snark_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,26 @@ def pop(self):
self._data.pop()


def poseidon16_compress(left, right, output, mode):
_ = left, right, output, mode
def poseidon16_compress(left, right, output):
_ = left, right, output


def poseidon16_compress_half(left, right, output):
"""Poseidon16 compression outputting only the first 4 FE (last 4 unconstrained)."""
_ = left, right, output


def poseidon16_compress_hardcoded_left(left, right, output, offset):
"""Poseidon16 compression where the first 4 FE of the left input are read from
memory[offset..offset+4] instead of memory[left..left+4]. The last 4 FE of the
left input come from memory[left..left+4]. `offset` must be a compile-time
constant expression."""
_ = left, right, output, offset


def poseidon16_compress_half_hardcoded_left(left, right, output, offset):
"""Composition of `poseidon16_compress_half` and `poseidon16_compress_hardcoded_left`."""
_ = left, right, output, offset


def add_be(a, b, result, length=None):
Expand Down
38 changes: 31 additions & 7 deletions crates/lean_compiler/src/a_simplify_lang/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use crate::{
};
use backend::PrimeCharacteristicRing;
use lean_vm::{
Boolean, BooleanExpr, CustomHint, ExtensionOpMode, FunctionName, PrecompileArgs, PrecompileCompTimeArgs,
SourceLocation, Table, TableT,
ALL_POSEIDON16_NAMES, Boolean, BooleanExpr, CustomHint, ExtensionOpMode, FunctionName,
POSEIDON16_HALF_HARDCODED_LEFT_NAME, POSEIDON16_HALF_NAME, POSEIDON16_HARDCODED_LEFT_NAME, PrecompileArgs,
PrecompileCompTimeArgs, SourceLocation,
};
use std::{
collections::{BTreeMap, BTreeSet},
Expand Down Expand Up @@ -2258,28 +2259,51 @@ fn simplify_lines(
continue;
}

// Special handling for poseidon16 precompile
if function_name == Table::poseidon16().name() {
// Special handling for poseidon16 precompile (4 variants)
if ALL_POSEIDON16_NAMES.contains(&function_name.as_str()) {
if !targets.is_empty() {
return Err(format!(
"Precompile {function_name} should not return values, at {location}"
));
}
if args.len() != 3 {
let half_output = [POSEIDON16_HALF_NAME, POSEIDON16_HALF_HARDCODED_LEFT_NAME]
.contains(&function_name.as_str());
let is_hardcoded_left =
[POSEIDON16_HARDCODED_LEFT_NAME, POSEIDON16_HALF_HARDCODED_LEFT_NAME]
.contains(&function_name.as_str());
let expected_args = if is_hardcoded_left { 4 } else { 3 };
if args.len() != expected_args {
let signature = if is_hardcoded_left {
"(ptr_a, ptr_b, ptr_res, offset)"
} else {
"(ptr_a, ptr_b, ptr_res)"
};
return Err(format!(
"Precompile {function_name} expects 3 arguments (ptr_a, ptr_b, ptr_res), got {}, at {location}",
"Precompile {function_name} expects {expected_args} arguments {signature}, got {}, at {location}",
args.len()
));
}
let simplified_args = args
.iter()
.map(|arg| simplify_expr(ctx, state, const_malloc, arg, &mut res))
.collect::<Result<Vec<_>, _>>()?;
let hardcoded_offset_left = if is_hardcoded_left {
Some(simplified_args[3].as_constant().ok_or_else(|| {
format!(
"{function_name}: offset argument must be a compile-time constant, at {location}"
)
})?)
} else {
None
};
res.push(SimpleLine::Precompile(PrecompileArgs {
arg_0: simplified_args[0].clone(),
arg_1: simplified_args[1].clone(),
res: simplified_args[2].clone(),
data: PrecompileCompTimeArgs::Poseidon16,
data: PrecompileCompTimeArgs::Poseidon16 {
half_output,
hardcoded_offset_left,
},
}));
continue;
}
Expand Down
12 changes: 11 additions & 1 deletion crates/lean_compiler/src/instruction_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,17 @@ pub fn field_representation(instr: &Instruction) -> [F; N_INSTRUCTION_COLUMNS] {
}
Instruction::Precompile(precompile) => {
let precompile_data = match &precompile.data {
PrecompileCompTimeArgs::Poseidon16 => POSEIDON_PRECOMPILE_DATA,
PrecompileCompTimeArgs::Poseidon16 {
half_output,
hardcoded_offset_left,
} => {
let flag_left = hardcoded_offset_left.is_some() as usize;
let hardcoded_offset_left_val = hardcoded_offset_left.unwrap_or(0);
POSEIDON_PRECOMPILE_DATA
+ POSEIDON_HALF_OUTPUT_SHIFT * (*half_output as usize)
+ POSEIDON_HARDCODED_LEFT_4_FLAG_SHIFT * flag_left
+ POSEIDON_HARDCODED_LEFT_4_OFFSET_SHIFT * hardcoded_offset_left_val
}
PrecompileCompTimeArgs::ExtensionOp { size, mode } => {
assert!(*size >= 1, "invalid extension_op size={size}");
mode.flag_encoding() + EXT_OP_LEN_MULTIPLIER * size
Expand Down
5 changes: 2 additions & 3 deletions crates/lean_compiler/src/parser/parsers/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
grammar::{ParsePair, Rule},
},
};
use lean_vm::{CUSTOM_HINTS, ExtensionOpMode, POSEIDON16_NAME};
use lean_vm::{ALL_POSEIDON16_NAMES, CUSTOM_HINTS, ExtensionOpMode};

/// Reserved function names that users cannot define.
pub const RESERVED_FUNCTION_NAMES: &[&str] = &[
Expand All @@ -33,8 +33,7 @@ fn is_reserved_function_name(name: &str) -> bool {
if RESERVED_FUNCTION_NAMES.contains(&name) || CUSTOM_HINTS.iter().any(|hint| hint.name() == name) {
return true;
}
// Check precompile names (poseidon16, extension_op functions)
if name == POSEIDON16_NAME {
if ALL_POSEIDON16_NAMES.contains(&name) {
return true;
}
if ExtensionOpMode::from_name(name).is_some() {
Expand Down
63 changes: 62 additions & 1 deletion crates/lean_prover/src/test_zkvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,43 @@ DIM = 5
N = 11
M = 3
DIGEST_LEN = 8
HALF_DIGEST_LEN = 4

def main():
pub_start = 0
poseidon16_compress(pub_start + 4 * DIGEST_LEN, pub_start + 5 * DIGEST_LEN, pub_start + 6 * DIGEST_LEN)

# poseidon16_compress_half: only first 4 FE constrained
full_out = pub_start + 6 * DIGEST_LEN
half_out = pub_start + 80
poseidon16_compress_half(pub_start + 4 * DIGEST_LEN, pub_start + 5 * DIGEST_LEN, half_out)
for i in unroll(0, HALF_DIGEST_LEN):
assert full_out[i] == half_out[i]

# poseidon16_compress_hardcoded_left: with the new convention, only 4 FE are read
# at the left pointer (the 4-element data digest at pub_start + 1496) and the first
# 4 FE of the left input come from memory[pub_start + 1500 .. pub_start + 1504]
# (the hardcoded prefix).
hardcoded_left = pub_start + 1496
hardcoded_full_out = pub_start + 1504
poseidon16_compress_hardcoded_left(
hardcoded_left,
pub_start + 5 * DIGEST_LEN,
hardcoded_full_out,
pub_start + 1500
)

# Same, but only first 4 FE of the output are constrained.
hardcoded_half_out = pub_start + 1512
poseidon16_compress_half_hardcoded_left(
hardcoded_left,
pub_start + 5 * DIGEST_LEN,
hardcoded_half_out,
pub_start + 1500
)
for i in unroll(0, HALF_DIGEST_LEN):
assert hardcoded_full_out[i] == hardcoded_half_out[i]

base_ptr = pub_start + 88
ext_a_ptr = pub_start + 88 + N
ext_b_ptr = pub_start + 88 + N * (DIM + 1)
Expand Down Expand Up @@ -58,9 +90,38 @@ def main():
// Poseidon test data
let poseidon_16_compress_input: [F; 16] = rng.random();
public_input[32..48].copy_from_slice(&poseidon_16_compress_input);
public_input[48..56].copy_from_slice(&poseidon16_compress(poseidon_16_compress_input)[..8]);
let poseidon_output = poseidon16_compress(poseidon_16_compress_input);
public_input[48..56].copy_from_slice(&poseidon_output[..8]);
let poseidon_24_input: [F; 24] = rng.random();
public_input[56..80].copy_from_slice(&poseidon_24_input);
// poseidon16_compress_half output at offset 80: first 4 = hash, last 4 = arbitrary pre-existing data
public_input[80..84].copy_from_slice(&poseidon_output[..4]);
public_input[84..88].copy_from_slice(&[
F::from_usize(111),
F::from_usize(222),
F::from_usize(333),
F::from_usize(444),
]);

let hardcoded_data: [F; 4] = rng.random();
let hardcoded_prefix: [F; 4] = rng.random();
public_input[1496..1500].copy_from_slice(&hardcoded_data);
public_input[1500..1504].copy_from_slice(&hardcoded_prefix);
let mut hardcoded_input = [F::ZERO; 16];
hardcoded_input[..4].copy_from_slice(&hardcoded_prefix);
hardcoded_input[4..8].copy_from_slice(&hardcoded_data);
hardcoded_input[8..16].copy_from_slice(&poseidon_16_compress_input[8..16]);
let hardcoded_output = poseidon16_compress(hardcoded_input);
// Full output at 1504..1512
public_input[1504..1512].copy_from_slice(&hardcoded_output);
// Half output at 1512..1520: first 4 = hash, last 4 = arbitrary pre-existing data
public_input[1512..1516].copy_from_slice(&hardcoded_output[..4]);
public_input[1516..1520].copy_from_slice(&[
F::from_usize(555),
F::from_usize(666),
F::from_usize(777),
F::from_usize(888),
]);

// Extension op operands: base[N], ext_a[N], ext_b[N]
let base_slice: [F; N] = rng.random();
Expand Down
22 changes: 22 additions & 0 deletions crates/lean_prover/src/trace_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,28 @@ pub fn get_execution_trace(bytecode: &Bytecode, execution_result: ExecutionResul
let poseidon_trace = traces.get_mut(&Table::poseidon16()).unwrap();
fill_trace_poseidon_16(&mut poseidon_trace.columns);

// For half_output rows, override last 4 output columns with actual memory values
// (the AIR doesn't constrain them, but the lookup checks against memory).
{
let split = POSEIDON_16_COL_OUTPUT_START + HALF_DIGEST_LEN;
let (left, right) = poseidon_trace.columns.split_at_mut(split);
let half_output_col = &left[POSEIDON_16_COL_FLAG_HALF_OUTPUT];
let res_col = &left[POSEIDON_16_COL_INDEX_INPUT_RES];
let output_cols: &mut [Vec<F>; HALF_DIGEST_LEN] = (&mut right[..HALF_DIGEST_LEN]).try_into().unwrap();

transposed_par_iter_mut(output_cols)
.zip(half_output_col)
.zip(res_col)
.for_each(|((row, &half), &res)| {
if half == F::ONE {
let base = res.to_usize() + HALF_DIGEST_LEN;
for j in 0..HALF_DIGEST_LEN {
*row[j] = memory_padded[base + j];
}
}
});
}

let extension_op_trace = traces.get_mut(&Table::extension_op()).unwrap();
fill_trace_extension_op(extension_op_trace, &memory_padded);

Expand Down
2 changes: 1 addition & 1 deletion crates/lean_vm/src/core/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub const MIN_BYTECODE_LOG_SIZE: usize = 8;
/// Minimum and maximum number of rows per table (as powers of two), both inclusive
pub const MIN_LOG_N_ROWS_PER_TABLE: usize = 8; // Zero padding will be added to each at least, if this minimum is not reached, (ensuring AIR / GKR work fine, with SIMD, without too much edge cases). Long term, we should find a more elegant solution.
pub const MAX_LOG_N_ROWS_PER_TABLE: [(Table, usize); 3] = [
(Table::execution(), 25),
(Table::execution(), 24),
(Table::extension_op(), 21),
(Table::poseidon16(), 21),
];
Expand Down
Loading
Loading