Skip to content

Commit 0354f5d

Browse files
authored
refactor: rewrite justification operations as pure functions (leanEthereum#165)
* refactor: rewrite justification operations as pure functions * fix: rename for readability * fix: linting * fix: linting * fix: linting
1 parent 2ff0629 commit 0354f5d

2 files changed

Lines changed: 88 additions & 73 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""Helpers for the State container."""
2+
3+
from typing import Dict, List
4+
5+
from lean_spec.subspecs.containers.state.types import (
6+
JustificationRoots,
7+
JustificationValidators,
8+
)
9+
from lean_spec.types import Boolean, Bytes32
10+
11+
12+
def get_justifications_map(
13+
justifications_roots: JustificationRoots,
14+
justifications_validators: JustificationValidators,
15+
validator_count: int,
16+
) -> Dict[Bytes32, List[Boolean]]:
17+
"""
18+
Reconstruct the justifications map from the state's flat data structures.
19+
20+
Parameters
21+
----------
22+
justifications_roots : JustificationRoots
23+
The block roots in alphabetical order.
24+
justifications_validators : JustificationValidators
25+
The list of validator justifications for each block root concatenated in the same order
26+
as the list of block roots.
27+
validator_count : int
28+
The number of validators in the state.
29+
30+
Returns:
31+
-------
32+
Dict[Bytes32, List[Boolean]]
33+
A mapping from a block root to the list of validator justifications for that root.
34+
"""
35+
# No justified roots means no justifications to reconstruct.
36+
if not justifications_roots:
37+
return {}
38+
39+
# Extract the flattened validator justifications.
40+
flat_justifications = list(justifications_validators)
41+
42+
# Reconstruct the map: each root gets its corresponding justification slice.
43+
return {
44+
root: flat_justifications[i * validator_count : (i + 1) * validator_count]
45+
for i, root in enumerate(justifications_roots)
46+
}
47+
48+
49+
def flatten_justifications_map(
50+
justifications_map: Dict[Bytes32, List[Boolean]], validator_count: int
51+
) -> tuple[JustificationRoots, JustificationValidators]:
52+
"""
53+
Flatten a map of validator justifications into the state's flat data structures
54+
for SSZ compatibility.
55+
56+
Parameters
57+
----------
58+
justifications_map : Dict[Bytes32, List[Boolean]]
59+
A mapping from a block root to the list of validator justifications for that root.
60+
validator_count : int
61+
The number of validators in the state.
62+
63+
Returns:
64+
-------
65+
JustificationRoots
66+
The block roots in alphabetical order.
67+
JustificationValidators
68+
The list of validator justifications for each block root concatenated in the same order
69+
as the list of block roots.
70+
"""
71+
# Build the flattened lists from the map, with sorted keys for deterministic order.
72+
roots_list = []
73+
justifications_list = []
74+
75+
for root in sorted(justifications_map.keys()):
76+
justifications = justifications_map[root]
77+
78+
# Validate that the justifications list has the expected length.
79+
if len(justifications) != validator_count:
80+
raise AssertionError(f"Justifications list for root {root.hex()} has incorrect length")
81+
82+
# Add the root to the roots list.
83+
roots_list.append(root)
84+
# Extend the flattened list with the justifications for this root.
85+
justifications_list.extend(justifications)
86+
87+
# Return a new state object with the updated fields.
88+
return JustificationRoots(data=roots_list), JustificationValidators(data=justifications_list)

src/lean_spec/subspecs/containers/state/state.py

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
justified and finalized.
77
"""
88

9-
from typing import Dict, List
10-
119
from lean_spec.subspecs.ssz.constants import ZERO_HASH
1210
from lean_spec.subspecs.ssz.hash import hash_tree_root
1311
from lean_spec.types import (
@@ -137,77 +135,6 @@ def is_proposer(self, validator_index: ValidatorIndex) -> bool:
137135
num_validators=Uint64(self.validators.count),
138136
)
139137

140-
def get_justifications(self) -> Dict[Bytes32, List[Boolean]]:
141-
"""
142-
Reconstruct a map from justified block roots to validator vote lists.
143-
144-
This method takes the flat state encoding and rebuilds the associative
145-
structure for easier processing.
146-
147-
Returns:
148-
-------
149-
Dict[Bytes32, List[Boolean]]
150-
A mapping from justified block root to the list of validator votes.
151-
"""
152-
# No justified roots means no justifications to reconstruct.
153-
if not self.justifications_roots:
154-
return {}
155-
156-
# Each root has exactly validator_count votes in the flat encoding.
157-
validator_count = self.validators.count
158-
159-
# Extract the flattened validator votes.
160-
flat_votes = list(self.justifications_validators)
161-
162-
# Reconstruct the map: each root gets its corresponding vote slice.
163-
return {
164-
root: flat_votes[i * validator_count : (i + 1) * validator_count]
165-
for i, root in enumerate(self.justifications_roots)
166-
}
167-
168-
def with_justifications(
169-
self,
170-
justifications: Dict[Bytes32, List[Boolean]],
171-
) -> "State":
172-
"""
173-
Update the state with a new set of justifications.
174-
175-
This method flattens the justifications map into the state's flat
176-
encoding for SSZ compatibility.
177-
178-
Parameters
179-
----------
180-
justifications : Dict[Bytes32, List[Boolean]]
181-
A mapping from justified block root to validator vote lists.
182-
183-
Returns:
184-
-------
185-
State
186-
A new state with updated justification data.
187-
"""
188-
# Build the flattened lists from the map, with sorted keys for deterministic order.
189-
roots_list = []
190-
votes_list = []
191-
for root in sorted(justifications.keys()):
192-
votes = justifications[root]
193-
# Validate that the vote list has the expected length.
194-
expected_len = self.validators.count
195-
if len(votes) != expected_len:
196-
raise AssertionError(f"Vote list for root {root.hex()} has incorrect length")
197-
198-
# Add the root to the roots list.
199-
roots_list.append(root)
200-
# Extend the flattened list with the votes for this root.
201-
votes_list.extend(votes)
202-
203-
# Return a new state object with the updated fields.
204-
return self.model_copy(
205-
update={
206-
"justifications_roots": JustificationRoots(data=roots_list),
207-
"justifications_validators": JustificationValidators(data=votes_list),
208-
}
209-
)
210-
211138
def process_slot(self) -> "State":
212139
"""
213140
Perform per-slot maintenance tasks.

0 commit comments

Comments
 (0)