diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2020-12-08 17:55:24 +0000 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2020-12-10 22:00:01 +0000 |
commit | d729730d380fa25f0b122dd4ef6f331a35748a4b (patch) | |
tree | 6e7725c03947b47272261de132abb166e11749b8 | |
parent | Merge pull request #7109 (diff) | |
download | monero-d729730d380fa25f0b122dd4ef6f331a35748a4b.tar.xz |
protocol: drop origin IP if a block fails to verify in sync mode
It would otherwise be possible for a peer to send bad blocks,
then disconnect and reconnect again, escaping bans
-rw-r--r-- | src/cryptonote_protocol/block_queue.cpp | 15 | ||||
-rw-r--r-- | src/cryptonote_protocol/block_queue.h | 18 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.h | 1 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 37 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 6 | ||||
-rw-r--r-- | tests/unit_tests/block_queue.cpp | 20 |
6 files changed, 64 insertions, 33 deletions
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index bbde91c1f..2f5b693dd 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -52,12 +52,12 @@ namespace std { namespace cryptonote { -void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, float rate, size_t size) +void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size) { boost::unique_lock<boost::recursive_mutex> lock(mutex); std::vector<crypto::hash> hashes; bool has_hashes = remove_span(height, &hashes); - blocks.insert(span(height, std::move(bcel), connection_id, rate, size)); + blocks.insert(span(height, std::move(bcel), connection_id, addr, rate, size)); if (has_hashes) { for (const crypto::hash &h: hashes) @@ -69,11 +69,11 @@ void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_comp } } -void block_queue::add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time) +void block_queue::add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, boost::posix_time::ptime time) { CHECK_AND_ASSERT_THROW_MES(nblocks > 0, "Empty span"); boost::unique_lock<boost::recursive_mutex> lock(mutex); - blocks.insert(span(height, nblocks, connection_id, time)); + blocks.insert(span(height, nblocks, connection_id, addr, time)); } void block_queue::flush_spans(const boost::uuids::uuid &connection_id, bool all) @@ -228,7 +228,7 @@ bool block_queue::have(const crypto::hash &hash) const return have_blocks.find(hash) != have_blocks.end(); } -std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<std::pair<crypto::hash, uint64_t>> &block_hashes, boost::posix_time::ptime time) +std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<std::pair<crypto::hash, uint64_t>> &block_hashes, boost::posix_time::ptime time) { boost::unique_lock<boost::recursive_mutex> lock(mutex); @@ -305,7 +305,7 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei return std::make_pair(0, 0); } MDEBUG("Reserving span " << span_start_height << " - " << (span_start_height + span_length - 1) << " for " << connection_id); - add_blocks(span_start_height, span_length, connection_id, time); + add_blocks(span_start_height, span_length, connection_id, addr, time); set_span_hashes(span_start_height, connection_id, hashes); return std::make_pair(span_start_height, span_length); } @@ -354,7 +354,7 @@ void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uui } } -bool block_queue::get_next_span(uint64_t &height, std::vector<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, bool filled) const +bool block_queue::get_next_span(uint64_t &height, std::vector<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, epee::net_utils::network_address &addr, bool filled) const { boost::unique_lock<boost::recursive_mutex> lock(mutex); if (blocks.empty()) @@ -367,6 +367,7 @@ bool block_queue::get_next_span(uint64_t &height, std::vector<cryptonote::block_ height = i->start_block_height; bcel = i->blocks; connection_id = i->connection_id; + addr = i->origin; return true; } } diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 57d2a6490..30fb5bc21 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -36,6 +36,7 @@ #include <unordered_set> #include <boost/thread/recursive_mutex.hpp> #include <boost/uuid/uuid.hpp> +#include "net/net_utils_base.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn.block_queue" @@ -57,19 +58,20 @@ namespace cryptonote float rate; size_t size; boost::posix_time::ptime time; + epee::net_utils::network_address origin{}; - span(uint64_t start_block_height, std::vector<cryptonote::block_complete_entry> blocks, const boost::uuids::uuid &connection_id, float rate, size_t size): - start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time() {} - span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time): - start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time) {} + span(uint64_t start_block_height, std::vector<cryptonote::block_complete_entry> blocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size): + start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time(boost::date_time::min_date_time), origin(addr) {} + span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, boost::posix_time::ptime time): + start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time), origin(addr) {} bool operator<(const span &s) const { return start_block_height < s.start_block_height; } }; typedef std::set<span> block_map; public: - void add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, float rate, size_t size); - void add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time = boost::date_time::min_date_time); + void add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size); + void add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, boost::posix_time::ptime time = boost::date_time::min_date_time); void flush_spans(const boost::uuids::uuid &connection_id, bool all = false); void flush_stale_spans(const std::set<boost::uuids::uuid> &live_connections); bool remove_span(uint64_t start_block_height, std::vector<crypto::hash> *hashes = NULL); @@ -78,12 +80,12 @@ namespace cryptonote void print() const; std::string get_overview(uint64_t blockchain_height) const; bool has_unpruned_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) const; - std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<std::pair<crypto::hash, uint64_t>> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); + std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<std::pair<crypto::hash, uint64_t>> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); uint64_t get_next_needed_height(uint64_t blockchain_height) const; std::pair<uint64_t, uint64_t> get_next_span_if_scheduled(std::vector<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const; void reset_next_span_time(boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time()); void set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector<crypto::hash> hashes); - bool get_next_span(uint64_t &height, std::vector<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, bool filled = true) const; + bool get_next_span(uint64_t &height, std::vector<cryptonote::block_complete_entry> &bcel, boost::uuids::uuid &connection_id, epee::net_utils::network_address &addr, bool filled = true) const; bool has_next_span(const boost::uuids::uuid &connection_id, bool &filled, boost::posix_time::ptime &time) const; bool has_next_span(uint64_t height, bool &filled, boost::posix_time::ptime &time, boost::uuids::uuid &connection_id) const; size_t get_data_size() const; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index ee3a67198..b416b86c8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -141,6 +141,7 @@ namespace cryptonote bool should_ask_for_pruned_data(cryptonote_connection_context& context, uint64_t first_block_height, uint64_t nblocks, bool check_block_weights) const; void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans); void drop_connection_with_score(cryptonote_connection_context &context, unsigned int score, bool flush_all_spans); + void drop_connections(const epee::net_utils::network_address address); bool kick_idle_peers(); bool check_standby_peers(); bool update_sync_search(); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c62a0cd31..cb743316a 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1257,7 +1257,7 @@ namespace cryptonote const boost::posix_time::time_duration dt = now - request_time; const float rate = size * 1e6 / (dt.total_microseconds() + 1); MDEBUG(context << " adding span: " << arg.blocks.size() << " at height " << start_height << ", " << dt.total_microseconds()/1e6 << " seconds, " << (rate/1024) << " kB/s, size now " << (m_block_queue.get_data_size() + blocks_size) / 1048576.f << " MB"); - m_block_queue.add_blocks(start_height, arg.blocks, context.m_connection_id, rate, blocks_size); + m_block_queue.add_blocks(start_height, arg.blocks, context.m_connection_id, context.m_remote_address, rate, blocks_size); const crypto::hash last_block_hash = cryptonote::get_block_hash(b); context.m_last_known_hash = last_block_hash; @@ -1358,7 +1358,8 @@ namespace cryptonote uint64_t start_height; std::vector<cryptonote::block_complete_entry> blocks; boost::uuids::uuid span_connection_id; - if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id)) + epee::net_utils::network_address span_origin; + if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id, span_origin)) { MDEBUG(context << " no next span found, going back to download"); break; @@ -1456,6 +1457,7 @@ namespace cryptonote if (!m_core.prepare_handle_incoming_blocks(blocks, pblocks)) { LOG_ERROR_CCONTEXT("Failure in prepare_handle_incoming_blocks"); + drop_connections(span_origin); return 1; } if (!pblocks.empty() && pblocks.size() != blocks.size()) @@ -1495,6 +1497,7 @@ namespace cryptonote { if(tvc[i].m_verifivation_failed) { + drop_connections(span_origin); if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ cryptonote::transaction tx; crypto::hash txid; @@ -1536,6 +1539,7 @@ namespace cryptonote if(bvc.m_verifivation_failed) { + drop_connections(span_origin); if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); drop_connection_with_score(context, bvc.m_bad_pow ? P2P_IP_FAILS_BEFORE_BLOCK : 1, true); @@ -1555,6 +1559,7 @@ namespace cryptonote } if(bvc.m_marked_as_orphaned) { + drop_connections(span_origin); if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ LOG_PRINT_CCONTEXT_L1("Block received at sync phase was marked as orphaned, dropping connection"); drop_connection(context, true, true); @@ -2173,7 +2178,7 @@ skip: const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; static const uint64_t bp_fork_height = m_core.get_earliest_ideal_height_for_version(8); bool sync_pruned_blocks = m_sync_pruned_blocks && first_block_height >= bp_fork_height && m_core.get_blockchain_pruning_seed(); - span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, sync_pruned_blocks, m_core.get_blockchain_pruning_seed(), context.m_pruning_seed, context.m_remote_blockchain_height, context.m_needed_objects); + span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_remote_address, sync_pruned_blocks, m_core.get_blockchain_pruning_seed(), context.m_pruning_seed, context.m_remote_blockchain_height, context.m_needed_objects); MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); if (span.second > 0) { @@ -2309,7 +2314,8 @@ skip: uint64_t start_height; std::vector<cryptonote::block_complete_entry> blocks; boost::uuids::uuid span_connection_id; - if (m_block_queue.get_next_span(start_height, blocks, span_connection_id, true)) + epee::net_utils::network_address span_origin; + if (m_block_queue.get_next_span(start_height, blocks, span_connection_id, span_origin, true)) { LOG_DEBUG_CC(context, "No other thread is adding blocks, resuming"); MLOG_PEER_STATE("will try to add blocks next"); @@ -2734,6 +2740,29 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + void t_cryptonote_protocol_handler<t_core>::drop_connections(const epee::net_utils::network_address address) + { + MWARNING("dropping connections to " << address.str()); + + m_p2p->add_host_fail(address, 5); + + std::vector<boost::uuids::uuid> drop; + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { + if (address.is_same_host(cntxt.m_remote_address)) + drop.push_back(cntxt.m_connection_id); + return true; + }); + for (const boost::uuids::uuid &id: drop) + { + m_block_queue.flush_spans(id, true); + m_p2p->for_connection(id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + drop_connection(context, true, false); + return true; + }); + } + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> void t_cryptonote_protocol_handler<t_core>::on_connection_close(cryptonote_connection_context &context) { uint64_t target = 0; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index f028fe06f..f7d5bf03c 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -2913,11 +2913,7 @@ namespace cryptonote block_queue.foreach([&](const cryptonote::block_queue::span &span) { const std::string span_connection_id = epee::string_tools::pod_to_hex(span.connection_id); uint32_t speed = (uint32_t)(100.0f * block_queue.get_speed(span.connection_id) + 0.5f); - std::string address = ""; - for (const auto &c: m_p2p.get_payload_object().get_connections()) - if (c.connection_id == span_connection_id) - address = c.address; - res.spans.push_back({span.start_block_height, span.nblocks, span_connection_id, (uint32_t)(span.rate + 0.5f), speed, span.size, address}); + res.spans.push_back({span.start_block_height, span.nblocks, span_connection_id, (uint32_t)(span.rate + 0.5f), speed, span.size, span.origin.str()}); return true; }); res.overview = block_queue.get_overview(res.height); diff --git a/tests/unit_tests/block_queue.cpp b/tests/unit_tests/block_queue.cpp index 77c7f97ff..01c742c11 100644 --- a/tests/unit_tests/block_queue.cpp +++ b/tests/unit_tests/block_queue.cpp @@ -52,36 +52,38 @@ TEST(block_queue, empty) TEST(block_queue, add_stepwise) { + epee::net_utils::network_address na; cryptonote::block_queue bq; - bq.add_blocks(0, 200, uuid1()); + bq.add_blocks(0, 200, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 199); - bq.add_blocks(200, 200, uuid1()); + bq.add_blocks(200, 200, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 399); - bq.add_blocks(401, 200, uuid1()); + bq.add_blocks(401, 200, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 600); - bq.add_blocks(400, 10, uuid1()); + bq.add_blocks(400, 10, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 600); } TEST(block_queue, flush_uuid) { cryptonote::block_queue bq; + epee::net_utils::network_address na; - bq.add_blocks(0, 200, uuid1()); + bq.add_blocks(0, 200, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 199); - bq.add_blocks(200, 200, uuid2()); + bq.add_blocks(200, 200, uuid2(), na); ASSERT_EQ(bq.get_max_block_height(), 399); bq.flush_spans(uuid2()); ASSERT_EQ(bq.get_max_block_height(), 199); bq.flush_spans(uuid1()); ASSERT_EQ(bq.get_max_block_height(), 0); - bq.add_blocks(0, 200, uuid1()); + bq.add_blocks(0, 200, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 199); - bq.add_blocks(200, 200, uuid2()); + bq.add_blocks(200, 200, uuid2(), na); ASSERT_EQ(bq.get_max_block_height(), 399); bq.flush_spans(uuid1()); ASSERT_EQ(bq.get_max_block_height(), 399); - bq.add_blocks(0, 200, uuid1()); + bq.add_blocks(0, 200, uuid1(), na); ASSERT_EQ(bq.get_max_block_height(), 399); } |