@@ -2,7 +2,6 @@ use std::{
2
2
cmp:: Ordering ,
3
3
collections:: { BTreeMap , HashMap } ,
4
4
} ;
5
- use store:: HotStateSummary ;
6
5
use types:: { Hash256 , Slot } ;
7
6
8
7
#[ derive( Debug , Clone , Copy ) ]
@@ -16,6 +15,8 @@ pub struct DAGStateSummary {
16
15
pub struct DAGStateSummaryV22 {
17
16
pub slot : Slot ,
18
17
pub latest_block_root : Hash256 ,
18
+ pub block_slot : Slot ,
19
+ pub block_parent_root : Hash256 ,
19
20
}
20
21
21
22
pub struct StateSummariesDAG {
@@ -27,12 +28,13 @@ pub struct StateSummariesDAG {
27
28
28
29
#[ derive( Debug ) ]
29
30
pub enum Error {
30
- MissingParentBlockRoot ( Hash256 ) ,
31
31
MissingStateSummary ( Hash256 ) ,
32
+ MissingStateSummaryByBlockRoot ( Hash256 ) ,
32
33
MissingStateSummaryAtSlot ( Hash256 , Slot ) ,
33
34
MissingChildBlockRoot ( Hash256 ) ,
34
35
MissingBlock ( Hash256 ) ,
35
36
RequestedSlotAboveSummary ( Hash256 , Slot ) ,
37
+ RootUnknownPreviousStateRoot ( Hash256 , Slot ) ,
36
38
}
37
39
38
40
impl StateSummariesDAG {
@@ -57,10 +59,15 @@ impl StateSummariesDAG {
57
59
}
58
60
}
59
61
62
+ /// Computes a DAG from a sequence of state summaries, including their parent block
63
+ /// relationships.
64
+ ///
65
+ /// - Expects summaries to be contiguous per slot: there must exist a summary at every slot
66
+ /// of each tree branch
67
+ /// - Maybe include multiple disjoint trees. The root of each tree will have a ZERO parent state
68
+ /// root, which will error later when calling `previous_state_root`.
60
69
pub fn new_from_v22 (
61
70
state_summaries_v22 : Vec < ( Hash256 , DAGStateSummaryV22 ) > ,
62
- parent_block_roots : HashMap < Hash256 , Hash256 > ,
63
- base_root : Hash256 ,
64
71
) -> Result < Self , Error > {
65
72
// Group them by latest block root, and sorted state slot
66
73
let mut state_summaries_by_block_root = HashMap :: < _ , BTreeMap < _ , _ > > :: new ( ) ;
@@ -76,32 +83,45 @@ impl StateSummariesDAG {
76
83
let state_summaries = state_summaries_v22
77
84
. iter ( )
78
85
. map ( |( state_root, summary) | {
79
- let previous_state_root = if summary. slot == 0 || * state_root == base_root {
86
+ let previous_state_root = if summary. slot == 0 {
80
87
Hash256 :: ZERO
81
88
} else {
82
89
let previous_slot = summary. slot - 1 ;
83
90
84
91
// Check the set of states in the same state's block root
85
92
let same_block_root_summaries = state_summaries_by_block_root
86
93
. get ( & summary. latest_block_root )
87
- . ok_or ( Error :: MissingStateSummary ( summary. latest_block_root ) ) ?;
94
+ // Should never error: we construct the HashMap here and must have at least
95
+ // one entry per block root
96
+ . ok_or ( Error :: MissingStateSummaryByBlockRoot (
97
+ summary. latest_block_root ,
98
+ ) ) ?;
88
99
if let Some ( ( state_root, _) ) = same_block_root_summaries. get ( & previous_slot) {
89
100
// Skipped slot: block root at previous slot is the same as latest block root.
90
101
* * state_root
91
102
} else {
92
103
// Common case: not a skipped slot.
93
- let parent_block_root = parent_block_roots
94
- . get ( & summary. latest_block_root )
95
- . ok_or ( Error :: MissingParentBlockRoot ( summary. latest_block_root ) ) ?;
96
- * state_summaries_by_block_root
97
- . get ( parent_block_root)
98
- . ok_or ( Error :: MissingStateSummary ( * parent_block_root) ) ?
99
- . get ( & previous_slot)
100
- . ok_or ( Error :: MissingStateSummaryAtSlot (
101
- * parent_block_root,
102
- previous_slot,
103
- ) ) ?
104
- . 0
104
+ let parent_block_root = summary. block_parent_root ;
105
+ if let Some ( parent_block_summaries) =
106
+ state_summaries_by_block_root. get ( & parent_block_root)
107
+ {
108
+ * parent_block_summaries
109
+ . get ( & previous_slot)
110
+ // Should never error: summaries are continuous, so if there's an
111
+ // entry it must contain at least one summary at the previous slot.
112
+ . ok_or ( Error :: MissingStateSummaryAtSlot (
113
+ parent_block_root,
114
+ previous_slot,
115
+ ) ) ?
116
+ . 0
117
+ } else {
118
+ // We don't know of any summary with this parent block root. We'll
119
+ // consider this summary to be a root of `state_summaries_v22`
120
+ // collection and mark it as zero.
121
+ // The test store_tests::finalizes_non_epoch_start_slot manages to send two
122
+ // disjoint trees on its second migration.
123
+ Hash256 :: ZERO
124
+ }
105
125
}
106
126
} ;
107
127
@@ -142,11 +162,18 @@ impl StateSummariesDAG {
142
162
}
143
163
144
164
pub fn previous_state_root ( & self , state_root : Hash256 ) -> Result < Hash256 , Error > {
145
- Ok ( self
165
+ let summary = self
146
166
. state_summaries_by_state_root
147
167
. get ( & state_root)
148
- . ok_or ( Error :: MissingStateSummary ( state_root) ) ?
149
- . previous_state_root )
168
+ . ok_or ( Error :: MissingStateSummary ( state_root) ) ?;
169
+ if summary. previous_state_root == Hash256 :: ZERO {
170
+ Err ( Error :: RootUnknownPreviousStateRoot (
171
+ state_root,
172
+ summary. slot ,
173
+ ) )
174
+ } else {
175
+ Ok ( summary. previous_state_root )
176
+ }
150
177
}
151
178
152
179
pub fn ancestor_state_root_at_slot (
@@ -170,7 +197,14 @@ impl StateSummariesDAG {
170
197
return Ok ( state_root) ;
171
198
}
172
199
Ordering :: Greater => {
173
- state_root = summary. previous_state_root ;
200
+ if summary. previous_state_root == Hash256 :: ZERO {
201
+ return Err ( Error :: RootUnknownPreviousStateRoot (
202
+ state_root,
203
+ summary. slot ,
204
+ ) ) ;
205
+ } else {
206
+ state_root = summary. previous_state_root ;
207
+ }
174
208
}
175
209
}
176
210
}
@@ -196,15 +230,6 @@ impl StateSummariesDAG {
196
230
}
197
231
}
198
232
199
- impl From < HotStateSummary > for DAGStateSummaryV22 {
200
- fn from ( value : HotStateSummary ) -> Self {
201
- Self {
202
- slot : value. slot ,
203
- latest_block_root : value. latest_block_root ,
204
- }
205
- }
206
- }
207
-
208
233
#[ derive( Debug , Clone , Copy ) ]
209
234
pub struct DAGBlockSummary {
210
235
pub slot : Slot ,
@@ -284,7 +309,6 @@ impl BlockSummariesDAG {
284
309
mod tests {
285
310
use super :: { BlockSummariesDAG , DAGBlockSummary , DAGStateSummaryV22 , Error , StateSummariesDAG } ;
286
311
use bls:: FixedBytesExtended ;
287
- use std:: collections:: HashMap ;
288
312
use types:: { Hash256 , Slot } ;
289
313
290
314
fn root ( n : u64 ) -> Hash256 {
@@ -300,7 +324,7 @@ mod tests {
300
324
301
325
#[ test]
302
326
fn new_from_v22_empty ( ) {
303
- StateSummariesDAG :: new_from_v22 ( vec ! [ ] , HashMap :: new ( ) , Hash256 :: ZERO ) . unwrap ( ) ;
327
+ StateSummariesDAG :: new_from_v22 ( vec ! [ ] ) . unwrap ( ) ;
304
328
}
305
329
306
330
#[ test]
@@ -311,17 +335,78 @@ mod tests {
311
335
let summary_1 = DAGStateSummaryV22 {
312
336
slot : Slot :: new ( 1 ) ,
313
337
latest_block_root : root_1,
338
+ block_parent_root : root_2,
339
+ block_slot : Slot :: new ( 1 ) ,
314
340
} ;
315
- // (child, parent)
316
- let parents = HashMap :: from_iter ( [ ( root_1, root_2) ] ) ;
317
341
318
- let dag =
319
- StateSummariesDAG :: new_from_v22 ( vec ! [ ( root_a, summary_1) ] , parents, root_a) . unwrap ( ) ;
342
+ let dag = StateSummariesDAG :: new_from_v22 ( vec ! [ ( root_a, summary_1) ] ) . unwrap ( ) ;
320
343
321
344
// The parent of the root summary is ZERO
322
345
assert_eq ! ( dag. previous_state_root( root_a) . unwrap( ) , Hash256 :: ZERO ) ;
323
346
}
324
347
348
+ #[ test]
349
+ fn new_from_v22_multiple_states ( ) {
350
+ let dag = StateSummariesDAG :: new_from_v22 ( vec ! [
351
+ (
352
+ root( 0xa ) ,
353
+ DAGStateSummaryV22 {
354
+ slot: Slot :: new( 3 ) ,
355
+ latest_block_root: root( 3 ) ,
356
+ block_parent_root: root( 1 ) ,
357
+ block_slot: Slot :: new( 3 ) ,
358
+ } ,
359
+ ) ,
360
+ (
361
+ root( 0xb ) ,
362
+ DAGStateSummaryV22 {
363
+ slot: Slot :: new( 4 ) ,
364
+ latest_block_root: root( 4 ) ,
365
+ block_parent_root: root( 3 ) ,
366
+ block_slot: Slot :: new( 4 ) ,
367
+ } ,
368
+ ) ,
369
+ // fork 1
370
+ (
371
+ root( 0xc ) ,
372
+ DAGStateSummaryV22 {
373
+ slot: Slot :: new( 5 ) ,
374
+ latest_block_root: root( 5 ) ,
375
+ block_parent_root: root( 4 ) ,
376
+ block_slot: Slot :: new( 5 ) ,
377
+ } ,
378
+ ) ,
379
+ // fork 2
380
+ // skipped slot
381
+ (
382
+ root( 0xd ) ,
383
+ DAGStateSummaryV22 {
384
+ slot: Slot :: new( 5 ) ,
385
+ latest_block_root: root( 4 ) ,
386
+ block_parent_root: root( 3 ) ,
387
+ block_slot: Slot :: new( 4 ) ,
388
+ } ,
389
+ ) ,
390
+ // normal slot
391
+ (
392
+ root( 0xe ) ,
393
+ DAGStateSummaryV22 {
394
+ slot: Slot :: new( 6 ) ,
395
+ latest_block_root: root( 6 ) ,
396
+ block_parent_root: root( 4 ) ,
397
+ block_slot: Slot :: new( 6 ) ,
398
+ } ,
399
+ ) ,
400
+ ] )
401
+ . unwrap ( ) ;
402
+
403
+ // The parent of the root summary is ZERO
404
+ assert_eq ! ( dag. previous_state_root( root( 0xa ) ) . unwrap( ) , Hash256 :: ZERO ) ;
405
+ assert_eq ! ( dag. previous_state_root( root( 0xc ) ) . unwrap( ) , root( 0xb ) ) ;
406
+ assert_eq ! ( dag. previous_state_root( root( 0xd ) ) . unwrap( ) , root( 0xb ) ) ;
407
+ assert_eq ! ( dag. previous_state_root( root( 0xe ) ) . unwrap( ) , root( 0xd ) ) ;
408
+ }
409
+
325
410
#[ test]
326
411
fn descendant_block_roots_of ( ) {
327
412
let root_1 = root ( 1 ) ;
0 commit comments