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: AuthorityIndex,
39        committee: Committee,
40        parameters: Parameters,
41        protocol_config: ConsensusProtocolConfig,
42        metrics: Arc<Metrics>,
43        clock: Arc<Clock>,
44    ) -> Self {
45        Self {
46            epoch_start_timestamp_ms,
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    pub fn new_for_test(committee_size: usize) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
58        Self::new_with_test_options(committee_size, true)
59    }
60
61    /// Create a test context with a committee of given size and even stake
62    pub fn new_with_test_options(
63        committee_size: usize,
64        unused_port: bool,
65    ) -> (Self, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
66        let (committee, keypairs) = consensus_config::local_committee_and_keys_with_test_options(
67            0,
68            vec![1; committee_size],
69            unused_port,
70        );
71        let metrics = test_metrics();
72        let temp_dir = TempDir::new().unwrap();
73        let clock = Arc::new(Clock::default());
74
75        let context = Context::new(
76            0,
77            AuthorityIndex::new_for_test(0),
78            committee,
79            Parameters {
80                db_path: temp_dir.keep(),
81                ..Default::default()
82            },
83            ConsensusProtocolConfig::for_testing(),
84            metrics,
85            clock,
86        );
87        (context, keypairs)
88    }
89
90    pub fn with_epoch_start_timestamp_ms(mut self, epoch_start_timestamp_ms: u64) -> Self {
91        self.epoch_start_timestamp_ms = epoch_start_timestamp_ms;
92        self
93    }
94
95    pub fn with_authority_index(mut self, authority: AuthorityIndex) -> Self {
96        self.own_index = authority;
97        self
98    }
99
100    pub fn with_committee(mut self, committee: Committee) -> Self {
101        self.committee = committee;
102        self
103    }
104
105    pub fn with_parameters(mut self, parameters: Parameters) -> Self {
106        self.parameters = parameters;
107        self
108    }
109
110    pub fn with_protocol_config(mut self, protocol_config: ConsensusProtocolConfig) -> Self {
111        self.protocol_config = protocol_config;
112        self
113    }
114
115    /// Returns true if this node is a validator (i.e., part of the committee).
116    pub fn is_validator(&self) -> bool {
117        self.committee.is_valid_index(self.own_index)
118    }
119}
120
121/// A clock that allows to derive the current UNIX system timestamp while guaranteeing that timestamp
122/// will be monotonically incremented, tolerating ntp and system clock changes and corrections.
123/// Explicitly avoid to make `[Clock]` cloneable to ensure that a single instance is shared behind an `[Arc]`
124/// wherever is needed in order to make sure that consecutive calls to receive the system timestamp
125/// will remain monotonically increasing.
126pub struct Clock {
127    initial_instant: Instant,
128    initial_system_time: SystemTime,
129    // `clock_drift` should be used only for testing
130    clock_drift: BlockTimestampMs,
131}
132
133impl Default for Clock {
134    fn default() -> Self {
135        Self {
136            initial_instant: Instant::now(),
137            initial_system_time: SystemTime::now(),
138            clock_drift: 0,
139        }
140    }
141}
142
143impl Clock {
144    pub fn new_for_test(clock_drift: BlockTimestampMs) -> Self {
145        Self {
146            initial_instant: Instant::now(),
147            initial_system_time: SystemTime::now(),
148            clock_drift,
149        }
150    }
151
152    // Returns the current time expressed as UNIX timestamp in milliseconds.
153    // Calculated with Tokio Instant to ensure monotonicity,
154    // and to allow testing with tokio clock.
155    pub(crate) fn timestamp_utc_ms(&self) -> BlockTimestampMs {
156        if cfg!(not(any(msim, test))) {
157            assert_eq!(
158                self.clock_drift, 0,
159                "Clock drift should not be set in non testing environments."
160            );
161        }
162
163        let now: Instant = Instant::now();
164        let monotonic_system_time = self
165            .initial_system_time
166            .checked_add(
167                now.checked_duration_since(self.initial_instant)
168                    .unwrap_or_else(|| {
169                        panic!(
170                            "current instant ({:?}) < initial instant ({:?})",
171                            now, self.initial_instant
172                        )
173                    }),
174            )
175            .expect("Computing system time should not overflow");
176        monotonic_system_time
177            .duration_since(SystemTime::UNIX_EPOCH)
178            .unwrap_or_else(|_| {
179                panic!(
180                    "system time ({:?}) < UNIX_EPOCH ({:?})",
181                    monotonic_system_time,
182                    SystemTime::UNIX_EPOCH,
183                )
184            })
185            .as_millis() as BlockTimestampMs
186            + self.clock_drift
187    }
188}