1
0
Fork 0
forked from lthn/blockchain

fix of currency protocol for long range splits

This commit is contained in:
cryptozoidberg 2025-02-28 19:11:37 +04:00
parent bb92e52ae2
commit a13eedf556
No known key found for this signature in database
GPG key ID: 2E10CC61CAC8F36D
10 changed files with 112 additions and 40 deletions

View file

@ -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<crypto::hash>& ids)const
void block_chain_shortener::get_short_chain_history(std::list<crypto::hash>& ids)const
{
ids.clear();
uint64_t i = 0;
@ -137,7 +151,7 @@ void wallet_chain_shortener::get_short_chain_history(std::list<crypto::hash>& 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<crypto::hash>& id
ids.push_back(m_genesis);
}
//----------------------------------------------------------------------------------------------------
bool wallet_chain_shortener::lookup_item_around(uint64_t i, std::pair<uint64_t, crypto::hash>& result)const
bool block_chain_shortener::lookup_item_around(uint64_t i, std::pair<uint64_t, crypto::hash>& 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::pair<uint64_t,
i = i - i % devider;
auto it = pcontainer->find(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<uint64_t, crypto::hash>& 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);

View file

@ -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<crypto::hash>& ids)const;
bool lookup_item_around(uint64_t i, std::pair<uint64_t, crypto::hash>& result)const;

View file

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

View file

@ -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<crypto::public_key>& pkeys)const;
bool get_short_chain_history(std::list<crypto::hash>& ids)const;

View file

@ -9,6 +9,7 @@
#include <atomic>
#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<block_context_info> m_needed_objects;
std::unordered_set<crypto::hash> m_requested_objects;
std::atomic<uint32_t> 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

View file

@ -744,10 +744,40 @@ namespace currency
{//we have to fetch more objects ids, request blockchain entry
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
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<NOTIFY_REQUEST_CHAIN>(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);

View file

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

View file

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

View file

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

View file

@ -4,12 +4,12 @@
#include <algorithm>
#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++)
{