1
1
import logging
2
2
import os
3
3
import pickle
4
- from collections import defaultdict
5
- from dataclasses import dataclass
6
4
from pathlib import Path
7
- from typing import Self
5
+ from typing import Self , NewType
8
6
9
7
from src .types import EpochNumber , ValidatorIndex
10
8
from src .utils .range import sequence
@@ -17,20 +15,14 @@ class InvalidState(ValueError):
17
15
"""State has data considered as invalid for a report"""
18
16
19
17
20
- @ dataclass
21
- class AttestationsAccumulator :
22
- """Accumulator of attestations duties observed for a validator"""
18
+ Assigned = NewType ( "Assigned" , int )
19
+ Included = NewType ( "Included" , int )
20
+ AttestationsAccumulator = NewType ( 'AttestationsAccumulator' , tuple [ Assigned , Included ])
23
21
24
- assigned : int = 0
25
- included : int = 0
26
22
27
- @property
28
- def perf (self ) -> float :
29
- return self .included / self .assigned if self .assigned else 0
30
-
31
- def add_duty (self , included : bool ) -> None :
32
- self .assigned += 1
33
- self .included += 1 if included else 0
23
+ def perf (acc : AttestationsAccumulator ) -> float :
24
+ assigned , included = acc
25
+ return included / assigned if assigned else 0
34
26
35
27
36
28
class State :
@@ -43,14 +35,14 @@ class State:
43
35
44
36
The state can be migrated to be used for another frame's report by calling the `migrate` method.
45
37
"""
46
-
47
- data : defaultdict [ ValidatorIndex , AttestationsAccumulator ]
38
+ # validator_index -> (assigned, included)
39
+ data : list [ AttestationsAccumulator | None ]
48
40
49
41
_epochs_to_process : set [EpochNumber ]
50
42
_processed_epochs : set [EpochNumber ]
51
43
52
- def __init__ (self , data : dict [ ValidatorIndex , AttestationsAccumulator ] | None = None ) -> None :
53
- self .data = defaultdict ( AttestationsAccumulator , data or {})
44
+ def __init__ (self , data : list [ AttestationsAccumulator | None ] | None = None ) -> None :
45
+ self .data = data or []
54
46
self ._epochs_to_process = set ()
55
47
self ._processed_epochs = set ()
56
48
@@ -88,13 +80,16 @@ def buffer(self) -> Path:
88
80
return self .file ().with_suffix (".buf" )
89
81
90
82
def clear (self ) -> None :
91
- self .data = defaultdict ( AttestationsAccumulator )
83
+ self .data = []
92
84
self ._epochs_to_process .clear ()
93
85
self ._processed_epochs .clear ()
94
86
assert self .is_empty
95
87
96
- def inc (self , key : ValidatorIndex , included : bool ) -> None :
97
- self .data [key ].add_duty (included )
88
+ def inc (self , key : ValidatorIndex , is_included : bool ) -> None :
89
+ if key >= len (self .data ):
90
+ self .data += [None ] * (key - len (self .data ) + 1 )
91
+ assigned , included = self .data [key ] or (Assigned (0 ), Included (0 ))
92
+ self .data [key ] = AttestationsAccumulator ((Assigned (assigned + 1 ), Included (included + 1 if is_included else included )))
98
93
99
94
def add_processed_epoch (self , epoch : EpochNumber ) -> None :
100
95
self ._processed_epochs .add (epoch )
@@ -149,15 +144,15 @@ def frame(self) -> tuple[EpochNumber, EpochNumber]:
149
144
def get_network_aggr (self ) -> AttestationsAccumulator :
150
145
"""Return `AttestationsAccumulator` over duties of all the network validators"""
151
146
152
- included = assigned = 0
153
- for validator , acc in self .data . items ( ):
154
- if acc . included > acc . assigned :
155
- raise ValueError ( f"Invalid accumulator: { validator = } , { acc = } " )
156
- included + = acc . included
157
- assigned += acc . assigned
158
- aggr = AttestationsAccumulator (
159
- included = included ,
160
- assigned = assigned ,
161
- )
162
- logger .info ({"msg" : "Network attestations aggregate computed" , "value" : repr (aggr ), "avg_perf" : aggr . perf })
147
+ net_included = net_assigned = 0
148
+ for validator_index , acc in enumerate ( self .data ):
149
+ if acc is None :
150
+ continue
151
+ assigned , included = acc
152
+ if included > assigned :
153
+ raise ValueError ( f"Invalid accumulator: { validator_index = } , { acc = } " )
154
+ net_included += included
155
+ net_assigned += assigned
156
+ aggr = AttestationsAccumulator (( Assigned ( net_assigned ), Included ( net_included )) )
157
+ logger .info ({"msg" : "Network attestations aggregate computed" , "value" : repr (aggr ), "avg_perf" : perf ( aggr ) })
163
158
return aggr
0 commit comments