Skip to content

Commit d62af71

Browse files
committed
fix: proper justifications handling
1 parent 2243990 commit d62af71

File tree

1 file changed

+228
-25
lines changed
  • crates/common/consensus/lean/src

1 file changed

+228
-25
lines changed

crates/common/consensus/lean/src/state.rs

Lines changed: 228 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@ use serde::{Deserialize, Serialize};
33
use ssz_derive::{Decode, Encode};
44
use ssz_types::{
55
BitList, VariableList,
6-
typenum::{U262144, U1073741824, Unsigned},
6+
typenum::{U262144, U1073741824},
77
};
88
use tree_hash_derive::TreeHash;
99

10-
use crate::{
11-
config::Config,
12-
MAX_HISTORICAL_BLOCK_HASHES,
13-
VALIDATOR_REGISTRY_LIMIT,
14-
};
10+
use crate::{VALIDATOR_REGISTRY_LIMIT, config::Config};
1511

1612
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)]
1713
pub struct LeanState {
@@ -34,31 +30,66 @@ pub struct LeanState {
3430
}
3531

3632
impl LeanState {
37-
fn get_justifications_roots_index(&self, root: &B256) -> Option<usize> {
38-
self.justifications_roots.iter().position(|r| r == root)
39-
}
33+
pub fn new(num_validators: u64) -> LeanState {
34+
LeanState {
35+
config: Config { num_validators },
4036

41-
fn get_justifications_roots_range(&self, index: &usize) -> (usize, usize) {
42-
let start_range = index * MAX_HISTORICAL_BLOCK_HASHES as usize;
43-
let end_range = start_range + VALIDATOR_REGISTRY_LIMIT as usize;
37+
latest_justified_hash: B256::ZERO,
38+
latest_justified_slot: 0,
39+
latest_finalized_hash: B256::ZERO,
40+
latest_finalized_slot: 0,
4441

45-
(start_range, end_range)
42+
historical_block_hashes: VariableList::empty(),
43+
justified_slots: VariableList::empty(),
44+
45+
justifications_roots: VariableList::empty(),
46+
justifications_roots_validators: BitList::with_capacity(0)
47+
.expect("Failed to initialize state's justifications_roots_validators"),
48+
}
49+
}
50+
51+
fn get_justifications_roots_index(&self, root: &B256) -> Option<usize> {
52+
self.justifications_roots.iter().position(|r| r == root)
4653
}
4754

4855
pub fn initialize_justifications_for_root(&mut self, root: &B256) {
4956
if !self.justifications_roots.contains(root) {
5057
self.justifications_roots
5158
.push(*root)
5259
.expect("Failed to insert root into justifications_roots");
60+
61+
let old_length = self.justifications_roots_validators.len();
62+
let new_length = old_length + VALIDATOR_REGISTRY_LIMIT as usize;
63+
64+
let mut new_justifications_roots_validators = BitList::with_capacity(new_length)
65+
.expect("Failed to initialize new justification bits");
66+
67+
for (i, bit) in self.justifications_roots_validators.iter().enumerate() {
68+
new_justifications_roots_validators
69+
.set(i, bit)
70+
.expect("Failed to initialize justification bits to existing values");
71+
}
72+
73+
for i in old_length..new_length {
74+
new_justifications_roots_validators
75+
.set(i, false)
76+
.expect("Failed to zero-fill justification bits");
77+
}
78+
79+
self.justifications_roots_validators = new_justifications_roots_validators;
5380
}
5481
}
5582

5683
pub fn set_justification(&mut self, root: &B256, validator_id: &u64, value: bool) {
5784
let index = self
5885
.get_justifications_roots_index(root)
5986
.expect("Failed to find the justifications index to set");
87+
6088
self.justifications_roots_validators
61-
.set(index * U262144::to_usize() + *validator_id as usize, value)
89+
.set(
90+
index * VALIDATOR_REGISTRY_LIMIT as usize + *validator_id as usize,
91+
value,
92+
)
6293
.expect("Failed to set justification bit");
6394
}
6495

@@ -67,29 +98,201 @@ impl LeanState {
6798
.get_justifications_roots_index(root)
6899
.expect("Could not find justifications for the provided block root");
69100

70-
let (start_range, end_range) = self.get_justifications_roots_range(&index);
101+
let start_range = index * VALIDATOR_REGISTRY_LIMIT as usize;
71102

72-
self.justifications_roots_validators.as_slice()[start_range..end_range]
103+
self.justifications_roots_validators
73104
.iter()
105+
.skip(start_range)
106+
.take(VALIDATOR_REGISTRY_LIMIT as usize)
74107
.fold(0, |acc, justification_bits| {
75-
acc + justification_bits.count_ones()
108+
acc + justification_bits as usize
76109
}) as u64
77110
}
78111

79112
pub fn remove_justifications(&mut self, root: &B256) {
80-
// Remove from `state.justifications_roots`
81113
let index = self
82114
.get_justifications_roots_index(root)
83115
.expect("Failed to find the justifications index to remove");
84116
self.justifications_roots.remove(index);
85117

86-
let (start_range, end_range) = self.get_justifications_roots_range(&index);
118+
let new_length = self.justifications_roots.len() * VALIDATOR_REGISTRY_LIMIT as usize;
119+
let mut new_justifications_roots_validators =
120+
BitList::<U1073741824>::with_capacity(new_length)
121+
.expect("Failed to recreate state's justifications_roots_validators");
87122

88-
// Remove from `state.justifications_roots_validators`
89-
for i in start_range..end_range {
90-
self.justifications_roots_validators
91-
.set(i, false)
92-
.expect("Failed to remove justifications");
93-
}
123+
// Take left side of the list (if any)
124+
self.justifications_roots_validators
125+
.iter()
126+
.take(index * VALIDATOR_REGISTRY_LIMIT as usize)
127+
.fold(0, |i, justification_bit| {
128+
new_justifications_roots_validators
129+
.set(i, justification_bit)
130+
.expect("Failed to set new justification bit");
131+
i + 1
132+
});
133+
134+
// Take right side of the list (if any)
135+
self.justifications_roots_validators
136+
.iter()
137+
.skip((index + 1) * VALIDATOR_REGISTRY_LIMIT as usize)
138+
.fold(
139+
index * VALIDATOR_REGISTRY_LIMIT as usize,
140+
|i, justification_bit| {
141+
new_justifications_roots_validators
142+
.set(i, justification_bit)
143+
.expect("Failed to set new justification bit");
144+
i + 1
145+
},
146+
);
147+
148+
self.justifications_roots_validators = new_justifications_roots_validators;
149+
}
150+
}
151+
152+
#[cfg(test)]
153+
mod test {
154+
use super::*;
155+
156+
#[test]
157+
fn initialize_justifications_for_root() {
158+
let mut state = LeanState::new(1);
159+
160+
// Initialize 1st root
161+
state.initialize_justifications_for_root(&B256::repeat_byte(1));
162+
assert_eq!(state.justifications_roots.len(), 1);
163+
assert_eq!(
164+
state.justifications_roots_validators.len(),
165+
VALIDATOR_REGISTRY_LIMIT as usize
166+
);
167+
168+
// Initialize an existing root should result in same lengths
169+
state.initialize_justifications_for_root(&B256::repeat_byte(1));
170+
assert_eq!(state.justifications_roots.len(), 1);
171+
assert_eq!(
172+
state.justifications_roots_validators.len(),
173+
VALIDATOR_REGISTRY_LIMIT as usize
174+
);
175+
176+
// Initialize 2nd root
177+
state.initialize_justifications_for_root(&B256::repeat_byte(2));
178+
assert_eq!(state.justifications_roots.len(), 2);
179+
assert_eq!(
180+
state.justifications_roots_validators.len(),
181+
2 * VALIDATOR_REGISTRY_LIMIT as usize
182+
);
183+
}
184+
185+
#[test]
186+
fn set_justification() {
187+
let mut state = LeanState::new(1);
188+
let root0 = B256::repeat_byte(1);
189+
let root1 = B256::repeat_byte(2);
190+
let validator_id = 7u64;
191+
192+
// Set for 1st root
193+
state.initialize_justifications_for_root(&root0);
194+
state.set_justification(&root0, &validator_id, true);
195+
assert!(
196+
state
197+
.justifications_roots_validators
198+
.get(validator_id as usize)
199+
.unwrap()
200+
);
201+
202+
// Set for 2nd root
203+
state.initialize_justifications_for_root(&root1);
204+
state.set_justification(&root1, &validator_id, true);
205+
assert!(
206+
state
207+
.justifications_roots_validators
208+
.get(VALIDATOR_REGISTRY_LIMIT as usize + validator_id as usize)
209+
.unwrap()
210+
);
211+
}
212+
213+
#[test]
214+
fn count_justifications() {
215+
let mut state = LeanState::new(1);
216+
let root0 = B256::repeat_byte(1);
217+
let root1 = B256::repeat_byte(2);
218+
219+
// Justifications for 1st root, up to 2 justifications
220+
state.initialize_justifications_for_root(&root0);
221+
222+
state.set_justification(&root0, &1u64, true);
223+
assert_eq!(state.count_justifications(&root0), 1);
224+
225+
state.set_justification(&root0, &2u64, true);
226+
assert_eq!(state.count_justifications(&root0), 2);
227+
228+
// Justifications for 2nd root, up to 3 justifications
229+
state.initialize_justifications_for_root(&root1);
230+
231+
state.set_justification(&root1, &11u64, true);
232+
assert_eq!(state.count_justifications(&root1), 1);
233+
234+
state.set_justification(&root1, &22u64, true);
235+
state.set_justification(&root1, &33u64, true);
236+
assert_eq!(state.count_justifications(&root1), 3);
237+
}
238+
239+
#[test]
240+
fn remove_justifications() {
241+
// Assuming 3 roots & 4 validators
242+
let mut state = LeanState::new(3);
243+
let root0 = B256::repeat_byte(1);
244+
let root1 = B256::repeat_byte(2);
245+
let root2 = B256::repeat_byte(3);
246+
247+
// Add justifications for left root
248+
state.initialize_justifications_for_root(&root0);
249+
state.set_justification(&root0, &0u64, true);
250+
251+
// Add justifications for middle root
252+
state.initialize_justifications_for_root(&root1);
253+
state.set_justification(&root1, &1u64, true);
254+
255+
// Add justifications for last root
256+
state.initialize_justifications_for_root(&root2);
257+
state.set_justification(&root2, &2u64, true);
258+
259+
// Assert before removal
260+
assert_eq!(state.justifications_roots.len(), 3);
261+
assert_eq!(
262+
state.justifications_roots_validators.len(),
263+
3 * VALIDATOR_REGISTRY_LIMIT as usize
264+
);
265+
266+
// Assert after removing middle root (root1)
267+
state.remove_justifications(&root1);
268+
269+
assert_eq!(
270+
state.get_justifications_roots_index(&root1),
271+
None,
272+
"Root still exists after removal"
273+
);
274+
assert_eq!(
275+
state.justifications_roots.len(),
276+
2,
277+
"Should be reduced by 1"
278+
);
279+
assert_eq!(
280+
state.justifications_roots_validators.len(),
281+
2 * VALIDATOR_REGISTRY_LIMIT as usize,
282+
"Should be reduced by VALIDATOR_REGISTRY_LIMIT"
283+
);
284+
285+
// Assert justifications
286+
assert!(
287+
state.justifications_roots_validators.get(0).unwrap(),
288+
"root0 should still be justified by validator0"
289+
);
290+
assert!(
291+
state
292+
.justifications_roots_validators
293+
.get(VALIDATOR_REGISTRY_LIMIT as usize + 2)
294+
.unwrap(),
295+
"root2 should still be justified by validator2"
296+
);
94297
}
95298
}

0 commit comments

Comments
 (0)