snarkos_node/
traits.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use snarkos_node_router::{Routing, messages::NodeType};
17use snarkvm::prelude::{Address, Network, PrivateKey, ViewKey};
18
19use once_cell::sync::OnceCell;
20use std::{
21    future::Future,
22    io,
23    sync::{
24        Arc,
25        atomic::{AtomicBool, Ordering},
26    },
27    time::Duration,
28};
29
30#[async_trait]
31pub trait NodeInterface<N: Network>: Routing<N> {
32    /// Returns the node type.
33    fn node_type(&self) -> NodeType {
34        self.router().node_type()
35    }
36
37    /// Returns the account private key of the node.
38    fn private_key(&self) -> &PrivateKey<N> {
39        self.router().private_key()
40    }
41
42    /// Returns the account view key of the node.
43    fn view_key(&self) -> &ViewKey<N> {
44        self.router().view_key()
45    }
46
47    /// Returns the account address of the node.
48    fn address(&self) -> Address<N> {
49        self.router().address()
50    }
51
52    /// Returns `true` if the node is in development mode.
53    fn is_dev(&self) -> bool {
54        self.router().is_dev()
55    }
56
57    /// Handles OS signals for the node to intercept and perform a clean shutdown.
58    /// The optional `shutdown_flag` flag can be used to cleanly terminate the syncing process.
59    fn handle_signals(shutdown_flag: Arc<AtomicBool>) -> Arc<OnceCell<Self>> {
60        // In order for the signal handler to be started as early as possible, a reference to the node needs
61        // to be passed to it at a later time.
62        let node: Arc<OnceCell<Self>> = Default::default();
63
64        #[cfg(target_family = "unix")]
65        fn signal_listener() -> impl Future<Output = io::Result<()>> {
66            use tokio::signal::unix::{SignalKind, signal};
67
68            // Handle SIGINT, SIGTERM, SIGQUIT, and SIGHUP.
69            let mut s_int = signal(SignalKind::interrupt()).unwrap();
70            let mut s_term = signal(SignalKind::terminate()).unwrap();
71            let mut s_quit = signal(SignalKind::quit()).unwrap();
72            let mut s_hup = signal(SignalKind::hangup()).unwrap();
73
74            // Return when any of the signals above is received.
75            async move {
76                tokio::select!(
77                    _ = s_int.recv() => (),
78                    _ = s_term.recv() => (),
79                    _ = s_quit.recv() => (),
80                    _ = s_hup.recv() => (),
81                );
82                Ok(())
83            }
84        }
85        #[cfg(not(target_family = "unix"))]
86        fn signal_listener() -> impl Future<Output = io::Result<()>> {
87            tokio::signal::ctrl_c()
88        }
89
90        let node_clone = node.clone();
91        tokio::task::spawn(async move {
92            match signal_listener().await {
93                Ok(()) => {
94                    warn!("==========================================================================================");
95                    warn!("⚠️  Attention - Starting the graceful shutdown procedure (ETA: 30 seconds)...");
96                    warn!("⚠️  Attention - To avoid DATA CORRUPTION, do NOT interrupt snarkOS (or press Ctrl+C again)");
97                    warn!("⚠️  Attention - Please wait until the shutdown gracefully completes (ETA: 30 seconds)");
98                    warn!("==========================================================================================");
99
100                    match node_clone.get() {
101                        // If the node is already initialized, then shut it down.
102                        Some(node) => node.shut_down().await,
103                        // Otherwise, if the node is not yet initialized, then set the shutdown flag directly.
104                        None => shutdown_flag.store(true, Ordering::Relaxed),
105                    }
106
107                    // A best-effort attempt to let any ongoing activity conclude.
108                    tokio::time::sleep(Duration::from_secs(3)).await;
109
110                    // Terminate the process.
111                    std::process::exit(0);
112                }
113                Err(error) => error!("tokio::signal::ctrl_c encountered an error: {}", error),
114            }
115        });
116
117        node
118    }
119
120    /// Shuts down the node.
121    async fn shut_down(&self);
122}