@@ -45,7 +45,12 @@ pub fn sync_committee_duties<T: BeaconChainTypes>(
4545 // the vast majority of requests. Rather than checking if we think the request will succeed in a
4646 // way prone to data races, we attempt the request immediately and check the error code.
4747 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+ }
4954 Err ( BeaconChainError :: SyncDutiesError ( BeaconStateError :: SyncCommitteeNotKnown {
5055 ..
5156 } ) )
@@ -64,7 +69,10 @@ pub fn sync_committee_duties<T: BeaconChainTypes>(
6469 ) ) ,
6570 e => warp_utils:: reject:: beacon_chain_error ( e) ,
6671 } ) ?;
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+ ) )
6876}
6977
7078/// 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>(
7381 request_indices : & [ u64 ] ,
7482 altair_fork_epoch : Epoch ,
7583 chain : & BeaconChain < T > ,
76- ) -> Result < Vec < Option < SyncDuty > > , BeaconChainError > {
84+ ) -> Result < Vec < Result < Option < SyncDuty > , BeaconStateError > > , BeaconChainError > {
7785 // Determine what the current epoch would be if we fast-forward our system clock by
7886 // `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
7987 //
@@ -121,6 +129,45 @@ fn duties_from_state_load<T: BeaconChainTypes>(
121129 }
122130}
123131
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+
124171fn convert_to_response ( duties : Vec < Option < SyncDuty > > , execution_optimistic : bool ) -> SyncDuties {
125172 api_types:: GenericResponse :: from ( duties. into_iter ( ) . flatten ( ) . collect :: < Vec < _ > > ( ) )
126173 . add_execution_optimistic ( execution_optimistic)
0 commit comments