aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_protocol/cryptonote_protocol_handler.inl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_protocol/cryptonote_protocol_handler.inl')
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl145
1 files changed, 128 insertions, 17 deletions
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index a72b7db79..37157e637 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -68,10 +68,12 @@
#define BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS 1000
#define REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD_STANDBY (5 * 1000000) // microseconds
#define REQUEST_NEXT_SCHEDULED_SPAN_THRESHOLD (30 * 1000000) // microseconds
-#define IDLE_PEER_KICK_TIME (600 * 1000000) // microseconds
+#define IDLE_PEER_KICK_TIME (240 * 1000000) // microseconds
+#define NON_RESPONSIVE_PEER_KICK_TIME (20 * 1000000) // microseconds
#define PASSIVE_PEER_KICK_TIME (60 * 1000000) // microseconds
#define DROP_ON_SYNC_WEDGE_THRESHOLD (30 * 1000000000ull) // nanoseconds
#define LAST_ACTIVITY_STALL_THRESHOLD (2.0f) // seconds
+#define DROP_PEERS_ON_SCORE -2
namespace cryptonote
{
@@ -142,6 +144,8 @@ namespace cryptonote
m_core.get_short_chain_history(r.block_ids);
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
r.prune = m_sync_pruned_blocks;
+ context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
+ context.m_expect_response = NOTIFY_RESPONSE_CHAIN_ENTRY::ID;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
MLOG_PEER_STATE("requesting chain");
@@ -326,6 +330,11 @@ namespace cryptonote
}
}
+ if (hshd.current_height < context.m_remote_blockchain_height)
+ {
+ MINFO(context << "Claims " << hshd.current_height << ", claimed " << context.m_remote_blockchain_height << " before");
+ hit_score(context, 1);
+ }
context.m_remote_blockchain_height = hshd.current_height;
context.m_pruning_seed = hshd.pruning_seed;
#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED
@@ -427,7 +436,7 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context)
{
- MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, "Received NOTIFY_NEW_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
+ MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, context << "Received NOTIFY_NEW_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
if(context.m_state != cryptonote_connection_context::state_normal)
return 1;
if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks
@@ -487,6 +496,8 @@ namespace cryptonote
m_core.get_short_chain_history(r.block_ids);
r.prune = m_sync_pruned_blocks;
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
+ context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
+ context.m_expect_response = NOTIFY_RESPONSE_CHAIN_ENTRY::ID;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
MLOG_PEER_STATE("requesting chain");
@@ -498,7 +509,7 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context)
{
- MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, "Received NOTIFY_NEW_FLUFFY_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
+ MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, context << "Received NOTIFY_NEW_FLUFFY_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)");
if(context.m_state != cryptonote_connection_context::state_normal)
return 1;
if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks
@@ -765,6 +776,8 @@ namespace cryptonote
m_core.get_short_chain_history(r.block_ids);
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
r.prune = m_sync_pruned_blocks;
+ context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
+ context.m_expect_response = NOTIFY_RESPONSE_CHAIN_ENTRY::ID;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
MLOG_PEER_STATE("requesting chain");
@@ -1029,6 +1042,7 @@ namespace cryptonote
drop_connection(context, false, false);
return 1;
}
+ context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()="
<< rsp.blocks.size() << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height
<< ", missed_ids.size()=" << rsp.missed_ids.size());
@@ -1062,6 +1076,14 @@ namespace cryptonote
boost::posix_time::ptime request_time = context.m_last_request_time;
context.m_last_request_time = boost::date_time::not_a_date_time;
+ if (context.m_expect_response != NOTIFY_RESPONSE_GET_OBJECTS::ID)
+ {
+ LOG_ERROR_CCONTEXT("Got NOTIFY_RESPONSE_GET_OBJECTS out of the blue, dropping connection");
+ drop_connection(context, true, false);
+ return 1;
+ }
+ context.m_expect_response = 0;
+
// calculate size of request
size_t size = 0;
size_t blocks_size = 0;
@@ -1107,6 +1129,11 @@ namespace cryptonote
return 1;
}
+ if (arg.current_blockchain_height < context.m_remote_blockchain_height)
+ {
+ MINFO(context << "Claims " << arg.current_blockchain_height << ", claimed " << context.m_remote_blockchain_height << " before");
+ hit_score(context, 1);
+ }
context.m_remote_blockchain_height = arg.current_blockchain_height;
if (context.m_remote_blockchain_height > m_core.get_target_blockchain_height())
m_core.set_target_blockchain_height(context.m_remote_blockchain_height);
@@ -1230,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;
@@ -1331,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;
@@ -1429,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())
@@ -1468,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;
@@ -1509,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);
@@ -1528,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);
@@ -1666,24 +1698,45 @@ skip:
bool t_cryptonote_protocol_handler<t_core>::kick_idle_peers()
{
MTRACE("Checking for idle peers...");
+ std::vector<std::pair<boost::uuids::uuid, unsigned>> idle_peers;
m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool
{
if (context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time != boost::date_time::not_a_date_time)
{
const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
const boost::posix_time::time_duration dt = now - context.m_last_request_time;
- if (dt.total_microseconds() > IDLE_PEER_KICK_TIME)
+ const auto ms = dt.total_microseconds();
+ if (ms > IDLE_PEER_KICK_TIME || (context.m_expect_response && ms > NON_RESPONSIVE_PEER_KICK_TIME))
{
- MINFO(context << " kicking idle peer, last update " << (dt.total_microseconds() / 1.e6) << " seconds ago");
- LOG_PRINT_CCONTEXT_L2("requesting callback");
- context.m_last_request_time = boost::date_time::not_a_date_time;
- context.m_state = cryptonote_connection_context::state_standby; // we'll go back to adding, then (if we can't), download
- ++context.m_callback_request_count;
- m_p2p->request_callback(context);
+ if (context.m_score-- >= 0)
+ {
+ MINFO(context << " kicking idle peer, last update " << (dt.total_microseconds() / 1.e6) << " seconds ago, expecting " << (int)context.m_expect_response);
+ LOG_PRINT_CCONTEXT_L2("requesting callback");
+ context.m_last_request_time = boost::date_time::not_a_date_time;
+ context.m_expect_response = 0;
+ context.m_state = cryptonote_connection_context::state_standby; // we'll go back to adding, then (if we can't), download
+ ++context.m_callback_request_count;
+ m_p2p->request_callback(context);
+ }
+ else
+ {
+ idle_peers.push_back(std::make_pair(context.m_connection_id, context.m_expect_response == 0 ? 1 : 5));
+ }
}
}
return true;
});
+
+ for (const auto &e: idle_peers)
+ {
+ const auto &uuid = e.first;
+ m_p2p->for_connection(uuid, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{
+ MINFO(ctx << "dropping idle peer with negative score");
+ drop_connection_with_score(ctx, e.second, false);
+ return true;
+ });
+ }
+
return true;
}
//------------------------------------------------------------------------------------------------------------------------
@@ -2125,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)
{
@@ -2206,6 +2259,7 @@ skip:
}
}
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
+ context.m_expect_response = NOTIFY_RESPONSE_GET_OBJECTS::ID;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size()
<< "requested blocks count=" << count << " / " << count_limit << " from " << span.first << ", first hash " << req.blocks.front());
//epee::net_utils::network_throttle_manager::get_global_throttle_inreq().logger_handle_net("log/dr-monero/net/req-all.data", sec, get_avg_block_size());
@@ -2260,8 +2314,8 @@ skip:
uint64_t start_height;
std::vector<cryptonote::block_complete_entry> blocks;
boost::uuids::uuid span_connection_id;
- bool filled = false;
- if (m_block_queue.get_next_span(start_height, blocks, span_connection_id, filled) && filled)
+ 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");
@@ -2296,6 +2350,7 @@ skip:
//LOG_PRINT_CCONTEXT_L1("r = " << 200);
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
+ context.m_expect_response = NOTIFY_RESPONSE_CHAIN_ENTRY::ID;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() << ", start_from_current_chain " << start_from_current_chain);
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
MLOG_PEER_STATE("requesting chain");
@@ -2321,7 +2376,7 @@ skip:
}
else
{
- MINFO(context << " we've reached this peer's blockchain height");
+ MINFO(context << " we've reached this peer's blockchain height (theirs " << context.m_remote_blockchain_height << ", our target " << m_core.get_target_blockchain_height());
}
}
return true;
@@ -2418,6 +2473,14 @@ skip:
<< ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height);
MLOG_PEER_STATE("received chain");
+ if (context.m_expect_response != NOTIFY_RESPONSE_CHAIN_ENTRY::ID)
+ {
+ LOG_ERROR_CCONTEXT("Got NOTIFY_RESPONSE_CHAIN_ENTRY out of the blue, dropping connection");
+ drop_connection(context, true, false);
+ return 1;
+ }
+ context.m_expect_response = 0;
+
context.m_last_request_time = boost::date_time::not_a_date_time;
m_sync_download_chain_size += arg.m_block_ids.size() * sizeof(crypto::hash);
@@ -2428,7 +2491,7 @@ skip:
drop_connection(context, true, false);
return 1;
}
- if (arg.total_height < arg.m_block_ids.size() || arg.start_height > arg.total_height - arg.m_block_ids.size())
+ if (arg.total_height < arg.m_block_ids.size() || arg.start_height > arg.total_height - arg.m_block_ids.size() || arg.start_height >= m_core.get_current_blockchain_height())
{
LOG_ERROR_CCONTEXT("sent invalid start/nblocks/height, dropping connection");
drop_connection(context, true, false);
@@ -2448,6 +2511,11 @@ skip:
drop_connection(context, false, false);
return 1;
}
+ if (arg.total_height < context.m_remote_blockchain_height)
+ {
+ MINFO(context << "Claims " << arg.total_height << ", claimed " << context.m_remote_blockchain_height << " before");
+ hit_score(context, 1);
+ }
context.m_remote_blockchain_height = arg.total_height;
context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1;
if(context.m_last_response_height > context.m_remote_blockchain_height)
@@ -2469,8 +2537,15 @@ skip:
context.m_needed_objects.clear();
uint64_t added = 0;
+ std::unordered_set<crypto::hash> blocks_found;
for (size_t i = 0; i < arg.m_block_ids.size(); ++i)
{
+ if (!blocks_found.insert(arg.m_block_ids[i]).second)
+ {
+ LOG_ERROR_CCONTEXT("Duplicate blocks in chain entry response, dropping connection");
+ drop_connection(context, true, false);
+ return 1;
+ }
const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i];
context.m_needed_objects.push_back(std::make_pair(arg.m_block_ids[i], block_weight));
if (++added == n_use_blocks)
@@ -2564,6 +2639,19 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
+ void t_cryptonote_protocol_handler<t_core>::hit_score(cryptonote_connection_context &context, int32_t score)
+ {
+ if (score <= 0)
+ {
+ MERROR("Negative score hit");
+ return;
+ }
+ context.m_score -= score;
+ if (context.m_score <= DROP_PEERS_ON_SCORE)
+ drop_connection_with_score(context, 5, false);
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
std::string t_cryptonote_protocol_handler<t_core>::get_peers_overview() const
{
std::stringstream ss;
@@ -2659,6 +2747,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;