├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── lib └── README.txt ├── randomx.go ├── randomx.h ├── randomx_test.go ├── rxCache.go ├── rxDataset.go ├── rxVM.go └── search.go /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeFiles 2 | CMakeCache.txt 3 | *.exe 4 | librandomx.a 5 | *.cmake 6 | Makefile 7 | gxminer -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Command M 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-randomx 2 | 3 | **WARNING: this is not a lib, but a binding** 4 | 5 | Do NOT use go mod import this 6 | 7 | **NOTICE**: For better go.mod experience, like direcly import go-randomx dep through `go get` or `go build`, check the https://github.com/ngchain/RandomX and https://github.com/ngchain/go-randomx and their github actions. 8 | 9 | ## Algorithms 10 | 11 | - random-x 12 | - random-xl 13 | - random-wow 14 | - random-arq 15 | - random-yada 16 | - ... 17 | 18 | ## Build 19 | 20 | ### Windows 21 | 22 | Firstly download and install the msys2, then open and install the following components: 23 | 24 | Take msys2's pacman for example 25 | 26 | ```bash 27 | pacman -Syu 28 | pacman -S git mingw64/mingw-w64-x86_64-go mingw64/mingw-w64-x86_64-gcc mingw64/mingw-w64-x86_64-cmake mingw64/mingw-w64-x86_64-make 29 | ``` 30 | 31 | Secondly clone this repo to your project folder 32 | ``` 33 | cd MyProject 34 | git clone https://github.com/maoxs2/go-randomx 35 | ``` 36 | 37 | And then run `./build.sh` to auto compile official random-x code 38 | ```bash 39 | # clone and compile RandomX source code into librandomx 40 | ./build random-x # random-x can be replaced with random-xl random-arq random-wow 41 | ``` 42 | 43 | Finally you can using the package as your internal one. 44 | 45 | Directly using it with `import "github.com/MyProject/go-randomx"` and then `randomx.AllocCache()` etc. 46 | 47 | ### Linux 48 | 49 | Take Ubuntu for example 50 | 51 | Download the latest go from [here](https://golang.org/dl/) and then install it following [this instruction](https://golang.org/doc/install#tarball) 52 | 53 | ```bash 54 | sudo apt update && sudo apt upgrade 55 | sudo apt install git cmake make gcc build-essential 56 | ``` 57 | 58 | Secondly clone this repo to your project folder 59 | 60 | ``` 61 | cd MyProject 62 | git clone https://github.com/maoxs2/go-randomx 63 | ``` 64 | 65 | And then run `go generate` to auto compile official random-x code 66 | 67 | ```bash 68 | # clone and compile RandomX source code into librandomx 69 | ./build random-x # random-x can be replaced with random-xl random-arq random-wow 70 | ``` 71 | 72 | Finally you can using the package as your internal one. 73 | 74 | Directly using it with `import "github.com/myname/my-project/go-randomx"` and then start the functions like `randomx.AllocCache()` etc. 75 | 76 | ## More 77 | 78 | If you have any better solution, tell me please. 79 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | # If you wanna using other randomx fork, change the branch 2 | # branches avaliable: master(=random-x) random-xl random-wow random-arq 3 | echo "Target $*" 4 | if [ ! -d "RandomX" ]; then 5 | git clone https://github.com/maoxs2/RandomX RandomX 6 | fi 7 | 8 | cd RandomX 9 | git checkout $* 10 | git pull origin $* 11 | mkdir build 12 | cd build 13 | cmake -G "Unix Makefiles" .. 14 | make -j`nproc` 15 | mv librandomx.a ../../lib 16 | cd .. 17 | rm -rf build 18 | cd .. 19 | -------------------------------------------------------------------------------- /lib/README.txt: -------------------------------------------------------------------------------- 1 | The folder holds the binary lib of random-x -------------------------------------------------------------------------------- /randomx.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | //#cgo CFLAGS: -I./randomx 4 | //#cgo LDFLAGS: -L${SRCDIR}/lib -lrandomx 5 | //#cgo LDFLAGS: -lstdc++ 6 | //#cgo LDFLAGS: -static -static-libgcc -static-libstdc++ -lpthread 7 | /* 8 | #include "randomx.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct arg_struct { 16 | randomx_dataset *dataset; 17 | randomx_cache *cache; 18 | void *seed; 19 | uint32_t a, b; 20 | }; 21 | 22 | void *init_full_dataset_thread(void *arguments) 23 | { 24 | struct arg_struct *args = arguments; 25 | randomx_init_dataset(args->dataset, args->cache, args->a, args->b - args->a); 26 | free(arguments); 27 | pthread_exit(NULL); 28 | return NULL; 29 | } 30 | 31 | void init_full_dataset(randomx_dataset *dataset, randomx_cache *cache, uint32_t numThreads) 32 | { 33 | const uint64_t datasetItemCount = randomx_dataset_item_count(); 34 | 35 | if (numThreads > 1) { 36 | pthread_t threads[numThreads]; 37 | 38 | for (uint64_t i = 0; i < numThreads; ++i) { 39 | pthread_t thread; 40 | threads[i] = thread; 41 | 42 | uint32_t a = (datasetItemCount * i) / numThreads; 43 | uint32_t b = (datasetItemCount * (i + 1)) / numThreads; 44 | 45 | struct arg_struct *args = malloc(sizeof(struct arg_struct)); 46 | args->dataset = dataset; 47 | args->cache = cache; 48 | args->a = a; 49 | args->b = b; 50 | 51 | pthread_create(&thread, NULL, &init_full_dataset_thread, (void *)args); 52 | } 53 | 54 | for (uint32_t i = 0; i < numThreads; ++i) { 55 | pthread_join(threads[i], NULL); 56 | } 57 | } 58 | else { 59 | randomx_init_dataset(dataset, cache, 0, datasetItemCount); 60 | } 61 | } 62 | */ 63 | import "C" 64 | import ( 65 | "errors" 66 | "sync" 67 | "unsafe" 68 | ) 69 | 70 | type Flag int 71 | 72 | var ( 73 | FlagDefault Flag = 0 // for all default 74 | FlagLargePages Flag = 1 // for dataset & rxCache & vm 75 | FlagHardAES Flag = 2 // for vm 76 | FlagFullMEM Flag = 4 // for vm 77 | FlagJIT Flag = 8 // for vm & cache 78 | FlagSecure Flag = 16 79 | FlagArgon2SSSE3 Flag = 32 // for cache 80 | FlagArgon2AVX2 Flag = 64 // for cache 81 | FlagArgon2 Flag = 96 // = avx2 + sse3 82 | ) 83 | 84 | func (f Flag) toC() C.randomx_flags { 85 | return (C.randomx_flags)(f) 86 | } 87 | 88 | func AllocCache(flags ...Flag) (*C.randomx_cache, error) { 89 | var SumFlag = FlagDefault 90 | var cache *C.randomx_cache 91 | 92 | for _, flag := range flags { 93 | SumFlag = SumFlag | flag 94 | } 95 | 96 | cache = C.randomx_alloc_cache(SumFlag.toC()) 97 | if cache == nil { 98 | return nil, errors.New("failed to alloc mem for rxCache") 99 | } 100 | 101 | return cache, nil 102 | } 103 | 104 | func InitCache(cache *C.randomx_cache, seed []byte) { 105 | if len(seed) == 0 { 106 | panic("seed cannot be NULL") 107 | } 108 | 109 | C.randomx_init_cache(cache, unsafe.Pointer(&seed[0]), C.size_t(len(seed))) 110 | } 111 | 112 | func ReleaseCache(cache *C.randomx_cache) { 113 | C.randomx_release_cache(cache) 114 | } 115 | 116 | func AllocDataset(flags ...Flag) (*C.randomx_dataset, error) { 117 | var SumFlag = FlagDefault 118 | for _, flag := range flags { 119 | SumFlag = SumFlag | flag 120 | } 121 | 122 | var dataset *C.randomx_dataset 123 | dataset = C.randomx_alloc_dataset(SumFlag.toC()) 124 | if dataset == nil { 125 | return nil, errors.New("failed to alloc mem for dataset") 126 | } 127 | 128 | return dataset, nil 129 | } 130 | 131 | func DatasetItemCount() uint32 { 132 | var length C.ulong 133 | length = C.randomx_dataset_item_count() 134 | return uint32(length) 135 | } 136 | 137 | func InitDataset(dataset *C.randomx_dataset, cache *C.randomx_cache, startItem uint32, itemCount uint32) { 138 | if dataset == nil { 139 | panic("alloc dataset mem is required") 140 | } 141 | 142 | if cache == nil { 143 | panic("alloc cache mem is required") 144 | } 145 | 146 | C.randomx_init_dataset(dataset, cache, C.ulong(startItem), C.ulong(itemCount)) 147 | } 148 | 149 | // FastInitFullDataset using c's pthread to boost the dataset init. 472s -> 466s 150 | func FastInitFullDataset(dataset *C.randomx_dataset, cache *C.randomx_cache, workerNum uint32) { 151 | if dataset == nil { 152 | panic("alloc dataset mem is required") 153 | } 154 | 155 | if cache == nil { 156 | panic("alloc cache mem is required") 157 | } 158 | 159 | var wg sync.WaitGroup 160 | go func() { 161 | wg.Add(1) 162 | C.init_full_dataset(dataset, cache, C.uint32_t(workerNum)) 163 | wg.Done() 164 | }() 165 | 166 | wg.Wait() 167 | } 168 | 169 | func GetDatasetMemory(dataset *C.randomx_dataset) unsafe.Pointer { 170 | return C.randomx_get_dataset_memory(dataset) 171 | } 172 | 173 | func ReleaseDataset(dataset *C.randomx_dataset) { 174 | C.randomx_release_dataset(dataset) 175 | } 176 | 177 | func CreateVM(cache *C.randomx_cache, dataset *C.randomx_dataset, flags ...Flag) (*C.randomx_vm, error) { 178 | var SumFlag = FlagDefault 179 | for _, flag := range flags { 180 | SumFlag = SumFlag | flag 181 | } 182 | 183 | if dataset == nil { 184 | panic("failed creating vm: using empty dataset") 185 | } 186 | 187 | vm := C.randomx_create_vm(SumFlag.toC(), cache, dataset) 188 | 189 | if vm == nil { 190 | return nil, errors.New("failed to create vm") 191 | } 192 | 193 | return vm, nil 194 | } 195 | 196 | func SetVMCache(vm *C.randomx_vm, cache *C.randomx_cache) { 197 | C.randomx_vm_set_cache(vm, cache) 198 | } 199 | 200 | func SetVMDataset(vm *C.randomx_vm, dataset *C.randomx_dataset) { 201 | C.randomx_vm_set_dataset(vm, dataset) 202 | } 203 | 204 | func DestroyVM(vm *C.randomx_vm) { 205 | C.randomx_destroy_vm(vm) 206 | } 207 | 208 | func CalculateHash(vm *C.randomx_vm, in []byte) []byte { 209 | out := make([]byte, C.RANDOMX_HASH_SIZE) 210 | if vm == nil { 211 | panic("failed hashing: using empty vm") 212 | } 213 | 214 | C.randomx_calculate_hash(vm, unsafe.Pointer(&in[0]), C.size_t(len(in)), unsafe.Pointer(&out[0])) 215 | return out 216 | } 217 | 218 | func CalculateHashFirst(vm *C.randomx_vm, in []byte) { 219 | if vm == nil { 220 | panic("failed hashing: using empty vm") 221 | } 222 | C.randomx_calculate_hash_first(vm, unsafe.Pointer(&in[0]), C.size_t(len(in))) 223 | } 224 | 225 | func CalculateHashNext(vm *C.randomx_vm, in []byte) []byte { 226 | out := make([]byte, C.RANDOMX_HASH_SIZE) 227 | if vm == nil { 228 | panic("failed hashing: using empty vm") 229 | } 230 | 231 | C.randomx_calculate_hash_next(vm, unsafe.Pointer(&in[0]), C.size_t(len(in)), unsafe.Pointer(&out[0])) 232 | return out 233 | } 234 | 235 | //// Types 236 | 237 | type RxCache struct { 238 | seed []byte 239 | cache *C.randomx_cache 240 | initCount uint64 241 | } 242 | 243 | type RxDataset struct { 244 | dataset *C.randomx_dataset 245 | rxCache *RxCache 246 | 247 | workerNum uint32 248 | } 249 | 250 | type RxVM struct { 251 | vm *C.randomx_vm 252 | rxDataset *RxDataset 253 | } 254 | -------------------------------------------------------------------------------- /randomx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018-2019, tevador 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the copyright holder nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef RANDOMX_H 30 | #define RANDOMX_H 31 | 32 | #include 33 | #include 34 | 35 | #define RANDOMX_HASH_SIZE 32 36 | #define RANDOMX_DATASET_ITEM_SIZE 64 37 | 38 | #ifndef RANDOMX_EXPORT 39 | #define RANDOMX_EXPORT 40 | #endif 41 | 42 | typedef enum { 43 | RANDOMX_FLAG_DEFAULT = 0, 44 | RANDOMX_FLAG_LARGE_PAGES = 1, 45 | RANDOMX_FLAG_HARD_AES = 2, 46 | RANDOMX_FLAG_FULL_MEM = 4, 47 | RANDOMX_FLAG_JIT = 8, 48 | RANDOMX_FLAG_SECURE = 16, 49 | RANDOMX_FLAG_ARGON2_SSSE3 = 32, 50 | RANDOMX_FLAG_ARGON2_AVX2 = 64, 51 | RANDOMX_FLAG_ARGON2 = 96 52 | } randomx_flags; 53 | 54 | typedef struct randomx_dataset randomx_dataset; 55 | typedef struct randomx_cache randomx_cache; 56 | typedef struct randomx_vm randomx_vm; 57 | 58 | 59 | #if defined(__cplusplus) 60 | 61 | #ifdef __cpp_constexpr 62 | #define CONSTEXPR constexpr 63 | #else 64 | #define CONSTEXPR 65 | #endif 66 | 67 | inline CONSTEXPR randomx_flags operator |(randomx_flags a, randomx_flags b) { 68 | return static_cast(static_cast(a) | static_cast(b)); 69 | } 70 | inline CONSTEXPR randomx_flags operator &(randomx_flags a, randomx_flags b) { 71 | return static_cast(static_cast(a) & static_cast(b)); 72 | } 73 | inline randomx_flags& operator |=(randomx_flags& a, randomx_flags b) { 74 | return a = a | b; 75 | } 76 | 77 | extern "C" { 78 | #endif 79 | 80 | /** 81 | * @return The recommended flags to be used on the current machine. 82 | * Does not include: 83 | * RANDOMX_FLAG_LARGE_PAGES 84 | * RANDOMX_FLAG_FULL_MEM 85 | * RANDOMX_FLAG_SECURE 86 | * These flags must be added manually if desired. 87 | * On OpenBSD RANDOMX_FLAG_SECURE is enabled by default in JIT mode as W^X is enforced by the OS. 88 | */ 89 | RANDOMX_EXPORT randomx_flags randomx_get_flags(void); 90 | 91 | /** 92 | * Creates a randomx_cache structure and allocates memory for RandomX Cache. 93 | * 94 | * @param flags is any combination of these 2 flags (each flag can be set or not set): 95 | * RANDOMX_FLAG_LARGE_PAGES - allocate memory in large pages 96 | * RANDOMX_FLAG_JIT - create cache structure with JIT compilation support; this makes 97 | * subsequent Dataset initialization faster 98 | * Optionally, one of these two flags may be selected: 99 | * RANDOMX_FLAG_ARGON2_SSSE3 - optimized Argon2 for CPUs with the SSSE3 instruction set 100 | * makes subsequent cache initialization faster 101 | * RANDOMX_FLAG_ARGON2_AVX2 - optimized Argon2 for CPUs with the AVX2 instruction set 102 | * makes subsequent cache initialization faster 103 | * 104 | * @return Pointer to an allocated randomx_cache structure. 105 | * Returns NULL if: 106 | * (1) memory allocation fails 107 | * (2) the RANDOMX_FLAG_JIT is set and JIT compilation is not supported on the current platform 108 | * (3) an invalid or unsupported RANDOMX_FLAG_ARGON2 value is set 109 | */ 110 | RANDOMX_EXPORT randomx_cache *randomx_alloc_cache(randomx_flags flags); 111 | 112 | /** 113 | * Initializes the cache memory and SuperscalarHash using the provided key value. 114 | * Does nothing if called again with the same key value. 115 | * 116 | * @param cache is a pointer to a previously allocated randomx_cache structure. Must not be NULL. 117 | * @param key is a pointer to memory which contains the key value. Must not be NULL. 118 | * @param keySize is the number of bytes of the key. 119 | */ 120 | RANDOMX_EXPORT void randomx_init_cache(randomx_cache *cache, const void *key, size_t keySize); 121 | 122 | /** 123 | * Releases all memory occupied by the randomx_cache structure. 124 | * 125 | * @param cache is a pointer to a previously allocated randomx_cache structure. 126 | */ 127 | RANDOMX_EXPORT void randomx_release_cache(randomx_cache* cache); 128 | 129 | /** 130 | * Creates a randomx_dataset structure and allocates memory for RandomX Dataset. 131 | * 132 | * @param flags is the initialization flags. Only one flag is supported (can be set or not set): 133 | * RANDOMX_FLAG_LARGE_PAGES - allocate memory in large pages 134 | * 135 | * @return Pointer to an allocated randomx_dataset structure. 136 | * NULL is returned if memory allocation fails. 137 | */ 138 | RANDOMX_EXPORT randomx_dataset *randomx_alloc_dataset(randomx_flags flags); 139 | 140 | /** 141 | * Gets the number of items contained in the dataset. 142 | * 143 | * @return the number of items contained in the dataset. 144 | */ 145 | RANDOMX_EXPORT unsigned long randomx_dataset_item_count(void); 146 | 147 | /** 148 | * Initializes dataset items. 149 | * 150 | * Note: In order to use the Dataset, all items from 0 to (randomx_dataset_item_count() - 1) must be initialized. 151 | * This may be done by several calls to this function using non-overlapping item sequences. 152 | * 153 | * @param dataset is a pointer to a previously allocated randomx_dataset structure. Must not be NULL. 154 | * @param cache is a pointer to a previously allocated and initialized randomx_cache structure. Must not be NULL. 155 | * @param startItem is the item number where intialization should start. 156 | * @param itemCount is the number of items that should be initialized. 157 | */ 158 | RANDOMX_EXPORT void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount); 159 | 160 | /** 161 | * Returns a pointer to the internal memory buffer of the dataset structure. The size 162 | * of the internal memory buffer is randomx_dataset_item_count() * RANDOMX_DATASET_ITEM_SIZE. 163 | * 164 | * @param dataset is a pointer to a previously allocated randomx_dataset structure. Must not be NULL. 165 | * 166 | * @return Pointer to the internal memory buffer of the dataset structure. 167 | */ 168 | RANDOMX_EXPORT void *randomx_get_dataset_memory(randomx_dataset *dataset); 169 | 170 | /** 171 | * Releases all memory occupied by the randomx_dataset structure. 172 | * 173 | * @param dataset is a pointer to a previously allocated randomx_dataset structure. 174 | */ 175 | RANDOMX_EXPORT void randomx_release_dataset(randomx_dataset *dataset); 176 | 177 | /** 178 | * Creates and initializes a RandomX virtual machine. 179 | * 180 | * @param flags is any combination of these 5 flags (each flag can be set or not set): 181 | * RANDOMX_FLAG_LARGE_PAGES - allocate scratchpad memory in large pages 182 | * RANDOMX_FLAG_HARD_AES - virtual machine will use hardware accelerated AES 183 | * RANDOMX_FLAG_FULL_MEM - virtual machine will use the full dataset 184 | * RANDOMX_FLAG_JIT - virtual machine will use a JIT compiler 185 | * RANDOMX_FLAG_SECURE - when combined with RANDOMX_FLAG_JIT, the JIT pages are never 186 | * writable and executable at the same time (W^X policy) 187 | * The numeric values of the first 4 flags are ordered so that a higher value will provide 188 | * faster hash calculation and a lower numeric value will provide higher portability. 189 | * Using RANDOMX_FLAG_DEFAULT (all flags not set) works on all platforms, but is the slowest. 190 | * @param cache is a pointer to an initialized randomx_cache structure. Can be 191 | * NULL if RANDOMX_FLAG_FULL_MEM is set. 192 | * @param dataset is a pointer to a randomx_dataset structure. Can be NULL 193 | * if RANDOMX_FLAG_FULL_MEM is not set. 194 | * 195 | * @return Pointer to an initialized randomx_vm structure. 196 | * Returns NULL if: 197 | * (1) Scratchpad memory allocation fails. 198 | * (2) The requested initialization flags are not supported on the current platform. 199 | * (3) cache parameter is NULL and RANDOMX_FLAG_FULL_MEM is not set 200 | * (4) dataset parameter is NULL and RANDOMX_FLAG_FULL_MEM is set 201 | */ 202 | RANDOMX_EXPORT randomx_vm *randomx_create_vm(randomx_flags flags, randomx_cache *cache, randomx_dataset *dataset); 203 | 204 | /** 205 | * Reinitializes a virtual machine with a new Cache. This function should be called anytime 206 | * the Cache is reinitialized with a new key. Does nothing if called with a Cache containing 207 | * the same key value as already set. 208 | * 209 | * @param machine is a pointer to a randomx_vm structure that was initialized 210 | * without RANDOMX_FLAG_FULL_MEM. Must not be NULL. 211 | * @param cache is a pointer to an initialized randomx_cache structure. Must not be NULL. 212 | */ 213 | RANDOMX_EXPORT void randomx_vm_set_cache(randomx_vm *machine, randomx_cache* cache); 214 | 215 | /** 216 | * Reinitializes a virtual machine with a new Dataset. 217 | * 218 | * @param machine is a pointer to a randomx_vm structure that was initialized 219 | * with RANDOMX_FLAG_FULL_MEM. Must not be NULL. 220 | * @param dataset is a pointer to an initialized randomx_dataset structure. Must not be NULL. 221 | */ 222 | RANDOMX_EXPORT void randomx_vm_set_dataset(randomx_vm *machine, randomx_dataset *dataset); 223 | 224 | /** 225 | * Releases all memory occupied by the randomx_vm structure. 226 | * 227 | * @param machine is a pointer to a previously created randomx_vm structure. 228 | */ 229 | RANDOMX_EXPORT void randomx_destroy_vm(randomx_vm *machine); 230 | 231 | /** 232 | * Calculates a RandomX hash value. 233 | * 234 | * @param machine is a pointer to a randomx_vm structure. Must not be NULL. 235 | * @param input is a pointer to memory to be hashed. Must not be NULL. 236 | * @param inputSize is the number of bytes to be hashed. 237 | * @param output is a pointer to memory where the hash will be stored. Must not 238 | * be NULL and at least RANDOMX_HASH_SIZE bytes must be available for writing. 239 | */ 240 | RANDOMX_EXPORT void randomx_calculate_hash(randomx_vm *machine, const void *input, size_t inputSize, void *output); 241 | 242 | /** 243 | * Paired functions used to calculate multiple RandomX hashes more efficiently. 244 | * randomx_calculate_hash_first is called for the first input value. 245 | * randomx_calculate_hash_next will output the hash value of the previous input. 246 | * 247 | * @param machine is a pointer to a randomx_vm structure. Must not be NULL. 248 | * @param input is a pointer to memory to be hashed. Must not be NULL. 249 | * @param inputSize is the number of bytes to be hashed. 250 | * @param nextInput is a pointer to memory to be hashed for the next hash. Must not be NULL. 251 | * @param nextInputSize is the number of bytes to be hashed for the next hash. 252 | * @param output is a pointer to memory where the hash will be stored. Must not 253 | * be NULL and at least RANDOMX_HASH_SIZE bytes must be available for writing. 254 | */ 255 | RANDOMX_EXPORT void randomx_calculate_hash_first(randomx_vm* machine, const void* input, size_t inputSize); 256 | RANDOMX_EXPORT void randomx_calculate_hash_next(randomx_vm* machine, const void* nextInput, size_t nextInputSize, void* output); 257 | 258 | #if defined(__cplusplus) 259 | } 260 | #endif 261 | 262 | #endif 263 | -------------------------------------------------------------------------------- /randomx_test.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/hex" 7 | "fmt" 8 | "log" 9 | "math/rand" 10 | //"math/rand" 11 | "runtime" 12 | "strconv" 13 | //"strconv" 14 | "sync" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | var testPairs = [][][]byte{ 20 | // randomX 21 | { 22 | []byte("test key 000"), 23 | []byte("This is a test"), 24 | []byte("639183aae1bf4c9a35884cb46b09cad9175f04efd7684e7262a0ac1c2f0b4e3f"), 25 | }, 26 | // randomXL 27 | { 28 | []byte("test key 000"), 29 | []byte("This is a test"), 30 | []byte("b291ec8a532bc4f78bd75b43d211e1169bb65b1a8f66d4250376ba1d6fcff1bd"), 31 | }, 32 | } 33 | 34 | func TestAllocCache(t *testing.T) { 35 | cache, _ := AllocCache(FlagDefault) 36 | InitCache(cache, []byte("123")) 37 | ReleaseCache(cache) 38 | } 39 | 40 | func TestAllocDataset(t *testing.T) { 41 | ds, _ := AllocDataset(FlagDefault) 42 | cache, _ := AllocCache(FlagDefault) 43 | 44 | seed := make([]byte, 32) 45 | InitCache(cache, seed) 46 | log.Println("rxCache initialization finished") 47 | 48 | count := DatasetItemCount() 49 | log.Println("dataset count:", count/1024/1024, "mb") 50 | InitDataset(ds, cache, 0, count) 51 | log.Println(GetDatasetMemory(ds)) 52 | 53 | ReleaseDataset(ds) 54 | ReleaseCache(cache) 55 | } 56 | 57 | func TestCreateVM(t *testing.T) { 58 | runtime.GOMAXPROCS(runtime.NumCPU()) 59 | var tp = testPairs[1] 60 | cache, _ := AllocCache(FlagDefault) 61 | log.Println("alloc cache mem finished") 62 | seed := tp[0] 63 | InitCache(cache, seed) 64 | log.Println("cache initialization finished") 65 | 66 | ds, _ := AllocDataset(FlagDefault) 67 | log.Println("alloc dataset mem finished") 68 | count := DatasetItemCount() 69 | log.Println("dataset count:", count) 70 | var wg sync.WaitGroup 71 | var workerNum = uint32(runtime.NumCPU()) 72 | for i := uint32(0); i < workerNum; i++ { 73 | wg.Add(1) 74 | a := (count * i) / workerNum 75 | b := (count * (i + 1)) / workerNum 76 | go func() { 77 | defer wg.Done() 78 | InitDataset(ds, cache, a, b-a) 79 | }() 80 | } 81 | wg.Wait() 82 | log.Println("dataset initialization finished") // too slow when one thread 83 | vm, _ := CreateVM(cache, ds, FlagJIT, FlagHardAES, FlagFullMEM) 84 | 85 | var hashCorrect = make([]byte, hex.DecodedLen(len(tp[2]))) 86 | _, err := hex.Decode(hashCorrect, tp[2]) 87 | if err != nil { 88 | log.Println(err) 89 | } 90 | 91 | if bytes.Compare(CalculateHash(vm, tp[1]), hashCorrect) != 0 { 92 | t.Fail() 93 | } 94 | } 95 | 96 | func TestNewRxVM(t *testing.T) { 97 | runtime.GOMAXPROCS(runtime.NumCPU()) 98 | start := time.Now() 99 | pair := testPairs[1] 100 | workerNum := uint32(runtime.NumCPU()) 101 | 102 | seed := pair[0] 103 | dataset, _ := NewRxDataset(FlagJIT) 104 | if dataset.GoInit(seed, workerNum) == false { 105 | log.Fatal("failed to init dataset") 106 | } 107 | //defer dataset.Close() 108 | fmt.Println("Finished generating dataset in", time.Since(start).Seconds(), "sec") 109 | 110 | vm, _ := NewRxVM(dataset, FlagFullMEM, FlagHardAES, FlagJIT, FlagSecure) 111 | //defer vm.Close() 112 | 113 | blob := pair[1] 114 | hash := vm.CalcHash(blob) 115 | 116 | var hashCorrect = make([]byte, hex.DecodedLen(len(pair[2]))) 117 | _, err := hex.Decode(hashCorrect, pair[2]) 118 | if err != nil { 119 | log.Println(err) 120 | } 121 | 122 | if bytes.Compare(hash, hashCorrect) != 0 { 123 | log.Println(hash) 124 | t.Fail() 125 | } 126 | } 127 | 128 | func TestCalculateHashFirst(t *testing.T) { 129 | runtime.GOMAXPROCS(runtime.NumCPU()) 130 | start := time.Now() 131 | pair := testPairs[1] 132 | workerNum := uint32(runtime.NumCPU()) 133 | 134 | seed := pair[0] 135 | dataset, _ := NewRxDataset(FlagJIT) 136 | if dataset.GoInit(seed, workerNum) == false { 137 | log.Fatal("failed to init dataset") 138 | } 139 | //defer dataset.Close() 140 | fmt.Println("Finished generating dataset in", time.Since(start).Seconds(), "sec") 141 | vm, _ := NewRxVM(dataset, FlagFullMEM, FlagHardAES, FlagJIT, FlagSecure) 142 | //defer vm.Close() 143 | 144 | targetBlob := make([]byte, 76) 145 | targetNonce := make([]byte, 4) 146 | binary.LittleEndian.PutUint32(targetNonce, 2333) 147 | copy(targetBlob[39:43], targetNonce) 148 | 149 | targetResult := vm.CalcHash(targetBlob) 150 | 151 | var wg sync.WaitGroup 152 | for i := 0; i < runtime.NumCPU(); i++ { 153 | vm, _ := NewRxVM(dataset, FlagFullMEM, FlagHardAES, FlagJIT, FlagSecure) 154 | 155 | wg.Add(1) 156 | blob := make([]byte, 76) 157 | vm.CalcHashFirst(blob) 158 | 159 | n := uint32(0) 160 | go func() { 161 | for { 162 | n++ 163 | nonce := make([]byte, 4) 164 | binary.LittleEndian.PutUint32(nonce, n) 165 | copy(blob[39:43], nonce) 166 | result := vm.CalcHashNext(blob) 167 | if bytes.Compare(result, targetResult) == 0 { 168 | fmt.Println(n, "found") 169 | wg.Done() 170 | } else { 171 | //fmt.Println(n, "failed") 172 | } 173 | } 174 | }() 175 | } 176 | wg.Wait() 177 | 178 | } 179 | 180 | // go test -v -bench "." -benchtime=30m 181 | func BenchmarkCalculateHash(b *testing.B) { 182 | cache, _ := AllocCache(FlagDefault) 183 | ds, _ := AllocDataset(FlagDefault) 184 | InitCache(cache, []byte("123")) 185 | FastInitFullDataset(ds, cache, uint32(runtime.NumCPU())) 186 | vm, _ := CreateVM(cache, ds, FlagDefault) 187 | for i := 0; i < b.N; i++ { 188 | nonce := strconv.FormatInt(rand.Int63(), 10) // just test 189 | CalculateHash(vm, []byte("123"+nonce)) 190 | } 191 | 192 | DestroyVM(vm) 193 | } 194 | -------------------------------------------------------------------------------- /rxCache.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | import "C" 4 | import ( 5 | "bytes" 6 | ) 7 | 8 | func NewRxCache(flags ...Flag) (*RxCache, error) { 9 | cache, err := AllocCache(flags...) 10 | if cache == nil { 11 | return nil, err 12 | } 13 | 14 | return &RxCache{cache: cache}, nil 15 | } 16 | 17 | func (c *RxCache) Close() { 18 | if c.cache != nil { 19 | ReleaseCache(c.cache) 20 | } 21 | } 22 | 23 | func (c *RxCache) Init(seed []byte) bool { 24 | if c.IsReady(seed) { 25 | return false 26 | } 27 | 28 | c.seed = seed 29 | InitCache(c.cache, c.seed) 30 | 31 | c.initCount++ 32 | 33 | return true 34 | } 35 | 36 | func (c *RxCache) IsReady(seed []byte) bool { 37 | return (c.initCount > 0) && (bytes.Compare(c.seed, seed) == 0) 38 | } 39 | -------------------------------------------------------------------------------- /rxDataset.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | import "C" 4 | import ( 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | func NewRxDataset(flags ...Flag) (*RxDataset, error) { 10 | cache, err := NewRxCache(flags...) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | dataset, err := AllocDataset(flags...) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return &RxDataset{ 21 | dataset: dataset, 22 | rxCache: cache, 23 | 24 | workerNum: 1, 25 | }, nil 26 | } 27 | 28 | func (ds *RxDataset) Close() { 29 | if ds.dataset != nil { 30 | ReleaseDataset(ds.dataset) 31 | } 32 | 33 | ds.rxCache.Close() 34 | } 35 | 36 | func (ds *RxDataset) GoInit(seed []byte, workerNum uint32) bool { 37 | if ds.rxCache.Init(seed) == false { 38 | fmt.Println("WARN: rxCache has already been initialized by the same seed") 39 | } 40 | 41 | if ds.rxCache == nil || ds.rxCache.cache == nil { 42 | return false 43 | } 44 | 45 | datasetItemCount := DatasetItemCount() 46 | var wg sync.WaitGroup 47 | 48 | for i := uint32(0); i < workerNum; i++ { 49 | a := (datasetItemCount * i) / workerNum 50 | b := (datasetItemCount * (i + 1)) / workerNum 51 | wg.Add(1) 52 | go func() { 53 | InitDataset(ds.dataset, ds.rxCache.cache, a, b-a) 54 | wg.Done() 55 | }() 56 | } 57 | wg.Wait() 58 | 59 | return true 60 | } 61 | 62 | func (ds *RxDataset) CInit(seed []byte, workerNum uint32) bool { 63 | if ds.rxCache.Init(seed) == false { 64 | fmt.Println("WARN: rxCache has already been initialized by the same seed") 65 | } 66 | 67 | if ds.rxCache == nil || ds.rxCache.cache == nil { 68 | return false 69 | } 70 | 71 | FastInitFullDataset(ds.dataset, ds.rxCache.cache, workerNum) 72 | 73 | return true 74 | } 75 | -------------------------------------------------------------------------------- /rxVM.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | func NewRxVM(rxDataset *RxDataset, flags ...Flag) (*RxVM, error) { 4 | if rxDataset.rxCache == nil { 5 | vm, err := CreateVM(nil, rxDataset.dataset, flags...) 6 | return &RxVM{ 7 | vm: vm, 8 | rxDataset: nil, 9 | }, err 10 | } 11 | vm, err := CreateVM(rxDataset.rxCache.cache, rxDataset.dataset, flags...) 12 | return &RxVM{ 13 | vm: vm, 14 | rxDataset: nil, 15 | }, err 16 | } 17 | 18 | func (vm *RxVM) Close() { 19 | if vm.vm != nil { 20 | DestroyVM(vm.vm) 21 | } 22 | } 23 | 24 | func (vm *RxVM) CalcHash(in []byte) []byte { 25 | return CalculateHash(vm.vm, in) 26 | } 27 | 28 | func (vm *RxVM) CalcHashFirst(in []byte) { 29 | CalculateHashFirst(vm.vm, in) 30 | } 31 | 32 | func (vm *RxVM) CalcHashNext(in []byte) []byte { 33 | return CalculateHashNext(vm.vm, in) 34 | } 35 | 36 | func (vm *RxVM) UpdateDataset(rxDataset *RxDataset) { 37 | SetVMCache(vm.vm, rxDataset.rxCache.cache) 38 | SetVMDataset(vm.vm, rxDataset.dataset) 39 | } 40 | -------------------------------------------------------------------------------- /search.go: -------------------------------------------------------------------------------- 1 | package randomx 2 | 3 | /* 4 | #include 5 | #include 6 | #include "randomx.h" 7 | 8 | bool search(randomx_vm* vm, void* in, const uint64_t target, const uint64_t max_times, const uint32_t jump, void* nonce, void* out, void* sol) 9 | { 10 | //randomx_calculate_hash_first(vm, in, 76); 11 | 12 | for (uint64_t i=0; i < max_times; i++) 13 | { 14 | *(uint32_t*)(in+39) = *(uint32_t*)(nonce) + jump; 15 | randomx_calculate_hash_next(vm, in, 76, out); 16 | 17 | *(uint32_t*)(sol) = *(uint32_t*)(nonce); 18 | *(uint32_t*)(nonce) = *(uint32_t*)(in+39); 19 | 20 | if (*(uint64_t*)(out+24) < target) { 21 | return true; 22 | } 23 | } 24 | 25 | return false; 26 | } 27 | */ 28 | import "C" 29 | import ( 30 | "unsafe" 31 | ) 32 | 33 | func Search(vm *C.randomx_vm, in []byte, target uint64, maxTimes uint64, jump uint32, nonce []byte) (Hash []byte, Found bool, Sol []byte) { 34 | hash := make([]byte, C.RANDOMX_HASH_SIZE) 35 | sol := make([]byte, 4) 36 | if vm == nil { 37 | panic("failed hashing: using empty vm") 38 | } 39 | 40 | var cFound C.bool 41 | cFound = C.search(vm, unsafe.Pointer(&in[0]), C.uint64_t(target), C.uint64_t(maxTimes), C.uint32_t(jump), unsafe.Pointer(&nonce[0]), unsafe.Pointer(&hash[0]), unsafe.Pointer(&sol[0])) 42 | return hash, bool(cFound), sol 43 | } 44 | 45 | func (vm *RxVM) Search(in []byte, target uint64, maxTimes uint64, jump uint32, nonce []byte) (hash []byte, found bool, sol []byte) { 46 | hash, found, sol = Search(vm.vm, in, target, maxTimes, jump, nonce) 47 | return 48 | } 49 | --------------------------------------------------------------------------------