mod canonical_proposal;
mod msg_type;
mod sign_proposal;
pub use self::canonical_proposal::CanonicalProposal;
pub use msg_type::Type;
pub use sign_proposal::{SignProposalRequest, SignedProposalResponse};
use crate::block::{Height, Id as BlockId, Round};
use crate::chain::Id as ChainId;
use crate::consensus::State;
use crate::Signature;
use crate::Time;
use crate::{Error, Kind};
use bytes::BufMut;
use std::convert::{TryFrom, TryInto};
use tendermint_proto::types::Proposal as RawProposal;
use tendermint_proto::{Error as ProtobufError, Protobuf};
#[derive(Clone, PartialEq, Debug)]
pub struct Proposal {
pub msg_type: Type,
pub height: Height,
pub round: Round,
pub pol_round: Option<Round>,
pub block_id: Option<BlockId>,
pub timestamp: Option<Time>,
pub signature: Signature,
}
impl Protobuf<RawProposal> for Proposal {}
impl TryFrom<RawProposal> for Proposal {
type Error = Error;
fn try_from(value: RawProposal) -> Result<Self, Self::Error> {
if value.pol_round < -1 {
return Err(Kind::NegativePolRound.into());
}
let pol_round = match value.pol_round {
-1 => None,
n => Some(Round::try_from(n)?),
};
Ok(Proposal {
msg_type: value.r#type.try_into()?,
height: value.height.try_into()?,
round: value.round.try_into()?,
pol_round,
block_id: value.block_id.map(TryInto::try_into).transpose()?,
timestamp: value.timestamp.map(TryInto::try_into).transpose()?,
signature: value.signature.try_into()?,
})
}
}
impl From<Proposal> for RawProposal {
fn from(value: Proposal) -> Self {
RawProposal {
r#type: value.msg_type.into(),
height: value.height.into(),
round: value.round.into(),
pol_round: value.pol_round.map_or(-1, Into::into),
block_id: value.block_id.map(Into::into),
timestamp: value.timestamp.map(Into::into),
signature: value.signature.into(),
}
}
}
impl Proposal {
pub fn to_signable_bytes<B>(
&self,
chain_id: ChainId,
sign_bytes: &mut B,
) -> Result<bool, ProtobufError>
where
B: BufMut,
{
CanonicalProposal::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> {
CanonicalProposal::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: 3,
block_id: self.block_id,
}
}
}
#[cfg(test)]
mod tests {
use crate::block::parts::Header;
use crate::block::Id as BlockId;
use crate::block::{Height, Round};
use crate::chain::Id as ChainId;
use crate::hash::{Algorithm, Hash};
use crate::proposal::SignProposalRequest;
use crate::signature::{Ed25519Signature, ED25519_SIGNATURE_SIZE};
use crate::{proposal::Type, Proposal, Signature};
use chrono::{DateTime, Utc};
use std::str::FromStr;
use tendermint_proto::Protobuf;
#[test]
fn test_serialization() {
let dt = "2018-02-11T07:09:22.765Z".parse::<DateTime<Utc>>().unwrap();
let proposal = Proposal {
msg_type: Type::Proposal,
height: Height::from(12345_u32),
round: Round::from(23456_u16),
pol_round: None,
block_id: Some(BlockId {
hash: Hash::from_hex_upper(
Algorithm::Sha256,
"DEADBEEFDEADBEEFBAFBAFBAFBAFBAFADEADBEEFDEADBEEFBAFBAFBAFBAFBAFA",
)
.unwrap(),
part_set_header: Header::new(
65535,
Hash::from_hex_upper(
Algorithm::Sha256,
"0022446688AACCEE1133557799BBDDFF0022446688AACCEE1133557799BBDDFF",
)
.unwrap(),
)
.unwrap(),
}),
timestamp: Some(dt.into()),
signature: Signature::Ed25519(Ed25519Signature::new([0; ED25519_SIGNATURE_SIZE])),
};
let mut got = vec![];
let request = SignProposalRequest {
proposal,
chain_id: ChainId::from_str("test_chain_id").unwrap(),
};
let _have = request.to_signable_bytes(&mut got);
let want = vec![
136, 1, 8, 32, 17, 57, 48, 0, 0, 0, 0, 0, 0, 25, 160, 91, 0, 0, 0, 0, 0, 0, 32, 255,
255, 255, 255, 255, 255, 255, 255, 255, 1, 42, 74, 10, 32, 222, 173, 190, 239, 222,
173, 190, 239, 186, 251, 175, 186, 251, 175, 186, 250, 222, 173, 190, 239, 222, 173,
190, 239, 186, 251, 175, 186, 251, 175, 186, 250, 18, 38, 8, 255, 255, 3, 18, 32, 0,
34, 68, 102, 136, 170, 204, 238, 17, 51, 85, 119, 153, 187, 221, 255, 0, 34, 68, 102,
136, 170, 204, 238, 17, 51, 85, 119, 153, 187, 221, 255, 50, 12, 8, 162, 216, 255, 211,
5, 16, 192, 242, 227, 236, 2, 58, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110,
95, 105, 100,
];
assert_eq!(got, want)
}
#[test]
fn test_encoding_with_empty_block_id() {
let dt = "2018-02-11T07:09:22.765Z".parse::<DateTime<Utc>>().unwrap();
let proposal = Proposal {
msg_type: Type::Proposal,
height: Height::from(12345_u32),
round: Round::from(23456_u16),
pol_round: None,
block_id: Some(BlockId {
hash: Hash::from_hex_upper(Algorithm::Sha256, "").unwrap(),
part_set_header: Header::new(
65535,
Hash::from_hex_upper(
Algorithm::Sha256,
"0022446688AACCEE1133557799BBDDFF0022446688AACCEE1133557799BBDDFF",
)
.unwrap(),
)
.unwrap(),
}),
timestamp: Some(dt.into()),
signature: Signature::Ed25519(Ed25519Signature::new([0; ED25519_SIGNATURE_SIZE])),
};
let mut got = vec![];
let request = SignProposalRequest {
proposal,
chain_id: ChainId::from_str("test_chain_id").unwrap(),
};
let _have = request.to_signable_bytes(&mut got);
let want = vec![
102, 8, 32, 17, 57, 48, 0, 0, 0, 0, 0, 0, 25, 160, 91, 0, 0, 0, 0, 0, 0, 32, 255, 255,
255, 255, 255, 255, 255, 255, 255, 1, 42, 40, 18, 38, 8, 255, 255, 3, 18, 32, 0, 34,
68, 102, 136, 170, 204, 238, 17, 51, 85, 119, 153, 187, 221, 255, 0, 34, 68, 102, 136,
170, 204, 238, 17, 51, 85, 119, 153, 187, 221, 255, 50, 12, 8, 162, 216, 255, 211, 5,
16, 192, 242, 227, 236, 2, 58, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110, 95,
105, 100,
];
assert_eq!(got, want)
}
#[test]
fn test_deserialization() {
let dt = "2018-02-11T07:09:22.765Z".parse::<DateTime<Utc>>().unwrap();
let proposal = Proposal {
msg_type: Type::Proposal,
height: Height::from(12345_u32),
round: Round::from(23456_u16),
timestamp: Some(dt.into()),
pol_round: None,
block_id: Some(BlockId {
hash: Hash::from_hex_upper(
Algorithm::Sha256,
"DEADBEEFDEADBEEFBAFBAFBAFBAFBAFADEADBEEFDEADBEEFBAFBAFBAFBAFBAFA",
)
.unwrap(),
part_set_header: Header::new(
65535,
Hash::from_hex_upper(
Algorithm::Sha256,
"0022446688AACCEE1133557799BBDDFF0022446688AACCEE1133557799BBDDFF",
)
.unwrap(),
)
.unwrap(),
}),
signature: Signature::Ed25519(Ed25519Signature::new([0; ED25519_SIGNATURE_SIZE])),
};
let want = SignProposalRequest {
proposal,
chain_id: ChainId::from_str("test_chain_id").unwrap(),
};
let data = vec![
10, 176, 1, 8, 32, 16, 185, 96, 24, 160, 183, 1, 32, 255, 255, 255, 255, 255, 255, 255,
255, 255, 1, 42, 74, 10, 32, 222, 173, 190, 239, 222, 173, 190, 239, 186, 251, 175,
186, 251, 175, 186, 250, 222, 173, 190, 239, 222, 173, 190, 239, 186, 251, 175, 186,
251, 175, 186, 250, 18, 38, 8, 255, 255, 3, 18, 32, 0, 34, 68, 102, 136, 170, 204, 238,
17, 51, 85, 119, 153, 187, 221, 255, 0, 34, 68, 102, 136, 170, 204, 238, 17, 51, 85,
119, 153, 187, 221, 255, 50, 12, 8, 162, 216, 255, 211, 5, 16, 192, 242, 227, 236, 2,
58, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 13, 116, 101, 115, 116, 95, 99, 104, 97, 105, 110, 95,
105, 100,
];
let have = SignProposalRequest::decode_vec(&data).unwrap();
assert_eq!(have, want);
}
}