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
141unsafe impl Send for RandomXCacheInner {}
142unsafe impl Sync for RandomXCacheInner {}
143
144impl Drop for RandomXCacheInner {
145 fn drop(&mut self) {
147 unsafe {
148 randomx_release_cache(self.cache_ptr);
149 }
150 }
151}
152
153#[derive(Debug, Clone)]
154pub struct RandomXCache {
156 inner: Arc<RandomXCacheInner>,
157}
158
159impl RandomXCache {
160 pub fn new(flags: RandomXFlag, key: &[u8]) -> Result<RandomXCache, RandomXError> {
173 if key.is_empty() {
174 Err(RandomXError::ParameterError("key is empty".to_string()))
175 } else {
176 let cache_ptr = unsafe { randomx_alloc_cache(flags.bits) };
177 if cache_ptr.is_null() {
178 Err(RandomXError::CreationError("Could not allocate cache".to_string()))
179 } else {
180 let inner = RandomXCacheInner { cache_ptr };
181 let result = RandomXCache { inner: Arc::new(inner) };
182 let key_ptr = key.as_ptr() as *mut c_void;
183 let key_size = key.len();
184 unsafe {
185 randomx_init_cache(result.inner.cache_ptr, key_ptr, key_size);
186 }
187 Ok(result)
188 }
189 }
190 }
191}
192
193#[derive(Debug)]
194struct RandomXDatasetInner {
195 dataset_ptr: *mut randomx_dataset,
196 dataset_count: u32,
197 #[allow(dead_code)]
198 cache: RandomXCache,
199}
200
201unsafe impl Send for RandomXDatasetInner {}
202unsafe impl Sync for RandomXDatasetInner {}
203
204impl Drop for RandomXDatasetInner {
205 fn drop(&mut self) {
207 unsafe {
208 randomx_release_dataset(self.dataset_ptr);
209 }
210 }
211}
212
213#[derive(Debug, Clone)]
214pub struct RandomXDataset {
216 inner: Arc<RandomXDatasetInner>,
217}
218
219impl RandomXDataset {
220 #[allow(clippy::useless_conversion)]
231 pub fn new(flags: RandomXFlag, cache: RandomXCache, start: u32) -> Result<RandomXDataset, RandomXError> {
232 let item_count = RandomXDataset::count()
233 .map_err(|e| RandomXError::CreationError(format!("Could not get dataset count: {e:?}")))?;
234
235 let test = unsafe { randomx_alloc_dataset(flags.bits) };
236 if test.is_null() {
237 Err(RandomXError::CreationError("Could not allocate dataset".to_string()))
238 } else {
239 let inner = RandomXDatasetInner {
240 dataset_ptr: test,
241 dataset_count: item_count,
242 cache,
243 };
244 let result = RandomXDataset { inner: Arc::new(inner) };
245
246 if start < item_count {
247 unsafe {
248 randomx_init_dataset(
249 result.inner.dataset_ptr,
250 result.inner.cache.inner.cache_ptr,
251 c_ulong::from(start),
252 c_ulong::from(item_count),
253 );
254 }
255 Ok(result)
256 } else {
257 Err(RandomXError::CreationError(format!(
258 "start must be less than item_count: start: {start}, item_count: {item_count}",
259 )))
260 }
261 }
262 }
263
264 pub fn count() -> Result<u32, RandomXError> {
266 match unsafe { randomx_dataset_item_count() } {
267 0 => Err(RandomXError::Other("Dataset item count was 0".to_string())),
268 x => {
269 #[cfg(target_os = "windows")]
271 return Ok(x);
272 #[cfg(not(target_os = "windows"))]
273 return Ok(u32::try_from(x)?);
274 },
275 }
276 }
277
278 pub fn get_data(&self) -> Result<Vec<u8>, RandomXError> {
280 let memory = unsafe { randomx_get_dataset_memory(self.inner.dataset_ptr) };
281 if memory.is_null() {
282 Err(RandomXError::Other("Could not get dataset memory".into()))
283 } else {
284 let count = usize::try_from(self.inner.dataset_count)?;
285 let mut result: Vec<u8> = vec![0u8; count];
286 let n = usize::try_from(self.inner.dataset_count)?;
287 unsafe {
288 libc::memcpy(result.as_mut_ptr() as *mut c_void, memory, n);
289 }
290 Ok(result)
291 }
292 }
293}
294
295#[derive(Debug)]
296pub struct RandomXVM {
298 flags: RandomXFlag,
299 vm: *mut randomx_vm,
300 linked_cache: Option<RandomXCache>,
301 linked_dataset: Option<RandomXDataset>,
302}
303
304unsafe impl Send for RandomXVM {}
305
306impl Drop for RandomXVM {
307 fn drop(&mut self) {
309 unsafe {
310 randomx_destroy_vm(self.vm);
311 }
312 }
313}
314
315impl RandomXVM {
316 pub fn new(
333 flags: RandomXFlag,
334 cache: Option<RandomXCache>,
335 dataset: Option<RandomXDataset>,
336 ) -> Result<RandomXVM, RandomXError> {
337 let is_full_mem = flags.contains(RandomXFlag::FLAG_FULL_MEM);
338 match (cache, dataset) {
339 (None, None) => Err(RandomXError::CreationError("Failed to allocate VM".to_string())),
340 (None, _) if !is_full_mem => Err(RandomXError::FlagConfigError(
341 "No cache and FLAG_FULL_MEM not set".to_string(),
342 )),
343 (_, None) if is_full_mem => Err(RandomXError::FlagConfigError(
344 "No dataset and FLAG_FULL_MEM set".to_string(),
345 )),
346 (cache, dataset) => {
347 let cache_ptr = cache
348 .as_ref()
349 .map(|stash| stash.inner.cache_ptr)
350 .unwrap_or_else(ptr::null_mut);
351 let dataset_ptr = dataset
352 .as_ref()
353 .map(|data| data.inner.dataset_ptr)
354 .unwrap_or_else(ptr::null_mut);
355 let vm = unsafe { randomx_create_vm(flags.bits, cache_ptr, dataset_ptr) };
356 Ok(RandomXVM {
357 vm,
358 flags,
359 linked_cache: cache,
360 linked_dataset: dataset,
361 })
362 },
363 }
364 }
365
366 pub fn reinit_cache(&mut self, cache: RandomXCache) -> Result<(), RandomXError> {
369 if self.flags.contains(RandomXFlag::FLAG_FULL_MEM) {
370 Err(RandomXError::FlagConfigError(
371 "Cannot reinit cache with FLAG_FULL_MEM set".to_string(),
372 ))
373 } else {
374 unsafe {
375 randomx_vm_set_cache(self.vm, cache.inner.cache_ptr);
376 }
377 self.linked_cache = Some(cache);
378 Ok(())
379 }
380 }
381
382 pub fn reinit_dataset(&mut self, dataset: RandomXDataset) -> Result<(), RandomXError> {
385 if self.flags.contains(RandomXFlag::FLAG_FULL_MEM) {
386 unsafe {
387 randomx_vm_set_dataset(self.vm, dataset.inner.dataset_ptr);
388 }
389 self.linked_dataset = Some(dataset);
390 Ok(())
391 } else {
392 Err(RandomXError::FlagConfigError(
393 "Cannot reinit dataset without FLAG_FULL_MEM set".to_string(),
394 ))
395 }
396 }
397
398 pub fn calculate_hash(&self, input: &[u8]) -> Result<Vec<u8>, RandomXError> {
402 if input.is_empty() {
403 Err(RandomXError::ParameterError("input was empty".to_string()))
404 } else {
405 let size_input = input.len();
406 let input_ptr = input.as_ptr() as *mut c_void;
407 let arr = [0; RANDOMX_HASH_SIZE as usize];
408 let output_ptr = arr.as_ptr() as *mut c_void;
409 unsafe {
410 randomx_calculate_hash(self.vm, input_ptr, size_input, output_ptr);
411 }
412 if arr == [0; RANDOMX_HASH_SIZE as usize] {
414 Err(RandomXError::Other("RandomX calculated hash was empty".to_string()))
415 } else {
416 let result = arr.to_vec();
417 Ok(result)
418 }
419 }
420 }
421
422 #[allow(clippy::needless_range_loop)] pub fn calculate_hash_set(&self, input: &[&[u8]]) -> Result<Vec<Vec<u8>>, RandomXError> {
427 if input.is_empty() {
428 return Err(RandomXError::ParameterError("input was empty".to_string()));
430 }
431
432 let mut result = Vec::new();
433 if input.len() == 1 {
435 let hash = self.calculate_hash(input[0])?;
436 result.push(hash);
437 return Ok(result);
438 }
439
440 let mut output_ptr: *mut c_void = ptr::null_mut();
442 let arr = [0; RANDOMX_HASH_SIZE as usize];
443
444 let iterations = input.len() + 1;
446 for i in 0..iterations {
447 if i == iterations - 1 {
448 unsafe {
450 randomx_calculate_hash_last(self.vm, output_ptr);
451 }
452 } else {
453 if input[i].is_empty() {
454 if arr != [0; RANDOMX_HASH_SIZE as usize] {
456 unsafe {
458 randomx_calculate_hash_last(self.vm, output_ptr);
459 }
460 }
461 return Err(RandomXError::ParameterError("input was empty".to_string()));
462 };
463 let size_input = input[i].len();
464 let input_ptr = input[i].as_ptr() as *mut c_void;
465 output_ptr = arr.as_ptr() as *mut c_void;
466 if i == 0 {
467 unsafe {
469 randomx_calculate_hash_first(self.vm, input_ptr, size_input);
470 }
471 } else {
472 unsafe {
473 randomx_calculate_hash_next(self.vm, input_ptr, size_input, output_ptr);
475 }
476 }
477 }
478
479 if i != 0 {
480 if arr == [0; RANDOMX_HASH_SIZE as usize] {
482 return Err(RandomXError::Other("RandomX hash was zero".to_string()));
483 }
484 let output: Vec<u8> = arr.to_vec();
485 result.push(output);
486 }
487 }
488 Ok(result)
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use std::{ptr, sync::Arc};
495
496 use crate::{RandomXCache, RandomXCacheInner, RandomXDataset, RandomXDatasetInner, RandomXFlag, RandomXVM};
497
498 #[test]
499 fn lib_alloc_cache() {
500 let flags = RandomXFlag::default();
501 let key = "Key";
502 let cache = RandomXCache::new(flags, key.as_bytes()).expect("Failed to allocate cache");
503 drop(cache);
504 }
505
506 #[test]
507 fn lib_alloc_dataset() {
508 let flags = RandomXFlag::default();
509 let key = "Key";
510 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
511 let dataset = RandomXDataset::new(flags, cache.clone(), 0).expect("Failed to allocate dataset");
512 drop(dataset);
513 drop(cache);
514 }
515
516 #[test]
517 fn lib_alloc_vm() {
518 let flags = RandomXFlag::default();
519 let key = "Key";
520 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
521 let mut vm = RandomXVM::new(flags, Some(cache.clone()), None).expect("Failed to allocate VM");
522 drop(vm);
523 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
524 vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).expect("Failed to allocate VM");
525 drop(dataset);
526 drop(cache);
527 drop(vm);
528 }
529
530 #[test]
531 fn lib_dataset_memory() {
532 let flags = RandomXFlag::default();
533 let key = "Key";
534 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
535 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
536 let memory = dataset.get_data().unwrap_or_else(|_| std::vec::Vec::new());
537 assert!(!memory.is_empty(), "Failed to get dataset memory");
538 let vec = vec![0u8; memory.len()];
539 assert_ne!(memory, vec);
540 drop(dataset);
541 drop(cache);
542 }
543
544 #[test]
545 fn test_null_assignments() {
546 let flags = RandomXFlag::get_recommended_flags();
547 if let Ok(mut vm) = RandomXVM::new(flags, None, None) {
548 let cache = RandomXCache {
549 inner: Arc::new(RandomXCacheInner {
550 cache_ptr: ptr::null_mut(),
551 }),
552 };
553 assert!(vm.reinit_cache(cache.clone()).is_err());
554 let dataset = RandomXDataset {
555 inner: Arc::new(RandomXDatasetInner {
556 dataset_ptr: ptr::null_mut(),
557 dataset_count: 0,
558 cache,
559 }),
560 };
561 assert!(vm.reinit_dataset(dataset.clone()).is_err());
562 }
563 }
564
565 #[test]
566 fn lib_calculate_hash() {
567 let flags = RandomXFlag::get_recommended_flags();
568 let flags2 = flags | RandomXFlag::FLAG_FULL_MEM;
569 let key = "Key";
570 let input = "Input";
571 let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
572 let mut vm1 = RandomXVM::new(flags, Some(cache1.clone()), None).unwrap();
573 let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
574 let vec = vec![0u8; hash1.len()];
575 assert_ne!(hash1, vec);
576 let reinit_cache = vm1.reinit_cache(cache1.clone());
577 assert!(reinit_cache.is_ok());
578 let hash2 = vm1.calculate_hash(input.as_bytes()).expect("no data");
579 assert_ne!(hash2, vec);
580 assert_eq!(hash1, hash2);
581
582 let cache2 = RandomXCache::new(flags, key.as_bytes()).unwrap();
583 let vm2 = RandomXVM::new(flags, Some(cache2.clone()), None).unwrap();
584 let hash3 = vm2.calculate_hash(input.as_bytes()).expect("no data");
585 assert_eq!(hash2, hash3);
586
587 let cache3 = RandomXCache::new(flags, key.as_bytes()).unwrap();
588 let dataset3 = RandomXDataset::new(flags, cache3.clone(), 0).unwrap();
589 let mut vm3 = RandomXVM::new(flags2, None, Some(dataset3.clone())).unwrap();
590 let hash4 = vm3.calculate_hash(input.as_bytes()).expect("no data");
591 assert_ne!(hash3, vec);
592 let reinit_dataset = vm3.reinit_dataset(dataset3.clone());
593 assert!(reinit_dataset.is_ok());
594 let hash5 = vm3.calculate_hash(input.as_bytes()).expect("no data");
595 assert_ne!(hash4, vec);
596 assert_eq!(hash4, hash5);
597
598 let cache4 = RandomXCache::new(flags, key.as_bytes()).unwrap();
599 let dataset4 = RandomXDataset::new(flags, cache4.clone(), 0).unwrap();
600 let vm4 = RandomXVM::new(flags2, Some(cache4), Some(dataset4.clone())).unwrap();
601 let hash6 = vm3.calculate_hash(input.as_bytes()).expect("no data");
602 assert_eq!(hash5, hash6);
603
604 drop(dataset3);
605 drop(dataset4);
606 drop(cache1);
607 drop(cache2);
608 drop(cache3);
609 drop(vm1);
610 drop(vm2);
611 drop(vm3);
612 drop(vm4);
613 }
614
615 #[test]
616 fn lib_calculate_hash_set() {
617 let flags = RandomXFlag::default();
618 let key = "Key";
619 let inputs = vec!["Input".as_bytes(), "Input 2".as_bytes(), "Inputs 3".as_bytes()];
620 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
621 let vm = RandomXVM::new(flags, Some(cache.clone()), None).unwrap();
622 let hashes = vm.calculate_hash_set(inputs.as_slice()).expect("no data");
623 assert_eq!(inputs.len(), hashes.len());
624 let mut prev_hash = Vec::new();
625 for (i, hash) in hashes.into_iter().enumerate() {
626 let vec = vec![0u8; hash.len()];
627 assert_ne!(hash, vec);
628 assert_ne!(hash, prev_hash);
629 let compare = vm.calculate_hash(inputs[i]).unwrap(); assert_eq!(hash, compare);
631 prev_hash = hash;
632 }
633 drop(cache);
634 drop(vm);
635 }
636
637 #[test]
638 fn lib_calculate_hash_is_consistent() {
639 let flags = RandomXFlag::get_recommended_flags();
640 let key = "Key";
641 let input = "Input";
642 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
643 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
644 let vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).unwrap();
645 let hash = vm.calculate_hash(input.as_bytes()).expect("no data");
646 assert_eq!(hash, [
647 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
648 172, 253, 155, 204, 111, 183, 213, 157, 155
649 ]);
650 drop(vm);
651 drop(dataset);
652 drop(cache);
653
654 let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
655 let dataset1 = RandomXDataset::new(flags, cache1.clone(), 0).unwrap();
656 let vm1 = RandomXVM::new(flags, Some(cache1.clone()), Some(dataset1.clone())).unwrap();
657 let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
658 assert_eq!(hash1, [
659 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
660 172, 253, 155, 204, 111, 183, 213, 157, 155
661 ]);
662 drop(vm1);
663 drop(dataset1);
664 drop(cache1);
665 }
666
667 #[test]
668 fn lib_check_cache_and_dataset_lifetimes() {
669 let flags = RandomXFlag::get_recommended_flags();
670 let key = "Key";
671 let input = "Input";
672 let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
673 let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
674 let vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).unwrap();
675 drop(dataset);
676 drop(cache);
677 let hash = vm.calculate_hash(input.as_bytes()).expect("no data");
678 assert_eq!(hash, [
679 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
680 172, 253, 155, 204, 111, 183, 213, 157, 155
681 ]);
682 drop(vm);
683
684 let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
685 let dataset1 = RandomXDataset::new(flags, cache1.clone(), 0).unwrap();
686 let vm1 = RandomXVM::new(flags, Some(cache1.clone()), Some(dataset1.clone())).unwrap();
687 drop(dataset1);
688 drop(cache1);
689 let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
690 assert_eq!(hash1, [
691 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83, 215, 213, 59, 71, 32,
692 172, 253, 155, 204, 111, 183, 213, 157, 155
693 ]);
694 drop(vm1);
695 }
696
697 #[test]
698 fn randomx_hash_fast_vs_light() {
699 let input = b"input";
700 let key = b"key";
701
702 let flags = RandomXFlag::get_recommended_flags() | RandomXFlag::FLAG_FULL_MEM;
703 let cache = RandomXCache::new(flags, key).unwrap();
704 let dataset = RandomXDataset::new(flags, cache, 0).unwrap();
705 let fast_vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
706
707 let flags = RandomXFlag::get_recommended_flags();
708 let cache = RandomXCache::new(flags, key).unwrap();
709 let light_vm = RandomXVM::new(flags, Some(cache), None).unwrap();
710
711 let fast = fast_vm.calculate_hash(input).unwrap();
712 let light = light_vm.calculate_hash(input).unwrap();
713 assert_eq!(fast, light);
714 }
715
716 #[test]
717 fn test_vectors_fast_mode() {
718 let key = b"test key 000";
720 let vectors = [
721 (
722 b"This is a test".as_slice(),
723 "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f",
724 ),
725 (
726 b"Lorem ipsum dolor sit amet".as_slice(),
727 "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969",
728 ),
729 (
730 b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
731 "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8",
732 ),
733 ];
734
735 let flags = RandomXFlag::get_recommended_flags() | RandomXFlag::FLAG_FULL_MEM;
736 let cache = RandomXCache::new(flags, key).unwrap();
737 let dataset = RandomXDataset::new(flags, cache, 0).unwrap();
738 let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
739
740 for (input, expected) in vectors {
741 let hash = vm.calculate_hash(input).unwrap();
742 assert_eq!(hex::decode(expected).unwrap(), hash);
743 }
744 }
745
746 #[test]
747 fn test_vectors_light_mode() {
748 let vectors = [
750 (
751 b"test key 000",
752 b"This is a test".as_slice(),
753 "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f",
754 ),
755 (
756 b"test key 000",
757 b"Lorem ipsum dolor sit amet".as_slice(),
758 "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969",
759 ),
760 (
761 b"test key 000",
762 b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
763 "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8",
764 ),
765 (
766 b"test key 001",
767 b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
768 "e9ff4503201c0c2cca26d285c93ae883f9b1d30c9eb240b820756f2d5a7905fc",
769 ),
770 ];
771
772 let flags = RandomXFlag::get_recommended_flags();
773 for (key, input, expected) in vectors {
774 let cache = RandomXCache::new(flags, key).unwrap();
775 let vm = RandomXVM::new(flags, Some(cache), None).unwrap();
776 let hash = vm.calculate_hash(input).unwrap();
777 assert_eq!(hex::decode(expected).unwrap(), hash);
778 }
779 }
780}