ant_node/
utils.rs

1// Copyright 2025 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use eyre::eyre;
10use libp2p::PeerId;
11use libp2p::identity::Keypair;
12use std::io::Write;
13use std::path::{Path, PathBuf};
14
15/// The keypair is located inside the root directory. At the same time, when no dir is specified,
16/// the dir name is derived from the keypair used in the application: the peer ID is used as the directory name.
17pub fn get_root_dir_and_keypair(root_dir: &Option<PathBuf>) -> eyre::Result<(PathBuf, Keypair)> {
18    match root_dir {
19        Some(dir) => {
20            std::fs::create_dir_all(dir)?;
21
22            let secret_key_path = dir.join("secret-key");
23            Ok((dir.clone(), keypair_from_path(secret_key_path)?))
24        }
25        None => {
26            let secret_key = libp2p::identity::ed25519::SecretKey::generate();
27            let keypair: Keypair =
28                libp2p::identity::ed25519::Keypair::from(secret_key.clone()).into();
29            let peer_id = keypair.public().to_peer_id();
30
31            let dir = get_antnode_root_dir(peer_id)?;
32            std::fs::create_dir_all(&dir)?;
33
34            let secret_key_path = dir.join("secret-key");
35
36            let mut file = create_secret_key_file(secret_key_path)
37                .map_err(|err| eyre!("could not create secret key file: {err}"))?;
38            file.write_all(secret_key.as_ref())?;
39
40            Ok((dir, keypair))
41        }
42    }
43}
44
45/// Get the default antnode root dir for the provided PeerId
46pub fn get_antnode_root_dir(peer_id: PeerId) -> eyre::Result<PathBuf> {
47    let dir = dirs_next::data_dir()
48        .ok_or_else(|| eyre!("could not obtain data dir"))?
49        .join("autonomi")
50        .join("node")
51        .join(peer_id.to_string());
52    Ok(dir)
53}
54
55fn keypair_from_path(path: impl AsRef<Path>) -> eyre::Result<Keypair> {
56    let keypair = match std::fs::read(&path) {
57        // If the file is opened successfully, read the key from it
58        Ok(key) => {
59            let keypair = Keypair::ed25519_from_bytes(key)
60                .map_err(|err| eyre!("could not read ed25519 key from file: {err}"))?;
61
62            info!("loaded secret key from file: {:?}", path.as_ref());
63
64            keypair
65        }
66        // In case the file is not found, generate a new keypair and write it to the file
67        Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
68            let secret_key = libp2p::identity::ed25519::SecretKey::generate();
69            let mut file = create_secret_key_file(&path)
70                .map_err(|err| eyre!("could not create secret key file: {err}"))?;
71            file.write_all(secret_key.as_ref())?;
72
73            info!("generated new key and stored to file: {:?}", path.as_ref());
74
75            libp2p::identity::ed25519::Keypair::from(secret_key).into()
76        }
77        // Else the file can't be opened, for whatever reason (e.g. permissions).
78        Err(err) => {
79            return Err(eyre!("failed to read secret key file: {err}"));
80        }
81    };
82
83    Ok(keypair)
84}
85
86fn create_secret_key_file(path: impl AsRef<Path>) -> eyre::Result<std::fs::File, std::io::Error> {
87    let mut opt = std::fs::OpenOptions::new();
88    let _ = opt.write(true).create_new(true);
89
90    // On Unix systems, make sure only the current user can read/write.
91    #[cfg(unix)]
92    {
93        use std::os::unix::fs::OpenOptionsExt;
94        let _ = opt.mode(0o600);
95    }
96
97    opt.open(path)
98}