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 lru_cache::LruCache;
13use std::{slice, sync::Arc};
14
15pub type Cache = Vec<Node>;
16
17const CACHE_KEEP: usize = 3;
22
23#[derive(Clone)]
24pub struct CacheBuilder {
25 seedhash: Arc<Mutex<SeedHashCompute>>,
26 caches: Arc<Mutex<LruCache<u64, Arc<Cache>>>>,
27}
28
29impl CacheBuilder {
30 pub fn new() -> Self {
31 CacheBuilder {
32 seedhash: Arc::new(Mutex::new(SeedHashCompute::default())),
33 caches: Arc::new(Mutex::new(LruCache::new(CACHE_KEEP))),
34 }
35 }
36
37 pub fn light(&self, block_height: u64) -> Light {
38 Light::new_with_builder(self, block_height)
39 }
40
41 fn block_height_to_ident(&self, block_height: u64) -> H256 {
42 self.seedhash.lock().hash_block_height(block_height)
43 }
44
45 #[allow(dead_code)]
46 fn stage_to_ident(&self, stage: u64) -> H256 {
47 self.seedhash.lock().hash_stage(stage)
48 }
49
50 pub fn new_cache(&self, block_height: u64) -> Arc<Cache> {
51 let stage = block_height / POW_STAGE_LENGTH;
52
53 let mut caches = self.caches.lock();
54 if let Some(cache) = caches.get_mut(&stage) {
55 return cache.clone();
56 }
57
58 let ident = self.block_height_to_ident(block_height);
59 let cache_size = get_cache_size(block_height);
60
61 debug_assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
65 let num_nodes = cache_size / NODE_BYTES;
66
67 let cache = Arc::new(make_memory_cache(num_nodes, &ident));
68 caches.insert(stage, cache.clone());
69
70 cache
71 }
72}
73
74fn make_memory_cache(num_nodes: usize, ident: &H256) -> Cache {
75 let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
76 unsafe {
79 initialize_memory(nodes.as_mut_ptr(), num_nodes, ident);
80 nodes.set_len(num_nodes);
81 }
82
83 nodes
84}
85
86unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) {
95 let dst = slice::from_raw_parts_mut(memory as *mut u8, NODE_BYTES);
97
98 debug_assert_eq!(ident.len(), 32);
99 keccak_512::write(&ident[..], dst);
100
101 for i in 1..num_nodes {
102 let dst = slice::from_raw_parts_mut(
104 memory.offset(i as _) as *mut u8,
105 NODE_BYTES,
106 );
107 let src = slice::from_raw_parts(
108 memory.offset(i as isize - 1) as *mut u8,
109 NODE_BYTES,
110 );
111 keccak_512::write(src, dst);
112 }
113
114 let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes);
116
117 for _ in 0..POW_CACHE_ROUNDS {
118 for i in 0..num_nodes {
119 let data_idx = (num_nodes - 1 + i) % num_nodes;
120 let idx =
121 nodes.get_unchecked_mut(i).as_words()[0] as usize % num_nodes;
122
123 let data = {
124 let mut data: Node = nodes.get_unchecked(data_idx).clone();
125 let rhs: &Node = nodes.get_unchecked(idx);
126
127 for (a, b) in
128 data.as_dwords_mut().iter_mut().zip(rhs.as_dwords())
129 {
130 *a ^= *b;
131 }
132
133 data
134 };
135
136 keccak_512::write(
137 &data.bytes,
138 &mut nodes.get_unchecked_mut(i).bytes,
139 );
140 }
141 }
142}