@@ -13,6 +13,7 @@ use ic_canister_sig_creation::signature_map::LABEL_SIG;
1313use ic_cdk:: api:: { caller, set_certified_data, trap} ;
1414use ic_cdk:: call;
1515use ic_cdk_macros:: { init, post_upgrade, pre_upgrade, query, update} ;
16+ use ic_cdk_timers:: TimerId ;
1617use internet_identity_interface:: archive:: types:: { BufferedEntry , Operation } ;
1718use internet_identity_interface:: http_gateway:: { HttpRequest , HttpResponse } ;
1819use internet_identity_interface:: internet_identity:: types:: openid:: {
@@ -25,7 +26,9 @@ use internet_identity_interface::internet_identity::types::vc_mvp::{
2526} ;
2627use internet_identity_interface:: internet_identity:: types:: * ;
2728use serde_bytes:: ByteBuf ;
29+ use std:: cell:: RefCell ;
2830use std:: collections:: HashMap ;
31+ use std:: time:: Duration ;
2932use storage:: account:: { AccountDelegationError , PrepareAccountDelegation } ;
3033use storage:: { Salt , Storage } ;
3134
@@ -34,6 +37,7 @@ mod anchor_management;
3437mod archive;
3538mod assets;
3639mod authz_utils;
40+ mod migrations;
3741
3842/// Type conversions between internal and external types.
3943mod conversions;
@@ -62,6 +66,51 @@ const INTERNETCOMPUTER_ORG_ORIGIN: &str = "https://identity.internetcomputer.org
6266const ID_AI_DOMAIN : & str = "id.ai" ;
6367const ID_AI_ORIGIN : & str = "https://id.ai" ;
6468
69+ /// Number of anchors to process in one batch during the recovery phrase migration.
70+ pub ( crate ) const RECOVERY_PHRASE_MIGRATION_BATCH_SIZE : u64 = 2000 ;
71+
72+ /// Batch dispatch frequency to minimize the chance of DoS.
73+ pub ( crate ) const RECOVERY_PHRASE_MIGRATION_BATCH_BACKOFF_SECONDS : Duration = Duration :: from_secs ( 1 ) ;
74+
75+ thread_local ! {
76+ // TODO: Remove this state after the data migration is complete.
77+ pub ( crate ) static RECOVERY_PHRASE_MIGRATION_BATCH_ID : RefCell <u64 > = const { RefCell :: new( 0 ) } ;
78+ pub ( crate ) static RECOVERY_PHRASE_MIGRATION_ERRORS : RefCell <Vec <String >> = const { RefCell :: new( Vec :: new( ) ) } ;
79+ pub ( crate ) static RECOVERY_PHRASE_MIGRATION_LAST_ANCHOR_ID : RefCell <Option <u64 >> = const { RefCell :: new( None ) } ;
80+
81+ static TIMER_ID : RefCell <Option <TimerId >> = const { RefCell :: new( None ) } ;
82+ }
83+
84+ /// Temporary function to list migration errors.
85+ ///
86+ /// Can be called to retrieve any errors that occurred during the recovery phrase migration.
87+ #[ update( hidden = true ) ]
88+ fn list_recovery_phrase_migration_errors ( ) -> Vec < String > {
89+ RECOVERY_PHRASE_MIGRATION_ERRORS . with_borrow ( |errors| errors. clone ( ) )
90+ }
91+
92+ /// Temporary function to fetch the current migration batch id.
93+ ///
94+ /// Can be called to retrieve the current batch id of the ongoing data recovery phrase migration.
95+ ///
96+ /// The special value `u64::MAX` indicates that the migration is complete.
97+ #[ query( hidden = true ) ]
98+ fn list_recovery_phrase_migration_current_batch_id ( ) -> u64 {
99+ RECOVERY_PHRASE_MIGRATION_BATCH_ID . with_borrow ( |id| * id)
100+ }
101+
102+ /// Temporary function to count migrated recovery phrases.
103+ ///
104+ /// Can be called to retrieve the number of recovery phrases indexed so far.
105+ #[ query( hidden = true ) ]
106+ fn count_recovery_phrases ( ) -> u64 {
107+ state:: storage_borrow ( |storage| {
108+ storage
109+ . lookup_anchor_with_recovery_phrase_principal_memory
110+ . len ( )
111+ } )
112+ }
113+
65114#[ update]
66115async fn init_salt ( ) {
67116 state:: init_salt ( ) . await ;
@@ -576,13 +625,46 @@ fn init(maybe_arg: Option<InternetIdentityInit>) {
576625 initialize ( maybe_arg) ;
577626}
578627
628+ async fn run_periodic_tasks ( ) {
629+ state:: storage_borrow_mut ( |storage| {
630+ storage. sync_anchor_indices ( RECOVERY_PHRASE_MIGRATION_BATCH_SIZE ) ;
631+ } ) ;
632+
633+ if RECOVERY_PHRASE_MIGRATION_BATCH_ID . with ( |id| * id. borrow ( ) ) == u64:: MAX {
634+ // Migration complete, clear timer.
635+ TIMER_ID . with_borrow_mut ( |saved_timer_id| {
636+ if let Some ( saved_timer_id) = * saved_timer_id {
637+ ic_cdk_timers:: clear_timer ( saved_timer_id) ;
638+ }
639+ * saved_timer_id = None ;
640+ } ) ;
641+ }
642+ }
643+
644+ fn init_timers ( ) {
645+ let new_timer_id =
646+ ic_cdk_timers:: set_timer_interval ( RECOVERY_PHRASE_MIGRATION_BATCH_BACKOFF_SECONDS , || {
647+ ic_cdk:: spawn ( run_periodic_tasks ( ) )
648+ } ) ;
649+
650+ TIMER_ID . with_borrow_mut ( |saved_timer_id| {
651+ if let Some ( saved_timer_id) = * saved_timer_id {
652+ ic_cdk_timers:: clear_timer ( saved_timer_id) ;
653+ }
654+ saved_timer_id. replace ( new_timer_id) ;
655+ } ) ;
656+ }
657+
579658#[ post_upgrade]
580659fn post_upgrade ( maybe_arg : Option < InternetIdentityInit > ) {
581660 state:: init_from_stable_memory ( ) ;
582661 // load the persistent state after initializing storage as it manages the respective stable cell
583662 state:: load_persistent_state ( ) ;
584663
585664 initialize ( maybe_arg) ;
665+
666+ // TODO: Remove the data migration.
667+ init_timers ( ) ;
586668}
587669
588670fn initialize ( maybe_arg : Option < InternetIdentityInit > ) {
0 commit comments