sui_simulator/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4#[cfg(msim)]
5pub use msim::*;
6
7#[cfg(msim)]
8use std::hash::Hasher;
9
10use std::sync::atomic::{AtomicUsize, Ordering};
11
12// Re-export things used by sui-macros
13pub use ::rand as rand_crate;
14pub use anemo;
15pub use anemo_tower;
16pub use fastcrypto;
17pub use lru;
18pub use move_package_alt;
19pub use move_package_alt_compilation;
20pub use mysten_network;
21pub use sui_framework;
22pub use sui_move_build;
23pub use sui_types;
24pub use telemetry_subscribers;
25pub use tempfile;
26pub use tower;
27
28#[cfg(msim)]
29pub mod configs {
30    use msim::*;
31    use std::collections::HashMap;
32    use std::ops::Range;
33    use std::time::Duration;
34
35    use tracing::info;
36
37    fn ms_to_dur(range: Range<u64>) -> Range<Duration> {
38        Duration::from_millis(range.start)..Duration::from_millis(range.end)
39    }
40
41    /// A network with constant uniform latency.
42    pub fn constant_latency_ms(latency: u64) -> SimConfig {
43        uniform_latency_ms(latency..(latency + 1))
44    }
45
46    /// A network with latency sampled uniformly from a range.
47    pub fn uniform_latency_ms(range: Range<u64>) -> SimConfig {
48        let range = ms_to_dur(range);
49        SimConfig {
50            net: NetworkConfig {
51                latency: LatencyConfig {
52                    default_latency: LatencyDistribution::uniform(range),
53                    ..Default::default()
54                },
55                ..Default::default()
56            },
57        }
58    }
59
60    /// A network with bimodal latency.
61    pub fn bimodal_latency_ms(
62        // The typical latency.
63        baseline: Range<u64>,
64        // The exceptional latency.
65        degraded: Range<u64>,
66        // The frequency (from 0.0 to 1.0) with which the exceptional distribution is sampled.
67        degraded_freq: f64,
68    ) -> SimConfig {
69        let baseline = ms_to_dur(baseline);
70        let degraded = ms_to_dur(degraded);
71        SimConfig {
72            net: NetworkConfig {
73                latency: LatencyConfig {
74                    default_latency: LatencyDistribution::bimodal(
75                        baseline,
76                        degraded,
77                        degraded_freq,
78                    ),
79                    ..Default::default()
80                },
81                ..Default::default()
82            },
83        }
84    }
85
86    /// Select from among a number of configs using the SUI_SIM_CONFIG env var.
87    pub fn env_config(
88        // Config to use when SUI_SIM_CONFIG is not set.
89        default: SimConfig,
90        // List of (&str, SimConfig) pairs - the SimConfig associated with the value
91        // of the SUI_SIM_CONFIG var is chosen.
92        env_configs: impl IntoIterator<Item = (&'static str, SimConfig)>,
93    ) -> SimConfig {
94        let mut env_configs = HashMap::<&'static str, SimConfig>::from_iter(env_configs);
95        if let Some(env) = std::env::var("SUI_SIM_CONFIG").ok() {
96            if let Some(cfg) = env_configs.remove(env.as_str()) {
97                info!("Using test config for SUI_SIM_CONFIG={}", env);
98                cfg
99            } else {
100                panic!(
101                    "No config found for SUI_SIM_CONFIG={}. Available configs are: {:?}",
102                    env,
103                    env_configs.keys()
104                );
105            }
106        } else {
107            info!("Using default test config");
108            default
109        }
110    }
111}
112
113thread_local! {
114    static NODE_COUNT: AtomicUsize = const { AtomicUsize::new(0) };
115}
116
117pub struct NodeLeakDetector(());
118
119impl NodeLeakDetector {
120    pub fn new() -> Self {
121        NODE_COUNT.with(|c| c.fetch_add(1, Ordering::SeqCst));
122        Self(())
123    }
124
125    pub fn get_current_node_count() -> usize {
126        NODE_COUNT.with(|c| c.load(Ordering::SeqCst))
127    }
128}
129
130impl Default for NodeLeakDetector {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl Drop for NodeLeakDetector {
137    fn drop(&mut self) {
138        NODE_COUNT.with(|c| c.fetch_sub(1, Ordering::SeqCst));
139    }
140}
141
142#[cfg(not(msim))]
143#[macro_export]
144macro_rules! return_if_killed {
145    () => {};
146}
147
148#[cfg(msim)]
149pub fn current_simnode_id() -> msim::task::NodeId {
150    msim::runtime::NodeHandle::current().id()
151}
152
153pub fn has_mainnet_protocol_config_override() -> bool {
154    use sui_types::{digests::ChainIdentifier, supported_protocol_versions::Chain};
155
156    ChainIdentifier::default().chain() == Chain::Mainnet
157}
158
159#[cfg(msim)]
160pub mod random {
161    use super::*;
162
163    use rand_crate::{Rng, SeedableRng, rngs::SmallRng, thread_rng};
164    use serde::Serialize;
165    use std::cell::RefCell;
166    use std::collections::HashSet;
167    use std::hash::Hash;
168
169    /// Given a value, produce a random probability using the value as a seed, with
170    /// an additional seed that is constant only for the current test thread.
171    pub fn deterministic_probability<T: Hash>(value: T, chance: f32) -> bool {
172        thread_local! {
173            // a random seed that is shared by the whole test process, so that equal `value`
174            // inputs produce different outputs when the test seed changes
175            static SEED: u64 = thread_rng().r#gen();
176        }
177
178        chance
179            > SEED.with(|seed| {
180                let mut hasher = std::collections::hash_map::DefaultHasher::new();
181                seed.hash(&mut hasher);
182                value.hash(&mut hasher);
183                let mut rng = SmallRng::seed_from_u64(hasher.finish());
184                rng.gen_range(0.0..1.0)
185            })
186    }
187
188    /// Like deterministic_probability, but only returns true once for each unique value. May eventually
189    /// consume all memory if there are a large number of unique, failing values.
190    pub fn deterministic_probability_once<T: Hash + Serialize>(value: T, chance: f32) -> bool {
191        thread_local! {
192            static FAILING_VALUES: RefCell<HashSet<(msim::task::NodeId, Vec<u8>)>> = RefCell::new(HashSet::new());
193        }
194
195        let bytes = bcs::to_bytes(&value).unwrap();
196        let key = (current_simnode_id(), bytes);
197
198        FAILING_VALUES.with(|failing_values| {
199            let mut failing_values = failing_values.borrow_mut();
200            if failing_values.contains(&key) {
201                false
202            } else if deterministic_probability(value, chance) {
203                failing_values.insert(key);
204                true
205            } else {
206                false
207            }
208        })
209    }
210}