From c31979635504dd8c2c983a111f41accdac9205c7 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 23 Apr 2019 20:32:27 +0100 Subject: [PATCH] RandomX integration Support RandomX PoW algorithm --- .gitmodules | 3 + CMakeLists.txt | 1 + external/CMakeLists.txt | 1 + external/randomx | 1 + src/crypto/CMakeLists.txt | 4 + src/crypto/c_threads.h | 56 +++ src/crypto/hash-ops.h | 11 + src/crypto/rx-slow-hash.c | 349 ++++++++++++++++++ src/crypto/slow-hash.c | 28 +- .../cryptonote_format_utils.cpp | 15 - .../cryptonote_format_utils.h | 2 - src/cryptonote_basic/miner.cpp | 11 +- src/cryptonote_basic/miner.h | 7 +- src/cryptonote_config.h | 1 + src/cryptonote_core/blockchain.cpp | 30 +- src/cryptonote_core/cryptonote_core.cpp | 4 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 48 ++- src/cryptonote_core/cryptonote_tx_utils.h | 7 + src/rpc/core_rpc_server.cpp | 17 +- src/rpc/core_rpc_server_commands_defs.h | 6 +- tests/core_proxy/core_proxy.cpp | 4 +- tests/core_tests/chaingen.cpp | 4 +- 22 files changed, 568 insertions(+), 42 deletions(-) create mode 160000 external/randomx create mode 100644 src/crypto/c_threads.h create mode 100644 src/crypto/rx-slow-hash.c diff --git a/.gitmodules b/.gitmodules index 6e2339fb9..f8e7c305b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "external/trezor-common"] path = external/trezor-common url = https://github.com/trezor/trezor-common.git +[submodule "external/randomx"] + path = external/randomx + url = https://github.com/tevador/RandomX diff --git a/CMakeLists.txt b/CMakeLists.txt index 154e291d1..a1bf8dc9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,7 @@ if(NOT MANUAL_SUBMODULES) check_submodule(external/unbound) check_submodule(external/rapidjson) check_submodule(external/trezor-common) + check_submodule(external/randomx) endif() endif() diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index a148b7e85..436f29ced 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -100,3 +100,4 @@ endif() add_subdirectory(db_drivers) add_subdirectory(easylogging++) +add_subdirectory(randomx) diff --git a/external/randomx b/external/randomx new file mode 160000 index 000000000..1276d67d2 --- /dev/null +++ b/external/randomx @@ -0,0 +1 @@ +Subproject commit 1276d67d2f594ea4a8e9cde28253b1a74769aeb9 diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 3a4d09fd8..80f1e5d7e 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -46,6 +46,7 @@ set(crypto_sources random.c skein.c slow-hash.c + rx-slow-hash.c CryptonightR_JIT.c tree-hash.c) @@ -53,6 +54,8 @@ if(ARCH_ID STREQUAL "i386" OR ARCH_ID STREQUAL "x86_64" OR ARCH_ID STREQUAL "x86 list(APPEND crypto_sources CryptonightR_template.S) endif() +include_directories(${RANDOMX_INCLUDE}) + set(crypto_headers) set(crypto_private_headers @@ -86,6 +89,7 @@ monero_add_library(cncrypto target_link_libraries(cncrypto PUBLIC epee + randomx ${Boost_SYSTEM_LIBRARY} ${SODIUM_LIBRARY} PRIVATE diff --git a/src/crypto/c_threads.h b/src/crypto/c_threads.h new file mode 100644 index 000000000..e73ebd9c3 --- /dev/null +++ b/src/crypto/c_threads.h @@ -0,0 +1,56 @@ +// Copyright (c) 2019, 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. + +/* Brain-dead simple portability wrapper over thread APIs for C */ +#pragma once + +#ifdef _WIN32 +#include +#define CTHR_MUTEX_TYPE HANDLE +#define CTHR_MUTEX_INIT NULL +#define CTHR_MUTEX_LOCK(x) do { if (x == NULL) { \ + HANDLE p = CreateMutex(NULL, FALSE, NULL); \ + if (InterlockedCompareExchangePointer((PVOID*)&x, (PVOID)p, NULL) != NULL) \ + CloseHandle(p); \ + } WaitForSingleObject(x, INFINITE) == WAIT_FAILED; } while(0) +#define CTHR_MUTEX_UNLOCK(x) (ReleaseMutex(x) == 0) +#define CTHR_THREAD_TYPE HANDLE +#define CTHR_THREAD_RTYPE void +#define CTHR_THREAD_CREATE(thr, func, arg) thr = _beginthread(func, 0, arg) +#define CTHR_THREAD_JOIN(thr) WaitForSingleObject(thr, INFINITE) +#else +#include +#define CTHR_MUTEX_TYPE pthread_mutex_t +#define CTHR_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +#define CTHR_MUTEX_LOCK(x) pthread_mutex_lock(&x) +#define CTHR_MUTEX_UNLOCK(x) pthread_mutex_unlock(&x) +#define CTHR_THREAD_TYPE pthread_t +#define CTHR_THREAD_RTYPE void * +#define CTHR_THREAD_CREATE(thr, func, arg) pthread_create(&thr, NULL, func, arg) +#define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL) +#endif diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 859c810bd..de91734d4 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -87,3 +87,14 @@ void hash_extra_jh(const void *data, size_t length, char *hash); 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 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_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 000000000..3982ddcea --- /dev/null +++ b/src/crypto/rx-slow-hash.c @@ -0,0 +1,349 @@ +// Copyright (c) 2019, 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 "randomx.h" +#include "c_threads.h" +#include "hash-ops.h" + +#if defined(_MSC_VER) +#define THREADV __declspec(thread) +#else +#define THREADV __thread +#endif + +static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT; + +typedef struct rx_state { + volatile uint64_t rs_height; + randomx_cache *rs_cache; +} rx_state; + +static rx_state rx_s[2]; + +static randomx_dataset *rx_dataset; +THREADV int rx_s_toggle; +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 +} + +/** + * @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_rx_jit_flag = -1; + +static inline int use_rx_jit(void) +{ +#if defined(__x86_64__) + + if (use_rx_jit_flag != -1) + return use_rx_jit_flag; + + const char *env = getenv("MONERO_USE_RX_JIT"); + if (!env) { + use_rx_jit_flag = 1; + } + else if (!strcmp(env, "0") || !strcmp(env, "no")) { + use_rx_jit_flag = 0; + } + 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 */ +#define SEEDHASH_EPOCH_LAG 64 + +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) + 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) { + 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 + 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; + 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); + return NULL; +} + +static void rx_initdata(randomx_cache *rs_cache, const int miners) { + 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; irs_cache; + if (rx_sp->rs_height != height || 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) + 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(hash, 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_vm == NULL) { + 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; + 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 (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) { + if (rx_dataset == NULL) { + CTHR_MUTEX_LOCK(rx_mutex); + 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); + } + CTHR_MUTEX_UNLOCK(rx_mutex); + } + if (rx_dataset != NULL) + flags |= RANDOMX_FLAG_FULL_MEM; + else + miners = 0; + } + rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset); + if(rx_vm == NULL) //large pages failed + 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"); + } + randomx_calculate_hash(rx_vm, data, length, hash); +} + +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; + } +} diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 32a843a1b..55c3b6b4e 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -653,7 +653,7 @@ BOOL SetLockPagesPrivilege(HANDLE hProcess, BOOL bEnable) * the allocated buffer. */ -void slow_hash_allocate_state(void) +void cn_slow_hash_allocate_state(void) { if(hp_state != NULL) return; @@ -686,7 +686,7 @@ void slow_hash_allocate_state(void) *@brief frees the state allocated by slow_hash_allocate_state */ -void slow_hash_free_state(void) +void cn_slow_hash_free_state(void) { if(hp_state == NULL) return; @@ -760,7 +760,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int // this isn't supposed to happen, but guard against it for now. if(hp_state == NULL) - slow_hash_allocate_state(); + cn_slow_hash_allocate_state(); /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ if (prehashed) { @@ -874,13 +874,13 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int } #elif !defined NO_AES && (defined(__arm__) || defined(__aarch64__)) -void slow_hash_allocate_state(void) +void cn_slow_hash_allocate_state(void) { // Do nothing, this is just to maintain compatibility with the upgraded slow-hash.c return; } -void slow_hash_free_state(void) +void cn_slow_hash_free_state(void) { // As above return; @@ -1440,13 +1440,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int #else // Portable implementation as a fallback -void slow_hash_allocate_state(void) +#define hp_jitfunc ((v4_random_math_JIT_func)NULL) + +void cn_slow_hash_allocate_state(void) { // Do nothing, this is just to maintain compatibility with the upgraded slow-hash.c return; } -void slow_hash_free_state(void) +void cn_slow_hash_free_state(void) { // As above return; @@ -1623,3 +1625,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int } #endif + +void slow_hash_allocate_state(void) +{ + cn_slow_hash_allocate_state(); + rx_slow_hash_allocate_state(); +} + +void slow_hash_free_state(void) +{ + cn_slow_hash_free_state(); + rx_slow_hash_free_state(); +} diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 421721764..fca10dfca 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1202,14 +1202,6 @@ namespace cryptonote return p; } //--------------------------------------------------------------- - bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) - { - blobdata bd = get_block_hashing_blob(b); - const int cn_variant = b.major_version >= 11 ? 4 : b.major_version >= 9 && b.major_version <= 10 ? 2 : 1; - crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, height); - return true; - } - //--------------------------------------------------------------- std::vector relative_output_offsets_to_absolute(const std::vector& off) { std::vector res = off; @@ -1230,13 +1222,6 @@ namespace cryptonote return res; } //--------------------------------------------------------------- - crypto::hash get_block_longhash(const block& b, uint64_t height) - { - crypto::hash p = null_hash; - get_block_longhash(b, p, height); - return p; - } - //--------------------------------------------------------------- bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash *block_hash) { std::stringstream ss; diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index c9de2a56e..284494299 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -117,8 +117,6 @@ namespace cryptonote bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata *blob = NULL); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); - bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); - crypto::hash get_block_longhash(const block& b, uint64_t height); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash *block_hash); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash &block_hash); diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 173679e21..5e3b7cda5 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -37,8 +37,10 @@ #include "syncobj.h" #include "cryptonote_basic_impl.h" #include "cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "file_io_utils.h" #include "common/command_line.h" +#include "common/util.h" #include "string_coding.h" #include "string_tools.h" #include "storages/portable_storage_template_helper.h" @@ -99,12 +101,13 @@ namespace cryptonote } - miner::miner(i_miner_handler* phandler):m_stop(1), + miner::miner(i_miner_handler* phandler, Blockchain* pbc):m_stop(1), m_template(boost::value_initialized()), m_template_no(0), m_diffic(0), m_thread_index(0), m_phandler(phandler), + m_pbc(pbc), m_height(0), m_threads_active(0), m_pausers_count(0), @@ -466,12 +469,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) + bool miner::find_nonce_for_given_block(const Blockchain *pbc, block& bl, const difficulty_type& diffic, uint64_t height) { for(; bl.nonce != std::numeric_limits::max(); bl.nonce++) { crypto::hash h; - get_block_longhash(bl, h, height); + get_block_longhash(pbc, bl, h, height, tools::get_max_concurrency()); if(check_hash(h, diffic)) { @@ -570,7 +573,7 @@ namespace cryptonote b.nonce = nonce; crypto::hash h; - get_block_longhash(b, h, height); + get_block_longhash(m_pbc, b, h, height, tools::get_max_concurrency()); if(check_hash(h, local_diff)) { diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 285075f51..4a65599ce 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -52,13 +52,15 @@ namespace cryptonote ~i_miner_handler(){}; }; + class Blockchain; + /************************************************************************/ /* */ /************************************************************************/ class miner { public: - miner(i_miner_handler* phandler); + miner(i_miner_handler* phandler, Blockchain* pbc); ~miner(); bool init(const boost::program_options::variables_map& vm, network_type nettype); static void init_options(boost::program_options::options_description& desc); @@ -74,7 +76,7 @@ namespace cryptonote bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height); + static bool find_nonce_for_given_block(const Blockchain *pbc, block& bl, const difficulty_type& diffic, uint64_t height); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -133,6 +135,7 @@ namespace cryptonote std::list m_threads; epee::critical_section m_threads_lock; i_miner_handler* m_phandler; + Blockchain* m_pbc; account_public_address m_mine_address; epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index c57347ecf..199977a0a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -103,6 +103,7 @@ #define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading +#define BLOCKS_SYNCHRONIZING_MAX_COUNT 2048 //must be a power of 2, greater than 128, equal to SEEDHASH_EPOCH_BLOCKS #define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 91f18aafc..e66e11fe0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1093,6 +1093,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list } m_hardfork->reorganize_from_chain_height(split_height); + get_block_longhash_reorg(split_height); std::shared_ptr reorg_notify = m_reorg_notify; if (reorg_notify) @@ -1757,7 +1758,30 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; - get_block_longhash(bei.bl, proof_of_work, bei.height); + if (b.major_version >= RX_BLOCK_VERSION) + { + crypto::hash seedhash = null_hash; + uint64_t seedheight = rx_seedheight(bei.height); + // seedblock is on the alt chain somewhere + if (alt_chain.size() && alt_chain.front().height <= seedheight) + { + for (auto it=alt_chain.begin(); it != alt_chain.end(); it++) + { + if (it->height == seedheight+1) + { + seedhash = it->bl.prev_id; + break; + } + } + } else + { + seedhash = get_block_id_by_height(seedheight); + } + get_altblock_longhash(bei.bl, proof_of_work, get_current_blockchain_height(), bei.height, seedheight, seedhash); + } else + { + get_block_longhash(this, bei.bl, proof_of_work, bei.height, 0); + } if(!check_hash(proof_of_work, current_diff)) { MERROR_VER("Block with id: " << id << std::endl << " for alternative chain, does not have enough proof of work: " << proof_of_work << std::endl << " expected difficulty: " << current_diff); @@ -3694,7 +3718,7 @@ leave: proof_of_work = it->second; } else - proof_of_work = get_block_longhash(bl, blockchain_height); + proof_of_work = get_block_longhash(this, bl, blockchain_height, 0); // validate proof_of_work versus difficulty target if(!check_hash(proof_of_work, current_diffic)) @@ -4195,7 +4219,7 @@ void Blockchain::block_longhash_worker(uint64_t height, const epee::span()), m_starter_message_showed(false), m_target_blockchain_height(0), @@ -654,6 +654,8 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); 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); MGINFO("Loading checkpoints"); diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 5816cf125..bea24c74f 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -37,6 +37,7 @@ using namespace epee; #include "common/apply_permutation.h" #include "cryptonote_tx_utils.h" #include "cryptonote_config.h" +#include "blockchain.h" #include "cryptonote_basic/miner.h" #include "cryptonote_basic/tx_extra.h" #include "crypto/crypto.h" @@ -660,9 +661,54 @@ namespace cryptonote bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; bl.timestamp = 0; bl.nonce = nonce; - miner::find_nonce_for_given_block(bl, 1, 0); + miner::find_nonce_for_given_block(NULL, bl, 1, 0); bl.invalidate_hashes(); return true; } //--------------------------------------------------------------- + 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); + } + + bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners) + { + // block 202612 bug workaround + if (height == 202612) + { + static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; + epee::string_tools::hex_to_pod(longhash_202612, res); + return true; + } + blobdata bd = get_block_hashing_blob(b); + const int pow_variant = b.major_version >= 7 ? b.major_version - 6 : 0; + if (pow_variant >= 6) { + uint64_t seed_height; + if (rx_needhash(height, &seed_height)) { + crypto::hash hash; + if (pbc != NULL) + hash = pbc->get_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); + } + rx_slow_hash(bd.data(), bd.size(), res.data, miners); + } else { + crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height); + } + return true; + } + + crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const int miners) + { + crypto::hash p = crypto::null_hash; + get_block_longhash(pbc, b, p, height, miners); + return p; + } + + void get_block_longhash_reorg(const uint64_t split_height) + { + rx_reorg(split_height); + } } diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index d38aa7474..a325cd35e 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -117,6 +117,13 @@ namespace cryptonote , uint32_t nonce ); + 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); + void get_block_longhash_reorg(const uint64_t split_height); + } BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 85cea5573..d48b634d2 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1012,6 +1012,7 @@ namespace cryptonote case 1: res.pow_algorithm = "CNv1 (Cryptonight variant 1)"; break; case 2: case 3: res.pow_algorithm = "CNv2 (Cryptonight variant 2)"; break; case 4: case 5: res.pow_algorithm = "CNv4 (Cryptonight variant 4)"; break; + case 6: res.pow_algorithm = "RandomX"; break; default: res.pow_algorithm = "I'm not sure actually"; break; } if (res.is_background_mining_enabled) @@ -1295,6 +1296,18 @@ namespace cryptonote LOG_ERROR("Failed to create block template"); return false; } + if (b.major_version >= RX_BLOCK_VERSION) + { + uint64_t seed_height, next_height; + crypto::hash seed_hash; + crypto::rx_seedheights(res.height, &seed_height, &next_height); + seed_hash = m_core.get_block_id_by_height(seed_height); + res.seed_hash = string_tools::pod_to_hex(seed_hash); + if (next_height != seed_height) { + seed_hash = m_core.get_block_id_by_height(next_height); + res.next_seed_hash = string_tools::pod_to_hex(seed_hash); + } + } store_difficulty(wdiff, res.difficulty, res.wide_difficulty, res.difficulty_top64); blobdata block_blob = t_serializable_object_to_blob(b); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); @@ -1437,7 +1450,7 @@ namespace cryptonote return false; } b.nonce = req.starting_nonce; - miner::find_nonce_for_given_block(b, template_res.difficulty, template_res.height); + miner::find_nonce_for_given_block(&(m_core.get_blockchain_storage()), b, template_res.difficulty, template_res.height); submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b)); r = on_submitblock(submit_req, submit_res, error_resp, ctx); @@ -1482,7 +1495,7 @@ namespace cryptonote response.reward = get_block_reward(blk); response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); - response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; + response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(&(m_core.get_blockchain_storage()), blk, height, 0)) : ""; response.long_term_weight = m_core.get_blockchain_storage().get_db().get_block_long_term_weight(height); response.miner_tx_hash = string_tools::pod_to_hex(cryptonote::get_transaction_hash(blk.miner_tx)); return true; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 7eef9e651..90879d394 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -84,7 +84,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 7 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -937,6 +937,8 @@ namespace cryptonote uint64_t reserved_offset; uint64_t expected_reward; std::string prev_hash; + std::string seed_hash; + std::string next_seed_hash; blobdata blocktemplate_blob; blobdata blockhashing_blob; std::string status; @@ -954,6 +956,8 @@ namespace cryptonote KV_SERIALIZE(blockhashing_blob) KV_SERIALIZE(status) KV_SERIALIZE(untrusted) + KV_SERIALIZE(seed_hash) + KV_SERIALIZE(next_seed_hash) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response; diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 388808269..ed68f04e7 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -209,7 +209,7 @@ bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_ crypto::hash lh; cout << "BLOCK" << endl << endl; cout << (h = get_block_hash(b)) << endl; - cout << (lh = get_block_longhash(b, 0)) << endl; + cout << (lh = get_block_longhash(NULL, b, 0, 0)) << endl; cout << get_transaction_hash(b.miner_tx) << endl; cout << ::get_object_blobsize(b.miner_tx) << endl; //cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl; @@ -236,7 +236,7 @@ void tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_i bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) { generate_genesis_block(m_genesis, config::GENESIS_TX, config::GENESIS_NONCE); crypto::hash h = get_block_hash(m_genesis); - add_block(h, get_block_longhash(m_genesis, 0), m_genesis, block_to_blob(m_genesis)); + add_block(h, get_block_longhash(NULL, m_genesis, 0, 0), m_genesis, block_to_blob(m_genesis)); return true; } diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 614585349..44c4c7125 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -186,7 +186,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co // Nonce search... blk.nonce = 0; - while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(hf_ver), height)) + while (!miner::find_nonce_for_given_block(NULL, blk, get_test_difficulty(hf_ver), height)) blk.timestamp++; add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1); @@ -796,7 +796,7 @@ void fill_tx_sources_and_destinations(const std::vector& event void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) { blk.nonce = 0; - while (!miner::find_nonce_for_given_block(blk, diffic, height)) + while (!miner::find_nonce_for_given_block(NULL, blk, diffic, height)) blk.timestamp++; }