@ -1,4 +1,4 @@
// Copyright (c) 2019 , The Monero Project
// Copyright (c) 2019 -2022 , The Monero Project
//
// All rights reserved.
//
@ -34,6 +34,7 @@
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <limits.h>
# include "randomx.h"
# include "c_threads.h"
@ -42,31 +43,38 @@
# define RX_LOGCAT "randomx"
static CTHR_RWLOCK_TYPE main_dataset_lock = CTHR_RWLOCK_INIT ;
static CTHR_RWLOCK_TYPE main_cache_lock = CTHR_RWLOCK_INIT ;
static randomx_dataset * main_dataset = NULL ;
static randomx_cache * main_cache = NULL ;
static char main_seedhash [ HASH_SIZE ] ;
static int main_seedhash_set = 0 ;
static CTHR_RWLOCK_TYPE secondary_cache_lock = CTHR_RWLOCK_INIT ;
static randomx_cache * secondary_cache = NULL ;
static char secondary_seedhash [ HASH_SIZE ] ;
static int secondary_seedhash_set = 0 ;
# if defined(_MSC_VER)
# define THREADV __declspec(thread)
# else
# define THREADV __thread
# endif
typedef struct rx_state {
CTHR_MUTEX_TYPE rs_mutex ;
char rs_hash [ 32 ] ;
uint64_t rs_height ;
randomx_cache * rs_cache ;
} rx_state ;
THREADV randomx_vm * main_vm_full = NULL ;
static THREADV randomx_vm * main_vm_light = NULL ;
static THREADV randomx_vm * secondary_vm_light = NULL ;
static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT ;
static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT ;
static THREADV uint32_t miner_thread = 0 ;
static rx_state rx_s [ 2 ] = { { CTHR_MUTEX_INIT , { 0 } , 0 , 0 } , { CTHR_MUTEX_INIT , { 0 } , 0 , 0 } } ;
static randomx_dataset * rx_dataset ;
static uint64_t rx_dataset_height ;
THREADV randomx_vm * rx_vm = NULL ;
static THREADV int rx_toggle ;
static bool is_main ( const char * seedhash ) { return main_seedhash_set & & ( memcmp ( seedhash , main_seedhash , HASH_SIZE ) = = 0 ) ; }
static bool is_secondary ( const char * seedhash ) { return secondary_seedhash_set & & ( memcmp ( seedhash , secondary_seedhash , HASH_SIZE ) = = 0 ) ; }
static void local_abort ( const char * msg )
{
merror ( RX_LOGCAT , " %s " , msg ) ;
fprintf ( stderr , " %s \n " , msg ) ;
# ifdef NDEBUG
_exit ( 1 ) ;
@ -75,108 +83,196 @@ static void local_abort(const char *msg)
# endif
}
/**
* @ brief uses cpuid to determine if the CPU supports the AES instructions
* @ return true if the CPU supports AES , false otherwise
*/
static void hash2hex ( const char * hash , char * hex ) {
const char * d = " 0123456789abcdef " ;
for ( int i = 0 ; i < HASH_SIZE ; + + i ) {
const uint8_t b = hash [ i ] ;
hex [ i * 2 + 0 ] = d [ b > > 4 ] ;
hex [ i * 2 + 1 ] = d [ b & 15 ] ;
}
hex [ HASH_SIZE * 2 ] = ' \0 ' ;
}
static inline int force_software_aes ( void )
{
static int use = - 1 ;
static inline int disabled_flags ( void ) {
static int flags = - 1 ;
if ( use ! = - 1 )
return use ;
if ( flags ! = - 1 ) {
return flags ;
}
const char * env = getenv ( " MONERO_ USE_SOFTWARE_AES " ) ;
const char * env = getenv ( " MONERO_ RANDOMX_UMASK " ) ;
if ( ! env ) {
use = 0 ;
}
else if ( ! strcmp ( env , " 0 " ) | | ! strcmp ( env , " no " ) ) {
use = 0 ;
flags = 0 ;
}
else {
use = 1 ;
char * endptr ;
long int value = strtol ( env , & endptr , 0 ) ;
if ( endptr ! = env & & value > = 0 & & value < INT_MAX ) {
flags = value ;
}
else {
flags = 0 ;
}
}
return use ;
return flags ;
}
static void cpuid ( int CPUInfo [ 4 ] , int InfoType )
{
# if defined(__x86_64__)
__asm __volatile__
(
" cpuid " :
" =a " ( CPUInfo [ 0 ] ) ,
" =b " ( CPUInfo [ 1 ] ) ,
" =c " ( CPUInfo [ 2 ] ) ,
" =d " ( CPUInfo [ 3 ] ) :
" a " ( InfoType ) , " c " ( 0 )
) ;
# endif
static inline int enabled_flags ( void ) {
static int flags = - 1 ;
if ( flags ! = - 1 ) {
return flags ;
}
flags = randomx_get_flags ( ) ;
return flags ;
}
static inline int check_aes_hw ( void )
# define SEEDHASH_EPOCH_BLOCKS 2048 /* Must be same as BLOCKS_SYNCHRONIZING_MAX_COUNT in cryptonote_config.h */
# define SEEDHASH_EPOCH_LAG 64
static inline int is_power_of_2 ( uint64_t n ) { return n & & ( n & ( n - 1 ) ) = = 0 ; }
static int get_seedhash_epoch_lag ( void )
{
# if defined(__x86_64__)
int cpuid_results [ 4 ] ;
static int supported = - 1 ;
static unsigned int lag = ( unsigned int ) - 1 ;
if ( lag ! = ( unsigned int ) - 1 )
return lag ;
const char * e = getenv ( " SEEDHASH_EPOCH_LAG " ) ;
if ( e )
{
lag = atoi ( e ) ;
if ( lag > SEEDHASH_EPOCH_LAG | | ! is_power_of_2 ( lag ) )
lag = SEEDHASH_EPOCH_LAG ;
}
else
{
lag = SEEDHASH_EPOCH_LAG ;
}
return lag ;
}
if ( supported > = 0 )
return supported ;
static unsigned int get_seedhash_epoch_blocks ( void )
{
static unsigned int blocks = ( unsigned int ) - 1 ;
if ( blocks ! = ( unsigned int ) - 1 )
return blocks ;
const char * e = getenv ( " SEEDHASH_EPOCH_BLOCKS " ) ;
if ( e )
{
blocks = atoi ( e ) ;
if ( blocks < 2 | | blocks > SEEDHASH_EPOCH_BLOCKS | | ! is_power_of_2 ( blocks ) )
blocks = SEEDHASH_EPOCH_BLOCKS ;
}
else
{
blocks = SEEDHASH_EPOCH_BLOCKS ;
}
return blocks ;
}
cpuid ( cpuid_results , 1 ) ;
return supported = cpuid_results [ 2 ] & ( 1 < < 25 ) ;
# else
return 0 ;
# endif
uint64_t me_rx_seedheight ( const uint64_t height ) {
const uint64_t seedhash_epoch_lag = get_seedhash_epoch_lag ( ) ;
const uint64_t seedhash_epoch_blocks = get_seedhash_epoch_blocks ( ) ;
uint64_t s_height = ( height < = seedhash_epoch_blocks + seedhash_epoch_lag ) ? 0 :
( height - seedhash_epoch_lag - 1 ) & ~ ( seedhash_epoch_blocks - 1 ) ;
return s_height ;
}
static volatile int use_rx_jit_flag = - 1 ;
void me_rx_seedheights ( const uint64_t height , uint64_t * seedheight , uint64_t * nextheight ) {
* seedheight = me_rx_seedheight ( height ) ;
* nextheight = me_rx_seedheight ( height + get_seedhash_epoch_lag ( ) ) ;
}
static inline int use_rx_jit ( void )
static void rx_alloc_dataset ( randomx_flags flags , randomx_dataset * * dataset , int ignore_env )
{
# if defined(__x86_64__)
if ( use_rx_jit_flag ! = - 1 )
return use_rx_jit_flag ;
if ( * dataset ) {
return ;
}
const char * env = getenv ( " MONERO_USE_RX_JIT " ) ;
if ( ! env ) {
use_rx_jit_flag = 1 ;
if ( disabled_flags ( ) & RANDOMX_FLAG_FULL_MEM ) {
static int shown = 0 ;
if ( ! shown ) {
shown = 1 ;
minfo ( RX_LOGCAT , " RandomX dataset is disabled by MONERO_RANDOMX_UMASK environment variable. " ) ;
}
return ;
}
else if ( ! strcmp ( env , " 0 " ) | | ! strcmp ( env , " no " ) ) {
use_rx_jit_flag = 0 ;
if ( ! ignore_env & & ! getenv ( " MONERO_RANDOMX_FULL_MEM " ) ) {
static int shown = 0 ;
if ( ! shown ) {
shown = 1 ;
minfo ( RX_LOGCAT , " RandomX dataset is not enabled by default. Use MONERO_RANDOMX_FULL_MEM environment variable to enable it. " ) ;
}
return ;
}
else {
use_rx_jit_flag = 1 ;
* dataset = randomx_alloc_dataset ( ( flags | RANDOMX_FLAG_LARGE_PAGES ) & ~ disabled_flags ( ) ) ;
if ( ! * dataset ) {
mwarning ( RX_LOGCAT , " Couldn't allocate RandomX dataset using large pages " ) ;
* dataset = randomx_alloc_dataset ( flags & ~ disabled_flags ( ) ) ;
if ( ! * dataset ) {
merror ( RX_LOGCAT , " Couldn't allocate RandomX dataset " ) ;
}
}
return use_rx_jit_flag ;
# else
return 0 ;
# endif
}
# define SEEDHASH_EPOCH_BLOCKS 2048 /* Must be same as BLOCKS_SYNCHRONIZING_MAX_COUNT in cryptonote_config.h */
# define SEEDHASH_EPOCH_LAG 64
static void rx_alloc_cache ( randomx_flags flags , randomx_cache * * cache )
{
if ( * cache ) {
return ;
}
void me_rx_reorg ( const uint64_t split_height ) {
int i ;
CTHR_MUTEX_LOCK ( rx_mutex ) ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( split_height < rx_s [ i ] . rs_height )
rx_s [ i ] . rs_height = 1 ; /* set to an invalid seed height */
* cache = randomx_alloc_cache ( ( flags | RANDOMX_FLAG_LARGE_PAGES ) & ~ disabled_flags ( ) ) ;
if ( ! * cache ) {
mwarning ( RX_LOGCAT , " Couldn't allocate RandomX cache using large pages " ) ;
* cache = randomx_alloc_cache ( flags & ~ disabled_flags ( ) ) ;
if ( ! * cache ) local_abort ( " Couldn't allocate RandomX cache " ) ;
}
CTHR_MUTEX_UNLOCK ( rx_mutex ) ;
}
uint64_t me_rx_seedheight ( const uint64_t height ) {
uint64_t s_height = ( height < = SEEDHASH_EPOCH_BLOCKS + SEEDHASH_EPOCH_LAG ) ? 0 :
( height - SEEDHASH_EPOCH_LAG - 1 ) & ~ ( SEEDHASH_EPOCH_BLOCKS - 1 ) ;
return s_height ;
static void rx_init_full_vm ( randomx_flags flags , randomx_vm * * vm )
{
if ( * vm | | ! main_dataset | | ( disabled_flags ( ) & RANDOMX_FLAG_FULL_MEM ) ) {
return ;
}
if ( ( flags & RANDOMX_FLAG_JIT ) & & ! miner_thread ) {
flags | = RANDOMX_FLAG_SECURE ;
}
* vm = randomx_create_vm ( ( flags | RANDOMX_FLAG_LARGE_PAGES | RANDOMX_FLAG_FULL_MEM ) & ~ disabled_flags ( ) , NULL , main_dataset ) ;
if ( ! * vm ) {
mwarning ( RX_LOGCAT , " Couldn't allocate RandomX full VM using large pages " ) ;
* vm = randomx_create_vm ( ( flags | RANDOMX_FLAG_FULL_MEM ) & ~ disabled_flags ( ) , NULL , main_dataset ) ;
if ( ! * vm ) {
merror ( RX_LOGCAT , " Couldn't allocate RandomX full VM " ) ;
}
}
}
void me_rx_seedheights ( const uint64_t height , uint64_t * seedheight , uint64_t * nextheight ) {
* seedheight = me_rx_seedheight ( height ) ;
* nextheight = me_rx_seedheight ( height + SEEDHASH_EPOCH_LAG ) ;
static void rx_init_light_vm ( randomx_flags flags , randomx_vm * * vm , randomx_cache * cache )
{
if ( * vm ) {
randomx_vm_set_cache ( * vm , cache ) ;
return ;
}
if ( ( flags & RANDOMX_FLAG_JIT ) & & ! miner_thread ) {
flags | = RANDOMX_FLAG_SECURE ;
}
flags & = ~ RANDOMX_FLAG_FULL_MEM ;
* vm = randomx_create_vm ( ( flags | RANDOMX_FLAG_LARGE_PAGES ) & ~ disabled_flags ( ) , cache , NULL ) ;
if ( ! * vm ) {
mwarning ( RX_LOGCAT , " Couldn't allocate RandomX light VM using large pages " ) ;
* vm = randomx_create_vm ( flags & ~ disabled_flags ( ) , cache , NULL ) ;
if ( ! * vm ) local_abort ( " Couldn't allocate RandomX light VM " ) ;
}
}
typedef struct seedinfo {
@ -185,165 +281,232 @@ typedef struct seedinfo {
unsigned long si_count ;
} seedinfo ;
static CTHR_THREAD_RTYPE rx_seedthread( void * arg ) {
static CTHR_THREAD_RTYPE me_ rx_seedthread( void * arg ) {
seedinfo * si = arg ;
randomx_init_dataset ( rx _dataset, si - > si_cache , si - > si_start , si - > si_count ) ;
randomx_init_dataset ( main _dataset, si - > si_cache , si - > si_start , si - > si_count ) ;
CTHR_THREAD_RETURN ;
}
static void rx_initdata ( randomx_cache * rs_cache , const int miners , const uint64_t seedheight ) {
if ( miners > 1 ) {
unsigned long delta = randomx_dataset_item_count ( ) / miners ;
unsigned long start = 0 ;
int i ;
seedinfo * si ;
CTHR_THREAD_TYPE * st ;
si = malloc ( miners * sizeof ( seedinfo ) ) ;
if ( si = = NULL )
local_abort ( " Couldn't allocate RandomX mining threadinfo " ) ;
st = malloc ( miners * sizeof ( CTHR_THREAD_TYPE ) ) ;
if ( st = = NULL ) {
free ( si ) ;
local_abort ( " Couldn't allocate RandomX mining threadlist " ) ;
}
for ( i = 0 ; i < miners - 1 ; i + + ) {
si [ i ] . si_cache = rs_cache ;
si [ i ] . si_start = start ;
si [ i ] . si_count = delta ;
start + = delta ;
}
si [ i ] . si_cache = rs_cache ;
static void me_rx_init_dataset ( size_t max_threads ) {
if ( ! main_dataset ) {
return ;
}
// leave 2 CPU cores for other tasks
const size_t num_threads = ( max_threads < 4 ) ? 1 : ( max_threads - 2 ) ;
seedinfo * si = malloc ( num_threads * sizeof ( seedinfo ) ) ;
if ( ! si ) local_abort ( " Couldn't allocate RandomX mining threadinfo " ) ;
const uint32_t delta = randomx_dataset_item_count ( ) / num_threads ;
uint32_t start = 0 ;
const size_t n1 = num_threads - 1 ;
for ( size_t i = 0 ; i < n1 ; + + i ) {
si [ i ] . si_cache = main_cache ;
si [ i ] . si_start = start ;
si [ i ] . si_count = randomx_dataset_item_count ( ) - start ;
for ( i = 1 ; i < miners ; i + + ) {
CTHR_THREAD_CREATE ( st [ i ] , rx_seedthread , & si [ i ] ) ;
}
randomx_init_dataset ( rx_dataset , rs_cache , 0 , si [ 0 ] . si_count ) ;
for ( i = 1 ; i < miners ; i + + ) {
CTHR_THREAD_JOIN ( st [ i ] ) ;
si [ i ] . si_count = delta ;
start + = delta ;
}
si [ n1 ] . si_cache = main_cache ;
si [ n1 ] . si_start = start ;
si [ n1 ] . si_count = randomx_dataset_item_count ( ) - start ;
CTHR_THREAD_TYPE * st = malloc ( num_threads * sizeof ( CTHR_THREAD_TYPE ) ) ;
if ( ! st ) local_abort ( " Couldn't allocate RandomX mining threadlist " ) ;
CTHR_RWLOCK_LOCK_READ ( main_cache_lock ) ;
for ( size_t i = 0 ; i < n1 ; + + i ) {
if ( ! CTHR_THREAD_CREATE ( st [ i ] , me_rx_seedthread , & si [ i ] ) ) {
local_abort ( " Couldn't start RandomX seed thread " ) ;
}
free ( st ) ;
free ( si ) ;
} else {
randomx_init_dataset ( rx_dataset , rs_cache , 0 , randomx_dataset_item_count ( ) ) ;
}
rx_dataset_height = seedheight ;
me_rx_seedthread ( & si [ n1 ] ) ;
for ( size_t i = 0 ; i < n1 ; + + i ) CTHR_THREAD_JOIN ( st [ i ] ) ;
CTHR_RWLOCK_UNLOCK_READ ( main_cache_lock ) ;
free ( st ) ;
free ( si ) ;
minfo ( RX_LOGCAT , " RandomX dataset initialized " ) ;
}
void me_rx_slow_hash ( const uint64_t mainheight , const uint64_t seedheight , const char * seedhash , const void * data , size_t length ,
char * hash , int miners , int is_alt ) {
uint64_t s_height = me_rx_seedheight ( mainheight ) ;
int changed = 0 ;
int toggle = is_alt ? s_height : seedheight ;
randomx_flags flags = RANDOMX_FLAG_DEFAULT ;
rx_state * rx_sp ;
randomx_cache * cache ;
toggle = ( toggle & SEEDHASH_EPOCH_BLOCKS ) ! = 0 ;
CTHR_MUTEX_LOCK ( rx_mutex ) ;
/* if alt block but with same seed as mainchain, no need for alt cache */
if ( is_alt & & s_height = = seedheight & & ! memcmp ( rx_s [ toggle ] . rs_hash , seedhash , sizeof ( rx_s [ toggle ] . rs_hash ) ) )
is_alt = 0 ;
/* RPC could request an earlier block on mainchain */
if ( ! is_alt & & s_height > seedheight )
is_alt = 1 ;
toggle ^ = ( is_alt ! = 0 ) ;
if ( toggle ! = rx_toggle )
changed = 1 ;
rx_toggle = toggle ;
rx_sp = & rx_s [ toggle ] ;
CTHR_MUTEX_LOCK ( rx_sp - > rs_mutex ) ;
CTHR_MUTEX_UNLOCK ( rx_mutex ) ;
cache = rx_sp - > rs_cache ;
if ( cache = = NULL ) {
if ( use_rx_jit ( ) )
flags | = RANDOMX_FLAG_JIT ;
if ( cache = = NULL ) {
cache = randomx_alloc_cache ( flags | RANDOMX_FLAG_LARGE_PAGES ) ;
if ( cache = = NULL ) {
mdebug ( RX_LOGCAT , " Couldn't use largePages for RandomX cache " ) ;
cache = randomx_alloc_cache ( flags ) ;
}
if ( cache = = NULL )
local_abort ( " Couldn't allocate RandomX cache " ) ;
}
typedef struct thread_info {
char seedhash [ HASH_SIZE ] ;
size_t max_threads ;
} thread_info ;
static CTHR_THREAD_RTYPE me_rx_set_main_seedhash_thread ( void * arg ) {
thread_info * info = arg ;
CTHR_RWLOCK_LOCK_WRITE ( main_dataset_lock ) ;
CTHR_RWLOCK_LOCK_WRITE ( main_cache_lock ) ;
// Double check that seedhash wasn't already updated
if ( is_main ( info - > seedhash ) ) {
CTHR_RWLOCK_UNLOCK_WRITE ( main_cache_lock ) ;
CTHR_RWLOCK_UNLOCK_WRITE ( main_dataset_lock ) ;
free ( info ) ;
CTHR_THREAD_RETURN ;
}
if ( rx_sp - > rs_height ! = seedheight | | rx_sp - > rs_cache = = NULL | | memcmp ( seedhash , rx_sp - > rs_hash , sizeof ( rx_sp - > rs_hash ) ) ) {
randomx_init_cache ( cache , seedhash , 32 ) ;
rx_sp - > rs_cache = cache ;
rx_sp - > rs_height = seedheight ;
memcpy ( rx_sp - > rs_hash , seedhash , sizeof ( rx_sp - > rs_hash ) ) ;
changed = 1 ;
}
if ( rx_vm = = NULL ) {
randomx_flags flags = RANDOMX_FLAG_DEFAULT ;
if ( use_rx_jit ( ) ) {
flags | = RANDOMX_FLAG_JIT ;
if ( ! miners )
flags | = RANDOMX_FLAG_SECURE ;
}
if ( ! force_software_aes ( ) & & check_aes_hw ( ) )
flags | = RANDOMX_FLAG_HARD_AES ;
if ( miners ) {
CTHR_MUTEX_LOCK ( rx_dataset_mutex ) ;
if ( rx_dataset = = NULL ) {
rx_dataset = randomx_alloc_dataset ( RANDOMX_FLAG_LARGE_PAGES ) ;
if ( rx_dataset = = NULL ) {
mdebug ( RX_LOGCAT , " Couldn't use largePages for RandomX dataset " ) ;
rx_dataset = randomx_alloc_dataset ( RANDOMX_FLAG_DEFAULT ) ;
memcpy ( main_seedhash , info - > seedhash , HASH_SIZE ) ;
main_seedhash_set = 1 ;
char buf [ HASH_SIZE * 2 + 1 ] ;
hash2hex ( main_seedhash , buf ) ;
minfo ( RX_LOGCAT , " RandomX new main seed hash is %s " , buf ) ;
const randomx_flags flags = enabled_flags ( ) & ~ disabled_flags ( ) ;
rx_alloc_dataset ( flags , & main_dataset , 0 ) ;
rx_alloc_cache ( flags , & main_cache ) ;
randomx_init_cache ( main_cache , info - > seedhash , HASH_SIZE ) ;
minfo ( RX_LOGCAT , " RandomX main cache initialized " ) ;
CTHR_RWLOCK_UNLOCK_WRITE ( main_cache_lock ) ;
// From this point, rx_slow_hash can calculate hashes in light mode, but dataset is not initialized yet
me_rx_init_dataset ( info - > max_threads ) ;
CTHR_RWLOCK_UNLOCK_WRITE ( main_dataset_lock ) ;
free ( info ) ;
CTHR_THREAD_RETURN ;
}
void me_rx_set_main_seedhash ( const char * seedhash , size_t max_dataset_init_threads ) {
// Early out if seedhash didn't change
if ( is_main ( seedhash ) ) {
return ;
}
// Update main cache and dataset in the background
thread_info * info = malloc ( sizeof ( thread_info ) ) ;
if ( ! info ) local_abort ( " Couldn't allocate RandomX mining threadinfo " ) ;
memcpy ( info - > seedhash , seedhash , HASH_SIZE ) ;
info - > max_threads = max_dataset_init_threads ;
CTHR_THREAD_TYPE t ;
if ( ! CTHR_THREAD_CREATE ( t , me_rx_set_main_seedhash_thread , info ) ) {
local_abort ( " Couldn't start RandomX seed thread " ) ;
}
}
void me_rx_slow_hash ( const char * seedhash , const void * data , size_t length , char * result_hash ) {
const randomx_flags flags = enabled_flags ( ) & ~ disabled_flags ( ) ;
int success = 0 ;
// Fast path (seedhash == main_seedhash)
// Multiple threads can run in parallel in fast or light mode, 1-2 ms or 10-15 ms per hash per thread
if ( is_main ( seedhash ) ) {
// If CTHR_RWLOCK_TRYLOCK_READ fails it means dataset is being initialized now, so use the light mode
if ( main_dataset & & CTHR_RWLOCK_TRYLOCK_READ ( main_dataset_lock ) ) {
// Double check that main_seedhash didn't change
if ( is_main ( seedhash ) ) {
rx_init_full_vm ( flags , & main_vm_full ) ;
if ( main_vm_full ) {
randomx_calculate_hash ( main_vm_full , data , length , result_hash ) ;
success = 1 ;
}
if ( rx_dataset ! = NULL )
rx_initdata ( rx_sp - > rs_cache , miners , seedheight ) ;
}
if ( rx_dataset ! = NULL )
flags | = RANDOMX_FLAG_FULL_MEM ;
else {
miners = 0 ;
mwarning ( RX_LOGCAT , " Couldn't allocate RandomX dataset for miner " ) ;
CTHR_RWLOCK_UNLOCK_READ ( main_dataset_lock ) ;
} else {
CTHR_RWLOCK_LOCK_READ ( main_cache_lock ) ;
// Double check that main_seedhash didn't change
if ( is_main ( seedhash ) ) {
rx_init_light_vm ( flags , & main_vm_light , main_cache ) ;
randomx_calculate_hash ( main_vm_light , data , length , result_hash ) ;
success = 1 ;
}
CTHR_MUTEX_UNLOCK ( rx_dataset_mutex ) ;
}
rx_vm = randomx_create_vm ( flags | RANDOMX_FLAG_LARGE_PAGES , rx_sp - > rs_cache , rx_dataset ) ;
if ( rx_vm = = NULL ) { //large pages failed
mdebug ( RX_LOGCAT , " Couldn't use largePages for RandomX VM " ) ;
rx_vm = randomx_create_vm ( flags , rx_sp - > rs_cache , rx_dataset ) ;
CTHR_RWLOCK_UNLOCK_READ ( main_cache_lock ) ;
}
if ( rx_vm = = NULL ) { //fallback if everything fails
flags = RANDOMX_FLAG_DEFAULT | ( miners ? RANDOMX_FLAG_FULL_MEM : 0 ) ;
rx_vm = randomx_create_vm ( flags , rx_sp - > rs_cache , rx_dataset ) ;
}
if ( success ) {
return ;
}
char buf [ HASH_SIZE * 2 + 1 ] ;
// Slow path (seedhash != main_seedhash, but seedhash == secondary_seedhash)
// Multiple threads can run in parallel in light mode, 10-15 ms per hash per thread
if ( ! secondary_cache ) {
CTHR_RWLOCK_LOCK_WRITE ( secondary_cache_lock ) ;
if ( ! secondary_cache ) {
hash2hex ( seedhash , buf ) ;
minfo ( RX_LOGCAT , " RandomX new secondary seed hash is %s " , buf ) ;
rx_alloc_cache ( flags , & secondary_cache ) ;
randomx_init_cache ( secondary_cache , seedhash , HASH_SIZE ) ;
minfo ( RX_LOGCAT , " RandomX secondary cache updated " ) ;
memcpy ( secondary_seedhash , seedhash , HASH_SIZE ) ;
secondary_seedhash_set = 1 ;
}
if ( rx_vm = = NULL )
local_abort ( " Couldn't allocate RandomX VM " ) ;
} else if ( miners ) {
CTHR_MUTEX_LOCK ( rx_dataset_mutex ) ;
if ( rx_dataset ! = NULL & & rx_dataset_height ! = seedheight )
rx_initdata ( cache , miners , seedheight ) ;
CTHR_MUTEX_UNLOCK ( rx_dataset_mutex ) ;
} else if ( changed ) {
randomx_vm_set_cache ( rx_vm , rx_sp - > rs_cache ) ;
}
/* mainchain users can run in parallel */
if ( ! is_alt )
CTHR_MUTEX_UNLOCK ( rx_sp - > rs_mutex ) ;
randomx_calculate_hash ( rx_vm , data , length , hash ) ;
/* altchain slot users always get fully serialized */
if ( is_alt )
CTHR_MUTEX_UNLOCK ( rx_sp - > rs_mutex ) ;
}
CTHR_RWLOCK_UNLOCK_WRITE ( secondary_cache_lock ) ;
}
CTHR_RWLOCK_LOCK_READ ( secondary_cache_lock ) ;
if ( is_secondary ( seedhash ) ) {
rx_init_light_vm ( flags , & secondary_vm_light , secondary_cache ) ;
randomx_calculate_hash ( secondary_vm_light , data , length , result_hash ) ;
success = 1 ;
}
CTHR_RWLOCK_UNLOCK_READ ( secondary_cache_lock ) ;
void me_rx_slow_hash_allocate_state ( void ) {
if ( success ) {
return ;
}
// Slowest path (seedhash != main_seedhash, seedhash != secondary_seedhash)
// Only one thread runs at a time and updates secondary_seedhash if needed, up to 200-500 ms per hash
CTHR_RWLOCK_LOCK_WRITE ( secondary_cache_lock ) ;
if ( ! is_secondary ( seedhash ) ) {
hash2hex ( seedhash , buf ) ;
minfo ( RX_LOGCAT , " RandomX new secondary seed hash is %s " , buf ) ;
randomx_init_cache ( secondary_cache , seedhash , HASH_SIZE ) ;
minfo ( RX_LOGCAT , " RandomX secondary cache updated " ) ;
memcpy ( secondary_seedhash , seedhash , HASH_SIZE ) ;
secondary_seedhash_set = 1 ;
}
rx_init_light_vm ( flags , & secondary_vm_light , secondary_cache ) ;
randomx_calculate_hash ( secondary_vm_light , data , length , result_hash ) ;
CTHR_RWLOCK_UNLOCK_WRITE ( secondary_cache_lock ) ;
}
void me_rx_slow_hash_free_state ( void ) {
if ( rx_vm ! = NULL ) {
randomx_destroy_vm ( rx_vm ) ;
rx_vm = NULL ;
void me_rx_set_miner_thread ( uint32_t value , size_t max_dataset_init_threads ) {
miner_thread = value ;
// If dataset is not allocated yet, try to allocate and initialize it
CTHR_RWLOCK_LOCK_WRITE ( main_dataset_lock ) ;
if ( main_dataset ) {
CTHR_RWLOCK_UNLOCK_WRITE ( main_dataset_lock ) ;
return ;
}
const randomx_flags flags = enabled_flags ( ) & ~ disabled_flags ( ) ;
rx_alloc_dataset ( flags , & main_dataset , 1 ) ;
me_rx_init_dataset ( max_dataset_init_threads ) ;
CTHR_RWLOCK_UNLOCK_WRITE ( main_dataset_lock ) ;
}
uint32_t me_rx_get_miner_thread ( ) {
return miner_thread ;
}
void me_rx_slow_hash_allocate_state ( ) { }
static void me_rx_destroy_vm ( randomx_vm * * vm ) {
if ( * vm ) {
randomx_destroy_vm ( * vm ) ;
* vm = NULL ;
}
}
void me_rx_slow_hash_free_state ( ) {
me_rx_destroy_vm ( & main_vm_full ) ;
me_rx_destroy_vm ( & main_vm_light ) ;
me_rx_destroy_vm ( & secondary_vm_light ) ;
}