Skip to content
This repository has been archived by the owner on Jun 24, 2024. It is now read-only.

Commit

Permalink
feat: implement verify_client_message method for DA (Celestia) part (
Browse files Browse the repository at this point in the history
…#117)

* chore: implement verify_client_message

* fix: use same validators set for all tests

* fix: use valid next_validators_hash for MockDaSpec

* chore: update basecoin rev
  • Loading branch information
Farhad-Shabani authored Apr 3, 2024
1 parent 63562e7 commit 05c8ebe
Show file tree
Hide file tree
Showing 16 changed files with 521 additions and 172 deletions.
158 changes: 59 additions & 99 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ thiserror = "1.0.38"
tracing = { version = "0.1.40", default-features = false }

# ibc depedenencies
ibc-core = { version = "0.51.0", default-features = false, features = ["borsh","schema"] }
ibc-core-client = { version = "0.51.0", default-features = false }
ibc-core-host-cosmos = { version = "0.51.0", default-features = false }
ibc-client-tendermint = { version = "0.51.0", default-features = false }
ibc-client-wasm-types = { version = "0.51.0", default-features = false }
ibc-app-transfer = { version = "0.51.0", default-features = false }
ibc-primitives = { version = "0.51.0", default-features = false }
ibc-query = { version = "0.51.0", default-features = false, features = ["schema"] }
ibc-testkit = { version = "0.51.0", default-features = false }
ibc-core = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false, features = ["borsh","schema"] }
ibc-core-client = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }
ibc-core-host-cosmos = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }
ibc-client-tendermint = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }
ibc-client-wasm-types = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }
ibc-app-transfer = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }
ibc-primitives = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }
ibc-query = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false, features = ["schema"] }
ibc-testkit = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d6cbee7201", default-features = false }

# NOTE: `ibc-proto` is solely required by `sov-ibc-proto`. When needing Protobuf
# Rust types in the project, importing from their respective `ibc` type crates is
Expand Down
39 changes: 32 additions & 7 deletions clients/sov-celestia-cw/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
use std::ops::Add;
use std::time::Duration;

use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_json, Deps, MessageInfo};
use ibc_client_tendermint::types::Header;
use ibc_core::client::types::{Height, Status};
use ibc_core::host::types::identifiers::ChainId;
use ibc_core::primitives::Timestamp;
use sov_celestia_client::types::client_message::test_util::dummy_sov_header;
use sov_celestia_client::types::client_message::{Root, SovTmHeader};
use sov_celestia_client::types::client_state::test_util::{
dummy_checksum, dummy_sov_client_state, dummy_sov_consensus_state,
dummy_checksum, dummy_sov_client_state, dummy_sov_consensus_state, mock_celestia_chain_id,
};
use sov_celestia_client::types::client_state::SovTmClientState;
use sov_celestia_client::types::codec::AnyCodec;
use sov_celestia_client::types::consensus_state::SovTmConsensusState;
use tendermint_testgen::Generator;
use tendermint_testgen::{Generator, Validator};

use crate::entrypoints::{instantiate, query, sudo};
use crate::types::{
Expand All @@ -23,10 +27,16 @@ pub fn dummy_msg_info() -> MessageInfo {
mock_info("creator", &coins(1000, "ibc"))
}

/// Returns a dummy timestamp for testing purposes. The value corresponds to the
/// timestamp of the `mock_env()`.
pub fn dummy_tm_time() -> Timestamp {
Timestamp::from_nanoseconds(1_571_797_419_879_305_533).expect("never fails")
}

pub fn dummy_instantiate_msg(latest_height: Height) -> InstantiateMsg {
let sov_client_state = dummy_sov_client_state(ChainId::new("rollup").unwrap(), latest_height);

let sov_consensus_state = dummy_sov_consensus_state();
let sov_consensus_state = dummy_sov_consensus_state(dummy_tm_time());

InstantiateMsg {
client_state: SovTmClientState::encode_thru_any(sov_client_state),
Expand All @@ -37,17 +47,32 @@ pub fn dummy_instantiate_msg(latest_height: Height) -> InstantiateMsg {

pub fn dummy_client_message(trusted_height: Height, target_height: Height) -> Vec<u8> {
let validators = vec![
tendermint_testgen::Validator::new("1"),
tendermint_testgen::Validator::new("2"),
Validator::new("1").voting_power(40),
Validator::new("2").voting_power(30),
Validator::new("3").voting_power(30),
];

let header =
tendermint_testgen::Header::new(&validators).height(target_height.revision_height());
let future_time = dummy_tm_time()
.add(Duration::from_secs(1))
.expect("never fails")
.into_tm_time()
.expect("Time exists");

let header = tendermint_testgen::Header::new(&validators)
.chain_id(mock_celestia_chain_id().as_str())
.height(target_height.revision_height())
.time(future_time)
.next_validators(&validators)
.app_hash(vec![0; 32].try_into().expect("never fails"));

let light_block = tendermint_testgen::LightBlock::new_default_with_header(header)
.generate()
.expect("failed to generate light block");

let val = light_block.next_validators.hash();

dbg!(val);

let tm_header = Header {
signed_header: light_block.signed_header,
validator_set: light_block.validators,
Expand Down
77 changes: 77 additions & 0 deletions clients/sov-celestia/src/client_state/misbehaviour.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use ibc_client_tendermint::client_state::verify_misbehaviour_header;
use ibc_client_tendermint::context::TmVerifier;
use ibc_core::client::types::error::ClientError;
use ibc_core::host::types::identifiers::{ChainId, ClientId};
use ibc_core::host::types::path::ClientConsensusStatePath;
use sov_celestia_client_types::client_message::SovTmMisbehaviour;
use tendermint_light_client_verifier::options::Options;

use crate::context::{ConsensusStateConverter, ValidationContext as SovValidationContext};

/// Determines whether or not two conflicting headers at the same height would
/// have convinced the light client.
pub fn verify_misbehaviour<V>(
ctx: &V,
misbehaviour: &SovTmMisbehaviour,
client_id: &ClientId,
chain_id: &ChainId,
options: &Options,
verifier: &impl TmVerifier,
) -> Result<(), ClientError>
where
V: SovValidationContext,
V::ConsensusStateRef: ConsensusStateConverter,
{
misbehaviour.validate_basic()?;

let header_1 = misbehaviour.header1();
let trusted_consensus_state_1 = {
let consensus_state_path = ClientConsensusStatePath::new(
client_id.clone(),
header_1.trusted_height().revision_number(),
header_1.trusted_height().revision_height(),
);
let consensus_state = ctx.consensus_state(&consensus_state_path)?;

consensus_state.try_into()?
};

let header_2 = misbehaviour.header2();
let trusted_consensus_state_2 = {
let consensus_state_path = ClientConsensusStatePath::new(
client_id.clone(),
header_2.trusted_height().revision_number(),
header_2.trusted_height().revision_height(),
);
let consensus_state = ctx.consensus_state(&consensus_state_path)?;

consensus_state.try_into()?
};

let current_timestamp = ctx.host_timestamp()?;

verify_misbehaviour_header(
&header_1.da_header,
chain_id,
options,
trusted_consensus_state_1.timestamp(),
trusted_consensus_state_1.da_params.next_validators_hash,
current_timestamp,
verifier,
)?;

verify_misbehaviour_header(
&header_2.da_header,
chain_id,
options,
trusted_consensus_state_2.timestamp(),
trusted_consensus_state_2.da_params.next_validators_hash,
current_timestamp,
verifier,
)?;

// TODO: Determine what sort of checks we need to carry out for detecting
// `AggregatedProofData` misbehaviour.

Ok(())
}
7 changes: 6 additions & 1 deletion clients/sov-celestia/src/client_state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
//! Implements the core [`ClientState`](ibc_core::client::context::client_state::ClientState) trait
//! Implements the core
//! [`ClientState`](ibc_core::client::context::client_state::ClientState) trait
//! for the Sovereign light client.
mod common;
mod execution;
mod misbehaviour;
mod update_client;
mod validation;

pub use execution::*;
use ibc_core::client::types::error::ClientError;
use ibc_core::primitives::prelude::*;
use ibc_core::primitives::proto::{Any, Protobuf};
pub use misbehaviour::*;
use sov_celestia_client_types::client_state::{
SovTmClientState, SOV_TENDERMINT_CLIENT_STATE_TYPE_URL,
};
use sov_celestia_client_types::proto::tendermint::v1::ClientState as RawSovTmClientState;
pub use update_client::*;

/// Newtype wrapper exists so that we can bypass Rust's orphan rules and
/// implement traits from `ibc::core::client::context` on the `ClientState`
Expand Down
125 changes: 125 additions & 0 deletions clients/sov-celestia/src/client_state/update_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use ibc_client_tendermint::context::TmVerifier;
use ibc_client_tendermint::types::Header as TmHeader;
use ibc_core::client::types::error::ClientError;
use ibc_core::host::types::identifiers::{ChainId, ClientId};
use ibc_core::host::types::path::ClientConsensusStatePath;
use sov_celestia_client_types::client_message::SovTmHeader;
use sov_celestia_client_types::error::IntoResult;
use tendermint_light_client_verifier::options::Options;
use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState};
use tendermint_light_client_verifier::Verifier;

use crate::context::{ConsensusStateConverter, ValidationContext as SovValidationContext};

/// Verifies the IBC header type for the Sovereign SDK rollups, which consists
/// of the DA header and the aggregated proof date validation.
pub fn verify_header<V>(
ctx: &V,
header: &SovTmHeader,
client_id: &ClientId,
chain_id: &ChainId,
options: &Options,
verifier: &impl TmVerifier,
) -> Result<(), ClientError>
where
V: SovValidationContext,
V::ConsensusStateRef: ConsensusStateConverter,
{
// Checks the sanity of the fields in the header.
header.validate_basic()?;

verify_da_header(
ctx,
&header.da_header,
client_id,
chain_id,
options,
verifier,
)?;

// TODO: Implement the verification of the `AggregatedProofData`.
// aggregated_proof_date.verify()?;

Ok(())
}

/// Verifies the DA header type for the Sovereign SDK rollups against the
/// trusted state.
pub fn verify_da_header<V>(
ctx: &V,
da_header: &TmHeader,
client_id: &ClientId,
chain_id: &ChainId,
options: &Options,
verifier: &impl TmVerifier,
) -> Result<(), ClientError>
where
V: SovValidationContext,
V::ConsensusStateRef: ConsensusStateConverter,
{
// The revision number of the `ChainId` tracked by the client state must
// match the `ChainId` in the DA header.
da_header
.verify_chain_id_version_matches_height(chain_id)
.map_err(|e| ClientError::Other {
description: format!("failed to verify chain id: {e}"),
})?;

let trusted_height = da_header.trusted_height;

let trusted_state = {
let trusted_client_cons_state_path = ClientConsensusStatePath::new(
client_id.clone(),
trusted_height.revision_number(),
trusted_height.revision_height(),
);
let trusted_consensus_state = ctx
.consensus_state(&trusted_client_cons_state_path)?
.try_into()?;

da_header.check_trusted_next_validator_set(
&trusted_consensus_state.da_params.next_validators_hash,
)?;

TrustedBlockState {
chain_id: &chain_id
.to_string()
.try_into()
.map_err(|e| ClientError::Other {
description: format!("failed to parse chain id: {e}"),
})?,
header_time: trusted_consensus_state.timestamp(),
height: trusted_height.revision_height().try_into().map_err(|_| {
ClientError::Other {
description: "failed to convert revision height to u64".to_string(),
}
})?,
next_validators: &da_header.trusted_next_validator_set,
next_validators_hash: trusted_consensus_state.da_params.next_validators_hash,
}
};

let untrusted_state = UntrustedBlockState {
signed_header: &da_header.signed_header,
validators: &da_header.validator_set,
// NB: This will skip the
// VerificationPredicates::next_validators_match check for the
// untrusted state.
next_validators: None,
};

let now = ctx
.host_timestamp()?
.into_tm_time()
.ok_or_else(|| ClientError::ClientSpecific {
description: "host timestamp is not a valid TM timestamp".to_string(),
})?;

// main header verification, delegated to the tendermint-light-client crate.
verifier
.verifier()
.verify_update_header(untrusted_state, trusted_state, options, now)
.into_result()?;

Ok(())
}
Loading

0 comments on commit 05c8ebe

Please sign in to comment.