snarkos_utilities/
node_data.rs1use std::path::{Path, PathBuf};
17
18pub const GATEWAY_PEER_CACHE_FILE: &str = "gateway-peer-cache";
20pub const LEGACY_GATEWAY_PEER_CACHE_FILE: &str = "cached_gateway_peers";
22
23pub const ROUTER_PEER_CACHE_FILE: &str = "router-peer-cache";
25pub const LEGACY_ROUTER_PEER_CACHE_FILE: &str = "cached_router_peers";
27
28pub const CURRENT_PROPOSAL_CACHE_FILE: &str = "current-proposal-cache";
30
31#[cfg(feature = "test_network")]
33pub const DEV_COMMITTEE_STATE_FILE: &str = "dev-committee-state";
34
35pub fn jwt_secret_file<D: std::fmt::Display>(address: &D) -> PathBuf {
37 PathBuf::from(format!("jwt_secret_{address}.txt"))
38}
39
40pub fn legacy_current_proposal_cache_file(network: u16, dev: Option<u16>) -> PathBuf {
42 if let Some(dev) = dev {
43 PathBuf::from(format!(".current-proposal-cache-{network}-{dev}"))
44 } else {
45 PathBuf::from(format!("current-proposal-cache-{network}"))
46 }
47}
48
49#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct NodeDataDir {
52 path: PathBuf,
53}
54
55impl NodeDataDir {
56 pub fn new(path: PathBuf) -> Self {
58 Self { path }
59 }
60
61 pub fn new_test(dev: Option<u16>) -> Self {
63 if let Some(dev) = dev {
64 Self { path: PathBuf::from(format!(".node-data-test-{dev}")) }
65 } else {
66 Self { path: PathBuf::from(".node-data-test") }
67 }
68 }
69
70 pub fn new_development(network: u16, dev: u16) -> Self {
72 let path = std::env::current_dir()
75 .unwrap_or(PathBuf::from(env!("CARGO_MANIFEST_DIR")))
76 .join(format!(".node-data-{network}-{dev}"));
77
78 Self::new(path)
79 }
80
81 pub fn path(&self) -> &Path {
82 &self.path
83 }
84
85 pub fn router_peer_cache_path(&self) -> PathBuf {
87 self.path.join(ROUTER_PEER_CACHE_FILE)
88 }
89
90 pub fn gateway_peer_cache_path(&self) -> PathBuf {
91 self.path.join(GATEWAY_PEER_CACHE_FILE)
92 }
93
94 pub fn current_proposal_cache_path(&self) -> PathBuf {
96 self.path.join(CURRENT_PROPOSAL_CACHE_FILE)
97 }
98
99 #[cfg(feature = "test_network")]
101 pub fn dev_committee_state_path(&self) -> PathBuf {
102 self.path.join(DEV_COMMITTEE_STATE_FILE)
103 }
104
105 pub fn jwt_secret_path<D: std::fmt::Display>(&self, address: &D) -> PathBuf {
107 self.path.join(jwt_secret_file(address))
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn the_cache_filenames_are_pinned() {
117 assert_eq!(ROUTER_PEER_CACHE_FILE, "router-peer-cache");
120 assert_eq!(GATEWAY_PEER_CACHE_FILE, "gateway-peer-cache");
121 assert_eq!(CURRENT_PROPOSAL_CACHE_FILE, "current-proposal-cache");
122 assert_eq!(LEGACY_ROUTER_PEER_CACHE_FILE, "cached_router_peers");
123 assert_eq!(LEGACY_GATEWAY_PEER_CACHE_FILE, "cached_gateway_peers");
124 }
125
126 #[test]
127 fn the_legacy_filenames_are_distinct_from_the_current_ones() {
128 assert_ne!(ROUTER_PEER_CACHE_FILE, LEGACY_ROUTER_PEER_CACHE_FILE);
129 assert_ne!(GATEWAY_PEER_CACHE_FILE, LEGACY_GATEWAY_PEER_CACHE_FILE);
130 assert_ne!(ROUTER_PEER_CACHE_FILE, GATEWAY_PEER_CACHE_FILE);
131 }
132
133 #[test]
134 fn every_path_is_rooted_at_the_data_dir() {
135 let dir = NodeDataDir::new(PathBuf::from("/var/lib/snarkos"));
136
137 for path in [dir.router_peer_cache_path(), dir.gateway_peer_cache_path(), dir.current_proposal_cache_path()] {
138 assert!(path.starts_with(dir.path()), "{path:?} escaped the data dir");
139 assert_eq!(path.parent().unwrap(), dir.path());
140 }
141 }
142
143 #[test]
144 fn each_cache_path_is_a_distinct_file() {
145 let dir = NodeDataDir::new(PathBuf::from("/var/lib/snarkos"));
146
147 assert_ne!(dir.router_peer_cache_path(), dir.gateway_peer_cache_path());
150 assert_ne!(dir.router_peer_cache_path(), dir.current_proposal_cache_path());
151 assert_ne!(dir.gateway_peer_cache_path(), dir.current_proposal_cache_path());
152 }
153
154 #[test]
155 fn the_jwt_secret_path_agrees_with_the_bare_filename_helper() {
156 let dir = NodeDataDir::new(PathBuf::from("/var/lib/snarkos"));
157 let address = "aleo1example";
158
159 assert_eq!(dir.jwt_secret_path(&address), dir.path().join(jwt_secret_file(&address)));
163 }
164
165 #[test]
166 fn the_jwt_secret_filename_is_scoped_to_the_address() {
167 assert_eq!(jwt_secret_file(&"aleo1abc"), PathBuf::from("jwt_secret_aleo1abc.txt"));
168 assert_ne!(jwt_secret_file(&"aleo1abc"), jwt_secret_file(&"aleo1def"));
169 }
170
171 #[test]
172 fn the_legacy_proposal_cache_filename_switches_on_dev() {
173 assert_eq!(legacy_current_proposal_cache_file(1, Some(3)), PathBuf::from(".current-proposal-cache-1-3"));
175 assert_eq!(legacy_current_proposal_cache_file(1, None), PathBuf::from("current-proposal-cache-1"));
176
177 assert_ne!(legacy_current_proposal_cache_file(1, Some(0)), legacy_current_proposal_cache_file(1, Some(1)));
179 assert_ne!(legacy_current_proposal_cache_file(0, None), legacy_current_proposal_cache_file(1, None));
181 }
182
183 #[test]
184 fn test_data_dirs_are_separated_by_dev_index() {
185 assert_eq!(NodeDataDir::new_test(None).path(), Path::new(".node-data-test"));
186 assert_eq!(NodeDataDir::new_test(Some(2)).path(), Path::new(".node-data-test-2"));
187
188 assert_ne!(NodeDataDir::new_test(Some(0)), NodeDataDir::new_test(Some(1)));
190 assert_ne!(NodeDataDir::new_test(None), NodeDataDir::new_test(Some(0)));
191 }
192
193 #[test]
194 fn development_data_dirs_are_absolute_and_separated_by_network_and_index() {
195 let first = NodeDataDir::new_development(1, 0);
196 let second = NodeDataDir::new_development(1, 1);
197 let other_network = NodeDataDir::new_development(2, 0);
198
199 assert!(first.path().is_absolute());
201 assert_eq!(first.path().file_name().unwrap(), ".node-data-1-0");
202
203 assert_ne!(first, second);
204 assert_ne!(first, other_network);
205 }
206}