@@ -105,6 +105,16 @@ impl LCPClient {
105105 client_state. zkdcap_verifier_infos[ 0 ] . as_type( ) == ZKVMType :: Risc0 ,
106106 "only Risc0 is supported"
107107 ) ;
108+ assert ! (
109+ client_state. current_tcb_evaluation_data_number != 0 ,
110+ "current_tcb_evaluation_data_number must not be 0"
111+ ) ;
112+ assert_eq ! (
113+ client_state. next_tcb_evaluation_data_number == 0 ,
114+ client_state. next_tcb_evaluation_data_number_update_time == 0 ,
115+ "next_tcb_evaluation_data_number and next_tcb_evaluation_data_number_update_time must be both 0 or both non-zero"
116+ ) ;
117+ assert ! ( client_state. next_tcb_evaluation_data_number == 0 || client_state. next_tcb_evaluation_data_number > client_state. current_tcb_evaluation_data_number, "next_tcb_evaluation_data_number must be greater than current_tcb_evaluation_data_number" ) ;
108118 }
109119
110120 // An initial consensus state must be empty
@@ -273,28 +283,11 @@ impl LCPClient {
273283 remote_attestation:: dcap:: INTEL_ROOT_CA_HASH ,
274284 ) ;
275285
276- #[ allow( clippy:: comparison_chain) ]
277- #[ allow( clippy:: assertions_on_constants) ]
278- let new_client_state = if client_state. latest_tcb_evaluation_data_number
279- < output. min_tcb_evaluation_data_number
280- {
281- Some ( ClientState {
282- latest_tcb_evaluation_data_number : output. min_tcb_evaluation_data_number ,
283- ..client_state. clone ( )
284- } )
285- } else if client_state. latest_tcb_evaluation_data_number
286- > output. min_tcb_evaluation_data_number
287- {
288- if !client_state. allow_previous_tcb_evaluation_data_number
289- || client_state. latest_tcb_evaluation_data_number
290- != output. min_tcb_evaluation_data_number + 1
291- {
292- assert ! ( false , "unexpected tcb evaluation data number" ) ;
293- }
294- None
295- } else {
296- None
297- } ;
286+ self . check_and_update_tcb_evaluation_data_number (
287+ ctx,
288+ client_id. clone ( ) ,
289+ output. min_tcb_evaluation_data_number ,
290+ ) ?;
298291
299292 assert ! (
300293 output
@@ -338,12 +331,122 @@ impl LCPClient {
338331 report_data. enclave_key ( ) ,
339332 EKOperatorInfo :: new ( output. validity . not_after_min , operator) ,
340333 ) ;
341- if let Some ( new_client_state) = new_client_state {
342- ctx. store_any_client_state ( client_id, new_client_state. into ( ) ) ?;
343- }
344334 Ok ( ( ) )
345335 }
346336
337+ /// check_and_update_tcb_evaluation_data_number checks if the current or next TCB evaluation data number update is required.
338+ #[ allow( clippy:: comparison_chain) ]
339+ fn check_and_update_tcb_evaluation_data_number (
340+ & self ,
341+ ctx : & mut dyn HostClientKeeper ,
342+ client_id : ClientId ,
343+ output_tcb_evaluation_data_number : u32 ,
344+ ) -> Result < ( bool , bool ) , Error > {
345+ let client_state = ClientState :: try_from ( ctx. client_state ( & client_id) ?) ?;
346+ let mut current_updated = false ;
347+
348+ // check if the current or next TCB evaluation data number update is required
349+ if client_state. next_tcb_evaluation_data_number != 0
350+ && ctx. host_timestamp ( ) . as_unix_timestamp_secs ( )
351+ >= client_state. next_tcb_evaluation_data_number_update_time
352+ {
353+ let mut new_client_state = client_state. clone ( ) ;
354+ new_client_state. current_tcb_evaluation_data_number =
355+ client_state. next_tcb_evaluation_data_number ;
356+ new_client_state. next_tcb_evaluation_data_number = 0 ;
357+ new_client_state. next_tcb_evaluation_data_number_update_time = 0 ;
358+ current_updated = true ;
359+ // NOTE:
360+ // - If the current number is updated again in a subsequent process, only one event is emitted
361+ // - A new next TCB evaluation data number is not set, so the `next_updated` is false here
362+ ctx. store_any_client_state ( client_id. clone ( ) , new_client_state. into ( ) ) ?;
363+ }
364+
365+ if output_tcb_evaluation_data_number > client_state. current_tcb_evaluation_data_number {
366+ if client_state. tcb_evaluation_data_number_update_grace_period == 0 {
367+ // If the grace period is zero, the client immediately updates the current TCB evaluation data number
368+ let mut new_client_state = client_state. clone ( ) ;
369+ new_client_state. current_tcb_evaluation_data_number =
370+ output_tcb_evaluation_data_number;
371+ // If the grace period is zero, the `next_tcb_evaluation_data_number` and `next_tcb_evaluation_data_number_update_time` must always be zero
372+ // Otherwise, there is an internal error in the client
373+ assert ! (
374+ new_client_state. next_tcb_evaluation_data_number == 0
375+ && new_client_state. next_tcb_evaluation_data_number_update_time == 0
376+ ) ;
377+ ctx. store_any_client_state ( client_id, new_client_state. into ( ) ) ?;
378+ Ok ( ( true , false ) )
379+ } else {
380+ // If the grace period is not zero, there may be a next TCB evaluation data number update in the client state
381+
382+ let next_update_time = ctx. host_timestamp ( ) . as_unix_timestamp_secs ( )
383+ + client_state. tcb_evaluation_data_number_update_grace_period as u64 ;
384+
385+ // If the next TCB evaluation data number is not set, the client sets the next TCB evaluation data number to the output's TCB evaluation data number
386+ if client_state. next_tcb_evaluation_data_number == 0 {
387+ let mut new_client_state = client_state. clone ( ) ;
388+ new_client_state. next_tcb_evaluation_data_number =
389+ output_tcb_evaluation_data_number;
390+ new_client_state. next_tcb_evaluation_data_number_update_time = next_update_time;
391+ ctx. store_any_client_state ( client_id, new_client_state. into ( ) ) ?;
392+ return Ok ( ( current_updated, true ) ) ;
393+ }
394+
395+ // If the next TCB evaluation data number is set, the client updates the next TCB evaluation data number
396+
397+ if output_tcb_evaluation_data_number > client_state. next_tcb_evaluation_data_number
398+ {
399+ // Edge case 1. clientState.current_tcb_evaluation_data_number < clientState.next_tcb_evaluation_data_number < outputTcbEvaluationDataNumber
400+ //
401+ // In this case, the client immediately updates the current TCB evaluation data number with the `clientState.next_tcb_evaluation_data_number`
402+ // and updates the next TCB evaluation data number with the `outputTcbEvaluationDataNumber`
403+ //
404+ // This case can be caused by too long grace period values or multiple TCB Recovery Events with very short intervals.
405+ // Note that in this case the current number is updated ignoring the grace period setting.
406+ // However, the current number is still a non-latest number, so there should be no problem for the operator operating as expected.
407+ let mut new_client_state = client_state. clone ( ) ;
408+ new_client_state. current_tcb_evaluation_data_number =
409+ client_state. next_tcb_evaluation_data_number ;
410+ new_client_state. next_tcb_evaluation_data_number =
411+ output_tcb_evaluation_data_number;
412+ new_client_state. next_tcb_evaluation_data_number_update_time = next_update_time;
413+ ctx. store_any_client_state ( client_id, new_client_state. into ( ) ) ?;
414+ Ok ( ( true , true ) )
415+ } else if output_tcb_evaluation_data_number
416+ < client_state. next_tcb_evaluation_data_number
417+ {
418+ // Edge case 2. clientState.current_tcb_evaluation_data_number < outputTcbEvaluationDataNumber < clientState.next_tcb_evaluation_data_number
419+ //
420+ // In this case, the client immediately updates the current TCB evaluation data number with the `outputTcbEvaluationDataNumber`
421+ // and does not update the next TCB evaluation data number.
422+ //
423+ // This case can be caused by too long grace period values or multiple TCB Recovery Events with very short intervals.
424+ // Note that in this case the current number is updated ignoring the grace period setting.
425+ // However, the current number is still a non-latest number, so there should be no problem for the operator operating as expected.
426+ let mut new_client_state = client_state. clone ( ) ;
427+ new_client_state. current_tcb_evaluation_data_number =
428+ output_tcb_evaluation_data_number;
429+ ctx. store_any_client_state ( client_id, new_client_state. into ( ) ) ?;
430+ Ok ( ( true , false ) )
431+ } else {
432+ // General case. outputTcbEvaluationDataNumber == clientState.next_tcb_evaluation_data_number
433+ // In this case, the client already has the next TCB evaluation data number, so it does not need to be updated
434+ Ok ( ( current_updated, false ) )
435+ }
436+ }
437+ } else if output_tcb_evaluation_data_number
438+ < client_state. current_tcb_evaluation_data_number
439+ {
440+ // The client must revert if the output's TCB evaluation data number is less than the current TCB evaluation data number
441+ Err ( Error :: unexpected_tcb_evaluation_data_number (
442+ client_state. current_tcb_evaluation_data_number ,
443+ ) )
444+ } else {
445+ // nop: case outputTcbEvaluationDataNumber == clientState.current_tcb_evaluation_data_number
446+ Ok ( ( current_updated, false ) )
447+ }
448+ }
449+
347450 fn update_operators (
348451 & self ,
349452 ctx : & mut dyn HostClientKeeper ,
0 commit comments