1mod bindings;
38pub mod test_utils;
40
41use std::{convert::TryFrom, num::TryFromIntError, ptr, sync::Arc};
42
43use bindings::{
44 randomx_alloc_cache,
45 randomx_alloc_dataset,
46 randomx_cache,
47 randomx_calculate_hash,
48 randomx_create_vm,
49 randomx_dataset,
50 randomx_dataset_item_count,
51 randomx_destroy_vm,
52 randomx_get_dataset_memory,
53 randomx_init_cache,
54 randomx_init_dataset,
55 randomx_release_cache,
56 randomx_release_dataset,
57 randomx_vm,
58 randomx_vm_set_cache,
59 randomx_vm_set_dataset,
60 RANDOMX_HASH_SIZE,
61};
62use bitflags::bitflags;
63use libc::{c_ulong, c_void};
64use thiserror::Error;
65
66use crate::bindings::{
67 randomx_calculate_hash_first,
68 randomx_calculate_hash_last,
69 randomx_calculate_hash_next,
70 randomx_get_flags,
71};
72
73bitflags! {
74 pub struct RandomXFlag: u32 {
76 const FLAG_DEFAULT = 0b0000_0000;
78 const FLAG_LARGE_PAGES = 0b0000_0001;
80 const FLAG_HARD_AES = 0b0000_0010;
82 const FLAG_FULL_MEM = 0b0000_0100;
84 const FLAG_JIT = 0b0000_1000;
86 const FLAG_SECURE = 0b0001_0000;
89 const FLAG_ARGON2_SSSE3 = 0b0010_0000;
91 const FLAG_ARGON2_AVX2 = 0b0100_0000;
93 const FLAG_ARGON2 = 0b0110_0000;
95 }
96}
97
98impl RandomXFlag {
99 pub fn get_recommended_flags() -> RandomXFlag {
108 RandomXFlag {
109 bits: unsafe { randomx_get_flags() },
110 }
111 }
112}
113
114impl Default for RandomXFlag {
115 fn default() -> RandomXFlag {
117 RandomXFlag::FLAG_DEFAULT
118 }
119}
120
121#[derive(Debug, Clone, Error)]
122pub enum RandomXError {
124 #[error("Problem creating the RandomX object: {0}")]
125 CreationError(String),
126 #[error("Problem with configuration flags: {0}")]
127 FlagConfigError(String),
128 #[error("Problem with parameters supplied: {0}")]
129 ParameterError(String),
130 #[error("Failed to convert Int to usize")]
131 TryFromIntError(#[from] TryFromIntError),
132 #[error("Unknown problem running RandomX: {0}")]
133 Other(String),
134}
135
136#[derive(Debug)]
137struct RandomXCacheInner {
138 cache_ptr: *mut randomx_cache,
139}
140
141impl Drop for RandomXCacheInner {
142 fn drop(&mut self) {
144 unsafe {
145 randomx_release_cache(self.cache_ptr);
146 }
147 }
148}
149
150#[derive(Debug, Clone)]
151pub struct RandomXCache {
153 inner: Arc<RandomXCacheInner>,
154}
155
156impl RandomXCache {
157 pub fn new(flags: RandomXFlag, key: &[u8]) -> Result<RandomXCache, RandomXError> {
170 if key.is_empty() {
171 Err(RandomXError::ParameterError("key is empty".to_string()))
172 } else {
173 let cache_ptr = unsafe { randomx_alloc_cache(flags.bits) };
174 if cache_ptr.is_null() {
175 Err(RandomXError::CreationError("Could not allocate cache".to_string()))
176 } else {
177 let inner = RandomXCacheInner { cache_ptr };
178 let result = RandomXCache { inner: Arc::new(inner) };
179 let key_ptr = key.as_ptr() as *mut c_void;
180 let key_size = key.len();
181 unsafe {
182 randomx_init_cache(result.inner.cache_ptr, key_ptr, key_size);
183 }
184 Ok(result)
185 }
186 }
187 }
188}
189
190#[derive(Debug)]
191struct RandomXDatasetInner {
192 dataset_ptr: *mut randomx_dataset,
193 dataset_count: u32,
194 #[allow(dead_code)]
195 cache: RandomXCache,
196}
197
198impl Drop for RandomXDatasetInner {
199 fn drop(&mut self) {
201 unsafe {
202 randomx_release_dataset(self.dataset_ptr);
203 }
204 }
205}
206
207#[derive(Debug, Clone)]
208pub struct RandomXDataset {
210 inner: Arc<RandomXDatasetInner>,
211}
212
213impl RandomXDataset {
214 #[allow(clippy::useless_conversion)]
225 pub fn new(flags: RandomXFlag, cache: RandomXCache, start: u32) -> Result<RandomXDataset, RandomXError> {
226 let item_count = RandomXDataset::count()
227 .map_err(|e| RandomXError::CreationError(format!("Could not get dataset count: {e:?}")))?;
228
229 let test = unsafe { randomx_alloc_dataset(flags.bits) };
230 if test.is_null() {
231 Err(RandomXError::CreationError("Could not allocate dataset".to_string()))
232 } else {
233 let inner = RandomXDatasetInner {
234 dataset_ptr: test,
235 dataset_count: item_count,
236 cache,
237 };
238 let result = RandomXDataset { inner: Arc::new(inner) };
239
240 if start < item_count {
241 unsafe {
242 randomx_init_dataset(
243 result.inner.dataset_ptr,
244 result.inner.cache.inner.cache_ptr,
245 c_ulong::from(start),
246 c_ulong::from(item_count),
247 );
248 }
249 Ok(result)
250 } else {
251 Err(RandomXError::CreationError(format!(
252 "start must be less than item_count: start: {start}, item_count: {item_count}",
253 )))
254 }
255 }
256 }
257
258 pub fn count() -> Result<u32, RandomXError> {
260 match unsafe { randomx_dataset_item_count() } {
261 0 => Err(RandomXError::Other("Dataset item count was 0".to_string())),
262 x => {
263 #[cfg(target_os = "windows")]
265 return Ok(x);
266 #[cfg(not(target_os = "windows"))]
267 return Ok(u32::try_from(x)?);
268 },
269 }
270 }
271
272 pub fn get_data(&self) -> Result<Vec<u8>, RandomXError> {
274 let memory = unsafe { randomx_get_dataset_memory(self.inner.dataset_ptr) };
275 if memory.is_null() {
276 Err(RandomXError::Other("Could not get dataset memory".into()))
277 } else {
278 let count = usize::try_from(self.inner.dataset_count)?;
279 let mut result: Vec<u8> = vec![0u8; count];
280 let n = usize::try_from(self.inner.dataset_count)?;
281 unsafe {
282 libc::memcpy(result.as_mut_ptr() as *mut c_void, memory, n);
283 }
284 Ok(result)
285 }
286 }
287}
288
289#[derive(Debug)]
290pub struct RandomXVM {
292 flags: RandomXFlag,
293 vm: *mut randomx_vm,
294 linked_cache: Option<RandomXCache>,
295 linked_dataset: Option<RandomXDataset>,
296}
297
298impl Drop for RandomXVM {
299 fn drop(&mut self) {
301 unsafe {
302 randomx_destroy_vm(self.vm);
303 }
304 }
305}
306
307impl RandomXVM {
308 pub fn new(
325 flags: RandomXFlag,
326 cache: Option<RandomXCache>,
327 dataset: Option<RandomXDataset>,
328 ) -> Result<RandomXVM, RandomXError> {
329 let is_full_mem = flags.contains(RandomXFlag::FLAG_FULL_MEM);
330 match (cache, dataset) {
331 (None, None) => Err(RandomXError::CreationError("Failed to allocate VM".to_string())),
332 (None, _) if !is_full_mem => Err(RandomXError::FlagConfigError(
333 "No cache and FLAG_FULL_MEM not set".to_string(),
334 )),
335 (_, None) if is_full_mem => Err(RandomXError::FlagConfigError(
336 "No dataset and FLAG_FULL_MEM set".to_string(),
337 )),
338 (cache, dataset) => {
339 let cache_ptr = cache
340 .as_ref()
341 .map(|stash| stash.inner.cache_ptr)
342 .unwrap_or_else(ptr::null_mut);
343 let dataset_ptr = dataset
344 .as_ref()
345 .map(|data| data.inner.dataset_ptr)
346 .unwrap_or_else(ptr::null_mut);
347 let vm = unsafe { randomx_create_vm(flags.bits, cache_ptr, dataset_ptr) };
348 Ok(RandomXVM {
349 vm,
350 flags,
351 linked_cache: cache,
352 linked_dataset: dataset,
353 })
354 },
355 }
356 }
357
358 pub fn reinit_cache(&mut self, cache: RandomXCache) -> Result<(), RandomXError> {
361 if self.flags.contains(RandomXFlag::FLAG_FULL_MEM) {
362 Err(RandomXError::FlagConfigError(
363 "Cannot reinit cache with FLAG_FULL_MEM set".to_string(),
364 ))
365 } else {
366 unsafe {
367 randomx_vm_set_cache(self.vm, cache.inner.cache_ptr);
368 }
369 self.linked_cache = Some(cache);
370 Ok(())
371 }
372 }
373
374 pub fn reinit_dataset(&mut self, dataset: RandomXDataset) -> Result<(), RandomXError> {
377 if self.flags.contains(RandomXFlag::FLAG_FULL_MEM) {
378 unsafe {
379 randomx_vm_set_dataset(self.vm, dataset.inner.dataset_ptr);
380 }
381 self.linked_dataset = Some(dataset);
382 Ok(())
383 } else {
384 Err(RandomXError::FlagConfigError(
385 "Cannot reinit dataset without FLAG_FULL_MEM set".to_string(),
386 ))
387 }
388 }
389
390 pub fn calculate_hash(&self, input: &[u8]) -> Result<Vec<u8>, RandomXError> {
394 if input.is_empty() {
395 Err(RandomXError::ParameterError("input was empty".to_string()))
396 } else {
397 let size_input = input.len();
398 let input_ptr = input.as_ptr() as *mut c_void;
399 let arr = [0; RANDOMX_HASH_SIZE as usize];
400 let output_ptr = arr.as_ptr() as *mut c_void;
401 unsafe {
402 randomx_calculate_hash(self.vm, input_ptr, size_input, output_ptr);
403 }
404 if arr == [0; RANDOMX_HASH_SIZE as usize] {
406 Err(RandomXError::Other("RandomX calculated hash was empty".to_string()))
407 } else {
408 let result = arr.to_vec();
409 Ok(result)
410 }
411 }
412 }
413
414 #[allow(clippy::needless_range_loop)] pub fn calculate_hash_set(&self, input: &[&[u8]]) -> Result<Vec<Vec<u8>>, RandomXError> {
419 if input.is_empty() {
420 return Err(RandomXError::ParameterError("input was empty".to_string()));
422 }
423
424 let mut result = Vec::new();
425 if input.len() == 1 {
427 let hash = self.calculate_hash(input[0])?;
428 result.push(hash);
429 return Ok(result);
430 }
431
432 let mut output_ptr: *mut c_void = ptr::null_mut();
434 let arr = [0; RANDOMX_HASH_SIZE as usize];
435
436 let iterations = input.len() + 1;
438 for i in 0..iterations {
439 if i == iterations - 1 {
440 unsafe {
442 randomx_calculate_hash_last(self.vm, output_ptr);
443 }
444 } else {
445 if input[i].is_empty() {
446 if arr != [0; RANDOMX_HASH_SIZE as usize] {
448 unsafe {
450 randomx_calculate_hash_last(self.vm, output_ptr);
451 }
452 }
453 return Err(RandomXError::ParameterError("input was empty".to_string()));
454 };
455 let size_input = input[i].len();
456 let input_ptr = input[i].as_ptr() as *mut c_void;
457 output_ptr = arr.as_ptr() as *mut c_void;
458 if i == 0 {
459 unsafe {
461 randomx_calculate_hash_first(self.vm, input_ptr, size_input);
462 }
463 } else {
464 unsafe {
465 randomx_calculate_hash_next(self.vm, input_ptr, size_input, output_ptr);
467 }
468 }
469 }
470
471 if i != 0 {
472 if arr == [0; RANDOMX_HASH_SIZE as usize] {
474 return Err(RandomXError::Other("RandomX hash was zero".to_string()));
475 }
476 let output: Vec<u8> = arr.to_vec();
477 result.push(output);
478 }
479 }
480 Ok(result)
481 }
482}
483
484#[cfg(test)]
485mod tests {
486 use std::{ptr, sync::Arc};
487
488 use crate::{RandomXCache, RandomXCacheInner, RandomXDataset, RandomXDatasetInner, RandomXFlag, RandomXVM};
489
490 #[test]
491 fn lib_alloc_cache() {
492 let flags = RandomXFlag::default();
493 let key = "Key";
494 let cache = RandomXCache::new(flags, key.as_bytes()).expect("Failed to allocate cache");
495 drop(cache);
496 }
497
498 #[test]
499 fn lib_alloc_dataset() {
500 let flags = RandomXFlag::default();
501 let key = "Key";
502 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
503 let dataset = RandomXDataset::new(flags, cache.clone(), 0).expect("Failed to allocate dataset");
504 drop(dataset);
505 drop(cache);
506 }
507
508 #[test]
509 fn lib_alloc_vm() {
510 let flags = RandomXFlag::default();
511 let key = "Key";
512 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
513 let mut vm = RandomXVM::new(flags, Some(cache.clone()), None).expect("Failed to allocate VM");
514 drop(vm);
515 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
516 vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).expect("Failed to allocate VM");
517 drop(dataset);
518 drop(cache);
519 drop(vm);
520 }
521
522 #[test]
523 fn lib_dataset_memory() {
524 let flags = RandomXFlag::default();
525 let key = "Key";
526 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
527 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
528 let memory = dataset.get_data().unwrap_or_else(|_| std::vec::Vec::new());
529 assert!(!memory.is_empty(), "Failed to get dataset memory");
530 let vec = vec![0u8; memory.len()];
531 assert_ne!(memory, vec);
532 drop(dataset);
533 drop(cache);
534 }
535
536 #[test]
537 fn test_null_assignments() {
538 let flags = RandomXFlag::get_recommended_flags();
539 if let Ok(mut vm) = RandomXVM::new(flags, None, None) {
540 let cache = RandomXCache {
541 inner: Arc::new(RandomXCacheInner {
542 cache_ptr: ptr::null_mut(),
543 }),
544 };
545 assert!(vm.reinit_cache(cache.clone()).is_err());
546 let dataset = RandomXDataset {
547 inner: Arc::new(RandomXDatasetInner {
548 dataset_ptr: ptr::null_mut(),
549 dataset_count: 0,
550 cache,
551 }),
552 };
553 assert!(vm.reinit_dataset(dataset.clone()).is_err());
554 }
555 }
556
557 #[test]
558 fn lib_calculate_hash() {
559 let flags = RandomXFlag::get_recommended_flags();
560 let flags2 = flags | RandomXFlag::FLAG_FULL_MEM;
561 let key = "Key";
562 let input = "Input";
563 let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
564 let mut vm1 = RandomXVM::new(flags, Some(cache1.clone()), None).unwrap();
565 let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
566 let vec = vec![0u8; hash1.len()];
567 assert_ne!(hash1, vec);
568 let reinit_cache = vm1.reinit_cache(cache1.clone());
569 assert!(reinit_cache.is_ok());
570 let hash2 = vm1.calculate_hash(input.as_bytes()).expect("no data");
571 assert_ne!(hash2, vec);
572 assert_eq!(hash1, hash2);
573
574 let cache2 = RandomXCache::new(flags, key.as_bytes()).unwrap();
575 let vm2 = RandomXVM::new(flags, Some(cache2.clone()), None).unwrap();
576 let hash3 = vm2.calculate_hash(input.as_bytes()).expect("no data");
577 assert_eq!(hash2, hash3);
578
579 let cache3 = RandomXCache::new(flags, key.as_bytes()).unwrap();
580 let dataset3 = RandomXDataset::new(flags, cache3.clone(), 0).unwrap();
581 let mut vm3 = RandomXVM::new(flags2, None, Some(dataset3.clone())).unwrap();
582 let hash4 = vm3.calculate_hash(input.as_bytes()).expect("no data");
583 assert_ne!(hash3, vec);
584 let reinit_dataset = vm3.reinit_dataset(dataset3.clone());
585 assert!(reinit_dataset.is_ok());
586 let hash5 = vm3.calculate_hash(input.as_bytes()).expect("no data");
587 assert_ne!(hash4, vec);
588 assert_eq!(hash4, hash5);
589
590 let cache4 = RandomXCache::new(flags, key.as_bytes()).unwrap();
591 let dataset4 = RandomXDataset::new(flags, cache4.clone(), 0).unwrap();
592 let vm4 = RandomXVM::new(flags2, Some(cache4), Some(dataset4.clone())).unwrap();
593 let hash6 = vm3.calculate_hash(input.as_bytes()).expect("no data");
594 assert_eq!(hash5, hash6);
595
596 drop(dataset3);
597 drop(dataset4);
598 drop(cache1);
599 drop(cache2);
600 drop(cache3);
601 drop(vm1);
602 drop(vm2);
603 drop(vm3);
604 drop(vm4);
605 }
606
607 #[test]
608 fn lib_calculate_hash_set() {
609 let flags = RandomXFlag::default();
610 let key = "Key";
611 let inputs = vec!["Input".as_bytes(), "Input 2".as_bytes(), "Inputs 3".as_bytes()];
612 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
613 let vm = RandomXVM::new(flags, Some(cache.clone()), None).unwrap();
614 let hashes = vm.calculate_hash_set(inputs.as_slice()).expect("no data");
615 assert_eq!(inputs.len(), hashes.len());
616 let mut prev_hash = Vec::new();
617 for (i, hash) in hashes.into_iter().enumerate() {
618 let vec = vec![0u8; hash.len()];
619 assert_ne!(hash, vec);
620 assert_ne!(hash, prev_hash);
621 let compare = vm.calculate_hash(inputs[i]).unwrap(); assert_eq!(hash, compare);
623 prev_hash = hash;
624 }
625 drop(cache);
626 drop(vm);
627 }
628
629 #[test]
630 fn lib_calculate_hash_is_consistent() {
631 let flags = RandomXFlag::get_recommended_flags();
632 let key = "Key";
633 let input = "Input";
634 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
635 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
636 let vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).unwrap();
637 let hash = vm.calculate_hash(input.as_bytes()).expect("no data");
638 assert_eq!(hash, [
639 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
640 172, 253, 155, 204, 111, 183, 213, 157, 155
641 ]);
642 drop(vm);
643 drop(dataset);
644 drop(cache);
645
646 let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
647 let dataset1 = RandomXDataset::new(flags, cache1.clone(), 0).unwrap();
648 let vm1 = RandomXVM::new(flags, Some(cache1.clone()), Some(dataset1.clone())).unwrap();
649 let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
650 assert_eq!(hash1, [
651 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
652 172, 253, 155, 204, 111, 183, 213, 157, 155
653 ]);
654 drop(vm1);
655 drop(dataset1);
656 drop(cache1);
657 }
658
659 #[test]
660 fn lib_check_cache_and_dataset_lifetimes() {
661 let flags = RandomXFlag::get_recommended_flags();
662 let key = "Key";
663 let input = "Input";
664 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
665 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
666 let vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).unwrap();
667 drop(dataset);
668 drop(cache);
669 let hash = vm.calculate_hash(input.as_bytes()).expect("no data");
670 assert_eq!(hash, [
671 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
672 172, 253, 155, 204, 111, 183, 213, 157, 155
673 ]);
674 drop(vm);
675
676 let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
677 let dataset1 = RandomXDataset::new(flags, cache1.clone(), 0).unwrap();
678 let vm1 = RandomXVM::new(flags, Some(cache1.clone()), Some(dataset1.clone())).unwrap();
679 drop(dataset1);
680 drop(cache1);
681 let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
682 assert_eq!(hash1, [
683 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
684 172, 253, 155, 204, 111, 183, 213, 157, 155
685 ]);
686 drop(vm1);
687 }
688
689 #[test]
690 fn randomx_hash_fast_vs_light() {
691 let input = b"input";
692 let key = b"key";
693
694 let flags = RandomXFlag::get_recommended_flags() | RandomXFlag::FLAG_FULL_MEM;
695 let cache = RandomXCache::new(flags, key).unwrap();
696 let dataset = RandomXDataset::new(flags, cache, 0).unwrap();
697 let fast_vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
698
699 let flags = RandomXFlag::get_recommended_flags();
700 let cache = RandomXCache::new(flags, key).unwrap();
701 let light_vm = RandomXVM::new(flags, Some(cache), None).unwrap();
702
703 let fast = fast_vm.calculate_hash(input).unwrap();
704 let light = light_vm.calculate_hash(input).unwrap();
705 assert_eq!(fast, light);
706 }
707
708 #[test]
709 fn test_vectors_fast_mode() {
710 let key = b"test key 000";
712 let vectors = [
713 (
714 b"This is a test".as_slice(),
715 "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f",
716 ),
717 (
718 b"Lorem ipsum dolor sit amet".as_slice(),
719 "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969",
720 ),
721 (
722 b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
723 "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8",
724 ),
725 ];
726
727 let flags = RandomXFlag::get_recommended_flags() | RandomXFlag::FLAG_FULL_MEM;
728 let cache = RandomXCache::new(flags, key).unwrap();
729 let dataset = RandomXDataset::new(flags, cache, 0).unwrap();
730 let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
731
732 for (input, expected) in vectors {
733 let hash = vm.calculate_hash(input).unwrap();
734 assert_eq!(hex::decode(expected).unwrap(), hash);
735 }
736 }
737
738 #[test]
739 fn test_vectors_light_mode() {
740 let vectors = [
742 (
743 b"test key 000",
744 b"This is a test".as_slice(),
745 "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f",
746 ),
747 (
748 b"test key 000",
749 b"Lorem ipsum dolor sit amet".as_slice(),
750 "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969",
751 ),
752 (
753 b"test key 000",
754 b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
755 "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8",
756 ),
757 (
758 b"test key 001",
759 b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
760 "e9ff4503201c0c2cca26d285c93ae883f9b1d30c9eb240b820756f2d5a7905fc",
761 ),
762 ];
763
764 let flags = RandomXFlag::get_recommended_flags();
765 for (key, input, expected) in vectors {
766 let cache = RandomXCache::new(flags, key).unwrap();
767 let vm = RandomXVM::new(flags, Some(cache), None).unwrap();
768 let hash = vm.calculate_hash(input).unwrap();
769 assert_eq!(hex::decode(expected).unwrap(), hash);
770 }
771 }
772}