1use crate::{
9 configurable_validator_signer::ConfigurableValidatorSigner,
10 consensus_state::ConsensusState,
11 error::Error,
12 logging::{LogEntry, LogEvent, SafetyLogSchema},
13 persistent_safety_storage::PersistentSafetyStorage,
14};
15use consensus_types::{
16 block::Block,
17 block_data::BlockData,
18 common::{Author, Round},
19 quorum_cert::QuorumCert,
20 safety_data::SafetyData,
21 timeout::Timeout,
22 vote::Vote,
23 vote_data::VoteData,
24 vote_proposal::{MaybeSignedVoteProposal, VoteProposal},
25};
26use diem_crypto::{
27 hash::{CryptoHash, HashValue},
28 traits::Signature,
29 PrivateKey,
30};
31use diem_logger::prelude::*;
32use diem_types::{
33 account_address::AccountAddress,
34 block_info::BlockInfo,
35 epoch_state::EpochState,
36 ledger_info::LedgerInfo,
37 validator_config::{ConsensusSignature, ConsensusVRFPrivateKey},
38};
39use log::error;
40use serde::Serialize;
41use std::cmp::Ordering;
42
43const SAFETY_STORAGE_SAVE_SUFFIX: &str = "json.save";
44
45pub struct SafetyRules {
47 persistent_storage: PersistentSafetyStorage,
48 export_consensus_key: bool,
49 validator_signer: Option<ConfigurableValidatorSigner>,
50 epoch_state: Option<EpochState>,
51 vrf_private_key: Option<ConsensusVRFPrivateKey>,
52}
53
54impl SafetyRules {
55 pub fn new(
58 persistent_storage: PersistentSafetyStorage,
59 export_consensus_key: bool,
60 vrf_private_key: Option<ConsensusVRFPrivateKey>,
61 author: AccountAddress,
62 ) -> Self {
63 if let Ok(storage_author) = persistent_storage.author() {
64 if storage_author != author {
65 diem_error!(
69 "author in secure_storage does not match PoS keys!"
70 );
71 error!("author in secure_storage does not match PoS keys!");
72 }
73 }
74 Self {
75 persistent_storage,
76 export_consensus_key,
77 validator_signer: None,
78 epoch_state: None,
79 vrf_private_key,
80 }
81 }
82
83 fn sign<T: Serialize + CryptoHash>(
84 &self, message: &T,
85 ) -> Result<ConsensusSignature, Error> {
86 let signer = self.signer()?;
87 signer.sign(message, &self.persistent_storage)
88 }
89
90 fn signer(&self) -> Result<&ConfigurableValidatorSigner, Error> {
91 self.validator_signer
92 .as_ref()
93 .ok_or_else(|| Error::NotInitialized("validator_signer".into()))
94 }
95
96 fn epoch_state(&self) -> Result<&EpochState, Error> {
97 self.epoch_state
98 .as_ref()
99 .ok_or_else(|| Error::NotInitialized("epoch_state".into()))
100 }
101
102 pub fn extension_check(
104 vote_proposal: &VoteProposal,
105 ) -> Result<VoteData, Error> {
106 let proposed_block = vote_proposal.block();
107 let new_tree = vote_proposal
108 .accumulator_extension_proof()
109 .verify(
110 proposed_block
111 .quorum_cert()
112 .certified_block()
113 .executed_state_id(),
114 )
115 .map_err(|e| Error::InvalidAccumulatorExtension(e.to_string()))?;
116 Ok(VoteData::new(
117 proposed_block.gen_block_info(
118 Default::default(),
119 new_tree.version(),
120 vote_proposal.next_epoch_state().cloned(),
121 vote_proposal.pivot_decision().clone(),
122 ),
123 proposed_block.quorum_cert().certified_block().clone(),
124 ))
125 }
126
127 pub fn construct_ledger_info(
134 proposed_block: &Block, consensus_data_hash: HashValue,
135 ) -> Result<LedgerInfo, Error> {
136 let block2 = proposed_block.round();
137 let block1 = proposed_block.quorum_cert().certified_block().round();
138 let block0 = proposed_block.quorum_cert().parent_block().round();
139
140 let next_round = |round: u64| {
142 u64::checked_add(round, 1).ok_or(Error::IncorrectRound(round))
143 };
144 let commit =
145 next_round(block0)? == block1 && next_round(block1)? == block2;
146
147 let commit_info = if commit {
149 proposed_block.quorum_cert().parent_block().clone()
150 } else {
151 BlockInfo::empty()
152 };
153
154 Ok(LedgerInfo::new(commit_info, consensus_data_hash))
155 }
156
157 fn verify_and_update_preferred_round(
159 &mut self, quorum_cert: &QuorumCert, safety_data: &mut SafetyData,
160 ) -> Result<bool, Error> {
161 let preferred_round = safety_data.preferred_round;
162 let one_chain_round = quorum_cert.certified_block().round();
163 let two_chain_round = quorum_cert.parent_block().round();
164
165 if one_chain_round < preferred_round {
166 return Err(Error::IncorrectPreferredRound(
167 one_chain_round,
168 preferred_round,
169 ));
170 }
171
172 let updated = match two_chain_round.cmp(&preferred_round) {
173 Ordering::Greater => {
174 safety_data.preferred_round = two_chain_round;
175 diem_info!(SafetyLogSchema::new(
176 LogEntry::PreferredRound,
177 LogEvent::Update
178 )
179 .preferred_round(safety_data.preferred_round));
180 true
181 }
182 Ordering::Less => {
183 diem_trace!(
184 "2-chain round {} is lower than preferred round {} but 1-chain round {} is higher.",
185 two_chain_round, preferred_round, one_chain_round
186 );
187 false
188 }
189 Ordering::Equal => false,
190 };
191 Ok(updated)
192 }
193
194 fn verify_author(&self, author: Option<Author>) -> Result<(), Error> {
196 let validator_signer_author = &self.signer()?.author();
197 let author = author.ok_or_else(|| {
198 Error::InvalidProposal("No author found in the proposal".into())
199 })?;
200 if validator_signer_author != &author {
201 return Err(Error::InvalidProposal(
202 "Proposal author is not validator signer!".into(),
203 ));
204 }
205 Ok(())
206 }
207
208 fn verify_epoch(
211 &self, epoch: u64, safety_data: &SafetyData,
212 ) -> Result<(), Error> {
213 if epoch != safety_data.epoch {
214 return Err(Error::IncorrectEpoch(epoch, safety_data.epoch));
215 }
216
217 Ok(())
218 }
219
220 fn verify_and_update_last_vote_round(
222 &self, round: Round, safety_data: &mut SafetyData,
223 ) -> Result<(), Error> {
224 if round <= safety_data.last_voted_round {
225 return Err(Error::IncorrectLastVotedRound(
226 round,
227 safety_data.last_voted_round,
228 ));
229 }
230
231 safety_data.last_voted_round = round;
232 diem_info!(SafetyLogSchema::new(
233 LogEntry::LastVotedRound,
234 LogEvent::Update
235 )
236 .last_voted_round(safety_data.last_voted_round));
237
238 Ok(())
239 }
240
241 fn verify_qc(&self, qc: &QuorumCert) -> Result<(), Error> {
243 let epoch_state = self.epoch_state()?;
244
245 qc.verify(&epoch_state.verifier())
246 .map_err(|e| Error::InvalidQuorumCertificate(e.to_string()))?;
247 Ok(())
248 }
249
250 fn guarded_consensus_state(&mut self) -> Result<ConsensusState, Error> {
254 let safety_data = self.persistent_storage.safety_data()?;
255
256 diem_info!(SafetyLogSchema::new(LogEntry::State, LogEvent::Update)
257 .author(self.persistent_storage.author()?)
258 .epoch(safety_data.epoch)
259 .last_voted_round(safety_data.last_voted_round)
260 .preferred_round(safety_data.preferred_round));
261
262 Ok(ConsensusState::new(
263 self.persistent_storage.safety_data()?,
264 self.signer().is_ok(),
265 ))
266 }
267
268 fn guarded_initialize(
269 &mut self, epoch_state: &EpochState,
270 ) -> Result<(), Error> {
271 let current_epoch = self.persistent_storage.safety_data()?.epoch;
272
273 if current_epoch < epoch_state.epoch {
274 self.persistent_storage.set_safety_data(SafetyData::new(
275 epoch_state.epoch,
276 0,
277 0,
278 None,
279 ))?;
280
281 diem_info!(SafetyLogSchema::new(LogEntry::Epoch, LogEvent::Update)
282 .epoch(epoch_state.epoch));
283 }
284 self.epoch_state = Some(epoch_state.clone());
285
286 let author = self.persistent_storage.author()?;
287 let expected_key = epoch_state.verifier().get_public_key(&author);
288 let initialize_result = match expected_key {
289 None => {
290 diem_debug!(
291 "guarded_initialize: not a validator in epoch_set={:?}",
292 epoch_state
293 );
294 self.validator_signer = None;
295 Ok(())
296 }
297 Some(expected_key) => {
298 let current_key = self.signer().ok().map(|s| s.public_key());
299 if current_key == Some(expected_key.clone()) {
300 diem_debug!(
301 SafetyLogSchema::new(
302 LogEntry::KeyReconciliation,
303 LogEvent::Success
304 ),
305 "in set",
306 );
307 Ok(())
308 } else if self.export_consensus_key {
309 match self
311 .persistent_storage
312 .consensus_key_for_version(expected_key.clone())
313 {
314 Ok(consensus_key) => {
315 if consensus_key.public_key() != expected_key {
316 Err(Error::ValidatorKeyNotFound("exported key does not match the expected key".into()))
317 } else {
318 self.validator_signer = Some(
319 ConfigurableValidatorSigner::new_signer(
320 author,
321 consensus_key,
322 self.vrf_private_key.clone(),
323 ),
324 );
325 Ok(())
326 }
327 }
328 Err(Error::SecureStorageMissingDataError(error)) => {
329 Err(Error::ValidatorKeyNotFound(error))
330 }
331 Err(error) => Err(error),
332 }
333 } else {
334 self.validator_signer =
338 Some(ConfigurableValidatorSigner::new_handle(
339 author,
340 expected_key.clone(),
341 ));
342 self.sign(&Timeout::new(0, 0)).and_then(|signature| {
343 signature
344 .verify(&Timeout::new(0, 0), &expected_key)
345 .map_err(|error| {
346 Error::ValidatorKeyNotFound(error.to_string())
347 })
348 })
349 }
350 }
351 };
352 initialize_result.map_err(|error| {
353 diem_info!(SafetyLogSchema::new(
354 LogEntry::KeyReconciliation,
355 LogEvent::Error
356 )
357 .error(&error),);
358 self.validator_signer = None;
359 error
360 })
361 }
362
363 fn guarded_construct_and_sign_vote(
364 &mut self, maybe_signed_vote_proposal: &MaybeSignedVoteProposal,
365 ) -> Result<Vote, Error> {
366 self.signer()?;
368
369 let vote_proposal = &maybe_signed_vote_proposal.vote_proposal;
370 let proposed_block = vote_proposal.block();
371 let mut safety_data = self.persistent_storage.safety_data()?;
372
373 self.verify_epoch(proposed_block.epoch(), &safety_data)?;
374
375 if let Some(vote) = safety_data.last_vote.clone() {
379 if vote.vote_data().proposed().round() == proposed_block.round() {
380 return Ok(vote);
381 }
382 }
383
384 self.verify_qc(proposed_block.quorum_cert())?;
385 proposed_block
386 .validate_signature(&self.epoch_state()?.verifier())
387 .map_err(|error| Error::InternalError(error.to_string()))?;
388
389 self.verify_and_update_preferred_round(
390 proposed_block.quorum_cert(),
391 &mut safety_data,
392 )?;
393 self.verify_and_update_last_vote_round(
394 proposed_block.block_data().round(),
395 &mut safety_data,
396 )?;
397
398 let vote_data = Self::extension_check(vote_proposal)?;
400 let author = self.signer()?.author();
401 let ledger_info =
402 Self::construct_ledger_info(proposed_block, vote_data.hash())?;
403 let signature = self.sign(&ledger_info)?;
404 let vote =
405 Vote::new_with_signature(vote_data, author, ledger_info, signature);
406
407 safety_data.last_vote = Some(vote.clone());
408 self.persistent_storage.set_safety_data(safety_data)?;
409
410 Ok(vote)
411 }
412
413 fn guarded_sign_proposal(
414 &mut self, block_data: BlockData,
415 ) -> Result<Block, Error> {
416 self.signer()?;
417 self.verify_author(block_data.author())?;
418
419 let mut safety_data = self.persistent_storage.safety_data()?;
420 self.verify_epoch(block_data.epoch(), &safety_data)?;
421
422 if block_data.round() <= safety_data.last_voted_round {
423 return Err(Error::InvalidProposal(format!(
424 "Proposed round {} is not higher than last voted round {}",
425 block_data.round(),
426 safety_data.last_voted_round
427 )));
428 }
429
430 self.verify_qc(block_data.quorum_cert())?;
431 if self.verify_and_update_preferred_round(
432 block_data.quorum_cert(),
433 &mut safety_data,
434 )? {
435 self.persistent_storage.set_safety_data(safety_data)?;
436 }
437
438 let signature = self.sign(&block_data)?;
439 Ok(Block::new_proposal_from_block_data_and_signature(
440 block_data, signature, None,
441 ))
442 }
443
444 fn guarded_sign_timeout(
445 &mut self, timeout: &Timeout,
446 ) -> Result<ConsensusSignature, Error> {
447 self.signer()?;
448
449 let mut safety_data = self.persistent_storage.safety_data()?;
450 self.verify_epoch(timeout.epoch(), &safety_data)?;
451
452 if timeout.round() <= safety_data.preferred_round {
453 return Err(Error::IncorrectPreferredRound(
454 timeout.round(),
455 safety_data.preferred_round,
456 ));
457 }
458 if timeout.round() < safety_data.last_voted_round {
459 return Err(Error::IncorrectLastVotedRound(
460 timeout.round(),
461 safety_data.last_voted_round,
462 ));
463 }
464 if timeout.round() > safety_data.last_voted_round {
465 self.verify_and_update_last_vote_round(
466 timeout.round(),
467 &mut safety_data,
468 )?;
469 self.persistent_storage.set_safety_data(safety_data)?;
470 }
471
472 let signature = self.sign(timeout)?;
473 Ok(signature)
474 }
475
476 pub fn start_voting(&mut self, initialize: bool) -> Result<(), Error> {
477 if initialize {
478 Ok(())
481 } else {
482 self.persistent_storage
483 .replace_with_suffix(SAFETY_STORAGE_SAVE_SUFFIX)
484 }
485 }
486
487 pub fn stop_voting(&mut self) -> Result<(), Error> {
488 self.persistent_storage
489 .save_to_suffix(SAFETY_STORAGE_SAVE_SUFFIX)
490 }
491
492 pub fn consensus_state(&mut self) -> Result<ConsensusState, Error> {
493 let cb = || self.guarded_consensus_state();
494 run_and_log(cb, |log| log, LogEntry::ConsensusState)
495 }
496
497 pub fn initialize(
498 &mut self, epoch_state: &EpochState,
499 ) -> Result<(), Error> {
500 let cb = || self.guarded_initialize(epoch_state);
501 run_and_log(cb, |log| log, LogEntry::Initialize)
502 }
503
504 pub fn construct_and_sign_vote(
505 &mut self, maybe_signed_vote_proposal: &MaybeSignedVoteProposal,
506 ) -> Result<Vote, Error> {
507 let round = maybe_signed_vote_proposal.vote_proposal.block().round();
508 let cb =
509 || self.guarded_construct_and_sign_vote(maybe_signed_vote_proposal);
510 run_and_log(cb, |log| log.round(round), LogEntry::ConstructAndSignVote)
511 }
512
513 pub fn sign_proposal(
514 &mut self, block_data: BlockData,
515 ) -> Result<Block, Error> {
516 let round = block_data.round();
517 let cb = || self.guarded_sign_proposal(block_data);
518 run_and_log(cb, |log| log.round(round), LogEntry::SignProposal)
519 }
520
521 pub fn sign_timeout(
522 &mut self, timeout: &Timeout,
523 ) -> Result<ConsensusSignature, Error> {
524 let cb = || self.guarded_sign_timeout(timeout);
525 run_and_log(cb, |log| log.round(timeout.round()), LogEntry::SignTimeout)
526 }
527}
528
529fn run_and_log<F, L, R>(
530 callback: F, log_cb: L, log_entry: LogEntry,
531) -> Result<R, Error>
532where
533 F: FnOnce() -> Result<R, Error>,
534 L: for<'a> Fn(SafetyLogSchema<'a>) -> SafetyLogSchema<'a>,
535{
536 diem_debug!(log_cb(SafetyLogSchema::new(log_entry, LogEvent::Request)));
537 callback()
538 .map(|v| {
539 diem_info!(log_cb(SafetyLogSchema::new(
540 log_entry,
541 LogEvent::Success
542 )));
543 v
544 })
545 .map_err(|err| {
546 diem_error!(log_cb(SafetyLogSchema::new(
547 log_entry,
548 LogEvent::Error
549 ))
550 .error(&err));
551 err
552 })
553}