Skip to main content

walletkit_core/storage/
paths.rs

1//! Storage path helpers.
2//!
3//! [`StoragePaths`] governs the on-disk files under `<root>/worldid/`:
4//!
5//! ```text
6//! account.cache.sqlite        # sqlite3mc-encrypted cache DB (keyed by K_intermediate)
7//! account.vault.sqlite        # sqlite3mc-encrypted vault DB (keyed by K_intermediate)
8//! lock                        # account-scoped lock
9//! groth16/                    # cached Groth16 proving material
10//! ```
11//!
12//! The account key envelope (`account_keys.bin`) is **not** covered by
13//! [`StoragePaths`]. It is persisted through the host's `AtomicBlobStore` using the
14//! bare filename `account_keys.bin`, so its physical location is whatever root the
15//! host roots that blob store at — which need not be `<root>/worldid/`. For example,
16//! the CLI provider roots the blob store at `<root>`, writing the envelope to
17//! `<root>/account_keys.bin` alongside (not inside) the `worldid/` directory. Host
18//! implementors backing up or deleting an account MUST include the envelope from the
19//! blob store in addition to the `worldid/` files.
20
21use std::path::{Path, PathBuf};
22
23const VAULT_FILENAME: &str = "account.vault.sqlite";
24const CACHE_FILENAME: &str = "account.cache.sqlite";
25const LOCK_FILENAME: &str = "lock";
26const GROTH16_DIRNAME: &str = "groth16";
27const QUERY_ZKEY_FILENAME: &str = "OPRFQuery.arks.zkey";
28const NULLIFIER_ZKEY_FILENAME: &str = "OPRFNullifier.arks.zkey";
29const QUERY_GRAPH_FILENAME: &str = "OPRFQueryGraph.bin";
30const NULLIFIER_GRAPH_FILENAME: &str = "OPRFNullifierGraph.bin";
31
32/// Paths for credential storage artifacts under `<root>/worldid`.
33#[derive(Debug, Clone, uniffi::Object)]
34pub struct StoragePaths {
35    root: PathBuf,
36    worldid_dir: PathBuf,
37}
38
39impl StoragePaths {
40    /// Builds storage paths rooted at `root`.
41    #[must_use]
42    pub fn new(root: impl AsRef<Path>) -> Self {
43        let root = root.as_ref().to_path_buf();
44        let worldid_dir = root.join("worldid");
45        Self { root, worldid_dir }
46    }
47
48    /// Returns the storage root directory.
49    #[must_use]
50    pub fn root(&self) -> &Path {
51        &self.root
52    }
53
54    /// Returns the World ID storage directory.
55    #[must_use]
56    pub fn worldid_dir(&self) -> &Path {
57        &self.worldid_dir
58    }
59
60    /// Returns the path to the vault database.
61    #[must_use]
62    pub fn vault_db_path(&self) -> PathBuf {
63        self.worldid_dir.join(VAULT_FILENAME)
64    }
65
66    /// Returns the path to the cache database.
67    #[must_use]
68    pub fn cache_db_path(&self) -> PathBuf {
69        self.worldid_dir.join(CACHE_FILENAME)
70    }
71
72    /// Returns the path to the lock file.
73    #[must_use]
74    pub fn lock_path(&self) -> PathBuf {
75        self.worldid_dir.join(LOCK_FILENAME)
76    }
77
78    /// Returns the path to the Groth16 material directory.
79    #[must_use]
80    pub fn groth16_dir(&self) -> PathBuf {
81        self.worldid_dir.join(GROTH16_DIRNAME)
82    }
83
84    /// Returns the path to the query zkey file.
85    #[must_use]
86    pub fn query_zkey_path(&self) -> PathBuf {
87        self.groth16_dir().join(QUERY_ZKEY_FILENAME)
88    }
89
90    /// Returns the path to the nullifier zkey file.
91    #[must_use]
92    pub fn nullifier_zkey_path(&self) -> PathBuf {
93        self.groth16_dir().join(NULLIFIER_ZKEY_FILENAME)
94    }
95
96    /// Returns the path to the query graph file.
97    #[must_use]
98    pub fn query_graph_path(&self) -> PathBuf {
99        self.groth16_dir().join(QUERY_GRAPH_FILENAME)
100    }
101
102    /// Returns the path to the nullifier graph file.
103    #[must_use]
104    pub fn nullifier_graph_path(&self) -> PathBuf {
105        self.groth16_dir().join(NULLIFIER_GRAPH_FILENAME)
106    }
107}
108
109#[uniffi::export]
110impl StoragePaths {
111    /// Builds storage paths rooted at `root`.
112    #[uniffi::constructor]
113    #[must_use]
114    pub fn from_root(root: String) -> Self {
115        Self::new(PathBuf::from(root))
116    }
117
118    /// Returns the storage root directory as a string.
119    #[must_use]
120    pub fn root_path_string(&self) -> String {
121        self.root.to_string_lossy().to_string()
122    }
123
124    /// Returns the World ID storage directory as a string.
125    #[must_use]
126    pub fn worldid_dir_path_string(&self) -> String {
127        self.worldid_dir.to_string_lossy().to_string()
128    }
129
130    /// Returns the path to the vault database as a string.
131    #[must_use]
132    pub fn vault_db_path_string(&self) -> String {
133        self.vault_db_path().to_string_lossy().to_string()
134    }
135
136    /// Returns the path to the cache database as a string.
137    #[must_use]
138    pub fn cache_db_path_string(&self) -> String {
139        self.cache_db_path().to_string_lossy().to_string()
140    }
141
142    /// Returns the path to the lock file as a string.
143    #[must_use]
144    pub fn lock_path_string(&self) -> String {
145        self.lock_path().to_string_lossy().to_string()
146    }
147
148    /// Returns the path to the Groth16 material directory as a string.
149    #[must_use]
150    pub fn groth16_dir_path_string(&self) -> String {
151        self.groth16_dir().to_string_lossy().to_string()
152    }
153
154    /// Returns the path to the query zkey file as a string.
155    #[must_use]
156    pub fn query_zkey_path_string(&self) -> String {
157        self.query_zkey_path().to_string_lossy().to_string()
158    }
159
160    /// Returns the path to the nullifier zkey file as a string.
161    #[must_use]
162    pub fn nullifier_zkey_path_string(&self) -> String {
163        self.nullifier_zkey_path().to_string_lossy().to_string()
164    }
165
166    /// Returns the path to the query graph file as a string.
167    #[must_use]
168    pub fn query_graph_path_string(&self) -> String {
169        self.query_graph_path().to_string_lossy().to_string()
170    }
171
172    /// Returns the path to the nullifier graph file as a string.
173    #[must_use]
174    pub fn nullifier_graph_path_string(&self) -> String {
175        self.nullifier_graph_path().to_string_lossy().to_string()
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::StoragePaths;
182    use std::path::PathBuf;
183
184    #[test]
185    fn test_groth16_paths() {
186        let root = PathBuf::from("/tmp/walletkit-paths");
187        let paths = StoragePaths::new(&root);
188        let worldid = root.join("worldid");
189        let groth16 = worldid.join("groth16");
190
191        assert_eq!(paths.groth16_dir(), groth16);
192        assert_eq!(paths.query_zkey_path(), groth16.join("OPRFQuery.arks.zkey"));
193        assert_eq!(
194            paths.nullifier_zkey_path(),
195            groth16.join("OPRFNullifier.arks.zkey")
196        );
197        assert_eq!(paths.query_graph_path(), groth16.join("OPRFQueryGraph.bin"));
198        assert_eq!(
199            paths.nullifier_graph_path(),
200            groth16.join("OPRFNullifierGraph.bin")
201        );
202    }
203
204    #[test]
205    fn test_groth16_path_strings() {
206        let root = PathBuf::from("/tmp/walletkit-paths");
207        let paths = StoragePaths::new(&root);
208
209        assert_eq!(
210            paths.groth16_dir_path_string(),
211            paths.groth16_dir().to_string_lossy()
212        );
213        assert_eq!(
214            paths.query_zkey_path_string(),
215            paths.query_zkey_path().to_string_lossy()
216        );
217        assert_eq!(
218            paths.nullifier_zkey_path_string(),
219            paths.nullifier_zkey_path().to_string_lossy()
220        );
221        assert_eq!(
222            paths.query_graph_path_string(),
223            paths.query_graph_path().to_string_lossy()
224        );
225        assert_eq!(
226            paths.nullifier_graph_path_string(),
227            paths.nullifier_graph_path().to_string_lossy()
228        );
229    }
230}