diff --git a/ConanPresets.json b/ConanPresets.json index 57065026..6e1749b3 100644 --- a/ConanPresets.json +++ b/ConanPresets.json @@ -4,6 +4,6 @@ "conan": {} }, "include": [ - "build/release/conan/build/debug/generators/CMakePresets.json" + "build/release/generators/CMakePresets.json" ] } \ No newline at end of file diff --git a/Makefile b/Makefile index e14b4bad..3d0ca4a5 100644 --- a/Makefile +++ b/Makefile @@ -166,8 +166,10 @@ test-debug: # Conan management — cmake modules from .build submodule conan-get: cmake -P $(CMAKE_DIR)/ConanGet.cmake +ifneq ($(CONAN_USER),) (CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote add conan_build $(CONAN_URL) && \ CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote login conan_build $(CONAN_USER) -p $(CONAN_PASSWORD)) || true +endif conan-upload: CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) upload "*" -r=conan_build --confirm diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 8b1445cb..1f3d0b0b 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(db) add_subdirectory(ethereum) +add_subdirectory(randomx) if(USE_BITCOIN_SECP256K1_FOR_ECDSA) option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF) diff --git a/contrib/randomx b/contrib/randomx new file mode 160000 index 00000000..cf15f402 --- /dev/null +++ b/contrib/randomx @@ -0,0 +1 @@ +Subproject commit cf15f4023ec8ddaa842f49111ba80ead1b5937f1 diff --git a/genesis-work/genesis_config.json b/genesis-work/genesis_config.json new file mode 100644 index 00000000..4feae998 --- /dev/null +++ b/genesis-work/genesis_config.json @@ -0,0 +1,30 @@ +{ + "payments": [ + { + "address_this": "iTHNdRWX6pKGXF1HqHgZyXie8EdaCwwdWUmsEnQhNojSUrkrR2eTemhFYkjWXbhdbyGcRBLgV2RYp3HhNAFCQ67w4bTHErZL1J", + "amount_this": 10000000.0, + "this_usd_price": "1.0", + "paid_prm": "", + "prm_usd_price": "", + "paid_xmr": "", + "xmr_usd_price": "", + "paid_qtum": "", + "qtum_usd_price": "", + "paid_bch": "", + "bch_usd_price": "", + "paid_rep": "", + "rep_usd_price": "", + "paid_dash": "", + "dash_usd_price": "", + "paid_ltc": "", + "ltc_usd_price": "", + "paid_eos": "", + "eos_usd_price": "", + "paid_eth": "", + "eth_usd_price": "", + "paid_btc": "", + "btc_usd_price": "" + } + ], + "proof_string": "Lethean genesis — the advantage of the nature of information being easy to spread but hard to stifle. - Satoshi Nakamoto" +} diff --git a/genesis-work/genesis_config.json.genesis.dictionary.txt b/genesis-work/genesis_config.json.genesis.dictionary.txt new file mode 100644 index 00000000..d49bb837 --- /dev/null +++ b/genesis-work/genesis_config.json.genesis.dictionary.txt @@ -0,0 +1,7 @@ +-------------genesis_acc.cpp------------- +const std::string ggenesis_tx_pub_key_str = "503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b"; +const crypto::public_key ggenesis_tx_pub_key = epee::string_tools::parse_tpod_from_hex_string(ggenesis_tx_pub_key_str); +extern const genesis_tx_dictionary_entry ggenesis_dict[1]; +const genesis_tx_dictionary_entry ggenesis_dict[1] = { +{12778448838847345770ULL,0} +}; diff --git a/genesis-work/genesis_config.json.genesis.txt b/genesis-work/genesis_config.json.genesis.txt new file mode 100644 index 00000000..95ebbe63 --- /dev/null +++ b/genesis-work/genesis_config.json.genesis.txt @@ -0,0 +1 @@ +01010000018080a0cfc8e0c8e38a0103bd6b4dda729d39dc85cb22a0b3db9b4b04d6464be9f3c84640bdca8bc0afb148000516503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b137a4c65746865616e2067656e6573697320e280942074686520616476616e74616765206f6620746865206e6174757265206f6620696e666f726d6174696f6e206265696e67206561737920746f2073707265616420627574206861726420746f20737469666c652e202d205361746f736869204e616b616d6f746f15000b029e4e0e0a0000 \ No newline at end of file diff --git a/genesis-work/genesis_config.json.genesis.uint64.array.txt b/genesis-work/genesis_config.json.genesis.uint64.array.txt new file mode 100644 index 00000000..94e32578 --- /dev/null +++ b/genesis-work/genesis_config.json.genesis.uint64.array.txt @@ -0,0 +1,14 @@ +--------- genesis.h--------- +#pragma pack(push, 1) +struct genesis_tx_raw_data +{ + uint64_t const v[27]; + uint8_t const r[1]; +}; +#pragma pack(pop) +extern const genesis_tx_raw_data ggenesis_tx_raw; + +--------- genesis.cpp--------- +const genesis_tx_raw_data ggenesis_tx_raw = {{ +0xa080800100000101,0x03018ae3c8e0c8cf,0xdc399d72da4d6bbd,0x4b9bdbb3a022cb85,0x46c8f3e94b46d604,0x48b1afc08bcabd40,0x67c1c73e50160500,0xbcbc01d8d7eaf2e3,0x26baa2848ac5b3eb,0xa15f105c1fcb1ea2,0x74654c7a132b9359,0x6e6567206e616568,0x9480e22073697365,0x7664612065687420,0x6f20656761746e61,0x616e206568742066,0x20666f2065727574,0x74616d726f666e69,0x6e696562206e6f69,0x7420797361652067,0x646165727073206f,0x7261682074756220,0x697473206f742064,0x53202d202e656c66,0x4e206968736f7461,0x156f746f6d616b61,0x000a0e4e9e020b00}, +{0x00}}; diff --git a/genesis-work/genesis_config.json.premine_stat.txt b/genesis-work/genesis_config.json.premine_stat.txt new file mode 100644 index 00000000..06d3f50c --- /dev/null +++ b/genesis-work/genesis_config.json.premine_stat.txt @@ -0,0 +1,14 @@ +NAME AMOUNT AMOUNT THIS USD EQ +bch 0.0000000000 0.000000000000 0.0000000000 +btc 0.0000000000 0.000000000000 0.0000000000 +dash 0.0000000000 0.000000000000 0.0000000000 +eos 0.0000000000 0.000000000000 0.0000000000 +eth 0.0000000000 0.000000000000 0.0000000000 +ltc 0.0000000000 0.000000000000 0.0000000000 +prm 0.0000000000 0.000000000000 0.0000000000 +qtum 0.0000000000 0.000000000000 0.0000000000 +rep 0.0000000000 0.000000000000 0.0000000000 + +TOTAL 0.000000000000 0.0000000000 + +PREMINE_AMOUNT 0 (0.000000000000THIS) diff --git a/genesis-work/premine_wallet b/genesis-work/premine_wallet new file mode 100644 index 00000000..69d00f3c Binary files /dev/null and b/genesis-work/premine_wallet differ diff --git a/genesis-work/premine_wallet.address b/genesis-work/premine_wallet.address new file mode 100644 index 00000000..3ca9e2d4 --- /dev/null +++ b/genesis-work/premine_wallet.address @@ -0,0 +1 @@ +iTHNdRWX6pKGXF1HqHgZyXie8EdaCwwdWUmsEnQhNojSUrkrR2eTemhFYkjWXbhdbyGcRBLgV2RYp3HhNAFCQ67w4bTHErZL1J \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d276457..d991d76e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -119,6 +119,7 @@ endif() add_library(currency_core ${CURRENCY_CORE}) add_dependencies(currency_core version config ${PCH_LIB_NAME}) target_link_libraries(currency_core config) +target_include_directories(currency_core PRIVATE ${RANDOMX_INCLUDE}) ENABLE_SHARED_PCH(currency_core CURRENCY_CORE) add_library(wallet ${WALLET}) @@ -164,19 +165,19 @@ target_link_libraries(currency_core config lmdb mdbx) add_executable(daemon ${DAEMON} ${P2P} ${CURRENCY_PROTOCOL}) add_dependencies(daemon version) -target_link_libraries(daemon rpc stratum currency_core crypto common miniupnpc::miniupnpc ZLIB::ZLIB ethash api::server ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(daemon rpc stratum currency_core crypto common miniupnpc::miniupnpc ZLIB::ZLIB ethash randomx api::server ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) ENABLE_SHARED_PCH(daemon DAEMON) ENABLE_SHARED_PCH_EXECUTABLE(daemon) add_executable(connectivity_tool ${CONN_TOOL}) add_dependencies(connectivity_tool version) -target_link_libraries(connectivity_tool currency_core crypto common ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(connectivity_tool currency_core crypto common ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) ENABLE_SHARED_PCH(connectivity_tool CONN_TOOL) ENABLE_SHARED_PCH_EXECUTABLE(connectivity_tool) add_executable(simplewallet ${SIMPLEWALLET}) add_dependencies(simplewallet version) -target_link_libraries(simplewallet wallet rpc currency_core crypto common ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(simplewallet wallet rpc currency_core crypto common ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) ENABLE_SHARED_PCH(simplewallet SIMPLEWALLET) ENABLE_SHARED_PCH_EXECUTABLE(simplewallet) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index e325f3a3..4304b9ac 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -59,6 +59,7 @@ target_link_libraries(api_server crypto common ethash + randomx ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} OpenSSL::SSL diff --git a/src/config/default.cmake b/src/config/default.cmake index 56929c50..b81a766f 100644 --- a/src/config/default.cmake +++ b/src/config/default.cmake @@ -21,7 +21,7 @@ set(base_reward_dust_threshold 1000000 CACHE STRING "BASE_REWARD_DUST_THRESHOLD" set(default_dust_threshold 0 CACHE STRING "DEFAULT_DUST_THRESHOLD" ) set(difficulty_pow_starter 1 CACHE STRING "DIFFICULTY_POW_STARTER" ) set(difficulty_pos_target 120 CACHE STRING "DIFFICULTY_POS_TARGET" ) -set(difficulty_pow_target 120 CACHE STRING "DIFFICULTY_POW_TARGET" ) +set(difficulty_pow_target 10 CACHE STRING "DIFFICULTY_POW_TARGET" ) set(difficulty_window 720 CACHE STRING "DIFFICULTY_WINDOW" ) set(difficulty_lag 15 CACHE STRING "DIFFICULTY_LAG" ) set(difficulty_cut 60 CACHE STRING "DIFFICULTY_CUT" ) diff --git a/src/currency_core/basic_pow_helpers.cpp b/src/currency_core/basic_pow_helpers.cpp index d0afcc7b..9ea80e12 100644 --- a/src/currency_core/basic_pow_helpers.cpp +++ b/src/currency_core/basic_pow_helpers.cpp @@ -27,39 +27,102 @@ using namespace epee; #include "crypto/crypto.h" #include "crypto/hash.h" #include "common/int-util.h" + +// RandomX PoW +#include "randomx.h" + +// Keep ethash headers for stratum/RPC compatibility (epoch seed helpers) #include "ethereum/libethash/ethash/ethash.hpp" -#include "ethereum/libethash/ethash/progpow.hpp" + +#include +#include +#include +#include +#include namespace currency { + //-------------------------------------------------------------- + // RandomX global state — full dataset mode for fast hashing + //-------------------------------------------------------------- + static randomx_cache* rx_cache = nullptr; + static randomx_dataset* rx_dataset = nullptr; + static randomx_flags rx_flags = RANDOMX_FLAG_DEFAULT; + static std::once_flag rx_init_flag; - //-------------------------------------------------------------- - int ethash_custom_log_get_level() + static void init_randomx() { - return epee::log_space::get_set_log_detalisation_level(); - } - //-------------------------------------------------------------- - void ethash_custom_log(const std::string& m, bool add_callstack) - { - std::string msg = epee::log_space::log_singletone::get_prefix_entry() + "[ETHASH]" + m; - if (add_callstack) - msg = msg + "callstask: " + epee::misc_utils::get_callstack(); + rx_flags = randomx_get_flags() | RANDOMX_FLAG_FULL_MEM; + LOG_PRINT_L0("[RandomX] Initializing with flags: " << static_cast(rx_flags)); - epee::log_space::log_singletone::do_log_message(msg, LOG_LEVEL_0, epee::log_space::console_color_default, true, LOG_DEFAULT_TARGET); - } - //-------------------------------------------------------------- - void init_ethash_log_if_necessary() - { - static bool inited = false; - if (inited) + rx_cache = randomx_alloc_cache(rx_flags); + if (!rx_cache) + { + LOG_ERROR("[RandomX] Failed to allocate cache"); + throw std::bad_alloc(); + } + + static const char rx_key[] = "LetheanRandomXv1"; + randomx_init_cache(rx_cache, rx_key, sizeof(rx_key) - 1); + LOG_PRINT_L0("[RandomX] Cache initialized with key \"LetheanRandomXv1\""); + + rx_dataset = randomx_alloc_dataset(rx_flags); + if (!rx_dataset) + { + LOG_ERROR("[RandomX] Failed to allocate dataset, falling back to light mode"); + rx_flags = rx_flags & ~RANDOMX_FLAG_FULL_MEM; return; + } - ethash::access_custom_log_level_function() = ðash_custom_log_get_level; - ethash::access_custom_log_function() = ðash_custom_log; + // Multi-threaded dataset init + unsigned long item_count = randomx_dataset_item_count(); + unsigned int num_threads = std::thread::hardware_concurrency(); + if (num_threads == 0) num_threads = 4; + if (num_threads > 16) num_threads = 16; - inited = true; + LOG_PRINT_L0("[RandomX] Initializing dataset (" << item_count << " items, " << num_threads << " threads)..."); + + std::vector threads; + unsigned long per_thread = item_count / num_threads; + unsigned long remainder = item_count % num_threads; + + for (unsigned int i = 0; i < num_threads; ++i) + { + unsigned long start = i * per_thread + std::min((unsigned long)i, remainder); + unsigned long count = per_thread + (i < remainder ? 1 : 0); + threads.emplace_back([start, count]() { + randomx_init_dataset(rx_dataset, rx_cache, start, count); + }); + } + for (auto& t : threads) t.join(); + + LOG_PRINT_L0("[RandomX] Dataset initialized OK"); } - //------------------------------------------------------------------ + + static randomx_vm* get_thread_vm() + { + std::call_once(rx_init_flag, init_randomx); + + static thread_local randomx_vm* vm = nullptr; + if (!vm) + { + if (rx_dataset) + vm = randomx_create_vm(rx_flags, nullptr, rx_dataset); + else + vm = randomx_create_vm(rx_flags & ~RANDOMX_FLAG_FULL_MEM, rx_cache, nullptr); + + if (!vm) + { + LOG_ERROR("[RandomX] Failed to create VM"); + throw std::bad_alloc(); + } + } + return vm; + } + + //-------------------------------------------------------------- + // Ethash helpers kept for stratum/RPC backward compat + //-------------------------------------------------------------- int ethash_height_to_epoch(uint64_t height) { return static_cast(height / ETHASH_EPOCH_LENGTH); @@ -75,17 +138,20 @@ namespace currency //-------------------------------------------------------------- crypto::hash get_block_longhash(uint64_t height, const crypto::hash& block_header_hash, uint64_t nonce) { - init_ethash_log_if_necessary(); - int epoch = ethash_height_to_epoch(height); - std::shared_ptr p_context = progpow::get_global_epoch_context_full(static_cast(epoch)); - if (!p_context) - { - LOG_ERROR("fatal error: get_global_epoch_context_full failed, throwing bad_alloc..."); - throw std::bad_alloc(); - } - auto res_eth = progpow::hash(*p_context, static_cast(height), *(ethash::hash256*)&block_header_hash, nonce); + // Combine header hash + nonce into a single input buffer + struct { + crypto::hash header; + uint64_t nonce; + } input; + input.header = block_header_hash; + input.nonce = nonce; + + char hash_out[RANDOMX_HASH_SIZE]; + randomx_vm* vm = get_thread_vm(); + randomx_calculate_hash(vm, &input, sizeof(input), hash_out); + crypto::hash result = currency::null_hash; - memcpy(&result.data, &res_eth.final_hash, sizeof(res_eth.final_hash)); + std::memcpy(&result.data, hash_out, sizeof(result.data)); return result; } //--------------------------------------------------------------- diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 62a43393..f056193f 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -1428,14 +1428,7 @@ wide_difficulty_type blockchain_storage::calc_diff_at_h_from_timestamps(std::vec { wide_difficulty_type dif; TIME_MEASURE_START_PD(target_calculating_calc); - if (m_core_runtime_config.is_hardfork_active_for_height(1, h)) - { - dif = next_difficulty_2(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); - } - else - { - dif = next_difficulty_1(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); - } + dif = next_difficulty_lwma(timestamps, commulative_difficulties, pos ? global_difficulty_pos_target : global_difficulty_pow_target, pos ? global_difficulty_pos_starter : global_difficulty_pow_starter); TIME_MEASURE_FINISH_PD(target_calculating_calc); return dif; } diff --git a/src/currency_core/difficulty.cpp b/src/currency_core/difficulty.cpp index 592b3be7..843d7823 100644 --- a/src/currency_core/difficulty.cpp +++ b/src/currency_core/difficulty.cpp @@ -267,4 +267,75 @@ namespace currency { } return summ / devider; } + + //-------------------------------------------------------------- + // LWMA-1 difficulty algorithm (zawy12) + // Linear Weighted Moving Average — adjusts every block with a + // ~60-block window. Battle-tested in Monero against ASICs and + // botnets. Much faster convergence than the 720-block Zano algo. + //-------------------------------------------------------------- + wide_difficulty_type next_difficulty_lwma(vector& timestamps, vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter) + { + const int64_t T = static_cast(target_seconds); + const size_t N = 60; // LWMA window size (solve times) + + size_t length = timestamps.size(); + CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), difficulty_starter, + "LWMA: timestamps/difficulties size mismatch"); + + if (length <= 1) + return difficulty_starter; + + // We need at most N+1 entries (giving N solve times) + if (length > N + 1) + { + timestamps.resize(N + 1); + cumulative_difficulties.resize(N + 1); + length = N + 1; + } + + // Input arrives newest-first; LWMA needs oldest-first + std::reverse(timestamps.begin(), timestamps.end()); + std::reverse(cumulative_difficulties.begin(), cumulative_difficulties.end()); + + // Now: [0]=oldest … [length-1]=newest + size_t n = length - 1; // number of solve-time intervals + + int64_t weighted_solvetimes = 0; + + for (size_t i = 1; i <= n; i++) + { + int64_t st = static_cast(timestamps[i]) + - static_cast(timestamps[i - 1]); + + // Clamp to [-6T, 6T] to limit timestamp-manipulation impact + if (st < -(6 * T)) st = -(6 * T); + if (st > (6 * T)) st = (6 * T); + + weighted_solvetimes += st * static_cast(i); + } + + // Guard against zero / negative (would be pathological timestamps) + if (weighted_solvetimes <= 0) + weighted_solvetimes = 1; + + wide_difficulty_type total_work = cumulative_difficulties[n] - cumulative_difficulties[0]; + + // LWMA-1 formula: + // next_D = total_work * T * (n+1) / (2 * weighted_solvetimes * n) + // + // The divisor (n+1)/(2*n) normalises the linear weights 1..n + // whose sum is n*(n+1)/2. + boost::multiprecision::uint256_t next_d = + (boost::multiprecision::uint256_t(total_work) * T * (n + 1)) + / (boost::multiprecision::uint256_t(2) * weighted_solvetimes * n); + + if (next_d < 1) + next_d = 1; + + if (next_d > max128bit) + return difficulty_starter; + + return next_d.convert_to(); + } } diff --git a/src/currency_core/difficulty.h b/src/currency_core/difficulty.h index 8ffb64df..f4187c05 100644 --- a/src/currency_core/difficulty.h +++ b/src/currency_core/difficulty.h @@ -32,6 +32,7 @@ namespace currency bool check_hash(const crypto::hash &hash, wide_difficulty_type difficulty); wide_difficulty_type next_difficulty_1(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter); wide_difficulty_type next_difficulty_2(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter); + wide_difficulty_type next_difficulty_lwma(std::vector& timestamps, std::vector& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter); uint64_t difficulty_to_boundary(wide_difficulty_type difficulty); void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result); } diff --git a/src/genesis/_genesis.cpp.gen b/src/genesis/_genesis.cpp.gen index 24ca4e63..cfc5867b 100644 --- a/src/genesis/_genesis.cpp.gen +++ b/src/genesis/_genesis.cpp.gen @@ -1,3 +1,3 @@ const genesis_tx_raw_data ggenesis_tx_raw = {{ - 0xe980800100000101,0xfbfa3a0316deb183,0x0e4d0bf7103b2df2,0x682df55627fc33ed,0xcd945b3d70689611,0x16050066b218269a,0x34af8b24a9c955d2,0x3f514b3a2be34ee9,0xb6525b442410e776,0x256d4d83666fb42a,0x6b61742074496b13,0x6e61766461207365,0x20666f2065676174,0x7574616e20656874,0x6e6920666f206572,0x6f6974616d726f66,0x20676e696562206e,0x206f742079736165,0x6220646165727073,0x2064726168207475,0x6c66697473206f74,0x746153202d202e65,0x6b614e206968736f,0x0b00156f746f6d61}, - {0x02,0x8a,0x56,0x0e,0x0a,0x00,0x00}}; +0xa080800100000101,0x03018ae3c8e0c8cf,0xdc399d72da4d6bbd,0x4b9bdbb3a022cb85,0x46c8f3e94b46d604,0x48b1afc08bcabd40,0x67c1c73e50160500,0xbcbc01d8d7eaf2e3,0x26baa2848ac5b3eb,0xa15f105c1fcb1ea2,0x74654c7a132b9359,0x6e6567206e616568,0x9480e22073697365,0x7664612065687420,0x6f20656761746e61,0x616e206568742066,0x20666f2065727574,0x74616d726f666e69,0x6e696562206e6f69,0x7420797361652067,0x646165727073206f,0x7261682074756220,0x697473206f742064,0x53202d202e656c66,0x4e206968736f7461,0x156f746f6d616b61,0x000a0e4e9e020b00}, +{0x00}}; diff --git a/src/genesis/_genesis.h.gen b/src/genesis/_genesis.h.gen index 086767c9..9ac78c62 100644 --- a/src/genesis/_genesis.h.gen +++ b/src/genesis/_genesis.h.gen @@ -1,5 +1,5 @@ struct genesis_tx_raw_data { - uint64_t const v[42]; - uint8_t const r[42]; + uint64_t const v[27]; + uint8_t const r[1]; }; diff --git a/src/genesis/_genesis_acc.cpp.gen b/src/genesis/_genesis_acc.cpp.gen index 2e2c8807..fc496fbc 100644 --- a/src/genesis/_genesis_acc.cpp.gen +++ b/src/genesis/_genesis_acc.cpp.gen @@ -1,6 +1,6 @@ -const std::string ggenesis_tx_pub_key_str = "d255c9a9248baf34e94ee32b3a4b513f76e71024445b52b62ab46f66834d6d25"; +const std::string ggenesis_tx_pub_key_str = "503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b"; const crypto::public_key ggenesis_tx_pub_key = epee::string_tools::parse_tpod_from_hex_string(ggenesis_tx_pub_key_str); extern const genesis_tx_dictionary_entry ggenesis_dict[1]; const genesis_tx_dictionary_entry ggenesis_dict[1] = { - {1056117391700764468ULL,0} +{12778448838847345770ULL,0} }; diff --git a/src/stratum/stratum_server.cpp b/src/stratum/stratum_server.cpp index 05e1c6cc..b811301f 100644 --- a/src/stratum/stratum_server.cpp +++ b/src/stratum/stratum_server.cpp @@ -808,6 +808,7 @@ namespace , m_context(context) , m_connection_initialized(false) , m_last_reported_hashrate(0) + , m_use_standard_stratum(false) { LOG_PRINT_CC(m_context, "stratum_protocol_handler::ctor()", LOG_LEVEL_4); } @@ -915,6 +916,11 @@ namespace m_methods_handlers.insert(std::make_pair("eth_getWork", &this_t::handle_method_eth_getWork)); m_methods_handlers.insert(std::make_pair("eth_submitHashrate", &this_t::handle_method_eth_submitHashrate)); m_methods_handlers.insert(std::make_pair("eth_submitWork", &this_t::handle_method_eth_submitWork)); + // Standard ETH stratum (mining.*) — used by XMRig-based miners + m_methods_handlers.insert(std::make_pair("mining.subscribe", &this_t::handle_method_mining_subscribe)); + m_methods_handlers.insert(std::make_pair("mining.authorize", &this_t::handle_method_mining_authorize)); + m_methods_handlers.insert(std::make_pair("mining.submit", &this_t::handle_method_mining_submit)); + m_methods_handlers.insert(std::make_pair("mining.extranonce.subscribe", &this_t::handle_method_mining_extranonce_subscribe)); } } @@ -994,6 +1000,58 @@ namespace return m_config.handle_work(this, id, worker, nonce, header_hash); } + // --- Standard ETH stratum (mining.*) handlers --- + + bool handle_method_mining_subscribe(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + m_use_standard_stratum = true; + // Respond with: [[["mining.notify", "subscription_id"]], "extranonce"] + // Use "0000" as a default 2-byte extra nonce + send_response(id, R"("result":[["mining.notify","1"],"0000"])"); + return true; + } + + bool handle_method_mining_authorize(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + // params: [user, pass] — same format as eth_submitLogin + std::string user_str, pass_str; + epee::serialization::harray params_array = ps.get_first_value("params", user_str, nullptr); + if (params_array != nullptr) + ps.get_next_value(params_array, pass_str); + + std::string worker_str = std::to_string(m_config.get_number_id_for_nameless_worker()); + + LOG_PRINT_CC(m_context, "Stratum [mining.authorize] USER: " << user_str << ", pass: " << pass_str << ", worker: " << worker_str, LOG_LEVEL_3); + return m_config.handle_login(this, id, user_str, pass_str, worker_str, 0); + } + + bool handle_method_mining_submit(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + // params: [worker, job_id, nonce, header_hash, mix_hash] + std::string worker_str, job_id_str, nonce_str, header_str, mixhash_str; + epee::serialization::harray params_array = ps.get_first_value("params", worker_str, nullptr); + bool r = params_array != nullptr && ps.get_next_value(params_array, job_id_str); + r = r && ps.get_next_value(params_array, nonce_str); + r = r && ps.get_next_value(params_array, header_str); + r = r && ps.get_next_value(params_array, mixhash_str); + CHECK_AND_ASSERT_MES(r, false, "Incorrect parameters for mining.submit"); + + uint64_t nonce = 0; + CHECK_AND_ASSERT_MES(pod_from_net_format_reverse(nonce_str, nonce, true), false, "Can't parse nonce from " << nonce_str); + crypto::hash header_hash = null_hash; + CHECK_AND_ASSERT_MES(pod_from_net_format(header_str, header_hash), false, "Can't parse header hash from " << header_str); + + return m_config.handle_work(this, id, worker_str, nonce, header_hash); + } + + bool handle_method_mining_extranonce_subscribe(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section) + { + send_response(id, R"("result":true)"); + return true; + } + + // --- + void send(const std::string& data) { static_cast(m_p_connection)->do_send(data.c_str(), data.size()); @@ -1002,6 +1060,40 @@ namespace void send_notification(const std::string& json) { + if (m_use_standard_stratum) + { + // Convert EthProxy "result":["header","seed","target","height"] to + // standard stratum mining.notify format: "method":"mining.notify","params":["job_id","header","seed","target",true,"height"] + // json looks like: "result":["0xHEADER","0xSEED","0xTARGET","0xHEIGHT"] + // We need: "method":"mining.notify","params":["job_id","0xHEADER","0xSEED","0xTARGET",true,"0xHEIGHT"] + std::string notify_json = json; + size_t pos = notify_json.find(R"("result":)"); + if (pos != std::string::npos) + { + // Extract the array content from "result":[...] + size_t arr_start = notify_json.find('[', pos); + size_t arr_end = notify_json.rfind(']'); + if (arr_start != std::string::npos && arr_end != std::string::npos) + { + std::string arr_content = notify_json.substr(arr_start + 1, arr_end - arr_start - 1); + // Insert job ID at beginning and clean_jobs=true before height + // arr_content is: "0xHEADER","0xSEED","0xTARGET","0xHEIGHT" + // We need: "job_id","0xHEADER","0xSEED","0xTARGET",true,"0xHEIGHT" + static uint64_t s_job_counter = 0; + std::string job_id = "\"" + std::to_string(++s_job_counter) + "\""; + + // Find the last comma to insert clean_jobs before height + size_t last_comma = arr_content.rfind(','); + if (last_comma != std::string::npos) + { + std::string params = job_id + "," + arr_content.substr(0, last_comma) + ",true" + arr_content.substr(last_comma); + send(R"({"jsonrpc":"2.0","method":"mining.notify","params":[)" + params + "]}\n"); + return; + } + } + } + } + // EthProxy mode (default): send as-is // JSON-RPC 2.0 spec: "A Notification is a Request object without an "id" member." send(R"({"jsonrpc":"2.0",)" + json + "}" "\n"); // LF character is not specified by JSON-RPC standard, but it is REQUIRED by ethminer 0.12 to work } @@ -1083,10 +1175,11 @@ namespace epee::critical_section m_work_change_lock; uint64_t m_last_reported_hashrate; + bool m_use_standard_stratum; // true = mining.* protocol, false = eth_* EthProxy protocol typedef bool (this_t::*method_handler_func_t)(const jsonrpc_id_t& id, epee::serialization::portable_storage& ps, epee::serialization::portable_storage::hsection params_section); static std::unordered_map m_methods_handlers; - + std::atomic m_connection_initialized; }; // class stratum_protocol_handler //============================================================================================================================== diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 812dca5d..0d0896c6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,14 +28,14 @@ add_executable(net_load_tests_srv net_load_tests/srv.cpp) add_dependencies(coretests version) -target_link_libraries(coretests rpc wallet currency_core common crypto ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(functional_tests rpc wallet currency_core crypto common ZLIB::ZLIB ethash miniupnpc::miniupnpc ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(coretests rpc wallet currency_core common crypto ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(functional_tests rpc wallet currency_core crypto common ZLIB::ZLIB ethash randomx miniupnpc::miniupnpc ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) target_link_libraries(hash-tests crypto ethash) -target_link_libraries(hash-target-tests crypto currency_core ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(performance_tests wallet rpc currency_core common crypto ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(unit_tests wallet currency_core common crypto gtest_main ZLIB::ZLIB ethash ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) -target_link_libraries(net_load_tests_clt currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) -target_link_libraries(net_load_tests_srv currency_core common crypto gtest_main ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(hash-target-tests crypto currency_core ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(performance_tests wallet rpc currency_core common crypto ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(unit_tests wallet currency_core common crypto gtest_main ZLIB::ZLIB ethash randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(net_load_tests_clt currency_core common crypto gtest_main randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) +target_link_libraries(net_load_tests_srv currency_core common crypto gtest_main randomx ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) if(NOT MSVC) set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") diff --git a/tests/core_tests/alias_tests.cpp b/tests/core_tests/alias_tests.cpp index 266e4164..c12ea649 100644 --- a/tests/core_tests/alias_tests.cpp +++ b/tests/core_tests/alias_tests.cpp @@ -1046,17 +1046,20 @@ bool gen_alias_too_small_reward::init_runtime_config(currency::core& c, size_t e bool gen_alias_too_small_reward::generate(std::vector& events) const { // pay for alias too small and see, if it's ok + // Generate distinct addresses from fresh accounts + account_base alias_accs[10]; + for (auto& a : alias_accs) a.generate(); const alias_entry aliases[] = { - {"a", pub_addr_from_string("ZxD95C97dFqVBsuTMA1VKjFxx8Cc7by2YTA9kLhFc8JB3zJ3qKKXRm9Lu2YQsjPP5UKhCSfLLEqJr5ovyQNYYQWV1Pv98fRzt")}, - {"bb", pub_addr_from_string("ZxDL9euTT4C9FUj28QY1RdWnreMHJiWfrPs39rXhrgai8H4pmaFzJ4vUUYRmHhNxToN64H1U5sMnaHuD3S4kVbyY1mKHnERVZ")}, - {"ccc", pub_addr_from_string("ZxBrpHp3xrjLrMMSyJUg44YmyJVZjetouVFdtqLfxpHUMSxiEyyQ7iKSj4sr6gn7qwXrj6YSw7UjJZLyc1H37QtF2p96c2gAD")}, - {"dddd", pub_addr_from_string("ZxDtT1pTwt6R2t3eGw9VD6N1heHCKNLKuCFUvqgHpXkAVnPkfai4KDYEjRSV8E42XKN3MJeaHJMaxa9hUmaXLyHm2nQ12aX93")}, - {"eeeee", pub_addr_from_string("ZxCABdwUJpqHstWJUHQ21piADBwaSsXcAh5EPtpSr8xXderWqvDef566ReFGrRqBUrE2tCgZ3HE5XRuxoq8mNTrP2X4J35yQq")}, - {"ffffff", pub_addr_from_string("ZxC34uAJJ2iW15GkvcqaQd4RKZdu16tpmf4ubmsirw7eFtKoLi2xswhNqy3Q4VacCq5mM7zuYyoWEW8AS5HGtoXr1m9RuTUuu")}, - {"ggggggg", pub_addr_from_string("ZxDHxZizSe5MNQoRkC1unqTrhYUkh1ZG7iEXMzLatyZ5EHRPat4Ls4ZRnN4CYLvJLq5F5gxdDtu17Zrvur7dcqU52sv2pryn7")}, - {"hhhhhhhh", pub_addr_from_string("ZxDXME4qrbh7mAbrqDmCbzGj14VQP1n9KLLi7fXBMNvDd5UUpcevCSXQ9zSkZcJtbzBS7u16NiykAiv3q9VkZySL2ySB6hTfL")}, - {"iiiiiiiii", pub_addr_from_string("ZxDtpxbC2bN8yu3J49tsUYUSoPYTnAgjmBogzFviUg3t2fGfWzmZ2gbKNC1XKVdMEE2hoW5sULs2hAF5T3igoAVW2MsHUmaj4")}, - {"jjjjjjjjjj", pub_addr_from_string("ZxCBLxnctYwB37YZi7MsJqBCujXzkBeJEh7wPbYrFUvMiqXiPLkyBRAh6ahQ6wre2tGR8FHesZwKn2zYPkTuibyu2648g2CGV")} + {"a", alias_accs[0].get_keys().account_address}, + {"bb", alias_accs[1].get_keys().account_address}, + {"ccc", alias_accs[2].get_keys().account_address}, + {"dddd", alias_accs[3].get_keys().account_address}, + {"eeeee", alias_accs[4].get_keys().account_address}, + {"ffffff", alias_accs[5].get_keys().account_address}, + {"ggggggg", alias_accs[6].get_keys().account_address}, + {"hhhhhhhh", alias_accs[7].get_keys().account_address}, + {"iiiiiiiii", alias_accs[8].get_keys().account_address}, + {"jjjjjjjjjj", alias_accs[9].get_keys().account_address} }; const size_t aliases_count = sizeof aliases / sizeof aliases[0]; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 9bff9d9b..29852434 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -133,13 +133,13 @@ bool test_block_creation() { uint64_t vszs[] = {80,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,9391,476,476,475,475,474,475,8819,8301,475,472,4302,5316,14347,16620,19583,19403,19728,19442,19852,19015,19000,19016,19795,19749,18087,19787,19704,19750,19267,19006,19050,19445,19407,19522,19546,19788,19369,19486,19329,19370,18853,19600,19110,19320,19746,19474,19474,19743,19494,19755,19715,19769,19620,19368,19839,19532,23424,28287,30707}; std::vector szs(&vszs[0], &vszs[90]); - account_public_address adr; - bool r = get_account_address_from_str(adr, "ZxDLGBGXbjo5w51tJkvxEPHFRr7Xft4hf33N8EkJPndoGCqocQF1mzpZqYwXByx5gMbfQuPAAB9vj79EFR6Jwkgu1o3aMQPwJ"); - CHECK_AND_ASSERT_MES(r, false, "failed to import"); + account_base acc; + acc.generate(); + account_public_address adr = acc.get_keys().account_address; uint64_t block_reward_without_fee = 0; uint64_t block_reward = 0; block b; - r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, block_reward_without_fee, block_reward, TRANSACTION_VERSION_PRE_HF4, 0); + bool r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, adr, b.miner_tx, block_reward_without_fee, block_reward, TRANSACTION_VERSION_PRE_HF4, 0); return r; } diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index adf78c48..0aa2d0f0 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -441,7 +441,6 @@ namespace "\x22\x09\x39\x68\x9e\xdf\x1a\xbd\x5b\xc1\xd0\x31\xf7\x3e\xcd\x6c" "\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d" "\x00"); - std::string test_keys_addr_str = "ZxDqHy6WnyYY5yQcdApjMb8tVPik5BC3LFdaevfbGq7X1KY5vdsWmUi5UQgse2GBZFbMsb47TFqBmPpdFHDDwDxR2ZuZ6zX4W"; // correct str address depends on CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX value } TEST(get_account_address_as_str, works_correctly) @@ -449,13 +448,23 @@ TEST(get_account_address_as_str, works_correctly) currency::account_public_address addr; ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr)); std::string addr_str = currency::get_account_address_as_str(addr); - ASSERT_EQ(addr_str, test_keys_addr_str); + // Verify round-trip: parse the generated address back and check keys match + currency::account_public_address addr2; + ASSERT_TRUE(currency::get_account_address_from_str(addr2, addr_str)); + std::string blob; + ASSERT_TRUE(serialization::dump_binary(addr2, blob)); + ASSERT_EQ(blob, test_serialized_keys); } TEST(get_account_address_from_str, handles_valid_address) { + // Generate a valid address from the test keys + currency::account_public_address addr_orig; + ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr_orig)); + std::string valid_addr_str = currency::get_account_address_as_str(addr_orig); + currency::account_public_address addr; - ASSERT_TRUE(currency::get_account_address_from_str(addr, test_keys_addr_str)); + ASSERT_TRUE(currency::get_account_address_from_str(addr, valid_addr_str)); std::string blob; ASSERT_TRUE(serialization::dump_binary(addr, blob)); @@ -464,10 +473,12 @@ TEST(get_account_address_from_str, handles_valid_address) TEST(get_account_address_from_str, fails_on_invalid_address_format) { - currency::account_public_address addr; - std::string addr_str = test_keys_addr_str; + currency::account_public_address addr_orig; + ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr_orig)); + std::string addr_str = currency::get_account_address_as_str(addr_orig); addr_str[0] = '0'; + currency::account_public_address addr; ASSERT_FALSE(currency::get_account_address_from_str(addr, addr_str)); } @@ -560,11 +571,11 @@ struct addr_entry_t uint8_t flags; }; -addr_entry_t addr_entries[] = +addr_entry_t addr_entries[] = { { - // classic normal address - "ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH", // address + // classic normal address — generated at runtime from keys + "", // address (empty = auto-generate from keys + prefix) "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "", // payment_id_hex @@ -572,31 +583,15 @@ addr_entry_t addr_entries[] = }, { // classic integrated address - "iZ2Zi6RmTWwcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTqEsjvJoco1aLSZXS6T", // address + "", "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "87440d0b9acc42f1", // payment_id_hex 0 // flags }, - { - // new format normal address with custom flags - "ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3APrDvRoL5C", // address - "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key - "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key - "", // payment_id_hex - 0xfe // flags - }, - { - // new format integrated address with custom flags - "iZ4mBxubNfqcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTrG7nU5rRCWmcozLaMoY95sAbo6", // address - "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key - "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key - "3ba0527bcfb1fa93630d28eed6", // payment_id - 0xfe // flags - }, { // normal auditable address - "aZxb9Et6FhP9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJMEmqQFn", // address + "", "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "", // payment_id @@ -604,7 +599,7 @@ addr_entry_t addr_entries[] = }, { // auditable integrated address - "aiZXDondHWu9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJM9xJH8EbjuRiMJgFmPRATsEV9", // address + "", "a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key "9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key "3ba0527bcfb1fa93630d28eed6", // payment_id @@ -614,16 +609,34 @@ addr_entry_t addr_entries[] = void check_add_entry(const addr_entry_t& ae) { - std::string payment_id, payment_id_hex; - currency::account_public_address addr = AUTO_VAL_INIT(addr); + // Build address struct from hex keys + currency::account_public_address addr_built = AUTO_VAL_INIT(addr_built); + ASSERT_TRUE(epee::string_tools::parse_tpod_from_hex_string(ae.view_pub_key, addr_built.view_public_key)); + ASSERT_TRUE(epee::string_tools::parse_tpod_from_hex_string(ae.spend_pub_key, addr_built.spend_public_key)); + addr_built.flags = ae.flags; - ASSERT_TRUE(currency::get_account_address_and_payment_id_from_str(addr, payment_id, ae.address)); - payment_id_hex = epee::string_tools::buff_to_hex_nodelimer(payment_id); + // Encode to string (with or without payment id) + std::string payment_id_bin; + if (!ae.payment_id_hex.empty()) + epee::string_tools::parse_hexstr_to_binbuff(ae.payment_id_hex, payment_id_bin); - ASSERT_EQ(ae.flags, addr.flags); - ASSERT_EQ(ae.payment_id_hex, payment_id_hex); - ASSERT_EQ(ae.view_pub_key, epee::string_tools::pod_to_hex(addr.view_public_key)); - ASSERT_EQ(ae.spend_pub_key, epee::string_tools::pod_to_hex(addr.spend_public_key)); + std::string addr_str = payment_id_bin.empty() + ? currency::get_account_address_as_str(addr_built) + : currency::get_account_address_and_payment_id_as_str(addr_built, payment_id_bin); + + ASSERT_FALSE(addr_str.empty()); + + // Round-trip: parse the generated address back + currency::account_public_address addr_parsed = AUTO_VAL_INIT(addr_parsed); + std::string payment_id_parsed; + ASSERT_TRUE(currency::get_account_address_and_payment_id_from_str(addr_parsed, payment_id_parsed, addr_str)); + + std::string payment_id_parsed_hex = epee::string_tools::buff_to_hex_nodelimer(payment_id_parsed); + + ASSERT_EQ(ae.flags, addr_parsed.flags); + ASSERT_EQ(ae.payment_id_hex, payment_id_parsed_hex); + ASSERT_EQ(ae.view_pub_key, epee::string_tools::pod_to_hex(addr_parsed.view_public_key)); + ASSERT_EQ(ae.spend_pub_key, epee::string_tools::pod_to_hex(addr_parsed.spend_public_key)); } TEST(auditable_addresses, basic) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index fd0807b5..59c36829 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -10,6 +10,7 @@ #include #include "currency_core/currency_basic.h" #include "currency_core/currency_format_utils.h" +#include "currency_core/account.h" #include "serialization/serialization.h" #include "serialization/binary_archive.h" #include "serialization/json_archive.h" @@ -727,7 +728,7 @@ TEST(Serialization, serializes_transacion_versions) c.comment = "sdcwdcwcewdcecevthbtg"; tx.extra.push_back(c); extra_alias_entry eae = AUTO_VAL_INIT(eae); - currency::get_account_address_from_str(eae.m_address, "ZxDcDWmA7Yj32srfjMHAY6WPzBB8uqpvzKxEsAjDZU6NRg1yZsRfmr87mLXCvMRHXd5n2kdRWhbqA3WWTbEW4jLd1XxL46tnv"); + { currency::account_base tmp_acc; tmp_acc.generate(); eae.m_address = tmp_acc.get_keys().account_address; } eae.m_alias = "eokcmeockme"; eae.m_text_comment = "sdssccsc"; tx.extra.push_back(eae);