use core::{
convert::{TryFrom, TryInto},
slice,
};
use serde::{Deserialize, Serialize};
use tendermint_proto::{
google::protobuf::Duration as RawDuration,
types::{
evidence::{Sum as RawSum, Sum},
DuplicateVoteEvidence as RawDuplicateVoteEvidence, Evidence as RawEvidence,
EvidenceList as RawEvidenceList, EvidenceParams as RawEvidenceParams,
},
Protobuf,
};
use crate::{
block::signed_header::SignedHeader, error::Error, prelude::*, serializers, vote::Power, Time,
Vote,
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "RawEvidence", into = "RawEvidence")] #[allow(clippy::large_enum_variant)]
pub enum Evidence {
DuplicateVote(DuplicateVoteEvidence),
ConflictingHeaders(Box<ConflictingHeadersEvidence>),
LightClientAttackEvidence,
}
impl TryFrom<RawEvidence> for Evidence {
type Error = Error;
fn try_from(value: RawEvidence) -> Result<Self, Self::Error> {
match value.sum.ok_or_else(Error::invalid_evidence)? {
Sum::DuplicateVoteEvidence(ev) => Ok(Evidence::DuplicateVote(ev.try_into()?)),
Sum::LightClientAttackEvidence(_ev) => Ok(Evidence::LightClientAttackEvidence),
}
}
}
impl From<Evidence> for RawEvidence {
fn from(value: Evidence) -> Self {
match value {
Evidence::DuplicateVote(ev) => RawEvidence {
sum: Some(RawSum::DuplicateVoteEvidence(ev.into())),
},
Evidence::ConflictingHeaders(_ev) => RawEvidence { sum: None }, Evidence::LightClientAttackEvidence => RawEvidence { sum: None }, }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DuplicateVoteEvidence {
pub vote_a: Vote,
pub vote_b: Vote,
pub total_voting_power: Power,
pub validator_power: Power,
pub timestamp: Time,
}
impl TryFrom<RawDuplicateVoteEvidence> for DuplicateVoteEvidence {
type Error = Error;
fn try_from(value: RawDuplicateVoteEvidence) -> Result<Self, Self::Error> {
Ok(Self {
vote_a: value
.vote_a
.ok_or_else(Error::missing_evidence)?
.try_into()?,
vote_b: value
.vote_b
.ok_or_else(Error::missing_evidence)?
.try_into()?,
total_voting_power: value.total_voting_power.try_into()?,
validator_power: value.validator_power.try_into()?,
timestamp: value
.timestamp
.ok_or_else(Error::missing_timestamp)?
.try_into()?,
})
}
}
impl From<DuplicateVoteEvidence> for RawDuplicateVoteEvidence {
fn from(value: DuplicateVoteEvidence) -> Self {
RawDuplicateVoteEvidence {
vote_a: Some(value.vote_a.into()),
vote_b: Some(value.vote_b.into()),
total_voting_power: value.total_voting_power.into(),
validator_power: value.total_voting_power.into(),
timestamp: Some(value.timestamp.into()),
}
}
}
impl DuplicateVoteEvidence {
pub fn new(vote_a: Vote, vote_b: Vote) -> Result<Self, Error> {
if vote_a.height != vote_b.height {
return Err(Error::invalid_evidence());
}
Ok(Self {
vote_a,
vote_b,
total_voting_power: Default::default(),
validator_power: Default::default(),
timestamp: Time::unix_epoch(),
})
}
pub fn votes(&self) -> (&Vote, &Vote) {
(&self.vote_a, &self.vote_b)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConflictingHeadersEvidence {
pub h1: SignedHeader,
pub h2: SignedHeader,
}
impl ConflictingHeadersEvidence {
pub fn new(h1: SignedHeader, h2: SignedHeader) -> Self {
Self { h1, h2 }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "RawEvidenceList", into = "RawEvidenceList")]
pub struct Data {
evidence: Option<Vec<Evidence>>,
}
impl TryFrom<RawEvidenceList> for Data {
type Error = Error;
fn try_from(value: RawEvidenceList) -> Result<Self, Self::Error> {
if value.evidence.is_empty() {
return Ok(Self { evidence: None });
}
let evidence: Result<Vec<Evidence>, Error> =
value.evidence.into_iter().map(TryInto::try_into).collect();
Ok(Self {
evidence: Some(evidence?),
})
}
}
impl From<Data> for RawEvidenceList {
fn from(value: Data) -> Self {
RawEvidenceList {
evidence: value
.evidence
.unwrap_or_default()
.into_iter()
.map(Into::into)
.collect(),
}
}
}
impl Data {
pub fn new<I>(into_evidence: I) -> Data
where
I: Into<Vec<Evidence>>,
{
Data {
evidence: Some(into_evidence.into()),
}
}
pub fn into_vec(self) -> Vec<Evidence> {
self.iter().cloned().collect()
}
pub fn iter(&self) -> slice::Iter<'_, Evidence> {
self.as_ref().iter()
}
}
impl AsRef<[Evidence]> for Data {
fn as_ref(&self) -> &[Evidence] {
self.evidence.as_deref().unwrap_or(&[])
}
}
#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
pub struct Params {
#[serde(with = "serializers::from_str")]
pub max_age_num_blocks: u64,
pub max_age_duration: Duration,
#[serde(with = "serializers::from_str", default)]
pub max_bytes: i64,
}
impl Protobuf<RawEvidenceParams> for Params {}
impl TryFrom<RawEvidenceParams> for Params {
type Error = Error;
fn try_from(value: RawEvidenceParams) -> Result<Self, Self::Error> {
Ok(Self {
max_age_num_blocks: value
.max_age_num_blocks
.try_into()
.map_err(Error::negative_max_age_num)?,
max_age_duration: value
.max_age_duration
.ok_or_else(Error::missing_max_age_duration)?
.try_into()?,
max_bytes: value.max_bytes,
})
}
}
impl From<Params> for RawEvidenceParams {
fn from(value: Params) -> Self {
Self {
max_age_num_blocks: value.max_age_num_blocks.try_into().unwrap(),
max_age_duration: Some(value.max_age_duration.into()),
max_bytes: value.max_bytes,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Duration(#[serde(with = "serializers::time_duration")] pub core::time::Duration);
impl From<Duration> for core::time::Duration {
fn from(d: Duration) -> core::time::Duration {
d.0
}
}
impl Protobuf<RawDuration> for Duration {}
impl TryFrom<RawDuration> for Duration {
type Error = Error;
fn try_from(value: RawDuration) -> Result<Self, Self::Error> {
Ok(Self(core::time::Duration::new(
value.seconds.try_into().map_err(Error::integer_overflow)?,
value.nanos.try_into().map_err(Error::integer_overflow)?,
)))
}
}
impl From<Duration> for RawDuration {
fn from(value: Duration) -> Self {
Self {
seconds: value.0.as_secs() as i64,
nanos: value.0.subsec_nanos() as i32,
}
}
}