tendermint/block/
commit_sig.rsuse tendermint_proto::google::protobuf::Timestamp;
use crate::{account, prelude::*, Signature, Time};
const ZERO_TIMESTAMP: Timestamp = Timestamp {
seconds: -62135596800,
nanos: 0,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CommitSig {
BlockIdFlagAbsent,
BlockIdFlagCommit {
validator_address: account::Id,
timestamp: Time,
signature: Option<Signature>,
},
BlockIdFlagNil {
validator_address: account::Id,
timestamp: Time,
signature: Option<Signature>,
},
}
impl CommitSig {
pub fn validator_address(&self) -> Option<account::Id> {
match self {
Self::BlockIdFlagCommit {
validator_address, ..
} => Some(*validator_address),
Self::BlockIdFlagNil {
validator_address, ..
} => Some(*validator_address),
_ => None,
}
}
pub fn is_absent(&self) -> bool {
self == &Self::BlockIdFlagAbsent
}
pub fn is_commit(&self) -> bool {
matches!(self, Self::BlockIdFlagCommit { .. })
}
pub fn is_nil(&self) -> bool {
matches!(self, Self::BlockIdFlagNil { .. })
}
}
tendermint_pb_modules! {
use super::{CommitSig, ZERO_TIMESTAMP};
use crate::{error::Error, prelude::*, Signature};
use num_traits::ToPrimitive;
use pb::types::{BlockIdFlag, CommitSig as RawCommitSig};
impl TryFrom<RawCommitSig> for CommitSig {
type Error = Error;
fn try_from(value: RawCommitSig) -> Result<Self, Self::Error> {
if value.block_id_flag == BlockIdFlag::Absent.to_i32().unwrap() {
if value.timestamp.is_some() {
let timestamp = value.timestamp.unwrap();
if timestamp.nanos != 0 || timestamp.seconds != -62135596800 {
return Err(Error::invalid_timestamp(
"absent commitsig has non-zero timestamp".to_string(),
));
}
}
if !value.signature.is_empty() {
return Err(Error::invalid_signature(
"expected empty signature for absent commitsig".to_string(),
));
}
return Ok(CommitSig::BlockIdFlagAbsent);
}
if value.block_id_flag == BlockIdFlag::Commit.to_i32().unwrap() {
if value.signature.is_empty() {
return Err(Error::invalid_signature(
"expected non-empty signature for regular commitsig".to_string(),
));
}
if value.validator_address.is_empty() {
return Err(Error::invalid_validator_address());
}
let timestamp = value
.timestamp
.ok_or_else(Error::missing_timestamp)?
.try_into()?;
return Ok(CommitSig::BlockIdFlagCommit {
validator_address: value.validator_address.try_into()?,
timestamp,
signature: Signature::new(value.signature)?,
});
}
if value.block_id_flag == BlockIdFlag::Nil.to_i32().unwrap() {
if value.signature.is_empty() {
return Err(Error::invalid_signature(
"nil commitsig has no signature".to_string(),
));
}
if value.validator_address.is_empty() {
return Err(Error::invalid_validator_address());
}
return Ok(CommitSig::BlockIdFlagNil {
validator_address: value.validator_address.try_into()?,
timestamp: value
.timestamp
.ok_or_else(Error::missing_timestamp)?
.try_into()?,
signature: Signature::new(value.signature)?,
});
}
Err(Error::block_id_flag())
}
}
impl From<CommitSig> for RawCommitSig {
fn from(commit: CommitSig) -> RawCommitSig {
match commit {
CommitSig::BlockIdFlagAbsent => RawCommitSig {
block_id_flag: BlockIdFlag::Absent.to_i32().unwrap(),
validator_address: Vec::new(),
timestamp: Some(ZERO_TIMESTAMP),
signature: Vec::new(),
},
CommitSig::BlockIdFlagNil {
validator_address,
timestamp,
signature,
} => RawCommitSig {
block_id_flag: BlockIdFlag::Nil.to_i32().unwrap(),
validator_address: validator_address.into(),
timestamp: Some(timestamp.into()),
signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
},
CommitSig::BlockIdFlagCommit {
validator_address,
timestamp,
signature,
} => RawCommitSig {
block_id_flag: BlockIdFlag::Commit.to_i32().unwrap(),
validator_address: validator_address.into(),
timestamp: Some(timestamp.into()),
signature: signature.map(|s| s.into_bytes()).unwrap_or_default(),
},
}
}
}
#[test]
#[cfg(test)]
fn test_block_id_flag_absent_serialization() {
let absent = CommitSig::BlockIdFlagAbsent;
let raw_absent = RawCommitSig::from(absent);
let expected = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
let output = serde_json::to_string(&raw_absent).unwrap();
assert_eq!(expected, &output);
}
#[test]
#[cfg(test)]
fn test_block_id_flag_absent_deserialization() {
let json = r#"{"block_id_flag":1,"validator_address":"","timestamp":"0001-01-01T00:00:00Z","signature":""}"#;
let raw_commit_sg = serde_json::from_str::<RawCommitSig>(json).unwrap();
let commit_sig = CommitSig::try_from(raw_commit_sg).unwrap();
assert_eq!(commit_sig, CommitSig::BlockIdFlagAbsent);
}
}