|
|
@@ -2,42 +2,16 @@
|
|
|
#![allow(non_camel_case_types)]
|
|
|
#![allow(non_snake_case)]
|
|
|
|
|
|
-//! (Most of the code is taken from https://github.com/moneromint/randomx4r)
|
|
|
-//! Rust bindings to librandomx, a library for computing RandomX hashes.
|
|
|
+//! Rust bindings to randomx, a library for computing RandomX hashes.
|
|
|
//!
|
|
|
-//! # Examples
|
|
|
+//! "RandomX is a proof-of-work (PoW) algorithm that is optimized for general-purpose CPUs. RandomX uses random code
|
|
|
+//! execution together with several memory-hard techniques to minimize the efficiency advantage of specialized
|
|
|
+//! hardware."
|
|
|
//!
|
|
|
-//! ## Light mode hash
|
|
|
+//! Read more about how RandomX works in the [design document].
|
|
|
//!
|
|
|
-//! Requires 256M of shared memory.
|
|
|
-//!
|
|
|
-//! ```no_run
|
|
|
-//! use randomx::{RandomXCache, RandomXError, RandomXFlags, RandomXVM};
|
|
|
-//!
|
|
|
-//! // Get flags supported by this system.
|
|
|
-//! let flags = RandomXFlags::default();
|
|
|
-//! let cache = RandomXCache::new(flags, b"key")?;
|
|
|
-//! let vm = RandomXVM::new(flags, &cache)?;
|
|
|
-//! let hash = vm.hash(b"input"); // is a [u8; 32]
|
|
|
-//! # Ok::<(), RandomXError>(())
|
|
|
-//! ```
|
|
|
-//!
|
|
|
-//! ## Fast mode hash
|
|
|
-//!
|
|
|
-//! Requires 2080M of shared memory.
|
|
|
-//!
|
|
|
-//! ```no_run
|
|
|
-//! use randomx::{RandomXDataset, RandomXError, RandomXFlags, RandomXVM};
|
|
|
-//!
|
|
|
-//! // OR the default flags with FULLMEM (aka. fast mode)
|
|
|
-//! let flags = RandomXFlags::default() | RandomXFlags::FULLMEM;
|
|
|
-//! // Speed up dataset initialisation
|
|
|
-//! let threads = std::thread::available_parallelism().unwrap().get();
|
|
|
-//! let dataset = RandomXDataset::new(flags, b"key", threads)?;
|
|
|
-//! let vm = RandomXVM::new_fast(flags, &dataset)?;
|
|
|
-//! let hash = vm.hash(b"input");
|
|
|
-//! # Ok::<(), RandomXError>(())
|
|
|
-//! ```
|
|
|
+//! [RandomX github repo]: <https://github.com/tevador/RandomX>
|
|
|
+//! [design document]: <https://github.com/tevador/RandomX/blob/master/doc/design.md>
|
|
|
//!
|
|
|
//! # Errors
|
|
|
//!
|
|
|
@@ -48,13 +22,13 @@
|
|
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
|
|
|
-use std::marker::PhantomData;
|
|
|
+use std::ptr;
|
|
|
use std::sync::Arc;
|
|
|
-use std::thread;
|
|
|
|
|
|
use bitflags::bitflags;
|
|
|
+use libc::{c_ulong, c_void, memcpy};
|
|
|
|
|
|
-#[derive(Debug, Copy, Clone)]
|
|
|
+#[derive(Debug, Clone)]
|
|
|
pub enum RandomXError {
|
|
|
/// Occurs when allocating the RandomX cache fails.
|
|
|
///
|
|
|
@@ -64,27 +38,50 @@ pub enum RandomXError {
|
|
|
/// * An invalid or unsupported ARGON2 value is set
|
|
|
CacheAllocError,
|
|
|
|
|
|
+ /// Occurs when RandomX cache being reinitialized fails.
|
|
|
+ ///
|
|
|
+ /// Reasons include:
|
|
|
+ /// * VM is initialized with FULLMEM flag set.
|
|
|
+ CacheReinitError,
|
|
|
+
|
|
|
/// Occurs when allocating a RandomX dataset fails.
|
|
|
///
|
|
|
/// Reasons include:
|
|
|
/// * Memory allocation fails
|
|
|
DatasetAllocError,
|
|
|
|
|
|
+ /// Occurs when RandomX dataset being reinitialized fails.
|
|
|
+ ///
|
|
|
+ /// Reasons include:
|
|
|
+ /// * VM is initialized without FULLMEM flag set.
|
|
|
+ DatasetReinitError,
|
|
|
+
|
|
|
/// Occurs when creating a VM fails.
|
|
|
///
|
|
|
/// Reasons included:
|
|
|
/// * Scratchpad memory allocation fails
|
|
|
/// * Unsupported flags
|
|
|
VmAllocError,
|
|
|
+
|
|
|
+ /// Various parameter errors; self-explanatory
|
|
|
+ ParameterError(String),
|
|
|
+
|
|
|
+ /// Other errors
|
|
|
+ Other(String),
|
|
|
}
|
|
|
|
|
|
impl std::error::Error for RandomXError {}
|
|
|
+
|
|
|
impl std::fmt::Display for RandomXError {
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
match self {
|
|
|
Self::CacheAllocError => write!(f, "Failed to allocate RandomX cache"),
|
|
|
+ Self::CacheReinitError => write!(f, "Can't reinit RandomX cache w/ FULLMEM set"),
|
|
|
Self::DatasetAllocError => write!(f, "Failed to allocate RandomX dataset"),
|
|
|
+ Self::DatasetReinitError => write!(f, "Can't reinit RandomX dataset w/o FULLMEM set"),
|
|
|
Self::VmAllocError => write!(f, "Failed to allocate RandomX VM"),
|
|
|
+ Self::ParameterError(e) => write!(f, "{}", e),
|
|
|
+ Self::Other(e) => write!(f, "{}", e),
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -92,7 +89,7 @@ impl std::fmt::Display for RandomXError {
|
|
|
bitflags! {
|
|
|
/// Represents options that can be used when allocating the
|
|
|
/// RandomX dataset or VM.
|
|
|
- #[derive(Copy, Clone)]
|
|
|
+ #[derive(Debug, Copy, Clone)]
|
|
|
pub struct RandomXFlags: u32 {
|
|
|
/// Use defaults.
|
|
|
const DEFAULT = randomx_flags_RANDOMX_FLAG_DEFAULT;
|
|
|
@@ -124,232 +121,702 @@ bitflags! {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Default for RandomXFlags {
|
|
|
- /// Get the recommended flags to use on the current machine.
|
|
|
+impl RandomXFlags {
|
|
|
+ /// Returns the recommended flags to be used.
|
|
|
///
|
|
|
- /// Does not include any of the following flags:
|
|
|
+ /// Does not include:
|
|
|
/// * LARGEPAGES
|
|
|
- /// * JIT
|
|
|
+ /// * FULLMEM
|
|
|
/// * SECURE
|
|
|
- fn default() -> Self {
|
|
|
- // Explode if bits do not match up
|
|
|
+ ///
|
|
|
+ /// The above flags need to be set manually, if required.
|
|
|
+ pub fn get_recommended_flags() -> Self {
|
|
|
unsafe { Self::from_bits(randomx_get_flags()).unwrap() }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// Dataset cache for light-mode hashing.
|
|
|
+impl Default for RandomXFlags {
|
|
|
+ /// Default value for RandomXFlags
|
|
|
+ fn default() -> RandomXFlags {
|
|
|
+ RandomXFlags::DEFAULT
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+struct RandomXCacheInner {
|
|
|
+ cache_ptr: *mut randomx_cache,
|
|
|
+}
|
|
|
+
|
|
|
+unsafe impl Send for RandomXCacheInner {}
|
|
|
+unsafe impl Sync for RandomXCacheInner {}
|
|
|
+
|
|
|
+impl Drop for RandomXCacheInner {
|
|
|
+ /// Deallocates memory for the `cache` object
|
|
|
+ fn drop(&mut self) {
|
|
|
+ unsafe {
|
|
|
+ randomx_release_cache(self.cache_ptr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug, Clone)]
|
|
|
+/// The Cache is used for light verification and Dataset construction.
|
|
|
pub struct RandomXCache {
|
|
|
- pub(crate) cache: *mut randomx_cache,
|
|
|
+ inner: Arc<RandomXCacheInner>,
|
|
|
}
|
|
|
|
|
|
impl RandomXCache {
|
|
|
- pub fn new(flags: RandomXFlags, key: &[u8]) -> Result<Self, RandomXError> {
|
|
|
- let cache = unsafe { randomx_alloc_cache(flags.bits()) };
|
|
|
+ /// Creates and allocates memory for a new cache object, and initializes
|
|
|
+ /// it with the key value.
|
|
|
+ ///
|
|
|
+ /// `flags` is any combination of the following two flags:
|
|
|
+ /// * LARGEPAGES
|
|
|
+ /// * JIT
|
|
|
+ ///
|
|
|
+ /// and (optionally) one of the following flags (depending on instruction set)
|
|
|
+ /// * ARGON2_SSSE3
|
|
|
+ /// * ARGON2_AVX2
|
|
|
+ ///
|
|
|
+ /// `key` is a sequence of u8 used to initialize SuperScalarHash.
|
|
|
+ pub fn new(flags: RandomXFlags, key: &[u8]) -> Result<RandomXCache, RandomXError> {
|
|
|
+ if key.is_empty() {
|
|
|
+ return Err(RandomXError::ParameterError(
|
|
|
+ "RandomX cache key is empty".to_string(),
|
|
|
+ ));
|
|
|
+ }
|
|
|
|
|
|
- if cache.is_null() {
|
|
|
+ let cache_ptr = unsafe { randomx_alloc_cache(flags.bits()) };
|
|
|
+ if cache_ptr.is_null() {
|
|
|
return Err(RandomXError::CacheAllocError);
|
|
|
}
|
|
|
|
|
|
+ let inner = RandomXCacheInner { cache_ptr };
|
|
|
+ let result = RandomXCache {
|
|
|
+ inner: Arc::new(inner),
|
|
|
+ };
|
|
|
+ let key_ptr = key.as_ptr() as *mut c_void;
|
|
|
+ let key_size = key.len();
|
|
|
+
|
|
|
unsafe {
|
|
|
- randomx_init_cache(cache, key.as_ptr() as *const std::ffi::c_void, key.len());
|
|
|
+ randomx_init_cache(result.inner.cache_ptr, key_ptr, key_size);
|
|
|
}
|
|
|
|
|
|
- Ok(RandomXCache { cache })
|
|
|
+ Ok(result)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Drop for RandomXCache {
|
|
|
+#[derive(Debug)]
|
|
|
+struct RandomXDatasetInner {
|
|
|
+ dataset_ptr: *mut randomx_dataset,
|
|
|
+ dataset_count: u32,
|
|
|
+ #[allow(dead_code)]
|
|
|
+ cache: RandomXCache,
|
|
|
+}
|
|
|
+
|
|
|
+unsafe impl Send for RandomXDatasetInner {}
|
|
|
+unsafe impl Sync for RandomXDatasetInner {}
|
|
|
+
|
|
|
+impl Drop for RandomXDatasetInner {
|
|
|
+ /// Deallocates memory for the `dataset` object.
|
|
|
fn drop(&mut self) {
|
|
|
- unsafe { randomx_release_cache(self.cache) }
|
|
|
+ unsafe {
|
|
|
+ randomx_release_dataset(self.dataset_ptr);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-unsafe impl Send for RandomXCache {}
|
|
|
-unsafe impl Sync for RandomXCache {}
|
|
|
-
|
|
|
+#[derive(Debug, Clone)]
|
|
|
+/// The Dataset is a read-only memory structure that is used during
|
|
|
+/// VM program execution.
|
|
|
pub struct RandomXDataset {
|
|
|
- pub(crate) dataset: *mut randomx_dataset,
|
|
|
+ inner: Arc<RandomXDatasetInner>,
|
|
|
}
|
|
|
|
|
|
impl RandomXDataset {
|
|
|
- pub fn new(flags: RandomXFlags, key: &[u8], n_threads: usize) -> Result<Self, RandomXError> {
|
|
|
- assert!(n_threads > 0);
|
|
|
+ /// Creates a new dataset object, allocates memory to the `dataset` object
|
|
|
+ /// and initializes it.
|
|
|
+ ///
|
|
|
+ /// `flags` is one of the following:
|
|
|
+ /// * DEFAULT
|
|
|
+ /// * LARGEPAGES
|
|
|
+ ///
|
|
|
+ /// `cache` is a cache object.
|
|
|
+ ///
|
|
|
+ /// `start` is the item number where initialization should start,
|
|
|
+ /// recommended to pass in 0.
|
|
|
+ ///
|
|
|
+ /// Conversions may be lossy on Windows or Linux.
|
|
|
+ #[allow(clippy::useless_conversion)]
|
|
|
+ pub fn new(
|
|
|
+ flags: RandomXFlags,
|
|
|
+ cache: RandomXCache,
|
|
|
+ start: u32,
|
|
|
+ ) -> Result<RandomXDataset, RandomXError> {
|
|
|
+ let item_count = RandomXDataset::count()?;
|
|
|
|
|
|
- let cache = RandomXCache::new(flags, key)?;
|
|
|
- let dataset = unsafe { randomx_alloc_dataset(flags.bits()) };
|
|
|
+ let test = unsafe { randomx_alloc_dataset(flags.bits()) };
|
|
|
+ if test.is_null() {
|
|
|
+ return Err(RandomXError::DatasetAllocError);
|
|
|
+ }
|
|
|
+
|
|
|
+ let inner = RandomXDatasetInner {
|
|
|
+ dataset_ptr: test,
|
|
|
+ dataset_count: item_count,
|
|
|
+ cache,
|
|
|
+ };
|
|
|
|
|
|
- if dataset.is_null() {
|
|
|
+ let result = RandomXDataset {
|
|
|
+ inner: Arc::new(inner),
|
|
|
+ };
|
|
|
+
|
|
|
+ if start >= item_count {
|
|
|
return Err(RandomXError::DatasetAllocError);
|
|
|
}
|
|
|
|
|
|
- let mut dataset = RandomXDataset { dataset };
|
|
|
+ unsafe {
|
|
|
+ randomx_init_dataset(
|
|
|
+ result.inner.dataset_ptr,
|
|
|
+ result.inner.cache.inner.cache_ptr,
|
|
|
+ c_ulong::from(start),
|
|
|
+ c_ulong::from(item_count),
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- let count = unsafe { randomx_dataset_item_count() };
|
|
|
+ Ok(result)
|
|
|
+ }
|
|
|
|
|
|
- if n_threads == 1 {
|
|
|
- unsafe {
|
|
|
- randomx_init_dataset(dataset.dataset, cache.cache, 0, count);
|
|
|
+ /// Returns the number of items in the `dataset` or an error on failure.
|
|
|
+ pub fn count() -> Result<u32, RandomXError> {
|
|
|
+ match unsafe { randomx_dataset_item_count() } {
|
|
|
+ 0 => Err(RandomXError::ParameterError(
|
|
|
+ "Dataset item count is zero".to_string(),
|
|
|
+ )),
|
|
|
+ x => {
|
|
|
+ // This weirdness brought to you by c_ulong being different on Windows and Linux
|
|
|
+ #[cfg(target_os = "windows")]
|
|
|
+ return Ok(x);
|
|
|
+ #[cfg(not(target_os = "windows"))]
|
|
|
+ return u32::try_from(x).map_err(|e| RandomXError::Other(e.to_string()));
|
|
|
}
|
|
|
- } else {
|
|
|
- let mut handles = Vec::new();
|
|
|
- let cache_arc = Arc::new(cache);
|
|
|
- let dataset_arc = Arc::new(dataset);
|
|
|
-
|
|
|
- let size = count / n_threads as u64;
|
|
|
- let last = count % n_threads as u64;
|
|
|
- let mut start = 0;
|
|
|
-
|
|
|
- for i in 0..n_threads {
|
|
|
- let cache = cache_arc.clone();
|
|
|
- let dataset = dataset_arc.clone();
|
|
|
- let mut this_size = size;
|
|
|
- if i == n_threads - 1 {
|
|
|
- this_size += last;
|
|
|
- }
|
|
|
- let this_start = start;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- handles.push(thread::spawn(move || unsafe {
|
|
|
- randomx_init_dataset(dataset.dataset, cache.cache, this_start, this_size);
|
|
|
- }));
|
|
|
+ /// Returns the values of the internal memory buffer of the `dataset` or an error on failure.
|
|
|
+ pub fn get_data(&self) -> Result<Vec<u8>, RandomXError> {
|
|
|
+ let memory = unsafe { randomx_get_dataset_memory(self.inner.dataset_ptr) };
|
|
|
+ if memory.is_null() {
|
|
|
+ return Err(RandomXError::DatasetAllocError);
|
|
|
+ }
|
|
|
|
|
|
- start += this_size;
|
|
|
- }
|
|
|
+ let count = usize::try_from(self.inner.dataset_count)
|
|
|
+ .map_err(|e| RandomXError::Other(e.to_string()))?;
|
|
|
|
|
|
- for handle in handles {
|
|
|
- let _ = handle.join();
|
|
|
- }
|
|
|
+ let mut result: Vec<u8> = vec![0u8; count];
|
|
|
|
|
|
- dataset = match Arc::try_unwrap(dataset_arc) {
|
|
|
- Ok(dataset) => dataset,
|
|
|
- Err(_) => return Err(RandomXError::DatasetAllocError),
|
|
|
- };
|
|
|
+ let n = usize::try_from(self.inner.dataset_count)
|
|
|
+ .map_err(|e| RandomXError::Other(e.to_string()))?;
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ memcpy(result.as_mut_ptr() as *mut c_void, memory, n);
|
|
|
}
|
|
|
|
|
|
- Ok(dataset)
|
|
|
+ Ok(result)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl Drop for RandomXDataset {
|
|
|
- fn drop(&mut self) {
|
|
|
- unsafe { randomx_release_dataset(self.dataset) }
|
|
|
- }
|
|
|
+#[derive(Debug)]
|
|
|
+/// The RandomX Virtual Machine (VM) is a complex instruction set computer
|
|
|
+/// that executes generated programs.
|
|
|
+pub struct RandomXVM {
|
|
|
+ flags: RandomXFlags,
|
|
|
+ vm: *mut randomx_vm,
|
|
|
+ linked_cache: Option<RandomXCache>,
|
|
|
+ linked_dataset: Option<RandomXDataset>,
|
|
|
}
|
|
|
|
|
|
-unsafe impl Send for RandomXDataset {}
|
|
|
-unsafe impl Sync for RandomXDataset {}
|
|
|
+unsafe impl Send for RandomXVM {}
|
|
|
+unsafe impl Sync for RandomXVM {}
|
|
|
|
|
|
-pub struct RandomXVM<'a, T: 'a> {
|
|
|
- vm: *mut randomx_vm,
|
|
|
- phantom: PhantomData<&'a T>,
|
|
|
+impl Drop for RandomXVM {
|
|
|
+ /// De-allocates memory for the `VM` object.
|
|
|
+ fn drop(&mut self) {
|
|
|
+ unsafe {
|
|
|
+ randomx_destroy_vm(self.vm);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-impl RandomXVM<'_, RandomXCache> {
|
|
|
- pub fn new(flags: RandomXFlags, cache: &'_ RandomXCache) -> Result<Self, RandomXError> {
|
|
|
- if flags.contains(RandomXFlags::FULLMEM) {
|
|
|
- return Err(RandomXError::VmAllocError);
|
|
|
+impl RandomXVM {
|
|
|
+ /// Creates a new `VM` and initializes it, error on failure.
|
|
|
+ ///
|
|
|
+ /// `flags` is any combination of the following 5 flags:
|
|
|
+ /// * LARGEPAGES
|
|
|
+ /// * HARDAES
|
|
|
+ /// * FULLMEM
|
|
|
+ /// * JIT
|
|
|
+ /// * SECURE
|
|
|
+ ///
|
|
|
+ /// Or
|
|
|
+ ///
|
|
|
+ /// * DEFAULT
|
|
|
+ ///
|
|
|
+ /// `cache` is a cache object, optional if FULLMEM is set.
|
|
|
+ ///
|
|
|
+ /// `dataset` is a dataset object, optional if FULLMEM is not set.
|
|
|
+ pub fn new(
|
|
|
+ flags: RandomXFlags,
|
|
|
+ cache: Option<RandomXCache>,
|
|
|
+ dataset: Option<RandomXDataset>,
|
|
|
+ ) -> Result<RandomXVM, RandomXError> {
|
|
|
+ let is_full_mem = flags.contains(RandomXFlags::FULLMEM);
|
|
|
+
|
|
|
+ match (cache, dataset) {
|
|
|
+ (None, None) => Err(RandomXError::VmAllocError),
|
|
|
+ (None, _) if !is_full_mem => Err(RandomXError::VmAllocError),
|
|
|
+ (_, None) if is_full_mem => Err(RandomXError::VmAllocError),
|
|
|
+ (cache, dataset) => {
|
|
|
+ let cache_ptr = cache
|
|
|
+ .as_ref()
|
|
|
+ .map(|stash| stash.inner.cache_ptr)
|
|
|
+ .unwrap_or_else(ptr::null_mut);
|
|
|
+ let dataset_ptr = dataset
|
|
|
+ .as_ref()
|
|
|
+ .map(|data| data.inner.dataset_ptr)
|
|
|
+ .unwrap_or_else(ptr::null_mut);
|
|
|
+
|
|
|
+ let vm = unsafe { randomx_create_vm(flags.bits(), cache_ptr, dataset_ptr) };
|
|
|
+
|
|
|
+ Ok(RandomXVM {
|
|
|
+ vm,
|
|
|
+ flags,
|
|
|
+ linked_cache: cache,
|
|
|
+ linked_dataset: dataset,
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- let vm = unsafe { randomx_create_vm(flags.bits(), cache.cache, std::ptr::null_mut()) };
|
|
|
+ /// Reinitializes the `VM` with a new cache that was initialized without
|
|
|
+ /// `RandomXFlags::FULLMEM`.
|
|
|
+ pub fn reinit_cache(&mut self, cache: RandomXCache) -> Result<(), RandomXError> {
|
|
|
+ if self.flags.contains(RandomXFlags::FULLMEM) {
|
|
|
+ return Err(RandomXError::CacheReinitError);
|
|
|
+ }
|
|
|
|
|
|
- if vm.is_null() {
|
|
|
- return Err(RandomXError::VmAllocError);
|
|
|
+ unsafe {
|
|
|
+ randomx_vm_set_cache(self.vm, cache.inner.cache_ptr);
|
|
|
}
|
|
|
|
|
|
- Ok(Self {
|
|
|
- vm,
|
|
|
- phantom: PhantomData,
|
|
|
- })
|
|
|
+ self.linked_cache = Some(cache);
|
|
|
+
|
|
|
+ Ok(())
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-impl RandomXVM<'_, RandomXDataset> {
|
|
|
- pub fn new_fast(
|
|
|
- flags: RandomXFlags,
|
|
|
- dataset: &'_ RandomXDataset,
|
|
|
- ) -> Result<Self, RandomXError> {
|
|
|
- if !flags.contains(RandomXFlags::FULLMEM) {
|
|
|
- return Err(RandomXError::VmAllocError);
|
|
|
+ /// Reinitializes the `VM` with a new dataset that was initialized with
|
|
|
+ /// `RandomXFlags::FULLMEM`.
|
|
|
+ pub fn reinit_dataset(&mut self, dataset: RandomXDataset) -> Result<(), RandomXError> {
|
|
|
+ if !self.flags.contains(RandomXFlags::FULLMEM) {
|
|
|
+ return Err(RandomXError::DatasetReinitError);
|
|
|
}
|
|
|
|
|
|
- let vm = unsafe { randomx_create_vm(flags.bits(), std::ptr::null_mut(), dataset.dataset) };
|
|
|
-
|
|
|
- if vm.is_null() {
|
|
|
- return Err(RandomXError::VmAllocError);
|
|
|
+ unsafe {
|
|
|
+ randomx_vm_set_dataset(self.vm, dataset.inner.dataset_ptr);
|
|
|
}
|
|
|
|
|
|
- Ok(Self {
|
|
|
- vm,
|
|
|
- phantom: PhantomData,
|
|
|
- })
|
|
|
+ self.linked_dataset = Some(dataset);
|
|
|
+
|
|
|
+ Ok(())
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-impl<T> RandomXVM<'_, T> {
|
|
|
- /// Calculate the RandomX hash of some data.
|
|
|
+ /// Calculates a RandomX hash value and returns it, error on failure.
|
|
|
///
|
|
|
- /// ```no_run
|
|
|
- /// # // ^ no_run, this is already tested in the actual tests
|
|
|
- /// use randomx::*;
|
|
|
- /// let flags = RandomXFlags::default();
|
|
|
- /// let cache = RandomXCache::new(flags, "key".as_bytes())?;
|
|
|
- /// let vm = RandomXVM::new(flags, &cache)?;
|
|
|
- /// let hash = vm.hash("input".as_bytes());
|
|
|
- /// # Ok::<(), RandomXError>(())
|
|
|
- /// ```
|
|
|
- pub fn hash(&self, input: &[u8]) -> [u8; RANDOMX_HASH_SIZE as usize] {
|
|
|
- let mut hash = std::mem::MaybeUninit::<[u8; RANDOMX_HASH_SIZE as usize]>::uninit();
|
|
|
+ /// `input` is a sequence of u8 to be hashed.
|
|
|
+ pub fn calculate_hash(&self, input: &[u8]) -> Result<Vec<u8>, RandomXError> {
|
|
|
+ if input.is_empty() {
|
|
|
+ return Err(RandomXError::ParameterError(
|
|
|
+ "RandomX VM input empty".to_string(),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ let input_size = input.len();
|
|
|
+ let input_ptr = input.as_ptr() as *mut c_void;
|
|
|
+ let arr = [0; RANDOMX_HASH_SIZE as usize];
|
|
|
+ let output_ptr = arr.as_ptr() as *mut c_void;
|
|
|
|
|
|
unsafe {
|
|
|
- randomx_calculate_hash(
|
|
|
- self.vm,
|
|
|
- input.as_ptr() as *const std::ffi::c_void,
|
|
|
- input.len(),
|
|
|
- hash.as_mut_ptr() as *mut std::ffi::c_void,
|
|
|
- );
|
|
|
+ randomx_calculate_hash(self.vm, input_ptr, input_size, output_ptr);
|
|
|
+ }
|
|
|
|
|
|
- hash.assume_init()
|
|
|
+ // If this failed, arr should still be empty
|
|
|
+ if arr == [0; RANDOMX_HASH_SIZE as usize] {
|
|
|
+ return Err(RandomXError::Other(
|
|
|
+ "RandomX calculated hash was empty".to_string(),
|
|
|
+ ));
|
|
|
}
|
|
|
+
|
|
|
+ Ok(arr.to_vec())
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-impl<T> Drop for RandomXVM<'_, T> {
|
|
|
- fn drop(&mut self) {
|
|
|
- unsafe { randomx_destroy_vm(self.vm) }
|
|
|
+ /// Calculates hashes from a set of inputs.
|
|
|
+ ///
|
|
|
+ /// `input` is an array of a sequence of u8 to be hashed.
|
|
|
+ pub fn calculate_hash_set(&self, input: &[&[u8]]) -> Result<Vec<Vec<u8>>, RandomXError> {
|
|
|
+ if input.is_empty() {
|
|
|
+ // Empty set
|
|
|
+ return Err(RandomXError::ParameterError(
|
|
|
+ "RandomX VM input set empty".to_string(),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut result = vec![];
|
|
|
+
|
|
|
+ // For single input
|
|
|
+ if input.len() == 1 {
|
|
|
+ let hash = self.calculate_hash(input[0])?;
|
|
|
+ result.push(hash);
|
|
|
+ return Ok(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ // For multiple inputs
|
|
|
+ let mut output_ptr: *mut c_void = ptr::null_mut();
|
|
|
+ let arr = [0; RANDOMX_HASH_SIZE as usize];
|
|
|
+
|
|
|
+ // Not len() as last iteration assigns final hash
|
|
|
+ let iterations = input.len() + 1;
|
|
|
+
|
|
|
+ #[allow(clippy::needless_range_loop)]
|
|
|
+ for i in 0..iterations {
|
|
|
+ if i == iterations - 1 {
|
|
|
+ // For last iteration
|
|
|
+ unsafe {
|
|
|
+ randomx_calculate_hash_last(self.vm, output_ptr);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if input[i].is_empty() {
|
|
|
+ // Stop calculations
|
|
|
+ if arr != [0; RANDOMX_HASH_SIZE as usize] {
|
|
|
+ // Complete what was started
|
|
|
+ unsafe {
|
|
|
+ randomx_calculate_hash_last(self.vm, output_ptr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Err(RandomXError::ParameterError(
|
|
|
+ "RandomX VM input was empty".to_string(),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ let input_size = input[i].len();
|
|
|
+ let input_ptr = input[i].as_ptr() as *mut c_void;
|
|
|
+ output_ptr = arr.as_ptr() as *mut c_void;
|
|
|
+
|
|
|
+ if i == 0 {
|
|
|
+ // For first iteration
|
|
|
+ unsafe {
|
|
|
+ randomx_calculate_hash_first(self.vm, input_ptr, input_size);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // For every other iteration
|
|
|
+ unsafe {
|
|
|
+ randomx_calculate_hash_next(self.vm, input_ptr, input_size, output_ptr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if i != 0 {
|
|
|
+ // First hash is only available in 2nd iteration
|
|
|
+ if arr == [0; RANDOMX_HASH_SIZE as usize] {
|
|
|
+ return Err(RandomXError::Other("RandomX hash was zero".to_string()));
|
|
|
+ }
|
|
|
+ let output: Vec<u8> = arr.to_vec();
|
|
|
+ result.push(output);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(result)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-unsafe impl<T> Send for RandomXVM<'_, T> {}
|
|
|
-
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::*;
|
|
|
|
|
|
#[test]
|
|
|
- fn can_calc_hash() {
|
|
|
+ fn lib_alloc_cache() {
|
|
|
+ let flags = RandomXFlags::default();
|
|
|
+ let key = "Key";
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).expect("Failed to allocate cache");
|
|
|
+ drop(cache);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn lib_alloc_dataset() {
|
|
|
+ let flags = RandomXFlags::default();
|
|
|
+ let key = "Key";
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset =
|
|
|
+ RandomXDataset::new(flags, cache.clone(), 0).expect("Failed to allocate dataset");
|
|
|
+ drop(dataset);
|
|
|
+ drop(cache);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn lib_alloc_vm() {
|
|
|
+ let flags = RandomXFlags::default();
|
|
|
+ let key = "Key";
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let mut vm =
|
|
|
+ RandomXVM::new(flags, Some(cache.clone()), None).expect("Failed to allocate VM");
|
|
|
+ drop(vm);
|
|
|
+ let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
|
|
|
+ vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone()))
|
|
|
+ .expect("Failed to allocate VM");
|
|
|
+ drop(dataset);
|
|
|
+ drop(cache);
|
|
|
+ drop(vm);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn lib_dataset_memory() {
|
|
|
let flags = RandomXFlags::default();
|
|
|
- let cache = RandomXCache::new(flags, "RandomX example key\0".as_bytes()).unwrap();
|
|
|
- let vm = RandomXVM::new(flags, &cache).unwrap();
|
|
|
- let hash = vm.hash("RandomX example input\0".as_bytes());
|
|
|
- let expected = [
|
|
|
- 138, 72, 229, 249, 219, 69, 171, 121, 217, 8, 5, 116, 196, 216, 25, 84, 254, 106, 198,
|
|
|
- 56, 66, 33, 74, 255, 115, 194, 68, 178, 99, 48, 183, 201,
|
|
|
+ let key = "Key";
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
|
|
|
+ let memory = dataset.get_data().unwrap_or_else(|_| Vec::new());
|
|
|
+ assert!(!memory.is_empty(), "Failed to get dataset memory");
|
|
|
+ let v = vec![0u8; memory.len()];
|
|
|
+ assert_ne!(memory, v);
|
|
|
+ drop(dataset);
|
|
|
+ drop(cache);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn lib_calculate_hash() {
|
|
|
+ let flags = RandomXFlags::get_recommended_flags();
|
|
|
+ let flags2 = flags | RandomXFlags::FULLMEM;
|
|
|
+ let key = "Key";
|
|
|
+ let input = "Input";
|
|
|
+
|
|
|
+ let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let mut vm1 = RandomXVM::new(flags, Some(cache1.clone()), None).unwrap();
|
|
|
+ let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ let v = vec![0u8; hash1.len()];
|
|
|
+ assert_ne!(hash1, v);
|
|
|
+ assert!(vm1.reinit_cache(cache1.clone()).is_ok());
|
|
|
+ let hash2 = vm1.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_ne!(hash2, v);
|
|
|
+ assert_eq!(hash1, hash2);
|
|
|
+
|
|
|
+ let cache2 = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let vm2 = RandomXVM::new(flags, Some(cache2.clone()), None).unwrap();
|
|
|
+ let hash3 = vm2.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_eq!(hash2, hash3);
|
|
|
+
|
|
|
+ let cache3 = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset3 = RandomXDataset::new(flags, cache3.clone(), 0).unwrap();
|
|
|
+ let mut vm3 = RandomXVM::new(flags2, None, Some(dataset3.clone())).unwrap();
|
|
|
+ let hash4 = vm3.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_ne!(hash3, v);
|
|
|
+ assert!(vm3.reinit_dataset(dataset3.clone()).is_ok());
|
|
|
+ let hash5 = vm3.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_ne!(hash4, v);
|
|
|
+ assert_eq!(hash4, hash5);
|
|
|
+
|
|
|
+ let cache4 = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset4 = RandomXDataset::new(flags, cache4.clone(), 0).unwrap();
|
|
|
+ let vm4 = RandomXVM::new(flags2, Some(cache4), Some(dataset4.clone())).unwrap();
|
|
|
+ let hash6 = vm3.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_eq!(hash5, hash6);
|
|
|
+
|
|
|
+ drop(dataset3);
|
|
|
+ drop(dataset4);
|
|
|
+ drop(cache1);
|
|
|
+ drop(cache2);
|
|
|
+ drop(cache3);
|
|
|
+ drop(vm1);
|
|
|
+ drop(vm2);
|
|
|
+ drop(vm3);
|
|
|
+ drop(vm4);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn lib_calculate_hash_set() {
|
|
|
+ let flags = RandomXFlags::default();
|
|
|
+ let key = "Key";
|
|
|
+ let inputs = vec![
|
|
|
+ "Input".as_bytes(),
|
|
|
+ "Input 2".as_bytes(),
|
|
|
+ "Inputs 3".as_bytes(),
|
|
|
];
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let vm = RandomXVM::new(flags, Some(cache.clone()), None).unwrap();
|
|
|
+ let hashes = vm.calculate_hash_set(inputs.as_slice()).expect("no data");
|
|
|
+ assert_eq!(inputs.len(), hashes.len());
|
|
|
+ let mut prev_hash = Vec::new();
|
|
|
+ for (i, hash) in hashes.into_iter().enumerate() {
|
|
|
+ let v = vec![0u8; hash.len()];
|
|
|
+ assert_ne!(hash, v);
|
|
|
+ assert_ne!(hash, prev_hash);
|
|
|
+ let compare = vm.calculate_hash(inputs[i]).unwrap(); // sanity check
|
|
|
+ assert_eq!(hash, compare);
|
|
|
+ prev_hash = hash;
|
|
|
+ }
|
|
|
+ drop(cache);
|
|
|
+ drop(vm);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn lib_calculate_hash_is_consistent() {
|
|
|
+ let flags = RandomXFlags::get_recommended_flags();
|
|
|
+ let key = "Key";
|
|
|
+ let input = "Input";
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
|
|
|
+ let vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).unwrap();
|
|
|
+ let hash = vm.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_eq!(
|
|
|
+ hash,
|
|
|
+ [
|
|
|
+ 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83,
|
|
|
+ 215, 213, 59, 71, 32, 172, 253, 155, 204, 111, 183, 213, 157, 155
|
|
|
+ ]
|
|
|
+ );
|
|
|
+ drop(vm);
|
|
|
+ drop(dataset);
|
|
|
+ drop(cache);
|
|
|
+
|
|
|
+ let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset1 = RandomXDataset::new(flags, cache1.clone(), 0).unwrap();
|
|
|
+ let vm1 = RandomXVM::new(flags, Some(cache1.clone()), Some(dataset1.clone())).unwrap();
|
|
|
+ let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_eq!(
|
|
|
+ hash1,
|
|
|
+ [
|
|
|
+ 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83,
|
|
|
+ 215, 213, 59, 71, 32, 172, 253, 155, 204, 111, 183, 213, 157, 155
|
|
|
+ ]
|
|
|
+ );
|
|
|
+ drop(vm1);
|
|
|
+ drop(dataset1);
|
|
|
+ drop(cache1);
|
|
|
+ }
|
|
|
|
|
|
- assert_eq!(expected, hash);
|
|
|
+ #[test]
|
|
|
+ fn lib_check_cache_and_dataset_lifetimes() {
|
|
|
+ let flags = RandomXFlags::get_recommended_flags();
|
|
|
+ let key = "Key";
|
|
|
+ let input = "Input";
|
|
|
+ let cache = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset = RandomXDataset::new(flags, cache.clone(), 0).unwrap();
|
|
|
+ let vm = RandomXVM::new(flags, Some(cache.clone()), Some(dataset.clone())).unwrap();
|
|
|
+ drop(dataset);
|
|
|
+ drop(cache);
|
|
|
+ let hash = vm.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_eq!(
|
|
|
+ hash,
|
|
|
+ [
|
|
|
+ 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83,
|
|
|
+ 215, 213, 59, 71, 32, 172, 253, 155, 204, 111, 183, 213, 157, 155
|
|
|
+ ]
|
|
|
+ );
|
|
|
+ drop(vm);
|
|
|
+
|
|
|
+ let cache1 = RandomXCache::new(flags, key.as_bytes()).unwrap();
|
|
|
+ let dataset1 = RandomXDataset::new(flags, cache1.clone(), 0).unwrap();
|
|
|
+ let vm1 = RandomXVM::new(flags, Some(cache1.clone()), Some(dataset1.clone())).unwrap();
|
|
|
+ drop(dataset1);
|
|
|
+ drop(cache1);
|
|
|
+ let hash1 = vm1.calculate_hash(input.as_bytes()).expect("no data");
|
|
|
+ assert_eq!(
|
|
|
+ hash1,
|
|
|
+ [
|
|
|
+ 114, 81, 192, 5, 165, 242, 107, 100, 184, 77, 37, 129, 52, 203, 217, 227, 65, 83,
|
|
|
+ 215, 213, 59, 71, 32, 172, 253, 155, 204, 111, 183, 213, 157, 155
|
|
|
+ ]
|
|
|
+ );
|
|
|
+ drop(vm1);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
- fn can_calc_hash_fast() {
|
|
|
- let flags = RandomXFlags::default() | RandomXFlags::FULLMEM;
|
|
|
- let n = thread::available_parallelism().unwrap().get();
|
|
|
- let dataset = RandomXDataset::new(flags, "RandomX example key\0".as_bytes(), n).unwrap();
|
|
|
- let vm = RandomXVM::new_fast(flags, &dataset).unwrap();
|
|
|
- let hash = vm.hash("RandomX example input\0".as_bytes());
|
|
|
- let expected = [
|
|
|
- 138, 72, 229, 249, 219, 69, 171, 121, 217, 8, 5, 116, 196, 216, 25, 84, 254, 106, 198,
|
|
|
- 56, 66, 33, 74, 255, 115, 194, 68, 178, 99, 48, 183, 201,
|
|
|
+ fn randomx_hash_fast_vs_light() {
|
|
|
+ let input = b"input";
|
|
|
+ let key = b"key";
|
|
|
+
|
|
|
+ let flags = RandomXFlags::get_recommended_flags() | RandomXFlags::FULLMEM;
|
|
|
+ let cache = RandomXCache::new(flags, key).unwrap();
|
|
|
+ let dataset = RandomXDataset::new(flags, cache, 0).unwrap();
|
|
|
+ let fast_vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
|
|
|
+
|
|
|
+ let flags = RandomXFlags::get_recommended_flags();
|
|
|
+ let cache = RandomXCache::new(flags, key).unwrap();
|
|
|
+ let light_vm = RandomXVM::new(flags, Some(cache), None).unwrap();
|
|
|
+
|
|
|
+ let fast = fast_vm.calculate_hash(input).unwrap();
|
|
|
+ let light = light_vm.calculate_hash(input).unwrap();
|
|
|
+ assert_eq!(fast, light);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_vectors_fast_mode() {
|
|
|
+ // https://github.com/tevador/RandomX/blob/040f4500a6e79d54d84a668013a94507045e786f/src/tests/tests.cpp#L963-L979
|
|
|
+ let key = b"test key 000";
|
|
|
+ let vectors = [
|
|
|
+ (
|
|
|
+ b"This is a test".as_slice(),
|
|
|
+ "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f",
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ b"Lorem ipsum dolor sit amet".as_slice(),
|
|
|
+ "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969",
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
|
|
|
+ "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8",
|
|
|
+ ),
|
|
|
];
|
|
|
|
|
|
- assert_eq!(expected, hash);
|
|
|
+ let flags = RandomXFlags::get_recommended_flags() | RandomXFlags::FULLMEM;
|
|
|
+ let cache = RandomXCache::new(flags, key).unwrap();
|
|
|
+ let dataset = RandomXDataset::new(flags, cache, 0).unwrap();
|
|
|
+ let vm = RandomXVM::new(flags, None, Some(dataset)).unwrap();
|
|
|
+
|
|
|
+ for (input, expected) in vectors {
|
|
|
+ let hash = vm.calculate_hash(input).unwrap();
|
|
|
+ assert_eq!(hex::decode(expected).unwrap(), hash);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_vectors_light_mode() {
|
|
|
+ // https://github.com/tevador/RandomX/blob/040f4500a6e79d54d84a668013a94507045e786f/src/tests/tests.cpp#L963-L985
|
|
|
+ let vectors = [
|
|
|
+ (
|
|
|
+ b"test key 000",
|
|
|
+ b"This is a test".as_slice(),
|
|
|
+ "639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f",
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ b"test key 000",
|
|
|
+ b"Lorem ipsum dolor sit amet".as_slice(),
|
|
|
+ "300a0adb47603dedb42228ccb2b211104f4da45af709cd7547cd049e9489c969",
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ b"test key 000",
|
|
|
+ b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
|
|
|
+ "c36d4ed4191e617309867ed66a443be4075014e2b061bcdaf9ce7b721d2b77a8",
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ b"test key 001",
|
|
|
+ b"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".as_slice(),
|
|
|
+ "e9ff4503201c0c2cca26d285c93ae883f9b1d30c9eb240b820756f2d5a7905fc",
|
|
|
+ ),
|
|
|
+ ];
|
|
|
+
|
|
|
+ let flags = RandomXFlags::get_recommended_flags();
|
|
|
+ for (key, input, expected) in vectors {
|
|
|
+ let cache = RandomXCache::new(flags, key).unwrap();
|
|
|
+ let vm = RandomXVM::new(flags, Some(cache), None).unwrap();
|
|
|
+ let hash = vm.calculate_hash(input).unwrap();
|
|
|
+ assert_eq!(hex::decode(expected).unwrap(), hash);
|
|
|
+ }
|
|
|
}
|
|
|
}
|