From a13eedf55674e49075f8b5a064d2dac5c793c1f5 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 28 Feb 2025 19:11:37 +0400 Subject: [PATCH] fix of currency protocol for long range splits --- .../block_chain_shortener.cpp} | 72 +++++++++++-------- .../block_chain_shortener.h} | 3 +- src/currency_core/blockchain_storage.cpp | 16 +++++ src/currency_core/blockchain_storage.h | 2 + src/currency_core/connection_context.h | 4 +- .../currency_protocol_handler.inl | 42 ++++++++++- src/wallet/wallet2.h | 4 +- src/wallet/wallet2_base.h | 2 +- tests/performance_tests/main.cpp | 1 + .../wallet_chain_shortener_test.cpp | 6 +- 10 files changed, 112 insertions(+), 40 deletions(-) rename src/{wallet/wallet_chain_shortener.cpp => currency_core/block_chain_shortener.cpp} (70%) rename src/{wallet/wallet_chain_shortener.h => currency_core/block_chain_shortener.h} (96%) diff --git a/src/wallet/wallet_chain_shortener.cpp b/src/currency_core/block_chain_shortener.cpp similarity index 70% rename from src/wallet/wallet_chain_shortener.cpp rename to src/currency_core/block_chain_shortener.cpp index 39d37c75..d9aa9d49 100644 --- a/src/wallet/wallet_chain_shortener.cpp +++ b/src/currency_core/block_chain_shortener.cpp @@ -4,19 +4,19 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "wallet_chain_shortener.h" -#include "wallet_errors.h" +#include "block_chain_shortener.h" +//#include "wallet_errors.h" -#define WALLET_EVERYBLOCK_SIZE 20 -#define WALLET_EVERY_10_BLOCKS_SIZE 144 -#define WALLET_EVERY_100_BLOCKS_SIZE 144 -#define WALLET_EVERY_1000_BLOCKS_SIZE 144 +#define SHORTENER_EVERYBLOCK_SIZE 20 +#define SHORTENER_EVERY_10_BLOCKS_SIZE 144 +#define SHORTENER_EVERY_100_BLOCKS_SIZE 144 +#define SHORTENER_EVERY_1000_BLOCKS_SIZE 144 static void exception_handler(){} -void wallet_chain_shortener::clear() +void block_chain_shortener::clear() { m_local_bc_size = 1; m_last_20_blocks.clear(); @@ -26,29 +26,42 @@ void wallet_chain_shortener::clear() } //---------------------------------------------------------------------------------------------------- -uint64_t wallet_chain_shortener::get_blockchain_current_size() const +uint64_t block_chain_shortener::get_blockchain_current_size() const { return m_local_bc_size; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet_chain_shortener::get_top_block_height() const +uint64_t block_chain_shortener::get_top_block_height() const { return m_local_bc_size - 1; } //---------------------------------------------------------------------------------------------------- -void wallet_chain_shortener::set_genesis(const crypto::hash& id) +crypto::hash block_chain_shortener::get_top_block_id() const +{ + if (m_last_20_blocks.size()) + return (--m_last_20_blocks.end())->second; + + else return currency::null_hash; +} +//---------------------------------------------------------------------------------------------------- +void block_chain_shortener::set_genesis(const crypto::hash& id) { m_genesis = id; m_local_bc_size = 1; } //---------------------------------------------------------------------------------------------------- -const crypto::hash& wallet_chain_shortener::get_genesis() +const crypto::hash& block_chain_shortener::get_genesis() { return m_genesis; } //---------------------------------------------------------------------------------------------------- -void wallet_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t height) +void block_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t height) { + if (height < m_local_bc_size) + { + detach(height); + } + if (height == 0) { m_genesis = id; @@ -60,12 +73,13 @@ void wallet_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t //self check if (m_local_bc_size != 1) { - THROW_IF_FALSE_WALLET_INT_ERR_EX(get_blockchain_current_size() == height, "Inernal error: get_blockchain_current_height(){" << get_blockchain_current_size() << "} == height{" << height << "} is not equal"); + CHECK_AND_ASSERT_THROW_MES(get_blockchain_current_size() == height, "Inernal error: get_blockchain_current_height(){" << get_blockchain_current_size() << "} == height{" << height << "} is not equal") + //THROW_IF_FALSE_INT_ERR_EX(); } m_local_bc_size = height+1; m_last_20_blocks[height] = id; - if (m_last_20_blocks.size() > WALLET_EVERYBLOCK_SIZE) + if (m_last_20_blocks.size() > SHORTENER_EVERYBLOCK_SIZE) { m_last_20_blocks.erase(m_last_20_blocks.begin()); } @@ -76,10 +90,10 @@ void wallet_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t //self check if (!m_last_144_blocks_every_10.empty()) { - THROW_IF_FALSE_WALLET_INT_ERR_EX((--m_last_144_blocks_every_10.end())->first + 10 == height, "Inernal error: (--m_last_144_blocks_every_10.end())->first + 10{" << (--m_last_144_blocks_every_10.end())->first + 10 << "} == height{" << height << "} is not equal"); + CHECK_AND_ASSERT_THROW_MES((--m_last_144_blocks_every_10.end())->first + 10 == height, "Inernal error: (--m_last_144_blocks_every_10.end())->first + 10{" << (--m_last_144_blocks_every_10.end())->first + 10 << "} == height{" << height << "} is not equal"); } m_last_144_blocks_every_10[height] = id; - if (m_last_144_blocks_every_10.size() > WALLET_EVERY_10_BLOCKS_SIZE) + if (m_last_144_blocks_every_10.size() > SHORTENER_EVERY_10_BLOCKS_SIZE) { m_last_144_blocks_every_10.erase(m_last_144_blocks_every_10.begin()); } @@ -90,10 +104,10 @@ void wallet_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t //self check if (!m_last_144_blocks_every_100.empty()) { - THROW_IF_FALSE_WALLET_INT_ERR_EX((--m_last_144_blocks_every_100.end())->first + 100 == height, "Inernal error: (--m_last_144_blocks_every_100.end())->first + 100{" << (--m_last_144_blocks_every_100.end())->first + 100 << "} == height{" << height << "} is not equal"); + CHECK_AND_ASSERT_THROW_MES((--m_last_144_blocks_every_100.end())->first + 100 == height, "Inernal error: (--m_last_144_blocks_every_100.end())->first + 100{" << (--m_last_144_blocks_every_100.end())->first + 100 << "} == height{" << height << "} is not equal"); } m_last_144_blocks_every_100[height] = id; - if (m_last_144_blocks_every_100.size() > WALLET_EVERY_100_BLOCKS_SIZE) + if (m_last_144_blocks_every_100.size() > SHORTENER_EVERY_100_BLOCKS_SIZE) { m_last_144_blocks_every_100.erase(m_last_144_blocks_every_100.begin()); } @@ -105,10 +119,10 @@ void wallet_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t //self check if (!m_last_144_blocks_every_1000.empty()) { - THROW_IF_FALSE_WALLET_INT_ERR_EX((--m_last_144_blocks_every_1000.end())->first + 1000 == height, "Inernal error: (--m_last_144_blocks_every_1000.end())->first + 1000{" << (--m_last_144_blocks_every_1000.end())->first + 1000 << "} == height{" << height << "} is not equal"); + CHECK_AND_ASSERT_THROW_MES((--m_last_144_blocks_every_1000.end())->first + 1000 == height, "Inernal error: (--m_last_144_blocks_every_1000.end())->first + 1000{" << (--m_last_144_blocks_every_1000.end())->first + 1000 << "} == height{" << height << "} is not equal"); } m_last_144_blocks_every_1000[height] = id; - if (m_last_144_blocks_every_1000.size() > WALLET_EVERY_1000_BLOCKS_SIZE) + if (m_last_144_blocks_every_1000.size() > SHORTENER_EVERY_1000_BLOCKS_SIZE) { m_last_144_blocks_every_1000.erase(m_last_144_blocks_every_1000.begin()); } @@ -118,7 +132,7 @@ void wallet_chain_shortener::push_new_block_id(const crypto::hash& id, uint64_t } //---------------------------------------------------------------------------------------------------- -void wallet_chain_shortener::get_short_chain_history(std::list& ids)const +void block_chain_shortener::get_short_chain_history(std::list& ids)const { ids.clear(); uint64_t i = 0; @@ -137,7 +151,7 @@ void wallet_chain_shortener::get_short_chain_history(std::list& id uint64_t current_back_offset = ids.size()+1; //self check - THROW_IF_FALSE_WALLET_INT_ERR_EX(current_back_offset == sz - i + 1 || !count, "Inernal error: current_back_offset{" << current_back_offset << "} == sz-i{" << sz << " - " << i << "} is not equal"); + CHECK_AND_ASSERT_THROW_MES(current_back_offset == sz - i + 1 || !count, "Inernal error: current_back_offset{" << current_back_offset << "} == sz-i{" << sz << " - " << i << "} is not equal"); uint64_t current_offset_distance = 1; while (current_back_offset < sz) @@ -157,7 +171,7 @@ void wallet_chain_shortener::get_short_chain_history(std::list& id ids.push_back(m_genesis); } //---------------------------------------------------------------------------------------------------- -bool wallet_chain_shortener::lookup_item_around(uint64_t i, std::pair& result)const +bool block_chain_shortener::lookup_item_around(uint64_t i, std::pair& result)const { //in which container we are looking for? uint64_t devider = 0; @@ -189,14 +203,14 @@ bool wallet_chain_shortener::lookup_item_around(uint64_t i, std::pairfind(i); //self check - THROW_IF_FALSE_WALLET_INT_ERR_EX(it != pcontainer->end(), + CHECK_AND_ASSERT_THROW_MES(it != pcontainer->end(), "Inernal error: index " << i << " not found for devider " << devider << " pcontainer={" << pcontainer->begin()->first << ":" << (--pcontainer->end())->first << "}"); result = *it; return true; } //---------------------------------------------------------------------------------------------------- -void wallet_chain_shortener::check_if_block_matched(uint64_t i, const crypto::hash& id, bool& block_found, bool& block_matched, bool& full_reset_needed)const +void block_chain_shortener::check_if_block_matched(uint64_t i, const crypto::hash& id, bool& block_found, bool& block_matched, bool& full_reset_needed)const { if (i == 0) { @@ -221,12 +235,12 @@ void wallet_chain_shortener::check_if_block_matched(uint64_t i, const crypto::ha { //must be in short sequence (m_last_20_blocks) //self check - THROW_IF_FALSE_WALLET_INT_ERR_EX((--m_last_20_blocks.end())->first >= i, + CHECK_AND_ASSERT_THROW_MES((--m_last_20_blocks.end())->first >= i, "Inernal error: index " << i << " is not located in expected range of m_last_20_blocks={" << m_last_20_blocks.begin()->first << ":" << (--m_last_20_blocks.end())->first << "}"); auto it = m_last_20_blocks.find(i); - THROW_IF_FALSE_WALLET_INT_ERR_EX(it != m_last_20_blocks.end(), + CHECK_AND_ASSERT_THROW_MES(it != m_last_20_blocks.end(), "Inernal error: filde to find index " << i << " in m_last_20_blocks={" << m_last_20_blocks.begin()->first << ":" << (--m_last_20_blocks.end())->first << "}"); @@ -271,7 +285,7 @@ void wallet_chain_shortener::check_if_block_matched(uint64_t i, const crypto::ha } } //---------------------------------------------------------------------------------------------------- -std::string wallet_chain_shortener::get_internal_state_text() const +std::string block_chain_shortener::get_internal_state_text() const { std::stringstream ss; #define PRINT_CHAIN_SHORTENER_STATE_INFO(cont_name) \ @@ -296,7 +310,7 @@ void clean_map_from_items_above(std::map& container, uin } } //---------------------------------------------------------------------------------------------------- -void wallet_chain_shortener::detach(uint64_t including_height) +void block_chain_shortener::detach(uint64_t including_height) { clean_map_from_items_above(m_last_20_blocks, including_height); clean_map_from_items_above(m_last_144_blocks_every_10, including_height); diff --git a/src/wallet/wallet_chain_shortener.h b/src/currency_core/block_chain_shortener.h similarity index 96% rename from src/wallet/wallet_chain_shortener.h rename to src/currency_core/block_chain_shortener.h index a5e66a9a..dfd64de1 100644 --- a/src/wallet/wallet_chain_shortener.h +++ b/src/currency_core/block_chain_shortener.h @@ -19,11 +19,12 @@ #include "crypto/crypto.h" #include "currency_core/currency_basic.h" -class wallet_chain_shortener +class block_chain_shortener { public: void push_new_block_id(const crypto::hash& id, uint64_t height); uint64_t get_top_block_height() const; + crypto::hash get_top_block_id() const; uint64_t get_blockchain_current_size() const; void get_short_chain_history(std::list& ids)const; bool lookup_item_around(uint64_t i, std::pair& result)const; diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index c13f8cbd..6893ca27 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -3761,6 +3761,22 @@ bool blockchain_storage::have_block(const crypto::hash& id)const return false; } //------------------------------------------------------------------ +bool blockchain_storage::have_block_main(const crypto::hash& id) const +{ + CRITICAL_REGION_LOCAL(m_read_lock); + if (m_db_blocks_index.find(id)) + return true; + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::have_block_alt(const crypto::hash& id) const +{ + CRITICAL_REGION_LOCAL1(m_alternative_chains_lock); + if (m_alternative_chains.count(id)) + return true; + return false; +} +//------------------------------------------------------------------ bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc) { crypto::hash id = get_block_hash(bl); diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index c98e86eb..600cc9d3 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -280,6 +280,8 @@ namespace currency bool create_block_template(const create_block_template_params& params, create_block_template_response& resp) const; bool have_block(const crypto::hash& id) const; + bool have_block_main(const crypto::hash& id) const; + bool have_block_alt(const crypto::hash& id) const; size_t get_total_transactions()const; bool get_outs(uint64_t amount, std::list& pkeys)const; bool get_short_chain_history(std::list& ids)const; diff --git a/src/currency_core/connection_context.h b/src/currency_core/connection_context.h index 1320619c..43f372c5 100644 --- a/src/currency_core/connection_context.h +++ b/src/currency_core/connection_context.h @@ -9,6 +9,7 @@ #include #include "net/net_utils_base.h" #include "copyable_atomic.h" +#include "block_chain_shortener.h" namespace currency { @@ -33,7 +34,8 @@ namespace currency std::list m_needed_objects; std::unordered_set m_requested_objects; std::atomic m_callback_request_count; //in debug purpose: problem with double callback rise - + // + block_chain_shortener m_last_fetched_block_ids; }; struct currency_connection_context: public epee::net_utils::connection_context_base diff --git a/src/currency_protocol/currency_protocol_handler.inl b/src/currency_protocol/currency_protocol_handler.inl index 94b77505..86abbfd1 100644 --- a/src/currency_protocol/currency_protocol_handler.inl +++ b/src/currency_protocol/currency_protocol_handler.inl @@ -744,10 +744,40 @@ namespace currency {//we have to fetch more objects ids, request blockchain entry NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); - LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); - LOG_PRINT_L3("[NOTIFY]NOTIFY_REQUEST_CHAIN: " << ENDL << print_kv_structure(r) ); + if (context.m_priv.m_last_fetched_block_ids.get_top_block_height() > 0) + { + bool last_received_block_is_in_mainchain = m_core.get_blockchain_storage().have_block_main(context.m_priv.m_last_fetched_block_ids.get_top_block_id()); + bool far_from_top = context.m_priv.m_last_fetched_block_ids.get_top_block_height() + BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT < m_core.get_blockchain_storage().get_current_blockchain_size(); + if (!last_received_block_is_in_mainchain || (last_received_block_is_in_mainchain && far_from_top)) + { + block_extended_info blk = AUTO_VAL_INIT(blk); + // In this scenario, it's likely the remote daemon is on an alternate chain + // where the network split goes deeper than 2000 blocks. The NOTIFY_REQUEST_GET_OBJECTS + // call returns a batch of BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT IDs, which may + // start well beyond the original split point. + // + // If we repeatedly call NOTIFY_REQUEST_GET_OBJECTS with the IDs obtained from + // get_short_chain_history, it would create an endless loop. However, we still need + // to retrieve the full alternate chain from the remote daemon because it could + // potentially be “heavier” (in terms of consensus). + // + // Therefore, we provide only the last ten blocks returned by the remote daemon + // in the NOTIFY_REQUEST_CHAIN request, expecting to receive the subsequent batch + // of alternate blocks next. + context.m_priv.m_last_fetched_block_ids.get_short_chain_history(r.block_ids); + //add genesis to the latest + LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_CHAIN: requesting alt version starting from " << r.block_ids.front()); + } + } + + if (!r.block_ids.size()) + { + m_core.get_short_chain_history(r.block_ids); + } + LOG_PRINT_L2("[NOTIFY]NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size()); + LOG_PRINT_L3("[NOTIFY]NOTIFY_REQUEST_CHAIN: " << ENDL << print_kv_structure(r)); post_notify(r, context); + }else { CHECK_AND_ASSERT_MES(context.m_last_response_height == context.m_remote_blockchain_height-1 @@ -976,10 +1006,16 @@ namespace currency m_p2p->add_ip_fail(context.m_remote_ip); } + uint64_t height = arg.start_height; BOOST_FOREACH(auto& bl_details, arg.m_block_ids) { if (!m_core.have_block(bl_details.h)) + { context.m_priv.m_needed_objects.push_back(bl_details); + } + + context.m_priv.m_last_fetched_block_ids.push_new_block_id(bl_details.h, height); + height++; } request_missing_objects(context, false); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index cc4d8f53..029d6ee1 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -41,7 +41,7 @@ #include "currency_core/bc_offers_serialization.h" #include "currency_core/bc_escrow_service.h" #include "common/pod_array_file_container.h" -#include "wallet_chain_shortener.h" +#include "currency_core/block_chain_shortener.h" #include "tor-connect/torlib/tor_lib_iface.h" #include "currency_core/pos_mining.h" #include "view_iface.h" @@ -127,7 +127,7 @@ namespace tools */ struct wallet2_base_state { - wallet_chain_shortener m_chain; + block_chain_shortener m_chain; uint64_t m_minimum_height = WALLET_MINIMUM_HEIGHT_UNSET_CONST; amount_gindex_to_transfer_id_container m_amount_gindex_to_transfer_id; transfer_container m_transfers; diff --git a/src/wallet/wallet2_base.h b/src/wallet/wallet2_base.h index 76fffc18..07aae2eb 100644 --- a/src/wallet/wallet2_base.h +++ b/src/wallet/wallet2_base.h @@ -39,7 +39,7 @@ #include "currency_core/bc_offers_serialization.h" #include "currency_core/bc_escrow_service.h" #include "common/pod_array_file_container.h" -#include "wallet_chain_shortener.h" +#include "currency_core/block_chain_shortener.h" #include "tor-connect/torlib/tor_lib_iface.h" #include "currency_core/pos_mining.h" #include "view_iface.h" diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index cf95e980..62be5de5 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -58,6 +58,7 @@ void test_plain_wallet() //res = plain_wallet::sync_call("reset_connection_url", 0, "195.201.107.230:33336"); //res = plain_wallet::sync_call("reset_connection_url", 0, "https://node.zano.org:443"); + res = plain_wallet::sync_call("reset_connection_url", 0, "https://zano.cakewallet.com"); r = plain_wallet::sync_call("run_wallet", instance_id, ""); diff --git a/tests/unit_tests/wallet_chain_shortener_test.cpp b/tests/unit_tests/wallet_chain_shortener_test.cpp index f814bcf0..8e0c1592 100644 --- a/tests/unit_tests/wallet_chain_shortener_test.cpp +++ b/tests/unit_tests/wallet_chain_shortener_test.cpp @@ -4,12 +4,12 @@ #include #include "gtest/gtest.h" -#include "wallet/wallet_chain_shortener.h" +#include "currency_core/block_chain_shortener.h" -TEST(wallet_chain_shortener, wallet_chain_shortener) +TEST(block_chain_shortener, block_chain_shortener) { uint64_t counter = 0; - wallet_chain_shortener ws; + block_chain_shortener ws; for (counter = 1; counter != 1000000; counter++) {