Merge pull request #281 from moneroexamples/devel

Devel
master
moneroexamples 2 years ago committed by GitHub
commit 6c5d7b17c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -72,7 +72,6 @@ The key features of the Onion Monero Blockchain Explorer are:
- can provide total amount of all miner fees, - can provide total amount of all miner fees,
- decoding encrypted payment id, - decoding encrypted payment id,
- decoding outputs and proving txs sent to sub-address. - decoding outputs and proving txs sent to sub-address.
- listing RandomX code for each block
## Development branch ## Development branch
@ -244,7 +243,6 @@ xmrblocks, Onion Monero Blockchain Explorer:
-t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain -t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain
-s [ --stagenet ] [=arg(=1)] (=0) use stagenet blockchain -s [ --stagenet ] [=arg(=1)] (=0) use stagenet blockchain
--enable-pusher [=arg(=1)] (=0) enable signed transaction pusher --enable-pusher [=arg(=1)] (=0) enable signed transaction pusher
--enable-randomx [=arg(=1)] (=0) enable generation of randomx code
--enable-mixin-details [=arg(=1)] (=0) --enable-mixin-details [=arg(=1)] (=0)
enable mixin details for key images, enable mixin details for key images,
e.g., timescale, mixin of mixins, in tx e.g., timescale, mixin of mixins, in tx

@ -105,6 +105,12 @@ main(int ac, const char* av[])
bool enable_as_hex {*enable_as_hex_opt}; bool enable_as_hex {*enable_as_hex_opt};
bool enable_emission_monitor {*enable_emission_monitor_opt}; bool enable_emission_monitor {*enable_emission_monitor_opt};
//temprorary disable randomx
if (enable_randomx == true) {
cout << "Support for randomx code is disabled due to issues with it"<< endl;
enable_randomx = false;
}
// set monero log output level // set monero log output level
uint32_t log_level = 0; uint32_t log_level = 0;
mlog_configure("", true); mlog_configure("", true);

@ -1,4 +1,4 @@
// Copyright (c) 2019, The Monero Project // Copyright (c) 2019-2022, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -34,6 +34,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <limits.h>
#include "randomx.h" #include "randomx.h"
#include "c_threads.h" #include "c_threads.h"
@ -42,31 +43,38 @@
#define RX_LOGCAT "randomx" #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) #if defined(_MSC_VER)
#define THREADV __declspec(thread) #define THREADV __declspec(thread)
#else #else
#define THREADV __thread #define THREADV __thread
#endif #endif
typedef struct rx_state { THREADV randomx_vm *main_vm_full = NULL;
CTHR_MUTEX_TYPE rs_mutex; static THREADV randomx_vm *main_vm_light = NULL;
char rs_hash[32]; static THREADV randomx_vm *secondary_vm_light = NULL;
uint64_t rs_height;
randomx_cache *rs_cache;
} rx_state;
static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT;
static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT;
static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}}; static THREADV uint32_t miner_thread = 0;
static randomx_dataset *rx_dataset; static bool is_main(const char* seedhash) { return main_seedhash_set && (memcmp(seedhash, main_seedhash, HASH_SIZE) == 0); }
static uint64_t rx_dataset_height; static bool is_secondary(const char* seedhash) { return secondary_seedhash_set && (memcmp(seedhash, secondary_seedhash, HASH_SIZE) == 0); }
THREADV randomx_vm *rx_vm = NULL;
static THREADV int rx_toggle;
static void local_abort(const char *msg) static void local_abort(const char *msg)
{ {
merror(RX_LOGCAT, "%s", msg);
fprintf(stderr, "%s\n", msg); fprintf(stderr, "%s\n", msg);
#ifdef NDEBUG #ifdef NDEBUG
_exit(1); _exit(1);
@ -75,108 +83,196 @@ static void local_abort(const char *msg)
#endif #endif
} }
/** static void hash2hex(const char* hash, char* hex) {
* @brief uses cpuid to determine if the CPU supports the AES instructions const char* d = "0123456789abcdef";
* @return true if the CPU supports AES, false otherwise 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 inline int disabled_flags(void) {
{ static int flags = -1;
static int use = -1;
if (use != -1) if (flags != -1) {
return use; return flags;
}
const char *env = getenv("MONERO_USE_SOFTWARE_AES"); const char *env = getenv("MONERO_RANDOMX_UMASK");
if (!env) { if (!env) {
use = 0; flags = 0;
} }
else if (!strcmp(env, "0") || !strcmp(env, "no")) { else {
use = 0; char* endptr;
long int value = strtol(env, &endptr, 0);
if (endptr != env && value >= 0 && value < INT_MAX) {
flags = value;
} }
else { else {
use = 1; flags = 0;
}
}
return flags;
} }
return use;
static inline int enabled_flags(void) {
static int flags = -1;
if (flags != -1) {
return flags;
} }
static void cpuid(int CPUInfo[4], int InfoType) flags = randomx_get_flags();
return flags;
}
#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__) static unsigned int lag = (unsigned int)-1;
__asm __volatile__ if (lag != (unsigned int)-1)
( return lag;
"cpuid": const char *e = getenv("SEEDHASH_EPOCH_LAG");
"=a" (CPUInfo[0]), if (e)
"=b" (CPUInfo[1]), {
"=c" (CPUInfo[2]), lag = atoi(e);
"=d" (CPUInfo[3]) : if (lag > SEEDHASH_EPOCH_LAG || !is_power_of_2(lag))
"a" (InfoType), "c" (0) lag = SEEDHASH_EPOCH_LAG;
);
#endif
} }
static inline int check_aes_hw(void) else
{ {
#if defined(__x86_64__) lag = SEEDHASH_EPOCH_LAG;
int cpuid_results[4]; }
static int supported = -1; return lag;
}
if(supported >= 0) static unsigned int get_seedhash_epoch_blocks(void)
return supported; {
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); uint64_t me_rx_seedheight(const uint64_t height) {
return supported = cpuid_results[2] & (1 << 25); const uint64_t seedhash_epoch_lag = get_seedhash_epoch_lag();
#else const uint64_t seedhash_epoch_blocks = get_seedhash_epoch_blocks();
return 0; uint64_t s_height = (height <= seedhash_epoch_blocks+seedhash_epoch_lag) ? 0 :
#endif (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 (*dataset) {
return;
}
if (use_rx_jit_flag != -1) if (disabled_flags() & RANDOMX_FLAG_FULL_MEM) {
return use_rx_jit_flag; static int shown = 0;
if (!shown) {
shown = 1;
minfo(RX_LOGCAT, "RandomX dataset is disabled by MONERO_RANDOMX_UMASK environment variable.");
}
return;
}
const char *env = getenv("MONERO_USE_RX_JIT"); if (!ignore_env && !getenv("MONERO_RANDOMX_FULL_MEM")) {
if (!env) { static int shown = 0;
use_rx_jit_flag = 1; if (!shown) {
shown = 1;
minfo(RX_LOGCAT, "RandomX dataset is not enabled by default. Use MONERO_RANDOMX_FULL_MEM environment variable to enable it.");
} }
else if (!strcmp(env, "0") || !strcmp(env, "no")) { return;
use_rx_jit_flag = 0; }
*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");
} }
else {
use_rx_jit_flag = 1;
} }
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 */ static void rx_alloc_cache(randomx_flags flags, randomx_cache** cache)
#define SEEDHASH_EPOCH_LAG 64 {
if (*cache) {
return;
}
void me_rx_reorg(const uint64_t split_height) { *cache = randomx_alloc_cache((flags | RANDOMX_FLAG_LARGE_PAGES) & ~disabled_flags());
int i; if (!*cache) {
CTHR_MUTEX_LOCK(rx_mutex); mwarning(RX_LOGCAT, "Couldn't allocate RandomX cache using large pages");
for (i=0; i<2; i++) { *cache = randomx_alloc_cache(flags & ~disabled_flags());
if (split_height < rx_s[i].rs_height) if (!*cache) local_abort("Couldn't allocate RandomX cache");
rx_s[i].rs_height = 1; /* set to an invalid seed height */
} }
CTHR_MUTEX_UNLOCK(rx_mutex);
} }
uint64_t me_rx_seedheight(const uint64_t height) { static void rx_init_full_vm(randomx_flags flags, randomx_vm** vm)
uint64_t s_height = (height <= SEEDHASH_EPOCH_BLOCKS+SEEDHASH_EPOCH_LAG) ? 0 : {
(height - SEEDHASH_EPOCH_LAG - 1) & ~(SEEDHASH_EPOCH_BLOCKS-1); if (*vm || !main_dataset || (disabled_flags() & RANDOMX_FLAG_FULL_MEM)) {
return s_height; return;
} }
void me_rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) { if ((flags & RANDOMX_FLAG_JIT) && !miner_thread) {
*seedheight = me_rx_seedheight(height); flags |= RANDOMX_FLAG_SECURE;
*nextheight = me_rx_seedheight(height + SEEDHASH_EPOCH_LAG); }
*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");
}
}
}
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 { typedef struct seedinfo {
@ -185,165 +281,232 @@ typedef struct seedinfo {
unsigned long si_count; unsigned long si_count;
} seedinfo; } seedinfo;
static CTHR_THREAD_RTYPE rx_seedthread(void *arg) { static CTHR_THREAD_RTYPE me_rx_seedthread(void *arg) {
seedinfo *si = 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; CTHR_THREAD_RETURN;
} }
static void rx_initdata(randomx_cache *rs_cache, const int miners, const uint64_t seedheight) { static void me_rx_init_dataset(size_t max_threads) {
if (miners > 1) { if (!main_dataset) {
unsigned long delta = randomx_dataset_item_count() / miners; return;
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; // 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_start = start;
si[i].si_count = delta; si[i].si_count = delta;
start += delta; start += delta;
} }
si[i].si_cache = rs_cache;
si[i].si_start = start; si[n1].si_cache = main_cache;
si[i].si_count = randomx_dataset_item_count() - start; si[n1].si_start = start;
for (i=1; i<miners; i++) { si[n1].si_count = randomx_dataset_item_count() - start;
CTHR_THREAD_CREATE(st[i], rx_seedthread, &si[i]);
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");
} }
randomx_init_dataset(rx_dataset, rs_cache, 0, si[0].si_count);
for (i=1; i<miners; i++) {
CTHR_THREAD_JOIN(st[i]);
} }
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(st);
free(si); free(si);
minfo(RX_LOGCAT, "RandomX dataset initialized");
}
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;
}
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;
}
}
CTHR_RWLOCK_UNLOCK_READ(main_dataset_lock);
} else { } else {
randomx_init_dataset(rx_dataset, rs_cache, 0, randomx_dataset_item_count()); CTHR_RWLOCK_LOCK_READ(main_cache_lock);
} // Double check that main_seedhash didn't change
rx_dataset_height = seedheight; if (is_main(seedhash)) {
} rx_init_light_vm(flags, &main_vm_light, main_cache);
randomx_calculate_hash(main_vm_light, data, length, result_hash);
void me_rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length, success = 1;
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");
}
}
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()) CTHR_RWLOCK_UNLOCK_READ(main_cache_lock);
flags |= RANDOMX_FLAG_HARD_AES; }
if (miners) { }
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (rx_dataset == NULL) { if (success) {
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); return;
if (rx_dataset == NULL) { }
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT); char buf[HASH_SIZE * 2 + 1];
}
if (rx_dataset != NULL) // Slow path (seedhash != main_seedhash, but seedhash == secondary_seedhash)
rx_initdata(rx_sp->rs_cache, miners, seedheight); // Multiple threads can run in parallel in light mode, 10-15 ms per hash per thread
} if (!secondary_cache) {
if (rx_dataset != NULL) CTHR_RWLOCK_LOCK_WRITE(secondary_cache_lock);
flags |= RANDOMX_FLAG_FULL_MEM; if (!secondary_cache) {
else { hash2hex(seedhash, buf);
miners = 0; minfo(RX_LOGCAT, "RandomX new secondary seed hash is %s", buf);
mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
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;
} }
CTHR_MUTEX_UNLOCK(rx_dataset_mutex); CTHR_RWLOCK_UNLOCK_WRITE(secondary_cache_lock);
} }
rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset);
if(rx_vm == NULL) { //large pages failed CTHR_RWLOCK_LOCK_READ(secondary_cache_lock);
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM"); if (is_secondary(seedhash)) {
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset); rx_init_light_vm(flags, &secondary_vm_light, secondary_cache);
randomx_calculate_hash(secondary_vm_light, data, length, result_hash);
success = 1;
} }
if(rx_vm == NULL) {//fallback if everything fails CTHR_RWLOCK_UNLOCK_READ(secondary_cache_lock);
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;
} }
if (rx_vm == NULL)
local_abort("Couldn't allocate RandomX VM"); // Slowest path (seedhash != main_seedhash, seedhash != secondary_seedhash)
} else if (miners) { // Only one thread runs at a time and updates secondary_seedhash if needed, up to 200-500 ms per hash
CTHR_MUTEX_LOCK(rx_dataset_mutex); CTHR_RWLOCK_LOCK_WRITE(secondary_cache_lock);
if (rx_dataset != NULL && rx_dataset_height != seedheight) if (!is_secondary(seedhash)) {
rx_initdata(cache, miners, seedheight); hash2hex(seedhash, buf);
CTHR_MUTEX_UNLOCK(rx_dataset_mutex); minfo(RX_LOGCAT, "RandomX new secondary seed hash is %s", buf);
} else if (changed) {
randomx_vm_set_cache(rx_vm, rx_sp->rs_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;
} }
/* mainchain users can run in parallel */ rx_init_light_vm(flags, &secondary_vm_light, secondary_cache);
if (!is_alt) randomx_calculate_hash(secondary_vm_light, data, length, result_hash);
CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex); CTHR_RWLOCK_UNLOCK_WRITE(secondary_cache_lock);
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);
} }
void me_rx_slow_hash_allocate_state(void) { 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;
} }
void me_rx_slow_hash_free_state(void) { const randomx_flags flags = enabled_flags() & ~disabled_flags();
if (rx_vm != NULL) { rx_alloc_dataset(flags, &main_dataset, 1);
randomx_destroy_vm(rx_vm); me_rx_init_dataset(max_dataset_init_threads);
rx_vm = NULL;
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);
}

@ -42,7 +42,7 @@ extern "C" void me_rx_slow_hash(const uint64_t mainheight, const uint64_t seedhe
char *hash, int miners, int is_alt); char *hash, int miners, int is_alt);
//extern "C" void me_rx_reorg(const uint64_t split_height); //extern "C" void me_rx_reorg(const uint64_t split_height);
extern __thread randomx_vm *rx_vm; extern __thread randomx_vm *main_vm_full;
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
@ -5695,17 +5695,29 @@ json_networkinfo()
return j_response; return j_response;
} }
uint64_t fee_estimated {0}; uint64_t per_kb_fee_estimated {0};
// get dynamic fee estimate from last 10 blocks // get dynamic fee estimate from last 10 blocks
if (!get_dynamic_per_kb_fee_estimate(fee_estimated)) if (!get_dynamic_per_kb_fee_estimate(per_kb_fee_estimated))
{ {
j_response["status"] = "error"; j_response["status"] = "error";
j_response["message"] = "Cant get dynamic fee esimate"; j_response["message"] = "Cant get per kb dynamic fee esimate";
return j_response; return j_response;
} }
j_info["fee_per_kb"] = fee_estimated; uint64_t fee_estimated {0};
// get dynamic fee estimate from last 10 blocks
//@todo: make this work
// if (!get_base_fee_estimate(fee_estimated))
// {
// j_response["status"] = "error";
// j_response["message"] = "Cant get dynamic fee esimate";
// return j_response;
// }
j_info["fee_per_kb"] = per_kb_fee_estimated;
j_info["fee_estimate"] = fee_estimated;
j_info["tx_pool_size"] = MempoolStatus::mempool_no.load(); j_info["tx_pool_size"] = MempoolStatus::mempool_no.load();
j_info["tx_pool_size_kbytes"] = MempoolStatus::mempool_size.load(); j_info["tx_pool_size_kbytes"] = MempoolStatus::mempool_size.load();
@ -6836,6 +6848,25 @@ get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated)
return true; return true;
} }
bool
get_base_fee_estimate(uint64_t& fee_estimated)
{
string error_msg;
if (!rpc.get_base_fee_estimate(
FEE_ESTIMATE_GRACE_BLOCKS,
fee_estimated))
{
cerr << "rpc.get_base_fee_estimate failed" << endl;
return false;
}
(void) error_msg;
return true;
}
bool bool
are_absolute_offsets_good( are_absolute_offsets_good(
std::vector<uint64_t> const& absolute_offsets, std::vector<uint64_t> const& absolute_offsets,
@ -6947,18 +6978,18 @@ get_randomx_code(uint64_t blk_height,
std::lock_guard<std::mutex> lk {mtx}; std::lock_guard<std::mutex> lk {mtx};
if (!rx_vm) if (!main_vm_full)
{ {
crypto::hash block_hash; crypto::hash block_hash;
// this will create rx_vm instance if one // this will create main_vm_full instance if one
// does not exist // does not exist
me_get_block_longhash(core_storage, blk, block_hash, blk_height, 0); me_get_block_longhash(core_storage, blk, block_hash, blk_height, 0);
if (!rx_vm) if (!main_vm_full)
{ {
cerr << "rx_vm is still null!"; cerr << "main_vm_full is still null!";
return {}; return {};
} }
} }
@ -6969,32 +7000,32 @@ get_randomx_code(uint64_t blk_height,
alignas(16) uint64_t tempHash[8]; alignas(16) uint64_t tempHash[8];
blake2b(tempHash, sizeof(tempHash), bd.data(), bd.size(), nullptr, 0); blake2b(tempHash, sizeof(tempHash), bd.data(), bd.size(), nullptr, 0);
rx_vm->initScratchpad(&tempHash); main_vm_full->initScratchpad(&tempHash);
rx_vm->resetRoundingMode(); main_vm_full->resetRoundingMode();
for (int chain = 0; chain < RANDOMX_PROGRAM_COUNT - 1; ++chain) for (int chain = 0; chain < RANDOMX_PROGRAM_COUNT - 1; ++chain)
{ {
rx_vm->run(&tempHash); main_vm_full->run(&tempHash);
blake2b(tempHash, sizeof(tempHash), blake2b(tempHash, sizeof(tempHash),
rx_vm->getRegisterFile(), main_vm_full->getRegisterFile(),
sizeof(randomx::RegisterFile), nullptr, 0); sizeof(randomx::RegisterFile), nullptr, 0);
rx_code.push_back({}); rx_code.push_back({});
rx_code.back().prog = rx_vm->getProgram(); rx_code.back().prog = main_vm_full->getProgram();
rx_code.back().reg_file = *(rx_vm->getRegisterFile()); rx_code.back().reg_file = *(main_vm_full->getRegisterFile());
} }
rx_vm->run(&tempHash); main_vm_full->run(&tempHash);
rx_code.push_back({}); rx_code.push_back({});
rx_code.back().prog = rx_vm->getProgram(); rx_code.back().prog = main_vm_full->getProgram();
rx_code.back().reg_file = *(rx_vm->getRegisterFile()); rx_code.back().reg_file = *(main_vm_full->getRegisterFile());
//crypto::hash res2; //crypto::hash res2;
//rx_vm->getFinalResult(res2.data, RANDOMX_HASH_SIZE); //main_vm_full->getFinalResult(res2.data, RANDOMX_HASH_SIZE);
//cout << "pow2: " << pod_to_hex(res2) << endl; //cout << "pow2: " << pod_to_hex(res2) << endl;
return rx_code; return rx_code;

@ -40,6 +40,33 @@ rpccalls::connect_to_monero_daemon()
return m_http_client.connect(timeout_time_ms); return m_http_client.connect(timeout_time_ms);
} }
bool
rpccalls::get_base_fee_estimate(uint64_t grace_blocks,
uint64_t& fee_estimate) {
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response res;
req.grace_blocks = grace_blocks;
if (!connect_to_monero_daemon())
{
cerr << "get_base_fee_estimate: not connected to daemon" << endl;
return false;
}
bool r = epee::net_utils::invoke_http_json(
"/get_fee_estimate",
req, res, m_http_client, timeout_time_ms);
fee_estimate = res.fee;
return r;
}
uint64_t uint64_t
rpccalls::get_current_height() rpccalls::get_current_height()
{ {

@ -105,6 +105,9 @@ public:
bool bool
get_network_info(COMMAND_RPC_GET_INFO::response& info); get_network_info(COMMAND_RPC_GET_INFO::response& info);
bool
get_base_fee_estimate(uint64_t grace_blocks, uint64_t& fee_estimate);
bool bool
get_hardfork_info( COMMAND_RPC_HARD_FORK_INFO::response& res); get_hardfork_info( COMMAND_RPC_HARD_FORK_INFO::response& res);

@ -627,8 +627,6 @@ sum_fees_in_txs(const vector<transaction>& txs)
return fees_sum; return fees_sum;
} }
vector<output_tuple_with_tag> vector<output_tuple_with_tag>
get_ouputs(const transaction& tx) get_ouputs(const transaction& tx)
{ {

Loading…
Cancel
Save