randomx_rust/
types.rs

1extern crate libc;
2
3use std::ptr::null_mut;
4use std::ptr::NonNull;
5use std::sync::{Arc, RwLock};
6use std::thread;
7
8use ffi::*;
9use libc::c_void;
10
11struct Wrapper<T>(NonNull<T>);
12unsafe impl<T> std::marker::Send for Wrapper<T> {}
13
14pub enum RxAction {
15	Changed,
16	NotChanged,
17}
18
19#[derive(Debug)]
20pub struct Trash {
21	cache: Option<RxCache>,
22	dataset: Option<RxDataset>,
23}
24
25impl Trash {
26	pub fn empty(&mut self) {
27		self.cache = None;
28		self.dataset = None;
29	}
30}
31
32impl Default for Trash {
33	fn default() -> Self {
34		Trash {
35			cache: None,
36			dataset: None,
37		}
38	}
39}
40
41#[derive(Debug)]
42pub struct RxCache {
43	cache: *mut randomx_cache,
44}
45
46impl Drop for RxCache {
47	fn drop(&mut self) {
48		unsafe {
49			randomx_release_cache(self.cache);
50		}
51	}
52}
53
54#[derive(Debug)]
55pub struct RxDataset {
56	dataset: *mut randomx_dataset,
57}
58
59impl Drop for RxDataset {
60	fn drop(&mut self) {
61		unsafe {
62			randomx_release_dataset(self.dataset);
63		}
64	}
65}
66
67#[derive(Debug)]
68pub struct RxState {
69	pub seed: [u8; 32],
70	pub hard_aes: bool,
71	pub full_mem: bool,
72	pub large_pages: bool,
73	pub jit_compiler: bool,
74	cache: Option<RxCache>,
75	dataset: Option<RxDataset>,
76	vms: Vec<Arc<RwLock<RxVM>>>,
77	trash: Trash,
78}
79
80#[derive(Debug, PartialEq)]
81pub struct RxVM {
82	pub vm: *mut randomx_vm,
83}
84
85impl Drop for RxVM {
86	fn drop(&mut self) {
87		unsafe {
88			randomx_destroy_vm(self.vm);
89		}
90	}
91}
92
93unsafe impl Sync for RxState {}
94unsafe impl Send for RxState {}
95
96impl RxState {
97	pub fn new() -> Self {
98		RxState {
99			seed: [0; 32],
100			hard_aes: false,
101			full_mem: false,
102			large_pages: false,
103			jit_compiler: false,
104			cache: None,
105			dataset: None,
106			vms: vec![],
107			trash: Trash::default(),
108		}
109	}
110
111	pub fn is_initialized(&self) -> bool {
112		self.cache.is_some()
113	}
114
115	pub fn get_flags(&self) -> randomx_flags {
116		let mut flags = randomx_flags_RANDOMX_FLAG_DEFAULT;
117
118		if self.jit_compiler {
119			flags |= randomx_flags_RANDOMX_FLAG_JIT;
120		}
121
122		if self.hard_aes {
123			flags |= randomx_flags_RANDOMX_FLAG_HARD_AES
124		}
125
126		if self.full_mem {
127			flags |= randomx_flags_RANDOMX_FLAG_FULL_MEM;
128		}
129
130		if self.large_pages {
131			flags |= randomx_flags_RANDOMX_FLAG_LARGE_PAGES;
132		}
133
134		flags
135	}
136
137	pub fn init_cache(&mut self, seed: &[u8]) -> Result<RxAction, &str> {
138		if self.cache.is_some() && self.is_same_seed(seed) {
139			return Ok(RxAction::NotChanged);
140		}
141
142		let flags = self.get_flags();
143		let cache_ptr = unsafe { randomx_alloc_cache(flags) };
144
145		if cache_ptr.is_null() {
146			return Err("cache not allocated");
147		}
148
149		unsafe {
150			randomx_init_cache(cache_ptr, seed.as_ptr() as *const c_void, seed.len());
151		}
152
153		self.trash.cache = self.cache.take();
154		self.cache = Some(RxCache { cache: cache_ptr });
155		self.seed.copy_from_slice(seed);
156
157		Ok(RxAction::Changed)
158	}
159
160	pub fn is_same_seed(&self, seed: &[u8]) -> bool {
161		&self.seed == seed
162	}
163
164	pub fn init_dataset(&mut self, threads_count: u8) -> Result<(), &str> {
165		let cache = self.cache.as_ref().ok_or("cache is not initialized")?;
166
167		//let mut dataset_ptr =
168		//	unsafe { randomx_alloc_dataset(randomx_flags_RANDOMX_FLAG_LARGE_PAGES) };
169		let dataset_ptr = unsafe { randomx_alloc_dataset(self.get_flags()) };
170
171		/*if dataset_ptr.is_null() {
172			dataset_ptr = unsafe { randomx_alloc_dataset(self.get_flags()) };
173		}*/
174
175		if dataset_ptr.is_null() {
176			return Err("it's not possible initialize a dataset");
177		}
178
179		let mut threads = Vec::new();
180		let mut start: u64 = 0;
181		let count: u64 = unsafe { randomx_dataset_item_count() } as u64;
182		let perth: u64 = count / threads_count as u64;
183		let remainder: u64 = count % threads_count as u64;
184
185		for i in 0..threads_count {
186			let cache = Wrapper(NonNull::new(cache.cache).unwrap());
187			let dataset = Wrapper(NonNull::new(dataset_ptr).unwrap());
188			let count = perth
189				+ if i == (threads_count - 1) {
190					remainder
191				} else {
192					0
193				};
194			threads.push(thread::spawn(move || {
195				let d = dataset.0.as_ptr();
196				let c = cache.0.as_ptr();
197				unsafe {
198					randomx_init_dataset(d, c, start.into(), count.into());
199				}
200			}));
201			start += count;
202		}
203
204		for th in threads {
205			th.join().map_err(|_| "failed to join threads")?;
206		}
207
208		self.trash.dataset = self.dataset.take();
209		self.dataset = Some(RxDataset {
210			dataset: dataset_ptr,
211		});
212
213		Ok(())
214	}
215
216	pub fn create_vm(&mut self) -> Result<Arc<RwLock<RxVM>>, &str> {
217		let cache = self.cache.as_ref().ok_or("cache is not initialized")?;
218
219		let dataset = self
220			.dataset
221			.as_ref()
222			.map(|d| d.dataset)
223			.unwrap_or(null_mut());
224
225		let flags = self.get_flags()
226			| if !dataset.is_null() {
227				randomx_flags_RANDOMX_FLAG_FULL_MEM
228			} else {
229				0
230			};
231
232		let mut vm = unsafe { randomx_create_vm(flags, cache.cache, dataset) };
233
234		if vm.is_null() {
235			vm = unsafe {
236				randomx_create_vm(randomx_flags_RANDOMX_FLAG_DEFAULT, cache.cache, dataset)
237			};
238		}
239
240		if !vm.is_null() {
241			self.vms.push(Arc::new(RwLock::new(RxVM { vm })));
242			Ok(self.vms.last().unwrap().clone())
243		} else {
244			Err("unable to create RxVM")
245		}
246	}
247
248	pub fn get_or_create_vm(&mut self) -> Result<Arc<RwLock<RxVM>>, &str> {
249		if self.vms.len() == 0 {
250			self.create_vm()
251		} else {
252			Ok(self.vms.last().unwrap().clone())
253		}
254	}
255
256	pub fn update_vms(&mut self) {
257		for vm in &self.vms {
258			let vm_lock = vm.write().unwrap();
259			unsafe {
260				self.cache
261					.as_ref()
262					.map(|x| randomx_vm_set_cache(vm_lock.vm, x.cache));
263				self.dataset
264					.as_ref()
265					.map(|x| randomx_vm_set_dataset(vm_lock.vm, x.dataset));
266			}
267		}
268
269		if self.cache.is_some() {
270			self.trash.empty();
271		}
272	}
273}