1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! The module provides the functionality that verifies the blocks and headers based
//! on the used consensus.

use crate::block_verifier::config::Config;
use anyhow::ensure;
use fuel_core_poa::ports::Database as PoAVerifierDatabase;
use fuel_core_storage::transactional::AtomicView;
use fuel_core_types::{
    blockchain::{
        block::Block,
        consensus::Consensus,
        header::BlockHeader,
        primitives::DaBlockHeight,
        SealedBlockHeader,
    },
    fuel_types::{
        BlockHeight,
        Bytes32,
    },
    tai64::Tai64,
};

pub mod config;

#[cfg(test)]
mod tests;

/// Verifier is responsible for validation of the blocks and headers.
pub struct Verifier<V> {
    config: Config,
    view_provider: V,
}

impl<V> Verifier<V> {
    /// Creates a new instance of the verifier.
    pub fn new(config: Config, view_provider: V) -> Self {
        Self {
            config,
            view_provider,
        }
    }
}

impl<V> Verifier<V>
where
    V: AtomicView,
    V::View: PoAVerifierDatabase,
{
    /// Verifies **all** fields of the block based on used consensus to produce a block.
    ///
    /// Return an error if the verification failed, otherwise `Ok(())`.
    pub fn verify_block_fields(
        &self,
        consensus: &Consensus,
        block: &Block,
    ) -> anyhow::Result<()> {
        match consensus {
            Consensus::Genesis(_) => {
                let expected_genesis_height = self.config.block_height;
                let expected_genesis_da_height = self.config.da_block_height;
                verify_genesis_block_fields(
                    expected_genesis_height,
                    expected_genesis_da_height,
                    block.header(),
                )
            }
            Consensus::PoA(_) => {
                let view = self.view_provider.latest_view();
                fuel_core_poa::verifier::verify_block_fields(&view, block)
            }
            _ => Err(anyhow::anyhow!("Unsupported consensus: {:?}", consensus)),
        }
    }

    /// Verifies the consensus of the block header.
    pub fn verify_consensus(&self, header: &SealedBlockHeader) -> bool {
        let SealedBlockHeader {
            entity: header,
            consensus,
        } = header;
        match consensus {
            Consensus::Genesis(_) => true,
            Consensus::PoA(consensus) => fuel_core_poa::verifier::verify_consensus(
                &self.config.consensus,
                header,
                consensus,
            ),
            _ => false,
        }
    }
}

fn verify_genesis_block_fields(
    expected_genesis_height: BlockHeight,
    expected_genesis_da_height: DaBlockHeight,
    header: &BlockHeader,
) -> anyhow::Result<()> {
    let actual_genesis_height = *header.height();

    ensure!(
        header.prev_root() == &Bytes32::zeroed(),
        "The genesis previous root should be zeroed"
    );
    ensure!(
        header.time() == Tai64::UNIX_EPOCH,
        "The genesis time should be unix epoch time"
    );
    ensure!(
        header.da_height == expected_genesis_da_height,
        "The genesis `da_height` is not as expected"
    );
    ensure!(
        expected_genesis_height == actual_genesis_height,
        "The genesis height is not as expected"
    );
    Ok(())
}