Skip to content

Commit 03c294d

Browse files
committed
add validation and tracking of TCB evaluation data number to lcp client
Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
1 parent 8b5a9d4 commit 03c294d

File tree

6 files changed

+182
-52
lines changed

6 files changed

+182
-52
lines changed

modules/lcp-client/src/client_def.rs

Lines changed: 128 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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,

modules/lcp-client/src/client_state.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ pub struct ClientState {
2929
pub operators_nonce: u64,
3030
pub operators_threshold_numerator: u64,
3131
pub operators_threshold_denominator: u64,
32-
pub latest_tcb_evaluation_data_number: u32,
33-
pub allow_previous_tcb_evaluation_data_number: bool,
32+
pub current_tcb_evaluation_data_number: u32,
33+
pub tcb_evaluation_data_number_update_grace_period: u32,
34+
pub next_tcb_evaluation_data_number: u32,
35+
pub next_tcb_evaluation_data_number_update_time: u64,
3436
pub zkdcap_verifier_infos: Vec<ZKDCAPVerifierInfo>,
3537
}
3638

@@ -147,9 +149,12 @@ impl From<ClientState> for RawClientState {
147149
operators_nonce: 0,
148150
operators_threshold_numerator: 0,
149151
operators_threshold_denominator: 0,
150-
latest_tcb_evalulation_data_number: value.latest_tcb_evaluation_data_number,
151-
allow_previous_tcb_evalulation_data_number: value
152-
.allow_previous_tcb_evaluation_data_number,
152+
current_tcb_evaluation_data_number: value.current_tcb_evaluation_data_number,
153+
tcb_evaluation_data_number_update_grace_period: value
154+
.tcb_evaluation_data_number_update_grace_period,
155+
next_tcb_evaluation_data_number: value.next_tcb_evaluation_data_number,
156+
next_tcb_evaluation_data_number_update_time: value
157+
.next_tcb_evaluation_data_number_update_time,
153158
zkdcap_verifier_infos: value
154159
.zkdcap_verifier_infos
155160
.iter()
@@ -179,9 +184,12 @@ impl TryFrom<RawClientState> for ClientState {
179184
operators_nonce: raw.operators_nonce,
180185
operators_threshold_numerator: raw.operators_threshold_numerator,
181186
operators_threshold_denominator: raw.operators_threshold_denominator,
182-
latest_tcb_evaluation_data_number: raw.latest_tcb_evalulation_data_number,
183-
allow_previous_tcb_evaluation_data_number: raw
184-
.allow_previous_tcb_evalulation_data_number,
187+
current_tcb_evaluation_data_number: raw.current_tcb_evaluation_data_number,
188+
tcb_evaluation_data_number_update_grace_period: raw
189+
.tcb_evaluation_data_number_update_grace_period,
190+
next_tcb_evaluation_data_number: raw.next_tcb_evaluation_data_number,
191+
next_tcb_evaluation_data_number_update_time: raw
192+
.next_tcb_evaluation_data_number_update_time,
185193
zkdcap_verifier_infos: raw
186194
.zkdcap_verifier_infos
187195
.into_iter()

modules/lcp-client/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ define_error! {
6161
"Invalid Risc0 proof format"
6262
},
6363

64+
UnexpectedTcbEvaluationDataNumber {
65+
number: u32
66+
}
67+
|e| {
68+
format_args!("Unexpected TCB evaluation data number: number={}", e.number)
69+
},
70+
6471
AttestationReport
6572
[attestation_report::Error]
6673
|_| { "Attestation report error" },

proto/definitions/ibc/lightclients/lcp/v1/lcp.proto

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,20 @@ message ClientState {
8282
uint64 operators_threshold_numerator = 9;
8383
// The denominator of the signature threshold for operator updates.
8484
uint64 operators_threshold_denominator = 10;
85-
// The latest TCB evaluation data number
85+
// The current TCB evaluation data number
8686
//
87-
// The client updates this when receiving TCB evaluation data whose number is greater than the current number.
88-
uint32 latest_tcb_evalulation_data_number = 11;
89-
// Indicates whether to allow the previous TCB evaluation data number.
87+
// The client only accepts the zkDCAP output generated using collateral with a TCB evaluation data number equal to or greater than this number.
88+
uint32 current_tcb_evaluation_data_number = 11;
89+
// The grace period for updating to the latest TCB evaluation data number (in seconds)
9090
//
91-
// If this is true, the client will accept the previous TCB evaluation data number (i.e., `latest_tcb_evalulation_data_number` - 1).
92-
// Otherwise, the client will only accept the latest TCB evaluation data number or greater.
93-
bool allow_previous_tcb_evalulation_data_number = 12;
91+
// Note that this grace period is not affected for updates to non-latest numbers.
92+
uint32 tcb_evaluation_data_number_update_grace_period = 12;
93+
// The next TCB evaluation data number to be updated
94+
//
95+
// If this number is non-zero, `next_tcb_evaluation_data_number` must be greater than `current_tcb_evaluation_data_number`.
96+
uint32 next_tcb_evaluation_data_number = 13;
97+
// The update time of the next TCB evaluation data number (in UNIX time seconds)
98+
uint64 next_tcb_evaluation_data_number_update_time = 14;
9499
// The verifier information for the zkDCAP
95100
//
96101
// The format is as follows:
@@ -101,7 +106,7 @@ message ClientState {
101106
// | 0 | 1 - 31 | 32 - 64 |
102107
// |---|----------|-----------|
103108
// | 1 | reserved | image id |
104-
repeated bytes zkdcap_verifier_infos = 14;
109+
repeated bytes zkdcap_verifier_infos = 15;
105110
}
106111

107112
message ConsensusState {
@@ -110,6 +115,6 @@ message ConsensusState {
110115
// Please check the state ID details: <https://docs.lcp.network/protocol/elc#state-id>
111116
bytes state_id = 1;
112117
// The timestamp of the target chain's block corresponding to the consensus height,
113-
// expressed in Unix time (seconds).
118+
// expressed in UNIX time (seconds).
114119
uint64 timestamp = 2;
115120
}

proto/src/descriptor.bin

447 Bytes
Binary file not shown.

proto/src/prost/ibc.lightclients.lcp.v1.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,24 @@ pub struct ClientState {
106106
/// The denominator of the signature threshold for operator updates.
107107
#[prost(uint64, tag = "10")]
108108
pub operators_threshold_denominator: u64,
109-
/// The latest TCB evaluation data number
109+
/// The current TCB evaluation data number
110110
///
111-
/// The client updates this when receiving TCB evaluation data whose number is greater than the current number.
111+
/// The client only accepts the zkDCAP output generated using collateral with a TCB evaluation data number equal to or greater than this number.
112112
#[prost(uint32, tag = "11")]
113-
pub latest_tcb_evalulation_data_number: u32,
114-
/// Indicates whether to allow the previous TCB evaluation data number.
113+
pub current_tcb_evaluation_data_number: u32,
114+
/// The grace period for updating to the latest TCB evaluation data number (in seconds)
115115
///
116-
/// If this is true, the client will accept the previous TCB evaluation data number (i.e., `latest_tcb_evalulation_data_number` - 1).
117-
/// Otherwise, the client will only accept the latest TCB evaluation data number or greater.
118-
#[prost(bool, tag = "12")]
119-
pub allow_previous_tcb_evalulation_data_number: bool,
116+
/// Note that this grace period is not affected for updates to non-latest numbers.
117+
#[prost(uint32, tag = "12")]
118+
pub tcb_evaluation_data_number_update_grace_period: u32,
119+
/// The next TCB evaluation data number to be updated
120+
///
121+
/// If this number is non-zero, `next_tcb_evaluation_data_number` must be greater than `current_tcb_evaluation_data_number`.
122+
#[prost(uint32, tag = "13")]
123+
pub next_tcb_evaluation_data_number: u32,
124+
/// The update time of the next TCB evaluation data number (in UNIX time seconds)
125+
#[prost(uint64, tag = "14")]
126+
pub next_tcb_evaluation_data_number_update_time: u64,
120127
/// The verifier information for the zkDCAP
121128
///
122129
/// The format is as follows:
@@ -127,7 +134,7 @@ pub struct ClientState {
127134
/// | 0 | 1 - 31 | 32 - 64 |
128135
/// |---|----------|-----------|
129136
/// | 1 | reserved | image id |
130-
#[prost(bytes = "vec", repeated, tag = "14")]
137+
#[prost(bytes = "vec", repeated, tag = "15")]
131138
pub zkdcap_verifier_infos: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
132139
}
133140
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -139,7 +146,7 @@ pub struct ConsensusState {
139146
#[prost(bytes = "vec", tag = "1")]
140147
pub state_id: ::prost::alloc::vec::Vec<u8>,
141148
/// The timestamp of the target chain's block corresponding to the consensus height,
142-
/// expressed in Unix time (seconds).
149+
/// expressed in UNIX time (seconds).
143150
#[prost(uint64, tag = "2")]
144151
pub timestamp: u64,
145152
}

0 commit comments

Comments
 (0)