@@ -252,29 +252,20 @@ impl Whitenoise {
252252 ///
253253 /// Returns a [`WhitenoiseError`] if any step fails. The operation is atomic with cleanup on failure.
254254 pub async fn create_identity ( & self ) -> Result < Account > {
255- // Step 1: Generate a new keypair
256255 let keys = Keys :: generate ( ) ;
257256 tracing:: debug!( target: "whitenoise::create_identity" , "Generated new keypair: {}" , keys. public_key( ) . to_hex( ) ) ;
258257
259- // Step 2: Setup the account (this handles all the common logic)
260- let account = self . setup_account ( & keys , true ) . await ? ;
258+ let mut account = self . create_base_account_with_private_key ( & keys ) ? ;
259+ tracing :: debug! ( target : "whitenoise::create_identity" , "Keys stored in secret store" ) ;
261260
262- // Step 3: For new accounts only, create and publish metadata with petname
263- let petname = petname:: petname ( 2 , " " )
264- . unwrap_or_else ( || "Anonymous User" . to_string ( ) )
265- . split_whitespace ( )
266- . map ( Whitenoise :: capitalize_first_letter)
267- . collect :: < Vec < _ > > ( )
268- . join ( " " ) ;
261+ self . setup_relays_for_new_account ( & mut account) . await ?;
262+ tracing:: debug!( target: "whitenoise::create_identity" , "Relays setup" ) ;
269263
270- let metadata = Metadata {
271- name : Some ( petname. clone ( ) ) ,
272- display_name : Some ( petname) ,
273- ..Default :: default ( )
274- } ;
264+ self . persist_and_activate_account ( & account) . await ?;
265+ tracing:: debug!( target: "whitenoise::create_identity" , "Account persisted and activated" ) ;
275266
276- self . update_metadata ( & metadata , & account) . await ?;
277- tracing:: debug!( target: "whitenoise::create_identity" , "Created and published metadata with petname: {}" , metadata . name . as_ref ( ) . unwrap_or ( & "Unknown" . to_string ( ) ) ) ;
267+ self . setup_metadata ( & account) . await ?;
268+ tracing:: debug!( target: "whitenoise::create_identity" , "Metadata setup" ) ;
278269
279270 tracing:: debug!( target: "whitenoise::create_identity" , "Successfully created new identity: {}" , account. pubkey. to_hex( ) ) ;
280271 Ok ( account)
@@ -302,7 +293,17 @@ impl Whitenoise {
302293 let pubkey = keys. public_key ( ) ;
303294 tracing:: debug!( target: "whitenoise::login" , "Logging in with pubkey: {}" , pubkey. to_hex( ) ) ;
304295
305- let account = self . setup_account ( & keys, false ) . await ?;
296+ let mut account = self . create_base_account_with_private_key ( & keys) ?;
297+ tracing:: debug!( target: "whitenoise::login" , "Keys stored in secret store" ) ;
298+
299+ self . setup_relays_for_existing_account ( & mut account) . await ?;
300+ tracing:: debug!( target: "whitenoise::login" , "Relays setup" ) ;
301+
302+ self . persist_and_activate_account ( & account) . await ?;
303+ tracing:: debug!( target: "whitenoise::login" , "Account persisted and activated" ) ;
304+
305+ self . background_fetch_account_data ( & account) . await ?;
306+ tracing:: debug!( target: "whitenoise::login" , "Background data fetch triggered" ) ;
306307
307308 tracing:: debug!( target: "whitenoise::login" , "Successfully logged in: {}" , account. pubkey. to_hex( ) ) ;
308309 Ok ( account)
@@ -565,164 +566,152 @@ impl Whitenoise {
565566 } )
566567 }
567568
568- /// Sets up an account for use in Whitenoise (shared logic for both new and existing accounts).
569- ///
570- /// This method handles the common setup logic for both new accounts (created via create_identity)
571- /// and existing accounts (loaded via login). The process is atomic with automatic cleanup on failure.
572- ///
573- /// The operation follows this sequence:
574- /// 1. **Store private key** - Ensures the private key is saved to the system keychain/secret store
575- /// 2. **Handle relay lists** - For existing accounts, fetches from network; for new accounts or missing lists, uses defaults
576- /// 3. **Create/update account struct** - Builds the account with proper relay configuration
577- /// 4. **Save account to database** - Persists the account record
578- /// 5. **Publish relay lists** - Only publishes if we had to create default relay lists
579- /// 6. **Setup account in memory** - Adds to in-memory accounts list and connects to relays
580- /// 7. **Setup subscriptions** - Configures Nostr subscriptions for the account
581- /// 8. **Handle key package** - Publishes a key package if none exists
582- ///
583- /// # Arguments
584- ///
585- /// * `keys` - The Nostr keypair for the account
586- /// * `is_new_account` - Whether this is a newly created account (vs an existing one being loaded)
587- ///
588- /// # Returns
589- ///
590- /// Returns the fully configured `Account` ready for use.
591- ///
592- /// # Errors
593- ///
594- /// Returns a `WhitenoiseError` if any critical operation fails. On failure, partial state is cleaned up.
595- /// TODO: Refactor this method to clean up on error and return a proper error state.
596- async fn setup_account ( & self , keys : & Keys , is_new_account : bool ) -> Result < Account > {
597- let pubkey = keys. public_key ( ) ;
598- tracing:: debug!( target: "whitenoise::setup_account" , "Setting up account for pubkey: {} (new: {})" , pubkey. to_hex( ) , is_new_account) ;
599-
600- // Step 1: Store private key first
601- self . secrets_store . store_private_key ( keys) . map_err ( |e| {
602- tracing:: error!( target: "whitenoise::setup_account" , "Failed to store private key: {}" , e) ;
603- e
604- } ) ?;
605- tracing:: debug!( target: "whitenoise::setup_account" , "Keys stored in secret store" ) ;
606-
607- // Step 2: Handle relay lists - fetch for existing accounts, use defaults for new or missing
608- // Track which relay types need to be published (defaulted to fallback values)
609- let mut need_to_publish_nip65 = is_new_account; // Always publish for new accounts
610- let mut need_to_publish_inbox = is_new_account;
611- let mut need_to_publish_key_package = is_new_account;
612-
613- let nip65_relays = if is_new_account {
614- Account :: default_relays ( )
615- } else {
616- match self
617- . fetch_relays_from ( Account :: default_relays ( ) , pubkey, RelayType :: Nostr )
618- . await
619- {
620- Ok ( relays) if !relays. is_empty ( ) => relays,
621- _ => {
622- need_to_publish_nip65 = true ;
623- Account :: default_relays ( )
624- }
625- }
626- } ;
569+ async fn persist_and_activate_account ( & self , account : & Account ) -> Result < ( ) > {
570+ self . persist_account ( account) . await ?;
571+ tracing:: debug!( target: "whitenoise::persist_and_activate_account" , "Account saved to database" ) ;
572+ self . connect_account_relays ( account) . await ?;
573+ tracing:: debug!( target: "whitenoise::persist_and_activate_account" , "Relays connected" ) ;
574+ self . setup_subscriptions ( account) . await ?;
575+ tracing:: debug!( target: "whitenoise::persist_and_activate_account" , "Subscriptions setup" ) ;
576+ self . setup_key_package ( account) . await ?;
577+ tracing:: debug!( target: "whitenoise::persist_and_activate_account" , "Key package setup" ) ;
578+ Ok ( ( ) )
579+ }
627580
628- let inbox_relays = if is_new_account {
629- Account :: default_relays ( )
630- } else {
631- match self
632- . fetch_relays_from ( nip65_relays. clone ( ) , pubkey, RelayType :: Inbox )
633- . await
634- {
635- Ok ( relays) if !relays. is_empty ( ) => relays,
636- _ => {
637- need_to_publish_inbox = true ;
638- Account :: default_relays ( )
639- }
640- }
641- } ;
581+ async fn setup_metadata ( & self , account : & Account ) -> Result < ( ) > {
582+ let petname = petname:: petname ( 2 , " " )
583+ . unwrap_or_else ( || "Anonymous User" . to_string ( ) )
584+ . split_whitespace ( )
585+ . map ( Whitenoise :: capitalize_first_letter)
586+ . collect :: < Vec < _ > > ( )
587+ . join ( " " ) ;
642588
643- let key_package_relays = if is_new_account {
644- Account :: default_relays ( )
645- } else {
646- match self
647- . fetch_relays_from ( nip65_relays. clone ( ) , pubkey, RelayType :: KeyPackage )
648- . await
649- {
650- Ok ( relays) if !relays. is_empty ( ) => relays,
651- _ => {
652- need_to_publish_key_package = true ;
653- Account :: default_relays ( )
654- }
655- }
589+ let metadata = Metadata {
590+ name : Some ( petname. clone ( ) ) ,
591+ display_name : Some ( petname) ,
592+ ..Default :: default ( )
656593 } ;
657594
658- // Step 3: Create account struct
659- let account = Account {
660- pubkey,
661- settings : AccountSettings :: default ( ) ,
662- last_synced : Timestamp :: zero ( ) ,
663- nip65_relays,
664- inbox_relays,
665- key_package_relays,
666- nostr_mls : Account :: create_nostr_mls ( pubkey, & self . config . data_dir ) ?,
667- } ;
595+ self . update_metadata ( & metadata, account) . await ?;
596+ tracing:: debug!( target: "whitenoise::setup_metadata" , "Created and published metadata with petname: {}" , metadata. name. as_ref( ) . unwrap_or( & "Unknown" . to_string( ) ) ) ;
597+ Ok ( ( ) )
598+ }
668599
669- // Step 4: Save account to database
670- self . save_account ( & account) . await . map_err ( |e| {
600+ async fn persist_account ( & self , account : & Account ) -> Result < ( ) > {
601+ self . save_account ( account) . await . map_err ( |e| {
671602 tracing:: error!( target: "whitenoise::setup_account" , "Failed to save account: {}" , e) ;
672603 // Try to clean up stored private key
673- if let Err ( cleanup_err) = self . secrets_store . remove_private_key_for_pubkey ( & pubkey) {
604+ if let Err ( cleanup_err) = self . secrets_store . remove_private_key_for_pubkey ( & account . pubkey ) {
674605 tracing:: error!( target: "whitenoise::setup_account" , "Failed to cleanup private key after account save failure: {}" , cleanup_err) ;
675606 }
676607 e
677608 } ) ?;
678609 tracing:: debug!( target: "whitenoise::setup_account" , "Account saved to database" ) ;
679610
680- // Step 5: Publish relay lists if we created defaults (only publish what defaulted)
681- if need_to_publish_nip65 {
682- self . publish_relay_list_for_account ( & account, RelayType :: Nostr , & None )
683- . await ?;
684- }
685- if need_to_publish_inbox {
686- self . publish_relay_list_for_account ( & account, RelayType :: Inbox , & None )
687- . await ?;
688- }
689- if need_to_publish_key_package {
690- self . publish_relay_list_for_account ( & account, RelayType :: KeyPackage , & None )
691- . await ?;
692- }
693- tracing:: debug!( target: "whitenoise::setup_account" , "Published relay lists for defaulted types" ) ;
694-
695- // Step 6: Setup account in memory and connect to relays
696- self . connect_account_relays ( & account) . await ?;
697611 {
698612 let mut accounts = self . write_accounts ( ) . await ;
699613 accounts. insert ( account. pubkey , account. clone ( ) ) ;
700614 }
701- tracing:: debug!( target: "whitenoise::setup_account" , "Account added to memory and relays connected" ) ;
702-
703- // Step 7: Setup subscriptions
704- self . setup_subscriptions ( & account) . await ?;
705- tracing:: debug!( target: "whitenoise::setup_account" , "Subscriptions setup" ) ;
615+ tracing:: debug!( target: "whitenoise::setup_account" , "Account added to memory" ) ;
616+ Ok ( ( ) )
617+ }
706618
707- // Step 8: Handle key package - publish if none exists
619+ async fn setup_key_package ( & self , account : & Account ) -> Result < ( ) > {
708620 let key_package_event = self
709- . fetch_key_package_event_from ( account. key_package_relays . clone ( ) , pubkey)
621+ . fetch_key_package_event_from ( account. key_package_relays . clone ( ) , account . pubkey )
710622 . await ?;
711623 if key_package_event. is_none ( ) {
712- self . publish_key_package_for_account ( & account) . await ?;
624+ self . publish_key_package_for_account ( account) . await ?;
713625 tracing:: debug!( target: "whitenoise::setup_account" , "Published key package" ) ;
714626 }
627+ Ok ( ( ) )
628+ }
715629
716- // For existing accounts, trigger background data fetch
717- if !is_new_account {
718- self . background_fetch_account_data ( & account) . await ?;
719- tracing:: debug!( target: "whitenoise::setup_account" , "Background data fetch triggered" ) ;
720- }
630+ fn create_base_account_with_private_key ( & self , keys : & Keys ) -> Result < Account > {
631+ let pubkey = keys. public_key ( ) ;
632+ let account = Account {
633+ pubkey,
634+ settings : AccountSettings :: default ( ) ,
635+ last_synced : Timestamp :: zero ( ) ,
636+ nip65_relays : DashSet :: new ( ) ,
637+ inbox_relays : DashSet :: new ( ) ,
638+ key_package_relays : DashSet :: new ( ) ,
639+ nostr_mls : Account :: create_nostr_mls ( pubkey, & self . config . data_dir ) ?,
640+ } ;
641+
642+ self . secrets_store . store_private_key ( keys) . map_err ( |e| {
643+ tracing:: error!( target: "whitenoise::setup_account" , "Failed to store private key: {}" , e) ;
644+ e
645+ } ) ?;
721646
722- tracing:: debug!( target: "whitenoise::setup_account" , "Account setup completed successfully" ) ;
723647 Ok ( account)
724648 }
725649
650+ async fn setup_relays_for_existing_account ( & self , account : & mut Account ) -> Result < ( ) > {
651+ let pubkey = account. pubkey ;
652+ account. nip65_relays = self
653+ . fetch_or_publish_default_relays ( pubkey, RelayType :: Nostr , Account :: default_relays ( ) )
654+ . await ?;
655+ account. inbox_relays = self
656+ . fetch_or_publish_default_relays ( pubkey, RelayType :: Inbox , account. nip65_relays . clone ( ) )
657+ . await ?;
658+ account. key_package_relays = self
659+ . fetch_or_publish_default_relays (
660+ pubkey,
661+ RelayType :: KeyPackage ,
662+ account. nip65_relays . clone ( ) ,
663+ )
664+ . await ?;
665+
666+ Ok ( ( ) )
667+ }
668+
669+ async fn fetch_or_publish_default_relays (
670+ & self ,
671+ pubkey : PublicKey ,
672+ relay_type : RelayType ,
673+ source_relays : DashSet < RelayUrl > ,
674+ ) -> Result < DashSet < RelayUrl > > {
675+ match self
676+ . fetch_relays_from ( source_relays. clone ( ) , pubkey, relay_type)
677+ . await
678+ {
679+ Ok ( relays) if !relays. is_empty ( ) => Ok ( relays) ,
680+ _ => {
681+ let default_relays = Account :: default_relays ( ) ;
682+ self . publish_relay_list_for_pubkey (
683+ pubkey,
684+ default_relays. clone ( ) ,
685+ relay_type,
686+ source_relays,
687+ )
688+ . await ?;
689+ Ok ( default_relays)
690+ }
691+ }
692+ }
693+
694+ async fn setup_relays_for_new_account ( & self , account : & mut Account ) -> Result < ( ) > {
695+ let default_relays = Account :: default_relays ( ) ;
696+
697+ // New accounts use default relays for all relay types
698+ for relay_type in [ RelayType :: Nostr , RelayType :: Inbox , RelayType :: KeyPackage ] {
699+ self . publish_relay_list_for_pubkey (
700+ account. pubkey ,
701+ default_relays. clone ( ) ,
702+ relay_type,
703+ default_relays. clone ( ) ,
704+ )
705+ . await ?;
706+ }
707+
708+ account. nip65_relays = default_relays. clone ( ) ;
709+ account. inbox_relays = default_relays. clone ( ) ;
710+ account. key_package_relays = default_relays;
711+
712+ Ok ( ( ) )
713+ }
714+
726715 /// Saves the provided `Account` to the database.
727716 ///
728717 /// This method inserts or updates the account record in the database, serializing all
0 commit comments