Skip to main content

dusk_node/chain/
header_validation.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use 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
41// Small grace window, in seconds, for timestamps that are slightly ahead of
42// local wall clock. This is used both for concrete header checks and for the
43// earlier sync-target plausibility gate.
44pub(crate) const MARGIN_TIMESTAMP: u64 = 3;
45
46// TODO: Use thiserror instead of anyhow
47
48/// An implementation of the all validation checks of a candidate block header
49/// according to current context
50pub(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    /// Verifies all header fields except state root and event bloom
70    ///
71    /// # Arguments
72    ///
73    /// * `header` - the block header to verify
74    /// * `expected_generator` - the generator extracted by the Deterministic
75    ///   Sortition algorithm
76    /// * `check_attestation` - `true` if the Attestation has to be verified
77    ///   (this should be `false` for Candidate headers, for which no
78    ///   Attestation has been yet produced)
79    ///
80    /// # Returns
81    ///
82    /// * the number of Previous Non-Attested Iterations (PNI)
83    /// * the voters of the previous block Certificate
84    /// * the voters of current block Attestation (if `check_attestation` is
85    ///   `true`)
86    pub async fn verify_block_header_fields(
87        &self,
88        header: &ledger::Header,
89        expected_generator: &PublicKeyBytes,
90        check_attestation: bool,
91    ) -> Result<(u8, Vec<Voter>, Vec<Voter>), HeaderError> {
92        self.verify_block_header_basic_fields(header, expected_generator)
93            .await?;
94
95        let cert_voters = self.verify_prev_block_cert(header).await?;
96
97        let mut att_voters = vec![];
98        if check_attestation {
99            att_voters = verify_att(
100                &header.att,
101                header.to_consensus_header(),
102                self.prev_header.seed,
103                self.provisioners.current(),
104                Some(RatificationResult::Success(Vote::Valid(header.hash))),
105            )
106            .await?;
107        }
108
109        let pni = self.verify_failed_iterations(header).await?;
110        Ok((pni, cert_voters, att_voters))
111    }
112
113    fn verify_block_generator(
114        &self,
115        header: &'a ledger::Header,
116        expected_generator: &PublicKeyBytes,
117    ) -> Result<MultisigPublicKey, HeaderError> {
118        if expected_generator != &header.generator_bls_pubkey {
119            return Err(HeaderError::InvalidBlockSignature(
120                "Signed by a different generator:".into(),
121            ));
122        }
123
124        // Get generator MultisigPublicKey
125        let generator = header.generator_bls_pubkey.inner();
126        let generator = BlsPublicKey::from_bytes(generator).map_err(|err| {
127            HeaderError::InvalidBlockSignature(format!(
128                "invalid pk bytes: {err:?}"
129            ))
130        })?;
131        let bls_version = bls_version_at(header.height);
132        let generator = core_bls::aggregate(&[generator], bls_version)
133            .map_err(|err| {
134                HeaderError::InvalidBlockSignature(format!(
135                    "failed aggregating single key: {err:?}"
136                ))
137            })?;
138
139        // Verify block signature
140        let block_sig = MultisigSignature::from_bytes(header.signature.inner())
141            .map_err(|err| {
142                HeaderError::InvalidBlockSignature(format!(
143                    "invalid block signature bytes: {err:?}"
144                ))
145            })?;
146        core_bls::verify_multisig(
147            &generator,
148            &block_sig,
149            &header.hash,
150            bls_version,
151        )
152        .map_err(|err| {
153            HeaderError::InvalidBlockSignature(format!(
154                "invalid block signature: {err:?}"
155            ))
156        })?;
157
158        Ok(generator)
159    }
160
161    /// Performs sanity checks for basic fields of the block header
162    async fn verify_block_header_basic_fields(
163        &self,
164        header: &'a ledger::Header,
165        expected_generator: &PublicKeyBytes,
166    ) -> Result<(), HeaderError> {
167        let generator =
168            self.verify_block_generator(header, expected_generator)?;
169
170        if header.version != BLOCK_HEADER_VERSION {
171            return Err(HeaderError::UnsupportedVersion);
172        }
173
174        if header.hash == [0u8; 32] {
175            return Err(HeaderError::EmptyHash);
176        }
177
178        if header.height != self.prev_header.height + 1 {
179            return Err(HeaderError::MismatchHeight(
180                header.height,
181                self.prev_header.height,
182            ));
183        }
184
185        // Ensure rule of minimum block time is addressed
186        if header.timestamp < self.prev_header.timestamp + *MINIMUM_BLOCK_TIME {
187            return Err(HeaderError::BlockTimeLess);
188        }
189
190        // The Emergency Block can only be produced after all iterations in a
191        // round have failed. To ensure Dusk (or anyone in possess of the Dusk
192        // private key) is not able to shortcircuit a round with an arbitrary
193        // block, nodes should only accept an Emergency Block if its timestamp
194        // is higher than the maximum time needed to run all round iterations.
195        // This guarantees the network has enough time to actually produce a
196        // block, if possible.
197        if is_emergency_block(header.iteration)
198            && header.timestamp
199                < self.prev_header.timestamp
200                    + MIN_EMERGENCY_BLOCK_TIME.as_secs()
201        {
202            return Err(HeaderError::BlockTimeLess);
203        }
204
205        let local_time = get_current_timestamp();
206
207        if header.timestamp > local_time + MARGIN_TIMESTAMP {
208            return Err(HeaderError::BlockTimeHigher(header.timestamp));
209        }
210
211        if header.prev_block_hash != self.prev_header.hash {
212            return Err(HeaderError::PrevBlockHash);
213        }
214
215        // Ensure block is not already in the ledger
216        let block_exists = self
217            .db
218            .read()
219            .await
220            .view(|db| db.block_exists(&header.hash))
221            .map_err(|e| {
222                HeaderError::Storage(
223                    "error checking Ledger::get_block_exists",
224                    e,
225                )
226            })?;
227
228        if block_exists {
229            return Err(HeaderError::BlockExists);
230        }
231
232        // Verify seed field
233        self.verify_seed_field(header.seed.inner(), &generator, header.height)?;
234
235        Ok(())
236    }
237
238    fn verify_seed_field(
239        &self,
240        seed: &[u8; 48],
241        pk: &MultisigPublicKey,
242        block_height: u64,
243    ) -> Result<(), HeaderError> {
244        let signature = MultisigSignature::from_bytes(seed).map_err(|err| {
245            HeaderError::InvalidSeed(format!(
246                "invalid seed signature bytes: {err:?}"
247            ))
248        })?;
249
250        core_bls::verify_multisig(
251            pk,
252            &signature,
253            self.prev_header.seed.inner(),
254            bls_version_at(block_height),
255        )
256        .map_err(|err| {
257            HeaderError::InvalidSeed(format!("invalid seed: {err:?}"))
258        })?;
259
260        Ok(())
261    }
262
263    async fn verify_prev_block_cert(
264        &self,
265        candidate_block: &'a ledger::Header,
266    ) -> Result<Vec<Voter>, HeaderError> {
267        if self.prev_header.height == 0
268            || is_emergency_block(self.prev_header.iteration)
269        {
270            return Ok(vec![]);
271        }
272
273        let prev_block_hash = candidate_block.prev_block_hash;
274
275        let prev_block_seed = self
276            .db
277            .read()
278            .await
279            .view(|v| v.block_header(&self.prev_header.prev_block_hash))
280            .map_err(|e| {
281                HeaderError::Storage(
282                    "error checking Ledger::fetch_block_header",
283                    e,
284                )
285            })?
286            .ok_or(HeaderError::Generic("Header not found"))
287            .map(|h| h.seed)?;
288
289        let voters = verify_att(
290            &candidate_block.prev_block_cert,
291            self.prev_header.to_consensus_header(),
292            prev_block_seed,
293            self.provisioners.prev(),
294            Some(RatificationResult::Success(Vote::Valid(prev_block_hash))),
295        )
296        .await?;
297
298        Ok(voters)
299    }
300
301    /// Verify the Failed Iterations field in a block.
302    ///
303    /// Return the number of attested failed iterations. We refer to this number
304    /// as Previous Non-Attested Iterations, or PNI
305    async fn verify_failed_iterations(
306        &self,
307        candidate_block: &'a ledger::Header,
308    ) -> Result<u8, FailedIterationError> {
309        let mut failed_atts = 0u8;
310
311        let att_list = &candidate_block.failed_iterations.att_list;
312
313        if att_list.len() > RELAX_ITERATION_THRESHOLD as usize {
314            return Err(FailedIterationError::TooMany(att_list.len()));
315        }
316
317        for (iter, att) in att_list.iter().enumerate() {
318            if let Some((att, pk)) = att {
319                debug!(event = "verify fail attestation", iter);
320
321                let expected_pk = self.provisioners.current().get_generator(
322                    iter as u8,
323                    self.prev_header.seed,
324                    candidate_block.height,
325                );
326
327                if pk != &expected_pk {
328                    return Err(FailedIterationError::InvalidGenerator(
329                        expected_pk,
330                    ));
331                }
332
333                let mut consensus_header =
334                    candidate_block.to_consensus_header();
335                consensus_header.iteration = iter as u8;
336
337                verify_att(
338                    att,
339                    consensus_header,
340                    self.prev_header.seed,
341                    self.provisioners.current(),
342                    Some(RatificationResult::Fail(Vote::default())),
343                )
344                .await?;
345
346                failed_atts += 1;
347            }
348        }
349
350        // In case of Emergency Block, which iteration number is u8::MAX, we
351        // count failed iterations up to CONSENSUS_MAX_ITER
352        let last_iter = cmp::min(candidate_block.iteration, CONSENSUS_MAX_ITER);
353
354        Ok(last_iter - failed_atts)
355    }
356
357    /// Extracts voters list of a block.
358    ///
359    /// Returns a list of voters with their credits for both ratification and
360    /// validation step
361    pub async fn get_voters(
362        blk: &'a ledger::Header,
363        provisioners: &Provisioners,
364        prev_block_seed: Seed,
365    ) -> Vec<Voter> {
366        let att = &blk.att;
367        let consensus_header = blk.to_consensus_header();
368
369        let committee = RwLock::new(CommitteeSet::new(provisioners));
370
371        let validation_voters = verifiers::get_step_voters(
372            &consensus_header,
373            &att.validation,
374            &committee,
375            prev_block_seed,
376            StepName::Validation,
377        )
378        .await;
379
380        let ratification_voters = verifiers::get_step_voters(
381            &consensus_header,
382            &att.ratification,
383            &committee,
384            prev_block_seed,
385            StepName::Ratification,
386        )
387        .await;
388
389        merge_voters(validation_voters, ratification_voters)
390    }
391
392    /// Verify faults inside a block.
393    pub async fn verify_faults(
394        &self,
395        current_height: u64,
396        faults: &[Fault],
397    ) -> Result<(), InvalidFault> {
398        verify_faults(self.db.clone(), current_height, faults).await
399    }
400}
401
402pub async fn verify_faults<DB: database::DB>(
403    db: Arc<RwLock<DB>>,
404    current_height: u64,
405    faults: &[Fault],
406) -> Result<(), InvalidFault> {
407    for f in faults {
408        let fault_header = f.validate(current_height)?;
409        if is_emergency_iter(fault_header.iteration) {
410            return Err(InvalidFault::EmergencyIteration);
411        }
412        db.read()
413            .await
414            .view(|db| {
415                let prev_header = db
416                    .block_header(&fault_header.prev_block_hash)?
417                    .ok_or(anyhow::anyhow!("Slashing a non accepted header"))?;
418                // No overflow here, since the header has been already validated
419                // not to be 0
420                if prev_header.height != fault_header.round - 1 {
421                    anyhow::bail!("Invalid height for fault");
422                }
423
424                // FIX_ME: Instead of fetching all store faults, check the fault
425                // id directly This needs the fault id to be
426                // changed into "HEIGHT|TYPE|PROV_KEY"
427                let start_height = fault_header.round.saturating_sub(EPOCH);
428                let stored_faults = db.faults_by_block(start_height)?;
429                if stored_faults.iter().any(|other| f.same(other)) {
430                    anyhow::bail!("Double fault detected");
431                }
432
433                Ok(())
434            })
435            .map_err(|e| InvalidFault::Other(format!("{e:?}")))?;
436    }
437    Ok(())
438}
439
440pub async fn verify_att(
441    att: &ledger::Attestation,
442    consensus_header: ConsensusHeader,
443    seed: Seed,
444    eligible_provisioners: &Provisioners,
445    expected_result: Option<RatificationResult>,
446) -> Result<Vec<Voter>, AttestationError> {
447    // Check expected result
448    if let Some(expected) = expected_result {
449        match (att.result, expected) {
450            // Both are Success and the inner Valid(Hash) values match
451            (
452                RatificationResult::Success(Vote::Valid(r_hash)),
453                RatificationResult::Success(Vote::Valid(e_hash)),
454            ) => {
455                if r_hash != e_hash {
456                    error!(
457                        "Invalid Attestation. Expected: Valid({:?}), got: Valid({:?})",
458                        hex::encode(e_hash),
459                        hex::encode(r_hash)
460                    );
461                    return Err(AttestationError::InvalidHash(e_hash, r_hash));
462                }
463            }
464            // Both are Fail
465            (RatificationResult::Fail(_), RatificationResult::Fail(_)) => {}
466            // All other mismatches
467            _ => {
468                error!(
469                    "Invalid Attestation. Expected: {:?}, got: {:?}",
470                    expected, att.result
471                );
472                return Err(AttestationError::InvalidResult(
473                    att.result, expected,
474                ));
475            }
476        }
477    }
478
479    let committee_set = RwLock::new(CommitteeSet::new(eligible_provisioners));
480    let vote = att.result.vote();
481
482    // Verify Validation votes
483    let validation_voters = verifiers::verify_step_votes(
484        &consensus_header,
485        vote,
486        &att.validation,
487        &committee_set,
488        seed,
489        StepName::Validation,
490    )
491    .await
492    .map_err(|s| AttestationError::InvalidVotes(StepName::Validation, s))?;
493
494    // Verify Ratification votes
495    let ratification_voters = verifiers::verify_step_votes(
496        &consensus_header,
497        vote,
498        &att.ratification,
499        &committee_set,
500        seed,
501        StepName::Ratification,
502    )
503    .await
504    .map_err(|s| AttestationError::InvalidVotes(StepName::Ratification, s))?;
505
506    let voters = merge_voters(validation_voters, ratification_voters);
507    Ok(voters)
508}
509
510/// Merges two Vec<Voter>, summing up the usize values if the PublicKey is
511/// repeated
512fn merge_voters(v1: Vec<Voter>, v2: Vec<Voter>) -> Vec<Voter> {
513    let mut voter_map = BTreeMap::new();
514
515    for (pk, count) in v1.into_iter().chain(v2.into_iter()) {
516        let counter = voter_map.entry(pk).or_default();
517        *counter += count;
518    }
519
520    voter_map.into_iter().collect()
521}