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
//! Copyright (c) 2023 MASSA LABS <info@massa.net>

//! Speculative list of previously executed denunciations, to prevent reuse.

use std::sync::Arc;

use parking_lot::RwLock;

use crate::active_history::{ActiveHistory, HistorySearchResult};
use massa_executed_ops::ExecutedDenunciationsChanges;
use massa_final_state::FinalStateController;
use massa_models::denunciation::DenunciationIndex;

/// Speculative state of executed denunciations
pub(crate) struct SpeculativeExecutedDenunciations {
    /// Thread-safe shared access to the final state. For reading only.
    final_state: Arc<RwLock<dyn FinalStateController>>,

    /// History of the outputs of recently executed slots.
    /// Slots should be consecutive, newest at the back.
    active_history: Arc<RwLock<ActiveHistory>>,

    /// executed operations: maps the operation ID to its validity slot end - included
    executed_denunciations: ExecutedDenunciationsChanges,
}

impl SpeculativeExecutedDenunciations {
    /// Creates a new `SpeculativeExecutedDenunciations`
    ///
    /// # Arguments
    /// * `final_state`: thread-safe shared access the the final state
    /// * `active_history`: thread-safe shared access the speculative execution history
    pub fn new(
        final_state: Arc<RwLock<dyn FinalStateController>>,
        active_history: Arc<RwLock<ActiveHistory>>,
    ) -> Self {
        Self {
            final_state,
            active_history,
            executed_denunciations: Default::default(),
        }
    }

    /// Returns the set of operation IDs caused to the `SpeculativeExecutedDenunciations` since
    /// its creation, and resets their local value to nothing
    pub fn take(&mut self) -> ExecutedDenunciationsChanges {
        std::mem::take(&mut self.executed_denunciations)
    }

    /// Takes a snapshot (clone) of the changes since its creation
    pub fn get_snapshot(&self) -> ExecutedDenunciationsChanges {
        self.executed_denunciations.clone()
    }

    /// Resets the `SpeculativeRollState` to a snapshot (see `get_snapshot` method)
    pub fn reset_to_snapshot(&mut self, snapshot: ExecutedDenunciationsChanges) {
        self.executed_denunciations = snapshot;
    }

    /// Checks if a denunciation was executed previously
    pub fn is_denunciation_executed(&self, de_idx: &DenunciationIndex) -> bool {
        // check in the current changes
        if self.executed_denunciations.contains(de_idx) {
            return true;
        }

        // check in the active history, backwards
        match self
            .active_history
            .read()
            .fetch_executed_denunciation(de_idx)
        {
            HistorySearchResult::Present(_) => {
                return true;
            }
            HistorySearchResult::NoInfo => {}
            HistorySearchResult::Absent => unreachable!(), // fetch_executed_denunciation does not return Absent
        }

        // check in the final state
        self.final_state
            .read()
            .get_executed_denunciations()
            .contains(de_idx)
    }

    /// Insert an executed denunciation.
    pub fn insert_executed_denunciation(&mut self, de_idx: DenunciationIndex) {
        self.executed_denunciations.insert(de_idx);
    }
}