ccp_randomx/
vm.rs

1/*
2 * Copyright 2024 Fluence DAO
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::bindings::vm::*;
18use crate::cache::Cache;
19use crate::cache::CacheRawAPI;
20use crate::dataset::Dataset;
21use crate::dataset::DatasetRawAPI;
22use crate::errors::VmCreationError;
23use crate::flags::RandomXFlags;
24use crate::result_hash::ResultHash;
25use crate::result_hash::ToRawMut;
26use crate::try_alloc;
27use crate::RResult;
28
29#[derive(Debug)]
30pub struct RandomXVM<T> {
31    vm: *mut randomx_vm,
32    // too ensure that state outlives VM
33    _state: T,
34}
35
36impl<T> RandomXVM<T>
37where
38    T: CacheRawAPI,
39{
40    pub fn light(cache: T, flags: RandomXFlags) -> RResult<Self> {
41        if !flags.is_light_mode() {
42            return Err(VmCreationError::IncorrectLightModeFlag { flags })?;
43        }
44
45        let vm = try_alloc! { randomx_create_vm(flags.bits(), cache.raw(), std::ptr::null_mut()), VmCreationError::AllocationFailed {flags} };
46
47        let vm = RandomXVM { vm, _state: cache };
48        Ok(vm)
49    }
50
51    /// (from RandomX doc) Reinitializes a virtual machine with a new Cache.
52    /// This function should be called anytime the Cache is reinitialized with a new key.
53    /// Does nothing if called with a Cache containing the same key value as already set.
54    pub fn set_new_cache(&mut self, cache: &'_ Cache) {
55        unsafe { randomx_vm_set_cache(self.vm, cache.raw()) }
56    }
57}
58
59impl<T> RandomXVM<T>
60where
61    T: DatasetRawAPI,
62{
63    pub fn fast(dataset: T, flags: RandomXFlags) -> RResult<Self> {
64        if !flags.is_fast_mode() {
65            return Err(VmCreationError::IncorrectFastModeFlag { flags })?;
66        }
67
68        let vm = try_alloc! { randomx_create_vm(flags.bits(), std::ptr::null_mut(), dataset.raw()), VmCreationError::AllocationFailed {flags} };
69
70        let vm = RandomXVM {
71            vm,
72            _state: dataset,
73        };
74
75        Ok(vm)
76    }
77
78    /// Reinitializes a virtual machine with a new Dataset.
79    pub fn set_new_dataset(&mut self, dataset: &'_ Dataset) {
80        unsafe { randomx_vm_set_dataset(self.vm, dataset.raw()) }
81    }
82}
83
84impl<T> Drop for RandomXVM<T> {
85    fn drop(&mut self) {
86        unsafe { randomx_destroy_vm(self.vm) }
87    }
88}
89
90impl<T> RandomXVM<T> {
91    /// Calculates a RandomX hash value.
92    pub fn hash(&self, local_nonce: &[u8]) -> ResultHash {
93        let mut hash = ResultHash::empty();
94
95        unsafe {
96            randomx_calculate_hash(
97                self.vm,
98                local_nonce.as_ptr() as *const std::ffi::c_void,
99                local_nonce.len(),
100                hash.as_raw_mut(),
101            )
102        };
103
104        hash
105    }
106
107    /// Begins a RandomX hash calculation.
108    pub fn hash_first(&self, local_nonce: &[u8]) {
109        unsafe {
110            randomx_calculate_hash_first(
111                self.vm,
112                local_nonce.as_ptr() as *const std::ffi::c_void,
113                local_nonce.len(),
114            )
115        };
116    }
117
118    /// Output the hash value of the previous input
119    /// and begin the calculation of the next hash.
120    pub fn hash_next(&self, local_nonce: &[u8]) -> ResultHash {
121        let mut hash = ResultHash::empty();
122
123        unsafe {
124            randomx_calculate_hash_next(
125                self.vm,
126                local_nonce.as_ptr() as *const std::ffi::c_void,
127                local_nonce.len(),
128                hash.as_raw_mut(),
129            )
130        };
131
132        hash
133    }
134
135    /// Output the hash value of the previous input.
136    pub fn hash_last(&self) -> ResultHash {
137        let mut hash = ResultHash::empty();
138
139        unsafe { randomx_calculate_hash_last(self.vm, hash.as_raw_mut()) };
140
141        hash
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use crate::{Cache, Dataset, RandomXFlags, RandomXVM};
148
149    #[test]
150    fn light_no_creates_with_full_mem() {
151        let flags = RandomXFlags::recommended_full_mem();
152        let cache = Cache::new(&[0, 1], flags).unwrap();
153        let vm = RandomXVM::light(cache.handle(), flags);
154
155        assert!(vm.is_err());
156    }
157
158    #[test]
159    fn fast_no_creates_without_full_mem() {
160        let flags = RandomXFlags::recommended();
161        let dataset = Dataset::new(&[0, 1], flags).unwrap();
162        let vm = RandomXVM::fast(dataset.handle(), flags);
163
164        assert!(vm.is_err());
165    }
166}