openmina_core/block/
prevalidate.rs

1use serde::{Deserialize, Serialize};
2
3use super::ArcBlockWithHash;
4use crate::constants::PROTOCOL_VERSION;
5
6#[derive(Serialize, Deserialize, Debug, Clone)]
7pub enum BlockPrevalidationError {
8    GenesisNotReady,
9    ReceivedTooEarly {
10        current_global_slot: u32,
11        block_global_slot: u32,
12    },
13    ReceivedTooLate {
14        current_global_slot: u32,
15        block_global_slot: u32,
16        delta: u32,
17    },
18    InvalidGenesisProtocolState,
19    InvalidProtocolVersion,
20    MismatchedProtocolVersion,
21    ConsantsMismatch,
22    InvalidDeltaBlockChainProof,
23}
24
25impl BlockPrevalidationError {
26    pub fn is_forever_invalid(&self) -> bool {
27        !matches!(self, Self::ReceivedTooEarly { .. })
28    }
29}
30
31pub fn validate_block_timing(
32    block: &ArcBlockWithHash,
33    genesis: &ArcBlockWithHash,
34    cur_global_slot: u32,
35    allow_block_too_late: bool,
36) -> Result<(), BlockPrevalidationError> {
37    let block_global_slot = block.global_slot();
38    let delta = genesis.constants().delta.as_u32();
39
40    if cur_global_slot < block_global_slot {
41        return Err(BlockPrevalidationError::ReceivedTooEarly {
42            current_global_slot: cur_global_slot,
43            block_global_slot,
44        });
45    } else if !allow_block_too_late && cur_global_slot.saturating_sub(block_global_slot) > delta {
46        return Err(BlockPrevalidationError::ReceivedTooLate {
47            current_global_slot: cur_global_slot,
48            block_global_slot,
49            delta,
50        });
51    }
52
53    Ok(())
54}
55
56pub fn validate_genesis_state(
57    block: &ArcBlockWithHash,
58    genesis: &ArcBlockWithHash,
59) -> Result<(), BlockPrevalidationError> {
60    if block.header().genesis_state_hash() != genesis.hash() {
61        return Err(BlockPrevalidationError::InvalidGenesisProtocolState);
62    }
63    Ok(())
64}
65
66pub fn validate_protocol_versions(block: &ArcBlockWithHash) -> Result<(), BlockPrevalidationError> {
67    let min_transaction_version = 1.into();
68    let v = &block.header().current_protocol_version;
69    let nv = block
70        .header()
71        .proposed_protocol_version_opt
72        .as_ref()
73        .unwrap_or(v);
74
75    // Our version values are unsigned, so there is no need to check that the
76    // other parts are not negative.
77    let valid =
78        v.transaction >= min_transaction_version && nv.transaction >= min_transaction_version;
79    if !valid {
80        return Err(BlockPrevalidationError::InvalidProtocolVersion);
81    }
82
83    let compatible =
84        v.transaction == PROTOCOL_VERSION.transaction && v.network == PROTOCOL_VERSION.network;
85    if !compatible {
86        return Err(BlockPrevalidationError::MismatchedProtocolVersion);
87    }
88
89    Ok(())
90}
91
92pub fn validate_constants(
93    block: &ArcBlockWithHash,
94    genesis: &ArcBlockWithHash,
95) -> Result<(), BlockPrevalidationError> {
96    // NOTE: currently these cannot change between blocks, but that
97    // may not always be true?
98    if block.constants() != genesis.constants() {
99        return Err(BlockPrevalidationError::ConsantsMismatch);
100    }
101    Ok(())
102}
103
104pub fn prevalidate_block(
105    block: &ArcBlockWithHash,
106    genesis: &ArcBlockWithHash,
107    cur_global_slot: u32,
108    allow_block_too_late: bool,
109) -> Result<(), BlockPrevalidationError> {
110    validate_block_timing(block, genesis, cur_global_slot, allow_block_too_late)?;
111    validate_genesis_state(block, genesis)?;
112    validate_protocol_versions(block)?;
113    validate_constants(block, genesis)?;
114
115    // TODO(tizoc): check for InvalidDeltaBlockChainProof
116    // <https://github.com/MinaProtocol/mina/blob/d800da86a764d8d37ffb8964dd8d54d9f522b358/src/lib/mina_block/validation.ml#L369>
117    // <https://github.com/MinaProtocol/mina/blob/d800da86a764d8d37ffb8964dd8d54d9f522b358/src/lib/transition_chain_verifier/transition_chain_verifier.ml>
118
119    Ok(())
120}