fedimint_server/config/
io.rs

1use std::fmt::Display;
2use std::fs;
3use std::io::Write;
4use std::path::Path;
5
6use fedimint_aead::{encrypted_read, encrypted_write, get_encryption_key, LessSafeKey};
7use fedimint_core::config::ServerModuleInitRegistry;
8use serde::de::DeserializeOwned;
9use serde::Serialize;
10
11use crate::config::ServerConfig;
12
13/// Client configuration file
14pub const CLIENT_CONFIG: &str = "client";
15
16/// Server encrypted private keys file
17pub const PRIVATE_CONFIG: &str = "private";
18
19/// Server locally configurable file
20pub const LOCAL_CONFIG: &str = "local";
21
22/// Server consensus-only configurable file
23pub const CONSENSUS_CONFIG: &str = "consensus";
24
25/// Client connection string file
26pub const CLIENT_INVITE_CODE_FILE: &str = "invite-code";
27
28/// Salt backup for combining with the private key
29pub const SALT_FILE: &str = "private.salt";
30
31/// Plain-text stored password, used to restart the server without having to
32/// send a password in via the API
33pub const PLAINTEXT_PASSWORD: &str = "password.private";
34
35/// Database file name
36pub const DB_FILE: &str = "database";
37
38pub const JSON_EXT: &str = "json";
39
40pub const ENCRYPTED_EXT: &str = "encrypt";
41
42/// Reads the server from the local, private, and consensus cfg files
43pub fn read_server_config(password: &str, path: &Path) -> anyhow::Result<ServerConfig> {
44    let salt = fs::read_to_string(path.join(SALT_FILE))?;
45    let key = get_encryption_key(password, &salt)?;
46
47    Ok(ServerConfig {
48        consensus: plaintext_json_read(&path.join(CONSENSUS_CONFIG))?,
49        local: plaintext_json_read(&path.join(LOCAL_CONFIG))?,
50        private: encrypted_json_read(&key, &path.join(PRIVATE_CONFIG))?,
51    })
52}
53
54/// Reads a plaintext json file into a struct
55fn plaintext_json_read<T: Serialize + DeserializeOwned>(path: &Path) -> anyhow::Result<T> {
56    let string = fs::read_to_string(path.with_extension(JSON_EXT))?;
57    Ok(serde_json::from_str(&string)?)
58}
59
60/// Reads an encrypted json file into a struct
61fn encrypted_json_read<T: Serialize + DeserializeOwned>(
62    key: &LessSafeKey,
63    path: &Path,
64) -> anyhow::Result<T> {
65    let decrypted = encrypted_read(key, path.with_extension(ENCRYPTED_EXT));
66    let string = String::from_utf8(decrypted?)?;
67    Ok(serde_json::from_str(&string)?)
68}
69
70/// Writes the server into configuration files (private keys encrypted)
71pub fn write_server_config(
72    server: &ServerConfig,
73    path: &Path,
74    password: &str,
75    module_config_gens: &ServerModuleInitRegistry,
76    api_secret: Option<String>,
77) -> anyhow::Result<()> {
78    let salt = fs::read_to_string(path.join(SALT_FILE))?;
79    let key = get_encryption_key(password, &salt)?;
80
81    let client_config = server.consensus.to_client_config(module_config_gens)?;
82    plaintext_json_write(&server.local, &path.join(LOCAL_CONFIG))?;
83    plaintext_json_write(&server.consensus, &path.join(CONSENSUS_CONFIG))?;
84    plaintext_display_write(
85        &server.get_invite_code(api_secret),
86        &path.join(CLIENT_INVITE_CODE_FILE),
87    )?;
88    plaintext_json_write(&client_config, &path.join(CLIENT_CONFIG))?;
89    encrypted_json_write(&server.private, &key, &path.join(PRIVATE_CONFIG))
90}
91
92/// Writes struct into a plaintext json file
93fn plaintext_json_write<T: Serialize + DeserializeOwned>(
94    obj: &T,
95    path: &Path,
96) -> anyhow::Result<()> {
97    let file = fs::File::options()
98        .create_new(true)
99        .write(true)
100        .open(path.with_extension(JSON_EXT))?;
101
102    serde_json::to_writer_pretty(file, obj)?;
103    Ok(())
104}
105
106fn plaintext_display_write<T: Display>(obj: &T, path: &Path) -> anyhow::Result<()> {
107    let mut file = fs::File::options()
108        .create_new(true)
109        .write(true)
110        .open(path)?;
111    file.write_all(obj.to_string().as_bytes())?;
112    Ok(())
113}
114
115/// Writes struct into an encrypted json file
116fn encrypted_json_write<T: Serialize + DeserializeOwned>(
117    obj: &T,
118    key: &LessSafeKey,
119    path: &Path,
120) -> anyhow::Result<()> {
121    let bytes = serde_json::to_string(obj)?.into_bytes();
122    encrypted_write(bytes, key, path.with_extension(ENCRYPTED_EXT))
123}