From 8910bfae01b5e957fbcdaf0fb02c3472b0ea0e6e Mon Sep 17 00:00:00 2001 From: hayzamjs Date: Wed, 23 Sep 2020 07:14:08 +0000 Subject: [PATCH 1/5] Start change to new repo for Panthera --- .gitmodules | 4 ---- external/panthera | 1 - 2 files changed, 5 deletions(-) delete mode 160000 external/panthera diff --git a/.gitmodules b/.gitmodules index a74cc0ec..b4c95b51 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,10 +10,6 @@ path = external/rapidjson url = https://github.com/Tencent/rapidjson branch = master -[submodule "external/panthera"] - path = external/panthera - url = https://github.com/scala-network/DefyX - branch = Panthera_DP [submodule "external/trezor-common"] path = external/trezor-common url = https://github.com/trezor/trezor-common diff --git a/external/panthera b/external/panthera deleted file mode 160000 index 4e079018..00000000 --- a/external/panthera +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4e0790189f15b27d869d099981821e7ba915eee1 From 5919b215553a57ace7fb5d73dffbf98c8a7c8ea4 Mon Sep 17 00:00:00 2001 From: hayzamjs Date: Wed, 23 Sep 2020 07:17:26 +0000 Subject: [PATCH 2/5] Use actual Panthera repo as submodule --- .gitmodules | 3 +++ external/randomx | 1 + 2 files changed, 4 insertions(+) create mode 160000 external/randomx diff --git a/.gitmodules b/.gitmodules index b4c95b51..3266fece 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ path = external/trezor-common url = https://github.com/trezor/trezor-common branch = master +[submodule "external/randomx"] + path = external/randomx + url = https://github.com/scala-network/Panthera diff --git a/external/randomx b/external/randomx new file mode 160000 index 00000000..3dd1195f --- /dev/null +++ b/external/randomx @@ -0,0 +1 @@ +Subproject commit 3dd1195f7955f7b74c18ebf072dcadbaf5f6e282 From 1b97bf37b007aaa4faa4bd3e80f3f5fdc4f9f4aa Mon Sep 17 00:00:00 2001 From: hayzamjs Date: Wed, 23 Sep 2020 07:48:22 +0000 Subject: [PATCH 3/5] Panthera changes, versioning update, smaller sync block count Updated the repo to use a different repo for the Panthera PoW, from this release forward we switch to semantic versioning and block sync count to 20 dramatically speeds up blockchain sync --- CMakeLists.txt | 4 +- external/CMakeLists.txt | 2 +- src/crypto/CMakeLists.txt | 4 +- src/crypto/dx-slow-hash.c | 353 ------------------- src/crypto/hash-ops.h | 12 +- src/crypto/rx-slow-hash.c | 367 ++++++++++++++++++++ src/crypto/slow-hash.c | 4 +- src/cryptonote_basic/miner.cpp | 4 +- src/cryptonote_core/blockchain.cpp | 2 +- src/cryptonote_core/cryptonote_core.cpp | 5 - src/cryptonote_core/cryptonote_tx_utils.cpp | 8 +- src/rpc/core_rpc_server.cpp | 2 +- src/rpc/rpc_payment.cpp | 2 +- src/version.cpp.in | 2 +- src/wallet/wallet_rpc_payments.cpp | 2 +- 15 files changed, 391 insertions(+), 382 deletions(-) delete mode 100644 src/crypto/dx-slow-hash.c create mode 100644 src/crypto/rx-slow-hash.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 141c76bf..a55188f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,13 +198,13 @@ if(NOT MANUAL_SUBMODULES) message(FATAL_ERROR "Submodule '${relative_path}' is not up-to-date. Please update all submodules with\ngit submodule update --init --force\nor run cmake with -DMANUAL_SUBMODULES=1\n") endif() endfunction () - + message(STATUS "Checking submodules") check_submodule(external/miniupnp) check_submodule(external/unbound) check_submodule(external/rapidjson) check_submodule(external/trezor-common) - check_submodule(external/panthera) + check_submodule(external/randomx) endif() endif() diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 32845e5d..71b165f4 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -80,4 +80,4 @@ endif() add_subdirectory(db_drivers) add_subdirectory(easylogging++) -add_subdirectory(panthera EXCLUDE_FROM_ALL) +add_subdirectory(randomx EXCLUDE_FROM_ALL) diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 910ea998..47ad8441 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -47,7 +47,7 @@ set(crypto_sources random.c skein.c slow-hash.c - dx-slow-hash.c + rx-slow-hash.c CryptonightR_JIT.c tree-hash.c) @@ -90,7 +90,7 @@ scala_add_library(cncrypto target_link_libraries(cncrypto PUBLIC epee - defyx + randomx ${Boost_SYSTEM_LIBRARY} ${SODIUM_LIBRARY} PRIVATE diff --git a/src/crypto/dx-slow-hash.c b/src/crypto/dx-slow-hash.c deleted file mode 100644 index 01146395..00000000 --- a/src/crypto/dx-slow-hash.c +++ /dev/null @@ -1,353 +0,0 @@ -//Copyright (c) 2014-2019, The Monero Project -//Copyright (c) 2018-2020, The Scala Network -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "defyx.h" -#include "c_threads.h" -#include "hash-ops.h" - -#if defined(_MSC_VER) -#define THREADV __declspec(thread) -#else -#define THREADV __thread -#endif - -typedef struct dx_state { - CTHR_MUTEX_TYPE rs_mutex; - char rs_hash[HASH_SIZE]; - uint64_t rs_height; - defyx_cache *rs_cache; -} dx_state; - -static CTHR_MUTEX_TYPE dx_mutex = CTHR_MUTEX_INIT; -static CTHR_MUTEX_TYPE dx_dataset_mutex = CTHR_MUTEX_INIT; - -static dx_state dx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}}; - -static defyx_dataset *dx_dataset; -static uint64_t dx_dataset_height; -static THREADV defyx_vm *dx_vm = NULL; - -static void local_abort(const char *msg) -{ - fprintf(stderr, "%s\n", msg); -#ifdef NDEBUG - _exit(1); -#else - abort(); -#endif -} - -/** - * @brief uses cpuid to determine if the CPU supports the AES instructions - * @return true if the CPU supports AES, false otherwise - */ - -static inline int force_software_aes(void) -{ - static int use = -1; - - if (use != -1) - return use; - - const char *env = getenv("MONERO_USE_SOFTWARE_AES"); - if (!env) { - use = 0; - } - else if (!strcmp(env, "0") || !strcmp(env, "no")) { - use = 0; - } - else { - use = 1; - } - return use; -} - -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 check_aes_hw(void) -{ -#if defined(__x86_64__) - int cpuid_results[4]; - static int supported = -1; - - if(supported >= 0) - return supported; - - cpuid(cpuid_results,1); - return supported = cpuid_results[2] & (1 << 25); -#else - return 0; -#endif -} - -static volatile int use_dx_jit_flag = -1; - -static inline int use_dx_jit(void) -{ -#if defined(__x86_64__) - - if (use_dx_jit_flag != -1) - return use_dx_jit_flag; - - const char *env = getenv("MONERO_USE_RX_JIT"); - if (!env) { - use_dx_jit_flag = 1; - } - else if (!strcmp(env, "0") || !strcmp(env, "no")) { - use_dx_jit_flag = 0; - } - else { - use_dx_jit_flag = 1; - } - return use_dx_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 - -void dx_reorg(const uint64_t split_height) { - int i; - CTHR_MUTEX_LOCK(dx_mutex); - for (i=0; i<2; i++) { - if (split_height <= dx_s[i].rs_height) { - if (dx_s[i].rs_height == dx_dataset_height) - dx_dataset_height = 1; - dx_s[i].rs_height = 1; /* set to an invalid seed height */ - } - } - CTHR_MUTEX_UNLOCK(dx_mutex); -} - -uint64_t dx_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; -} - -void dx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) { - *seedheight = dx_seedheight(height); - *nextheight = dx_seedheight(height + SEEDHASH_EPOCH_LAG); -} - -typedef struct seedinfo { - defyx_cache *si_cache; - unsigned long si_start; - unsigned long si_count; -} seedinfo; - -static CTHR_THREAD_RTYPE dx_seedthread(void *arg) { - seedinfo *si = arg; - defyx_init_dataset(dx_dataset, si->si_cache, si->si_start, si->si_count); - CTHR_THREAD_RETURN; -} - -static void dx_initdata(defyx_cache *rs_cache, const int miners, const uint64_t seedheight) { - if (miners > 1) { - unsigned long delta = defyx_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 seedheight) - is_alt = 1; - /* miner can be ahead of mainchain */ - else if (s_height < seedheight) - toggle ^= 1; - } - - toggle ^= (is_alt != 0); - - dx_sp = &dx_s[toggle]; - CTHR_MUTEX_LOCK(dx_sp->rs_mutex); - CTHR_MUTEX_UNLOCK(dx_mutex); - - cache = dx_sp->rs_cache; - if (cache == NULL) { - if (use_dx_jit()) - flags |= RANDOMX_FLAG_JIT; - if (cache == NULL) { - cache = defyx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES); - if (cache == NULL) { - cache = defyx_alloc_cache(flags); - } - if (cache == NULL) - local_abort("Couldn't allocate RandomX cache"); - } - } - if (dx_sp->rs_height != seedheight || dx_sp->rs_cache == NULL || memcmp(seedhash, dx_sp->rs_hash, HASH_SIZE)) { - defyx_init_cache(cache, seedhash, HASH_SIZE); - dx_sp->rs_cache = cache; - dx_sp->rs_height = seedheight; - memcpy(dx_sp->rs_hash, seedhash, HASH_SIZE); - } - if (dx_vm == NULL) { - defyx_flags flags = RANDOMX_FLAG_DEFAULT; - if (use_dx_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(dx_dataset_mutex); - if (dx_dataset == NULL) { - dx_dataset = defyx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES); - if (dx_dataset == NULL) { - dx_dataset = defyx_alloc_dataset(RANDOMX_FLAG_DEFAULT); - } - if (dx_dataset != NULL) - dx_initdata(dx_sp->rs_cache, miners, seedheight); - } - if (dx_dataset != NULL) - flags |= RANDOMX_FLAG_FULL_MEM; - else { - miners = 0; - } - CTHR_MUTEX_UNLOCK(dx_dataset_mutex); - } - dx_vm = defyx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, dx_sp->rs_cache, dx_dataset); - if(dx_vm == NULL) { //large pages failed - dx_vm = defyx_create_vm(flags, dx_sp->rs_cache, dx_dataset); - } - if(dx_vm == NULL) {//fallback if everything fails - flags = RANDOMX_FLAG_DEFAULT | (miners ? RANDOMX_FLAG_FULL_MEM : 0); - dx_vm = defyx_create_vm(flags, dx_sp->rs_cache, dx_dataset); - } - if (dx_vm == NULL) - local_abort("Couldn't allocate RandomX VM"); - } else if (miners) { - CTHR_MUTEX_LOCK(dx_dataset_mutex); - if (dx_dataset != NULL && dx_dataset_height != seedheight) - dx_initdata(cache, miners, seedheight); - CTHR_MUTEX_UNLOCK(dx_dataset_mutex); - } else { - /* this is a no-op if the cache hasn't changed */ - defyx_vm_set_cache(dx_vm, dx_sp->rs_cache); - } - /* mainchain users can run in parallel */ - if (!is_alt) - CTHR_MUTEX_UNLOCK(dx_sp->rs_mutex); - defyx_calculate_hash(dx_vm, data, length, hash); - /* altchain slot users always get fully serialized */ - if (is_alt) - CTHR_MUTEX_UNLOCK(dx_sp->rs_mutex); -} - -void dx_slow_hash_allocate_state(void) { -} - -void dx_slow_hash_free_state(void) { - if (dx_vm != NULL) { - defyx_destroy_vm(dx_vm); - dx_vm = NULL; - } -} - -void dx_stop_mining(void) { - CTHR_MUTEX_LOCK(dx_dataset_mutex); - if (dx_dataset != NULL) { - defyx_dataset *rd = dx_dataset; - dx_dataset = NULL; - defyx_release_dataset(rd); - } - CTHR_MUTEX_UNLOCK(dx_dataset_mutex); -} \ No newline at end of file diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 4e74fd9d..253da202 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -90,9 +90,9 @@ void hash_extra_skein(const void *data, size_t length, char *hash); void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash); #define RX_BLOCK_VERSION 12 -void dx_slow_hash_allocate_state(void); -void dx_slow_hash_free_state(void); -uint64_t dx_seedheight(const uint64_t height); -void dx_seedheights(const uint64_t height, uint64_t *seed_height, uint64_t *next_height); -void dx_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 dx_reorg(const uint64_t split_height); +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); +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); diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c new file mode 100644 index 00000000..d461f3a4 --- /dev/null +++ b/src/crypto/rx-slow-hash.c @@ -0,0 +1,367 @@ +// Copyright (c) 2019-2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +#else +#define THREADV __thread +#endif + +typedef struct rx_state { + CTHR_MUTEX_TYPE rs_mutex; + char rs_hash[HASH_SIZE]; + 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 randomx_dataset *rx_dataset; +static int rx_dataset_nomem; +static uint64_t rx_dataset_height; +static THREADV randomx_vm *rx_vm = NULL; + +static void local_abort(const char *msg) +{ + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif +} + +static inline int disabled_flags(void) { + static int flags = -1; + + if (flags != -1) { + return flags; + } + + const char *env = getenv("MONERO_RANDOMX_UMASK"); + if (!env) { + flags = 0; + } + else { + char* endptr; + long int value = strtol(env, &endptr, 0); + if (endptr != env && value >= 0 && value < INT_MAX) { + flags = value; + } + else { + flags = 0; + } + } + + return flags; +} + +static inline int enabled_flags(void) { + static int flags = -1; + + if (flags != -1) { + return flags; + } + + 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) +{ + 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; +} + +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; +} + +void 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) { + if (rx_s[i].rs_height == rx_dataset_height) + rx_dataset_height = 1; + rx_s[i].rs_height = 1; /* set to an invalid seed height */ + } + } + CTHR_MUTEX_UNLOCK(rx_mutex); +} + +uint64_t 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; +} + +void rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) { + *seedheight = rx_seedheight(height); + *nextheight = rx_seedheight(height + get_seedhash_epoch_lag()); +} + +typedef struct seedinfo { + randomx_cache *si_cache; + unsigned long si_start; + unsigned long si_count; +} seedinfo; + +static CTHR_THREAD_RTYPE rx_seedthread(void *arg) { + seedinfo *si = arg; + randomx_init_dataset(rx_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 seedheight) + is_alt = 1; + /* miner can be ahead of mainchain */ + else if (s_height < seedheight) + toggle ^= 1; + } + + toggle ^= (is_alt != 0); + + 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 (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, HASH_SIZE)) { + randomx_init_cache(cache, seedhash, HASH_SIZE); + rx_sp->rs_cache = cache; + rx_sp->rs_height = seedheight; + memcpy(rx_sp->rs_hash, seedhash, HASH_SIZE); + } + if (rx_vm == NULL) { + if ((flags & RANDOMX_FLAG_JIT) && !miners) { + flags |= RANDOMX_FLAG_SECURE & ~disabled_flags(); + } + if (miners && (disabled_flags() & RANDOMX_FLAG_FULL_MEM)) { + miners = 0; + } + if (miners) { + CTHR_MUTEX_LOCK(rx_dataset_mutex); + if (!rx_dataset_nomem) { + 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); + } + if (rx_dataset != NULL) + rx_initdata(rx_sp->rs_cache, miners, seedheight); + } + } + if (rx_dataset != NULL) + flags |= RANDOMX_FLAG_FULL_MEM; + else { + miners = 0; + if (!rx_dataset_nomem) { + rx_dataset_nomem = 1; + 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 + 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); + else if (rx_dataset == NULL) { + /* this is a no-op if the cache hasn't changed */ + randomx_vm_set_cache(rx_vm, rx_sp->rs_cache); + } + CTHR_MUTEX_UNLOCK(rx_dataset_mutex); + } else { + /* this is a no-op if the cache hasn't 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) { +} + +void rx_slow_hash_free_state(void) { + if (rx_vm != NULL) { + randomx_destroy_vm(rx_vm); + rx_vm = NULL; + } +} + +void rx_stop_mining(void) { + CTHR_MUTEX_LOCK(rx_dataset_mutex); + if (rx_dataset != NULL) { + randomx_dataset *rd = rx_dataset; + rx_dataset = NULL; + randomx_release_dataset(rd); + } + rx_dataset_nomem = 0; + CTHR_MUTEX_UNLOCK(rx_dataset_mutex); +} + diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 424c8e3f..c73186f3 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1770,11 +1770,11 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int void slow_hash_allocate_state(void) { cn_slow_hash_allocate_state(); - dx_slow_hash_allocate_state(); + rx_slow_hash_allocate_state(); } void slow_hash_free_state(void) { cn_slow_hash_free_state(); - dx_slow_hash_free_state(); + rx_slow_hash_free_state(); } diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index b6a07c71..c79a2e80 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -435,7 +435,7 @@ namespace cryptonote { boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); } - extern "C" void dx_stop_mining(void); + extern "C" void rx_stop_mining(void); //----------------------------------------------------------------------------------------------------- bool miner::stop() { @@ -468,7 +468,7 @@ namespace cryptonote MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); m_threads.clear(); m_threads_autodetect.clear(); - dx_stop_mining(); + rx_stop_mining(); return true; } //----------------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e0be2395..f1904644 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1735,7 +1735,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id if (b.major_version >= RX_BLOCK_VERSION) { crypto::hash seedhash = null_hash; - uint64_t seedheight = dx_seedheight(bei.height); + uint64_t seedheight = rx_seedheight(bei.height); // seedblock is on the alt chain somewhere if (alt_chain.size() && alt_chain.front().height <= seedheight) { diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 7120b8e0..4b0c88a3 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1163,12 +1163,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- size_t core::get_block_sync_size(uint64_t height) const { - static const uint64_t quick_height = m_nettype == TESTNET ? 801219 : m_nettype == MAINNET ? 1220516 : 0; - if (block_sync_size > 0) - return block_sync_size; - if (height >= quick_height) return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; - return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4; } //----------------------------------------------------------------------------------------------- bool core::are_key_images_spent_in_pool(const std::vector& key_im, std::vector &spent) const diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 5b6b4a04..3a5cbf06 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -803,7 +803,7 @@ std::list listOfAddresses = { 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); - dx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1); + 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) @@ -815,7 +815,7 @@ std::list listOfAddresses = { crypto::hash hash; if (pbc != NULL) { - seed_height = dx_seedheight(height); + seed_height = rx_seedheight(height); hash = pbc->get_pending_block_id_by_height(seed_height); main_height = pbc->get_current_blockchain_height(); } else @@ -824,7 +824,7 @@ std::list listOfAddresses = { seed_height = 0; main_height = 0; } - dx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, miners, 0); + rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, miners, 0); } else { const int pow_variant = b.major_version >= 7 ? b.major_version - 6 : 0; crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height); @@ -841,6 +841,6 @@ std::list listOfAddresses = { void get_block_longhash_reorg(const uint64_t split_height) { - dx_reorg(split_height); + rx_reorg(split_height); } } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 5cb2b664..0897c8a9 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1625,7 +1625,7 @@ namespace cryptonote if (b.major_version >= RX_BLOCK_VERSION) { uint64_t next_height; - crypto::dx_seedheights(height, &seed_height, &next_height); + crypto::rx_seedheights(height, &seed_height, &next_height); seed_hash = m_core.get_block_id_by_height(seed_height); if (next_height != seed_height) next_seed_hash = m_core.get_block_id_by_height(next_height); diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index 835f0285..8fb9fad4 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -236,7 +236,7 @@ namespace cryptonote const uint64_t seed_height = is_current ? info.seed_height : info.previous_seed_height; const crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash; const uint64_t height = cryptonote::get_block_height(block); - crypto::dx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, 0, 0); + crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, 0, 0); } else { diff --git a/src/version.cpp.in b/src/version.cpp.in index 7b05b686..3333c60d 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_SCALA_VERSION_TAG "@VERSIONTAG@" -#define DEF_SCALA_VERSION "4.0.0.1" +#define DEF_SCALA_VERSION "4.1.0" #define DEF_SCALA_RELEASE_NAME "Panthera" #define DEF_SCALA_VERSION_FULL DEF_SCALA_VERSION "-" DEF_SCALA_VERSION_TAG #define DEF_SCALA_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp index 45362f06..3f45191c 100644 --- a/src/wallet/wallet_rpc_payments.cpp +++ b/src/wallet/wallet_rpc_payments.cpp @@ -146,7 +146,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, const std::functio if (major_version >= RX_BLOCK_VERSION) { const int miners = 1; - crypto::dx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, miners, 0); + crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, miners, 0); } else { From 98565e804c6a95f46f51aa67ddbea76ee4685ae9 Mon Sep 17 00:00:00 2001 From: hayzamjs Date: Wed, 23 Sep 2020 08:07:33 +0000 Subject: [PATCH 4/5] Enable per block checkpointing --- CMakeLists.txt | 2 +- src/blocks/checkpoints.dat | Bin 0 -> 4740 bytes src/cryptonote_core/blockchain.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a55188f6..6360e051 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,7 @@ set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") # set this to 0 if per-block checkpoint needs to be disabled -set(PER_BLOCK_CHECKPOINT 0) +set(PER_BLOCK_CHECKPOINT 1) if(PER_BLOCK_CHECKPOINT) add_definitions("-DPER_BLOCK_CHECKPOINT") diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2cf99088cbcc09e5096fb28eee1b7a9d4cc0603b 100644 GIT binary patch literal 4740 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 Date: Wed, 7 Oct 2020 10:19:18 +0000 Subject: [PATCH 5/5] Add new blocks.dat file --- src/blocks/checkpoints.dat | Bin 4740 -> 6020 bytes src/cryptonote_core/blockchain.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index 2cf99088cbcc09e5096fb28eee1b7a9d4cc0603b..d162842516267fe993e1fce3efbafda8eda7d21d 100644 GIT binary patch delta 1301 zcmV+w1?u{QC4?^lUa>RNw2PKS5#VqFXldM={Mos*|rTQ3RR<^7LiyBdby;wSAmj@Old%oa# zO{ALO{;JhMsdi7@N%A$jihq|k#bPBGw#`Ctz4MVC6cy9wz}H3z(tqKk&H<08_+^Fo zg>thHi}u^R?I?P35Gu0FRY;0E7-`coZpDk)dH;grsxEsy& zTeRwPjdZMA>Sp_dBLTbzMS;#^Vpm%EksE4$_}VT3f~Ml*3KfQiA+Zr^fzBaInFIm( z&!yRlAB`+L&y>=mMO$3LlTjH#4*AEgvAO|@;?7bV2WjCXYJc;FlBZuxfaypUw?P(u ztd^->zbylcNuCpiEZNqK^cu-~loSng0rAjayCQ=8X`4E<`{)hdfHg69G5IjumfIf2 zk+W74_CBWZIG~if*m+JFOkr$TN9WXUd{(HJ6AZqXuVZY6CucP%(Qy%=Rd~_TW>AXz z%3o6o*?KQ*p?{MeHaw?3+f<4S$+uSr8;Xoyg3D_GWq_v`6GRLG7eXLd2^Kgo(?a65 zN*2F)5zA@g{DX>nCKReG;jC4xe`=dO@?Jrz#{5X@>1lQHt{k>XeSv-!D=)r~$7%O9 z3if9`3S?l)3hKZ`-tqh)t(_7$-@@FsBL_qB7n;UV(avuih?hb8SUa03LFQA64oyabKABL zmu?y892Vv`CYJ1dCo<8!-h6f5rLZGdI7fqqfcOIL9Uq3An_cWrh}!X+ zw}C=X5PvJp3omhWT-S`r*TCPwzjeA*dZJCS0NKmk7Fle%6pX6NamB2(zmeL;W zmfh)AZQXmq4Ah)48G7{>ES5wPAIJ^m}m`*CE z<)kSb83?iAssQ89gd`~y9^(pEuxq3YH8?#NBO$xkB%ViO`b=72ba9&TnQ6V;_a%Ts z-7N;$L4wa&&uOR!6eO05w|>@-&pAC{^PND7u1%9^_Cc?!o-S@BVt5!4!_58s#@SL9 zT7T_dN~_uUX6N}qs%xk~hpOq(e(T%ty6h?#+;p&iAS1l>B}2U?zDOqe(2Ka6OP;oC zU^%>EsXVN;uGKb7W|@&c}P-U&eL8Wu+m|i*z`dOQh&AB*?_x&;Km6i)#M9X%NI8vQ|f6HTEZTz zWQ(fw=(&hXPyjC~-PWK|?t{?`kjNhm)L*fx6V#+jnwk5dihFwnq0l6e_n^~Mi`hMp z`gJDr!L9>kA=sR1wG7tPtGWvtoRTf*fUP|T&2L##JzXUA!SS}s^6(w;-e>m<77AzD LAeY7L>)