@@ -8,7 +8,7 @@ const MITHRIL_SIGNER_VERSION_HEADER: &str = "signer-node-version";
88pub fn routes (
99 dependency_manager : Arc < DependencyContainer > ,
1010) -> impl Filter < Extract = ( impl warp:: Reply , ) , Error = warp:: Rejection > + Clone {
11- register_signer ( dependency_manager)
11+ register_signer ( dependency_manager. clone ( ) ) . or ( registered_signers ( dependency_manager ) )
1212}
1313
1414/// POST /register-signer
@@ -31,10 +31,22 @@ fn register_signer(
3131 . and_then ( handlers:: register_signer)
3232}
3333
34+ /// Get /signers/registered/:epoch
35+ fn registered_signers (
36+ dependency_manager : Arc < DependencyContainer > ,
37+ ) -> impl Filter < Extract = ( impl warp:: Reply , ) , Error = warp:: Rejection > + Clone {
38+ warp:: path!( "signers" / "registered" / String )
39+ . and ( warp:: get ( ) )
40+ . and ( middlewares:: with_verification_key_store ( dependency_manager) )
41+ . and_then ( handlers:: registered_signers)
42+ }
43+
3444mod handlers {
45+ use crate :: entities:: SignerRegistrationsMessage ;
3546 use crate :: event_store:: { EventMessage , TransmitterService } ;
36- use crate :: FromRegisterSignerAdapter ;
3747 use crate :: { http_server:: routes:: reply, SignerRegisterer , SignerRegistrationError } ;
48+ use crate :: { FromRegisterSignerAdapter , VerificationKeyStorer } ;
49+ use mithril_common:: entities:: Epoch ;
3850 use mithril_common:: messages:: { RegisterSignerMessage , TryFromMessageAdapter } ;
3951 use mithril_common:: BeaconProvider ;
4052 use slog_scope:: { debug, warn} ;
@@ -139,21 +151,68 @@ mod handlers {
139151 }
140152 }
141153 }
154+
155+ /// Get Registered Signers for a given epoch
156+ pub async fn registered_signers (
157+ registered_at : String ,
158+ verification_key_store : Arc < dyn VerificationKeyStorer > ,
159+ ) -> Result < impl warp:: Reply , Infallible > {
160+ debug ! ( "⇄ HTTP SERVER: signers/registered/{:?}" , registered_at) ;
161+
162+ let registered_at = match registered_at. parse :: < u64 > ( ) {
163+ Ok ( epoch) => Epoch ( epoch) ,
164+ Err ( err) => {
165+ warn ! ( "registered_signers::invalid_epoch" ; "error" => ?err) ;
166+ return Ok ( reply:: bad_request (
167+ "invalid_epoch" . to_string ( ) ,
168+ err. to_string ( ) ,
169+ ) ) ;
170+ }
171+ } ;
172+
173+ // The given epoch is the epoch at which the signer registered, the store works on
174+ // the recording epoch so we need to offset.
175+ match verification_key_store
176+ . get_stake_distribution_for_epoch ( registered_at. offset_to_recording_epoch ( ) )
177+ . await
178+ {
179+ Ok ( Some ( stake_distribution) ) => {
180+ let message = SignerRegistrationsMessage :: new ( registered_at, stake_distribution) ;
181+ Ok ( reply:: json ( & message, StatusCode :: OK ) )
182+ }
183+ Ok ( None ) => {
184+ warn ! ( "registered_signers::not_found" ) ;
185+ Ok ( reply:: empty ( StatusCode :: NOT_FOUND ) )
186+ }
187+ Err ( err) => {
188+ warn ! ( "registered_signers::error" ; "error" => ?err) ;
189+ Ok ( reply:: internal_server_error ( err. to_string ( ) ) )
190+ }
191+ }
192+ }
142193}
143194
144195#[ cfg( test) ]
145196mod tests {
146- use mithril_common:: crypto_helper:: ProtocolRegistrationError ;
147- use mithril_common:: messages:: RegisterSignerMessage ;
148- use mithril_common:: test_utils:: apispec:: APISpec ;
149- use mithril_common:: test_utils:: fake_data;
150- use warp:: http:: Method ;
151- use warp:: test:: request;
197+ use mithril_common:: entities:: Epoch ;
198+ use mithril_common:: {
199+ crypto_helper:: ProtocolRegistrationError ,
200+ entities:: StakeDistribution ,
201+ messages:: RegisterSignerMessage ,
202+ store:: adapter:: AdapterError ,
203+ test_utils:: { apispec:: APISpec , fake_data} ,
204+ } ;
205+ use mockall:: predicate:: eq;
206+ use serde_json:: Value :: Null ;
207+ use warp:: { http:: Method , test:: request} ;
208+
209+ use crate :: {
210+ http_server:: SERVER_BASE_PATH , initialize_dependencies,
211+ signer_registerer:: MockSignerRegisterer , store:: MockVerificationKeyStorer ,
212+ SignerRegistrationError ,
213+ } ;
152214
153215 use super :: * ;
154- use crate :: http_server:: SERVER_BASE_PATH ;
155- use crate :: signer_registerer:: MockSignerRegisterer ;
156- use crate :: { initialize_dependencies, SignerRegistrationError } ;
157216
158217 fn setup_router (
159218 dependency_manager : Arc < DependencyContainer > ,
@@ -316,4 +375,128 @@ mod tests {
316375 & response,
317376 ) ;
318377 }
378+
379+ #[ tokio:: test]
380+ async fn test_registered_signers_get_offset_given_epoch_to_registration_epoch ( ) {
381+ let asked_epoch = Epoch ( 1 ) ;
382+ let expected_retrieval_epoch = asked_epoch. offset_to_recording_epoch ( ) ;
383+ let stake_distribution = StakeDistribution :: from_iter (
384+ fake_data:: signers_with_stakes ( 3 )
385+ . into_iter ( )
386+ . map ( |s| ( s. party_id , s. stake ) ) ,
387+ ) ;
388+ let mut mock_verification_key_store = MockVerificationKeyStorer :: new ( ) ;
389+ mock_verification_key_store
390+ . expect_get_stake_distribution_for_epoch ( )
391+ . with ( eq ( expected_retrieval_epoch) )
392+ . return_once ( |_| Ok ( Some ( stake_distribution) ) )
393+ . once ( ) ;
394+ let mut dependency_manager = initialize_dependencies ( ) . await ;
395+ dependency_manager. verification_key_store = Arc :: new ( mock_verification_key_store) ;
396+
397+ let method = Method :: GET . as_str ( ) ;
398+ let base_path = "/signers/registered" ;
399+
400+ let response = request ( )
401+ . method ( method)
402+ . path ( & format ! ( "/{SERVER_BASE_PATH}{base_path}/{}" , asked_epoch) )
403+ . reply ( & setup_router ( Arc :: new ( dependency_manager) ) )
404+ . await ;
405+
406+ assert ! (
407+ response. status( ) . is_success( ) ,
408+ "expected the response to succeed, was: {response:#?}"
409+ ) ;
410+ }
411+
412+ #[ tokio:: test]
413+ async fn test_registered_signers_get_ok ( ) {
414+ let stake_distribution = StakeDistribution :: from_iter (
415+ fake_data:: signers_with_stakes ( 3 )
416+ . into_iter ( )
417+ . map ( |s| ( s. party_id , s. stake ) ) ,
418+ ) ;
419+ let mut mock_verification_key_store = MockVerificationKeyStorer :: new ( ) ;
420+ mock_verification_key_store
421+ . expect_get_stake_distribution_for_epoch ( )
422+ . return_once ( |_| Ok ( Some ( stake_distribution) ) )
423+ . once ( ) ;
424+ let mut dependency_manager = initialize_dependencies ( ) . await ;
425+ dependency_manager. verification_key_store = Arc :: new ( mock_verification_key_store) ;
426+
427+ let base_path = "/signers/registered" ;
428+ let method = Method :: GET . as_str ( ) ;
429+
430+ let response = request ( )
431+ . method ( method)
432+ . path ( & format ! ( "/{SERVER_BASE_PATH}{base_path}/1" ) )
433+ . reply ( & setup_router ( Arc :: new ( dependency_manager) ) )
434+ . await ;
435+
436+ APISpec :: verify_conformity (
437+ APISpec :: get_all_spec_files ( ) ,
438+ method,
439+ & format ! ( "{base_path}/{{epoch}}" ) ,
440+ "application/json" ,
441+ & Null ,
442+ & response,
443+ ) ;
444+ }
445+
446+ #[ tokio:: test]
447+ async fn test_registered_signers_get_ok_noregistration ( ) {
448+ let mut mock_verification_key_store = MockVerificationKeyStorer :: new ( ) ;
449+ mock_verification_key_store
450+ . expect_get_stake_distribution_for_epoch ( )
451+ . return_once ( |_| Ok ( None ) )
452+ . once ( ) ;
453+ let mut dependency_manager = initialize_dependencies ( ) . await ;
454+ dependency_manager. verification_key_store = Arc :: new ( mock_verification_key_store) ;
455+
456+ let method = Method :: GET . as_str ( ) ;
457+ let base_path = "/signers/registered" ;
458+
459+ let response = request ( )
460+ . method ( method)
461+ . path ( & format ! ( "/{SERVER_BASE_PATH}{base_path}/3" ) )
462+ . reply ( & setup_router ( Arc :: new ( dependency_manager) ) )
463+ . await ;
464+
465+ APISpec :: verify_conformity (
466+ APISpec :: get_all_spec_files ( ) ,
467+ method,
468+ & format ! ( "{base_path}/{{epoch}}" ) ,
469+ "application/json" ,
470+ & Null ,
471+ & response,
472+ ) ;
473+ }
474+
475+ #[ tokio:: test]
476+ async fn test_registered_signers_get_ko ( ) {
477+ let mut mock_verification_key_store = MockVerificationKeyStorer :: new ( ) ;
478+ mock_verification_key_store
479+ . expect_get_stake_distribution_for_epoch ( )
480+ . return_once ( |_| Err ( AdapterError :: GeneralError ( "invalid query" . to_string ( ) ) . into ( ) ) ) ;
481+ let mut dependency_manager = initialize_dependencies ( ) . await ;
482+ dependency_manager. verification_key_store = Arc :: new ( mock_verification_key_store) ;
483+
484+ let method = Method :: GET . as_str ( ) ;
485+ let base_path = "/signers/registered" ;
486+
487+ let response = request ( )
488+ . method ( method)
489+ . path ( & format ! ( "/{SERVER_BASE_PATH}{base_path}/1" ) )
490+ . reply ( & setup_router ( Arc :: new ( dependency_manager) ) )
491+ . await ;
492+
493+ APISpec :: verify_conformity (
494+ APISpec :: get_all_spec_files ( ) ,
495+ method,
496+ & format ! ( "{base_path}/{{epoch}}" ) ,
497+ "application/json" ,
498+ & Null ,
499+ & response,
500+ ) ;
501+ }
319502}
0 commit comments