diff --git a/crates/ibc/Cargo.toml b/crates/ibc/Cargo.toml index 38ec203..8b93e39 100644 --- a/crates/ibc/Cargo.toml +++ b/crates/ibc/Cargo.toml @@ -16,10 +16,10 @@ ssz-rs = { git = "https://github.com/bluele/ssz_rs", branch = "serde-no-std", de hex = { version = "0.4.3", default-features = false } ethereum-ibc-proto = { path = "../../proto", default-features = false } -ethereum-consensus = { git = "https://github.com/datachainlab/ethereum-light-client-rs", rev = "v0.1.6", default-features = false } -ethereum-light-client-verifier = { git = "https://github.com/datachainlab/ethereum-light-client-rs", rev = "v0.1.6", default-features = false } +ethereum-consensus = { git = "https://github.com/datachainlab/ethereum-light-client-rs", rev = "v0.1.7", default-features = false } +ethereum-light-client-verifier = { git = "https://github.com/datachainlab/ethereum-light-client-rs", rev = "v0.1.7", default-features = false } [dev-dependencies] time = { version = "0.3", default-features = false, features = ["macros", "parsing"] } hex-literal = "0.4.1" -ethereum-light-client-verifier = { git = "https://github.com/datachainlab/ethereum-light-client-rs", rev = "v0.1.6", default-features = false, features = ["test-utils"] } +ethereum-light-client-verifier = { git = "https://github.com/datachainlab/ethereum-light-client-rs", rev = "v0.1.7", default-features = false, features = ["test-utils"] } diff --git a/crates/ibc/src/client_state.rs b/crates/ibc/src/client_state.rs index e5f016c..1967107 100644 --- a/crates/ibc/src/client_state.rs +++ b/crates/ibc/src/client_state.rs @@ -33,6 +33,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::protobuf::Protobuf; use prost::Message; use serde::{Deserialize, Serialize}; +use tiny_keccak::{Hasher, Keccak}; /// The revision number for the Ethereum light client is always 0. /// @@ -204,11 +205,12 @@ impl ClientState { } let key = calculate_ibc_commitment_storage_location(&self.ibc_commitments_slot, path.clone()); + let commitment = Self::keccak256(&value); self.execution_verifier .verify_membership( root, key.as_bytes(), - rlp::encode(&trim_left_zero(&value)).as_ref(), + rlp::encode(&trim_left_zero(commitment.as_bytes())).as_ref(), proof.clone(), ) .map_err(|e| ClientError::ClientSpecific { @@ -314,6 +316,14 @@ impl ClientState { Ok(()) } } + + fn keccak256(bz: &[u8]) -> H256 { + let mut hasher = Keccak::v256(); + let mut output = [0u8; 32]; + hasher.update(bz); + hasher.finalize(&mut output); + H256::from_slice(&output) + } } impl Ics2ClientState for ClientState { @@ -1018,6 +1028,7 @@ fn verify_delay_passed( #[cfg(test)] mod tests { use super::*; + use core::str::FromStr; use ethereum_consensus::fork::{ altair::ALTAIR_FORK_SPEC, bellatrix::BELLATRIX_FORK_SPEC, capella::CAPELLA_FORK_SPEC, deneb::DENEB_FORK_SPEC, @@ -1124,6 +1135,76 @@ mod tests { assert!(res.is_ok(), "{:?}", res); } + #[test] + fn test_verify_membership() { + let client_state = + ClientState::<{ ethereum_consensus::preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }> { + ibc_address: Address(hex!("a7f733a4fEA1071f58114b203F57444969b86524")), + ibc_commitments_slot: H256(hex!( + "1ee222554989dda120e26ecacf756fe1235cd8d726706b57517715dde4f0c900" + )), + latest_execution_block_number: 1.into(), + ..Default::default() + }; + let root = hex!("27cd08827e6bf1e435832f4b2660107beb562314287b3fa534f3b189574c0cca") + .to_vec() + .into(); + let (path, proof, value) = get_membership_proof(); + let proof_height = Height::new(ETHEREUM_CLIENT_REVISION_NUMBER, 1).unwrap(); + let res = client_state.verify_membership( + proof_height, + &Default::default(), + &proof.try_into().unwrap(), + &root, + Path::from_str(&path).unwrap(), + value, + ); + assert!(res.is_ok(), "{:?}", res); + } + + #[test] + fn test_verify_non_membership() { + let client_state = + ClientState::<{ ethereum_consensus::preset::minimal::PRESET.SYNC_COMMITTEE_SIZE }> { + ibc_address: Address(hex!("a7f733a4fEA1071f58114b203F57444969b86524")), + ibc_commitments_slot: H256(hex!( + "1ee222554989dda120e26ecacf756fe1235cd8d726706b57517715dde4f0c900" + )), + latest_execution_block_number: 1.into(), + ..Default::default() + }; + let root = hex!("27cd08827e6bf1e435832f4b2660107beb562314287b3fa534f3b189574c0cca") + .to_vec() + .into(); + let (path, proof) = get_non_membership_proof(); + let proof_height = Height::new(ETHEREUM_CLIENT_REVISION_NUMBER, 1).unwrap(); + let res = client_state.verify_non_membership( + proof_height, + &Default::default(), + &proof.try_into().unwrap(), + &root, + Path::from_str(&path).unwrap(), + ); + assert!(res.is_ok(), "{:?}", res); + } + + // returns: (path, proof, value) + fn get_membership_proof() -> (String, Vec, Vec) { + ( + "clients/lcp-client-0/clientState".to_string(), + hex!("f90159f901118080a0143145e818eeff83817419a6632ea193fd1acaa4f791eb17282f623f38117f56a0e6ee0a993a7254ee9253d766ea005aec74eb1e11656961f0fb11323f4f91075580808080a01efae04adc2e970b4af3517581f41ce2ba4ff60492d33696c1e2a5ab70cb55bba03bac3f5124774e41fb6efdd7219530846f9f6441045c4666d2855c6598cfca00a020d7122ffc86cb37228940b5a9441e9fd272a3450245c9130ca3ab00bc1cd6ef80a0047f255205a0f2b0e7d29d490abf02bfb62c3ed201c338bc7f0088fa9c5d77eda069fecc766fcb2df04eb3a834b1f4ba134df2be114479e251d9cc9b6ba493077b80a094c3ed6a7ef63a6a67e46cc9876b9b1882eeba3d28e6d61bb15cdfb207d077e180f843a03e077f3dfd0489e70c68282ced0126c62fcef50acdcb7f57aa4552b87b456b11a1a05dc044e92e82db28c96fd98edd502949612b06e8da6dd74664a43a5ed857b298").to_vec(), + hex!("0a242f6962632e6c69676874636c69656e74732e6c63702e76312e436c69656e74537461746512ed010a208083673c69fe3f098ea79a799d9dbb99c39b4b4f17a1a79ef58bdf8ae86299951080f524220310fb012a1353575f48415244454e494e475f4e45454445442a1147524f55505f4f55545f4f465f44415445320e494e54454c2d53412d3030323139320e494e54454c2d53412d3030323839320e494e54454c2d53412d3030333334320e494e54454c2d53412d3030343737320e494e54454c2d53412d3030363134320e494e54454c2d53412d3030363135320e494e54454c2d53412d3030363137320e494e54454c2d53412d30303832383a14cb96f8d6c2d543102184d679d7829b39434e4eec48015001").to_vec() + ) + } + + // returns: (path, proof) + fn get_non_membership_proof() -> (String, Vec) { + ( + "clients/lcp-client-1/clientState".to_string(), + hex!("f90114f901118080a0143145e818eeff83817419a6632ea193fd1acaa4f791eb17282f623f38117f56a0e6ee0a993a7254ee9253d766ea005aec74eb1e11656961f0fb11323f4f91075580808080a01efae04adc2e970b4af3517581f41ce2ba4ff60492d33696c1e2a5ab70cb55bba03bac3f5124774e41fb6efdd7219530846f9f6441045c4666d2855c6598cfca00a020d7122ffc86cb37228940b5a9441e9fd272a3450245c9130ca3ab00bc1cd6ef80a0047f255205a0f2b0e7d29d490abf02bfb62c3ed201c338bc7f0088fa9c5d77eda069fecc766fcb2df04eb3a834b1f4ba134df2be114479e251d9cc9b6ba493077b80a094c3ed6a7ef63a6a67e46cc9876b9b1882eeba3d28e6d61bb15cdfb207d077e180").to_vec() + ) + } + #[test] fn test_trusting_period_validation() { { diff --git a/crates/ibc/src/errors.rs b/crates/ibc/src/errors.rs index 4b34a89..7540e2f 100644 --- a/crates/ibc/src/errors.rs +++ b/crates/ibc/src/errors.rs @@ -99,6 +99,8 @@ pub enum Error { ContextError(ContextError), /// zero timestamp error ZeroTimestampError, + /// zero block number error + ZeroBlockNumberError, /// timestamp overflow error: `{0}` TimestampOverflowError(TimestampOverflowError), /// parse timestamp error: `{0}` diff --git a/crates/ibc/src/header.rs b/crates/ibc/src/header.rs index f0e17e1..45f1a7d 100644 --- a/crates/ibc/src/header.rs +++ b/crates/ibc/src/header.rs @@ -12,6 +12,7 @@ use crate::types::{ use bytes::Buf; use ethereum_consensus::compute::compute_timestamp_at_slot; use ethereum_consensus::context::ChainContext; +use ethereum_consensus::types::U64; use ethereum_ibc_proto::ibc::lightclients::ethereum::v1::Header as RawHeader; use ethereum_light_client_verifier::updates::ConsensusUpdate; use ibc::core::ics02_client::error::ClientError; @@ -86,6 +87,9 @@ impl Header { if self.timestamp.into_tm_time().is_none() { return Err(Error::ZeroTimestampError); } + if self.execution_update.block_number == U64(0) { + return Err(Error::ZeroBlockNumberError); + } let header_timestamp_nanos = self .timestamp .into_tm_time() @@ -237,7 +241,7 @@ mod tests { let dummy_execution_block_number = 1; for b in [false, true] { - let update = gen_light_client_update_with_params::<32, _>( + let (update, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, @@ -257,7 +261,10 @@ mod tests { is_next: true, }, consensus_update: update.clone(), - execution_update: ExecutionUpdateInfo::default(), + execution_update: ExecutionUpdateInfo { + block_number: U64(2), + ..Default::default() + }, account_update: AccountUpdateInfo::default(), timestamp: Timestamp::from_nanoseconds( compute_timestamp_at_slot(&ctx, update.finalized_beacon_header().slot).0 @@ -278,7 +285,10 @@ mod tests { is_next: true, }, consensus_update: update, - execution_update: ExecutionUpdateInfo::default(), + execution_update: ExecutionUpdateInfo { + block_number: U64(2), + ..Default::default() + }, account_update: AccountUpdateInfo::default(), timestamp: Timestamp::from_nanoseconds(0).unwrap(), }; @@ -287,7 +297,7 @@ mod tests { assert!(res.is_err(), "header with zero timestamp should fail"); } - let update = gen_light_client_update_with_params::<32, _>( + let (update, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, diff --git a/crates/ibc/src/misbehaviour.rs b/crates/ibc/src/misbehaviour.rs index 37970ce..c12c6e2 100644 --- a/crates/ibc/src/misbehaviour.rs +++ b/crates/ibc/src/misbehaviour.rs @@ -245,7 +245,7 @@ mod tests { let dummy_execution_state_root = [1u8; 32].into(); let dummy_execution_block_number = 1; - let update_1 = gen_light_client_update_with_params::<32, _>( + let (update_1, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, @@ -257,7 +257,7 @@ mod tests { true, 32, ); - let update_2 = gen_light_client_update_with_params::<32, _>( + let (update_2, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, @@ -288,7 +288,7 @@ mod tests { assert_eq!(misbehaviour, decoded); let different_dummy_execution_state_root = [2u8; 32].into(); - let update_3 = gen_light_client_update_with_params::<32, _>( + let (update_3, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, diff --git a/crates/ibc/src/update.rs b/crates/ibc/src/update.rs index ee44875..8c4dde9 100644 --- a/crates/ibc/src/update.rs +++ b/crates/ibc/src/update.rs @@ -177,7 +177,7 @@ mod tests { { // store_period == finalized_period == attested_period == signature_period - let update = to_consensus_update_info(gen_light_client_update_with_params::<32, _>( + let (update, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, @@ -188,7 +188,8 @@ mod tests { scm.get_committee(base_store_period + 1), true, 32, - )); + ); + let update = to_consensus_update_info(update); let new_block_number = 2.into(); let res = apply_updates( &ctx, @@ -226,7 +227,7 @@ mod tests { } { // store_period + 1 == finalized_period == attested_period == signature_period - let update = to_consensus_update_info(gen_light_client_update_with_params::<32, _>( + let (update, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot + slots_per_period, base_attested_slot + slots_per_period, @@ -237,7 +238,8 @@ mod tests { scm.get_committee(base_store_period + 2), true, 32, - )); + ); + let update = to_consensus_update_info(update); let new_block_number = 2.into(); let res = apply_updates( &ctx, @@ -276,7 +278,7 @@ mod tests { { // store_period + 1 == finalized_period == attested_period == signature_period // but the update has no next sync committee - let update = to_consensus_update_info(gen_light_client_update_with_params::<32, _>( + let (update, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot + slots_per_period, base_attested_slot + slots_per_period, @@ -287,7 +289,8 @@ mod tests { scm.get_committee(base_store_period + 2), false, 32, - )); + ); + let update = to_consensus_update_info(update); let new_block_number = 2.into(); let res = apply_updates( &ctx, @@ -314,7 +317,7 @@ mod tests { } { // finalized_period - 1 == store_period == attested_period == signature_period - let update = to_consensus_update_info(gen_light_client_update_with_params::<32, _>( + let (update, _) = gen_light_client_update_with_params::<32, _>( &ctx, base_signature_slot, base_attested_slot, @@ -325,7 +328,8 @@ mod tests { scm.get_committee(base_store_period), true, 32, - )); + ); + let update = to_consensus_update_info(update); let new_block_number = 2.into(); let res = apply_updates( &ctx,