1
0
Fork 0
forked from lthn/blockchain

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:
Claude 2026-02-06 13:22:25 +00:00
parent 7ee2265cae
commit 467c64d015
No known key found for this signature in database
GPG key ID: AF404715446AEB41
27 changed files with 423 additions and 109 deletions

View file

@ -4,6 +4,6 @@
"conan": {}
},
"include": [
"build/release/conan/build/debug/generators/CMakePresets.json"
"build/release/generators/CMakePresets.json"
]
}

View file

@ -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

View file

@ -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)

1
contrib/randomx Submodule

@ -0,0 +1 @@
Subproject commit cf15f4023ec8ddaa842f49111ba80ead1b5937f1

View 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"
}

View 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}
};

View file

@ -0,0 +1 @@
01010000018080a0cfc8e0c8e38a0103bd6b4dda729d39dc85cb22a0b3db9b4b04d6464be9f3c84640bdca8bc0afb148000516503ec7c167e3f2ead7d801bcbcebb3c58a84a2ba26a21ecb1f5c105fa159932b137a4c65746865616e2067656e6573697320e280942074686520616476616e74616765206f6620746865206e6174757265206f6620696e666f726d6174696f6e206265696e67206561737920746f2073707265616420627574206861726420746f20737469666c652e202d205361746f736869204e616b616d6f746f15000b029e4e0e0a0000

View 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}};

View 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

Binary file not shown.

View file

@ -0,0 +1 @@
iTHNdRWX6pKGXF1HqHgZyXie8EdaCwwdWUmsEnQhNojSUrkrR2eTemhFYkjWXbhdbyGcRBLgV2RYp3HhNAFCQ67w4bTHErZL1J

View file

@ -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)

View file

@ -59,6 +59,7 @@ target_link_libraries(api_server
crypto
common
ethash
randomx
${Boost_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
OpenSSL::SSL

View file

@ -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" )

View file

@ -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 <mutex>
#include <thread>
#include <vector>
#include <algorithm>
#include <cstring>
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<int>(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() = &ethash_custom_log_get_level;
ethash::access_custom_log_function() = &ethash_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<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)
{
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)
{
init_ethash_log_if_necessary();
int epoch = ethash_height_to_epoch(height);
std::shared_ptr<ethash::epoch_context_full> p_context = progpow::get_global_epoch_context_full(static_cast<int>(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<int>(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;
}
//---------------------------------------------------------------

View file

@ -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;
}

View file

@ -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<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>();
}
}

View file

@ -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<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);
void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result);
}

View file

@ -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}};

View file

@ -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];
};

View file

@ -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);
extern const genesis_tx_dictionary_entry ggenesis_dict[1];
const genesis_tx_dictionary_entry ggenesis_dict[1] = {
{1056117391700764468ULL,0}
{12778448838847345770ULL,0}
};

View file

@ -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<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)
{
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<std::string, method_handler_func_t> m_methods_handlers;
std::atomic<bool> m_connection_initialized;
}; // class stratum_protocol_handler
//==============================================================================================================================

View file

@ -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")

View file

@ -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
{
// 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];

View file

@ -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<uint64_t> 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;
}

View file

@ -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)

View file

@ -10,6 +10,7 @@
#include <boost/foreach.hpp>
#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);