1use crate::protocol::peer::{Peer, PeerPow};
27use crate::tools::keys::Keys;
28use crate::tools::parallel_pow_generator::ParallelPowGenerator;
29use crate::tools::time::{TimeMillis, TimeMillisBytes, TIME_MILLIS_BYTES};
30use crate::tools::time_provider::time_provider::TimeProvider;
31use crate::tools::types::{Hash, Id, PQCommitmentBytes, Pow, Salt, Signature, SignatureKey, VerificationKey, VerificationKeyBytes, HASH_BYTES, ID_BYTES, PQ_COMMITMENT_BYTES, SALT_BYTES, SIGNATURE_KEY_BYTES, VERIFICATION_KEY_BYTES};
32use crate::tools::{config, pow, tools, types};
33use std::fmt;
34
35#[derive(Clone)]
36pub struct ServerId {
37 pub keys: Keys,
38
39 pub sponsor_id: Id,
41 pub timestamp: TimeMillis,
42 pub hash: Hash,
43 pub salt: Salt,
44 pub pow: Pow,
45
46 pub id: Id,
47}
48
49impl ServerId {
50 pub fn id_hex(&self) -> String {
51 hex::encode(self.id)
52 }
53
54 pub fn server_pow_hash_to_id(hash: Hash) -> anyhow::Result<Id> {
55 if hash.len() != ID_BYTES {
56 anyhow::bail!("Invalid Hash length: expected {} bytes, got {} bytes", ID_BYTES, hash.len());
57 }
58
59 let id = Id(tools::reverse_bytes(hash.as_bytes()));
60 Ok(id)
61 }
62
63 pub async fn pow_generate(
65 time_provider: &dyn TimeProvider,
66 pow_min: Pow,
67 sponsor_id: &Id,
68 verification_key: &VerificationKeyBytes,
69 pq_commitment_bytes: &PQCommitmentBytes,
70 content_hash: &Hash,
71 pow_generator: &dyn ParallelPowGenerator,
72 ) -> anyhow::Result<(TimeMillis, Salt, Pow, Hash)> {
73 let timestamp = time_provider.current_time_millis();
74 let timestamp_be = timestamp.encode_be();
75 let datas = [sponsor_id.as_ref(), verification_key.as_ref(), pq_commitment_bytes.as_ref(), timestamp_be.as_ref(), content_hash.as_ref()];
76 let data_hash = pow::pow_compute_data_hash(&datas);
77 let (salt, pow, pow_hash) = pow_generator.generate("server_id", pow_min, data_hash).await?;
78 Ok((timestamp, salt, pow, pow_hash))
79 }
80
81 pub fn pow_measure(sponsor_id: &Id, verification_key: &VerificationKeyBytes, pqcommitment_bytes: &PQCommitmentBytes, timestamp_be: &TimeMillisBytes, content_hash: &Hash, salt: &Salt) -> anyhow::Result<(Pow, Hash)> {
82 pow::pow_measure(&[sponsor_id.as_ref(), verification_key.as_ref(), pqcommitment_bytes.as_ref(), timestamp_be.as_ref(), content_hash.as_ref()], salt)
83 }
84
85 pub async fn new(time_provider: &dyn TimeProvider, pow_min: Pow, skip_pq_commitment_bytes: bool, pow_generator: &dyn ParallelPowGenerator) -> anyhow::Result<Self> {
86 let sponsor_id = Id::random();
87 let keys = Keys::from_rnd(skip_pq_commitment_bytes)?;
88 let hash = Hash::random();
89 let (timestamp, salt, pow, pow_hash) = ServerId::pow_generate(time_provider, pow_min, &sponsor_id, &keys.verification_key_bytes, &keys.pq_commitment_bytes, &hash, pow_generator).await?;
90 let id = ServerId::server_pow_hash_to_id(pow_hash)?;
91
92 Ok(ServerId {
93 keys,
94 sponsor_id,
95 timestamp,
96 hash,
97 salt,
98 pow,
99 id,
100 })
101 }
102
103 pub fn to_peer(&self, time_provider: &dyn TimeProvider) -> anyhow::Result<Peer> {
104 let mut peer = Peer {
105 id: self.id,
106 verification_key_bytes: self.keys.verification_key_bytes,
107 pq_commitment_bytes: self.keys.pq_commitment_bytes,
108
109 pow_initial: PeerPow {
110 sponsor_id: self.sponsor_id,
111 timestamp: self.timestamp,
112 content_hash: self.hash,
113 salt: self.salt,
114 pow: self.pow,
115 },
116
117 pow_current_day: PeerPow {
118 sponsor_id: self.sponsor_id,
119 timestamp: self.timestamp,
120 content_hash: self.hash,
121 salt: self.salt,
122 pow: self.pow,
123 },
124
125 pow_current_month: PeerPow {
126 sponsor_id: self.sponsor_id,
127 timestamp: self.timestamp,
128 content_hash: self.hash,
129 salt: self.salt,
130 pow: self.pow,
131 },
132
133 address: "".to_string(),
134 version: env!("CARGO_PKG_VERSION").to_string(),
135
136 timestamp: TimeMillis::zero(),
137 signature: Signature::zero(),
138 };
139
140 peer.sign(time_provider, &self.keys.signature_key)?;
142
143 Ok(peer)
144 }
145
146 pub fn verify(&self) -> anyhow::Result<()> {
147 let (pow, pow_hash) = ServerId::pow_measure(&self.sponsor_id, &self.keys.verification_key_bytes, &self.keys.pq_commitment_bytes, &self.timestamp.encode_be(), &self.hash, &self.salt)?;
148 if pow != self.pow {
149 anyhow::bail!("ServerID pow does not verify");
150 }
151
152 if pow < config::SERVER_KEY_POW_MIN {
153 anyhow::bail!("ServerID pow is not sufficient");
154 }
155
156 let id = ServerId::server_pow_hash_to_id(pow_hash)?;
157
158 if id != self.id {
159 anyhow::bail!("ServerID id does not verify");
160 }
161
162 Ok(())
163 }
164
165 pub fn encode(&self) -> anyhow::Result<Vec<u8>> {
166 let mut bytes = Vec::new();
167 {
168 bytes.extend_from_slice(self.keys.signature_key.as_ref());
169 bytes.extend_from_slice(self.keys.verification_key.as_ref());
170 bytes.extend_from_slice(self.keys.pq_commitment_bytes.as_ref());
171 bytes.extend_from_slice(self.sponsor_id.as_ref());
172 bytes.extend_from_slice(self.timestamp.encode_be().as_ref());
173 bytes.extend_from_slice(self.hash.as_ref());
174 bytes.extend_from_slice(self.salt.as_ref());
175 bytes.push(self.pow.0);
176 bytes.extend_from_slice(self.id.as_ref());
177 }
178
179 let expected_len = SIGNATURE_KEY_BYTES + VERIFICATION_KEY_BYTES + PQ_COMMITMENT_BYTES + ID_BYTES + TIME_MILLIS_BYTES + HASH_BYTES + SALT_BYTES + 1 + types::ID_BYTES;
181 if bytes.len() != expected_len {
182 anyhow::bail!("incorrect byte count: expected {}, got {}", expected_len, bytes.len());
183 }
184
185 Ok(bytes)
186 }
187
188 pub fn decode(bytes: &[u8]) -> anyhow::Result<Self> {
189 let expected_len = SIGNATURE_KEY_BYTES + VERIFICATION_KEY_BYTES + PQ_COMMITMENT_BYTES + ID_BYTES + TIME_MILLIS_BYTES + HASH_BYTES + SALT_BYTES + 1 + types::ID_BYTES;
191 if bytes.len() != expected_len {
192 anyhow::bail!("incorrect byte count: expected {}, got {}", expected_len, bytes.len());
193 }
194
195 let mut pos = 0;
196
197 let signature_key_bytes = &bytes[pos..pos + SIGNATURE_KEY_BYTES];
198 pos += SIGNATURE_KEY_BYTES;
199 let verification_key_bytes = &bytes[pos..pos + VERIFICATION_KEY_BYTES];
200 pos += VERIFICATION_KEY_BYTES;
201 let pq_commitment_bytes = &bytes[pos..pos + PQ_COMMITMENT_BYTES];
202 pos += PQ_COMMITMENT_BYTES;
203 let sponsor_id = Id::from_slice(bytes[pos..pos + ID_BYTES].try_into()?)?;
204 pos += ID_BYTES;
205 let timestamp = TimeMillis::timestamp_decode_be(&TimeMillisBytes::from_bytes(&bytes[pos..pos + 8])?);
206 pos += TIME_MILLIS_BYTES;
207 let hash = Hash::from_slice(bytes[pos..pos + HASH_BYTES].try_into()?)?;
208 pos += HASH_BYTES;
209 let salt = Salt::from_slice(bytes[pos..pos + SALT_BYTES].try_into()?)?;
210 pos += SALT_BYTES;
211 let pow = Pow(bytes[pos]);
212 pos += 1;
213 let id_bytes = &bytes[pos..pos + types::ID_BYTES];
214
215 let signature_key_arr: &[u8; 32] = signature_key_bytes.try_into()?;
217 let verification_key_arr: &[u8; 32] = verification_key_bytes.try_into()?;
218 let signature_key = SignatureKey::from_bytes(signature_key_arr)?;
219 let verification_key = VerificationKey::from_bytes_raw(verification_key_arr)?;
220 let verification_key_bytes = verification_key.to_verification_key_bytes();
221 let pq_commitment = PQCommitmentBytes::from_slice(pq_commitment_bytes)?;
222
223 let keys = Keys {
224 signature_key,
225 verification_key,
226 verification_key_bytes,
227 pq_commitment_bytes: pq_commitment,
228 };
229
230 let id = Id::from_slice(id_bytes)?;
231
232 Ok(ServerId {
233 keys,
234 sponsor_id,
235 timestamp,
236 hash,
237 salt,
238 pow,
239 id,
240 })
241 }
242}
243
244impl fmt::Display for ServerId {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 write!(f, "[ id={} pow={} hash={} salt={} keys={} ]", self.id, self.pow, self.hash, self.salt, self.keys)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use crate::tools::parallel_pow_generator::StubParallelPowGenerator;
254 use crate::tools::time_provider::time_provider::RealTimeProvider;
255
256 #[tokio::test]
257 async fn pow_test() -> anyhow::Result<()> {
258 let time_provider = RealTimeProvider::default();
259 let pow_generator = StubParallelPowGenerator::new();
260 const POW_MAX: u8 = 2 * 8;
261 for pow_min in 0..POW_MAX {
262 let server_id = ServerId::new(&time_provider, Pow(pow_min), true, &pow_generator).await?;
263 assert!(server_id.pow >= Pow(pow_min));
264 }
265
266 Ok(())
267 }
268
269 #[tokio::test]
270 async fn server_id_encode_decode_verify() -> anyhow::Result<()> {
271 let time_provider = RealTimeProvider::default();
272 let pow_generator = StubParallelPowGenerator::new();
273 let server_id = ServerId::new(&time_provider, Pow(8), false, &pow_generator).await?;
274 let encoded = server_id.encode()?;
275 let decoded = ServerId::decode(&encoded)?;
276 decoded.verify()?;
277 Ok(())
278 }
279
280 #[tokio::test]
281 async fn server_id_encode_decode_reversibility() -> anyhow::Result<()> {
282 let time_provider = RealTimeProvider::default();
283 let pow_generator = StubParallelPowGenerator::new();
284 let server_id = ServerId::new(&time_provider, Pow(8), false, &pow_generator).await?;
285
286 let server_id_encoded = server_id.encode()?;
287 let server_id2 = ServerId::decode(&server_id_encoded)?;
288
289 assert_eq!(server_id.keys.signature_key, server_id2.keys.signature_key, "Keys do not match after decode");
292 assert_eq!(server_id.keys.verification_key, server_id2.keys.verification_key, "Keys do not match after decode");
293 assert_eq!(server_id.keys.pq_commitment_bytes, server_id2.keys.pq_commitment_bytes, "Keys do not match after decode");
294 assert_eq!(server_id.timestamp, server_id2.timestamp, "Timestamps do not match");
295 assert_eq!(server_id.salt, server_id2.salt, "Salts do not match");
296 assert_eq!(server_id.pow, server_id2.pow, "PoW bits do not match");
297 assert_eq!(server_id.id, server_id2.id, "IDs do not match");
298
299 Ok(())
300 }
301}