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
//! Copyright (c) 2022 MASSA LABS <info@massa.net>
//! Speculative list of previously executed operations, to prevent reuse.
use crate::active_history::{ActiveHistory, HistorySearchResult};
use massa_executed_ops::ExecutedOpsChanges;
use massa_final_state::FinalStateController;
use massa_models::{operation::OperationId, slot::Slot};
use parking_lot::RwLock;
use std::sync::Arc;
/// Speculative state of executed operations
pub(crate) struct SpeculativeExecutedOps {
/// 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_ops: ExecutedOpsChanges,
}
impl SpeculativeExecutedOps {
/// Creates a new `SpeculativeExecutedOps`
///
/// # 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 {
SpeculativeExecutedOps {
final_state,
active_history,
executed_ops: Default::default(),
}
}
/// Returns the set of operation IDs caused to the `SpeculativeExecutedOps` since its creation,
/// and resets their local value to nothing
pub fn take(&mut self) -> ExecutedOpsChanges {
std::mem::take(&mut self.executed_ops)
}
/// Takes a snapshot (clone) of the changes caused to the `SpeculativeExecutedOps` since its creation
pub fn get_snapshot(&self) -> ExecutedOpsChanges {
self.executed_ops.clone()
}
/// Resets the `SpeculativeRollState` to a snapshot (see `get_snapshot` method)
pub fn reset_to_snapshot(&mut self, snapshot: ExecutedOpsChanges) {
self.executed_ops = snapshot;
}
/// Checks if an operation was executed previously
pub fn is_op_executed(&self, op_id: &OperationId) -> bool {
// check in the current changes
if self.executed_ops.contains_key(op_id) {
return true;
}
// check in the active history, backwards
match self.active_history.read().fetch_executed_op(op_id) {
HistorySearchResult::Present(_) => {
return true;
}
HistorySearchResult::NoInfo => {}
HistorySearchResult::Absent => unreachable!(), // fetch_executed_op does not return Absent
}
// check in the final state
self.final_state.read().executed_ops_contains(op_id)
}
/// Insert an executed operation.
/// Does not check for reuse, please use `SpeculativeExecutedOps::is_op_executed` before.
///
/// # Arguments
/// * `op_id`: operation ID
/// * `op_exec_status` : the status of the execution of the operation.
/// * `op_valid_until_slot`: slot until which the operation remains valid (included)
pub fn insert_executed_op(
&mut self,
op_id: OperationId,
op_exec_status: bool,
op_valid_until_slot: Slot,
) {
self.executed_ops
.insert(op_id, (op_exec_status, op_valid_until_slot));
}
}