sui_types/
node_role.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::base_types::AuthorityName;
5use crate::committee::Committee;
6use serde::{Deserialize, Serialize};
7
8/// How a full node syncs data from the network.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "kebab-case")]
11pub enum FullNodeSyncMode {
12    /// Syncs exclusively via the state-sync protocol.
13    StateSyncOnly = 0,
14    /// Streams consensus blocks for faster ingestion, in addition to state sync.
15    ConsensusObserver = 1,
16}
17
18/// Represents the role a node plays in the network for a given epoch.
19/// A node is either a Validator (in the committee) or a FullNode (not in
20/// the committee). FullNodes carry a sync mode that determines whether
21/// they also participate in consensus as an observer.
22///
23/// Behavior should be gated through capability methods (e.g. `runs_consensus()`) rather than matching on variants directly.
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum NodeRole {
26    /// A validator that participates in consensus, proposes blocks and signs checkpoints.
27    Validator,
28    /// A full node that serves RPC/indexing and syncs via the configured mode.
29    FullNode(FullNodeSyncMode),
30}
31
32impl NodeRole {
33    /// Determines the node role from committee membership and the configured sync mode.
34    /// Used per-epoch in AuthorityPerEpochStore to derive the authoritative role.
35    pub fn from_committee(
36        committee: &Committee,
37        name: &AuthorityName,
38        fullnode_sync_mode: Option<FullNodeSyncMode>,
39    ) -> Self {
40        if committee.authority_exists(name) {
41            NodeRole::Validator
42        } else if let Some(mode) = fullnode_sync_mode {
43            NodeRole::FullNode(mode)
44        } else {
45            NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly)
46        }
47    }
48
49    pub fn is_fullnode(&self) -> bool {
50        matches!(self, Self::FullNode(_))
51    }
52
53    pub fn is_validator(&self) -> bool {
54        matches!(self, Self::Validator)
55    }
56
57    /// Whether this node runs consensus in proposer or observer mode.
58    /// Notably, consensus handler and its downstream components always run when this is true.
59    pub fn runs_consensus(&self) -> bool {
60        matches!(
61            self,
62            Self::Validator | Self::FullNode(FullNodeSyncMode::ConsensusObserver)
63        )
64    }
65
66    // --- Temporary feature flags ---
67    // The flags below are temporary and may not match the eventual conditions to enable each feature.
68    // They will be removed and the callsites will resolve to one of the three conditions above
69    // once observer mode is fully implemented.
70
71    /// Whether this node should create index stores for JSON-RPC and REST API.
72    pub fn should_enable_index_processing(&self) -> bool {
73        matches!(self, Self::FullNode(_))
74    }
75
76    /// Whether this node should process consensus commit output (execute
77    /// transactions, create checkpoints, etc.). Observers stream blocks but
78    /// rely on state-sync for execution, so they skip commit processing.
79    pub fn process_consensus_commits(&self) -> bool {
80        matches!(self, Self::Validator)
81    }
82
83    /// Whether this node should expose HTTP/RPC servers (JSON-RPC, REST).
84    pub fn should_run_rpc_servers(&self) -> bool {
85        matches!(self, Self::FullNode(_))
86    }
87}
88
89impl std::fmt::Display for NodeRole {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        match self {
92            Self::Validator => write!(f, "Validator"),
93            Self::FullNode(FullNodeSyncMode::StateSyncOnly) => write!(f, "FullNode"),
94            Self::FullNode(FullNodeSyncMode::ConsensusObserver) => {
95                write!(f, "FullNode(ConsensusObserver)")
96            }
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_validator_role() {
107        let role = NodeRole::Validator;
108        assert!(role.runs_consensus());
109        assert!(!role.should_enable_index_processing());
110        assert!(!role.should_run_rpc_servers());
111        assert!(role.process_consensus_commits());
112    }
113
114    #[test]
115    fn test_consensus_observer_role() {
116        let role = NodeRole::FullNode(FullNodeSyncMode::ConsensusObserver);
117        assert!(role.runs_consensus());
118        assert!(role.should_enable_index_processing());
119        assert!(role.should_run_rpc_servers());
120        assert!(!role.process_consensus_commits());
121    }
122
123    #[test]
124    fn test_fullnode_state_sync_role() {
125        let role = NodeRole::FullNode(FullNodeSyncMode::StateSyncOnly);
126        assert!(!role.runs_consensus());
127        assert!(role.should_enable_index_processing());
128        assert!(role.should_run_rpc_servers());
129        assert!(!role.process_consensus_commits());
130    }
131}