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
41const MARGIN_TIMESTAMP: u64 = 3;
42
43// TODO: Use thiserror instead of anyhow
44
45/// An implementation of the all validation checks of a candidate block header
46/// according to current context
47pub(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    /// Verifies all header fields except state root and event bloom
67    ///
68    /// # Arguments
69    ///
70    /// * `header` - the block header to verify
71    /// * `expected_generator` - the generator extracted by the Deterministic
72    ///   Sortition algorithm
73    /// * `check_attestation` - `true` if the Attestation has to be verified
74    ///   (this should be `false` for Candidate headers, for which no
75    ///   Attestation has been yet produced)
76    ///
77    /// # Returns
78    ///
79    /// * the number of Previous Non-Attested Iterations (PNI)
80    /// * the voters of the previous block Certificate
81    /// * the voters of current block Attestation (if `check_attestation` is
82    ///   `true`)
83    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        // Get generator MultisigPublicKey
122        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        // Verify block signature
137        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    /// Performs sanity checks for basic fields of the block header
159    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        // Ensure rule of minimum block time is addressed
183        if header.timestamp < self.prev_header.timestamp + *MINIMUM_BLOCK_TIME {
184            return Err(HeaderError::BlockTimeLess);
185        }
186
187        // The Emergency Block can only be produced after all iterations in a
188        // round have failed. To ensure Dusk (or anyone in possess of the Dusk
189        // private key) is not able to shortcircuit a round with an arbitrary
190        // block, nodes should only accept an Emergency Block if its timestamp
191        // is higher than the maximum time needed to run all round iterations.
192        // This guarantees the network has enough time to actually produce a
193        // block, if possible.
194        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        // Ensure block is not already in the ledger
213        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        // Verify seed field
230        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    /// Verify the Failed Iterations field in a block.
299    ///
300    /// Return the number of attested failed iterations. We refer to this number
301    /// as Previous Non-Attested Iterations, or PNI
302    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        // In case of Emergency Block, which iteration number is u8::MAX, we
348        // count failed iterations up to CONSENSUS_MAX_ITER
349        let last_iter = cmp::min(candidate_block.iteration, CONSENSUS_MAX_ITER);
350
351        Ok(last_iter - failed_atts)
352    }
353
354    /// Extracts voters list of a block.
355    ///
356    /// Returns a list of voters with their credits for both ratification and
357    /// validation step
358    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    /// Verify faults inside a block.
390    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                // No overflow here, since the header has been already validated
416                // not to be 0
417                if prev_header.height != fault_header.round - 1 {
418                    anyhow::bail!("Invalid height for fault");
419                }
420
421                // FIX_ME: Instead of fetching all store faults, check the fault
422                // id directly This needs the fault id to be
423                // changed into "HEIGHT|TYPE|PROV_KEY"
424                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    // Check expected result
445    if let Some(expected) = expected_result {
446        match (att.result, expected) {
447            // Both are Success and the inner Valid(Hash) values match
448            (
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            // Both are Fail
462            (RatificationResult::Fail(_), RatificationResult::Fail(_)) => {}
463            // All other mismatches
464            _ => {
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    // Verify Validation votes
480    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    // Verify Ratification votes
492    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
507/// Merges two Vec<Voter>, summing up the usize values if the PublicKey is
508/// repeated
509fn 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}