consensus_core/
context.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{sync::Arc, time::SystemTime};
6
7use consensus_config::{AuthorityIndex, Committee, Parameters};
8#[cfg(test)]
9use consensus_config::{NetworkKeyPair, ProtocolKeyPair};
10use iota_protocol_config::ProtocolConfig;
11#[cfg(test)]
12use tempfile::TempDir;
13use tokio::time::Instant;
14
15#[cfg(test)]
16use crate::metrics::test_metrics;
17use crate::{block::BlockTimestampMs, metrics::Metrics};
18
19/// Context contains per-epoch configuration and metrics shared by all
20/// components of this authority.
21#[derive(Clone)]
22pub(crate) struct Context {
23    /// Index of this authority in the committee.
24    pub own_index: AuthorityIndex,
25    /// Committee of the current epoch.
26    pub committee: Committee,
27    /// Parameters of this authority.
28    pub parameters: Parameters,
29    /// Protocol configuration of current epoch.
30    pub protocol_config: ProtocolConfig,
31    /// Metrics of this authority.
32    pub metrics: Arc<Metrics>,
33    /// Access to local clock
34    pub clock: Arc<Clock>,
35}
36
37impl Context {
38    pub(crate) fn new(
39        own_index: AuthorityIndex,
40        committee: Committee,
41        parameters: Parameters,
42        protocol_config: ProtocolConfig,
43        metrics: Arc<Metrics>,
44        clock: Arc<Clock>,
45    ) -> Self {
46        Self {
47            own_index,
48            committee,
49            parameters,
50            protocol_config,
51            metrics,
52            clock,
53        }
54    }
55
56    /// Create a test context with a committee of given size and even stake
57    #[cfg(test)]
58    pub(crate) fn new_for_test(
59        committee_size: usize,
60    ) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
61        let (committee, keypairs) =
62            consensus_config::local_committee_and_keys(0, vec![1; committee_size]);
63        let metrics = test_metrics();
64        let temp_dir = TempDir::new().unwrap();
65        let clock = Arc::new(Clock::new());
66
67        let context = Context::new(
68            AuthorityIndex::new_for_test(0),
69            committee,
70            Parameters {
71                db_path: temp_dir.into_path(),
72                ..Default::default()
73            },
74            ProtocolConfig::get_for_max_version_UNSAFE(),
75            metrics,
76            clock,
77        );
78        (context, keypairs)
79    }
80
81    #[cfg(test)]
82    pub(crate) fn with_authority_index(mut self, authority: AuthorityIndex) -> Self {
83        self.own_index = authority;
84        self
85    }
86
87    #[cfg(test)]
88    pub(crate) fn with_committee(mut self, committee: Committee) -> Self {
89        self.committee = committee;
90        self
91    }
92
93    #[cfg(test)]
94    pub(crate) fn with_parameters(mut self, parameters: Parameters) -> Self {
95        self.parameters = parameters;
96        self
97    }
98}
99
100/// A clock that allows to derive the current UNIX system timestamp while
101/// guaranteeing that timestamp will be monotonically incremented, tolerating
102/// ntp and system clock changes and corrections. Explicitly avoid to make
103/// `[Clock]` cloneable to ensure that a single instance is shared behind an
104/// `[Arc]` wherever is needed in order to make sure that consecutive calls to
105/// receive the system timestamp will remain monotonically increasing.
106pub(crate) struct Clock {
107    initial_instant: Instant,
108    initial_system_time: SystemTime,
109}
110
111impl Clock {
112    pub fn new() -> Self {
113        Self {
114            initial_instant: Instant::now(),
115            initial_system_time: SystemTime::now(),
116        }
117    }
118
119    // Returns the current time expressed as UNIX timestamp in milliseconds.
120    // Calculated with Tokio Instant to ensure monotonicity,
121    // and to allow testing with tokio clock.
122    pub(crate) fn timestamp_utc_ms(&self) -> BlockTimestampMs {
123        let now: Instant = Instant::now();
124        let monotonic_system_time = self
125            .initial_system_time
126            .checked_add(
127                now.checked_duration_since(self.initial_instant)
128                    .unwrap_or_else(|| {
129                        panic!(
130                            "current instant ({:?}) < initial instant ({:?})",
131                            now, self.initial_instant
132                        )
133                    }),
134            )
135            .expect("Computing system time should not overflow");
136        monotonic_system_time
137            .duration_since(SystemTime::UNIX_EPOCH)
138            .unwrap_or_else(|_| {
139                panic!(
140                    "system time ({:?}) < UNIX_EPOCH ({:?})",
141                    monotonic_system_time,
142                    SystemTime::UNIX_EPOCH,
143                )
144            })
145            .as_millis() as BlockTimestampMs
146    }
147}