Skip to content

Commit 23e4aae

Browse files
authored
ssz: improve test coverage for merkleization (leanEthereum#489)
1 parent 4e7d2bb commit 23e4aae

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

tests/lean_spec/subspecs/ssz/test_merkleization.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import pytest
66

77
from lean_spec.subspecs.ssz.merkleization import (
8+
_ZERO_HASHES,
9+
_merkleize_efficient,
810
_zero_tree_root,
911
merkleize,
1012
mix_in_length,
@@ -148,3 +150,57 @@ def test_zero_tree_root_internal() -> None:
148150
assert _zero_tree_root(4) == Z[2]
149151
assert _zero_tree_root(8) == Z[3]
150152
assert _zero_tree_root(16) == Z[4]
153+
154+
155+
def test_zero_tree_root_fallback_beyond_precomputed_depth() -> None:
156+
"""Tests the fallback path for trees deeper than the pre-computed cache (depth >= 65)."""
157+
# _ZERO_HASHES has 65 entries (indices 0..64).
158+
# width_pow2 = 2**65 gives depth = 65, which equals len(_ZERO_HASHES),
159+
# triggering the fallback that hashes upward from _ZERO_HASHES[-1].
160+
width_pow2 = 2**65
161+
result = _zero_tree_root(width_pow2)
162+
163+
# The fallback computes one extra hash step beyond the last cached value.
164+
# depth=65, len(_ZERO_HASHES)=65, so range(65 - 65 + 1) = range(1) -> one iteration.
165+
expected = h(_ZERO_HASHES[-1], _ZERO_HASHES[-1])
166+
assert result == expected
167+
168+
169+
def test_zero_tree_root_fallback_two_steps_beyond_cache() -> None:
170+
"""Tests the fallback path with depth two steps beyond the pre-computed cache."""
171+
# width_pow2 = 2**66 gives depth = 66, requiring two hash steps beyond cache.
172+
width_pow2 = 2**66
173+
result = _zero_tree_root(width_pow2)
174+
175+
step1 = h(_ZERO_HASHES[-1], _ZERO_HASHES[-1])
176+
expected = h(step1, step1)
177+
assert result == expected
178+
179+
180+
def test_merkleize_efficient_secondary_loop() -> None:
181+
"""
182+
Tests the secondary reduction loop in efficient merkleization.
183+
184+
When called directly with more chunks than width (not possible through merkleize),
185+
the main loop exits with multiple remaining nodes, triggering the secondary reduction loop.
186+
"""
187+
# 4 chunks with width=2: main loop exits after subtree_size reaches 2,
188+
# leaving level=[h(c0,c1), h(c2,c3)] with len=2, triggering secondary loop.
189+
result = _merkleize_efficient([c[0], c[1], c[2], c[3]], width=2)
190+
assert result == h(h(c[0], c[1]), h(c[2], c[3]))
191+
192+
193+
def test_merkleize_efficient_secondary_loop_odd_nodes() -> None:
194+
"""
195+
Tests the secondary reduction loop with an odd number of remaining nodes.
196+
197+
Exercises the zero-padding branch when a node has no right sibling.
198+
"""
199+
# 3 chunks with width=1: main loop never runs (subtree_size=1 >= width=1),
200+
# so level stays as [c0, c1, c2] with len=3, triggering secondary loop.
201+
#
202+
# Secondary loop iteration 1: pairs (c0,c1)->h01, c2 has no right sibling
203+
# -> h(c2, _zero_tree_root(1)) = h(c2, Z[0]). Level=[h01, h2z], subtree_size=2.
204+
# Secondary loop iteration 2: pairs -> h(h01, h2z). Level=[result].
205+
result = _merkleize_efficient([c[0], c[1], c[2]], width=1)
206+
assert result == h(h(c[0], c[1]), h(c[2], Z[0]))

0 commit comments

Comments
 (0)