@@ -45,7 +45,12 @@ pub fn sync_committee_duties<T: BeaconChainTypes>(
45
45
// the vast majority of requests. Rather than checking if we think the request will succeed in a
46
46
// way prone to data races, we attempt the request immediately and check the error code.
47
47
match chain. sync_committee_duties_from_head ( request_epoch, request_indices) {
48
- Ok ( duties) => return Ok ( convert_to_response ( duties, execution_optimistic) ) ,
48
+ Ok ( duties) => {
49
+ return Ok ( convert_to_response (
50
+ verify_unknown_validators ( duties, request_epoch, chain) ?,
51
+ execution_optimistic,
52
+ ) )
53
+ }
49
54
Err ( BeaconChainError :: SyncDutiesError ( BeaconStateError :: SyncCommitteeNotKnown {
50
55
..
51
56
} ) )
@@ -64,7 +69,10 @@ pub fn sync_committee_duties<T: BeaconChainTypes>(
64
69
) ) ,
65
70
e => warp_utils:: reject:: beacon_chain_error ( e) ,
66
71
} ) ?;
67
- Ok ( convert_to_response ( duties, execution_optimistic) )
72
+ Ok ( convert_to_response (
73
+ verify_unknown_validators ( duties, request_epoch, chain) ?,
74
+ execution_optimistic,
75
+ ) )
68
76
}
69
77
70
78
/// Slow path for duties: load a state and use it to compute the duties.
@@ -73,7 +81,7 @@ fn duties_from_state_load<T: BeaconChainTypes>(
73
81
request_indices : & [ u64 ] ,
74
82
altair_fork_epoch : Epoch ,
75
83
chain : & BeaconChain < T > ,
76
- ) -> Result < Vec < Option < SyncDuty > > , BeaconChainError > {
84
+ ) -> Result < Vec < Result < Option < SyncDuty > , BeaconStateError > > , BeaconChainError > {
77
85
// Determine what the current epoch would be if we fast-forward our system clock by
78
86
// `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
79
87
//
@@ -121,6 +129,45 @@ fn duties_from_state_load<T: BeaconChainTypes>(
121
129
}
122
130
}
123
131
132
+ fn verify_unknown_validators < T : BeaconChainTypes > (
133
+ duties : Vec < Result < Option < SyncDuty > , BeaconStateError > > ,
134
+ request_epoch : Epoch ,
135
+ chain : & BeaconChain < T > ,
136
+ ) -> Result < Vec < Option < SyncDuty > > , warp:: reject:: Rejection > {
137
+ // Lazily load the request_epoch_state, as it is only needed if there are any UnknownValidator
138
+ let mut request_epoch_state = None ;
139
+
140
+ duties
141
+ . into_iter ( )
142
+ . map ( |res| {
143
+ res. or_else ( |err| {
144
+ // Make sure the validator is really unknown w.r.t. the request_epoch
145
+ if let BeaconStateError :: UnknownValidator ( idx) = err {
146
+ let request_epoch_state = match & mut request_epoch_state {
147
+ Some ( state) => state,
148
+ None => request_epoch_state. insert ( chain. state_at_slot (
149
+ request_epoch. start_slot ( T :: EthSpec :: slots_per_epoch ( ) ) ,
150
+ StateSkipConfig :: WithoutStateRoots ,
151
+ ) ?) ,
152
+ } ;
153
+ request_epoch_state
154
+ . get_validator ( idx)
155
+ . map_err ( BeaconChainError :: SyncDutiesError )
156
+ . map ( |_| None )
157
+ } else {
158
+ Err ( BeaconChainError :: SyncDutiesError ( err) )
159
+ }
160
+ } )
161
+ } )
162
+ . collect :: < Result < Vec < _ > , _ > > ( )
163
+ . map_err ( |err| match err {
164
+ BeaconChainError :: SyncDutiesError ( BeaconStateError :: UnknownValidator ( idx) ) => {
165
+ warp_utils:: reject:: custom_bad_request ( format ! ( "invalid validator index: {idx}" ) )
166
+ }
167
+ e => warp_utils:: reject:: beacon_chain_error ( e) ,
168
+ } )
169
+ }
170
+
124
171
fn convert_to_response ( duties : Vec < Option < SyncDuty > > , execution_optimistic : bool ) -> SyncDuties {
125
172
api_types:: GenericResponse :: from ( duties. into_iter ( ) . flatten ( ) . collect :: < Vec < _ > > ( ) )
126
173
. add_execution_optimistic ( execution_optimistic)
0 commit comments