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