#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
pub use wbg::*;
#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
mod wbg {
use tendermint::evidence::{ConflictingBlock, Evidence};
use tendermint::vote::{Type as VoteType, Vote};
use wasm_bindgen::prelude::*;
use crate::block::{JsBlockId, JsSignedHeader};
use crate::signature::JsSignature;
use crate::validator_set::{JsValidatorInfo, JsValidatorSet};
#[derive(Clone, Debug)]
#[wasm_bindgen(getter_with_clone, js_name = "Vote")]
pub struct JsVote {
pub vote_type: JsVoteType,
pub height: u64,
pub round: u32,
pub block_id: Option<JsBlockId>,
pub timestamp: Option<String>,
pub validator_address: String,
pub validator_index: u32,
pub signature: Option<JsSignature>,
pub extension: Vec<u8>,
pub extension_signature: Option<JsSignature>,
}
impl From<Vote> for JsVote {
fn from(value: Vote) -> Self {
JsVote {
vote_type: value.vote_type.into(),
height: value.height.value(),
round: value.round.into(),
block_id: value.block_id.map(Into::into),
timestamp: value.timestamp.map(|ts| ts.to_rfc3339()),
validator_address: value.validator_address.to_string(),
validator_index: value.validator_index.into(),
signature: value.signature.map(Into::into),
extension: value.extension,
extension_signature: value.extension_signature.map(Into::into),
}
}
}
#[derive(Clone, Copy, Debug)]
#[wasm_bindgen(js_name = "VoteType")]
pub enum JsVoteType {
Prevote,
Precommit,
}
impl From<VoteType> for JsVoteType {
fn from(value: VoteType) -> Self {
match value {
VoteType::Prevote => JsVoteType::Prevote,
VoteType::Precommit => JsVoteType::Precommit,
}
}
}
#[derive(Clone, Debug)]
#[wasm_bindgen(getter_with_clone, js_name = "DuplicateVoteEvidence")]
pub struct JsDuplicateVoteEvidence {
pub vote_a: JsVote,
pub vote_b: JsVote,
pub total_voting_power: u64,
pub validator_power: u64,
pub timestamp: String,
}
#[derive(Clone, Debug)]
#[wasm_bindgen(getter_with_clone, js_name = "ConflictingBlock")]
pub struct JsConflictingBlock {
pub signed_header: JsSignedHeader,
pub validator_set: JsValidatorSet,
}
impl From<ConflictingBlock> for JsConflictingBlock {
fn from(value: ConflictingBlock) -> Self {
JsConflictingBlock {
signed_header: value.signed_header.into(),
validator_set: value.validator_set.into(),
}
}
}
#[derive(Clone, Debug)]
#[wasm_bindgen(getter_with_clone, js_name = "LightClientAttackEvidence")]
pub struct JsLightClientAttackEvidence {
pub conflicting_block: JsConflictingBlock,
pub common_height: u64,
pub byzantine_validators: Vec<JsValidatorInfo>,
pub total_voting_power: u64,
pub timestamp: String,
}
#[wasm_bindgen(js_name = "AttackType")]
pub enum JsAttackType {
DuplicateVote,
LightClient,
}
#[wasm_bindgen(js_name = Evidence)]
pub struct JsEvidence(Evidence);
#[wasm_bindgen]
impl JsEvidence {
pub fn attack_type(&self) -> JsAttackType {
match &self.0 {
Evidence::DuplicateVote(_) => JsAttackType::DuplicateVote,
Evidence::LightClientAttack(_) => JsAttackType::LightClient,
}
}
#[wasm_bindgen(getter)]
pub fn duplicate_vote_evidence(&self) -> Option<JsDuplicateVoteEvidence> {
let Evidence::DuplicateVote(e) = &self.0 else {
return None;
};
Some(JsDuplicateVoteEvidence {
vote_a: e.vote_a.clone().into(),
vote_b: e.vote_b.clone().into(),
total_voting_power: e.total_voting_power.into(),
validator_power: e.validator_power.into(),
timestamp: e.timestamp.to_rfc3339(),
})
}
#[wasm_bindgen(getter)]
pub fn light_client_attack_evidence(&self) -> Option<JsLightClientAttackEvidence> {
let Evidence::LightClientAttack(e) = &self.0 else {
return None;
};
Some(JsLightClientAttackEvidence {
conflicting_block: e.conflicting_block.clone().into(),
common_height: e.common_height.value(),
byzantine_validators: e
.byzantine_validators
.iter()
.cloned()
.map(Into::into)
.collect(),
total_voting_power: e.total_voting_power.into(),
timestamp: e.timestamp.to_rfc3339(),
})
}
}
impl From<Evidence> for JsEvidence {
fn from(value: Evidence) -> Self {
JsEvidence(value)
}
}
}
#[cfg(feature = "uniffi")]
pub mod uniffi_types {
use tendermint::evidence::{
ConflictingBlock as TendermintConfliclingBlock, DuplicateVoteEvidence,
Evidence as TendermintEvidence, LightClientAttackEvidence, List as TendermintEvidenceList,
};
use tendermint::vote::{Type as VoteType, Vote as TendermintVote};
use uniffi::{Enum, Record};
use crate::block::header::uniffi_types::SignedHeader;
use crate::block::uniffi_types::{BlockHeight, BlockId};
use crate::error::UniffiConversionError;
use crate::signature::uniffi_types::Signature;
use crate::state::UniffiAccountId;
use crate::uniffi_types::Time;
use crate::validator_set::uniffi_types::{ValidatorInfo, ValidatorSet};
#[derive(Record)]
pub struct ConflictingBlock {
pub signed_header: SignedHeader,
pub validator_set: ValidatorSet,
}
impl TryFrom<TendermintConfliclingBlock> for ConflictingBlock {
type Error = UniffiConversionError;
fn try_from(value: TendermintConfliclingBlock) -> Result<Self, Self::Error> {
Ok(ConflictingBlock {
signed_header: value.signed_header.try_into()?,
validator_set: value.validator_set.into(),
})
}
}
impl TryFrom<ConflictingBlock> for TendermintConfliclingBlock {
type Error = UniffiConversionError;
fn try_from(value: ConflictingBlock) -> Result<Self, Self::Error> {
Ok(TendermintConfliclingBlock {
signed_header: value.signed_header.try_into()?,
validator_set: value.validator_set.try_into()?,
})
}
}
uniffi::custom_type!(TendermintEvidenceList, Vec<Evidence>, {
remote,
try_lift: |value| {
let evidence : Vec<_> = value.into_iter().map(|e| e.try_into()).collect::<Result<_, _>>()?;
Ok(TendermintEvidenceList::new(evidence))
},
lower: |value| value.into_vec().into_iter().map(|e| e.try_into()).collect::<Result<_,_>>().expect("valid timestamps")
});
#[allow(clippy::large_enum_variant)]
#[derive(Enum)]
pub enum Evidence {
DuplicateVote {
vote_a: Vote,
vote_b: Vote,
total_voting_power: u64,
validator_power: u64,
timestamp: Time,
},
LightClientAttack {
conflicting_block: ConflictingBlock,
common_height: u64,
byzantine_validators: Vec<ValidatorInfo>,
total_voting_power: u64,
timestamp: Time,
},
}
impl TryFrom<TendermintEvidence> for Evidence {
type Error = UniffiConversionError;
fn try_from(value: TendermintEvidence) -> Result<Self, Self::Error> {
Ok(match value {
TendermintEvidence::DuplicateVote(e) => Evidence::DuplicateVote {
vote_a: e.vote_a.try_into()?,
vote_b: e.vote_b.try_into()?,
total_voting_power: e.total_voting_power.value(),
validator_power: e.validator_power.value(),
timestamp: e.timestamp.try_into()?,
},
TendermintEvidence::LightClientAttack(e) => Evidence::LightClientAttack {
conflicting_block: e.conflicting_block.try_into()?,
common_height: e.common_height.into(),
byzantine_validators: e
.byzantine_validators
.into_iter()
.map(|v| v.into())
.collect(),
total_voting_power: e.total_voting_power.value(),
timestamp: e.timestamp.try_into()?,
},
})
}
}
impl TryFrom<Evidence> for TendermintEvidence {
type Error = UniffiConversionError;
fn try_from(value: Evidence) -> Result<Self, Self::Error> {
match value {
Evidence::DuplicateVote {
vote_a,
vote_b,
total_voting_power,
validator_power,
timestamp,
} => {
let evidence = DuplicateVoteEvidence {
vote_a: vote_a.try_into()?,
vote_b: vote_b.try_into()?,
total_voting_power: total_voting_power
.try_into()
.map_err(|_| UniffiConversionError::InvalidVotingPower)?,
validator_power: validator_power
.try_into()
.map_err(|_| UniffiConversionError::InvalidVotingPower)?,
timestamp: timestamp.try_into()?,
};
Ok(TendermintEvidence::DuplicateVote(Box::new(evidence)))
}
Evidence::LightClientAttack {
conflicting_block,
common_height,
byzantine_validators,
total_voting_power,
timestamp,
} => {
let evidence = LightClientAttackEvidence {
conflicting_block: conflicting_block.try_into()?,
common_height: common_height
.try_into()
.map_err(|_| UniffiConversionError::HeaderHeightOutOfRange)?,
byzantine_validators: byzantine_validators
.into_iter()
.map(|v| v.try_into())
.collect::<Result<_, _>>()?,
total_voting_power: total_voting_power
.try_into()
.map_err(|_| UniffiConversionError::InvalidVotingPower)?,
timestamp: timestamp.try_into()?,
};
Ok(TendermintEvidence::LightClientAttack(Box::new(evidence)))
}
}
}
}
#[uniffi::remote(Enum)]
#[repr(u8)]
pub enum VoteType {
Prevote = 1,
Precommit = 2,
}
#[derive(Record)]
pub struct Vote {
pub vote_type: VoteType,
pub height: BlockHeight,
pub round: u32,
pub block_id: Option<BlockId>,
pub timestamp: Option<Time>,
pub validator_address: UniffiAccountId,
pub validator_index: u32,
pub signature: Option<Signature>,
pub extension: Vec<u8>,
pub extension_signature: Option<Signature>,
}
impl TryFrom<Vote> for TendermintVote {
type Error = UniffiConversionError;
fn try_from(value: Vote) -> Result<Self, Self::Error> {
Ok(TendermintVote {
vote_type: value.vote_type,
height: value.height.try_into()?,
round: value
.round
.try_into()
.map_err(|_| UniffiConversionError::InvalidRoundIndex)?,
block_id: value.block_id.map(TryInto::try_into).transpose()?,
timestamp: value.timestamp.map(TryInto::try_into).transpose()?,
validator_address: value.validator_address.try_into()?,
validator_index: value
.validator_index
.try_into()
.map_err(|_| UniffiConversionError::InvalidValidatorIndex)?,
signature: value.signature.map(TryInto::try_into).transpose()?,
extension: value.extension,
extension_signature: value
.extension_signature
.map(TryInto::try_into)
.transpose()?,
})
}
}
impl TryFrom<TendermintVote> for Vote {
type Error = UniffiConversionError;
fn try_from(value: TendermintVote) -> Result<Self, Self::Error> {
Ok(Vote {
vote_type: value.vote_type,
height: value.height.into(),
round: value.round.value(),
block_id: value.block_id.map(Into::into),
timestamp: value.timestamp.map(TryInto::try_into).transpose()?,
validator_address: value.validator_address.into(),
validator_index: value.validator_index.value(),
signature: value.signature.map(Into::into),
extension: value.extension,
extension_signature: value.extension_signature.map(Into::into),
})
}
}
uniffi::custom_type!(TendermintVote, Vote, {
remote,
try_lift: |value| Ok(value.try_into()?),
lower: |value| value.try_into().expect("valid tendermint time")
});
}