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, ConsensusProtocolConfig, Parameters};
7use consensus_config::{NetworkKeyPair, ProtocolKeyPair};
8use consensus_types::block::BlockTimestampMs;
9use tempfile::TempDir;
10use tokio::time::Instant;
11
12use crate::metrics::Metrics;
13use crate::metrics::test_metrics;
14
15/// Context contains per-epoch configuration and metrics shared by all components
16/// of this authority.
17#[derive(Clone)]
18pub struct Context {
19    /// Timestamp of the start of the current epoch.
20    pub epoch_start_timestamp_ms: u64,
21    /// Index of this authority in the committee.
22    pub own_index: AuthorityIndex,
23    /// Committee of the current epoch.
24    pub committee: Committee,
25    /// Parameters of this authority.
26    pub parameters: Parameters,
27    /// Protocol configuration of current epoch.
28    pub protocol_config: ConsensusProtocolConfig,
29    /// Metrics of this authority.
30    pub metrics: Arc<Metrics>,
31    /// Access to local clock
32    pub clock: Arc<Clock>,
33}
34
35impl Context {
36    pub(crate) fn new(
37        epoch_start_timestamp_ms: u64,
38        own_index: Option<AuthorityIndex>,
39        committee: Committee,
40        parameters: Parameters,
41        protocol_config: ConsensusProtocolConfig,
42        metrics: Arc<Metrics>,
43        clock: Arc<Clock>,
44    ) -> Self {
45        let own_index = if let Some(own_index) = own_index {
46            own_index
47        } else {
48            // If no index is provided, then this is an observer node. We assign a max index to it as a special value.
49            AuthorityIndex::MAX
50        };
51
52        Self {
53            epoch_start_timestamp_ms,
54            own_index,
55            committee,
56            parameters,
57            protocol_config,
58            metrics,
59            clock,
60        }
61    }
62
63    /// Create a test context with a committee of given size and even stake
64    pub fn new_for_test(committee_size: usize) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
65        Self::new_with_test_options(committee_size, true)
66    }
67
68    /// Create a test context with a committee of given size and even stake
69    pub fn new_with_test_options(
70        committee_size: usize,
71        unused_port: bool,
72    ) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
73        let (committee, keypairs) = consensus_config::local_committee_and_keys_with_test_options(
74            0,
75            vec![1; committee_size],
76            unused_port,
77        );
78        let metrics = test_metrics();
79        let temp_dir = TempDir::new().unwrap();
80        let clock = Arc::new(Clock::default());
81
82        let context = Context::new(
83            0,
84            Some(AuthorityIndex::new_for_test(0)),
85            committee,
86            Parameters {
87                db_path: temp_dir.keep(),
88                ..Default::default()
89            },
90            ConsensusProtocolConfig::for_testing(),
91            metrics,
92            clock,
93        );
94        (context, keypairs)
95    }
96
97    pub fn with_epoch_start_timestamp_ms(mut self, epoch_start_timestamp_ms: u64) -> Self {
98        self.epoch_start_timestamp_ms = epoch_start_timestamp_ms;
99        self
100    }
101
102    pub fn with_authority_index(mut self, authority: AuthorityIndex) -> Self {
103        self.own_index = authority;
104        self
105    }
106
107    pub fn with_committee(mut self, committee: Committee) -> Self {
108        self.committee = committee;
109        self
110    }
111
112    pub fn with_parameters(mut self, parameters: Parameters) -> Self {
113        self.parameters = parameters;
114        self
115    }
116
117    pub fn with_protocol_config(mut self, protocol_config: ConsensusProtocolConfig) -> Self {
118        self.protocol_config = protocol_config;
119        self
120    }
121
122    /// Returns true if this node is a validator (i.e., part of the committee).
123    pub fn is_validator(&self) -> bool {
124        self.committee.is_valid_index(self.own_index)
125    }
126
127    /// Returns true if this node is an observer (i.e., not part of the committee).
128    pub fn is_observer(&self) -> bool {
129        !self.is_validator()
130    }
131}
132
133/// A clock that allows to derive the current UNIX system timestamp while guaranteeing that timestamp
134/// will be monotonically incremented, tolerating ntp and system clock changes and corrections.
135/// Explicitly avoid to make `[Clock]` cloneable to ensure that a single instance is shared behind an `[Arc]`
136/// wherever is needed in order to make sure that consecutive calls to receive the system timestamp
137/// will remain monotonically increasing.
138pub struct Clock {
139    initial_instant: Instant,
140    initial_system_time: SystemTime,
141    // `clock_drift` should be used only for testing
142    clock_drift: BlockTimestampMs,
143}
144
145impl Default for Clock {
146    fn default() -> Self {
147        Self {
148            initial_instant: Instant::now(),
149            initial_system_time: SystemTime::now(),
150            clock_drift: 0,
151        }
152    }
153}
154
155impl Clock {
156    pub fn new_for_test(clock_drift: BlockTimestampMs) -> Self {
157        Self {
158            initial_instant: Instant::now(),
159            initial_system_time: SystemTime::now(),
160            clock_drift,
161        }
162    }
163
164    // Returns the current time expressed as UNIX timestamp in milliseconds.
165    // Calculated with Tokio Instant to ensure monotonicity,
166    // and to allow testing with tokio clock.
167    pub(crate) fn timestamp_utc_ms(&self) -> BlockTimestampMs {
168        if cfg!(not(any(msim, test))) {
169            assert_eq!(
170                self.clock_drift, 0,
171                "Clock drift should not be set in non testing environments."
172            );
173        }
174
175        let now: Instant = Instant::now();
176        let monotonic_system_time = self
177            .initial_system_time
178            .checked_add(
179                now.checked_duration_since(self.initial_instant)
180                    .unwrap_or_else(|| {
181                        panic!(
182                            "current instant ({:?}) < initial instant ({:?})",
183                            now, self.initial_instant
184                        )
185                    }),
186            )
187            .expect("Computing system time should not overflow");
188        monotonic_system_time
189            .duration_since(SystemTime::UNIX_EPOCH)
190            .unwrap_or_else(|_| {
191                panic!(
192                    "system time ({:?}) < UNIX_EPOCH ({:?})",
193                    monotonic_system_time,
194                    SystemTime::UNIX_EPOCH,
195                )
196            })
197            .as_millis() as BlockTimestampMs
198            + self.clock_drift
199    }
200}