1use std::cmp;
8use std::collections::BTreeMap;
9use std::sync::Arc;
10
11use dusk_bytes::Serializable;
12use dusk_consensus::config::{
13 is_emergency_block, is_emergency_iter, CONSENSUS_MAX_ITER,
14 MINIMUM_BLOCK_TIME, MIN_EMERGENCY_BLOCK_TIME, RELAX_ITERATION_THRESHOLD,
15};
16use dusk_consensus::errors::{
17 AttestationError, FailedIterationError, HeaderError,
18};
19use dusk_consensus::operations::Voter;
20use dusk_consensus::quorum::verifiers;
21use dusk_consensus::quorum::verifiers::QuorumResult;
22use dusk_consensus::user::committee::CommitteeSet;
23use dusk_consensus::user::provisioners::{ContextProvisioners, Provisioners};
24use dusk_core::signatures::bls::{
25 MultisigPublicKey, MultisigSignature, PublicKey as BlsPublicKey,
26};
27use dusk_core::stake::EPOCH;
28use hex;
29use node_data::bls::PublicKeyBytes;
30use node_data::ledger::{Fault, InvalidFault, Seed, Signature};
31use node_data::message::payload::{RatificationResult, Vote};
32use node_data::message::{ConsensusHeader, BLOCK_HEADER_VERSION};
33use node_data::{get_current_timestamp, ledger, StepName};
34use thiserror::Error;
35use tokio::sync::RwLock;
36use tracing::{debug, error};
37
38use crate::database;
39use crate::database::Ledger;
40
41const MARGIN_TIMESTAMP: u64 = 3;
42
43#[derive(Debug, Error)]
46enum HeaderVerificationErr {}
47
48pub(crate) struct Validator<'a, DB: database::DB> {
51 pub(crate) db: Arc<RwLock<DB>>,
52 prev_header: &'a ledger::Header,
53 provisioners: &'a ContextProvisioners,
54}
55
56impl<'a, DB: database::DB> Validator<'a, DB> {
57 pub fn new(
58 db: Arc<RwLock<DB>>,
59 prev_header: &'a ledger::Header,
60 provisioners: &'a ContextProvisioners,
61 ) -> Self {
62 Self {
63 db,
64 prev_header,
65 provisioners,
66 }
67 }
68
69 pub async fn execute_checks(
79 &self,
80 header: &ledger::Header,
81 expected_generator: &PublicKeyBytes,
82 check_attestation: bool,
83 ) -> Result<(u8, Vec<Voter>, Vec<Voter>), HeaderError> {
84 let generator =
85 self.verify_block_generator(header, expected_generator)?;
86 self.verify_basic_fields(header, &generator).await?;
87
88 let prev_block_voters = self.verify_prev_block_cert(header).await?;
89
90 let mut block_voters = vec![];
91 if check_attestation {
92 (_, _, block_voters) = verify_att(
93 &header.att,
94 header.to_consensus_header(),
95 self.prev_header.seed,
96 self.provisioners.current(),
97 Some(RatificationResult::Success(Vote::Valid(header.hash))),
98 )
99 .await?;
100 }
101
102 let pni = self.verify_failed_iterations(header).await?;
103 Ok((pni, prev_block_voters, block_voters))
104 }
105
106 fn verify_block_generator(
107 &self,
108 header: &'a ledger::Header,
109 expected_generator: &PublicKeyBytes,
110 ) -> Result<MultisigPublicKey, HeaderError> {
111 if expected_generator != &header.generator_bls_pubkey {
112 return Err(HeaderError::InvalidBlockSignature(
113 "Signed by a different generator:".into(),
114 ));
115 }
116
117 let generator = header.generator_bls_pubkey.inner();
119 let generator = BlsPublicKey::from_bytes(generator).map_err(|err| {
120 HeaderError::InvalidBlockSignature(format!(
121 "invalid pk bytes: {err:?}"
122 ))
123 })?;
124 let generator =
125 MultisigPublicKey::aggregate(&[generator]).map_err(|err| {
126 HeaderError::InvalidBlockSignature(format!(
127 "failed aggregating single key: {err:?}"
128 ))
129 })?;
130
131 let block_sig = MultisigSignature::from_bytes(header.signature.inner())
133 .map_err(|err| {
134 HeaderError::InvalidBlockSignature(format!(
135 "invalid block signature bytes: {err:?}"
136 ))
137 })?;
138 generator.verify(&block_sig, &header.hash).map_err(|err| {
139 HeaderError::InvalidBlockSignature(format!(
140 "invalid block signature: {err:?}"
141 ))
142 })?;
143
144 Ok(generator)
145 }
146
147 async fn verify_basic_fields(
149 &self,
150 candidate_block: &'a ledger::Header,
151 generator: &MultisigPublicKey,
152 ) -> Result<(), HeaderError> {
153 if candidate_block.version != BLOCK_HEADER_VERSION {
154 return Err(HeaderError::UnsupportedVersion);
155 }
156
157 if candidate_block.hash == [0u8; 32] {
158 return Err(HeaderError::EmptyHash);
159 }
160
161 if candidate_block.height != self.prev_header.height + 1 {
162 return Err(HeaderError::MismatchHeight(
163 candidate_block.height,
164 self.prev_header.height,
165 ));
166 }
167
168 if candidate_block.timestamp
170 < self.prev_header.timestamp + *MINIMUM_BLOCK_TIME
171 {
172 return Err(HeaderError::BlockTimeLess);
173 }
174
175 if is_emergency_block(candidate_block.iteration)
183 && candidate_block.timestamp
184 < self.prev_header.timestamp
185 + MIN_EMERGENCY_BLOCK_TIME.as_secs()
186 {
187 return Err(HeaderError::BlockTimeLess);
188 }
189
190 let local_time = get_current_timestamp();
191
192 if candidate_block.timestamp > local_time + MARGIN_TIMESTAMP {
193 return Err(HeaderError::BlockTimeHigher(
194 candidate_block.timestamp,
195 ));
196 }
197
198 if candidate_block.prev_block_hash != self.prev_header.hash {
199 return Err(HeaderError::PrevBlockHash);
200 }
201
202 let block_exists = self
204 .db
205 .read()
206 .await
207 .view(|db| db.block_exists(&candidate_block.hash))
208 .map_err(|e| {
209 HeaderError::Storage(
210 "error checking Ledger::get_block_exists",
211 e,
212 )
213 })?;
214
215 if block_exists {
216 return Err(HeaderError::BlockExists);
217 }
218
219 self.verify_seed_field(candidate_block.seed.inner(), generator)?;
221
222 Ok(())
223 }
224
225 fn verify_seed_field(
226 &self,
227 seed: &[u8; 48],
228 pk: &MultisigPublicKey,
229 ) -> Result<(), HeaderError> {
230 let signature = MultisigSignature::from_bytes(seed).map_err(|err| {
231 HeaderError::InvalidSeed(format!(
232 "invalid seed signature bytes: {err:?}"
233 ))
234 })?;
235
236 pk.verify(&signature, self.prev_header.seed.inner())
237 .map_err(|err| {
238 HeaderError::InvalidSeed(format!("invalid seed: {err:?}"))
239 })?;
240
241 Ok(())
242 }
243
244 async fn verify_prev_block_cert(
245 &self,
246 candidate_block: &'a ledger::Header,
247 ) -> Result<Vec<Voter>, HeaderError> {
248 if self.prev_header.height == 0
249 || is_emergency_block(self.prev_header.iteration)
250 {
251 return Ok(vec![]);
252 }
253
254 let prev_block_hash = candidate_block.prev_block_hash;
255
256 let prev_block_seed = self
257 .db
258 .read()
259 .await
260 .view(|v| v.block_header(&self.prev_header.prev_block_hash))
261 .map_err(|e| {
262 HeaderError::Storage(
263 "error checking Ledger::fetch_block_header",
264 e,
265 )
266 })?
267 .ok_or(HeaderError::Generic("Header not found"))
268 .map(|h| h.seed)?;
269
270 let (_, _, voters) = verify_att(
271 &candidate_block.prev_block_cert,
272 self.prev_header.to_consensus_header(),
273 prev_block_seed,
274 self.provisioners.prev(),
275 Some(RatificationResult::Success(Vote::Valid(prev_block_hash))),
276 )
277 .await?;
278
279 Ok(voters)
280 }
281
282 async fn verify_failed_iterations(
287 &self,
288 candidate_block: &'a ledger::Header,
289 ) -> Result<u8, FailedIterationError> {
290 let mut failed_atts = 0u8;
291
292 let att_list = &candidate_block.failed_iterations.att_list;
293
294 if att_list.len() > RELAX_ITERATION_THRESHOLD as usize {
295 return Err(FailedIterationError::TooMany(att_list.len()));
296 }
297
298 for (iter, att) in att_list.iter().enumerate() {
299 if let Some((att, pk)) = att {
300 debug!(event = "verify fail attestation", iter);
301
302 let expected_pk = self.provisioners.current().get_generator(
303 iter as u8,
304 self.prev_header.seed,
305 candidate_block.height,
306 );
307
308 if pk != &expected_pk {
309 return Err(FailedIterationError::InvalidGenerator(
310 expected_pk,
311 ));
312 }
313
314 let mut consensus_header =
315 candidate_block.to_consensus_header();
316 consensus_header.iteration = iter as u8;
317
318 verify_att(
319 att,
320 consensus_header,
321 self.prev_header.seed,
322 self.provisioners.current(),
323 Some(RatificationResult::Fail(Vote::default())),
324 )
325 .await?;
326
327 failed_atts += 1;
328 }
329 }
330
331 let last_iter = cmp::min(candidate_block.iteration, CONSENSUS_MAX_ITER);
334
335 Ok(last_iter - failed_atts)
336 }
337
338 pub async fn get_voters(
343 blk: &'a ledger::Header,
344 provisioners: &Provisioners,
345 prev_block_seed: Seed,
346 ) -> Vec<Voter> {
347 let att = &blk.att;
348 let consensus_header = blk.to_consensus_header();
349
350 let committee = RwLock::new(CommitteeSet::new(provisioners));
351
352 let validation_voters = verifiers::get_step_voters(
353 &consensus_header,
354 &att.validation,
355 &committee,
356 prev_block_seed,
357 StepName::Validation,
358 )
359 .await;
360
361 let ratification_voters = verifiers::get_step_voters(
362 &consensus_header,
363 &att.ratification,
364 &committee,
365 prev_block_seed,
366 StepName::Ratification,
367 )
368 .await;
369
370 merge_voters(validation_voters, ratification_voters)
371 }
372
373 pub async fn verify_faults(
375 &self,
376 current_height: u64,
377 faults: &[Fault],
378 ) -> Result<(), InvalidFault> {
379 verify_faults(self.db.clone(), current_height, faults).await
380 }
381}
382
383pub async fn verify_faults<DB: database::DB>(
384 db: Arc<RwLock<DB>>,
385 current_height: u64,
386 faults: &[Fault],
387) -> Result<(), InvalidFault> {
388 for f in faults {
389 let fault_header = f.validate(current_height)?;
390 if is_emergency_iter(fault_header.iteration) {
391 return Err(InvalidFault::EmergencyIteration);
392 }
393 db.read()
394 .await
395 .view(|db| {
396 let prev_header = db
397 .block_header(&fault_header.prev_block_hash)?
398 .ok_or(anyhow::anyhow!("Slashing a non accepted header"))?;
399 if prev_header.height != fault_header.round - 1 {
402 anyhow::bail!("Invalid height for fault");
403 }
404
405 let start_height = fault_header.round.saturating_sub(EPOCH);
409 let stored_faults = db.faults_by_block(start_height)?;
410 if stored_faults.iter().any(|other| f.same(other)) {
411 anyhow::bail!("Double fault detected");
412 }
413
414 Ok(())
415 })
416 .map_err(|e| InvalidFault::Other(format!("{e:?}")))?;
417 }
418 Ok(())
419}
420
421pub async fn verify_att(
422 att: &ledger::Attestation,
423 consensus_header: ConsensusHeader,
424 curr_seed: Signature,
425 curr_eligible_provisioners: &Provisioners,
426 expected_result: Option<RatificationResult>,
427) -> Result<(QuorumResult, QuorumResult, Vec<Voter>), AttestationError> {
428 if let Some(expected) = expected_result {
430 match (att.result, expected) {
431 (
433 RatificationResult::Success(Vote::Valid(r_hash)),
434 RatificationResult::Success(Vote::Valid(e_hash)),
435 ) => {
436 if r_hash != e_hash {
437 error!("Invalid Attestation. Expected: Valid({:?}), got: Valid({:?})", hex::encode(e_hash), hex::encode(r_hash));
438 return Err(AttestationError::InvalidHash(e_hash, r_hash));
439 }
440 }
441 (RatificationResult::Fail(_), RatificationResult::Fail(_)) => {}
443 _ => {
445 error!(
446 "Invalid Attestation. Expected: {:?}, got: {:?}",
447 expected, att.result
448 );
449 return Err(AttestationError::InvalidResult(
450 att.result, expected,
451 ));
452 }
453 }
454 }
455
456 let committee = RwLock::new(CommitteeSet::new(curr_eligible_provisioners));
457 let vote = att.result.vote();
458
459 let (val_result, validation_voters) = verifiers::verify_step_votes(
461 &consensus_header,
462 vote,
463 &att.validation,
464 &committee,
465 curr_seed,
466 StepName::Validation,
467 )
468 .await
469 .map_err(|s| AttestationError::InvalidVotes(StepName::Validation, s))?;
470
471 let (rat_result, ratification_voters) = verifiers::verify_step_votes(
473 &consensus_header,
474 vote,
475 &att.ratification,
476 &committee,
477 curr_seed,
478 StepName::Ratification,
479 )
480 .await
481 .map_err(|s| AttestationError::InvalidVotes(StepName::Ratification, s))?;
482
483 let voters = merge_voters(validation_voters, ratification_voters);
484 Ok((val_result, rat_result, voters))
485}
486
487fn merge_voters(v1: Vec<Voter>, v2: Vec<Voter>) -> Vec<Voter> {
490 let mut voter_map = BTreeMap::new();
491
492 for (pk, count) in v1.into_iter().chain(v2.into_iter()) {
493 let counter = voter_map.entry(pk).or_default();
494 *counter += count;
495 }
496
497 voter_map.into_iter().collect()
498}