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