ibc_client_tendermint_types/
misbehaviour.rs1use ibc_core_host_types::error::DecodingError;
4use ibc_core_host_types::identifiers::ClientId;
5use ibc_primitives::prelude::*;
6use ibc_proto::google::protobuf::Any;
7use ibc_proto::ibc::lightclients::tendermint::v1::Misbehaviour as RawMisbehaviour;
8use ibc_proto::Protobuf;
9use tendermint::crypto::Sha256;
10use tendermint::merkle::MerkleHash;
11
12use crate::error::TendermintClientError;
13use crate::header::Header;
14
15pub const TENDERMINT_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Misbehaviour";
16
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Misbehaviour {
21 client_id: ClientId,
22 header1: Box<Header>,
23 header2: Box<Header>,
24}
25
26impl Misbehaviour {
27 pub fn new(client_id: ClientId, header1: Header, header2: Header) -> Self {
28 Self {
29 client_id,
30 header1: Box::new(header1),
31 header2: Box::new(header2),
32 }
33 }
34
35 pub fn client_id(&self) -> &ClientId {
36 &self.client_id
37 }
38
39 pub fn header1(&self) -> &Header {
40 &self.header1
41 }
42
43 pub fn header2(&self) -> &Header {
44 &self.header2
45 }
46
47 pub fn validate_basic<H: MerkleHash + Sha256 + Default>(
48 &self,
49 ) -> Result<(), TendermintClientError> {
50 self.header1.validate_basic::<H>()?;
51 self.header2.validate_basic::<H>()?;
52
53 if self.header1.signed_header.header.chain_id != self.header2.signed_header.header.chain_id
54 {
55 return Err(TendermintClientError::MismatchedHeaderChainIds {
56 expected: self.header1.signed_header.header.chain_id.to_string(),
57 actual: self.header2.signed_header.header.chain_id.to_string(),
58 });
59 }
60
61 if self.header1.height() < self.header2.height() {
62 return Err(
63 TendermintClientError::InsufficientMisbehaviourHeaderHeight {
64 height_1: self.header1.height(),
65 height_2: self.header2.height(),
66 },
67 );
68 }
69
70 Ok(())
71 }
72}
73
74impl Protobuf<RawMisbehaviour> for Misbehaviour {}
75
76impl TryFrom<RawMisbehaviour> for Misbehaviour {
77 type Error = DecodingError;
78
79 #[allow(deprecated)]
80 fn try_from(raw: RawMisbehaviour) -> Result<Self, Self::Error> {
81 let client_id = raw.client_id.parse()?;
82
83 let header1: Header = raw
84 .header_1
85 .ok_or_else(|| DecodingError::missing_raw_data("misbehaviour header1"))?
86 .try_into()?;
87
88 let header2: Header = raw
89 .header_2
90 .ok_or_else(|| DecodingError::missing_raw_data("misbehaviour header2"))?
91 .try_into()?;
92
93 Ok(Self::new(client_id, header1, header2))
94 }
95}
96
97impl From<Misbehaviour> for RawMisbehaviour {
98 fn from(value: Misbehaviour) -> Self {
99 #[allow(deprecated)]
100 RawMisbehaviour {
101 client_id: value.client_id.to_string(),
102 header_1: Some((*value.header1).into()),
103 header_2: Some((*value.header2).into()),
104 }
105 }
106}
107
108impl Protobuf<Any> for Misbehaviour {}
109
110impl TryFrom<Any> for Misbehaviour {
111 type Error = DecodingError;
112
113 fn try_from(raw: Any) -> Result<Self, Self::Error> {
114 if let TENDERMINT_MISBEHAVIOUR_TYPE_URL = raw.type_url.as_str() {
115 Protobuf::<RawMisbehaviour>::decode(raw.value.as_ref()).map_err(Into::into)
116 } else {
117 Err(DecodingError::MismatchedResourceName {
118 expected: TENDERMINT_MISBEHAVIOUR_TYPE_URL.to_string(),
119 actual: raw.type_url,
120 })
121 }
122 }
123}
124
125impl From<Misbehaviour> for Any {
126 fn from(misbehaviour: Misbehaviour) -> Self {
127 Any {
128 type_url: TENDERMINT_MISBEHAVIOUR_TYPE_URL.to_string(),
129 value: Protobuf::<RawMisbehaviour>::encode_vec(misbehaviour),
130 }
131 }
132}
133
134impl core::fmt::Display for Misbehaviour {
135 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
136 write!(
137 f,
138 "{} h1: {}-{} h2: {}-{}",
139 self.client_id,
140 self.header1.height(),
141 self.header1.trusted_height,
142 self.header2.height(),
143 self.header2.trusted_height,
144 )
145 }
146}