diff options
Diffstat (limited to 'src/cryptonote_protocol/cryptonote_protocol_handler.inl')
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 174 |
1 files changed, 146 insertions, 28 deletions
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 27f8888bb..b57fc0f0f 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -137,10 +137,11 @@ namespace cryptonote CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); --context.m_callback_request_count; - if(context.m_state == cryptonote_connection_context::state_synchronizing) + if(context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time == boost::posix_time::not_a_date_time) { NOTIFY_REQUEST_CHAIN::request r = {}; context.m_needed_objects.clear(); + context.m_expect_height = m_core.get_current_blockchain_height(); 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; @@ -408,6 +409,7 @@ namespace cryptonote ++context.m_callback_request_count; m_p2p->request_callback(context); MLOG_PEER_STATE("requesting callback"); + context.m_num_requested = 0; return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -493,6 +495,7 @@ namespace cryptonote context.m_needed_objects.clear(); context.m_state = cryptonote_connection_context::state_synchronizing; NOTIFY_REQUEST_CHAIN::request r = {}; + context.m_expect_height = m_core.get_current_blockchain_height(); 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(?) @@ -546,6 +549,7 @@ namespace cryptonote } std::vector<tx_blob_entry> have_tx; + have_tx.reserve(new_block.tx_hashes.size()); // Instead of requesting missing transactions by hash like BTC, // we do it by index (thanks to a suggestion from moneromooo) because @@ -554,6 +558,7 @@ namespace cryptonote // Also, remember to pepper some whitespace changes around to bother // moneromooo ... only because I <3 him. std::vector<uint64_t> need_tx_indices; + need_tx_indices.reserve(new_block.tx_hashes.size()); transaction tx; crypto::hash tx_hash; @@ -773,6 +778,7 @@ namespace cryptonote context.m_needed_objects.clear(); context.m_state = cryptonote_connection_context::state_synchronizing; NOTIFY_REQUEST_CHAIN::request r = {}; + context.m_expect_height = m_core.get_current_blockchain_height(); 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; @@ -825,6 +831,7 @@ namespace cryptonote } std::vector<crypto::hash> txids; + txids.reserve(b.tx_hashes.size()); NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response; fluffy_response.b.block = t_serializable_object_to_blob(b); fluffy_response.current_blockchain_height = arg.current_blockchain_height; @@ -1168,7 +1175,16 @@ namespace cryptonote return 1; } if (start_height == std::numeric_limits<uint64_t>::max()) + { start_height = boost::get<txin_gen>(b.miner_tx.vin[0]).height; + if (start_height > context.m_expect_height) + { + LOG_ERROR_CCONTEXT("sent block ahead of expected height, dropping connection"); + drop_connection(context, false, false); + ++m_sync_bad_spans_downloaded; + return 1; + } + } auto req_it = context.m_requested_objects.find(block_hash); if(req_it == context.m_requested_objects.end()) @@ -1714,6 +1730,7 @@ skip: 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_expect_height = 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); @@ -1813,6 +1830,16 @@ skip: LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); return 1; } + if (r.m_block_ids.size() >= 2) + { + cryptonote::block b; + if (!m_core.get_block_by_hash(r.m_block_ids[1], b)) + { + LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN: first block not found"); + return 1; + } + r.first_block = cryptonote::block_to_blob(b); + } MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size()); post_notify<NOTIFY_RESPONSE_CHAIN_ENTRY>(r, context); return 1; @@ -1967,7 +1994,7 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> - void t_cryptonote_protocol_handler<t_core>::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const + size_t t_cryptonote_protocol_handler<t_core>::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const { // take out blocks we already have size_t skip = 0; @@ -1984,6 +2011,7 @@ skip: MDEBUG(context << "skipping " << skip << "/" << context.m_needed_objects.size() << " blocks"); context.m_needed_objects = std::vector<std::pair<crypto::hash, uint64_t>>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); } + return skip; } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> @@ -2037,7 +2065,17 @@ skip: const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD; bool queue_proceed = nspans < BLOCK_QUEUE_NSPANS_THRESHOLD || size < block_queue_size_threshold; // get rid of blocks we already requested, or already have - skip_unneeded_hashes(context, true); + if (skip_unneeded_hashes(context, true) && context.m_needed_objects.empty() && context.m_num_requested == 0) + { + if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(bc_height)) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } + MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done"); + context.m_state = cryptonote_connection_context::state_normal; + return true; + } uint64_t next_needed_height = m_block_queue.get_next_needed_height(bc_height); uint64_t next_block_height; if (context.m_needed_objects.empty()) @@ -2154,6 +2192,7 @@ skip: if (span.second > 0) { is_next = true; + req.blocks.reserve(hashes.size()); for (const auto &hash: hashes) { req.blocks.push_back(hash); @@ -2173,7 +2212,17 @@ skip: context.m_last_response_height = 0; goto skip; } - skip_unneeded_hashes(context, false); + if (skip_unneeded_hashes(context, false) && context.m_needed_objects.empty() && context.m_num_requested == 0) + { + if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(m_core.get_current_blockchain_height())) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } + MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done"); + context.m_state = cryptonote_connection_context::state_normal; + return true; + } 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); @@ -2202,6 +2251,7 @@ skip: if (span.second > 0) { is_next = true; + req.blocks.reserve(hashes.size()); for (const auto &hash: hashes) { req.blocks.push_back(hash); @@ -2235,6 +2285,7 @@ skip: return false; } + req.blocks.reserve(req.blocks.size() + span.second); for (size_t n = 0; n < span.second; ++n) { req.blocks.push_back(context.m_needed_objects[n].first); @@ -2259,6 +2310,7 @@ skip: } } context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); + context.m_expect_height = span.first; 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()); @@ -2269,35 +2321,22 @@ skip: << "/" << tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) << ", ours " << tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()) << ", peer stripe " << tools::get_pruning_stripe(context.m_pruning_seed)); + context.m_num_requested += req.blocks.size(); post_notify<NOTIFY_REQUEST_GET_OBJECTS>(req, context); MLOG_PEER_STATE("requesting objects"); return true; } - // if we're still around, we might be at a point where the peer is pruned, so we could either - // drop it to make space for other peers, or ask for a span further down the line - const uint32_t next_stripe = get_next_needed_pruning_stripe().first; - const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); - const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()); - if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe) + // we can do nothing, so drop this peer to make room for others unless we think we've downloaded all we need + const uint64_t blockchain_height = m_core.get_current_blockchain_height(); + if (std::max(blockchain_height, m_block_queue.get_next_needed_height(blockchain_height)) >= m_core.get_target_blockchain_height()) { - // at this point, we have to either close the connection, or start getting blocks past the - // current point, or become dormant - MDEBUG(context << "this peer is pruned at seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << - ", next stripe needed is " << next_stripe); - if (!context.m_is_income) - { - if (should_drop_connection(context, next_stripe)) - { - m_p2p->add_used_stripe_peer(context); - return false; // drop outgoing connections - } - } - // we'll get back stuck waiting for the go ahead context.m_state = cryptonote_connection_context::state_normal; MLOG_PEER_STATE("Nothing to do for now, switching to normal state"); return true; } + MLOG_PEER_STATE("We can download nothing from this peer, dropping"); + return false; } skip: @@ -2331,6 +2370,7 @@ skip: {//we have to fetch more objects ids, request blockchain entry NOTIFY_REQUEST_CHAIN::request r = {}; + context.m_expect_height = m_core.get_current_blockchain_height(); m_core.get_short_chain_history(r.block_ids); CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty"); @@ -2338,7 +2378,10 @@ skip: { // we'll want to start off from where we are on that peer, which may not be added yet if (context.m_last_known_hash != crypto::null_hash && r.block_ids.front() != context.m_last_known_hash) + { + context.m_expect_height = std::numeric_limits<uint64_t>::max(); r.block_ids.push_front(context.m_last_known_hash); + } } handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) @@ -2480,6 +2523,12 @@ skip: return 1; } context.m_expect_response = 0; + if (arg.start_height + 1 > context.m_expect_height) // we expect an overlapping block + { + LOG_ERROR_CCONTEXT("Got NOTIFY_RESPONSE_CHAIN_ENTRY past expected height, dropping connection"); + drop_connection(context, true, false); + return 1; + } context.m_last_request_time = boost::date_time::not_a_date_time; @@ -2491,7 +2540,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() || arg.start_height >= m_core.get_current_blockchain_height()) + if (arg.total_height < arg.m_block_ids.size() || arg.start_height > arg.total_height - arg.m_block_ids.size()) { LOG_ERROR_CCONTEXT("sent invalid start/nblocks/height, dropping connection"); drop_connection(context, true, false); @@ -2536,20 +2585,77 @@ skip: } context.m_needed_objects.clear(); + context.m_needed_objects.reserve(arg.m_block_ids.size()); uint64_t added = 0; std::unordered_set<crypto::hash> blocks_found; + bool first = true; + bool expect_unknown = false; 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); + drop_connection_with_score(context, 5, false); return 1; } + int where; + const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where); + if (first) + { + if (!have_block && !m_block_queue.requested(arg.m_block_ids[i]) && !m_block_queue.have(arg.m_block_ids[i])) + { + LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + if (!have_block) + expect_unknown = true; + } + if (!first) + { + // after the first, blocks may be known or unknown, but if they are known, + // they should be at the same height if on the main chain + if (have_block) + { + switch (where) + { + default: + case HAVE_BLOCK_INVALID: + LOG_ERROR_CCONTEXT("Block is invalid or known without known type, dropping connection"); + drop_connection(context, true, false); + return 1; + case HAVE_BLOCK_MAIN_CHAIN: + if (expect_unknown) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + if (m_core.get_block_id_by_height(arg.start_height + i) != arg.m_block_ids[i]) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but not at the expected height, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + break; + case HAVE_BLOCK_ALT_CHAIN: + if (expect_unknown) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + break; + } + } + else + expect_unknown = true; + } 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) break; + first = false; } context.m_last_response_height -= arg.m_block_ids.size() - n_use_blocks; @@ -2563,6 +2669,7 @@ skip: if (arg.total_height > m_core.get_target_blockchain_height()) m_core.set_target_blockchain_height(arg.total_height); + context.m_num_requested = 0; return 1; } //------------------------------------------------------------------------------------------------------------------------ @@ -2579,6 +2686,7 @@ skip: std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> fullConnections, fluffyConnections; m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags) { + // peer_id also filters out connections before handshake if (peer_id && exclude_context.m_connection_id != context.m_connection_id && context.m_remote_address.get_zone() == epee::net_utils::zone::public_) { if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS)) @@ -2726,18 +2834,28 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::is_busy_syncing() + { + const boost::unique_lock<boost::mutex> sync{m_sync_lock, boost::try_to_lock}; + return !sync.owns_lock(); + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> void t_cryptonote_protocol_handler<t_core>::drop_connection_with_score(cryptonote_connection_context &context, unsigned score, bool flush_all_spans) { LOG_DEBUG_CC(context, "dropping connection id " << context.m_connection_id << " (pruning seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << "), score " << score << ", flush_all_spans " << flush_all_spans); - if (score > 0) - m_p2p->add_host_fail(context.m_remote_address, score); - m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + // copy since dropping the connection will invalidate the context, and thus the address + const auto remote_address = context.m_remote_address; + m_p2p->drop_connection(context); + + if (score > 0) + m_p2p->add_host_fail(remote_address, score); } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> |