1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.

// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with OpenEthereum.  If not, see <http://www.gnu.org/licenses/>.

use std::{
    collections::{BTreeSet, HashMap, VecDeque},
    fmt,
    time::{Duration, SystemTime, UNIX_EPOCH},
};

use engines::{
    clique::{
        util::{extract_signers, recover_creator},
        VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS,
    },
    EngineError,
};
use error::{BlockError, Error};
use ethereum_types::{Address, H64};
use rand::Rng;
use time_utils::CheckedSystemTime;
use types::{header::Header, BlockNumber};
use unexpected::Mismatch;

/// Type that keeps track of the state for a given vote
// Votes that go against the proposal aren't counted since it's equivalent to not voting
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct VoteState {
    kind: VoteType,
    votes: u64,
}

/// Type that represent a vote
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Vote {
    block_number: BlockNumber,
    beneficiary: Address,
    kind: VoteType,
    signer: Address,
    reverted: bool,
}

/// Type that represent a pending vote
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd)]
pub struct PendingVote {
    signer: Address,
    beneficiary: Address,
}

/// Clique state for each block.
#[cfg(not(test))]
#[derive(Clone, Debug, Default)]
pub struct CliqueBlockState {
    /// Current votes for a beneficiary
    votes: HashMap<PendingVote, VoteState>,
    /// A list of all votes for the given epoch
    votes_history: Vec<Vote>,
    /// a list of all valid signer, sorted by ascending order.
    signers: BTreeSet<Address>,
    /// a deque of recent signer, new entry should be pushed front, apply() modifies this.
    recent_signers: VecDeque<Address>,
    /// inturn signing should wait until this time
    pub next_timestamp_inturn: Option<SystemTime>,
    /// noturn signing should wait until this time
    pub next_timestamp_noturn: Option<SystemTime>,
}

#[cfg(test)]
#[derive(Clone, Debug, Default)]
pub struct CliqueBlockState {
    /// All recorded votes for a given signer, `Vec<PendingVote>` is a stack of votes
    pub votes: HashMap<PendingVote, VoteState>,
    /// A list of all votes for the given epoch
    pub votes_history: Vec<Vote>,
    /// a list of all valid signer, sorted by ascending order.
    pub signers: BTreeSet<Address>,
    /// a deque of recent signer, new entry should be pushed front, apply() modifies this.
    pub recent_signers: VecDeque<Address>,
    /// inturn signing should wait until this time
    pub next_timestamp_inturn: Option<SystemTime>,
    /// noturn signing should wait until this time
    pub next_timestamp_noturn: Option<SystemTime>,
}

impl fmt::Display for CliqueBlockState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let signers: Vec<String> = self
            .signers
            .iter()
            .map(|s| {
                format!(
                    "{} {:?}",
                    s,
                    self.votes
                        .iter()
                        .map(|(v, s)| format!(
                            "[beneficiary {}, votes: {}]",
                            v.beneficiary, s.votes
                        ))
                        .collect::<Vec<_>>()
                )
            })
            .collect();

        let recent_signers: Vec<String> = self
            .recent_signers
            .iter()
            .map(|s| format!("{}", s))
            .collect();
        let num_votes = self.votes_history.len();
        let add_votes = self
            .votes_history
            .iter()
            .filter(|v| v.kind == VoteType::Add)
            .count();
        let rm_votes = self
            .votes_history
            .iter()
            .filter(|v| v.kind == VoteType::Remove)
            .count();
        let reverted_votes = self.votes_history.iter().filter(|v| v.reverted).count();

        write!(f,
			"Votes {{ \n signers: {:?} \n recent_signers: {:?} \n number of votes: {} \n number of add votes {}
			\r number of remove votes {} \n number of reverted votes: {}}}",
			signers, recent_signers, num_votes, add_votes, rm_votes, reverted_votes)
    }
}

impl CliqueBlockState {
    /// Create new state with given information, this is used creating new state from Checkpoint block.
    pub fn new(signers: BTreeSet<Address>) -> Self {
        CliqueBlockState {
            signers,
            ..Default::default()
        }
    }

    // see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L474
    fn verify(&self, header: &Header) -> Result<Address, Error> {
        let creator = recover_creator(header)?.clone();

        // The signer is not authorized
        if !self.signers.contains(&creator) {
            trace!(target: "engine", "current state: {}", self);
            Err(EngineError::NotAuthorized(creator))?
        }

        // The signer has signed a block too recently
        if self.recent_signers.contains(&creator) {
            trace!(target: "engine", "current state: {}", self);
            Err(EngineError::CliqueTooRecentlySigned(creator))?
        }

        // Wrong difficulty
        let inturn = self.is_inturn(header.number(), &creator);

        if inturn && *header.difficulty() != DIFF_INTURN {
            Err(BlockError::InvalidDifficulty(Mismatch {
                expected: DIFF_INTURN,
                found: *header.difficulty(),
            }))?
        }

        if !inturn && *header.difficulty() != DIFF_NOTURN {
            Err(BlockError::InvalidDifficulty(Mismatch {
                expected: DIFF_NOTURN,
                found: *header.difficulty(),
            }))?
        }

        Ok(creator)
    }

    /// Verify and apply a new header to current state
    pub fn apply(&mut self, header: &Header, is_checkpoint: bool) -> Result<Address, Error> {
        let creator = self.verify(header)?;
        self.recent_signers.push_front(creator);
        self.rotate_recent_signers();

        if is_checkpoint {
            // checkpoint block should not affect previous tallying, so we check that.
            let signers = extract_signers(header)?;
            if self.signers != signers {
                let invalid_signers: Vec<String> = signers
                    .into_iter()
                    .filter(|s| !self.signers.contains(s))
                    .map(|s| format!("{}", s))
                    .collect();
                Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers))?
            };

            // TODO(niklasad1): I'm not sure if we should shrink here because it is likely that next epoch
            // will need some memory and might be better for allocation algorithm to decide whether to shrink or not
            // (typically doubles or halves the allocted memory when necessary)
            self.votes.clear();
            self.votes_history.clear();
            self.votes.shrink_to_fit();
            self.votes_history.shrink_to_fit();
        }

        // Contains vote
        if *header.author() != NULL_AUTHOR {
            let decoded_seal = header.decode_seal::<Vec<_>>()?;
            if decoded_seal.len() != 2 {
                Err(BlockError::InvalidSealArity(Mismatch {
                    expected: 2,
                    found: decoded_seal.len(),
                }))?
            }

            let nonce = H64::from_slice(decoded_seal[1]);
            self.update_signers_on_vote(
                VoteType::from_nonce(nonce)?,
                creator,
                *header.author(),
                header.number(),
            )?;
        }

        Ok(creator)
    }

    fn update_signers_on_vote(
        &mut self,
        kind: VoteType,
        signer: Address,
        beneficiary: Address,
        block_number: u64,
    ) -> Result<(), Error> {
        trace!(target: "engine", "Attempt vote {:?} {:?}", kind, beneficiary);

        let pending_vote = PendingVote {
            signer,
            beneficiary,
        };

        let reverted = if self.is_valid_vote(&beneficiary, kind) {
            self.add_vote(pending_vote, kind)
        } else {
            // This case only happens if a `signer` wants to revert their previous vote
            // (does nothing if no previous vote was found)
            self.revert_vote(pending_vote)
        };

        // Add all votes to the history
        self.votes_history.push(Vote {
            block_number,
            beneficiary,
            kind,
            signer,
            reverted,
        });

        // If no vote was found for the beneficiary return `early` but don't propogate an error
        let (votes, vote_kind) = match self.get_current_votes_and_kind(beneficiary) {
            Some((v, k)) => (v, k),
            None => return Ok(()),
        };
        let threshold = self.signers.len() / 2;

        debug!(target: "engine", "{}/{} votes to have consensus", votes, threshold + 1);
        trace!(target: "engine", "votes: {:?}", votes);

        if votes > threshold {
            match vote_kind {
                VoteType::Add => {
                    if self.signers.insert(beneficiary) {
                        debug!(target: "engine", "added new signer: {}", beneficiary);
                    }
                }
                VoteType::Remove => {
                    if self.signers.remove(&beneficiary) {
                        debug!(target: "engine", "removed signer: {}", beneficiary);
                    }
                }
            }

            self.rotate_recent_signers();
            self.remove_all_votes_from(beneficiary);
        }

        Ok(())
    }

    /// Calculate the next timestamp for `inturn` and `noturn` fails if any of them can't be represented as
    /// `SystemTime`
    // TODO(niklasad1): refactor this method to be in constructor of `CliqueBlockState` instead.
    // This is a quite bad API because we must mutate both variables even when already `inturn` fails
    // That's why we can't return early and must have the `if-else` in the end
    pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), Error> {
        let inturn = CheckedSystemTime::checked_add(
            UNIX_EPOCH,
            Duration::from_secs(timestamp.saturating_add(period)),
        );

        self.next_timestamp_inturn = inturn;

        let delay = Duration::from_millis(rand::thread_rng().gen_range(
            0u64,
            (self.signers.len() as u64 / 2 + 1) * SIGNING_DELAY_NOTURN_MS,
        ));
        self.next_timestamp_noturn = inturn.map(|inturn| inturn + delay);

        if self.next_timestamp_inturn.is_some() && self.next_timestamp_noturn.is_some() {
            Ok(())
        } else {
            Err(BlockError::TimestampOverflow)?
        }
    }

    /// Returns true if the block difficulty should be `inturn`
    pub fn is_inturn(&self, current_block_number: u64, author: &Address) -> bool {
        if let Some(pos) = self.signers.iter().position(|x| *author == *x) {
            return current_block_number % self.signers.len() as u64 == pos as u64;
        }
        false
    }

    /// Returns whether the signer is authorized to sign a block
    pub fn is_authorized(&self, author: &Address) -> bool {
        self.signers.contains(author) && !self.recent_signers.contains(author)
    }

    /// Returns whether it makes sense to cast the specified vote in the
    /// current state (e.g. don't try to add an already authorized signer).
    pub fn is_valid_vote(&self, address: &Address, vote_type: VoteType) -> bool {
        let in_signer = self.signers.contains(address);
        match vote_type {
            VoteType::Add => !in_signer,
            VoteType::Remove => in_signer,
        }
    }

    /// Returns the list of current signers
    pub fn signers(&self) -> &BTreeSet<Address> {
        &self.signers
    }

    // Note this method will always return `true` but it is intended for a uniform `API`
    fn add_vote(&mut self, pending_vote: PendingVote, kind: VoteType) -> bool {
        self.votes
            .entry(pending_vote)
            .and_modify(|state| {
                state.votes = state.votes.saturating_add(1);
            })
            .or_insert_with(|| VoteState { kind, votes: 1 });
        true
    }

    fn revert_vote(&mut self, pending_vote: PendingVote) -> bool {
        let mut revert = false;
        let mut remove = false;

        self.votes.entry(pending_vote).and_modify(|state| {
            if state.votes.saturating_sub(1) == 0 {
                remove = true;
            }
            revert = true;
        });

        if remove {
            self.votes.remove(&pending_vote);
        }

        revert
    }

    fn get_current_votes_and_kind(&self, beneficiary: Address) -> Option<(usize, VoteType)> {
        let kind = self
            .votes
            .iter()
            .find(|(v, _t)| v.beneficiary == beneficiary)
            .map(|(_v, t)| t.kind)?;

        let votes = self
            .votes
            .keys()
            .filter(|vote| vote.beneficiary == beneficiary)
            .count();

        Some((votes, kind))
    }

    fn rotate_recent_signers(&mut self) {
        if self.recent_signers.len() >= (self.signers.len() / 2) + 1 {
            self.recent_signers.pop_back();
        }
    }

    fn remove_all_votes_from(&mut self, beneficiary: Address) {
        self.votes = std::mem::replace(&mut self.votes, HashMap::new())
            .into_iter()
            .filter(|(v, _t)| v.signer != beneficiary && v.beneficiary != beneficiary)
            .collect();
    }
}