mod canonical_vote;
mod power;
mod sign_vote;
mod validator_index;
use core::{fmt, str::FromStr};
use bytes::BufMut;
use serde::{Deserialize, Serialize};
use tendermint_proto::v0_38::types::{CanonicalVote as RawCanonicalVote, Vote as RawVote};
use tendermint_proto::{Error as ProtobufError, Protobuf};
pub use self::{
canonical_vote::CanonicalVote, power::Power, sign_vote::*, validator_index::ValidatorIndex,
};
use crate::{
account, block, chain::Id as ChainId, consensus::State, error::Error, hash, prelude::*,
Signature, Time,
};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(try_from = "RawVote", into = "RawVote")]
pub struct Vote {
pub vote_type: Type,
pub height: block::Height,
pub round: block::Round,
pub block_id: Option<block::Id>,
pub timestamp: Option<Time>,
pub validator_address: account::Id,
pub validator_index: ValidatorIndex,
pub signature: Option<Signature>,
pub extension: Vec<u8>,
pub extension_signature: Option<Signature>,
}
mod v0_34 {
use super::Vote;
use crate::{block, prelude::*, Error, Signature};
use tendermint_proto::v0_34::types::Vote as RawVote;
use tendermint_proto::Protobuf;
impl Protobuf<RawVote> for Vote {}
impl TryFrom<RawVote> for Vote {
type Error = Error;
fn try_from(value: RawVote) -> Result<Self, Self::Error> {
if value.timestamp.is_none() {
return Err(Error::missing_timestamp());
}
Ok(Vote {
vote_type: value.r#type.try_into()?,
height: value.height.try_into()?,
round: value.round.try_into()?,
block_id: value
.block_id
.map(TryInto::try_into)
.transpose()?
.filter(|i| i != &block::Id::default()),
timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
validator_address: value.validator_address.try_into()?,
validator_index: value.validator_index.try_into()?,
signature: Signature::new(value.signature)?,
extension: Default::default(),
extension_signature: None,
})
}
}
impl From<Vote> for RawVote {
fn from(value: Vote) -> Self {
RawVote {
r#type: value.vote_type.into(),
height: value.height.into(),
round: value.round.into(),
block_id: value.block_id.map(Into::into),
timestamp: value.timestamp.map(Into::into),
validator_address: value.validator_address.into(),
validator_index: value.validator_index.into(),
signature: value.signature.map(|s| s.into_bytes()).unwrap_or_default(),
}
}
}
}
mod v0_37 {
use super::Vote;
use crate::{block, prelude::*, Error, Signature};
use tendermint_proto::v0_37::types::Vote as RawVote;
use tendermint_proto::Protobuf;
impl Protobuf<RawVote> for Vote {}
impl TryFrom<RawVote> for Vote {
type Error = Error;
fn try_from(value: RawVote) -> Result<Self, Self::Error> {
if value.timestamp.is_none() {
return Err(Error::missing_timestamp());
}
Ok(Vote {
vote_type: value.r#type.try_into()?,
height: value.height.try_into()?,
round: value.round.try_into()?,
block_id: value
.block_id
.map(TryInto::try_into)
.transpose()?
.filter(|i| i != &block::Id::default()),
timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
validator_address: value.validator_address.try_into()?,
validator_index: value.validator_index.try_into()?,
signature: Signature::new(value.signature)?,
extension: Default::default(),
extension_signature: None,
})
}
}
impl From<Vote> for RawVote {
fn from(value: Vote) -> Self {
RawVote {
r#type: value.vote_type.into(),
height: value.height.into(),
round: value.round.into(),
block_id: value.block_id.map(Into::into),
timestamp: value.timestamp.map(Into::into),
validator_address: value.validator_address.into(),
validator_index: value.validator_index.into(),
signature: value.signature.map(|s| s.into_bytes()).unwrap_or_default(),
}
}
}
}
mod v0_38 {
use super::Vote;
use crate::{block, prelude::*, Error, Signature};
use tendermint_proto::v0_38::types::Vote as RawVote;
use tendermint_proto::Protobuf;
impl Protobuf<RawVote> for Vote {}
impl TryFrom<RawVote> for Vote {
type Error = Error;
fn try_from(value: RawVote) -> Result<Self, Self::Error> {
if value.timestamp.is_none() {
return Err(Error::missing_timestamp());
}
Ok(Vote {
vote_type: value.r#type.try_into()?,
height: value.height.try_into()?,
round: value.round.try_into()?,
block_id: value
.block_id
.map(TryInto::try_into)
.transpose()?
.filter(|i| i != &block::Id::default()),
timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
validator_address: value.validator_address.try_into()?,
validator_index: value.validator_index.try_into()?,
signature: Signature::new(value.signature)?,
extension: value.extension,
extension_signature: Signature::new(value.extension_signature)?,
})
}
}
impl From<Vote> for RawVote {
fn from(value: Vote) -> Self {
RawVote {
r#type: value.vote_type.into(),
height: value.height.into(),
round: value.round.into(),
block_id: value.block_id.map(Into::into),
timestamp: value.timestamp.map(Into::into),
validator_address: value.validator_address.into(),
validator_index: value.validator_index.into(),
signature: value.signature.map(|s| s.into_bytes()).unwrap_or_default(),
extension: value.extension,
extension_signature: value
.extension_signature
.map(|s| s.into_bytes())
.unwrap_or_default(),
}
}
}
}
impl Vote {
pub fn is_prevote(&self) -> bool {
match self.vote_type {
Type::Prevote => true,
Type::Precommit => false,
}
}
pub fn is_precommit(&self) -> bool {
match self.vote_type {
Type::Precommit => true,
Type::Prevote => false,
}
}
pub fn header_hash(&self) -> Option<hash::Hash> {
self.block_id.map(|b| b.hash)
}
pub fn to_signable_bytes<B>(
&self,
chain_id: ChainId,
sign_bytes: &mut B,
) -> Result<bool, ProtobufError>
where
B: BufMut,
{
let canonical = CanonicalVote::new(self.clone(), chain_id);
Protobuf::<RawCanonicalVote>::encode_length_delimited(canonical, sign_bytes)?;
Ok(true)
}
pub fn into_signable_vec(self, chain_id: ChainId) -> Vec<u8> {
let canonical = CanonicalVote::new(self, chain_id);
Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(canonical)
}
#[deprecated(
since = "0.17.0",
note = "This seems unnecessary, please raise it to the team, if you need it."
)]
pub fn consensus_state(&self) -> State {
State {
height: self.height,
round: self.round,
step: 6,
block_id: self.block_id,
}
}
}
pub struct SignedVote {
vote: CanonicalVote,
validator_address: account::Id,
signature: Signature,
}
impl SignedVote {
pub fn new(
vote: Vote,
chain_id: ChainId,
validator_address: account::Id,
signature: Signature,
) -> SignedVote {
let canonical_vote = CanonicalVote::new(vote, chain_id);
SignedVote {
vote: canonical_vote,
signature,
validator_address,
}
}
pub fn from_vote(vote: Vote, chain_id: ChainId) -> Option<Self> {
let validator_address = vote.validator_address;
vote.signature
.clone()
.map(|signature| Self::new(vote, chain_id, validator_address, signature))
}
pub fn validator_id(&self) -> account::Id {
self.validator_address
}
pub fn sign_bytes(&self) -> Vec<u8> {
Protobuf::<RawCanonicalVote>::encode_length_delimited_vec(self.vote.clone())
}
pub fn signature(&self) -> &Signature {
&self.signature
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
Prevote = 1,
Precommit = 2,
}
impl Protobuf<i32> for Type {}
impl TryFrom<i32> for Type {
type Error = Error;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
1 => Ok(Type::Prevote),
2 => Ok(Type::Precommit),
_ => Err(Error::invalid_message_type()),
}
}
}
impl From<Type> for i32 {
fn from(value: Type) -> Self {
value as i32
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let id = match self {
Type::Prevote => "Prevote",
Type::Precommit => "Precommit",
};
write!(f, "{id}")
}
}
impl FromStr for Type {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Prevote" => Ok(Self::Prevote),
"Precommit" => Ok(Self::Precommit),
_ => Err(Error::invalid_message_type()),
}
}
}