11use std:: {
2- collections:: HashSet ,
2+ collections:: { HashMap , HashSet } ,
33 net:: SocketAddr ,
44 sync:: {
55 Arc , RwLock ,
@@ -36,7 +36,10 @@ use cb_common::{
3636 get_consensus_version_header, get_content_type,
3737 } ,
3838} ;
39- use cb_pbs:: MAX_SIZE_SUBMIT_BLOCK_RESPONSE ;
39+ use cb_pbs:: {
40+ GET_HEADER_ENDPOINT_TAG , MAX_SIZE_SUBMIT_BLOCK_RESPONSE , REGISTER_VALIDATOR_ENDPOINT_TAG ,
41+ STATUS_ENDPOINT_TAG , SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG ,
42+ } ;
4043use lh_types:: KzgProof ;
4144use reqwest:: header:: { ACCEPT , CONTENT_TYPE } ;
4245use ssz:: Encode ;
@@ -95,7 +98,9 @@ pub struct MockRelayState {
9598 /// The raw `Accept` header PBS sent on the most recent get_header request,
9699 /// so a test can assert what encoding PBS asked the relay for.
97100 received_get_header_accept : RwLock < Option < String > > ,
98- last_register_api_key : RwLock < Option < String > > ,
101+ /// Api key header seen per endpoint tag, so a test can assert the relay's
102+ /// configured key rides on every request PBS sends it.
103+ api_keys_seen : RwLock < HashMap < & ' static str , String > > ,
99104}
100105
101106impl MockRelayState {
@@ -135,8 +140,14 @@ impl MockRelayState {
135140 pub fn set_response_override ( & self , status : StatusCode ) {
136141 * self . response_override . write ( ) . unwrap ( ) = Some ( status) ;
137142 }
138- pub fn last_register_api_key ( & self ) -> Option < String > {
139- self . last_register_api_key . read ( ) . unwrap ( ) . clone ( )
143+ /// Api key seen on `endpoint`, one of the `*_ENDPOINT_TAG` constants
144+ pub fn api_key_seen ( & self , endpoint : & str ) -> Option < String > {
145+ self . api_keys_seen . read ( ) . unwrap ( ) . get ( endpoint) . cloned ( )
146+ }
147+ fn record_api_key ( & self , endpoint : & ' static str , headers : & HeaderMap ) {
148+ if let Some ( api_key) = headers. get ( HEADER_API_KEY ) . and_then ( |key| key. to_str ( ) . ok ( ) ) {
149+ self . api_keys_seen . write ( ) . unwrap ( ) . insert ( endpoint, api_key. to_string ( ) ) ;
150+ }
140151 }
141152}
142153
@@ -158,7 +169,7 @@ impl MockRelayState {
158169 response_override : RwLock :: new ( None ) ,
159170 bid_value : RwLock :: new ( U256 :: from ( 10 ) ) ,
160171 received_get_header_accept : RwLock :: new ( None ) ,
161- last_register_api_key : RwLock :: new ( None ) ,
172+ api_keys_seen : RwLock :: new ( HashMap :: new ( ) ) ,
162173 supported_content_types : Arc :: new (
163174 [ EncodingType :: Json , EncodingType :: Ssz ] . iter ( ) . cloned ( ) . collect ( ) ,
164175 ) ,
@@ -260,6 +271,7 @@ async fn handle_get_header(
260271 headers : HeaderMap ,
261272) -> Response {
262273 state. received_get_header . fetch_add ( 1 , Ordering :: Relaxed ) ;
274+ state. record_api_key ( GET_HEADER_ENDPOINT_TAG , & headers) ;
263275 * state. received_get_header_accept . write ( ) . unwrap ( ) =
264276 headers. get ( ACCEPT ) . and_then ( |v| v. to_str ( ) . ok ( ) ) . map ( String :: from) ;
265277 let accept_types = get_accept_types ( & headers)
@@ -322,8 +334,12 @@ async fn handle_get_header(
322334 response
323335}
324336
325- async fn handle_get_status ( State ( state) : State < Arc < MockRelayState > > ) -> impl IntoResponse {
337+ async fn handle_get_status (
338+ State ( state) : State < Arc < MockRelayState > > ,
339+ headers : HeaderMap ,
340+ ) -> impl IntoResponse {
326341 state. received_get_status . fetch_add ( 1 , Ordering :: Relaxed ) ;
342+ state. record_api_key ( STATUS_ENDPOINT_TAG , & headers) ;
327343 // Production `get_status` dispatches relays concurrently via `select_ok`,
328344 // which cancels losing futures as soon as any relay returns OK. On a
329345 // loaded runner this can abort a sibling relay's reqwest send before
@@ -341,10 +357,7 @@ async fn handle_register_validator(
341357 Json ( validators) : Json < Vec < ValidatorRegistration > > ,
342358) -> impl IntoResponse {
343359 state. received_register_validator . fetch_add ( 1 , Ordering :: Relaxed ) ;
344- * state. last_register_api_key . write ( ) . unwrap ( ) = headers
345- . get ( HEADER_API_KEY )
346- . and_then ( |value| value. to_str ( ) . ok ( ) )
347- . map ( |value| value. to_string ( ) ) ;
360+ state. record_api_key ( REGISTER_VALIDATOR_ENDPOINT_TAG , & headers) ;
348361 debug ! ( "Received {} registrations" , validators. len( ) ) ;
349362
350363 if let Some ( status) = state. response_override . read ( ) . unwrap ( ) . as_ref ( ) {
@@ -363,6 +376,7 @@ async fn handle_submit_block_v1(
363376 return StatusCode :: NOT_FOUND . into_response ( ) ;
364377 }
365378 state. received_submit_block . fetch_add ( 1 , Ordering :: Relaxed ) ;
379+ state. record_api_key ( SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG , & headers) ;
366380 // Short-circuit SSZ requests with an overridden status so tests can
367381 // drive the PBS SSZ→JSON retry logic. JSON requests still take the
368382 // normal path so a single mock run can exercise both attempts.
@@ -475,6 +489,7 @@ async fn handle_submit_block_v2(
475489 return StatusCode :: NOT_FOUND . into_response ( ) ;
476490 }
477491 state. received_submit_block . fetch_add ( 1 , Ordering :: Relaxed ) ;
492+ state. record_api_key ( SUBMIT_BLINDED_BLOCK_ENDPOINT_TAG , & headers) ;
478493 // See comment in `handle_submit_block_v1`. Override SSZ with the
479494 // injected status so C3 tests can assert retry / no-retry behavior.
480495 if let Some ( status) = state. submit_block_ssz_status_override ( ) &&
0 commit comments