1use parking_lot::Mutex;
2
3use super::{
4 compute::Light,
5 keccak::{keccak_512, H256},
6 seed_compute::SeedHashCompute,
7 shared::{
8 get_cache_size, Node, NODE_BYTES, POW_CACHE_ROUNDS, POW_STAGE_LENGTH,
9 },
10};
11
12use std::{collections::HashMap, slice, sync::Arc};
13
14pub type Cache = Vec<Node>;
15
16#[derive(Clone)]
17pub struct CacheBuilder {
18 seedhash: Arc<Mutex<SeedHashCompute>>,
19 caches: Arc<Mutex<HashMap<u64, Arc<Cache>>>>,
20}
21
22impl CacheBuilder {
23 pub fn new() -> Self {
24 CacheBuilder {
25 seedhash: Arc::new(Mutex::new(SeedHashCompute::default())),
26 caches: Arc::new(Mutex::new(HashMap::new())),
27 }
28 }
29
30 pub fn light(&self, block_height: u64) -> Light {
31 Light::new_with_builder(self, block_height)
32 }
33
34 fn block_height_to_ident(&self, block_height: u64) -> H256 {
35 self.seedhash.lock().hash_block_height(block_height)
36 }
37
38 #[allow(dead_code)]
39 fn stage_to_ident(&self, stage: u64) -> H256 {
40 self.seedhash.lock().hash_stage(stage)
41 }
42
43 pub fn new_cache(&self, block_height: u64) -> Arc<Cache> {
44 let stage = block_height / POW_STAGE_LENGTH;
45
46 let mut caches = self.caches.lock();
47 if let Some(cache) = caches.get(&stage) {
48 return cache.clone();
49 }
50
51 let ident = self.block_height_to_ident(block_height);
52 let cache_size = get_cache_size(block_height);
53
54 debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
58 let num_nodes = cache_size / NODE_BYTES;
59
60 let cache = Arc::new(make_memory_cache(num_nodes, &ident));
61 caches.insert(stage, cache.clone());
62
63 cache
64 }
65}
66
67fn make_memory_cache(num_nodes: usize, ident: &H256) -> Cache {
68 let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
69 unsafe {
72 initialize_memory(nodes.as_mut_ptr(), num_nodes, ident);
73 nodes.set_len(num_nodes);
74 }
75
76 nodes
77}
78
79unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) {
88 let dst = slice::from_raw_parts_mut(memory as *mut u8, NODE_BYTES);
90
91 debug_assert_eq!(ident.len(), 32);
92 keccak_512::write(&ident[..], dst);
93
94 for i in 1..num_nodes {
95 let dst = slice::from_raw_parts_mut(
97 memory.offset(i as _) as *mut u8,
98 NODE_BYTES,
99 );
100 let src = slice::from_raw_parts(
101 memory.offset(i as isize - 1) as *mut u8,
102 NODE_BYTES,
103 );
104 keccak_512::write(src, dst);
105 }
106
107 let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes);
109
110 for _ in 0..POW_CACHE_ROUNDS {
111 for i in 0..num_nodes {
112 let data_idx = (num_nodes - 1 + i) % num_nodes;
113 let idx =
114 nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes;
115
116 let data = {
117 let mut data: Node = nodes.get_unchecked(data_idx).clone();
118 let rhs: &Node = nodes.get_unchecked(idx);
119
120 for (a, b) in
121 data.as_dwords_mut().iter_mut().zip(rhs.as_dwords())
122 {
123 *a ^= *b;
124 }
125
126 data
127 };
128
129 keccak_512::write(
130 &data.bytes,
131 &mut nodes.get_unchecked_mut(i).bytes,
132 );
133 }
134 }
135}