feat: RandomX PoW, LWMA difficulty, stratum mining.* protocol, new genesis
Replace ProgPowZ with RandomX for ASIC-resistant proof-of-work. The full dataset is initialized multi-threaded at startup with the key "LetheanRandomXv1". Thread-local VMs are created on demand. Switch difficulty algorithm from Zano's 720-block window to LWMA-1 (zawy12) with a 60-block window for much faster convergence after hashrate changes. Target block time set to 10s for PoW. Add standard stratum mining.* protocol handlers (subscribe, authorize, submit, extranonce.subscribe) alongside existing EthProxy eth_* handlers, with automatic protocol detection and mining.notify translation for XMRig-based miners. Generate fresh Lethean genesis block and premine wallet. Replace all remaining hardcoded Zano addresses in tests with runtime-generated keys to avoid prefix mismatches. Link RandomX library across all build targets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7ee2265cae
commit
467c64d015
27 changed files with 423 additions and 109 deletions
|
|
@ -4,6 +4,6 @@
|
||||||
"conan": {}
|
"conan": {}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"build/release/conan/build/debug/generators/CMakePresets.json"
|
"build/release/generators/CMakePresets.json"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
2
Makefile
2
Makefile
|
|
@ -166,8 +166,10 @@ test-debug:
|
||||||
# Conan management — cmake modules from .build submodule
|
# Conan management — cmake modules from .build submodule
|
||||||
conan-get:
|
conan-get:
|
||||||
cmake -P $(CMAKE_DIR)/ConanGet.cmake
|
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 add conan_build $(CONAN_URL) && \
|
||||||
CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote login conan_build $(CONAN_USER) -p $(CONAN_PASSWORD)) || true
|
CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) remote login conan_build $(CONAN_USER) -p $(CONAN_PASSWORD)) || true
|
||||||
|
endif
|
||||||
|
|
||||||
conan-upload:
|
conan-upload:
|
||||||
CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) upload "*" -r=conan_build --confirm
|
CONAN_HOME=$(CONAN_CACHE) $(CONAN_EXECUTABLE) upload "*" -r=conan_build --confirm
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
add_subdirectory(db)
|
add_subdirectory(db)
|
||||||
add_subdirectory(ethereum)
|
add_subdirectory(ethereum)
|
||||||
|
add_subdirectory(randomx)
|
||||||
|
|
||||||
if(USE_BITCOIN_SECP256K1_FOR_ECDSA)
|
if(USE_BITCOIN_SECP256K1_FOR_ECDSA)
|
||||||
option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF)
|
option(SECP256K1_BUILD_BENCHMARK "Build benchmarks." OFF)
|
||||||
|
|
|
||||||
1
contrib/randomx
Submodule
1
contrib/randomx
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit cf15f4023ec8ddaa842f49111ba80ead1b5937f1
|
||||||
30
genesis-work/genesis_config.json
Normal file
30
genesis-work/genesis_config.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
7
genesis-work/genesis_config.json.genesis.dictionary.txt
Normal file
7
genesis-work/genesis_config.json.genesis.dictionary.txt
Normal file
|
|
@ -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<crypto::public_key>(ggenesis_tx_pub_key_str);
|
||||||
|
extern const genesis_tx_dictionary_entry ggenesis_dict[1];
|
||||||
|
const genesis_tx_dictionary_entry ggenesis_dict[1] = {
|
||||||
|
{12778448838847345770ULL,0}
|
||||||
|
};
|
||||||
1
genesis-work/genesis_config.json.genesis.txt
Normal file
1
genesis-work/genesis_config.json.genesis.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
01010000018080a0cfc8e0c8e38a0103bd6b4dda729d39dc85cb22a0b3db9b4b04d6464be9f3c84640bdca8bc0afb148000516503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b137a4c65746865616e2067656e6573697320e280942074686520616476616e74616765206f6620746865206e6174757265206f6620696e666f726d6174696f6e206265696e67206561737920746f2073707265616420627574206861726420746f20737469666c652e202d205361746f736869204e616b616d6f746f15000b029e4e0e0a0000
|
||||||
14
genesis-work/genesis_config.json.genesis.uint64.array.txt
Normal file
14
genesis-work/genesis_config.json.genesis.uint64.array.txt
Normal file
|
|
@ -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}};
|
||||||
14
genesis-work/genesis_config.json.premine_stat.txt
Normal file
14
genesis-work/genesis_config.json.premine_stat.txt
Normal file
|
|
@ -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)
|
||||||
BIN
genesis-work/premine_wallet
Normal file
BIN
genesis-work/premine_wallet
Normal file
Binary file not shown.
1
genesis-work/premine_wallet.address
Normal file
1
genesis-work/premine_wallet.address
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
iTHNdRWX6pKGXF1HqHgZyXie8EdaCwwdWUmsEnQhNojSUrkrR2eTemhFYkjWXbhdbyGcRBLgV2RYp3HhNAFCQ67w4bTHErZL1J
|
||||||
|
|
@ -119,6 +119,7 @@ endif()
|
||||||
add_library(currency_core ${CURRENCY_CORE})
|
add_library(currency_core ${CURRENCY_CORE})
|
||||||
add_dependencies(currency_core version config ${PCH_LIB_NAME})
|
add_dependencies(currency_core version config ${PCH_LIB_NAME})
|
||||||
target_link_libraries(currency_core config)
|
target_link_libraries(currency_core config)
|
||||||
|
target_include_directories(currency_core PRIVATE ${RANDOMX_INCLUDE})
|
||||||
ENABLE_SHARED_PCH(currency_core CURRENCY_CORE)
|
ENABLE_SHARED_PCH(currency_core CURRENCY_CORE)
|
||||||
|
|
||||||
add_library(wallet ${WALLET})
|
add_library(wallet ${WALLET})
|
||||||
|
|
@ -164,19 +165,19 @@ target_link_libraries(currency_core config lmdb mdbx)
|
||||||
|
|
||||||
add_executable(daemon ${DAEMON} ${P2P} ${CURRENCY_PROTOCOL})
|
add_executable(daemon ${DAEMON} ${P2P} ${CURRENCY_PROTOCOL})
|
||||||
add_dependencies(daemon version)
|
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(daemon DAEMON)
|
||||||
ENABLE_SHARED_PCH_EXECUTABLE(daemon)
|
ENABLE_SHARED_PCH_EXECUTABLE(daemon)
|
||||||
|
|
||||||
add_executable(connectivity_tool ${CONN_TOOL})
|
add_executable(connectivity_tool ${CONN_TOOL})
|
||||||
add_dependencies(connectivity_tool version)
|
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(connectivity_tool CONN_TOOL)
|
||||||
ENABLE_SHARED_PCH_EXECUTABLE(connectivity_tool)
|
ENABLE_SHARED_PCH_EXECUTABLE(connectivity_tool)
|
||||||
|
|
||||||
add_executable(simplewallet ${SIMPLEWALLET})
|
add_executable(simplewallet ${SIMPLEWALLET})
|
||||||
add_dependencies(simplewallet version)
|
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(simplewallet SIMPLEWALLET)
|
||||||
ENABLE_SHARED_PCH_EXECUTABLE(simplewallet)
|
ENABLE_SHARED_PCH_EXECUTABLE(simplewallet)
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ target_link_libraries(api_server
|
||||||
crypto
|
crypto
|
||||||
common
|
common
|
||||||
ethash
|
ethash
|
||||||
|
randomx
|
||||||
${Boost_LIBRARIES}
|
${Boost_LIBRARIES}
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
OpenSSL::SSL
|
OpenSSL::SSL
|
||||||
|
|
|
||||||
|
|
@ -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(default_dust_threshold 0 CACHE STRING "DEFAULT_DUST_THRESHOLD" )
|
||||||
set(difficulty_pow_starter 1 CACHE STRING "DIFFICULTY_POW_STARTER" )
|
set(difficulty_pow_starter 1 CACHE STRING "DIFFICULTY_POW_STARTER" )
|
||||||
set(difficulty_pos_target 120 CACHE STRING "DIFFICULTY_POS_TARGET" )
|
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_window 720 CACHE STRING "DIFFICULTY_WINDOW" )
|
||||||
set(difficulty_lag 15 CACHE STRING "DIFFICULTY_LAG" )
|
set(difficulty_lag 15 CACHE STRING "DIFFICULTY_LAG" )
|
||||||
set(difficulty_cut 60 CACHE STRING "DIFFICULTY_CUT" )
|
set(difficulty_cut 60 CACHE STRING "DIFFICULTY_CUT" )
|
||||||
|
|
|
||||||
|
|
@ -27,39 +27,102 @@ using namespace epee;
|
||||||
#include "crypto/crypto.h"
|
#include "crypto/crypto.h"
|
||||||
#include "crypto/hash.h"
|
#include "crypto/hash.h"
|
||||||
#include "common/int-util.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/ethash.hpp"
|
||||||
#include "ethereum/libethash/ethash/progpow.hpp"
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace currency
|
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;
|
||||||
|
|
||||||
//--------------------------------------------------------------
|
static void init_randomx()
|
||||||
int ethash_custom_log_get_level()
|
|
||||||
{
|
{
|
||||||
return epee::log_space::get_set_log_detalisation_level();
|
rx_flags = randomx_get_flags() | RANDOMX_FLAG_FULL_MEM;
|
||||||
}
|
LOG_PRINT_L0("[RandomX] Initializing with flags: " << static_cast<int>(rx_flags));
|
||||||
//--------------------------------------------------------------
|
|
||||||
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();
|
|
||||||
|
|
||||||
epee::log_space::log_singletone::do_log_message(msg, LOG_LEVEL_0, epee::log_space::console_color_default, true, LOG_DEFAULT_TARGET);
|
rx_cache = randomx_alloc_cache(rx_flags);
|
||||||
}
|
if (!rx_cache)
|
||||||
//--------------------------------------------------------------
|
{
|
||||||
void init_ethash_log_if_necessary()
|
LOG_ERROR("[RandomX] Failed to allocate cache");
|
||||||
{
|
throw std::bad_alloc();
|
||||||
static bool inited = false;
|
}
|
||||||
if (inited)
|
|
||||||
|
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ethash::access_custom_log_level_function() = ðash_custom_log_get_level;
|
// Multi-threaded dataset init
|
||||||
ethash::access_custom_log_function() = ðash_custom_log;
|
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<std::thread> 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)
|
int ethash_height_to_epoch(uint64_t height)
|
||||||
{
|
{
|
||||||
return static_cast<int>(height / ETHASH_EPOCH_LENGTH);
|
return static_cast<int>(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)
|
crypto::hash get_block_longhash(uint64_t height, const crypto::hash& block_header_hash, uint64_t nonce)
|
||||||
{
|
{
|
||||||
init_ethash_log_if_necessary();
|
// Combine header hash + nonce into a single input buffer
|
||||||
int epoch = ethash_height_to_epoch(height);
|
struct {
|
||||||
std::shared_ptr<ethash::epoch_context_full> p_context = progpow::get_global_epoch_context_full(static_cast<int>(epoch));
|
crypto::hash header;
|
||||||
if (!p_context)
|
uint64_t nonce;
|
||||||
{
|
} input;
|
||||||
LOG_ERROR("fatal error: get_global_epoch_context_full failed, throwing bad_alloc...");
|
input.header = block_header_hash;
|
||||||
throw std::bad_alloc();
|
input.nonce = nonce;
|
||||||
}
|
|
||||||
auto res_eth = progpow::hash(*p_context, static_cast<int>(height), *(ethash::hash256*)&block_header_hash, 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;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1428,14 +1428,7 @@ wide_difficulty_type blockchain_storage::calc_diff_at_h_from_timestamps(std::vec
|
||||||
{
|
{
|
||||||
wide_difficulty_type dif;
|
wide_difficulty_type dif;
|
||||||
TIME_MEASURE_START_PD(target_calculating_calc);
|
TIME_MEASURE_START_PD(target_calculating_calc);
|
||||||
if (m_core_runtime_config.is_hardfork_active_for_height(1, h))
|
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);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
TIME_MEASURE_FINISH_PD(target_calculating_calc);
|
TIME_MEASURE_FINISH_PD(target_calculating_calc);
|
||||||
return dif;
|
return dif;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,4 +267,75 @@ namespace currency {
|
||||||
}
|
}
|
||||||
return summ / devider;
|
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<uint64_t>& timestamps, vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter)
|
||||||
|
{
|
||||||
|
const int64_t T = static_cast<int64_t>(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<int64_t>(timestamps[i])
|
||||||
|
- static_cast<int64_t>(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<int64_t>(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<wide_difficulty_type>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ namespace currency
|
||||||
bool check_hash(const crypto::hash &hash, wide_difficulty_type difficulty);
|
bool check_hash(const crypto::hash &hash, wide_difficulty_type difficulty);
|
||||||
wide_difficulty_type next_difficulty_1(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter);
|
wide_difficulty_type next_difficulty_1(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter);
|
||||||
wide_difficulty_type next_difficulty_2(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter);
|
wide_difficulty_type next_difficulty_2(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter);
|
||||||
|
wide_difficulty_type next_difficulty_lwma(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds, const wide_difficulty_type& difficulty_starter);
|
||||||
uint64_t difficulty_to_boundary(wide_difficulty_type difficulty);
|
uint64_t difficulty_to_boundary(wide_difficulty_type difficulty);
|
||||||
void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result);
|
void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
const genesis_tx_raw_data ggenesis_tx_raw = {{
|
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},
|
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},
|
||||||
{0x02,0x8a,0x56,0x0e,0x0a,0x00,0x00}};
|
{0x00}};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
struct genesis_tx_raw_data
|
struct genesis_tx_raw_data
|
||||||
{
|
{
|
||||||
uint64_t const v[42];
|
uint64_t const v[27];
|
||||||
uint8_t const r[42];
|
uint8_t const r[1];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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<crypto::public_key>(ggenesis_tx_pub_key_str);
|
const crypto::public_key ggenesis_tx_pub_key = epee::string_tools::parse_tpod_from_hex_string<crypto::public_key>(ggenesis_tx_pub_key_str);
|
||||||
extern const genesis_tx_dictionary_entry ggenesis_dict[1];
|
extern const genesis_tx_dictionary_entry ggenesis_dict[1];
|
||||||
const genesis_tx_dictionary_entry ggenesis_dict[1] = {
|
const genesis_tx_dictionary_entry ggenesis_dict[1] = {
|
||||||
{1056117391700764468ULL,0}
|
{12778448838847345770ULL,0}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -808,6 +808,7 @@ namespace
|
||||||
, m_context(context)
|
, m_context(context)
|
||||||
, m_connection_initialized(false)
|
, m_connection_initialized(false)
|
||||||
, m_last_reported_hashrate(0)
|
, m_last_reported_hashrate(0)
|
||||||
|
, m_use_standard_stratum(false)
|
||||||
{
|
{
|
||||||
LOG_PRINT_CC(m_context, "stratum_protocol_handler::ctor()", LOG_LEVEL_4);
|
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_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_submitHashrate", &this_t::handle_method_eth_submitHashrate));
|
||||||
m_methods_handlers.insert(std::make_pair("eth_submitWork", &this_t::handle_method_eth_submitWork));
|
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);
|
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)
|
void send(const std::string& data)
|
||||||
{
|
{
|
||||||
static_cast<epee::net_utils::i_service_endpoint*>(m_p_connection)->do_send(data.c_str(), data.size());
|
static_cast<epee::net_utils::i_service_endpoint*>(m_p_connection)->do_send(data.c_str(), data.size());
|
||||||
|
|
@ -1002,6 +1060,40 @@ namespace
|
||||||
|
|
||||||
void send_notification(const std::string& json)
|
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."
|
// 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
|
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,6 +1175,7 @@ namespace
|
||||||
|
|
||||||
epee::critical_section m_work_change_lock;
|
epee::critical_section m_work_change_lock;
|
||||||
uint64_t m_last_reported_hashrate;
|
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);
|
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<std::string, method_handler_func_t> m_methods_handlers;
|
static std::unordered_map<std::string, method_handler_func_t> m_methods_handlers;
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ add_executable(net_load_tests_srv net_load_tests/srv.cpp)
|
||||||
|
|
||||||
add_dependencies(coretests version)
|
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(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 miniupnpc::miniupnpc ${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-tests crypto ethash)
|
||||||
target_link_libraries(hash-target-tests crypto currency_core ethash ${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 ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
|
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 ${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 ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
|
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 ${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)
|
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")
|
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")
|
||||||
|
|
|
||||||
|
|
@ -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<test_event_entry>& events) const
|
bool gen_alias_too_small_reward::generate(std::vector<test_event_entry>& events) const
|
||||||
{
|
{
|
||||||
// pay for alias too small and see, if it's ok
|
// 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[] = {
|
const alias_entry aliases[] = {
|
||||||
{"a", pub_addr_from_string("ZxD95C97dFqVBsuTMA1VKjFxx8Cc7by2YTA9kLhFc8JB3zJ3qKKXRm9Lu2YQsjPP5UKhCSfLLEqJr5ovyQNYYQWV1Pv98fRzt")},
|
{"a", alias_accs[0].get_keys().account_address},
|
||||||
{"bb", pub_addr_from_string("ZxDL9euTT4C9FUj28QY1RdWnreMHJiWfrPs39rXhrgai8H4pmaFzJ4vUUYRmHhNxToN64H1U5sMnaHuD3S4kVbyY1mKHnERVZ")},
|
{"bb", alias_accs[1].get_keys().account_address},
|
||||||
{"ccc", pub_addr_from_string("ZxBrpHp3xrjLrMMSyJUg44YmyJVZjetouVFdtqLfxpHUMSxiEyyQ7iKSj4sr6gn7qwXrj6YSw7UjJZLyc1H37QtF2p96c2gAD")},
|
{"ccc", alias_accs[2].get_keys().account_address},
|
||||||
{"dddd", pub_addr_from_string("ZxDtT1pTwt6R2t3eGw9VD6N1heHCKNLKuCFUvqgHpXkAVnPkfai4KDYEjRSV8E42XKN3MJeaHJMaxa9hUmaXLyHm2nQ12aX93")},
|
{"dddd", alias_accs[3].get_keys().account_address},
|
||||||
{"eeeee", pub_addr_from_string("ZxCABdwUJpqHstWJUHQ21piADBwaSsXcAh5EPtpSr8xXderWqvDef566ReFGrRqBUrE2tCgZ3HE5XRuxoq8mNTrP2X4J35yQq")},
|
{"eeeee", alias_accs[4].get_keys().account_address},
|
||||||
{"ffffff", pub_addr_from_string("ZxC34uAJJ2iW15GkvcqaQd4RKZdu16tpmf4ubmsirw7eFtKoLi2xswhNqy3Q4VacCq5mM7zuYyoWEW8AS5HGtoXr1m9RuTUuu")},
|
{"ffffff", alias_accs[5].get_keys().account_address},
|
||||||
{"ggggggg", pub_addr_from_string("ZxDHxZizSe5MNQoRkC1unqTrhYUkh1ZG7iEXMzLatyZ5EHRPat4Ls4ZRnN4CYLvJLq5F5gxdDtu17Zrvur7dcqU52sv2pryn7")},
|
{"ggggggg", alias_accs[6].get_keys().account_address},
|
||||||
{"hhhhhhhh", pub_addr_from_string("ZxDXME4qrbh7mAbrqDmCbzGj14VQP1n9KLLi7fXBMNvDd5UUpcevCSXQ9zSkZcJtbzBS7u16NiykAiv3q9VkZySL2ySB6hTfL")},
|
{"hhhhhhhh", alias_accs[7].get_keys().account_address},
|
||||||
{"iiiiiiiii", pub_addr_from_string("ZxDtpxbC2bN8yu3J49tsUYUSoPYTnAgjmBogzFviUg3t2fGfWzmZ2gbKNC1XKVdMEE2hoW5sULs2hAF5T3igoAVW2MsHUmaj4")},
|
{"iiiiiiiii", alias_accs[8].get_keys().account_address},
|
||||||
{"jjjjjjjjjj", pub_addr_from_string("ZxCBLxnctYwB37YZi7MsJqBCujXzkBeJEh7wPbYrFUvMiqXiPLkyBRAh6ahQ6wre2tGR8FHesZwKn2zYPkTuibyu2648g2CGV")}
|
{"jjjjjjjjjj", alias_accs[9].get_keys().account_address}
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t aliases_count = sizeof aliases / sizeof aliases[0];
|
const size_t aliases_count = sizeof aliases / sizeof aliases[0];
|
||||||
|
|
|
||||||
|
|
@ -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};
|
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<uint64_t> szs(&vszs[0], &vszs[90]);
|
std::vector<uint64_t> szs(&vszs[0], &vszs[90]);
|
||||||
account_public_address adr;
|
account_base acc;
|
||||||
bool r = get_account_address_from_str(adr, "ZxDLGBGXbjo5w51tJkvxEPHFRr7Xft4hf33N8EkJPndoGCqocQF1mzpZqYwXByx5gMbfQuPAAB9vj79EFR6Jwkgu1o3aMQPwJ");
|
acc.generate();
|
||||||
CHECK_AND_ASSERT_MES(r, false, "failed to import");
|
account_public_address adr = acc.get_keys().account_address;
|
||||||
uint64_t block_reward_without_fee = 0;
|
uint64_t block_reward_without_fee = 0;
|
||||||
uint64_t block_reward = 0;
|
uint64_t block_reward = 0;
|
||||||
block b;
|
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;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -441,7 +441,6 @@ namespace
|
||||||
"\x22\x09\x39\x68\x9e\xdf\x1a\xbd\x5b\xc1\xd0\x31\xf7\x3e\xcd\x6c"
|
"\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"
|
"\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d"
|
||||||
"\x00");
|
"\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)
|
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;
|
currency::account_public_address addr;
|
||||||
ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr));
|
ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr));
|
||||||
std::string addr_str = currency::get_account_address_as_str(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)
|
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;
|
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;
|
std::string blob;
|
||||||
ASSERT_TRUE(serialization::dump_binary(addr, 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)
|
TEST(get_account_address_from_str, fails_on_invalid_address_format)
|
||||||
{
|
{
|
||||||
currency::account_public_address addr;
|
currency::account_public_address addr_orig;
|
||||||
std::string addr_str = test_keys_addr_str;
|
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';
|
addr_str[0] = '0';
|
||||||
|
|
||||||
|
currency::account_public_address addr;
|
||||||
ASSERT_FALSE(currency::get_account_address_from_str(addr, addr_str));
|
ASSERT_FALSE(currency::get_account_address_from_str(addr, addr_str));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -563,8 +574,8 @@ struct addr_entry_t
|
||||||
addr_entry_t addr_entries[] =
|
addr_entry_t addr_entries[] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
// classic normal address
|
// classic normal address — generated at runtime from keys
|
||||||
"ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH", // address
|
"", // address (empty = auto-generate from keys + prefix)
|
||||||
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
||||||
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
||||||
"", // payment_id_hex
|
"", // payment_id_hex
|
||||||
|
|
@ -572,31 +583,15 @@ addr_entry_t addr_entries[] =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// classic integrated address
|
// classic integrated address
|
||||||
"iZ2Zi6RmTWwcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTqEsjvJoco1aLSZXS6T", // address
|
"",
|
||||||
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
||||||
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
||||||
"87440d0b9acc42f1", // payment_id_hex
|
"87440d0b9acc42f1", // payment_id_hex
|
||||||
0 // flags
|
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
|
// normal auditable address
|
||||||
"aZxb9Et6FhP9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJMEmqQFn", // address
|
"",
|
||||||
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
||||||
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
||||||
"", // payment_id
|
"", // payment_id
|
||||||
|
|
@ -604,7 +599,7 @@ addr_entry_t addr_entries[] =
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// auditable integrated address
|
// auditable integrated address
|
||||||
"aiZXDondHWu9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJM9xJH8EbjuRiMJgFmPRATsEV9", // address
|
"",
|
||||||
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
"a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3", // view_pub_key
|
||||||
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
"9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa", // spend_pub_key
|
||||||
"3ba0527bcfb1fa93630d28eed6", // payment_id
|
"3ba0527bcfb1fa93630d28eed6", // payment_id
|
||||||
|
|
@ -614,16 +609,34 @@ addr_entry_t addr_entries[] =
|
||||||
|
|
||||||
void check_add_entry(const addr_entry_t& ae)
|
void check_add_entry(const addr_entry_t& ae)
|
||||||
{
|
{
|
||||||
std::string payment_id, payment_id_hex;
|
// Build address struct from hex keys
|
||||||
currency::account_public_address addr = AUTO_VAL_INIT(addr);
|
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));
|
// Encode to string (with or without payment id)
|
||||||
payment_id_hex = epee::string_tools::buff_to_hex_nodelimer(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);
|
std::string addr_str = payment_id_bin.empty()
|
||||||
ASSERT_EQ(ae.payment_id_hex, payment_id_hex);
|
? currency::get_account_address_as_str(addr_built)
|
||||||
ASSERT_EQ(ae.view_pub_key, epee::string_tools::pod_to_hex(addr.view_public_key));
|
: currency::get_account_address_and_payment_id_as_str(addr_built, payment_id_bin);
|
||||||
ASSERT_EQ(ae.spend_pub_key, epee::string_tools::pod_to_hex(addr.spend_public_key));
|
|
||||||
|
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)
|
TEST(auditable_addresses, basic)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include "currency_core/currency_basic.h"
|
#include "currency_core/currency_basic.h"
|
||||||
#include "currency_core/currency_format_utils.h"
|
#include "currency_core/currency_format_utils.h"
|
||||||
|
#include "currency_core/account.h"
|
||||||
#include "serialization/serialization.h"
|
#include "serialization/serialization.h"
|
||||||
#include "serialization/binary_archive.h"
|
#include "serialization/binary_archive.h"
|
||||||
#include "serialization/json_archive.h"
|
#include "serialization/json_archive.h"
|
||||||
|
|
@ -727,7 +728,7 @@ TEST(Serialization, serializes_transacion_versions)
|
||||||
c.comment = "sdcwdcwcewdcecevthbtg";
|
c.comment = "sdcwdcwcewdcecevthbtg";
|
||||||
tx.extra.push_back(c);
|
tx.extra.push_back(c);
|
||||||
extra_alias_entry eae = AUTO_VAL_INIT(eae);
|
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_alias = "eokcmeockme";
|
||||||
eae.m_text_comment = "sdssccsc";
|
eae.m_text_comment = "sdssccsc";
|
||||||
tx.extra.push_back(eae);
|
tx.extra.push_back(eae);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue