diff --git a/Cargo.lock b/Cargo.lock index b3209a3d0..000cfcce5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5139,6 +5139,7 @@ dependencies = [ "sp-runtime 7.0.0", "sp-runtime-interface 7.0.0", "sp-std 5.0.0", + "tendermint", "tendermint-proto", "thiserror", "twox-hash", diff --git a/light-clients/ics10-grandpa-cw/Cargo.toml b/light-clients/ics10-grandpa-cw/Cargo.toml index dd48aa8ff..43a87a451 100644 --- a/light-clients/ics10-grandpa-cw/Cargo.toml +++ b/light-clients/ics10-grandpa-cw/Cargo.toml @@ -61,6 +61,8 @@ hyperspace-primitives = { path = "../../hyperspace/primitives", features = ["tes pallet-ibc = { path = "../../contracts/pallet-ibc" } serde-json-wasm = { version = "0.5.0", default-features = false } serde_json = { version = "1.0.93", default-features = false } +tendermint = { git = "https://github.com/informalsystems/tendermint-rs", rev = "e81f7bf23d63ffbcd242381d1ce5e35da3515ff1", default-features = false } + [features] # for more explicit tests, cargo test --features=backtraces diff --git a/light-clients/ics10-grandpa-cw/src/contract.rs b/light-clients/ics10-grandpa-cw/src/contract.rs index a43c39206..dd31ca940 100644 --- a/light-clients/ics10-grandpa-cw/src/contract.rs +++ b/light-clients/ics10-grandpa-cw/src/contract.rs @@ -30,7 +30,7 @@ use byteorder::{ByteOrder, LittleEndian}; use core::hash::Hasher; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, StdError}; use cw_storage_plus::{Item, Map}; use digest::Digest; use grandpa_light_client_primitives::justification::AncestryChain; @@ -352,8 +352,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { let height = client_state.latest_height(); match get_consensus_state(deps, &client_id, height) { Ok(consensus_state_raw) => { - let consensus_state = Context::decode_consensus_state(&consensus_state_raw)?; - if client_state.expired(env.block.time.as_secs() - consensus_state.timestamp.as_seconds()) { + let consensus_state = Context::::decode_consensus_state(&consensus_state_raw).map_err(|e|StdError::serialize_err(e.to_string(), e.to_string()))?; + if client_state.expired(core::time::Duration::from_secs(env.block.time.seconds() - consensus_state.timestamp.unix_timestamp() as u64)) { return to_binary(&QueryResponse::status("Expired".to_string())); } to_binary(&QueryResponse::status("Active".to_string())) @@ -439,3 +439,66 @@ pub extern "C" fn ext_hashing_twox_64_version_1(data: i64) -> i32 { let out_ptr = Box::leak(hash).as_ptr(); out_ptr as i32 } + + +#[cfg(test)] +mod tests { + use cosmwasm_std::from_binary; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use tendermint::Time; + use ibc::core::ics02_client::client_state::ClientState as _; + + use crate::ics23::ClientStates; + + use super::*; + #[test] + fn test_query() { + + let mut deps = mock_dependencies(); + let env = mock_env(); + + + for (expected, offset) in [("Active", 0i64), ("Expired", env.block.time.seconds() as i64 - 10), ("Frozen", 0i64)] { + let mut client_state = ics10_grandpa::client_state::ClientState::::default(); + let mut consensus_state = ics10_grandpa::consensus_state::ConsensusState::new(vec![], Time::from_unix_timestamp(0, 0).unwrap()); + let height = Height { revision_number: 0, revision_height: 1000}; + client_state.latest_para_height = height.revision_height as _; + + + consensus_state.timestamp = Time::from_unix_timestamp(env.block.time.seconds() as i64 - offset, 0).unwrap(); + let deps_mut = deps.as_mut(); + let mut client_states = ClientStates::new(deps_mut.storage); + if expected == "Frozen" { + let height = Height { revision_number: 0, revision_height: height.revision_height - 100}; + client_state = client_state.with_frozen_height(height.clone()).unwrap(); + } + + client_states.insert(client_state.encode_to_vec().unwrap()); + + let mut context = Context::new(deps_mut, env.clone()); + context.store_client_state(ClientId::default(), client_state).unwrap(); + context.store_consensus_state(ClientId::default(), height , consensus_state).unwrap(); + + let resp = query( + deps.as_ref(), + mock_env(), + QueryMsg::Status(StatusMsg{}) + ).unwrap(); + + instantiate( + deps.as_mut(), + env.clone(), + mock_info("sender", &[]), + InstantiateMsg { } , + ) + .unwrap(); + + let resp: QueryResponse = from_binary(&resp).unwrap(); + + assert_eq!( + resp, + QueryResponse::status(expected.to_string()) + ); + } + } +} \ No newline at end of file