1use core::{
4 convert::{TryFrom, TryInto},
5 slice,
6};
7
8use celestia_tendermint_proto::google::protobuf::Duration as RawDuration;
9use celestia_tendermint_proto::Protobuf;
10use serde::{Deserialize, Serialize};
11
12use crate::{
13 block::{signed_header::SignedHeader, Height},
14 error::Error,
15 prelude::*,
16 serializers, validator,
17 vote::Power,
18 Time, Vote,
19};
20
21#[derive(Clone, Debug, PartialEq, Eq)]
23pub enum Evidence {
24 DuplicateVote(Box<DuplicateVoteEvidence>),
26
27 LightClientAttack(Box<LightClientAttackEvidence>),
29}
30
31impl From<LightClientAttackEvidence> for Evidence {
32 fn from(ev: LightClientAttackEvidence) -> Self {
33 Self::LightClientAttack(Box::new(ev))
34 }
35}
36
37impl From<DuplicateVoteEvidence> for Evidence {
38 fn from(ev: DuplicateVoteEvidence) -> Self {
39 Self::DuplicateVote(Box::new(ev))
40 }
41}
42
43#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct DuplicateVoteEvidence {
46 pub vote_a: Vote,
47 pub vote_b: Vote,
48 pub total_voting_power: Power,
49 pub validator_power: Power,
50 pub timestamp: Time,
51}
52
53impl DuplicateVoteEvidence {
54 pub fn new(vote_a: Vote, vote_b: Vote) -> Result<Self, Error> {
56 if vote_a.height != vote_b.height {
57 return Err(Error::invalid_evidence());
58 }
59
60 Ok(Self {
62 vote_a,
63 vote_b,
64 total_voting_power: Default::default(),
65 validator_power: Default::default(),
66 timestamp: Time::unix_epoch(),
67 })
68 }
69
70 pub fn votes(&self) -> (&Vote, &Vote) {
72 (&self.vote_a, &self.vote_b)
73 }
74}
75
76#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
78pub struct ConflictingBlock {
79 pub signed_header: SignedHeader,
80 pub validator_set: validator::Set,
81}
82
83#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
85pub struct LightClientAttackEvidence {
86 pub conflicting_block: ConflictingBlock,
87 pub common_height: Height,
88 pub byzantine_validators: Vec<validator::Info>,
89 pub total_voting_power: Power,
90 pub timestamp: Time,
91}
92
93#[derive(Clone, Debug, Default, PartialEq, Eq)]
97pub struct List(Vec<Evidence>);
98
99impl List {
100 pub fn new<I>(into_evidence: I) -> List
102 where
103 I: Into<Vec<Evidence>>,
104 {
105 List(into_evidence.into())
106 }
107
108 pub fn into_vec(self) -> Vec<Evidence> {
110 self.0
111 }
112
113 pub fn iter(&self) -> slice::Iter<'_, Evidence> {
115 self.0.iter()
116 }
117}
118
119impl AsRef<[Evidence]> for List {
120 fn as_ref(&self) -> &[Evidence] {
121 &self.0
122 }
123}
124
125#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
129pub struct Params {
130 #[serde(with = "serializers::from_str")]
132 pub max_age_num_blocks: u64,
133
134 pub max_age_duration: Duration,
141
142 #[serde(with = "serializers::from_str", default)]
146 pub max_bytes: i64,
147}
148
149tendermint_pb_modules! {
154 use pb::types as raw;
155
156 use super::{List, LightClientAttackEvidence, DuplicateVoteEvidence, ConflictingBlock, Evidence, Params};
157 use crate::{error::Error, prelude::*};
158
159 impl Protobuf<raw::Evidence> for Evidence {}
160
161 impl TryFrom<raw::Evidence> for Evidence {
162 type Error = Error;
163
164 fn try_from(value: raw::Evidence) -> Result<Self, Self::Error> {
165 match value.sum.ok_or_else(Error::invalid_evidence)? {
166 raw::evidence::Sum::DuplicateVoteEvidence(ev) => {
167 Ok(Evidence::DuplicateVote(Box::new(ev.try_into()?)))
168 },
169 raw::evidence::Sum::LightClientAttackEvidence(ev) => {
170 Ok(Evidence::LightClientAttack(Box::new(ev.try_into()?)))
171 },
172 }
173 }
174 }
175
176 impl From<Evidence> for raw::Evidence {
177 fn from(value: Evidence) -> Self {
178 match value {
179 Evidence::DuplicateVote(ev) => raw::Evidence {
180 sum: Some(raw::evidence::Sum::DuplicateVoteEvidence((*ev).into())),
181 },
182 Evidence::LightClientAttack(ev) => raw::Evidence {
183 sum: Some(raw::evidence::Sum::LightClientAttackEvidence((*ev).into())),
184 },
185 }
186 }
187 }
188
189 impl Protobuf<raw::DuplicateVoteEvidence> for DuplicateVoteEvidence {}
190
191 impl TryFrom<raw::DuplicateVoteEvidence> for DuplicateVoteEvidence {
192 type Error = Error;
193
194 fn try_from(value: raw::DuplicateVoteEvidence) -> Result<Self, Self::Error> {
195 Ok(Self {
196 vote_a: value
197 .vote_a
198 .ok_or_else(Error::missing_evidence)?
199 .try_into()?,
200 vote_b: value
201 .vote_b
202 .ok_or_else(Error::missing_evidence)?
203 .try_into()?,
204 total_voting_power: value.total_voting_power.try_into()?,
205 validator_power: value.validator_power.try_into()?,
206 timestamp: value
207 .timestamp
208 .ok_or_else(Error::missing_timestamp)?
209 .try_into()?,
210 })
211 }
212 }
213
214 impl From<DuplicateVoteEvidence> for raw::DuplicateVoteEvidence {
215 fn from(value: DuplicateVoteEvidence) -> Self {
216 raw::DuplicateVoteEvidence {
217 vote_a: Some(value.vote_a.into()),
218 vote_b: Some(value.vote_b.into()),
219 total_voting_power: value.total_voting_power.into(),
220 validator_power: value.total_voting_power.into(),
221 timestamp: Some(value.timestamp.into()),
222 }
223 }
224 }
225
226 impl Protobuf<raw::LightBlock> for ConflictingBlock {}
227
228 impl TryFrom<raw::LightBlock> for ConflictingBlock {
229 type Error = Error;
230
231 fn try_from(value: raw::LightBlock) -> Result<Self, Self::Error> {
232 Ok(ConflictingBlock {
233 signed_header: value
234 .signed_header
235 .ok_or_else(Error::missing_evidence)?
236 .try_into()?,
237 validator_set: value
238 .validator_set
239 .ok_or_else(Error::missing_evidence)?
240 .try_into()?,
241 })
242 }
243 }
244
245 impl From<ConflictingBlock> for raw::LightBlock {
246 fn from(value: ConflictingBlock) -> Self {
247 raw::LightBlock {
248 signed_header: Some(value.signed_header.into()),
249 validator_set: Some(value.validator_set.into()),
250 }
251 }
252 }
253
254 impl Protobuf<raw::LightClientAttackEvidence> for LightClientAttackEvidence {}
255
256 impl TryFrom<raw::LightClientAttackEvidence> for LightClientAttackEvidence {
257 type Error = Error;
258
259 fn try_from(ev: raw::LightClientAttackEvidence) -> Result<Self, Self::Error> {
260 Ok(LightClientAttackEvidence {
261 conflicting_block: ev
262 .conflicting_block
263 .ok_or_else(Error::missing_evidence)?
264 .try_into()?,
265 common_height: ev.common_height.try_into()?,
266 byzantine_validators: ev
267 .byzantine_validators
268 .into_iter()
269 .map(TryInto::try_into)
270 .collect::<Result<Vec<_>, _>>()?,
271 total_voting_power: ev.total_voting_power.try_into()?,
272 timestamp: ev
273 .timestamp
274 .ok_or_else(Error::missing_timestamp)?
275 .try_into()?,
276 })
277 }
278 }
279
280 impl From<LightClientAttackEvidence> for raw::LightClientAttackEvidence {
281 fn from(ev: LightClientAttackEvidence) -> Self {
282 raw::LightClientAttackEvidence {
283 conflicting_block: Some(ev.conflicting_block.into()),
284 common_height: ev.common_height.into(),
285 byzantine_validators: ev
286 .byzantine_validators
287 .into_iter()
288 .map(Into::into)
289 .collect(),
290 total_voting_power: ev.total_voting_power.into(),
291 timestamp: Some(ev.timestamp.into()),
292 }
293 }
294 }
295
296 impl Protobuf<raw::EvidenceList> for List {}
297
298 impl TryFrom<raw::EvidenceList> for List {
299 type Error = Error;
300 fn try_from(value: raw::EvidenceList) -> Result<Self, Self::Error> {
301 let evidence = value
302 .evidence
303 .into_iter()
304 .map(TryInto::try_into)
305 .collect::<Result<Vec<_>, _>>()?;
306 Ok(Self(evidence))
307 }
308 }
309
310 impl From<List> for raw::EvidenceList {
311 fn from(value: List) -> Self {
312 raw::EvidenceList {
313 evidence: value.0.into_iter().map(Into::into).collect(),
314 }
315 }
316 }
317
318 impl Protobuf<raw::EvidenceParams> for Params {}
319
320 impl TryFrom<raw::EvidenceParams> for Params {
321 type Error = Error;
322
323 fn try_from(value: raw::EvidenceParams) -> Result<Self, Self::Error> {
324 Ok(Self {
325 max_age_num_blocks: value
326 .max_age_num_blocks
327 .try_into()
328 .map_err(Error::negative_max_age_num)?,
329 max_age_duration: value
330 .max_age_duration
331 .ok_or_else(Error::missing_max_age_duration)?
332 .try_into()?,
333 max_bytes: value.max_bytes,
334 })
335 }
336 }
337
338 impl From<Params> for raw::EvidenceParams {
339 fn from(value: Params) -> Self {
340 Self {
341 max_age_num_blocks: value.max_age_num_blocks.try_into().unwrap(),
343 max_age_duration: Some(value.max_age_duration.into()),
344 max_bytes: value.max_bytes,
345 }
346 }
347 }
348}
349
350#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
356pub struct Duration(#[serde(with = "serializers::time_duration")] pub core::time::Duration);
357
358impl From<Duration> for core::time::Duration {
359 fn from(d: Duration) -> core::time::Duration {
360 d.0
361 }
362}
363
364impl Protobuf<RawDuration> for Duration {}
365
366impl TryFrom<RawDuration> for Duration {
367 type Error = Error;
368
369 fn try_from(value: RawDuration) -> Result<Self, Self::Error> {
370 Ok(Self(core::time::Duration::new(
371 value.seconds.try_into().map_err(Error::integer_overflow)?,
372 value.nanos.try_into().map_err(Error::integer_overflow)?,
373 )))
374 }
375}
376
377impl From<Duration> for RawDuration {
378 fn from(value: Duration) -> Self {
379 Self {
381 seconds: value.0.as_secs() as i64,
382 nanos: value.0.subsec_nanos() as i32,
383 }
384 }
385}