mod canonical_vote;
mod power;
mod sign_vote;
mod validator_index;
pub use self::canonical_vote::CanonicalVote;
pub use self::power::Power;
pub use self::sign_vote::*;
pub use self::validator_index::ValidatorIndex;
use crate::chain::Id as ChainId;
use crate::consensus::State;
use crate::hash;
use crate::{account, block, Signature, Time};
use crate::{Error, Kind::*};
use bytes::BufMut;
use ed25519::Signature as ed25519Signature;
use ed25519::SIGNATURE_LENGTH as ed25519SignatureLength;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::fmt;
use tendermint_proto::types::Vote as RawVote;
use tendermint_proto::{Error as ProtobufError, Protobuf};
use crate::signature::Signature::Ed25519;
use std::str::FromStr;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[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: Signature,
}
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(NoTimestamp.into());
}
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(TryInto::try_into).transpose()?,
validator_address: value.validator_address.try_into()?,
validator_index: value.validator_index.try_into()?,
signature: value.signature.try_into()?,
})
}
}
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.into(),
}
}
}
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.clone().map(|b| b.hash)
}
pub fn to_signable_bytes<B>(
&self,
chain_id: ChainId,
sign_bytes: &mut B,
) -> Result<bool, ProtobufError>
where
B: BufMut,
{
CanonicalVote::new(self.clone(), chain_id).encode_length_delimited(sign_bytes)?;
Ok(true)
}
pub fn to_signable_vec(&self, chain_id: ChainId) -> Result<Vec<u8>, ProtobufError> {
CanonicalVote::new(self.clone(), chain_id).encode_length_delimited_vec()
}
#[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,
}
}
}
impl Default for Vote {
fn default() -> Self {
Vote {
vote_type: Type::Prevote,
height: Default::default(),
round: Default::default(),
block_id: None,
timestamp: Some(Time::unix_epoch()),
validator_address: account::Id::new([0; account::LENGTH]),
validator_index: ValidatorIndex::try_from(0_i32).unwrap(),
signature: Ed25519(ed25519Signature::new([0; ed25519SignatureLength])),
}
}
}
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 validator_id(&self) -> account::Id {
self.validator_address
}
pub fn sign_bytes(&self) -> Vec<u8> {
self.vote.encode_length_delimited_vec().unwrap()
}
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(InvalidMessageType.into()),
}
}
}
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(InvalidMessageType.into()),
}
}
}