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