Skip to main content

tycho_core/node/
keys.rs

1use std::path::Path;
2
3use anyhow::{Context, Result};
4use rand::Rng;
5use serde::{Deserialize, Serialize};
6use tycho_crypto::ed25519;
7use tycho_types::cell::HashBytes;
8
9#[derive(Debug)]
10pub struct NodeKeys {
11    pub secret: HashBytes,
12}
13
14impl NodeKeys {
15    pub fn generate() -> Self {
16        rand::random()
17    }
18
19    /// Tries to load keys from the specified path.
20    ///
21    /// Generates and saves new keys if the file doesn't exist.
22    pub fn load_or_create<P: AsRef<Path>>(path: P) -> Result<Self> {
23        let path = path.as_ref();
24        if path.exists() {
25            NodeKeys::from_file(path).context("failed to load node keys")
26        } else {
27            let keys = NodeKeys::generate();
28            tracing::warn!(
29                node_keys_path = %path.display(),
30                public_key = %keys.public_key(),
31                "generated new node keys",
32            );
33
34            keys.save_to_file(path)
35                .context("failed to save new node keys")?;
36            Ok(keys)
37        }
38    }
39
40    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
41        tycho_util::serde_helpers::load_json_from_file(path)
42    }
43
44    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
45        tycho_util::serde_helpers::save_json_to_file(self, path)
46    }
47
48    pub fn as_secret(&self) -> ed25519::SecretKey {
49        ed25519::SecretKey::from_bytes(self.secret.0)
50    }
51
52    pub fn public_key(&self) -> ed25519::PublicKey {
53        ed25519::PublicKey::from(&self.as_secret())
54    }
55}
56
57impl<'de> Deserialize<'de> for NodeKeys {
58    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59    where
60        D: serde::Deserializer<'de>,
61    {
62        use serde::de::Error;
63
64        #[derive(Deserialize)]
65        struct PartialKeys {
66            secret: HashBytes,
67            #[serde(default)]
68            public: Option<HashBytes>,
69        }
70
71        let partial = PartialKeys::deserialize(deserializer)?;
72
73        let secret = ed25519::SecretKey::from_bytes(partial.secret.0);
74        let public = ed25519::PublicKey::from(&secret);
75
76        if let Some(stored_public) = partial.public
77            && stored_public.as_array() != public.as_bytes()
78        {
79            return Err(Error::custom(format!(
80                "public key mismatch (stored: {stored_public}, expected: {public})",
81            )));
82        }
83
84        Ok(NodeKeys {
85            secret: partial.secret,
86        })
87    }
88}
89
90impl Serialize for NodeKeys {
91    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
92    where
93        S: serde::Serializer,
94    {
95        #[derive(Serialize)]
96        struct FullKeys<'a> {
97            secret: &'a HashBytes,
98            public: &'a HashBytes,
99        }
100
101        let secret = ed25519::SecretKey::from_bytes(self.secret.0);
102        let public = ed25519::PublicKey::from(&secret);
103
104        FullKeys {
105            secret: &self.secret,
106            public: HashBytes::wrap(public.as_bytes()),
107        }
108        .serialize(serializer)
109    }
110}
111
112impl rand::distr::Distribution<NodeKeys> for rand::distr::StandardUniform {
113    #[inline]
114    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> NodeKeys {
115        NodeKeys {
116            secret: rand::distr::StandardUniform.sample(rng),
117        }
118    }
119}