ab_farmer/single_disk_farm/
identity.rs1use ab_core_primitives::ed25519::{Ed25519PublicKey, Ed25519Signature};
4use ab_core_primitives::hashes::Blake3Hash;
5use ed25519_dalek::{Signer, SigningKey, VerifyingKey};
6use parity_scale_codec::{Decode, Encode};
7use rand::TryRng;
8use rand::rngs::{SysError, SysRng};
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] SysError),
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>(base_directory: B) -> Result<Self, IdentityError>
62 where
63 B: AsRef<Path>,
64 {
65 if let Some(identity) = Self::open(base_directory.as_ref())? {
66 Ok(identity)
67 } else {
68 Self::create(base_directory)
69 }
70 }
71
72 pub fn open<B>(base_directory: B) -> Result<Option<Self>, IdentityError>
74 where
75 B: AsRef<Path>,
76 {
77 let identity_file = base_directory.as_ref().join(Self::FILE_NAME);
78 if identity_file.exists() {
79 debug!("Opening existing keypair");
80 let bytes = Zeroizing::new(fs::read(identity_file)?);
81 let IdentityFileContents { secret_key } =
82 IdentityFileContents::decode(&mut bytes.as_ref())?;
83
84 let signing_key = SigningKey::from(secret_key);
85
86 Ok(Some(Self { signing_key }))
87 } else {
88 debug!("Existing keypair not found");
89 Ok(None)
90 }
91 }
92
93 pub fn create<B>(base_directory: B) -> Result<Self, IdentityError>
95 where
96 B: AsRef<Path>,
97 {
98 let identity_file = base_directory.as_ref().join(Self::FILE_NAME);
99 debug!("Generating new keypair");
100
101 let signing_key = SigningKey::from({
102 let mut seed = [0u8; 32];
103 SysRng.try_fill_bytes(&mut seed)?;
104 seed
105 });
106
107 let identity_file_contents = Zeroizing::new(IdentityFileContents {
108 secret_key: signing_key.to_bytes(),
109 });
110
111 fs::write(
112 identity_file,
113 Zeroizing::new(identity_file_contents.encode()),
114 )?;
115
116 Ok(Self { signing_key })
117 }
118
119 pub fn public_key(&self) -> Ed25519PublicKey {
121 Ed25519PublicKey::from(VerifyingKey::from(&self.signing_key))
122 }
123
124 pub fn shard_commitments_seed(&self) -> Blake3Hash {
126 Blake3Hash::from(blake3::hash(&self.secret_key()))
127 }
128
129 pub fn secret_key(&self) -> [u8; 32] {
131 self.signing_key.to_bytes()
132 }
133
134 pub fn sign_pre_seal_hash(&self, pre_seal_hash: &Blake3Hash) -> Ed25519Signature {
136 Ed25519Signature::from(self.signing_key.sign(pre_seal_hash.as_ref()))
137 }
138}