ab_farmer/single_disk_farm/
identity.rs

1//! Farm identity
2
3use ab_core_primitives::ed25519::{Ed25519PublicKey, Ed25519Signature};
4use ab_core_primitives::hashes::Blake3Hash;
5use ed25519_zebra::{SigningKey, VerificationKey};
6use parity_scale_codec::{Decode, Encode};
7use rand::TryRngCore;
8use rand::rngs::{OsError, OsRng};
9use std::path::Path;
10use std::{fmt, fs, io};
11use thiserror::Error;
12use tracing::debug;
13use zeroize::{Zeroize, Zeroizing};
14
15#[derive(Debug, Encode, Decode, Zeroize)]
16struct IdentityFileContents {
17    secret_key: [u8; 32],
18}
19
20/// Errors happening when trying to create/open single disk farm
21#[derive(Debug, Error)]
22pub enum IdentityError {
23    /// I/O error occurred
24    #[error("Identity I/O error: {0}")]
25    Io(#[from] io::Error),
26    /// Decoding error
27    #[error("Decoding error: {0}")]
28    Decoding(#[from] parity_scale_codec::Error),
29    /// Failed to generate identity seed
30    #[error("Failed to generate identity seed: {0}")]
31    IdentitySeedGeneration(#[from] OsError),
32}
33
34/// `Identity` struct is an abstraction of public & secret key related operations.
35///
36/// It is basically a wrapper of the keypair (which holds public & secret keys)
37/// and a context that will be used for signing.
38#[derive(Clone)]
39pub struct Identity {
40    signing_key: SigningKey,
41}
42
43impl fmt::Debug for Identity {
44    #[inline]
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        f.debug_struct("Identity")
47            .field("keypair", &self.signing_key)
48            .finish_non_exhaustive()
49    }
50}
51
52impl Identity {
53    pub(crate) const FILE_NAME: &'static str = "identity.bin";
54
55    /// Size of the identity file on disk
56    pub fn file_size() -> usize {
57        IdentityFileContents { secret_key: [0; _] }.encoded_size()
58    }
59
60    /// Opens the existing identity, or creates a new one.
61    pub fn open_or_create<B: AsRef<Path>>(base_directory: B) -> Result<Self, IdentityError> {
62        if let Some(identity) = Self::open(base_directory.as_ref())? {
63            Ok(identity)
64        } else {
65            Self::create(base_directory)
66        }
67    }
68
69    /// Opens the existing identity, returns `Ok(None)` if it doesn't exist.
70    pub fn open<B: AsRef<Path>>(base_directory: B) -> Result<Option<Self>, IdentityError> {
71        let identity_file = base_directory.as_ref().join(Self::FILE_NAME);
72        if identity_file.exists() {
73            debug!("Opening existing keypair");
74            let bytes = Zeroizing::new(fs::read(identity_file)?);
75            let IdentityFileContents { secret_key } =
76                IdentityFileContents::decode(&mut bytes.as_ref())?;
77
78            let signing_key = SigningKey::from(secret_key);
79
80            Ok(Some(Self { signing_key }))
81        } else {
82            debug!("Existing keypair not found");
83            Ok(None)
84        }
85    }
86
87    /// Creates new identity, overrides identity that might already exist.
88    pub fn create<B: AsRef<Path>>(base_directory: B) -> Result<Self, IdentityError> {
89        let identity_file = base_directory.as_ref().join(Self::FILE_NAME);
90        debug!("Generating new keypair");
91
92        let signing_key = SigningKey::from({
93            let mut seed = [0u8; 32];
94            OsRng.try_fill_bytes(&mut seed)?;
95            seed
96        });
97
98        let identity_file_contents = Zeroizing::new(IdentityFileContents {
99            secret_key: signing_key.into(),
100        });
101
102        fs::write(
103            identity_file,
104            Zeroizing::new(identity_file_contents.encode()),
105        )?;
106
107        Ok(Self { signing_key })
108    }
109
110    /// Returns the public key of the identity.
111    pub fn public_key(&self) -> Ed25519PublicKey {
112        Ed25519PublicKey::from(VerificationKey::from(&self.signing_key))
113    }
114
115    /// Returns the secret key of the identity.
116    pub fn secret_key(&self) -> [u8; 32] {
117        self.signing_key.into()
118    }
119
120    /// Sign block's pre-seal hash
121    pub fn sign_pre_seal_hash(&self, pre_seal_hash: &Blake3Hash) -> Ed25519Signature {
122        Ed25519Signature::from(self.signing_key.sign(pre_seal_hash.as_ref()))
123    }
124}