use super::{
block_id::{BlockId, CanonicalBlockId, CanonicalPartSetHeader},
remote_error::RemoteError,
signature::SignableMsg,
time::TimeMsg,
validate::{ConsensusMessage, ValidationError, ValidationErrorKind::*},
SignedMsgType,
};
use crate::{
block::{self, ParseId},
chain, consensus,
error::Error,
};
use bytes::BufMut;
use prost::{error::EncodeError, Message};
use signatory::{ed25519, Signature};
const VALIDATOR_ADDR_SIZE: usize = 20;
#[derive(Clone, PartialEq, Message)]
pub struct Vote {
#[prost(uint32, tag = "1")]
pub vote_type: u32,
#[prost(int64)]
pub height: i64,
#[prost(int64)]
pub round: i64,
#[prost(message)]
pub block_id: Option<BlockId>,
#[prost(message)]
pub timestamp: Option<TimeMsg>,
#[prost(bytes)]
pub validator_address: Vec<u8>,
#[prost(int64)]
pub validator_index: i64,
#[prost(bytes)]
pub signature: Vec<u8>,
}
impl Vote {
fn msg_type(&self) -> Option<SignedMsgType> {
if self.vote_type == SignedMsgType::PreVote.to_u32() {
Some(SignedMsgType::PreVote)
} else if self.vote_type == SignedMsgType::PreCommit.to_u32() {
Some(SignedMsgType::PreCommit)
} else {
None
}
}
}
impl block::ParseHeight for Vote {
fn parse_block_height(&self) -> Result<block::Height, Error> {
block::Height::try_from_i64(self.height)
}
}
pub const AMINO_NAME: &str = "tendermint/remotesigner/SignVoteRequest";
#[derive(Clone, PartialEq, Message)]
#[amino_name = "tendermint/remotesigner/SignVoteRequest"]
pub struct SignVoteRequest {
#[prost(message, tag = "1")]
pub vote: Option<Vote>,
}
#[derive(Clone, PartialEq, Message)]
#[amino_name = "tendermint/remotesigner/SignedVoteResponse"]
pub struct SignedVoteResponse {
#[prost(message, tag = "1")]
pub vote: Option<Vote>,
#[prost(message, tag = "2")]
pub err: Option<RemoteError>,
}
#[derive(Clone, PartialEq, Message)]
pub struct CanonicalVote {
#[prost(uint32, tag = "1")]
pub vote_type: u32,
#[prost(sfixed64)]
pub height: i64,
#[prost(sfixed64)]
pub round: i64,
#[prost(message)]
pub block_id: Option<CanonicalBlockId>,
#[prost(message)]
pub timestamp: Option<TimeMsg>,
#[prost(string)]
pub chain_id: String,
}
impl chain::ParseId for CanonicalVote {
fn parse_chain_id(&self) -> Result<chain::Id, Error> {
self.chain_id.parse()
}
}
impl block::ParseHeight for CanonicalVote {
fn parse_block_height(&self) -> Result<block::Height, Error> {
block::Height::try_from_i64(self.height)
}
}
impl CanonicalVote {
fn new(vote: Vote, chain_id: &str) -> CanonicalVote {
CanonicalVote {
vote_type: vote.vote_type,
chain_id: chain_id.to_string(),
block_id: match vote.block_id {
Some(bid) => Some(CanonicalBlockId {
hash: bid.hash,
parts_header: match bid.parts_header {
Some(psh) => Some(CanonicalPartSetHeader {
hash: psh.hash,
total: psh.total,
}),
None => None,
},
}),
None => None,
},
height: vote.height,
round: vote.round,
timestamp: match vote.timestamp {
None => Some(TimeMsg {
seconds: -62_135_596_800,
nanos: 0,
}),
Some(t) => Some(t),
},
}
}
}
impl SignableMsg for SignVoteRequest {
fn sign_bytes<B>(&self, chain_id: chain::Id, sign_bytes: &mut B) -> Result<bool, EncodeError>
where
B: BufMut,
{
let mut svr = self.clone();
if let Some(ref mut vo) = svr.vote {
vo.signature = vec![];
}
let vote = svr.vote.unwrap();
let cv = CanonicalVote::new(vote, chain_id.as_str());
cv.encode_length_delimited(sign_bytes)?;
Ok(true)
}
fn set_signature(&mut self, sig: &ed25519::Signature) {
if let Some(ref mut vt) = self.vote {
vt.signature = sig.clone().into_vec();
}
}
fn validate(&self) -> Result<(), ValidationError> {
match self.vote {
Some(ref v) => v.validate_basic(),
None => Err(MissingConsensusMessage.into()),
}
}
fn consensus_state(&self) -> Option<consensus::State> {
match self.vote {
Some(ref v) => Some(consensus::State {
height: match block::Height::try_from_i64(v.height) {
Ok(h) => h,
Err(_err) => return None,
},
round: v.round,
step: 6,
block_id: {
match v.block_id {
Some(ref b) => match b.parse_block_id() {
Ok(id) => Some(id),
Err(_) => None,
},
None => None,
}
},
}),
None => None,
}
}
fn height(&self) -> Option<i64> {
self.vote.as_ref().map(|vote| vote.height)
}
fn msg_type(&self) -> Option<SignedMsgType> {
self.vote.as_ref().and_then(|vote| vote.msg_type())
}
}
impl ConsensusMessage for Vote {
fn validate_basic(&self) -> Result<(), ValidationError> {
if self.msg_type().is_none() {
return Err(InvalidMessageType.into());
}
if self.height < 0 {
return Err(NegativeHeight.into());
}
if self.round < 0 {
return Err(NegativeRound.into());
}
if self.validator_index < 0 {
return Err(NegativeValidatorIndex.into());
}
if self.validator_address.len() != VALIDATOR_ADDR_SIZE {
return Err(InvalidValidatorAddressSize.into());
}
self.block_id
.as_ref()
.map_or(Ok(()), ConsensusMessage::validate_basic)
}
}
#[cfg(test)]
mod tests {
use super::super::PartsSetHeader;
use super::*;
use crate::amino_types::SignedMsgType;
use chrono::{DateTime, Utc};
use prost::Message;
#[test]
fn test_vote_serialization() {
let dt = "2017-12-25T03:00:01.234Z".parse::<DateTime<Utc>>().unwrap();
let t = TimeMsg {
seconds: dt.timestamp(),
nanos: dt.timestamp_subsec_nanos() as i32,
};
let vote = Vote {
vote_type: SignedMsgType::PreVote.to_u32(),
height: 12345,
round: 2,
timestamp: Some(t),
block_id: Some(BlockId {
hash: "hash".as_bytes().to_vec(),
parts_header: Some(PartsSetHeader {
total: 1000000,
hash: "parts_hash".as_bytes().to_vec(),
}),
}),
validator_address: vec![
0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
],
validator_index: 56789,
signature: vec![],
};
let sign_vote_msg = SignVoteRequest { vote: Some(vote) };
let mut got = vec![];
let _have = sign_vote_msg.encode(&mut got);
let want = vec![
78, 243, 244, 18, 4, 10, 72, 8, 1, 16, 185, 96, 24, 2, 34, 24, 10, 4, 104, 97, 115,
104, 18, 16, 8, 192, 132, 61, 18, 10, 112, 97, 114, 116, 115, 95, 104, 97, 115, 104,
42, 11, 8, 177, 211, 129, 210, 5, 16, 128, 157, 202, 111, 50, 20, 163, 178, 204, 221,
113, 134, 241, 104, 95, 33, 242, 72, 42, 244, 251, 52, 70, 168, 75, 53, 56, 213, 187,
3,
];
let svr = SignVoteRequest::decode(got.clone()).unwrap();
println!("got back: {:?}", svr);
assert_eq!(got, want);
}
#[test]
fn test_sign_bytes_compatibility() {
let cv = CanonicalVote::new(Vote::default(), "");
let mut got = vec![];
cv.encode_length_delimited(&mut got).unwrap();
let want = vec![
0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1,
];
assert_eq!(got, want);
{
let mut vt_precommit = Vote::default();
vt_precommit.height = 1;
vt_precommit.round = 1;
vt_precommit.vote_type = SignedMsgType::PreCommit.to_u32();
println!("{:?}", vt_precommit);
let cv_precommit = CanonicalVote::new(vt_precommit, "");
got = vec![];
cv_precommit.encode(&mut got).unwrap();
let want = vec![
0x8,
0x2,
0x11,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x19,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2a,
0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1,
];
assert_eq!(got, want);
}
{
let mut vt_prevote = Vote::default();
vt_prevote.height = 1;
vt_prevote.round = 1;
vt_prevote.vote_type = SignedMsgType::PreVote.to_u32();
got = vec![];
let cv_prevote = CanonicalVote::new(vt_prevote, "");
cv_prevote.encode(&mut got).unwrap();
let want = vec![
0x8,
0x1,
0x11,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x19,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2a,
0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1,
];
assert_eq!(got, want);
}
{
let mut vt_no_type = Vote::default();
vt_no_type.height = 1;
vt_no_type.round = 1;
got = vec![];
let cv = CanonicalVote::new(vt_no_type, "");
cv.encode(&mut got).unwrap();
let want = vec![
0x11,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x19,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1,
];
assert_eq!(got, want);
}
{
let mut no_vote_type2 = Vote::default();
no_vote_type2.height = 1;
no_vote_type2.round = 1;
let with_chain_id = CanonicalVote::new(no_vote_type2, "test_chain_id");
got = vec![];
with_chain_id.encode(&mut got).unwrap();
let want = vec![
0x11,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x19,
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x2a,
0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff,
0x1,
0x32,
0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69,
0x64,
];
assert_eq!(got, want);
}
}
#[test]
fn test_vote_rountrip_with_sig() {
let dt = "2017-12-25T03:00:01.234Z".parse::<DateTime<Utc>>().unwrap();
let t = TimeMsg {
seconds: dt.timestamp(),
nanos: dt.timestamp_subsec_nanos() as i32,
};
let vote = Vote {
validator_address: vec![
0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
],
validator_index: 56789,
height: 12345,
round: 2,
timestamp: Some(t),
vote_type: 0x01,
block_id: Some(BlockId {
hash: "hash".as_bytes().to_vec(),
parts_header: Some(PartsSetHeader {
total: 1000000,
hash: "parts_hash".as_bytes().to_vec(),
}),
}),
signature: vec![
130u8, 246, 183, 50, 153, 248, 28, 57, 51, 142, 55, 217, 194, 24, 134, 212, 233,
100, 211, 10, 24, 174, 179, 117, 41, 65, 141, 134, 149, 239, 65, 174, 217, 42, 6,
184, 112, 17, 7, 97, 255, 221, 252, 16, 60, 144, 30, 212, 167, 39, 67, 35, 118,
192, 133, 130, 193, 115, 32, 206, 152, 91, 173, 10,
],
};
let mut got = vec![];
let _have = vote.encode(&mut got);
let v = Vote::decode(&got).unwrap();
assert_eq!(v, vote);
{
let svr = SignVoteRequest { vote: Some(vote) };
let mut got = vec![];
let _have = svr.encode(&mut got);
let svr2 = SignVoteRequest::decode(&got).unwrap();
assert_eq!(svr, svr2);
}
}
#[test]
fn test_deserialization() {
let encoded = vec![
78, 243, 244, 18, 4, 10, 72, 8, 1, 16, 185, 96, 24, 2, 34, 24, 10, 4, 104, 97, 115,
104, 18, 16, 8, 192, 132, 61, 18, 10, 112, 97, 114, 116, 115, 95, 104, 97, 115, 104,
42, 11, 8, 177, 211, 129, 210, 5, 16, 128, 157, 202, 111, 50, 20, 163, 178, 204, 221,
113, 134, 241, 104, 95, 33, 242, 72, 42, 244, 251, 52, 70, 168, 75, 53, 56, 213, 187,
3,
];
let dt = "2017-12-25T03:00:01.234Z".parse::<DateTime<Utc>>().unwrap();
let t = TimeMsg {
seconds: dt.timestamp(),
nanos: dt.timestamp_subsec_nanos() as i32,
};
let vote = Vote {
validator_address: vec![
0xa3, 0xb2, 0xcc, 0xdd, 0x71, 0x86, 0xf1, 0x68, 0x5f, 0x21, 0xf2, 0x48, 0x2a, 0xf4,
0xfb, 0x34, 0x46, 0xa8, 0x4b, 0x35,
],
validator_index: 56789,
height: 12345,
round: 2,
timestamp: Some(t),
vote_type: 0x01,
block_id: Some(BlockId {
hash: "hash".as_bytes().to_vec(),
parts_header: Some(PartsSetHeader {
total: 1000000,
hash: "parts_hash".as_bytes().to_vec(),
}),
}),
signature: vec![],
};
let want = SignVoteRequest { vote: Some(vote) };
match SignVoteRequest::decode(&encoded) {
Ok(have) => {
assert_eq!(have, want);
}
Err(err) => assert!(false, err.to_string()),
}
}
}