1use std::collections::BTreeMap;
21
22use alloy::primitives::{hex, keccak256};
23use serde::{Deserialize, Deserializer};
24
25use crate::error::Error;
26use crate::meta::NPE2Deployer;
27
28#[derive(Clone, Debug, Default, PartialEq, serde::Serialize)]
37pub struct MetaCache {
38 inner: BTreeMap<Vec<u8>, Vec<u8>>,
39}
40
41impl<'de> Deserialize<'de> for MetaCache {
51 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
52 #[derive(Deserialize)]
53 struct Wire {
54 inner: BTreeMap<Vec<u8>, Vec<u8>>,
55 }
56
57 let wire = Wire::deserialize(deserializer)?;
58 let mut cache = MetaCache::default();
59 for (hash, bytes) in wire.inner {
60 cache
61 .insert_verified(&hash, bytes)
62 .map_err(serde::de::Error::custom)?;
63 }
64 Ok(cache)
65 }
66}
67
68impl MetaCache {
69 pub fn insert_verified(&mut self, hash: &[u8], bytes: Vec<u8>) -> Result<&Vec<u8>, Error> {
77 if keccak256(&bytes).0 != hash {
78 return Err(Error::CorruptRecord(format!(
79 "bytes do not hash to the requested {}",
80 hex::encode_prefixed(hash)
81 )));
82 }
83 self.inner.insert(hash.to_vec(), bytes);
84 self.inner.get(hash).ok_or(Error::NoRecordFound)
85 }
86
87 pub fn get(&self, hash: &[u8]) -> Option<&Vec<u8>> {
89 self.inner.get(hash)
90 }
91
92 pub fn contains_key(&self, hash: &[u8]) -> bool {
94 self.inner.contains_key(hash)
95 }
96
97 pub fn remove(&mut self, hash: &[u8]) {
100 self.inner.remove(hash);
101 }
102
103 pub fn iter(&self) -> impl Iterator<Item = (&Vec<u8>, &Vec<u8>)> {
106 self.inner.iter()
107 }
108
109 pub fn is_empty(&self) -> bool {
111 self.inner.is_empty()
112 }
113
114 pub fn len(&self) -> usize {
116 self.inner.len()
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 fn hashed(bytes: &[u8]) -> Vec<u8> {
125 keccak256(bytes).0.to_vec()
126 }
127
128 #[test]
130 fn test_insert_verified_accepts_matching_bytes() {
131 let bytes = b"content".to_vec();
132 let hash = hashed(&bytes);
133 let mut cache = MetaCache::default();
134
135 assert_eq!(cache.insert_verified(&hash, bytes.clone()).unwrap(), &bytes);
136 assert_eq!(cache.get(&hash), Some(&bytes));
137 assert!(cache.contains_key(&hash));
138 assert_eq!(cache.len(), 1);
139 }
140
141 #[test]
144 fn test_insert_verified_rejects_mismatched_bytes_as_corrupt() {
145 let wrong_hash = vec![0x99u8; 32];
146 let mut cache = MetaCache::default();
147
148 match cache
149 .insert_verified(&wrong_hash, b"content".to_vec())
150 .unwrap_err()
151 {
152 Error::CorruptRecord(message) => assert!(
153 message.contains(&hex::encode_prefixed(&wrong_hash)),
154 "{}",
155 message
156 ),
157 other => panic!("expected CorruptRecord, got {:?}", other),
158 }
159
160 assert!(cache.is_empty());
162 assert!(!cache.contains_key(&wrong_hash));
163 }
164
165 #[test]
169 fn test_deserialize_rejects_an_unverified_entry() {
170 #[derive(serde::Serialize)]
171 struct Wire {
172 inner: std::collections::BTreeMap<Vec<u8>, Vec<u8>>,
173 }
174 let planted = Wire {
175 inner: std::collections::BTreeMap::from([(
176 vec![0x99u8; 32],
177 b"not the preimage".to_vec(),
178 )]),
179 };
180
181 let wire = serde_cbor::to_vec(&planted).unwrap();
182 let round: Result<MetaCache, _> = serde_cbor::from_slice(&wire);
183 assert!(round.is_err(), "an unverified entry round tripped in");
184 }
185
186 #[test]
189 fn test_deserialize_keeps_a_verified_entry() {
190 let bytes = b"content".to_vec();
191 let hash = hashed(&bytes);
192 let mut cache = MetaCache::default();
193 cache.insert_verified(&hash, bytes.clone()).unwrap();
194
195 let wire = serde_cbor::to_vec(&cache).unwrap();
196 let round: MetaCache = serde_cbor::from_slice(&wire).unwrap();
197 assert_eq!(round.get(&hash), Some(&bytes));
198 }
199
200 #[test]
203 fn test_serialization_is_deterministic() {
204 let mut cache = MetaCache::default();
205 for content in [b"one".to_vec(), b"two".to_vec(), b"three".to_vec()] {
206 let hash = hashed(&content);
207 cache.insert_verified(&hash, content).unwrap();
208 }
209 let a = serde_cbor::to_vec(&cache).unwrap();
210 let b = serde_cbor::to_vec(&cache.clone()).unwrap();
211 assert_eq!(a, b);
212 }
213}
214
215#[derive(Clone, Debug, Default, PartialEq, serde::Serialize)]
229pub struct DeployerCache {
230 inner: BTreeMap<Vec<u8>, NPE2Deployer>,
231}
232
233impl DeployerCache {
234 pub fn insert_verified(
244 &mut self,
245 key: &[u8],
246 deployer: NPE2Deployer,
247 ) -> Result<&NPE2Deployer, Error> {
248 if key.len() != 32 {
249 return Err(Error::CorruptRecord(format!(
250 "deployer key {} is {} bytes, not a 32 byte hash",
251 hex::encode_prefixed(key),
252 key.len()
253 )));
254 }
255 if let Some(field) = deployer.corrupt_field() {
256 return Err(Error::CorruptRecord(format!(
257 "deployer {} is empty, so it cannot be reproduced",
258 field
259 )));
260 }
261 if keccak256(&deployer.meta_bytes).0.as_slice() != deployer.meta_hash.as_slice() {
262 return Err(Error::CorruptRecord(format!(
263 "deployer meta bytes do not hash to its own meta hash {}",
264 hex::encode_prefixed(&deployer.meta_hash)
265 )));
266 }
267 self.inner.insert(key.to_vec(), deployer);
268 self.inner.get(key).ok_or(Error::NoRecordFound)
269 }
270
271 pub fn get(&self, key: &[u8]) -> Option<&NPE2Deployer> {
273 self.inner.get(key)
274 }
275
276 pub fn contains_key(&self, key: &[u8]) -> bool {
278 self.inner.contains_key(key)
279 }
280
281 pub fn iter(&self) -> impl Iterator<Item = (&Vec<u8>, &NPE2Deployer)> {
283 self.inner.iter()
284 }
285
286 pub fn is_empty(&self) -> bool {
288 self.inner.is_empty()
289 }
290}
291
292impl<'de> Deserialize<'de> for DeployerCache {
294 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
295 #[derive(Deserialize)]
296 struct Wire {
297 inner: BTreeMap<Vec<u8>, NPE2Deployer>,
298 }
299
300 let wire = Wire::deserialize(deserializer)?;
301 let mut cache = DeployerCache::default();
302 for (key, deployer) in wire.inner {
303 cache
304 .insert_verified(&key, deployer)
305 .map_err(serde::de::Error::custom)?;
306 }
307 Ok(cache)
308 }
309}
310
311#[cfg(test)]
312mod deployer_tests {
313 use super::*;
314
315 fn sound_deployer() -> NPE2Deployer {
318 let meta_bytes = b"constructor meta".to_vec();
319 NPE2Deployer {
320 meta_hash: keccak256(&meta_bytes).0.to_vec(),
321 meta_bytes,
322 bytecode: vec![0x01],
323 parser: vec![0x02],
324 store: vec![0x03],
325 interpreter: vec![0x04],
326 authoring_meta: None,
327 }
328 }
329
330 fn corrupt_message(result: Result<&NPE2Deployer, Error>) -> String {
331 match result.unwrap_err() {
332 Error::CorruptRecord(message) => message,
333 other => panic!("expected CorruptRecord, got {:?}", other),
334 }
335 }
336
337 #[test]
339 fn test_deployer_insert_verified_accepts_a_sound_record() {
340 let deployer = sound_deployer();
341 let key = vec![0x11u8; 32];
342 let mut cache = DeployerCache::default();
343
344 assert_eq!(
345 cache.insert_verified(&key, deployer.clone()).unwrap(),
346 &deployer
347 );
348 assert_eq!(cache.get(&key), Some(&deployer));
349 }
350
351 #[test]
355 fn test_deployer_insert_verified_rejects_a_key_that_is_not_a_hash() {
356 let mut cache = DeployerCache::default();
357
358 for key in [vec![], vec![0x11u8; 31], vec![0x11u8; 33]] {
359 let message = corrupt_message(cache.insert_verified(&key, sound_deployer()));
360 assert!(
361 message.contains("not a 32 byte hash"),
362 "{} bytes: {}",
363 key.len(),
364 message
365 );
366 assert!(cache.is_empty());
367 }
368 }
369
370 #[test]
373 fn test_deployer_insert_verified_rejects_a_record_missing_a_field() {
374 let key = vec![0x11u8; 32];
375
376 for field in [
377 "meta_hash",
378 "meta_bytes",
379 "bytecode",
380 "parser",
381 "store",
382 "interpreter",
383 ] {
384 let mut deployer = sound_deployer();
385 match field {
386 "meta_hash" => deployer.meta_hash = vec![],
387 "meta_bytes" => deployer.meta_bytes = vec![],
388 "bytecode" => deployer.bytecode = vec![],
389 "parser" => deployer.parser = vec![],
390 "store" => deployer.store = vec![],
391 "interpreter" => deployer.interpreter = vec![],
392 _ => unreachable!(),
393 }
394
395 let mut cache = DeployerCache::default();
396 let message = corrupt_message(cache.insert_verified(&key, deployer));
397 assert!(message.contains(field), "{}: {}", field, message);
398 assert!(cache.is_empty());
399 }
400 }
401
402 #[test]
406 fn test_deployer_insert_verified_rejects_a_lying_meta_hash() {
407 let mut deployer = sound_deployer();
408 deployer.meta_bytes = b"different bytes".to_vec();
409 let mut cache = DeployerCache::default();
410
411 let message = corrupt_message(cache.insert_verified(&[0x11u8; 32], deployer));
412 assert!(message.contains("do not hash to"), "{}", message);
413 assert!(cache.is_empty());
414 }
415
416 #[test]
418 fn test_deployer_deserialize_rejects_an_unverified_entry() {
419 #[derive(serde::Serialize)]
420 struct Wire {
421 inner: BTreeMap<Vec<u8>, NPE2Deployer>,
422 }
423
424 for (key, deployer) in [
425 (vec![0x11u8; 31], sound_deployer()),
426 (vec![0x11u8; 32], {
427 let mut d = sound_deployer();
428 d.parser = vec![];
429 d
430 }),
431 (vec![0x11u8; 32], {
432 let mut d = sound_deployer();
433 d.meta_bytes = b"different bytes".to_vec();
434 d
435 }),
436 ] {
437 let planted = Wire {
438 inner: BTreeMap::from([(key, deployer)]),
439 };
440 let wire = serde_cbor::to_vec(&planted).unwrap();
441 let round: Result<DeployerCache, _> = serde_cbor::from_slice(&wire);
442 assert!(round.is_err(), "an unverified entry round tripped in");
443 }
444 }
445
446 #[test]
449 fn test_deployer_deserialize_keeps_a_verified_entry() {
450 let deployer = sound_deployer();
451 let key = vec![0x11u8; 32];
452 let mut cache = DeployerCache::default();
453 cache.insert_verified(&key, deployer.clone()).unwrap();
454
455 let wire = serde_cbor::to_vec(&cache).unwrap();
456 let round: DeployerCache = serde_cbor::from_slice(&wire).unwrap();
457 assert_eq!(round.get(&key), Some(&deployer));
458 }
459}