1extern crate argon2;
2
3use std::sync::{Arc, RwLock};
4use std::time::Instant;
5
6use self::argon2::block::Block;
7
8use super::byte_string;
9use super::superscalar::{Blake2Generator, ScProgram};
10
11const RANDOMX_ARGON_LANES: u32 = 1;
12const RANDOMX_ARGON_MEMORY: u32 = 262144;
13const RANDOMX_ARGON_SALT: &[u8; 8] = b"RandomX\x03";
14const RANDOMX_ARGON_ITERATIONS: u32 = 3;
15const RANDOMX_CACHE_ACCESSES: usize = 8;
16
17const ARGON2_SYNC_POINTS: u32 = 4;
18const ARGON_BLOCK_SIZE: u32 = 1024;
19
20pub const CACHE_LINE_SIZE: u64 = 64;
21pub const DATASET_ITEM_COUNT: usize = (2147483648 + 33554368) / 64; const SUPERSCALAR_MUL_0: u64 = 6364136223846793005;
24const SUPERSCALAR_ADD_1: u64 = 9298411001130361340;
25const SUPERSCALAR_ADD_2: u64 = 12065312585734608966;
26const SUPERSCALAR_ADD_3: u64 = 9306329213124626780;
27const SUPERSCALAR_ADD_4: u64 = 5281919268842080866;
28const SUPERSCALAR_ADD_5: u64 = 10536153434571861004;
29const SUPERSCALAR_ADD_6: u64 = 3398623926847679864;
30const SUPERSCALAR_ADD_7: u64 = 9549104520008361294;
31
32pub struct SeedMemory {
34 pub blocks: Box<[Block]>,
35 pub programs: Vec<ScProgram<'static>>,
36}
37
38impl SeedMemory {
39 pub fn no_memory() -> SeedMemory {
40 SeedMemory {
41 blocks: Box::new([]),
42 programs: Vec::with_capacity(0),
43 }
44 }
45
46 pub fn new_initialised(key: &[u8]) -> SeedMemory {
48 let mut mem = argon2::memory::Memory::new(RANDOMX_ARGON_LANES, RANDOMX_ARGON_MEMORY);
49 let context = &create_argon_context(key);
50 argon2::core::initialize(context, &mut mem);
51 argon2::core::fill_memory_blocks(context, &mut mem);
52
53 let mut programs = Vec::with_capacity(RANDOMX_CACHE_ACCESSES);
54 let mut generator = Blake2Generator::new(key, 0);
55 for _ in 0..RANDOMX_CACHE_ACCESSES {
56 programs.push(ScProgram::generate(&mut generator));
57 }
58
59 SeedMemory {
60 blocks: mem.blocks,
61 programs,
62 }
63 }
64}
65
66fn create_argon_context<'a>(key: &'a [u8]) -> argon2::context::Context<'a> {
67 let segment_length = RANDOMX_ARGON_MEMORY / (RANDOMX_ARGON_LANES * ARGON2_SYNC_POINTS);
68 let config = argon2::config::Config {
69 ad: &[],
70 hash_length: 0,
71 lanes: RANDOMX_ARGON_LANES,
72 mem_cost: RANDOMX_ARGON_MEMORY,
73 secret: &[],
74 time_cost: RANDOMX_ARGON_ITERATIONS,
75 variant: argon2::Variant::Argon2d,
76 version: argon2::Version::Version13,
77 };
78 argon2::context::Context {
79 config,
80 memory_blocks: RANDOMX_ARGON_MEMORY,
81 pwd: key,
82 salt: RANDOMX_ARGON_SALT,
83 lane_length: segment_length * ARGON2_SYNC_POINTS,
84 segment_length,
85 }
86}
87
88fn mix_block_value(seed_mem: &SeedMemory, reg_value: u64, r: usize) -> u64 {
89 let mask = (((RANDOMX_ARGON_MEMORY * ARGON_BLOCK_SIZE) as u64) / CACHE_LINE_SIZE) - 1;
90 let byte_offset = ((reg_value & mask) * CACHE_LINE_SIZE) + (8 * r as u64);
91
92 let block_ix = byte_offset / ARGON_BLOCK_SIZE as u64;
93 let block_v_ix = (byte_offset - (block_ix * ARGON_BLOCK_SIZE as u64)) / 8;
94 seed_mem.blocks[block_ix as usize][block_v_ix as usize]
95}
96
97pub fn init_dataset_item(seed_mem: &SeedMemory, item_num: u64) -> [u64; 8] {
98 let mut ds = [0; 8];
99
100 let mut reg_value = item_num;
101 ds[0] = (item_num + 1).wrapping_mul(SUPERSCALAR_MUL_0);
102 ds[1] = ds[0] ^ SUPERSCALAR_ADD_1;
103 ds[2] = ds[0] ^ SUPERSCALAR_ADD_2;
104 ds[3] = ds[0] ^ SUPERSCALAR_ADD_3;
105 ds[4] = ds[0] ^ SUPERSCALAR_ADD_4;
106 ds[5] = ds[0] ^ SUPERSCALAR_ADD_5;
107 ds[6] = ds[0] ^ SUPERSCALAR_ADD_6;
108 ds[7] = ds[0] ^ SUPERSCALAR_ADD_7;
109
110 for prog in &seed_mem.programs {
111 prog.execute(&mut ds);
112
113 for (r, v) in ds.iter_mut().enumerate() {
114 let mix_value = mix_block_value(seed_mem, reg_value, r);
115 *v ^= mix_value;
116 }
117 reg_value = ds[prog.address_reg];
118 }
119 ds
120}
121
122#[derive(Clone)]
123pub struct VmMemoryAllocator {
124 pub vm_memory_seed: String,
125 pub vm_memory: Arc<VmMemory>,
126}
127
128impl VmMemoryAllocator {
129 pub fn initial() -> VmMemoryAllocator {
130 VmMemoryAllocator {
131 vm_memory_seed: "".to_string(),
132 vm_memory: Arc::new(VmMemory::no_memory()),
133 }
134 }
135
136 pub fn reallocate(&mut self, seed: String) {
137 if seed != self.vm_memory_seed {
138 let mem_init_start = Instant::now();
139 self.vm_memory = Arc::new(VmMemory::full(&byte_string::string_to_u8_array(&seed)));
140 self.vm_memory_seed = seed;
141 info!(
142 "memory init took {}ms with seed_hash: {}",
143 mem_init_start.elapsed().as_millis(),
144 self.vm_memory_seed,
145 );
146 }
147 }
148}
149
150pub struct VmMemory {
151 pub seed_memory: SeedMemory,
152 pub dataset_memory: RwLock<Vec<Option<[u64; 8]>>>,
153 pub cache: bool,
154}
155
156impl VmMemory {
157 pub fn no_memory() -> VmMemory {
159 VmMemory {
160 seed_memory: SeedMemory::no_memory(),
161 cache: false,
162 dataset_memory: RwLock::new(Vec::with_capacity(0)),
163 }
164 }
165
166 pub fn light(key: &[u8]) -> VmMemory {
167 VmMemory {
168 seed_memory: SeedMemory::new_initialised(key),
169 cache: false,
170 dataset_memory: RwLock::new(Vec::with_capacity(0)),
171 }
172 }
173 pub fn full(key: &[u8]) -> VmMemory {
174 let seed_mem = SeedMemory::new_initialised(key);
175 let mem = vec![None; DATASET_ITEM_COUNT];
176 VmMemory {
177 seed_memory: seed_mem,
178 cache: true,
179 dataset_memory: RwLock::new(mem),
180 }
181 }
182
183 pub fn dataset_read(&self, offset: u64, reg: &mut [u64; 8]) {
184 let item_num = offset / CACHE_LINE_SIZE;
185
186 if self.cache {
187 {
188 let mem = self.dataset_memory.read().unwrap();
189 let rl_cached = &mem[item_num as usize];
190 if let Some(rl) = rl_cached {
191 for i in 0..8 {
192 reg[i] ^= rl[i];
193 }
194 return;
195 }
196 }
197 {
198 let rl = init_dataset_item(&self.seed_memory, item_num);
199 let mut mem_mut = self.dataset_memory.write().unwrap();
200 mem_mut[item_num as usize] = Some(rl);
201 for i in 0..8 {
202 reg[i] ^= rl[i];
203 }
204 }
205 } else {
206 let rl = init_dataset_item(&self.seed_memory, item_num);
207 for i in 0..8 {
208 reg[i] ^= rl[i];
209 }
210 }
211 }
212}