Updated RandomX integration from upstream

Support RandomX PoW algorithm
This commit is contained in:
Howard Chu 2019-04-23 20:32:27 +01:00 committed by Jason Rhinelander
parent f0ab90e99e
commit 3b8dd4b25b
7 changed files with 115 additions and 105 deletions

View file

@ -500,7 +500,10 @@ static bool mlog(el::Level level, const char *category, const char *format, va_l
try
{
/* TODO(loki): when pulling upstream epee changes change this to:
MCLOG(level, category, el::Color::Default, p);
*/
MCLOG(level, category, p);
}
catch(...)
{

View file

@ -75,8 +75,5 @@ void rx_slow_hash_allocate_state(void);
void rx_slow_hash_free_state(void);
uint64_t rx_seedheight(const uint64_t height);
void rx_seedheights(const uint64_t height, uint64_t *seed_height, uint64_t *next_height);
bool rx_needhash(const uint64_t height, uint64_t *seedheight);
void rx_seedhash(const uint64_t seedheight, const char *hash, const int miners);
void rx_slow_hash(const void *data, size_t length, char *hash, const int miners);
void rx_alt_slowhash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length, char *hash);
void 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);
void rx_reorg(const uint64_t split_height);

View file

@ -38,6 +38,9 @@
#include "randomx.h"
#include "c_threads.h"
#include "hash-ops.h"
#include "misc_log_ex.h"
#define RX_LOGCAT "randomx"
#if defined(_MSC_VER)
#define THREADV __declspec(thread)
@ -45,18 +48,22 @@
#define THREADV __thread
#endif
static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT;
typedef struct rx_state {
volatile uint64_t rs_height;
CTHR_MUTEX_TYPE rs_mutex;
char rs_hash[32];
uint64_t rs_height;
randomx_cache *rs_cache;
} rx_state;
static rx_state rx_s[2];
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 randomx_dataset *rx_dataset;
THREADV int rx_s_toggle;
THREADV randomx_vm *rx_vm = NULL;
static uint64_t rx_dataset_height;
static THREADV randomx_vm *rx_vm = NULL;
static THREADV int rx_toggle;
static void local_abort(const char *msg)
{
@ -93,9 +100,9 @@ static inline int force_software_aes(void)
return use;
}
#if defined(__x86_64__)
static void cpuid(int CPUInfo[4], int InfoType)
{
#if defined(__x86_64__)
__asm __volatile__
(
"cpuid":
@ -105,9 +112,8 @@ static void cpuid(int CPUInfo[4], int InfoType)
"=d" (CPUInfo[3]) :
"a" (InfoType), "c" (0)
);
}
#endif
}
static inline int check_aes_hw(void)
{
#if defined(__x86_64__)
@ -173,22 +179,6 @@ void rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nexth
*nextheight = rx_seedheight(height + SEEDHASH_EPOCH_LAG);
}
bool rx_needhash(const uint64_t height, uint64_t *seedheight) {
rx_state *rx_sp;
uint64_t s_height = rx_seedheight(height);
int toggle = (s_height & SEEDHASH_EPOCH_BLOCKS) != 0;
bool ret;
bool changed = (toggle != rx_s_toggle);
*seedheight = s_height;
rx_s_toggle = toggle;
rx_sp = &rx_s[rx_s_toggle];
ret = (rx_sp->rs_cache == NULL) || (rx_sp->rs_height != s_height);
/* if cache is ok but we've flipped caches, reset vm */
if (!ret && changed && rx_vm != NULL)
randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
return ret;
}
typedef struct seedinfo {
randomx_cache *si_cache;
unsigned long si_start;
@ -201,7 +191,7 @@ static CTHR_THREAD_RTYPE rx_seedthread(void *arg) {
CTHR_THREAD_RETURN;
}
static void rx_initdata(randomx_cache *rs_cache, const int miners) {
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;
@ -237,106 +227,113 @@ static void rx_initdata(randomx_cache *rs_cache, const int miners) {
} else {
randomx_init_dataset(rx_dataset, rs_cache, 0, randomx_dataset_item_count());
}
rx_dataset_height = seedheight;
}
static void rx_seedhash_int(rx_state *rx_sp, const uint64_t height, const char *hash, const int miners) {
void 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 = 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 (rx_sp->rs_height != height || cache == NULL) {
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)
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");
}
randomx_init_cache(cache, hash, 32);
if (miners && rx_dataset != NULL)
rx_initdata(cache, miners);
rx_sp->rs_height = height;
if (rx_sp->rs_cache == NULL)
rx_sp->rs_cache = cache;
}
CTHR_MUTEX_UNLOCK(rx_mutex);
if (rx_vm != NULL)
randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
}
void rx_seedhash(const uint64_t height, const char *hash, const int miners) {
rx_state *rx_sp = &rx_s[rx_s_toggle];
rx_seedhash_int(rx_sp, height, hash, miners);
}
static char rx_althash[32]; // seedhash for alternate blocks
void rx_alt_slowhash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length, char *hash) {
uint64_t s_height = rx_seedheight(mainheight);
int alt_toggle = (s_height & SEEDHASH_EPOCH_BLOCKS) == 0;
rx_state *rx_sp = &rx_s[alt_toggle];
if (rx_sp->rs_height != seedheight || rx_sp->rs_cache == NULL || memcmp(seedhash, rx_althash, sizeof(rx_althash))) {
memcpy(rx_althash, seedhash, sizeof(rx_althash));
rx_sp->rs_height = 1;
rx_seedhash_int(rx_sp, seedheight, seedhash, 0);
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())
if (use_rx_jit()) {
flags |= RANDOMX_FLAG_JIT;
if(!force_software_aes() && check_aes_hw())
flags |= RANDOMX_FLAG_HARD_AES;
rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, NULL);
if(rx_vm == NULL) //large pages failed
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, NULL);
if(rx_vm == NULL) {//fallback if everything fails
flags = RANDOMX_FLAG_DEFAULT;
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, NULL);
if (!miners)
flags |= RANDOMX_FLAG_SECURE;
}
if (rx_vm == NULL)
local_abort("Couldn't allocate RandomX VM");
}
randomx_calculate_hash(rx_vm, data, length, hash);
}
void rx_slow_hash(const void *data, size_t length, char *hash, int miners) {
if (rx_vm == NULL) {
rx_state *rx_sp = &rx_s[rx_s_toggle];
randomx_flags flags = RANDOMX_FLAG_DEFAULT;
if (use_rx_jit())
flags |= RANDOMX_FLAG_JIT;
if(!force_software_aes() && check_aes_hw())
flags |= RANDOMX_FLAG_HARD_AES;
if (miners) {
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (rx_dataset == NULL) {
CTHR_MUTEX_LOCK(rx_mutex);
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
if (rx_dataset == NULL) {
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
if (rx_dataset == NULL)
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
if (rx_dataset != NULL)
rx_initdata(rx_sp->rs_cache, miners);
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
}
CTHR_MUTEX_UNLOCK(rx_mutex);
if (rx_dataset != NULL)
rx_initdata(rx_sp->rs_cache, miners, seedheight);
}
if (rx_dataset != NULL)
flags |= RANDOMX_FLAG_FULL_MEM;
else
else {
miners = 0;
mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
}
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
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);
}
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 (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);
}
void rx_slow_hash_allocate_state(void) {
@ -350,8 +347,11 @@ void rx_slow_hash_free_state(void) {
}
void rx_stop_mining(void) {
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (rx_dataset != NULL) {
randomx_release_dataset(rx_dataset);
rx_dataset = NULL;
randomx_dataset *rd = rx_dataset;
rx_dataset = NULL;
randomx_release_dataset(rd);
}
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
}

View file

@ -759,7 +759,7 @@ namespace cryptonote
block_sync_size = command_line::get_arg(vm, arg_block_sync_size);
if (block_sync_size > BLOCKS_SYNCHRONIZING_MAX_COUNT)
MERROR("Error --dblock-sync-size cannot be greater than " << BLOCKS_SYNCHRONIZING_MAX_COUNT);
MERROR("Error --block-sync-size cannot be greater than " << BLOCKS_SYNCHRONIZING_MAX_COUNT);
MGINFO("Loading checkpoints");
CHECK_AND_ASSERT_MES(update_checkpoints_from_json_file(), false, "One or more checkpoints loaded from json conflicted with existing checkpoints.");

View file

@ -980,10 +980,10 @@ namespace cryptonote
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height, const uint64_t seed_height, const crypto::hash& seed_hash)
{
blobdata bd = get_block_hashing_blob(b);
rx_alt_slowhash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data);
rx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1);
}
bool get_block_longhash(const Blockchain *pbc, const block &b, crypto::hash &res, const uint64_t height, const int miners)
bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners)
{
const blobdata bd = get_block_hashing_blob(b);
const uint8_t hf_version = b.major_version;
@ -994,16 +994,20 @@ namespace cryptonote
#endif
if (hf_version >= network_version_12_checkpointing) {
uint64_t seed_height;
if (rx_needhash(height, &seed_height)) {
crypto::hash hash;
if (pbc != NULL)
hash = pbc->get_pending_block_id_by_height(seed_height);
else
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
rx_seedhash(seed_height, hash.data, miners);
uint64_t seed_height, main_height;
crypto::hash hash;
if (pbc != NULL)
{
seed_height = rx_seedheight(height);
hash = pbc->get_pending_block_id_by_height(seed_height);
main_height = pbc->get_current_blockchain_height();
} else
{
memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
seed_height = 0;
main_height = 0;
}
rx_slow_hash(bd.data(), bd.size(), res.data, miners);
rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, miners, 0);
return true;
}

View file

@ -207,7 +207,8 @@ namespace cryptonote
, uint32_t nonce
);
bool get_block_longhash(const class Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners);
class Blockchain;
bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners);
void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height,
const uint64_t seed_height, const crypto::hash& seed_hash);
crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const int miners);

View file

@ -1,5 +1,5 @@
// Copyright (c) 2014-2019, The Monero Project
// Copyright (c) 2018, The Loki Project
// Copyright (c) 2019, The Loki Project
//
// All rights reserved.
//
@ -83,6 +83,9 @@ using namespace epee;
#include "miner.h"
extern "C" void rx_slow_hash_allocate_state();
extern "C" void rx_slow_hash_free_state();
namespace cryptonote
{
@ -526,6 +529,7 @@ namespace cryptonote
difficulty_type local_diff = 0;
uint32_t local_template_ver = 0;
block b;
rx_slow_hash_allocate_state();
++m_threads_active;
while(!m_stop)
{
@ -598,6 +602,7 @@ namespace cryptonote
++m_hashes;
++m_total_hashes;
}
rx_slow_hash_free_state();
MGINFO("Miner thread stopped ["<< th_local_index << "]");
--m_threads_active;
#if defined(LOKI_ENABLE_INTEGRATION_TEST_HOOKS)