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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! The module provides the functionality that verifies the blocks and headers based
//! on the used consensus.

pub mod config;

#[cfg(test)]
mod tests;

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

/// Verifier is responsible for validation of the blocks and headers.
pub struct Verifier<D, R> {
    config: Config,
    database: D,
    relayer: R,
}

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

impl<D, R> Verifier<D, R>
where
    D: PoAVerifierDatabase,
    R: RelayerPort,
{
    /// 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
                    .chain_config
                    .initial_state
                    .as_ref()
                    .map(|config| config.height.unwrap_or_else(|| 0u32.into()))
                    .unwrap_or_else(|| 0u32.into());
                verify_genesis_block_fields(expected_genesis_height, block.header())
            }
            Consensus::PoA(_) => fuel_core_poa::verifier::verify_block_fields(
                &self.config.poa,
                &self.database,
                block,
            ),
        }
    }

    /// 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.chain_config.consensus,
                header,
                consensus,
            ),
        }
    }

    /// Wait for the relayer to be in sync with the given DA height
    /// if the `da_height` is within the range of the current
    /// relayer sync'd height - `max_da_lag`.
    pub async fn await_da_height(&self, da_height: &DaBlockHeight) -> anyhow::Result<()> {
        tokio::time::timeout(
            self.config.relayer.max_wait_time,
            self.relayer
                .await_until_if_in_range(da_height, &self.config.relayer.max_da_lag),
        )
        .await?
    }
}

fn verify_genesis_block_fields(
    expected_genesis_height: BlockHeight,
    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!(
        // TODO: Set `da_height` based on the chain config.
        header.da_height == Default::default(),
        "The genesis `da_height` is not as expected"
    );
    ensure!(
        expected_genesis_height == actual_genesis_height,
        "The genesis height is not as expected"
    );
    Ok(())
}