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