consensus_core/
context.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{sync::Arc, time::SystemTime};
5
6use consensus_config::{AuthorityIndex, Committee, Parameters};
7use consensus_config::{NetworkKeyPair, ProtocolKeyPair};
8use consensus_types::block::BlockTimestampMs;
9use sui_protocol_config::ProtocolConfig;
10use tempfile::TempDir;
11use tokio::time::Instant;
12
13use crate::metrics::Metrics;
14use crate::metrics::test_metrics;
15
16/// Context contains per-epoch configuration and metrics shared by all components
17/// of this authority.
18#[derive(Clone)]
19pub struct Context {
20    /// Timestamp of the start of the current epoch.
21    pub epoch_start_timestamp_ms: u64,
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        epoch_start_timestamp_ms: u64,
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            epoch_start_timestamp_ms,
48            own_index,
49            committee,
50            parameters,
51            protocol_config,
52            metrics,
53            clock,
54        }
55    }
56
57    /// Create a test context with a committee of given size and even stake
58    pub fn new_for_test(committee_size: usize) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
59        Self::new_with_test_options(committee_size, true)
60    }
61
62    /// Create a test context with a committee of given size and even stake
63    pub fn new_with_test_options(
64        committee_size: usize,
65        unused_port: bool,
66    ) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
67        let (committee, keypairs) = consensus_config::local_committee_and_keys_with_test_options(
68            0,
69            vec![1; committee_size],
70            unused_port,
71        );
72        let metrics = test_metrics();
73        let temp_dir = TempDir::new().unwrap();
74        let clock = Arc::new(Clock::default());
75
76        let context = Context::new(
77            0,
78            AuthorityIndex::new_for_test(0),
79            committee,
80            Parameters {
81                db_path: temp_dir.keep(),
82                ..Default::default()
83            },
84            ProtocolConfig::get_for_max_version_UNSAFE(),
85            metrics,
86            clock,
87        );
88        (context, keypairs)
89    }
90
91    pub fn with_epoch_start_timestamp_ms(mut self, epoch_start_timestamp_ms: u64) -> Self {
92        self.epoch_start_timestamp_ms = epoch_start_timestamp_ms;
93        self
94    }
95
96    pub fn with_authority_index(mut self, authority: AuthorityIndex) -> Self {
97        self.own_index = authority;
98        self
99    }
100
101    pub fn with_committee(mut self, committee: Committee) -> Self {
102        self.committee = committee;
103        self
104    }
105
106    pub fn with_parameters(mut self, parameters: Parameters) -> Self {
107        self.parameters = parameters;
108        self
109    }
110
111    pub fn with_protocol_config(mut self, protocol_config: ProtocolConfig) -> Self {
112        self.protocol_config = protocol_config;
113        self
114    }
115}
116
117/// A clock that allows to derive the current UNIX system timestamp while guaranteeing that timestamp
118/// will be monotonically incremented, tolerating ntp and system clock changes and corrections.
119/// Explicitly avoid to make `[Clock]` cloneable to ensure that a single instance is shared behind an `[Arc]`
120/// wherever is needed in order to make sure that consecutive calls to receive the system timestamp
121/// will remain monotonically increasing.
122pub struct Clock {
123    initial_instant: Instant,
124    initial_system_time: SystemTime,
125    // `clock_drift` should be used only for testing
126    clock_drift: BlockTimestampMs,
127}
128
129impl Default for Clock {
130    fn default() -> Self {
131        Self {
132            initial_instant: Instant::now(),
133            initial_system_time: SystemTime::now(),
134            clock_drift: 0,
135        }
136    }
137}
138
139impl Clock {
140    pub fn new_for_test(clock_drift: BlockTimestampMs) -> Self {
141        Self {
142            initial_instant: Instant::now(),
143            initial_system_time: SystemTime::now(),
144            clock_drift,
145        }
146    }
147
148    // Returns the current time expressed as UNIX timestamp in milliseconds.
149    // Calculated with Tokio Instant to ensure monotonicity,
150    // and to allow testing with tokio clock.
151    pub(crate) fn timestamp_utc_ms(&self) -> BlockTimestampMs {
152        if cfg!(not(any(msim, test))) {
153            assert_eq!(
154                self.clock_drift, 0,
155                "Clock drift should not be set in non testing environments."
156            );
157        }
158
159        let now: Instant = Instant::now();
160        let monotonic_system_time = self
161            .initial_system_time
162            .checked_add(
163                now.checked_duration_since(self.initial_instant)
164                    .unwrap_or_else(|| {
165                        panic!(
166                            "current instant ({:?}) < initial instant ({:?})",
167                            now, self.initial_instant
168                        )
169                    }),
170            )
171            .expect("Computing system time should not overflow");
172        monotonic_system_time
173            .duration_since(SystemTime::UNIX_EPOCH)
174            .unwrap_or_else(|_| {
175                panic!(
176                    "system time ({:?}) < UNIX_EPOCH ({:?})",
177                    monotonic_system_time,
178                    SystemTime::UNIX_EPOCH,
179                )
180            })
181            .as_millis() as BlockTimestampMs
182            + self.clock_drift
183    }
184}