@@ -9,6 +9,7 @@ use duration_str::deserialize_duration;
99use semver:: VersionReq ;
1010use serde:: { Deserialize , Serialize } ;
1111use std:: time:: Duration ;
12+ use sui_sdk_types:: Address ;
1213use sui_types:: base_types:: ObjectID ;
1314use tracing:: info;
1415
@@ -47,6 +48,14 @@ pub enum ServerMode {
4748 // Master key is expected to by 32 byte HKDF seed
4849 client_configs : Vec < ClientConfig > ,
4950 } ,
51+ Committee {
52+ member_address : Address ,
53+ key_server_obj_id : Address ,
54+ /// The target key server version set during rotation. During rotation, this can be exactly
55+ /// 1 greater than the onchain version. Onchain rotation is completed, the onchain version
56+ /// matches this version, NEXT_MASTER_SHARE will be used.
57+ target_key_server_version : u32 ,
58+ } ,
5059}
5160
5261/// Configuration for the RPC client.
@@ -255,26 +264,6 @@ impl KeyServerOptions {
255264 }
256265 Ok ( ( ) )
257266 }
258-
259- pub ( crate ) fn get_supported_key_server_object_ids ( & self ) -> Vec < ObjectID > {
260- match & self . server_mode {
261- ServerMode :: Open {
262- key_server_object_id,
263- } => {
264- vec ! [ * key_server_object_id]
265- }
266- ServerMode :: Permissioned { client_configs } => client_configs
267- . iter ( )
268- . filter ( |c| {
269- matches ! (
270- c. client_master_key,
271- ClientKeyType :: Derived { .. } | ClientKeyType :: Imported { .. }
272- )
273- } )
274- . map ( |c| c. key_server_object_id )
275- . collect ( ) ,
276- }
277- }
278267}
279268
280269fn default_checkpoint_update_interval ( ) -> Duration {
@@ -378,9 +367,17 @@ server_mode: !Open
378367 }
379368}
380369
381- #[ test]
382- fn test_parse_permissioned_config ( ) {
370+ #[ tokio:: test]
371+ async fn test_parse_permissioned_config ( ) {
372+ use crate :: master_keys:: MasterKeys ;
373+ use crate :: types:: IbeMasterKey ;
374+ use crate :: DefaultEncoding ;
375+ use crate :: Server ;
376+ use fastcrypto:: encoding:: Encoding ;
377+ use fastcrypto:: groups:: GroupElement ;
378+ use seal_committee:: create_grpc_client;
383379 use std:: str:: FromStr ;
380+ use temp_env:: async_with_vars;
384381
385382 let valid_configuration = r#"
386383network: Mainnet
@@ -416,19 +413,37 @@ session_key_ttl_max: '60s'
416413 let options: KeyServerOptions =
417414 serde_yaml:: from_str ( valid_configuration) . expect ( "Failed to parse valid configuration" ) ;
418415
419- assert_eq ! (
420- options. get_supported_key_server_object_ids( ) ,
421- vec![
422- ObjectID :: from_str(
423- "0xaaaa000000000000000000000000000000000000000000000000000000000001"
424- )
425- . unwrap( ) ,
426- ObjectID :: from_str(
427- "0xbbbb000000000000000000000000000000000000000000000000000000000002"
428- )
429- . unwrap( ) ,
430- ]
431- ) ;
416+ let seed_encoded = DefaultEncoding :: encode ( [ 1u8 ; 32 ] ) ;
417+ let bob_key = IbeMasterKey :: generator ( ) ;
418+ let bob_key_encoded = DefaultEncoding :: encode ( bcs:: to_bytes ( & bob_key) . unwrap ( ) ) ;
419+
420+ async_with_vars (
421+ [
422+ ( "MASTER_KEY" , Some ( & seed_encoded) ) ,
423+ ( "BOB_BLS_KEY" , Some ( & bob_key_encoded) ) ,
424+ ] ,
425+ async {
426+ let master_keys = MasterKeys :: load ( & options) . expect ( "Failed to load master keys" ) ;
427+ let grpc_client = create_grpc_client ( & seal_committee:: Network :: Testnet ) . unwrap ( ) ;
428+ let map = Server :: build_key_server_pop_map ( & options, & master_keys, grpc_client)
429+ . await
430+ . unwrap ( ) ;
431+ assert_eq ! ( map. keys( ) . len( ) , 2 ) ;
432+ assert ! ( map. contains_key(
433+ & ObjectID :: from_str(
434+ "0xaaaa000000000000000000000000000000000000000000000000000000000001"
435+ )
436+ . unwrap( )
437+ ) ) ;
438+ assert ! ( map. contains_key(
439+ & ObjectID :: from_str(
440+ "0xbbbb000000000000000000000000000000000000000000000000000000000002"
441+ )
442+ . unwrap( )
443+ ) ) ;
444+ } ,
445+ )
446+ . await ;
432447}
433448
434449#[ test]
@@ -569,3 +584,72 @@ server_mode: !Permissioned
569584 assert_eq ! ( result. unwrap_err( ) . to_string( ) , expected_error) ;
570585 }
571586}
587+
588+ #[ test]
589+ fn test_committee_mode_master_keys ( ) {
590+ use crate :: master_keys:: MasterKeys ;
591+ use crate :: types:: IbeMasterKey ;
592+ use crate :: DefaultEncoding ;
593+ use fastcrypto:: encoding:: Encoding ;
594+ use fastcrypto:: groups:: GroupElement ;
595+ use temp_env:: with_vars;
596+
597+ // master_share with version 0 (fresh DKG), no next_master_share expected.
598+ let config_fresh_dkg = r#"
599+ network: Mainnet
600+ server_mode: !Committee
601+ member_address: '0x0000000000000000000000000000000000000000000000000000000000000000'
602+ key_server_obj_id: '0x0000000000000000000000000000000000000000000000000000000000000001'
603+ target_key_server_version: 0
604+ "# ;
605+ let options: KeyServerOptions =
606+ serde_yaml:: from_str ( config_fresh_dkg) . expect ( "Failed to parse configuration" ) ;
607+
608+ let master_share = IbeMasterKey :: generator ( ) ;
609+ let master_share_encoded = DefaultEncoding :: encode ( bcs:: to_bytes ( & master_share) . unwrap ( ) ) ;
610+
611+ with_vars ( [ ( "MASTER_SHARE" , Some ( & master_share_encoded) ) ] , || {
612+ assert ! ( MasterKeys :: load( & options) . is_ok( ) ) ;
613+ } ) ;
614+
615+ // master_share and next_master_share with version > 0 (rotation), both shares expected.
616+ let config_rotation = r#"
617+ network: Mainnet
618+ server_mode: !Committee
619+ member_address: '0x0000000000000000000000000000000000000000000000000000000000000000'
620+ key_server_obj_id: '0x0000000000000000000000000000000000000000000000000000000000000001'
621+ target_key_server_version: 1
622+ "# ;
623+ let options_rotation: KeyServerOptions =
624+ serde_yaml:: from_str ( config_rotation) . expect ( "Failed to parse configuration" ) ;
625+
626+ let next_master_share = IbeMasterKey :: generator ( ) ;
627+ let next_master_share_encoded =
628+ DefaultEncoding :: encode ( bcs:: to_bytes ( & next_master_share) . unwrap ( ) ) ;
629+
630+ with_vars (
631+ [
632+ ( "MASTER_SHARE" , Some ( & master_share_encoded) ) ,
633+ ( "NEXT_MASTER_SHARE" , Some ( & next_master_share_encoded) ) ,
634+ ] ,
635+ || {
636+ assert ! ( MasterKeys :: load( & options_rotation) . is_ok( ) ) ;
637+ } ,
638+ ) ;
639+
640+ // next_master_share present but version is 0 (fresh DKG), should fail.
641+ with_vars (
642+ [
643+ ( "MASTER_SHARE" , Some ( & master_share_encoded) ) ,
644+ ( "NEXT_MASTER_SHARE" , Some ( & next_master_share_encoded) ) ,
645+ ] ,
646+ || {
647+ assert ! ( MasterKeys :: load( & options) . is_err( ) ) ;
648+ } ,
649+ ) ;
650+
651+ // next_master_share absent but version > 0 (rotation complete), should succeed.
652+ with_vars ( [ ( "MASTER_SHARE" , Some ( & master_share_encoded) ) ] , || {
653+ assert ! ( MasterKeys :: load( & options_rotation) . is_ok( ) ) ;
654+ } ) ;
655+ }
0 commit comments