22
33use axum:: { routing:: get, routing:: post, Router } ;
44use axum_prometheus:: PrometheusMetricLayer ;
5+ use calendar_duration:: CalendarDuration ;
56use clap:: Parser ;
67use metrics_exporter_prometheus:: PrometheusHandle ;
78use rlimit:: Resource ;
8- use std :: sync :: { Arc , RwLock } ;
9+ use state :: { OPRFServer , OPRFState } ;
910use tikv_jemallocator:: Jemalloc ;
10- use time:: format_description:: well_known:: Rfc3339 ;
1111use time:: OffsetDateTime ;
1212use tracing:: { debug, info, metadata:: LevelFilter } ;
1313use tracing_subscriber:: EnvFilter ;
14+ use util:: { assert_unique_names, parse_timestamp} ;
1415
1516#[ global_allocator]
1617static GLOBAL : Jemalloc = Jemalloc ;
1718
1819mod handler;
1920mod state;
20-
21- pub use state:: OPRFState ;
21+ mod util;
2222
2323#[ cfg( test) ]
2424mod tests;
@@ -27,15 +27,22 @@ mod tests;
2727const MAX_POINTS : usize = 1024 ;
2828
2929/// Command line switches
30- #[ derive( Parser , Debug ) ]
30+ #[ derive( Parser , Debug , Clone ) ]
3131#[ command( author, version, about, long_about = None ) ]
3232pub struct Config {
3333 /// Host and port to listen for http connections
3434 #[ arg( long, default_value = "127.0.0.1:8080" ) ]
3535 listen : String ,
36- /// Duration of each randomness epoch
37- #[ arg( long, default_value_t = 5 ) ]
38- epoch_seconds : u32 ,
36+ /// Name of OPRF instance contained in server. Multiple instances may be defined
37+ /// by defining this switch multiple times. The first defined instance will
38+ /// become the default instance.
39+ #[ arg( long = "instance-name" , default_value = "main" ) ]
40+ instance_names : Vec < String > ,
41+ /// Duration of each randomness epoch. This switch may be defined multiple times
42+ /// to set the epoch length for each respective instance, if multiple instances
43+ /// are defined.
44+ #[ arg( long = "epoch-duration" , value_name = "Duration string i.e. 1mon5h2s" , default_values = [ "5s" ] ) ]
45+ epoch_durations : Vec < CalendarDuration > ,
3946 /// First epoch tag to make available
4047 #[ arg( long, default_value_t = 0 ) ]
4148 first_epoch : u8 ,
@@ -56,20 +63,24 @@ pub struct Config {
5663 prometheus_listen : Option < String > ,
5764}
5865
59- /// Parse a timestamp given as a config option
60- fn parse_timestamp ( stamp : & str ) -> Result < OffsetDateTime , & ' static str > {
61- OffsetDateTime :: parse ( stamp, & Rfc3339 ) . map_err ( |_| "Try something like '2023-05-15T04:30:00Z'." )
62- }
63-
6466/// Initialize an axum::Router for our web service
6567/// Having this as a separate function makes testing easier.
6668fn app ( oprf_state : OPRFState ) -> Router {
6769 Router :: new ( )
6870 // Friendly default route to identify the site
6971 . route ( "/" , get ( || async { "STAR randomness server\n " } ) )
70- // Main endpoints
71- . route ( "/randomness" , post ( handler:: randomness) )
72- . route ( "/info" , get ( handler:: info) )
72+ // Endpoints for all instances
73+ . route (
74+ "/instances/:instance/randomness" ,
75+ post ( handler:: specific_instance_randomness) ,
76+ )
77+ . route (
78+ "/instances/:instance/info" ,
79+ get ( handler:: specific_instance_info) ,
80+ )
81+ // Endpoints for default instance
82+ . route ( "/randomness" , post ( handler:: default_instance_randomness) )
83+ . route ( "/info" , get ( handler:: default_instance_info) )
7384 // Attach shared state
7485 . with_state ( oprf_state)
7586 // Logging must come after active routes
@@ -126,22 +137,28 @@ async fn main() {
126137 increase_nofile_limit ( ) ;
127138 }
128139
129- // Oblivious function state
130- info ! ( "initializing OPRF state..." ) ;
131- let server = state:: OPRFServer :: new ( & config) . expect ( "Could not initialize PPOPRF state" ) ;
132- info ! ( "epoch now {}" , server. epoch) ;
133- let oprf_state = Arc :: new ( RwLock :: new ( server) ) ;
140+ assert_unique_names ( & config. instance_names ) ;
141+ assert ! (
142+ !config. epoch_durations. iter( ) . any( |d| d. is_zero( ) ) ,
143+ "all epoch lengths must be non-zero"
144+ ) ;
145+ assert ! (
146+ !config. instance_names. is_empty( ) ,
147+ "at least one instance name must be defined"
148+ ) ;
149+ assert ! (
150+ config. instance_names. len( ) == config. epoch_durations. len( ) ,
151+ "instance-name switch count must match epoch-seconds switch count"
152+ ) ;
134153
135154 let metric_layer = config. prometheus_listen . as_ref ( ) . map ( |listen| {
136155 let ( layer, handle) = PrometheusMetricLayer :: pair ( ) ;
137156 start_prometheus_server ( handle, listen. clone ( ) ) ;
138157 layer
139158 } ) ;
140159
141- // Spawn a background process to advance the epoch
142- info ! ( "Spawning background epoch rotation task..." ) ;
143- let background_state = oprf_state. clone ( ) ;
144- tokio:: spawn ( async move { state:: epoch_loop ( background_state, & config) . await } ) ;
160+ let oprf_state = OPRFServer :: new ( & config) ;
161+ oprf_state. start_background_tasks ( & config) ;
145162
146163 // Set up routes and middleware
147164 info ! ( "initializing routes..." ) ;
0 commit comments