|
| 1 | +use crate::prelude::*; |
| 2 | +use ibc::{ |
| 3 | + clients::ics07_tendermint::{ |
| 4 | + client_state::ClientState, client_type, |
| 5 | + consensus_state::ConsensusState as TmConsensusState, error::Error, |
| 6 | + header::Header as TmHeader, |
| 7 | + }, |
| 8 | + core::{ |
| 9 | + ics02_client::{ |
| 10 | + client_state::{ClientState as Ics2ClientState, UpdatedState}, |
| 11 | + consensus_state::ConsensusState, |
| 12 | + error::ClientError, |
| 13 | + }, |
| 14 | + ics24_host::{identifier::ClientId, path::ClientConsensusStatePath}, |
| 15 | + ContextError, ValidationContext, |
| 16 | + }, |
| 17 | +}; |
| 18 | +use lcp_proto::google::protobuf::Any; |
| 19 | +use tendermint_light_client_verifier::{ |
| 20 | + types::{TrustedBlockState, UntrustedBlockState}, |
| 21 | + ProdVerifier, Verdict, Verifier, |
| 22 | +}; |
| 23 | + |
| 24 | +/// Fork of the `check_header_and_update_state` function from ibc-rs v0.29.0 |
| 25 | +/// https://github.com/cosmos/ibc-rs/blob/10b47c077065a07ded9ac7f03fdb6c0980592d81/crates/ibc/src/clients/ics07_tendermint/client_state.rs#L457 |
| 26 | +pub(crate) fn check_header_and_update_state( |
| 27 | + client_state: &ClientState, |
| 28 | + ctx: &dyn ValidationContext, |
| 29 | + client_id: ClientId, |
| 30 | + header: Any, |
| 31 | +) -> Result<UpdatedState, ClientError> { |
| 32 | + let client_state = downcast_tm_client_state(client_state)?.clone(); |
| 33 | + let header = TmHeader::try_from(header)?; |
| 34 | + |
| 35 | + if header.height().revision_number() != client_state.chain_id().version() { |
| 36 | + return Err(ClientError::ClientSpecific { |
| 37 | + description: Error::MismatchedRevisions { |
| 38 | + current_revision: client_state.chain_id().version(), |
| 39 | + update_revision: header.height().revision_number(), |
| 40 | + } |
| 41 | + .to_string(), |
| 42 | + }); |
| 43 | + } |
| 44 | + |
| 45 | + let trusted_client_cons_state_path = |
| 46 | + ClientConsensusStatePath::new(&client_id, &header.trusted_height); |
| 47 | + let trusted_consensus_state = downcast_tm_consensus_state( |
| 48 | + ctx.consensus_state(&trusted_client_cons_state_path) |
| 49 | + .map_err(|e| match e { |
| 50 | + ContextError::ClientError(e) => e, |
| 51 | + _ => ClientError::Other { |
| 52 | + description: e.to_string(), |
| 53 | + }, |
| 54 | + })? |
| 55 | + .as_ref(), |
| 56 | + )?; |
| 57 | + |
| 58 | + let trusted_state = TrustedBlockState { |
| 59 | + chain_id: &client_state.chain_id.clone().into(), |
| 60 | + header_time: trusted_consensus_state.timestamp, |
| 61 | + height: header |
| 62 | + .trusted_height |
| 63 | + .revision_height() |
| 64 | + .try_into() |
| 65 | + .map_err(|_| ClientError::ClientSpecific { |
| 66 | + description: Error::InvalidHeaderHeight { |
| 67 | + height: header.trusted_height.revision_height(), |
| 68 | + } |
| 69 | + .to_string(), |
| 70 | + })?, |
| 71 | + next_validators: &header.trusted_validator_set, |
| 72 | + next_validators_hash: trusted_consensus_state.next_validators_hash, |
| 73 | + }; |
| 74 | + |
| 75 | + let untrusted_state = UntrustedBlockState { |
| 76 | + signed_header: &header.signed_header, |
| 77 | + validators: &header.validator_set, |
| 78 | + // NB: This will skip the |
| 79 | + // VerificationPredicates::next_validators_match check for the |
| 80 | + // untrusted state. |
| 81 | + next_validators: None, |
| 82 | + }; |
| 83 | + |
| 84 | + let options = client_state.as_light_client_options()?; |
| 85 | + let now = ctx |
| 86 | + .host_timestamp() |
| 87 | + .map_err(|e| ClientError::Other { |
| 88 | + description: e.to_string(), |
| 89 | + })? |
| 90 | + .into_tm_time() |
| 91 | + .unwrap(); |
| 92 | + |
| 93 | + match ProdVerifier::default().verify(untrusted_state, trusted_state, &options, now) { |
| 94 | + Verdict::Success => Ok(()), |
| 95 | + Verdict::NotEnoughTrust(reason) => Err(Error::NotEnoughTrustedValsSigned { reason }), |
| 96 | + Verdict::Invalid(detail) => Err(Error::VerificationError { detail }), |
| 97 | + }?; |
| 98 | + |
| 99 | + Ok(UpdatedState { |
| 100 | + client_state: client_state.with_header(header.clone())?.into_box(), |
| 101 | + consensus_state: TmConsensusState::from(header).into_box(), |
| 102 | + }) |
| 103 | +} |
| 104 | + |
| 105 | +fn downcast_tm_client_state(cs: &dyn Ics2ClientState) -> Result<&ClientState, ClientError> { |
| 106 | + cs.as_any() |
| 107 | + .downcast_ref::<ClientState>() |
| 108 | + .ok_or_else(|| ClientError::ClientArgsTypeMismatch { |
| 109 | + client_type: client_type(), |
| 110 | + }) |
| 111 | +} |
| 112 | + |
| 113 | +fn downcast_tm_consensus_state(cs: &dyn ConsensusState) -> Result<TmConsensusState, ClientError> { |
| 114 | + cs.as_any() |
| 115 | + .downcast_ref::<TmConsensusState>() |
| 116 | + .ok_or_else(|| ClientError::ClientArgsTypeMismatch { |
| 117 | + client_type: client_type(), |
| 118 | + }) |
| 119 | + .map(Clone::clone) |
| 120 | +} |
0 commit comments