Skip to content

test: Serialization FFI error cases#376

Merged
romanzac merged 9 commits intomasterfrom
test-ffi-error-cases
Feb 28, 2026
Merged

test: Serialization FFI error cases#376
romanzac merged 9 commits intomasterfrom
test-ffi-error-cases

Conversation

@romanzac
Copy link
Contributor

@romanzac romanzac commented Feb 11, 2026

Description

A batch of tests to cover serialization and FFI error cases for witness/proof values. C FFI coverage extension for out-of-bounds, bad depth, and invalid inputs.

Tests modified/added

rln/tests/ffi.rs:

  • test_witness_serialization_truncated_extra_bytes_ffi
  • test_proof_values_serialization_truncated_bytes_ffi
  • test_rln_invalid_witness_input_ffi
  • test_rln_out_of_bounds_ffi

rln/tests/protocol.rs:

  • test_witness_serialization
  • test_witness_serialization_be_roundtrip_and_length_check
  • test_proof_values_serialization_be_roundtrip

Issues reported

Coverage changed

Before 88.05%
Download HTML Report

After 88.98%
Download HTML Report

Checklist

  • I have run the CI coverage report. Add the run-coverage label to this PR to enable it.

test: truncated extra bytes rejection
@romanzac romanzac marked this pull request as ready for review February 11, 2026 06:37
@github-actions
Copy link

Benchmark for 7c72fdd

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.2±0.22µs 17.2±0.15µs 0.00%
FullMerkleTree::get 0.8±0.01ns 0.8±0.00ns 0.00%
FullMerkleTree::get_empty_leaves_indices 982.1±12.63µs 982.7±8.70µs +0.06%
FullMerkleTree::get_subtree_root 11.7±0.08ns 11.7±0.12ns 0.00%
FullMerkleTree::override_range 2.3±0.13ms 2.3±0.09ms 0.00%
FullMerkleTree::set 8.6±0.10µs 8.6±0.07µs 0.00%
OptimalMerkleTree::delete 19.9±0.28µs 19.9±0.26µs 0.00%
OptimalMerkleTree::get 50.2±0.51ns 50.3±0.33ns +0.20%
OptimalMerkleTree::get_empty_leaves_indices 981.0±4.47µs 982.0±12.54µs +0.10%
OptimalMerkleTree::get_subtree_root 26.0±0.18ns 25.8±0.22ns -0.77%
OptimalMerkleTree::override_range 4.0±0.23ms 4.2±0.49ms +5.00%
OptimalMerkleTree::set 10.0±0.12µs 10.0±0.32µs 0.00%
poseidon Fr/Array hash/10 138.7±0.25µs 70.4 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1489.8±2.08µs 65.5 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.03ms 65.0 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.02µs 69.4 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for 7c72fdd

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 13.4±0.20ms 13.6±0.15ms +1.49%
OptimalMerkleTree::::full_depth_gen 799.8±4.92µs 785.7±6.49µs -1.76%
Pmtree::delete 50.9±0.34µs 50.9±0.41µs 0.00%
Pmtree::get 364.9±7.55ns 370.2±5.50ns +1.45%
Pmtree::get_empty_leaves_indices 5.0±0.05ns 5.0±0.08ns 0.00%
Pmtree::get_subtree_root 504.2±3.71ns 510.9±7.52ns +1.33%
Pmtree::override_range 103.2±3.27µs 103.7±5.88µs +0.48%
Pmtree::set 50.8±0.45µs 50.9±0.49µs +0.20%
Set/FullMerkleTree::::set/1 515.0±3.09µs 515.7±4.46µs +0.14%
Set/FullMerkleTree::::set/10 5.2±0.01ms 5.1±0.05ms -1.92%
Set/FullMerkleTree::::set/100 51.8±0.08ms 51.5±0.09ms -0.58%
Set/FullMerkleTree::::set_range/1 510.8±4.50µs 515.1±5.05µs +0.84%
Set/FullMerkleTree::::set_range/10 702.1±2.73µs 702.9±5.61µs +0.11%
Set/FullMerkleTree::::set_range/100 1885.2±17.69µs 1883.9±28.00µs -0.07%
Set/OptimalMerkleTree::::set/1 512.6±3.59µs 517.3±5.08µs +0.92%
Set/OptimalMerkleTree::::set/10 5.2±0.04ms 5.2±0.01ms 0.00%
Set/OptimalMerkleTree::::set/100 51.8±0.30ms 52.0±0.36ms +0.39%
Set/OptimalMerkleTree::::set_range/1 517.0±5.16µs 517.1±4.66µs +0.02%
Set/OptimalMerkleTree::::set_range/10 711.9±6.69µs 724.9±18.55µs +1.83%
Set/OptimalMerkleTree::::set_range/100 1866.5±52.94µs 1868.8±39.65µs +0.12%

@romanzac
Copy link
Contributor Author

romanzac commented Feb 11, 2026

Hi @vinhtc27, I've noticed while writing tests for FFI, that ffi_rln_new doesn't validate tree depth. Would it make sense to add extra large tree depth validation with warning or error ?

My second question: Extra-bytes behavior for proof values is undocumented in tests; both Rust (rln/tests/protocol.rs:198) and FFI (rln/tests/ffi.rs:971) only test truncation, so any silent acceptance of trailing bytes would go unnoticed. Should we reject extra bytes with error or silently ignore them or just document in comment ?

@github-actions
Copy link

Benchmark for a55667f

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.3±0.83µs 17.2±0.38µs -0.58%
FullMerkleTree::get 0.8±0.02ns 0.8±0.01ns 0.00%
FullMerkleTree::get_empty_leaves_indices 981.4±5.00µs 981.4±5.22µs 0.00%
FullMerkleTree::get_subtree_root 11.7±0.11ns 11.8±0.65ns +0.85%
FullMerkleTree::override_range 2.3±0.10ms 2.3±0.07ms 0.00%
FullMerkleTree::set 8.6±0.13µs 8.6±0.12µs 0.00%
OptimalMerkleTree::delete 19.9±0.28µs 20.1±1.13µs +1.01%
OptimalMerkleTree::get 51.4±0.40ns 51.5±0.30ns +0.19%
OptimalMerkleTree::get_empty_leaves_indices 981.3±8.32µs 981.9±9.09µs +0.06%
OptimalMerkleTree::get_subtree_root 25.9±0.34ns 25.9±0.33ns 0.00%
OptimalMerkleTree::override_range 4.6±0.65ms 4.5±0.58ms -2.17%
OptimalMerkleTree::set 10.0±0.13µs 10.2±0.25µs +2.00%
poseidon Fr/Array hash/10 139.2±0.49µs 70.1 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1496.4±3.14µs 65.3 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.1±0.04ms 64.9 KElem/sec N/A N/A
poseidon Fr/Single hash 13.7±0.02µs 69.4 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for a55667f

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 13.7±0.16ms 12.7±0.15ms -7.30%
OptimalMerkleTree::::full_depth_gen 782.9±6.77µs 782.1±7.68µs -0.10%
Pmtree::delete 50.7±0.33µs 51.3±3.68µs +1.18%
Pmtree::get 352.9±6.04ns 365.2±6.08ns +3.49%
Pmtree::get_empty_leaves_indices 5.0±0.11ns 5.0±0.08ns 0.00%
Pmtree::get_subtree_root 502.7±16.17ns 505.4±9.00ns +0.54%
Pmtree::override_range 101.7±0.89µs 102.6±1.19µs +0.88%
Pmtree::set 50.7±0.43µs 51.2±1.55µs +0.99%
Set/FullMerkleTree::::set/1 516.9±7.55µs 520.2±4.91µs +0.64%
Set/FullMerkleTree::::set/10 5.1±0.01ms 5.2±0.01ms +1.96%
Set/FullMerkleTree::::set/100 51.8±0.19ms 52.2±0.11ms +0.77%
Set/FullMerkleTree::::set_range/1 515.9±2.74µs 520.4±5.18µs +0.87%
Set/FullMerkleTree::::set_range/10 702.5±5.12µs 707.6±4.23µs +0.73%
Set/FullMerkleTree::::set_range/100 1888.0±37.06µs 1904.3±37.74µs +0.86%
Set/OptimalMerkleTree::::set/1 518.9±5.35µs 516.7±4.44µs -0.42%
Set/OptimalMerkleTree::::set/10 5.2±0.07ms 5.2±0.03ms 0.00%
Set/OptimalMerkleTree::::set/100 52.2±0.41ms 52.1±0.64ms -0.19%
Set/OptimalMerkleTree::::set_range/1 518.3±4.74µs 511.1±5.04µs -1.39%
Set/OptimalMerkleTree::::set_range/10 716.4±5.56µs 709.7±12.05µs -0.94%
Set/OptimalMerkleTree::::set_range/100 1866.9±43.25µs 1853.9±29.38µs -0.70%

@github-actions
Copy link

Benchmark for f7156a1

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.3±0.20µs 17.2±0.17µs -0.58%
FullMerkleTree::get 0.8±0.00ns 0.8±0.01ns 0.00%
FullMerkleTree::get_empty_leaves_indices 983.8±7.46µs 983.0±6.43µs -0.08%
FullMerkleTree::get_subtree_root 11.7±0.08ns 11.7±0.16ns 0.00%
FullMerkleTree::override_range 2.3±0.12ms 2.3±0.08ms 0.00%
FullMerkleTree::set 8.6±0.09µs 8.6±0.10µs 0.00%
OptimalMerkleTree::delete 20.2±0.25µs 20.6±1.13µs +1.98%
OptimalMerkleTree::get 52.4±0.55ns 56.9±18.22ns +8.59%
OptimalMerkleTree::get_empty_leaves_indices 982.9±8.15µs 982.5±5.24µs -0.04%
OptimalMerkleTree::get_subtree_root 25.8±0.36ns 26.2±0.50ns +1.55%
OptimalMerkleTree::override_range 6.4±0.65ms 5.6±0.27ms -12.50%
OptimalMerkleTree::set 10.3±0.13µs 10.4±0.12µs +0.97%
poseidon Fr/Array hash/10 140.7±0.31µs 69.4 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1489.3±1.58µs 65.6 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.01ms 65.0 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.01µs 69.3 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for f7156a1

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 12.8±0.20ms 14.0±0.41ms +9.38%
OptimalMerkleTree::::full_depth_gen 800.9±16.94µs 779.7±5.70µs -2.65%
Pmtree::delete 51.1±1.83µs 51.2±1.88µs +0.20%
Pmtree::get 351.4±9.20ns 352.7±5.11ns +0.37%
Pmtree::get_empty_leaves_indices 5.0±0.08ns 5.0±0.06ns 0.00%
Pmtree::get_subtree_root 493.7±11.14ns 495.0±10.09ns +0.26%
Pmtree::override_range 102.2±0.75µs 101.8±0.96µs -0.39%
Pmtree::set 50.8±0.76µs 51.2±1.16µs +0.79%
Set/FullMerkleTree::::set/1 510.9±3.70µs 517.6±7.10µs +1.31%
Set/FullMerkleTree::::set/10 5.2±0.02ms 5.1±0.02ms -1.92%
Set/FullMerkleTree::::set/100 51.9±0.31ms 51.7±0.30ms -0.39%
Set/FullMerkleTree::::set_range/1 510.7±3.38µs 514.0±4.66µs +0.65%
Set/FullMerkleTree::::set_range/10 701.8±4.33µs 703.7±8.39µs +0.27%
Set/FullMerkleTree::::set_range/100 1890.5±21.80µs 1890.6±23.43µs +0.01%
Set/OptimalMerkleTree::::set/1 519.1±12.20µs 517.2±11.12µs -0.37%
Set/OptimalMerkleTree::::set/10 5.2±0.01ms 5.2±0.01ms 0.00%
Set/OptimalMerkleTree::::set/100 51.6±0.26ms 51.9±0.21ms +0.58%
Set/OptimalMerkleTree::::set_range/1 517.5±4.01µs 517.7±6.24µs +0.04%
Set/OptimalMerkleTree::::set_range/10 722.0±11.68µs 721.0±13.20µs -0.14%
Set/OptimalMerkleTree::::set_range/100 1859.9±36.75µs 1868.1±35.96µs +0.44%

@github-actions
Copy link

Benchmark for dc8c9de

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.2±0.25µs 17.2±0.19µs 0.00%
FullMerkleTree::get 0.8±0.01ns 0.8±0.01ns 0.00%
FullMerkleTree::get_empty_leaves_indices 982.8±7.66µs 981.7±5.09µs -0.11%
FullMerkleTree::get_subtree_root 11.7±0.14ns 11.7±0.27ns 0.00%
FullMerkleTree::override_range 2.3±0.14ms 2.3±0.06ms 0.00%
FullMerkleTree::set 8.6±0.22µs 8.6±0.11µs 0.00%
OptimalMerkleTree::delete 20.2±0.29µs 20.3±0.42µs +0.50%
OptimalMerkleTree::get 51.2±0.60ns 50.7±0.50ns -0.98%
OptimalMerkleTree::get_empty_leaves_indices 982.8±7.86µs 981.5±5.31µs -0.13%
OptimalMerkleTree::get_subtree_root 26.0±0.88ns 25.8±0.44ns -0.77%
OptimalMerkleTree::override_range 5.8±0.23ms 5.6±0.28ms -3.45%
OptimalMerkleTree::set 10.3±0.13µs 10.4±0.34µs +0.97%
poseidon Fr/Array hash/10 138.8±0.27µs 70.3 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1490.9±4.55µs 65.5 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.1±0.17ms 64.8 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.04µs 69.2 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for dc8c9de

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 12.9±0.19ms 12.9±0.21ms 0.00%
OptimalMerkleTree::::full_depth_gen 798.7±10.35µs 796.4±7.48µs -0.29%
Pmtree::delete 50.7±0.39µs 50.7±0.53µs 0.00%
Pmtree::get 356.2±8.13ns 353.5±6.78ns -0.76%
Pmtree::get_empty_leaves_indices 5.0±0.08ns 5.0±0.08ns 0.00%
Pmtree::get_subtree_root 493.3±7.53ns 494.9±27.16ns +0.32%
Pmtree::override_range 99.3±1.91µs 98.8±0.96µs -0.50%
Pmtree::set 50.7±0.41µs 50.7±0.49µs 0.00%
Set/FullMerkleTree::::set/1 515.5±4.73µs 511.7±6.01µs -0.74%
Set/FullMerkleTree::::set/10 5.2±0.01ms 5.2±0.01ms 0.00%
Set/FullMerkleTree::::set/100 51.8±0.06ms 51.8±0.06ms 0.00%
Set/FullMerkleTree::::set_range/1 515.7±4.28µs 516.0±4.45µs +0.06%
Set/FullMerkleTree::::set_range/10 702.3±2.87µs 702.7±4.24µs +0.06%
Set/FullMerkleTree::::set_range/100 1874.0±25.75µs 1877.4±29.03µs +0.18%
Set/OptimalMerkleTree::::set/1 517.0±2.33µs 516.8±4.33µs -0.04%
Set/OptimalMerkleTree::::set/10 5.2±0.01ms 5.2±0.01ms 0.00%
Set/OptimalMerkleTree::::set/100 52.0±0.09ms 52.2±1.40ms +0.38%
Set/OptimalMerkleTree::::set_range/1 519.1±15.37µs 517.1±3.70µs -0.39%
Set/OptimalMerkleTree::::set_range/10 704.8±11.10µs 707.3±5.93µs +0.35%
Set/OptimalMerkleTree::::set_range/100 1851.7±49.08µs 1855.7±33.48µs +0.22%

@romanzac romanzac added the run-coverage Runs coverage report in CI once added to the open PR. label Feb 12, 2026
@github-actions
Copy link

Benchmark for 4288136

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.2±0.20µs 17.3±0.17µs +0.58%
FullMerkleTree::get 0.8±0.00ns 0.8±0.00ns 0.00%
FullMerkleTree::get_empty_leaves_indices 981.9±7.57µs 981.6±8.18µs -0.03%
FullMerkleTree::get_subtree_root 11.7±0.12ns 11.8±0.54ns +0.85%
FullMerkleTree::override_range 2.3±0.06ms 2.3±0.09ms 0.00%
FullMerkleTree::set 8.6±0.09µs 8.6±0.13µs 0.00%
OptimalMerkleTree::delete 20.1±0.27µs 20.1±0.18µs 0.00%
OptimalMerkleTree::get 50.9±0.53ns 51.8±0.48ns +1.77%
OptimalMerkleTree::get_empty_leaves_indices 982.1±8.88µs 981.9±6.95µs -0.02%
OptimalMerkleTree::get_subtree_root 25.9±0.33ns 26.1±0.36ns +0.77%
OptimalMerkleTree::override_range 5.3±0.15ms 5.5±0.20ms +3.77%
OptimalMerkleTree::set 10.2±0.20µs 10.4±0.90µs +1.96%
poseidon Fr/Array hash/10 138.7±0.15µs 70.4 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1490.2±1.59µs 65.5 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.01ms 65.0 KElem/sec N/A N/A
poseidon Fr/Single hash 13.7±0.02µs 69.4 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for 4288136

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 13.7±0.14ms 12.9±0.15ms -5.84%
OptimalMerkleTree::::full_depth_gen 783.0±9.89µs 801.7±24.84µs +2.39%
Pmtree::delete 50.7±0.30µs 50.8±0.74µs +0.20%
Pmtree::get 360.8±8.11ns 389.2±10.83ns +7.87%
Pmtree::get_empty_leaves_indices 5.0±0.07ns 5.0±0.06ns 0.00%
Pmtree::get_subtree_root 499.0±7.31ns 532.3±14.62ns +6.67%
Pmtree::override_range 101.6±0.96µs 101.7±0.78µs +0.10%
Pmtree::set 50.8±0.88µs 50.8±0.55µs 0.00%
Set/FullMerkleTree::::set/1 517.5±4.25µs 515.8±5.32µs -0.33%
Set/FullMerkleTree::::set/10 5.2±0.01ms 5.1±0.02ms -1.92%
Set/FullMerkleTree::::set/100 52.0±0.11ms 51.8±0.08ms -0.38%
Set/FullMerkleTree::::set_range/1 517.2±5.08µs 516.6±5.94µs -0.12%
Set/FullMerkleTree::::set_range/10 707.1±11.95µs 701.9±3.36µs -0.74%
Set/FullMerkleTree::::set_range/100 1889.5±22.33µs 1884.4±25.56µs -0.27%
Set/OptimalMerkleTree::::set/1 513.7±10.23µs 512.3±3.58µs -0.27%
Set/OptimalMerkleTree::::set/10 5.2±0.08ms 5.2±0.11ms 0.00%
Set/OptimalMerkleTree::::set/100 51.7±0.22ms 51.7±0.81ms 0.00%
Set/OptimalMerkleTree::::set_range/1 517.2±7.45µs 517.6±7.95µs +0.08%
Set/OptimalMerkleTree::::set_range/10 715.2±12.39µs 715.5±7.35µs +0.04%
Set/OptimalMerkleTree::::set_range/100 1864.3±57.09µs 1858.3±38.80µs -0.32%

@github-actions
Copy link

Coverage report uploaded. Download HTML Report

@vinhtc27
Copy link
Contributor

Hi @vinhtc27, I've noticed while writing tests for FFI, that ffi_rln_new doesn't validate tree depth. Would it make sense to add extra large tree depth validation with warning or error ?

Can you explain what you mean by “validate the tree depth”? The FFI is basically just a wrapper around the public API, so it behaves the same as the public API, and all validation is performed within the public API functionality.

My second question: Extra-bytes behavior for proof values is undocumented in tests; both Rust (rln/tests/protocol.rs:198) and FFI (rln/tests/ffi.rs:971) only test truncation, so any silent acceptance of trailing bytes would go unnoticed. Should we reject extra bytes with error or silently ignore them or just document in comment ?

You can safely ignore it. I’m working on another Zerokit feature (PR) that allows us to pack multiple message IDs at once, which will extend the serialized bytes at the end. If we add validation for extra bytes, it’s going to conflict with this.
Any features will append their serialization data to the end of the current format, so it’s better to keep it compatible.

@romanzac
Copy link
Contributor Author

Hi @vinhtc27, I've noticed while writing tests for FFI, that ffi_rln_new doesn't validate tree depth. Would it make sense to add extra large tree depth validation with warning or error ?

Can you explain what you mean by “validate the tree depth”? The FFI is basically just a wrapper around the public API, so it behaves the same as the public API, and all validation is performed within the public API functionality.

My second question: Extra-bytes behavior for proof values is undocumented in tests; both Rust (rln/tests/protocol.rs:198) and FFI (rln/tests/ffi.rs:971) only test truncation, so any silent acceptance of trailing bytes would go unnoticed. Should we reject extra bytes with error or silently ignore them or just document in comment ?

You can safely ignore it. I’m working on another Zerokit feature (PR) that allows us to pack multiple message IDs at once, which will extend the serialized bytes at the end. If we add validation for extra bytes, it’s going to conflict with this. Any features will append their serialization data to the end of the current format, so it’s better to keep it compatible.

What I mean is large tree depth could result in a massive amount of memory consumed. Merkle tree of depth 50 would need ~32 PiB storing 32B leaves only.

@vinhtc27
Copy link
Contributor

What I mean is large tree depth could result in a massive amount of memory consumed. Merkle tree of depth 50 would need ~32 PiB storing 32B leaves only.

That's okay. The user can input any depth value they want as long as they provide the correct resources, such as the proving key and graph file. The tree will grow leaf by leaf, so it won't be a problem in term of memory.

@seemenkina
Copy link
Collaborator

@romanzac looks good

Can you just check that test_rln_out_of_bounds_ffi covers all leaf operations , but ffi_delete_leaf with an out-of-bounds index is missing

@romanzac romanzac removed the run-coverage Runs coverage report in CI once added to the open PR. label Feb 14, 2026
@github-actions
Copy link

Benchmark for ec3a8b8

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.2±0.17µs 17.2±0.25µs 0.00%
FullMerkleTree::get 0.8±0.01ns 0.8±0.00ns 0.00%
FullMerkleTree::get_empty_leaves_indices 982.9±8.79µs 981.7±5.50µs -0.12%
FullMerkleTree::get_subtree_root 11.7±0.14ns 11.7±0.09ns 0.00%
FullMerkleTree::override_range 2.3±0.10ms 2.3±0.10ms 0.00%
FullMerkleTree::set 8.6±0.12µs 8.6±0.15µs 0.00%
OptimalMerkleTree::delete 19.9±0.19µs 20.0±0.37µs +0.50%
OptimalMerkleTree::get 50.2±0.36ns 51.4±1.61ns +2.39%
OptimalMerkleTree::get_empty_leaves_indices 983.7±12.35µs 982.4±6.65µs -0.13%
OptimalMerkleTree::get_subtree_root 25.8±0.24ns 26.3±1.88ns +1.94%
OptimalMerkleTree::override_range 4.4±0.19ms 4.6±0.20ms +4.55%
OptimalMerkleTree::set 10.0±0.15µs 10.0±0.19µs 0.00%
poseidon Fr/Array hash/10 139.0±0.28µs 70.3 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1490.6±2.34µs 65.5 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.01ms 65.0 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.02µs 69.3 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for ec3a8b8

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 12.6±0.13ms 12.5±0.09ms -0.79%
OptimalMerkleTree::::full_depth_gen 695.4±9.95µs 684.1±6.93µs -1.62%
Pmtree::delete 50.9±0.51µs 50.9±0.83µs 0.00%
Pmtree::get 350.1±5.38ns 349.9±5.10ns -0.06%
Pmtree::get_empty_leaves_indices 5.0±0.05ns 5.0±0.05ns 0.00%
Pmtree::get_subtree_root 502.1±7.29ns 499.6±5.92ns -0.50%
Pmtree::override_range 100.8±1.21µs 100.6±1.14µs -0.20%
Pmtree::set 50.7±0.39µs 50.9±0.75µs +0.39%
Set/FullMerkleTree::::set/1 511.4±5.37µs 514.1±6.02µs +0.53%
Set/FullMerkleTree::::set/10 5.2±0.03ms 5.1±0.01ms -1.92%
Set/FullMerkleTree::::set/100 52.3±2.86ms 51.9±0.07ms -0.76%
Set/FullMerkleTree::::set_range/1 511.2±5.04µs 511.7±5.35µs +0.10%
Set/FullMerkleTree::::set_range/10 703.3±3.72µs 706.0±10.31µs +0.38%
Set/FullMerkleTree::::set_range/100 1852.4±34.20µs 1851.0±52.53µs -0.08%
Set/OptimalMerkleTree::::set/1 518.2±5.13µs 517.9±3.82µs -0.06%
Set/OptimalMerkleTree::::set/10 5.2±0.01ms 5.2±0.24ms 0.00%
Set/OptimalMerkleTree::::set/100 52.5±2.82ms 52.0±0.15ms -0.95%
Set/OptimalMerkleTree::::set_range/1 517.9±6.00µs 511.8±2.08µs -1.18%
Set/OptimalMerkleTree::::set_range/10 715.3±5.48µs 719.5±3.72µs +0.59%
Set/OptimalMerkleTree::::set_range/100 1810.9±44.89µs 1811.7±46.74µs +0.04%

@github-actions
Copy link

Benchmark for 1466125

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.2±0.24µs 17.4±1.38µs +1.16%
FullMerkleTree::get 0.8±0.00ns 0.8±0.05ns 0.00%
FullMerkleTree::get_empty_leaves_indices 982.0±6.67µs 982.8±8.98µs +0.08%
FullMerkleTree::get_subtree_root 11.7±0.12ns 11.7±0.23ns 0.00%
FullMerkleTree::override_range 2.4±0.12ms 2.3±0.10ms -4.17%
FullMerkleTree::set 8.6±0.09µs 8.6±0.15µs 0.00%
OptimalMerkleTree::delete 20.4±0.21µs 19.9±0.30µs -2.45%
OptimalMerkleTree::get 50.4±0.43ns 50.5±0.39ns +0.20%
OptimalMerkleTree::get_empty_leaves_indices 982.1±7.78µs 983.8±12.97µs +0.17%
OptimalMerkleTree::get_subtree_root 25.9±0.61ns 25.9±0.25ns 0.00%
OptimalMerkleTree::override_range 4.5±0.18ms 4.6±0.26ms +2.22%
OptimalMerkleTree::set 10.2±0.14µs 10.2±0.44µs 0.00%
poseidon Fr/Array hash/10 139.4±1.96µs 70.1 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1511.7±70.43µs 64.6 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.05ms 64.9 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.03µs 69.3 MElem/sec N/A N/A

@github-actions
Copy link

Benchmark for 1466125

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 10.3±0.15ms 12.9±0.12ms +25.24%
OptimalMerkleTree::::full_depth_gen 696.7±6.47µs 695.4±10.73µs -0.19%
Pmtree::delete 50.9±0.70µs 50.8±0.25µs -0.20%
Pmtree::get 353.7±7.09ns 369.7±15.14ns +4.52%
Pmtree::get_empty_leaves_indices 5.0±0.06ns 5.0±0.06ns 0.00%
Pmtree::get_subtree_root 501.3±8.86ns 506.3±11.18ns +1.00%
Pmtree::override_range 100.2±0.75µs 100.1±1.11µs -0.10%
Pmtree::set 50.8±0.37µs 50.9±0.81µs +0.20%
Set/FullMerkleTree::::set/1 511.2±3.58µs 516.7±5.66µs +1.08%
Set/FullMerkleTree::::set/10 5.1±0.02ms 5.2±0.01ms +1.96%
Set/FullMerkleTree::::set/100 51.9±0.44ms 51.9±0.20ms 0.00%
Set/FullMerkleTree::::set_range/1 515.8±3.44µs 516.3±4.97µs +0.10%
Set/FullMerkleTree::::set_range/10 703.8±5.42µs 704.4±11.40µs +0.09%
Set/FullMerkleTree::::set_range/100 1849.4±27.94µs 1853.9±45.62µs +0.24%
Set/OptimalMerkleTree::::set/1 518.5±2.91µs 517.8±5.34µs -0.14%
Set/OptimalMerkleTree::::set/10 5.2±0.01ms 5.2±0.01ms 0.00%
Set/OptimalMerkleTree::::set/100 52.0±0.10ms 52.0±0.13ms 0.00%
Set/OptimalMerkleTree::::set_range/1 518.3±3.85µs 512.9±5.25µs -1.04%
Set/OptimalMerkleTree::::set_range/10 713.7±5.83µs 715.5±18.09µs +0.25%
Set/OptimalMerkleTree::::set_range/100 1829.6±53.39µs 1810.7±39.49µs -1.03%

@romanzac
Copy link
Contributor Author

@romanzac looks good

Can you just check that test_rln_out_of_bounds_ffi covers all leaf operations , but ffi_delete_leaf with an out-of-bounds index is missing

Thanks for the reminder. Test with better coverage helped to discover #379

@seemenkina
Copy link
Collaborator

@romanzac looks good
Can you just check that test_rln_out_of_bounds_ffi covers all leaf operations , but ffi_delete_leaf with an out-of-bounds index is missing

Thanks for the reminder. Test with better coverage helped to discover #379

thank you, I'll make a fix

@seemenkina
Copy link
Collaborator

@romanzac fix in PR #380, waiting approve from team, you can also take a look

…validation (#380)

## Description

This PR fixes panic paths in `override_range`/`atomic_operation` flows
by centralizing index/range validation and converting invalid inputs
into explicit errors.

Related to: #379 


## Changes

- Added shared validator: `validate_override_range_inputs` in
`utils/src/merkle_tree/override_range_validation.rs`.
- Added `EmptyIndicesPolicy` enum (`Allow` / `Reject`) to make
adapter-specific empty-index behavior explicit.
- Wired shared validation into:
  - `rln/src/pm_tree_adapter.rs`
  - `utils/src/merkle_tree/full_merkle_tree.rs`
  - `utils/src/merkle_tree/optimal_merkle_tree.rs`
- Replaced unchecked `start + leaves_len` arithmetic with `checked_add`
in `set_range` paths for full/optimal trees.

## Testing

- Added regression coverage in `rln/tests/ffi.rs` for:
- valid `start` with problematic delete-index layout (previously could
panic),
  - overflowing `start` (`usize::MAX`) in atomic operation.
- Added unit tests for validator behavior (overflow, incompatible mixed
offsets, empty-index policy).

Covered scenarios:
- OOB delete indices now return error (no panic).
- Overflow in `start + leaves_len` now returns error (no panic).
- Empty-index handling is explicit per implementation via
`EmptyIndicesPolicy`.

---

## PR Lifecycle

> [!IMPORTANT]
> **Draft PRs** signal that work is still in progress and **will not
trigger CI**.
> Only mark your PR as **Ready for review** when you believe it is
complete.
> All CI checks **must pass** before requesting a review.

## Code Guidelines

Please keep the following in mind (see
[CONTRIBUTING.md](../CONTRIBUTING.md) for full details):

### Commits

- Follow [Conventional
Commits](https://www.conventionalcommits.org/en/v1.0.0/) (`feat(rln):`,
`fix(utils):`, `chore:`, etc.)
- Use the appropriate scope: `rln`, `rln-cli`, `rln-wasm`, `utils`, `ci`
- GPG-sign your commits

### Error Handling

- **No panics in library code.** Do not use `unwrap()`, `expect()`, or
`panic!()`
  in production paths inside `rln/src/` or `utils/src/`.
The only acceptable exception is an internal invariant that is
statically guaranteed — and even then,
  prefer returning an error.
- Use the project's `thiserror`-based error types (`RLNError`,
`ProtocolError`, `UtilsError`, etc.)
  and propagate errors with `?`.
- Provide context in error variants (e.g., `InsufficientData { expected,
actual }`).
- `unwrap()` is fine in **tests**.

### Code Style

- Run `cargo fmt --all -- --check` to verify formatting (CI enforces
this on stable).
- Group imports: std first, then external crates, then local modules
(see `rustfmt.toml`).
- Use `pub(crate)` for items that should not be part of the public API.
- Apply `Zeroize` / `ZeroizeOnDrop` to any struct holding secret
material.

### Linting (mirrors CI)

CI runs clippy across multiple crate/feature combinations. Run the
relevant checks locally before pushing:

```bash
# Default features — workspace root (rln + utils)
cargo clippy --all-targets --tests --release -- -D warnings

# Stateless feature — from rln/
cd rln && cargo clippy --all-targets --tests --release \
  --features=stateless --no-default-features -- -D warnings

# WASM target — from rln-wasm/
cd rln-wasm && cargo clippy --target wasm32-unknown-unknown \
  --tests --release -- -D warnings
```

At minimum, run the default-features check. If your changes touch
`stateless` or `rln-wasm`, run those checks as well.

## Checklist

- [x] My PR title follows [Conventional
Commits](https://www.conventionalcommits.org/en/v1.0.0/) format
- [x] I have linked the related issue(s)
- [x] `cargo fmt --all -- --check` produces no changes
- [x] Clippy passes for all affected crate/feature combinations (see
[Linting](#linting-mirrors-ci) above)
- [x] `make test` passes locally
- [x] No new `unwrap()` / `expect()` / `panic!()` in library code
- [x] New code includes appropriate tests (unit / integration / WASM
where applicable)
- [x] I have run the CI coverage report — add the `run-coverage` label
to enable it
- [x] All CI checks pass and the PR is marked **Ready for review**
@github-actions
Copy link

Benchmark for db9e347

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 15.1±1.12ms 15.4±1.29ms +1.99%
OptimalMerkleTree::::full_depth_gen 789.0±21.60µs 808.3±12.86µs +2.45%
Pmtree::delete 50.8±0.54µs 50.7±1.08µs -0.20%
Pmtree::get 350.3±6.86ns 356.0±12.72ns +1.63%
Pmtree::get_empty_leaves_indices 5.0±0.15ns 4.7±0.10ns -6.00%
Pmtree::get_subtree_root 493.8±4.63ns 509.1±8.74ns +3.10%
Pmtree::override_range 99.6±1.09µs 99.7±1.11µs +0.10%
Pmtree::set 50.7±0.22µs 50.6±0.83µs -0.20%
Set/FullMerkleTree::::set/1 518.7±19.53µs 519.5±5.08µs +0.15%
Set/FullMerkleTree::::set/10 5.2±0.02ms 5.2±0.02ms 0.00%
Set/FullMerkleTree::::set/100 51.9±0.15ms 52.2±0.60ms +0.58%
Set/FullMerkleTree::::set_range/1 516.1±4.44µs 519.3±3.08µs +0.62%
Set/FullMerkleTree::::set_range/10 704.6±5.12µs 707.6±2.88µs +0.43%
Set/FullMerkleTree::::set_range/100 1878.0±39.02µs 1885.1±21.08µs +0.38%
Set/OptimalMerkleTree::::set/1 516.2±2.67µs 521.8±5.92µs +1.08%
Set/OptimalMerkleTree::::set/10 5.2±0.03ms 5.2±0.16ms 0.00%
Set/OptimalMerkleTree::::set/100 52.2±1.26ms 52.3±0.10ms +0.19%
Set/OptimalMerkleTree::::set_range/1 517.8±5.35µs 520.9±4.30µs +0.60%
Set/OptimalMerkleTree::::set_range/10 711.8±12.64µs 717.0±9.29µs +0.73%
Set/OptimalMerkleTree::::set_range/100 1847.8±33.68µs 1851.5±36.74µs +0.20%

@github-actions
Copy link

Benchmark for db9e347

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.3±0.17µs 17.3±0.17µs 0.00%
FullMerkleTree::get 0.8±0.01ns 0.8±0.08ns 0.00%
FullMerkleTree::get_empty_leaves_indices 981.2±6.73µs 982.0±8.45µs +0.08%
FullMerkleTree::get_subtree_root 11.7±0.09ns 11.6±0.11ns -0.85%
FullMerkleTree::override_range 2.2±0.07ms 2.3±0.07ms +4.55%
FullMerkleTree::set 8.6±0.08µs 8.6±0.17µs 0.00%
OptimalMerkleTree::delete 19.9±0.24µs 19.9±0.31µs 0.00%
OptimalMerkleTree::get 51.0±0.50ns 51.5±0.33ns +0.98%
OptimalMerkleTree::get_empty_leaves_indices 981.6±7.50µs 981.4±7.98µs -0.02%
OptimalMerkleTree::get_subtree_root 26.0±1.17ns 26.7±0.33ns +2.69%
OptimalMerkleTree::override_range 4.5±0.38ms 4.2±0.40ms -6.67%
OptimalMerkleTree::set 10.2±0.29µs 10.0±0.17µs -1.96%
poseidon Fr/Array hash/10 139.0±1.00µs 70.3 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1489.3±1.54µs 65.6 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.02ms 65.0 KElem/sec N/A N/A
poseidon Fr/Single hash 13.7±0.01µs 69.4 MElem/sec N/A N/A
validate_override_range/FullMerkleTree/override_range/1024 572.5±20.47µs N/A N/A
validate_override_range/FullMerkleTree/override_range/64 116.0±4.62µs N/A N/A
validate_override_range/FullMerkleTree/override_range/65536 438.3±3.68ms N/A N/A
validate_override_range/FullMerkleTree/override_range/8192 9.0±0.39ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/1024 805.2±47.38µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/64 142.9±3.84µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/65536 461.8±1.73ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/8192 10.1±0.02ms N/A N/A
validate_override_range/validate_only/1024 10.6±0.14µs N/A N/A
validate_override_range/validate_only/64 341.3±4.57ns N/A N/A
validate_override_range/validate_only/65536 842.8±10.39µs N/A N/A
validate_override_range/validate_only/8192 103.1±1.51µs N/A N/A

@seemenkina
Copy link
Collaborator

@romanzac please update branch and me can merge this pr as bug was fixed (if not let me know)

@github-actions
Copy link

Benchmark for 022c5f2

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 12.7±0.10ms 12.1±0.08ms -4.72%
OptimalMerkleTree::::full_depth_gen 700.8±5.94µs 702.0±6.39µs +0.17%
Pmtree::delete 50.8±0.53µs 50.7±0.53µs -0.20%
Pmtree::get 348.8±6.82ns 360.0±16.69ns +3.21%
Pmtree::get_empty_leaves_indices 5.0±0.08ns 4.7±0.08ns -6.00%
Pmtree::get_subtree_root 493.4±13.76ns 504.6±21.11ns +2.27%
Pmtree::override_range 101.0±1.09µs 100.8±0.91µs -0.20%
Pmtree::set 50.8±0.69µs 50.6±0.45µs -0.39%
Set/FullMerkleTree::::set/1 514.5±2.87µs 517.8±4.25µs +0.64%
Set/FullMerkleTree::::set/10 5.2±0.02ms 5.2±0.01ms 0.00%
Set/FullMerkleTree::::set/100 51.6±0.06ms 52.1±0.38ms +0.97%
Set/FullMerkleTree::::set_range/1 518.2±3.76µs 518.3±7.09µs +0.02%
Set/FullMerkleTree::::set_range/10 703.7±3.00µs 707.5±4.44µs +0.54%
Set/FullMerkleTree::::set_range/100 1859.2±40.74µs 1862.7±41.89µs +0.19%
Set/OptimalMerkleTree::::set/1 514.9±2.56µs 517.8±3.58µs +0.56%
Set/OptimalMerkleTree::::set/10 5.2±0.02ms 5.2±0.01ms 0.00%
Set/OptimalMerkleTree::::set/100 51.8±0.07ms 52.2±0.19ms +0.77%
Set/OptimalMerkleTree::::set_range/1 516.3±7.02µs 520.1±19.29µs +0.74%
Set/OptimalMerkleTree::::set_range/10 715.8±11.32µs 720.8±7.80µs +0.70%
Set/OptimalMerkleTree::::set_range/100 1810.3±41.04µs 1817.2±36.95µs +0.38%

@github-actions
Copy link

Benchmark for 022c5f2

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.0±0.24µs 17.0±0.28µs 0.00%
FullMerkleTree::get 0.8±0.03ns 0.8±0.01ns 0.00%
FullMerkleTree::get_empty_leaves_indices 983.0±14.32µs 1011.5±110.14µs +2.90%
FullMerkleTree::get_subtree_root 11.6±0.18ns 11.8±0.22ns +1.72%
FullMerkleTree::override_range 2.3±0.11ms 2.3±0.08ms 0.00%
FullMerkleTree::set 8.5±0.14µs 8.5±0.21µs 0.00%
OptimalMerkleTree::delete 20.5±0.32µs 19.8±0.92µs -3.41%
OptimalMerkleTree::get 51.1±0.42ns 51.1±0.52ns 0.00%
OptimalMerkleTree::get_empty_leaves_indices 982.6±9.72µs 982.5±4.73µs -0.01%
OptimalMerkleTree::get_subtree_root 26.0±0.26ns 26.0±0.28ns 0.00%
OptimalMerkleTree::override_range 5.0±0.29ms 4.8±0.39ms -4.00%
OptimalMerkleTree::set 10.4±0.21µs 9.9±0.22µs -4.81%
poseidon Fr/Array hash/10 139.1±0.59µs 70.2 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1492.7±6.52µs 65.4 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.1±0.05ms 64.8 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.33µs 68.9 MElem/sec N/A N/A
validate_override_range/FullMerkleTree/override_range/1024 609.7±48.96µs N/A N/A
validate_override_range/FullMerkleTree/override_range/64 100.1±18.11µs N/A N/A
validate_override_range/FullMerkleTree/override_range/65536 437.7±2.21ms N/A N/A
validate_override_range/FullMerkleTree/override_range/8192 8.8±0.06ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/1024 805.2±39.53µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/64 122.9±15.16µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/65536 461.8±2.25ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/8192 10.4±0.22ms N/A N/A
validate_override_range/validate_only/1024 10.6±0.08µs N/A N/A
validate_override_range/validate_only/64 345.9±2.05ns N/A N/A
validate_override_range/validate_only/65536 850.3±8.06µs N/A N/A
validate_override_range/validate_only/8192 103.9±1.81µs N/A N/A

@romanzac romanzac added the run-coverage Runs coverage report in CI once added to the open PR. label Feb 26, 2026
@github-actions
Copy link

Benchmark for 022c5f2

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 13.1±0.14ms 12.0±0.14ms -8.40%
OptimalMerkleTree::::full_depth_gen 700.8±19.31µs 709.6±23.09µs +1.26%
Pmtree::delete 50.7±0.29µs 50.6±0.42µs -0.20%
Pmtree::get 349.8±3.95ns 352.5±6.84ns +0.77%
Pmtree::get_empty_leaves_indices 5.0±0.09ns 5.2±0.26ns +4.00%
Pmtree::get_subtree_root 491.6±4.39ns 503.7±9.70ns +2.46%
Pmtree::override_range 101.1±0.89µs 100.7±0.89µs -0.40%
Pmtree::set 50.7±0.52µs 50.5±0.52µs -0.39%
Set/FullMerkleTree::::set/1 518.3±5.04µs 517.2±2.93µs -0.21%
Set/FullMerkleTree::::set/10 5.2±0.09ms 5.2±0.02ms 0.00%
Set/FullMerkleTree::::set/100 51.7±0.08ms 51.9±0.20ms +0.39%
Set/FullMerkleTree::::set_range/1 514.3±6.10µs 515.8±2.29µs +0.29%
Set/FullMerkleTree::::set_range/10 704.8±6.91µs 706.6±3.48µs +0.26%
Set/FullMerkleTree::::set_range/100 1858.1±31.16µs 1866.2±52.01µs +0.44%
Set/OptimalMerkleTree::::set/1 515.8±5.28µs 519.7±5.74µs +0.76%
Set/OptimalMerkleTree::::set/10 5.2±0.02ms 5.2±0.03ms 0.00%
Set/OptimalMerkleTree::::set/100 51.9±0.24ms 52.4±0.90ms +0.96%
Set/OptimalMerkleTree::::set_range/1 516.0±6.18µs 520.8±24.68µs +0.93%
Set/OptimalMerkleTree::::set_range/10 718.2±7.73µs 721.1±5.78µs +0.40%
Set/OptimalMerkleTree::::set_range/100 1821.3±59.56µs 1826.8±44.90µs +0.30%

@github-actions
Copy link

Benchmark for 022c5f2

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.0±0.17µs 17.0±0.26µs 0.00%
FullMerkleTree::get 0.8±0.01ns 0.8±0.01ns 0.00%
FullMerkleTree::get_empty_leaves_indices 982.3±5.31µs 983.2±12.76µs +0.09%
FullMerkleTree::get_subtree_root 11.6±0.10ns 11.8±0.15ns +1.72%
FullMerkleTree::override_range 2.2±0.09ms 2.3±0.07ms +4.55%
FullMerkleTree::set 8.5±0.15µs 8.5±0.11µs 0.00%
OptimalMerkleTree::delete 19.9±0.17µs 19.9±0.30µs 0.00%
OptimalMerkleTree::get 51.3±0.34ns 50.5±0.30ns -1.56%
OptimalMerkleTree::get_empty_leaves_indices 982.8±9.44µs 982.6±10.71µs -0.02%
OptimalMerkleTree::get_subtree_root 26.0±0.37ns 26.1±0.36ns +0.38%
OptimalMerkleTree::override_range 5.2±0.14ms 5.2±0.15ms 0.00%
OptimalMerkleTree::set 10.1±0.19µs 10.2±0.42µs +0.99%
poseidon Fr/Array hash/10 139.0±0.47µs 70.3 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1495.5±18.54µs 65.3 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.06ms 64.9 KElem/sec N/A N/A
poseidon Fr/Single hash 13.8±0.02µs 69.2 MElem/sec N/A N/A
validate_override_range/FullMerkleTree/override_range/1024 645.7±64.81µs N/A N/A
validate_override_range/FullMerkleTree/override_range/64 98.4±15.50µs N/A N/A
validate_override_range/FullMerkleTree/override_range/65536 435.0±1.37ms N/A N/A
validate_override_range/FullMerkleTree/override_range/8192 8.7±0.06ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/1024 801.9±40.36µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/64 130.6±21.94µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/65536 462.2±2.68ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/8192 11.2±0.78ms N/A N/A
validate_override_range/validate_only/1024 11.4±0.15µs N/A N/A
validate_override_range/validate_only/64 342.9±5.15ns N/A N/A
validate_override_range/validate_only/65536 851.0±8.86µs N/A N/A
validate_override_range/validate_only/8192 103.7±4.95µs N/A N/A

@github-actions
Copy link

Coverage report uploaded. Download HTML Report

@romanzac
Copy link
Contributor Author

@romanzac please update branch and me can merge this pr as bug was fixed (if not let me know)

All good. Thanks!

@romanzac romanzac requested a review from seemenkina February 28, 2026 01:12
@romanzac
Copy link
Contributor Author

Hi @vinhtc27 , please kindly add your review. New changes require approval from someone other than seemenkina because they were the last pusher.

@github-actions
Copy link

Benchmark for c8de892

Click to view benchmark
Test Base PR %
FullMerkleTree::::full_depth_gen 12.4±0.17ms 12.1±0.13ms -2.42%
OptimalMerkleTree::::full_depth_gen 688.8±12.02µs 692.3±19.33µs +0.51%
Pmtree::delete 51.1±2.04µs 50.6±0.62µs -0.98%
Pmtree::get 351.7±8.11ns 356.2±7.71ns +1.28%
Pmtree::get_empty_leaves_indices 5.0±0.08ns 4.7±0.10ns -6.00%
Pmtree::get_subtree_root 494.3±8.89ns 499.4±8.95ns +1.03%
Pmtree::override_range 101.4±0.84µs 101.7±0.84µs +0.30%
Pmtree::set 50.8±0.63µs 50.5±0.51µs -0.59%
Set/FullMerkleTree::::set/1 513.3±2.09µs 525.7±14.55µs +2.42%
Set/FullMerkleTree::::set/10 5.1±0.05ms 5.2±0.01ms +1.96%
Set/FullMerkleTree::::set/100 51.7±0.29ms 52.4±0.16ms +1.35%
Set/FullMerkleTree::::set_range/1 513.7±4.05µs 523.2±7.81µs +1.85%
Set/FullMerkleTree::::set_range/10 705.0±6.19µs 713.3±6.78µs +1.18%
Set/FullMerkleTree::::set_range/100 1860.8±48.63µs 1895.8±72.82µs +1.88%
Set/OptimalMerkleTree::::set/1 515.2±5.47µs 516.8±5.62µs +0.31%
Set/OptimalMerkleTree::::set/10 5.2±0.08ms 5.2±0.05ms 0.00%
Set/OptimalMerkleTree::::set/100 51.8±0.13ms 52.2±0.07ms +0.77%
Set/OptimalMerkleTree::::set_range/1 515.7±6.22µs 518.2±3.46µs +0.48%
Set/OptimalMerkleTree::::set_range/10 720.0±5.28µs 724.5±6.36µs +0.62%
Set/OptimalMerkleTree::::set_range/100 1831.0±53.84µs 1826.1±54.78µs -0.27%

@github-actions
Copy link

Benchmark for c8de892

Click to view benchmark
Test Base PR %
FullMerkleTree::delete 17.0±0.26µs 17.0±0.18µs 0.00%
FullMerkleTree::get 0.8±0.01ns 0.8±0.01ns 0.00%
FullMerkleTree::get_empty_leaves_indices 982.9±6.22µs 982.8±7.97µs -0.01%
FullMerkleTree::get_subtree_root 11.6±0.15ns 11.9±0.51ns +2.59%
FullMerkleTree::override_range 2.2±0.11ms 2.3±0.13ms +4.55%
FullMerkleTree::set 8.5±0.17µs 8.5±0.13µs 0.00%
OptimalMerkleTree::delete 19.7±0.28µs 20.3±0.72µs +3.05%
OptimalMerkleTree::get 51.5±0.32ns 51.5±0.84ns 0.00%
OptimalMerkleTree::get_empty_leaves_indices 983.3±9.41µs 985.6±28.31µs +0.23%
OptimalMerkleTree::get_subtree_root 26.1±0.38ns 25.9±0.26ns -0.77%
OptimalMerkleTree::override_range 4.3±0.26ms 4.4±0.44ms +2.33%
OptimalMerkleTree::set 9.9±0.31µs 10.2±0.55µs +3.03%
poseidon Fr/Array hash/10 139.0±0.39µs 70.2 KElem/sec N/A N/A
poseidon Fr/Array hash/100 1490.3±2.50µs 65.5 KElem/sec N/A N/A
poseidon Fr/Array hash/1000 15.0±0.02ms 65.0 KElem/sec N/A N/A
poseidon Fr/Single hash 13.7±0.02µs 69.4 MElem/sec N/A N/A
validate_override_range/FullMerkleTree/override_range/1024 608.2±38.83µs N/A N/A
validate_override_range/FullMerkleTree/override_range/64 94.0±9.59µs N/A N/A
validate_override_range/FullMerkleTree/override_range/65536 433.6±2.27ms N/A N/A
validate_override_range/FullMerkleTree/override_range/8192 8.9±0.06ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/1024 802.5±27.45µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/64 126.3±18.16µs N/A N/A
validate_override_range/OptimalMerkleTree/override_range/65536 460.1±2.29ms N/A N/A
validate_override_range/OptimalMerkleTree/override_range/8192 10.2±0.11ms N/A N/A
validate_override_range/validate_only/1024 8.1±0.13µs N/A N/A
validate_override_range/validate_only/64 343.7±6.17ns N/A N/A
validate_override_range/validate_only/65536 847.5±7.96µs N/A N/A
validate_override_range/validate_only/8192 103.3±1.59µs N/A N/A

@github-actions
Copy link

Coverage report uploaded. Download HTML Report

@romanzac romanzac merged commit 2d33344 into master Feb 28, 2026
16 checks passed
@romanzac romanzac deleted the test-ffi-error-cases branch February 28, 2026 01:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-coverage Runs coverage report in CI once added to the open PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants