diff --git a/crates/ibc/src/client_state.rs b/crates/ibc/src/client_state.rs index 77b7175..c564512 100644 --- a/crates/ibc/src/client_state.rs +++ b/crates/ibc/src/client_state.rs @@ -312,7 +312,10 @@ impl Ics2ClientState for ClientState Result { + let cc = self.build_context(ctx); let header = Header::::try_from(header)?; + header.validate(&cc)?; + let trusted_sync_committee = header.trusted_sync_committee; let consensus_state = match maybe_consensus_state( ctx, @@ -338,7 +341,6 @@ impl Ics2ClientState for ClientState( RawHeader::decode(buf).map_err(Error::Decode)?.try_into() } +impl Header { + pub fn validate(&self, ctx: &C) -> Result<(), Error> { + self.trusted_sync_committee.sync_committee.validate()?; + let header_timestamp = self.timestamp.into_tm_time().unwrap().unix_timestamp() as u64; + let timestamp = + compute_timestamp_at_slot(ctx, self.consensus_update.finalized_beacon_header().slot); + if header_timestamp != timestamp.0 { + return Err(Error::UnexpectedTimestamp( + timestamp.into(), + header_timestamp, + )); + } + Ok(()) + } +} + impl Ics02Header for Header { fn height(&self) -> ibc::Height { ibc::Height::new(0, self.execution_update.block_number.into()).unwrap() @@ -220,8 +239,14 @@ mod tests { consensus_update: update.clone(), execution_update: ExecutionUpdateInfo::default(), account_update: AccountUpdateInfo::default(), - timestamp: Timestamp::from_nanoseconds(1730729158 * 1_000_000_000).unwrap(), + timestamp: Timestamp::from_nanoseconds( + compute_timestamp_at_slot(&ctx, update.finalized_beacon_header().slot).0 + * 1_000_000_000, + ) + .unwrap(), }; + let res = header.validate(&ctx); + assert!(res.is_ok(), "header validation failed: {:?}", res); let any = IBCAny::from(header.clone()); let decoded = Header::<32>::try_from(any).unwrap(); assert_eq!(header, decoded); diff --git a/crates/ibc/src/update.rs b/crates/ibc/src/update.rs index 9c8a064..4608978 100644 --- a/crates/ibc/src/update.rs +++ b/crates/ibc/src/update.rs @@ -3,8 +3,7 @@ use crate::{ types::ConsensusUpdateInfo, }; use ethereum_consensus::{ - beacon::Slot, - compute::{compute_sync_committee_period_at_slot, compute_timestamp_at_slot}, + compute::compute_sync_committee_period_at_slot, context::ChainContext, types::{H256, U64}, }; @@ -23,20 +22,11 @@ pub fn apply_updates( consensus_update: ConsensusUpdateInfo, block_number: U64, account_storage_root: H256, - timestamp: Timestamp, + header_timestamp: Timestamp, ) -> Result<(ClientState, ConsensusState), Error> { let store_period = consensus_state.current_period(ctx); let update_finalized_slot = consensus_update.finalized_header.0.slot; let update_finalized_period = compute_sync_committee_period_at_slot(ctx, update_finalized_slot); - let timestamp = timestamp.into_tm_time().unwrap().unix_timestamp() as u64; - let finalized_header_timestamp: u64 = - compute_timestamp_at_slot(ctx, update_finalized_slot).into(); - if finalized_header_timestamp != timestamp { - return Err(Error::UnexpectedTimestamp( - finalized_header_timestamp, - timestamp, - )); - } // Let `store_period` be the period of the current sync committe of the consensus state, then the state transition is the following: // - If `store_period == update_finalized_period`, then the new consensus state will have the same sync committee info as the current consensus state. @@ -47,7 +37,7 @@ pub fn apply_updates( ConsensusState { slot: update_finalized_slot, storage_root: account_storage_root.0.to_vec().into(), - timestamp: wrap_compute_timestamp_at_slot(ctx, update_finalized_slot)?, + timestamp: header_timestamp, current_sync_committee: consensus_state.current_sync_committee.clone(), next_sync_committee: consensus_state.next_sync_committee.clone(), } @@ -59,7 +49,7 @@ pub fn apply_updates( ConsensusState { slot: update_finalized_slot, storage_root: account_storage_root.0.to_vec().into(), - timestamp: wrap_compute_timestamp_at_slot(ctx, update_finalized_slot)?, + timestamp: header_timestamp, current_sync_committee: consensus_state.next_sync_committee.clone(), next_sync_committee: update_next_sync_committee.aggregate_pubkey, } @@ -87,13 +77,3 @@ pub fn apply_updates( new_consensus_state.validate()?; Ok((new_client_state, new_consensus_state)) } - -fn wrap_compute_timestamp_at_slot( - ctx: &C, - slot: Slot, -) -> Result { - // NOTE: The return value of `compute_timestamp_at_slot`'s unit is seconds, - // so we need to convert it to nanoseconds. - let timestamp = compute_timestamp_at_slot(ctx, slot); - Ok(Timestamp::from_nanoseconds(timestamp.0 * 1_000_000_000)?) -}