tendermint_testgen/
vote.rs

1use gumdrop::Options;
2use serde::{Deserialize, Serialize};
3use simple_error::*;
4use tendermint::{
5    block::{self, parts::Header as PartSetHeader},
6    signature::{Ed25519Signature, Signature},
7    vote,
8    vote::ValidatorIndex,
9};
10
11use crate::{helpers::*, Generator, Header, Validator};
12
13#[derive(Debug, Options, Serialize, Deserialize, Clone)]
14pub struct Vote {
15    #[options(
16        help = "validator of this vote (required; can be passed via STDIN)",
17        parse(try_from_str = "parse_as::<Validator>")
18    )]
19    pub validator: Option<Validator>,
20    #[options(help = "validator index (default: from commit header)")]
21    pub index: Option<u16>,
22    #[options(help = "header to sign (default: commit header)")]
23    pub header: Option<Header>,
24    #[options(help = "vote type; 'prevote' if set, otherwise 'precommit' (default)")]
25    pub prevote: Option<()>,
26    #[options(help = "block height (default: from header)")]
27    pub height: Option<u64>,
28    #[options(help = "time (default: from header)")]
29    pub time: Option<u64>,
30    #[options(help = "commit round (default: from commit)")]
31    pub round: Option<u32>,
32    #[options(
33        help = "to indicate if the vote is nil; produces a 'BlockIdFlagNil' if set, otherwise 'BlockIdFlagCommit' (default)"
34    )]
35    pub nil: Option<()>,
36}
37
38impl Vote {
39    pub fn new(validator: Validator, header: Header) -> Self {
40        Vote {
41            validator: Some(validator),
42            index: None,
43            header: Some(header),
44            prevote: None,
45            height: None,
46            time: None,
47            round: None,
48            nil: None,
49        }
50    }
51    set_option!(index, u16);
52    set_option!(header, Header);
53    set_option!(prevote, bool, if prevote { Some(()) } else { None });
54    set_option!(height, u64);
55    set_option!(time, u64);
56    set_option!(round, u32);
57    set_option!(nil, bool, if nil { Some(()) } else { None });
58}
59
60impl std::str::FromStr for Vote {
61    type Err = SimpleError;
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        parse_as::<Vote>(s)
64    }
65}
66
67impl Generator<vote::Vote> for Vote {
68    fn merge_with_default(self, default: Self) -> Self {
69        Vote {
70            validator: self.validator.or(default.validator),
71            index: self.index.or(default.index),
72            header: self.header.or(default.header),
73            prevote: self.prevote.or(default.prevote),
74            height: self.height.or(default.height),
75            time: self.time.or(default.time),
76            round: self.round.or(default.round),
77            nil: self.nil.or(default.nil),
78        }
79    }
80
81    fn generate(&self) -> Result<vote::Vote, SimpleError> {
82        let validator = match &self.validator {
83            None => bail!("failed to generate vote: validator is missing"),
84            Some(v) => v,
85        };
86        let header = match &self.header {
87            None => bail!("failed to generate vote: header is missing"),
88            Some(h) => h,
89        };
90        let signing_key = validator.get_private_key()?;
91        let signing_key = ed25519_consensus::SigningKey::try_from(signing_key).unwrap();
92        let block_validator = validator.generate()?;
93        let block_header = header.generate()?;
94        let block_id = if self.nil.is_some() {
95            None
96        } else {
97            Some(block::Id {
98                hash: block_header.hash(),
99                part_set_header: PartSetHeader::new(1, block_header.hash()).unwrap(),
100            })
101        };
102        let validator_index = match self.index {
103            Some(i) => i,
104            None => {
105                let position = header
106                    .validators
107                    .as_ref()
108                    .unwrap()
109                    .iter()
110                    .position(|v| *v == *validator);
111                match position {
112                    Some(i) => i as u16, // Todo: possible overflow
113                    None => 0,           // we allow non-present validators for testing purposes
114                }
115            },
116        };
117        let timestamp = if let Some(t) = self.time {
118            get_time(t)?
119        } else {
120            block_header.time
121        };
122        let mut vote = vote::Vote {
123            vote_type: if self.prevote.is_some() {
124                vote::Type::Prevote
125            } else {
126                vote::Type::Precommit
127            },
128            height: block_header.height,
129            round: block::Round::try_from(self.round.unwrap_or(1)).unwrap(),
130            block_id,
131            timestamp: Some(timestamp),
132            validator_address: block_validator.address,
133            validator_index: ValidatorIndex::try_from(validator_index as u32).unwrap(),
134            signature: Signature::new(vec![0_u8; Ed25519Signature::BYTE_SIZE])
135                .map_err(|e| SimpleError::new(e.to_string()))?,
136            extension: vec![],
137            extension_signature: None,
138        };
139
140        let sign_bytes = get_vote_sign_bytes(block_header.chain_id, &vote);
141        vote.signature = Some(signing_key.sign(sign_bytes.as_slice()).into());
142
143        Ok(vote)
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use crate::Time;
151
152    #[test]
153    fn test_vote() {
154        let valset1 = [
155            Validator::new("a"),
156            Validator::new("b"),
157            Validator::new("c"),
158        ];
159        let valset2 = [
160            Validator::new("b"),
161            Validator::new("c"),
162            Validator::new("d"),
163        ];
164
165        let now = Time::new(10).generate().unwrap();
166        let header = Header::new(&valset1)
167            .next_validators(&valset2)
168            .height(10)
169            .time(tendermint::Time::from_unix_timestamp(10, 0).unwrap());
170
171        let val = &valset1[1];
172        let vote = Vote::new(val.clone(), header.clone()).round(2);
173
174        let block_val = val.generate().unwrap();
175        let block_header = header.generate().unwrap();
176        let block_vote = vote.generate().unwrap();
177
178        assert_eq!(block_vote.validator_address, block_val.address);
179        assert_eq!(block_vote.height, block_header.height);
180        assert_eq!(block_vote.round.value(), 2);
181        assert_eq!(block_vote.timestamp.unwrap(), now);
182        assert_eq!(block_vote.validator_index.value(), 1);
183        assert_eq!(block_vote.vote_type, vote::Type::Precommit);
184
185        let sign_bytes = get_vote_sign_bytes(block_header.chain_id, &block_vote);
186        assert!(!verify_signature(
187            &valset1[0].get_public_key().unwrap(),
188            &sign_bytes,
189            block_vote.signature.as_ref().unwrap()
190        ));
191        assert!(verify_signature(
192            &valset1[1].get_public_key().unwrap(),
193            &sign_bytes,
194            block_vote.signature.as_ref().unwrap()
195        ));
196    }
197}