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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use massa_models::{
    active_block::ActiveBlock,
    address::Address,
    block::{Block, SecureShareBlock},
    block_header::SecuredHeader,
    block_id::BlockId,
    prehash::PreHashSet,
    slot::Slot,
};
use massa_storage::Storage;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum HeaderOrBlock {
    Header(SecuredHeader),
    Block {
        id: BlockId,
        slot: Slot,
        storage: Storage,
    },
}

impl HeaderOrBlock {
    /// Gets slot for that header or block
    pub fn get_slot(&self) -> Slot {
        match self {
            HeaderOrBlock::Header(header) => header.content.slot,
            HeaderOrBlock::Block { slot, .. } => *slot,
        }
    }
}

/// Something can be discarded
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DiscardReason {
    /// Block is invalid, either structurally, or because of some incompatibility. The String contains the reason for info or debugging.
    Invalid(String),
    /// Block is incompatible with a final block.
    Stale,
    /// Block has enough fitness.
    Final,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlockStatusId {
    Incoming = 0,
    WaitingForSlot = 1,
    WaitingForDependencies = 2,
    Active = 3,
    Discarded = 4,
}

impl From<&BlockStatus> for BlockStatusId {
    fn from(status: &BlockStatus) -> Self {
        match status {
            BlockStatus::Incoming(_) => BlockStatusId::Incoming,
            BlockStatus::WaitingForSlot(_) => BlockStatusId::WaitingForSlot,
            BlockStatus::WaitingForDependencies { .. } => BlockStatusId::WaitingForDependencies,
            BlockStatus::Active { .. } => BlockStatusId::Active,
            BlockStatus::Discarded { .. } => BlockStatusId::Discarded,
        }
    }
}

/// A structure defining whether we keep a full block with operations in storage
/// or just the raw signed block without operations
#[derive(Debug, Clone)]
pub enum StorageOrBlock {
    /// Keep a full storage with operations
    Storage(Storage),
    /// Keep only the block header and list of ops (but not the ops)
    Block(Box<SecureShareBlock>),
}

impl StorageOrBlock {
    /// Return a clone of the underlying block.
    /// This is used when we want to get a copy of the block that is referenced by a block status
    /// (and not a copy of the block status itself)
    pub fn clone_block(&self, block_id: &BlockId) -> SecureShareBlock {
        match self {
            StorageOrBlock::Storage(storage) => storage
                .read_blocks()
                .get(block_id)
                .expect("block absent from its own storage")
                .clone(),
            StorageOrBlock::Block(block) => *block.clone(),
        }
    }

    /// Convert any StorageOrBlock variant into a StorageOrBlock::Block variant.
    /// This effectively drops the operations of the block.
    pub fn strip_to_block(&mut self, block_id: &BlockId) {
        let block = if let StorageOrBlock::Storage(storage) = self {
            storage
                .read_blocks()
                .get(block_id)
                .expect("block absent from its own storage")
                .clone()
        } else {
            return;
        };
        *self = StorageOrBlock::Block(Box::new(block));
    }
}

/// Enum used in `BlockGraph`'s state machine
#[derive(Debug, Clone)]
pub enum BlockStatus {
    /// The block/header has reached consensus but no consensus-level check has been performed.
    /// It will be processed during the next iteration
    Incoming(HeaderOrBlock),
    /// The block's or header's slot is too much in the future.
    /// It will be processed at the block/header slot
    WaitingForSlot(HeaderOrBlock),
    /// The block references an unknown Block id
    WaitingForDependencies {
        /// Given header/block
        header_or_block: HeaderOrBlock,
        /// includes self if it's only a header
        unsatisfied_dependencies: PreHashSet<BlockId>,
        /// Used to limit and sort the number of blocks/headers waiting for dependencies
        sequence_number: u64,
    },
    /// The block was checked and included in the blockgraph
    Active {
        a_block: Box<ActiveBlock>,
        storage_or_block: StorageOrBlock,
    },
    /// The block was discarded and is kept to avoid reprocessing it
    Discarded {
        /// Just the slot of that block
        slot: Slot,
        /// Address of the creator of the block
        creator: Address,
        /// Ids of parents blocks
        parents: Vec<BlockId>,
        /// why it was discarded
        reason: DiscardReason,
        /// Used to limit and sort the number of blocks/headers waiting for dependencies
        sequence_number: u64,
    },
}

/// Block status in the graph that can be exported.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExportBlockStatus {
    /// received but not yet graph processed
    Incoming,
    /// waiting for its slot
    WaitingForSlot,
    /// waiting for a missing dependency
    WaitingForDependencies,
    /// valid and not yet final
    Active(Block),
    /// immutable
    Final(Block),
    /// not part of the graph
    Discarded(DiscardReason),
}

/// The block version that can be exported.
/// Note that the detailed list of operation is not exported
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportCompiledBlock {
    /// Header of the corresponding block.
    pub header: SecuredHeader,
    /// For (i, set) in children,
    /// set contains the headers' hashes
    /// of blocks referencing exported block as a parent,
    /// in thread i.
    pub children: Vec<PreHashSet<BlockId>>,
    /// Active or final
    pub is_final: bool,
}

/// Status
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum Status {
    /// without enough fitness to be part of immutable history
    Active,
    /// with enough fitness to be part of immutable history
    Final,
}