mina_node_testing/scenarios/solo_node/
bootstrap.rs

1use std::time::Duration;
2
3use mina_core::constants::constraint_constants;
4use node::transition_frontier::sync::TransitionFrontierSyncState;
5use redux::Instant;
6
7use crate::{
8    hosts,
9    node::RustNodeTestingConfig,
10    scenario::{ListenerNode, ScenarioStep},
11    scenarios::ClusterRunner,
12};
13
14/// Set up single Rust node and bootstrap snarked ledger, bootstrap ledger and blocks.
15///
16/// 1. Node will connect to replayer.
17/// 2. Observe that stacking ledger is synchronized.
18/// 3. Observe that next epoch ledger is synchronized.
19/// 4. Continue until transition frontier is synchronized.
20#[derive(documented::Documented, Default, Clone, Copy)]
21pub struct SoloNodeBootstrap;
22
23// TODO(tizoc): this is ugly, do a cleaner conversion or figure out a better way.
24// This test will fail if we don't start with this as the initial time because
25// the time validation for the first block will reject it.
26fn first_block_slot_timestamp_nanos(config: &RustNodeTestingConfig) -> u64 {
27    let first_block_global_slot = 46891; // Update if replay changes
28    let protocol_constants = config.genesis.protocol_constants().unwrap();
29    let genesis_timestamp_ms = protocol_constants.genesis_state_timestamp.0.as_u64();
30    let milliseconds_per_slot = constraint_constants().block_window_duration_ms;
31    let first_block_global_slot_delta_ms = first_block_global_slot * milliseconds_per_slot;
32
33    // Convert to nanos
34    genesis_timestamp_ms
35        .checked_add(first_block_global_slot_delta_ms)
36        .unwrap()
37        .checked_mul(1_000_000)
38        .unwrap()
39}
40
41impl SoloNodeBootstrap {
42    pub async fn run(self, mut runner: ClusterRunner<'_>) {
43        use self::TransitionFrontierSyncState::*;
44
45        const TIMEOUT: Duration = Duration::from_secs(60 * 40);
46
47        let replayer = hosts::replayer();
48
49        let mut config = RustNodeTestingConfig::devnet_default();
50
51        config.initial_time = redux::Timestamp::new(first_block_slot_timestamp_nanos(&config));
52
53        let node_id =
54            runner.add_rust_node(config.initial_peers(vec![ListenerNode::Custom(replayer)]));
55        eprintln!("launch Mina Rust node with default configuration, id: {node_id}");
56
57        let mut timeout = TIMEOUT;
58        let mut last_time = Instant::now();
59        loop {
60            if runner
61                .wait_for_pending_events_with_timeout(Duration::from_secs(1))
62                .await
63            {
64                let steps = runner
65                    .pending_events(true)
66                    .flat_map(|(node_id, _, events)| {
67                        events.map(move |(_, event)| ScenarioStep::Event {
68                            node_id,
69                            event: event.to_string(),
70                        })
71                    })
72                    .collect::<Vec<_>>();
73
74                for step in steps {
75                    runner.exec_step(step).await.unwrap();
76                }
77            }
78
79            runner
80                .exec_step(ScenarioStep::CheckTimeouts { node_id })
81                .await
82                .unwrap();
83
84            let new = Instant::now();
85            let elapsed = new - last_time;
86            last_time = new;
87
88            match timeout.checked_sub(elapsed) {
89                Some(new_timeout) => timeout = new_timeout,
90                None => panic!("timeout"),
91            }
92
93            runner
94                .exec_step(ScenarioStep::AdvanceNodeTime {
95                    node_id,
96                    by_nanos: elapsed.as_nanos() as _,
97                })
98                .await
99                .unwrap();
100
101            let this = runner.node(node_id).unwrap();
102            let best_chain = &this.state().transition_frontier.best_chain;
103            let sync_state = &this.state().transition_frontier.sync;
104            if let Synced { time } = &sync_state {
105                if best_chain.len() > 1 {
106                    eprintln!(
107                        "node: {node_id}, is synced at {time:?}, total elapsed {:?}",
108                        TIMEOUT - timeout
109                    );
110
111                    break;
112                }
113            }
114        }
115    }
116}