ab_farmer/single_disk_farm/
identity.rs1use 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#[derive(Debug, Error)]
22pub enum IdentityError {
23 #[error("Identity I/O error: {0}")]
25 Io(#[from] io::Error),
26 #[error("Decoding error: {0}")]
28 Decoding(#[from] parity_scale_codec::Error),
29 #[error("Failed to generate identity seed: {0}")]
31 IdentitySeedGeneration(#[from] OsError),
32}
33
34#[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 pub fn file_size() -> usize {
57 IdentityFileContents { secret_key: [0; _] }.encoded_size()
58 }
59
60 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 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 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 pub fn public_key(&self) -> Ed25519PublicKey {
112 Ed25519PublicKey::from(VerificationKey::from(&self.signing_key))
113 }
114
115 pub fn secret_key(&self) -> [u8; 32] {
117 self.signing_key.into()
118 }
119
120 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}