Skip to content

Commit 0bbf0ef

Browse files
authored
Merge pull request #28 from datachainlab/refactoring
Refactoring and add `verify_delay_passed` checks Signed-off-by: Jun Kimura <[email protected]>
2 parents 23cba6f + b7867e3 commit 0bbf0ef

File tree

5 files changed

+124
-47
lines changed

5 files changed

+124
-47
lines changed

crates/ibc/src/client_state.rs

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::commitment::{calculate_ibc_commitment_storage_key, decode_eip1184_rlp_proof};
1+
use crate::commitment::{calculate_ibc_commitment_storage_location, decode_eip1184_rlp_proof};
22
use crate::consensus_state::{ConsensusState, TrustedConsensusState};
33
use crate::errors::Error;
44
use crate::header::Header;
@@ -22,6 +22,7 @@ use ibc::core::ics02_client::client_state::{ClientState as Ics2ClientState, Upda
2222
use ibc::core::ics02_client::client_type::ClientType;
2323
use ibc::core::ics02_client::consensus_state::ConsensusState as Ics02ConsensusState;
2424
use ibc::core::ics02_client::error::ClientError;
25+
use ibc::core::ics03_connection::connection::ConnectionEnd;
2526
use ibc::core::ics24_host::identifier::{ChainId, ClientId};
2627
use ibc::core::ics24_host::path::ClientConsensusStatePath;
2728
use ibc::core::ics24_host::Path;
@@ -43,29 +44,42 @@ pub const ETHEREUM_ACCOUNT_STORAGE_ROOT_INDEX: usize = 2;
4344

4445
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
4546
pub struct ClientState<const SYNC_COMMITTEE_SIZE: usize> {
46-
/// Chain parameters
47+
// Verification parameters
48+
/// `genesis_validators_root` of the target beacon chain's BeaconState
4749
pub genesis_validators_root: Root,
50+
/// https://github.com/ethereum/consensus-specs/blob/a09d0c321550c5411557674a981e2b444a1178c0/specs/altair/light-client/sync-protocol.md#misc
4851
pub min_sync_committee_participants: U64,
52+
/// `genesis_time` of the target beacon chain's BeaconState
4953
pub genesis_time: U64,
54+
/// fork parameters of the target beacon chain
5055
pub fork_parameters: ForkParameters,
56+
/// https://github.com/ethereum/consensus-specs/blob/a09d0c321550c5411557674a981e2b444a1178c0/configs/mainnet.yaml#L69
5157
pub seconds_per_slot: U64,
58+
/// https://github.com/ethereum/consensus-specs/blob/a09d0c321550c5411557674a981e2b444a1178c0/presets/mainnet/phase0.yaml#L36
5259
pub slots_per_epoch: Slot,
60+
/// https://github.com/ethereum/consensus-specs/blob/a09d0c321550c5411557674a981e2b444a1178c0/presets/mainnet/altair.yaml#L18
5361
pub epochs_per_sync_committee_period: Epoch,
5462

55-
/// IBC Solidity parameters
63+
/// An address of IBC contract on execution layer
5664
pub ibc_address: Address,
65+
/// The IBC contract's base storage location for storing commitments
66+
/// https://github.com/hyperledger-labs/yui-ibc-solidity/blob/0e83dc7aadf71380dae6e346492e148685510663/docs/architecture.md#L46
5767
pub ibc_commitments_slot: H256,
5868

59-
/// Light Client parameters
69+
/// `trust_level` is threshold of sync committee participants to consider the attestation as valid. Highly recommended to be 2/3.
6070
pub trust_level: Fraction,
71+
/// `trusting_period` is the period in which the consensus state is considered trusted
6172
pub trusting_period: Duration,
73+
/// `max_clock_drift` defines how much new finalized header's time can drift into the future
6274
pub max_clock_drift: Duration,
6375

64-
/// State
76+
// State
77+
/// The latest block number of the stored consensus state
6578
pub latest_execution_block_number: U64,
79+
/// `frozen_height` is the height at which the client is considered frozen. If `None`, the client is unfrozen.
6680
pub frozen_height: Option<Height>,
6781

68-
/// Verifier
82+
// Verifiers
6983
#[serde(skip)]
7084
pub consensus_verifier:
7185
SyncProtocolVerifier<SYNC_COMMITTEE_SIZE, TrustedConsensusState<SYNC_COMMITTEE_SIZE>>,
@@ -188,7 +202,8 @@ impl<const SYNC_COMMITTEE_SIZE: usize> ClientState<SYNC_COMMITTEE_SIZE> {
188202
),
189203
});
190204
}
191-
let key = calculate_ibc_commitment_storage_key(&self.ibc_commitments_slot, path.clone());
205+
let key =
206+
calculate_ibc_commitment_storage_location(&self.ibc_commitments_slot, path.clone());
192207
self.execution_verifier
193208
.verify_membership(
194209
root,
@@ -226,7 +241,8 @@ impl<const SYNC_COMMITTEE_SIZE: usize> ClientState<SYNC_COMMITTEE_SIZE> {
226241
),
227242
});
228243
}
229-
let key = calculate_ibc_commitment_storage_key(&self.ibc_commitments_slot, path.clone());
244+
let key =
245+
calculate_ibc_commitment_storage_location(&self.ibc_commitments_slot, path.clone());
230246
self.execution_verifier
231247
.verify_non_membership(root, key.as_bytes(), proof.clone())
232248
.map_err(|e| ClientError::ClientSpecific {
@@ -424,6 +440,12 @@ impl<const SYNC_COMMITTEE_SIZE: usize> Ics2ClientState for ClientState<SYNC_COMM
424440
}
425441
let misbehaviour = Misbehaviour::<SYNC_COMMITTEE_SIZE>::try_from(misbehaviour)?;
426442
misbehaviour.validate()?;
443+
if misbehaviour.client_id != client_id {
444+
return Err(
445+
Error::UnexpectedClientIdInMisbehaviour(client_id, misbehaviour.client_id).into(),
446+
);
447+
}
448+
427449
let consensus_state = match maybe_consensus_state(
428450
ctx,
429451
&ClientConsensusStatePath::new(&client_id, &misbehaviour.trusted_sync_committee.height),
@@ -557,14 +579,15 @@ impl<const SYNC_COMMITTEE_SIZE: usize> Ics2ClientState for ClientState<SYNC_COMM
557579

558580
fn verify_packet_data(
559581
&self,
560-
_ctx: &dyn ibc::core::ValidationContext,
582+
ctx: &dyn ibc::core::ValidationContext,
561583
proof_height: ibc::Height,
562584
connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd,
563585
proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes,
564586
root: &ibc::core::ics23_commitment::commitment::CommitmentRoot,
565587
commitment_path: &ibc::core::ics24_host::path::CommitmentPath,
566588
commitment: ibc::core::ics04_channel::commitment::PacketCommitment,
567589
) -> Result<(), ClientError> {
590+
verify_delay_passed(ctx, proof_height, connection_end)?;
568591
self.verify_membership(
569592
proof_height,
570593
connection_end.counterparty().prefix(),
@@ -577,14 +600,15 @@ impl<const SYNC_COMMITTEE_SIZE: usize> Ics2ClientState for ClientState<SYNC_COMM
577600

578601
fn verify_packet_acknowledgement(
579602
&self,
580-
_ctx: &dyn ibc::core::ValidationContext,
603+
ctx: &dyn ibc::core::ValidationContext,
581604
proof_height: ibc::Height,
582605
connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd,
583606
proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes,
584607
root: &ibc::core::ics23_commitment::commitment::CommitmentRoot,
585608
ack_path: &ibc::core::ics24_host::path::AckPath,
586609
ack: ibc::core::ics04_channel::commitment::AcknowledgementCommitment,
587610
) -> Result<(), ClientError> {
611+
verify_delay_passed(ctx, proof_height, connection_end)?;
588612
self.verify_membership(
589613
proof_height,
590614
connection_end.counterparty().prefix(),
@@ -597,14 +621,15 @@ impl<const SYNC_COMMITTEE_SIZE: usize> Ics2ClientState for ClientState<SYNC_COMM
597621

598622
fn verify_next_sequence_recv(
599623
&self,
600-
_ctx: &dyn ibc::core::ValidationContext,
624+
ctx: &dyn ibc::core::ValidationContext,
601625
proof_height: ibc::Height,
602626
connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd,
603627
proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes,
604628
root: &ibc::core::ics23_commitment::commitment::CommitmentRoot,
605629
seq_recv_path: &ibc::core::ics24_host::path::SeqRecvPath,
606630
sequence: ibc::core::ics04_channel::packet::Sequence,
607631
) -> Result<(), ClientError> {
632+
verify_delay_passed(ctx, proof_height, connection_end)?;
608633
let mut seq_bytes = Vec::new();
609634
u64::from(sequence)
610635
.encode(&mut seq_bytes)
@@ -622,13 +647,14 @@ impl<const SYNC_COMMITTEE_SIZE: usize> Ics2ClientState for ClientState<SYNC_COMM
622647

623648
fn verify_packet_receipt_absence(
624649
&self,
625-
_ctx: &dyn ibc::core::ValidationContext,
650+
ctx: &dyn ibc::core::ValidationContext,
626651
proof_height: ibc::Height,
627652
connection_end: &ibc::core::ics03_connection::connection::ConnectionEnd,
628653
proof: &ibc::core::ics23_commitment::commitment::CommitmentProofBytes,
629654
root: &ibc::core::ics23_commitment::commitment::CommitmentRoot,
630655
receipt_path: &ibc::core::ics24_host::path::ReceiptPath,
631656
) -> Result<(), ClientError> {
657+
verify_delay_passed(ctx, proof_height, connection_end)?;
632658
self.verify_non_membership(
633659
proof_height,
634660
connection_end.counterparty().prefix(),
@@ -937,6 +963,58 @@ fn trim_left_zero(value: &[u8]) -> &[u8] {
937963
&value[pos..]
938964
}
939965

966+
// A copy from https://github.com/cosmos/ibc-rs/blob/eea4f0e7a1887f2f1cb18a550d08bb805a08240a/crates/ibc/src/clients/ics07_tendermint/client_state.rs#L1031
967+
fn verify_delay_passed(
968+
ctx: &dyn ValidationContext,
969+
height: Height,
970+
connection_end: &ConnectionEnd,
971+
) -> Result<(), ClientError> {
972+
let current_timestamp = ctx.host_timestamp().map_err(|e| ClientError::Other {
973+
description: e.to_string(),
974+
})?;
975+
let current_height = ctx.host_height().map_err(|e| ClientError::Other {
976+
description: e.to_string(),
977+
})?;
978+
979+
let client_id = connection_end.client_id();
980+
let processed_time =
981+
ctx.client_update_time(client_id, &height)
982+
.map_err(|_| Error::ProcessedTimeNotFound {
983+
client_id: client_id.clone(),
984+
height,
985+
})?;
986+
let processed_height = ctx.client_update_height(client_id, &height).map_err(|_| {
987+
Error::ProcessedHeightNotFound {
988+
client_id: client_id.clone(),
989+
height,
990+
}
991+
})?;
992+
993+
let delay_period_time = connection_end.delay_period();
994+
let delay_period_height = ctx.block_delay(&delay_period_time);
995+
996+
let earliest_time =
997+
(processed_time + delay_period_time).map_err(Error::TimestampOverflowError)?;
998+
if !(current_timestamp == earliest_time || current_timestamp.after(&earliest_time)) {
999+
return Err(Error::NotEnoughTimeElapsed {
1000+
current_timestamp,
1001+
earliest_time,
1002+
}
1003+
.into());
1004+
}
1005+
1006+
let earliest_height = processed_height.add(delay_period_height);
1007+
if current_height < earliest_height {
1008+
return Err(Error::NotEnoughBlocksElapsed {
1009+
current_height,
1010+
earliest_height,
1011+
}
1012+
.into());
1013+
}
1014+
1015+
Ok(())
1016+
}
1017+
9401018
#[cfg(test)]
9411019
mod tests {
9421020
use super::*;

crates/ibc/src/commitment.rs

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
use crate::errors::Error;
22
use crate::internal_prelude::*;
3-
use ethereum_consensus::types::{Address, H256};
3+
use ethereum_consensus::types::H256;
44
use ibc::core::ics24_host::Path;
55
use rlp::Rlp;
66
use tiny_keccak::{Hasher, Keccak};
77

8-
pub fn calculate_account_path(ibc_address: &Address) -> H256 {
9-
keccak_256(&ibc_address.0).into()
10-
}
11-
12-
pub fn calculate_ibc_commitment_storage_key(ibc_commitments_slot: &H256, path: Path) -> H256 {
8+
/// Calculate the storage location for the commitment stored in the IBC contract
9+
///
10+
/// The spec is here: https://github.com/hyperledger-labs/yui-ibc-solidity/blob/0e83dc7aadf71380dae6e346492e148685510663/docs/architecture.md#L46
11+
pub fn calculate_ibc_commitment_storage_location(ibc_commitments_slot: &H256, path: Path) -> H256 {
1312
keccak_256(
1413
&[
1514
&keccak_256(&path.into_bytes()),
@@ -37,24 +36,6 @@ pub fn decode_eip1184_rlp_proof(proof: Vec<u8>) -> Result<Vec<Vec<u8>>, Error> {
3736
}
3837
}
3938

40-
pub fn extract_storage_root_from_account(account_rlp: &[u8]) -> Result<H256, Error> {
41-
let r = Rlp::new(account_rlp);
42-
if !r.is_list() {
43-
let items: Vec<Vec<u8>> = r.as_list()?;
44-
if items.len() != 4 {
45-
Err(Error::InvalidProofFormatError(
46-
"proof must be rlp list".into(),
47-
))
48-
} else {
49-
Ok(H256::from_slice(items.get(2).unwrap()))
50-
}
51-
} else {
52-
Err(Error::InvalidProofFormatError(
53-
"proof must be rlp list".into(),
54-
))
55-
}
56-
}
57-
5839
fn keccak_256(input: &[u8]) -> [u8; 32] {
5940
let mut out = [0u8; 32];
6041
let mut k = Keccak::v256();

crates/ibc/src/errors.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ use ethereum_consensus::{
88
types::{H256, U64},
99
};
1010
use ibc::{
11-
core::{ics02_client::error::ClientError, ics24_host::error::ValidationError, ContextError},
11+
core::{
12+
ics02_client::error::ClientError,
13+
ics24_host::{error::ValidationError, identifier::ClientId},
14+
ContextError,
15+
},
1216
timestamp::{ParseTimestampError, Timestamp, TimestampOverflowError},
1317
Height,
1418
};
@@ -34,12 +38,8 @@ pub enum Error {
3438
InvalidNextSyncCommitteeKeys(PublicKey, PublicKey),
3539
/// invalid proof format error: {0}
3640
InvalidProofFormatError(String),
37-
/// rlp decode error: {0}
38-
RLPDecodeError(rlp::DecoderError),
3941
/// account storage root mismatch: expected={0} actual={1} state_root={2} address={3} account_proof={4:?}
4042
AccountStorageRootMismatch(H256, H256, H256, String, Vec<String>),
41-
/// invalid account storage root: {0:?}
42-
InvalidAccountStorageRoot(Vec<u8>),
4343
/// store does not support the finalized_period: store_period={0} finalized_period={1}
4444
StoreNotSupportedFinalizedPeriod(U64, U64),
4545
/// both updates of misbehaviour data must have same period: {0} != {1}
@@ -115,6 +115,22 @@ pub enum Error {
115115
UnknownMessageType(String),
116116
/// cannot initialize frozen client
117117
CannotInitializeFrozenClient,
118+
/// unexpected client ID in misbehaviour: expected={0} got={1}
119+
UnexpectedClientIdInMisbehaviour(ClientId, ClientId),
120+
/// Processed time for the client `{client_id}` at height `{height}` not found
121+
ProcessedTimeNotFound { client_id: ClientId, height: Height },
122+
/// Processed height for the client `{client_id}` at height `{height}` not found
123+
ProcessedHeightNotFound { client_id: ClientId, height: Height },
124+
/// not enough time elapsed, current timestamp `{current_timestamp}` is still less than earliest acceptable timestamp `{earliest_time}`
125+
NotEnoughTimeElapsed {
126+
current_timestamp: Timestamp,
127+
earliest_time: Timestamp,
128+
},
129+
/// not enough blocks elapsed, current height `{current_height}` is still less than earliest acceptable height `{earliest_height}`
130+
NotEnoughBlocksElapsed {
131+
current_height: Height,
132+
earliest_height: Height,
133+
},
118134
}
119135

120136
impl Error {
@@ -137,12 +153,6 @@ impl From<Error> for ContextError {
137153
}
138154
}
139155

140-
impl From<rlp::DecoderError> for Error {
141-
fn from(value: rlp::DecoderError) -> Self {
142-
Error::RLPDecodeError(value)
143-
}
144-
}
145-
146156
impl From<ethereum_consensus::errors::Error> for Error {
147157
fn from(value: ethereum_consensus::errors::Error) -> Self {
148158
Error::EthereumConsensusError(value)

crates/ibc/src/header.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,15 @@ impl<const SYNC_COMMITTEE_SIZE: usize> From<ClientMessage<SYNC_COMMITTEE_SIZE>>
6262

6363
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6464
pub struct Header<const SYNC_COMMITTEE_SIZE: usize> {
65+
/// trusted sync committee corresponding to the period of the signature slot of the `consensus_update`
6566
pub trusted_sync_committee: TrustedSyncCommittee<SYNC_COMMITTEE_SIZE>,
67+
/// consensus update attested by the `trusted_sync_committee`
6668
pub consensus_update: ConsensusUpdateInfo<SYNC_COMMITTEE_SIZE>,
69+
/// execution update based on the `consensus_update.finalized_header`
6770
pub execution_update: ExecutionUpdateInfo,
71+
/// account update based on the `execution_update.state_root`
6872
pub account_update: AccountUpdateInfo,
73+
/// timestamp of the `consensus_update.finalized_header`
6974
pub timestamp: Timestamp,
7075
}
7176

crates/ibc/src/misbehaviour.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ pub const ETHEREUM_NEXT_SYNC_COMMITTEE_MISBEHAVIOUR_TYPE_URL: &str =
3131

3232
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
3333
pub struct Misbehaviour<const SYNC_COMMITTEE_SIZE: usize> {
34+
/// The client identifier
3435
pub client_id: ClientId,
36+
/// The sync committee related to the misbehaviour
3537
pub trusted_sync_committee: TrustedSyncCommittee<SYNC_COMMITTEE_SIZE>,
38+
/// The misbehaviour data
3639
pub data: MisbehaviourData<SYNC_COMMITTEE_SIZE, ConsensusUpdateInfo<SYNC_COMMITTEE_SIZE>>,
3740
}
3841

0 commit comments

Comments
 (0)