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_network::{NodeType, PeerPoolHandling};
17use snarkos_node_router::Routing;
18use snarkvm::prelude::{Address, Network, PrivateKey, ViewKey};
19
20use once_cell::sync::OnceCell;
21use std::{
22    future::Future,
23    io,
24    sync::{
25        Arc,
26        atomic::{AtomicBool, Ordering},
27    },
28    time::Duration,
29};
30
31#[async_trait]
32pub trait NodeInterface<N: Network>: Routing<N> {
33    /// Returns the node type.
34    fn node_type(&self) -> NodeType {
35        self.router().node_type()
36    }
37
38    /// Returns the account private key of the node.
39    fn private_key(&self) -> &PrivateKey<N> {
40        self.router().private_key()
41    }
42
43    /// Returns the account view key of the node.
44    fn view_key(&self) -> &ViewKey<N> {
45        self.router().view_key()
46    }
47
48    /// Returns the account address of the node.
49    fn address(&self) -> Address<N> {
50        self.router().address()
51    }
52
53    /// Returns `true` if the node is in development mode.
54    fn is_dev(&self) -> bool {
55        self.router().is_dev()
56    }
57
58    /// Handles OS signals for the node to intercept and perform a clean shutdown.
59    /// The optional `shutdown_flag` flag can be used to cleanly terminate the syncing process.
60    fn handle_signals(shutdown_flag: Arc<AtomicBool>) -> Arc<OnceCell<Self>> {
61        // In order for the signal handler to be started as early as possible, a reference to the node needs
62        // to be passed to it at a later time.
63        let node: Arc<OnceCell<Self>> = Default::default();
64
65        #[cfg(target_family = "unix")]
66        fn signal_listener() -> impl Future<Output = io::Result<()>> {
67            use tokio::signal::unix::{SignalKind, signal};
68
69            // Handle SIGINT, SIGTERM, SIGQUIT, and SIGHUP.
70            let mut s_int = signal(SignalKind::interrupt()).unwrap();
71            let mut s_term = signal(SignalKind::terminate()).unwrap();
72            let mut s_quit = signal(SignalKind::quit()).unwrap();
73            let mut s_hup = signal(SignalKind::hangup()).unwrap();
74
75            // Return when any of the signals above is received.
76            async move {
77                tokio::select!(
78                    _ = s_int.recv() => (),
79                    _ = s_term.recv() => (),
80                    _ = s_quit.recv() => (),
81                    _ = s_hup.recv() => (),
82                );
83                Ok(())
84            }
85        }
86        #[cfg(not(target_family = "unix"))]
87        fn signal_listener() -> impl Future<Output = io::Result<()>> {
88            tokio::signal::ctrl_c()
89        }
90
91        let node_clone = node.clone();
92        tokio::task::spawn(async move {
93            match signal_listener().await {
94                Ok(()) => {
95                    warn!("==========================================================================================");
96                    warn!("⚠️  Attention - Starting the graceful shutdown procedure (ETA: 30 seconds)...");
97                    warn!("⚠️  Attention - To avoid DATA CORRUPTION, do NOT interrupt snarkOS (or press Ctrl+C again)");
98                    warn!("⚠️  Attention - Please wait until the shutdown gracefully completes (ETA: 30 seconds)");
99                    warn!("==========================================================================================");
100
101                    match node_clone.get() {
102                        // If the node is already initialized, then shut it down.
103                        Some(node) => node.shut_down().await,
104                        // Otherwise, if the node is not yet initialized, then set the shutdown flag directly.
105                        None => shutdown_flag.store(true, Ordering::Relaxed),
106                    }
107
108                    // A best-effort attempt to let any ongoing activity conclude.
109                    tokio::time::sleep(Duration::from_secs(3)).await;
110
111                    // Terminate the process.
112                    std::process::exit(0);
113                }
114                Err(error) => error!("tokio::signal::ctrl_c encountered an error: {}", error),
115            }
116        });
117
118        node
119    }
120
121    /// Shuts down the node.
122    async fn shut_down(&self);
123}