diff options
Diffstat (limited to 'src/cryptonote_protocol/cryptonote_protocol_handler.inl')
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 199 |
1 files changed, 117 insertions, 82 deletions
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 4652bf23e..5b3b059a4 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -209,10 +209,10 @@ namespace cryptonote cnx.support_flags = support_flags; cnx.recv_count = cntxt.m_recv_cnt; - cnx.recv_idle_time = timestamp - cntxt.m_last_recv; + cnx.recv_idle_time = timestamp - std::max(cntxt.m_started, cntxt.m_last_recv); cnx.send_count = cntxt.m_send_cnt; - cnx.send_idle_time = timestamp - cntxt.m_last_send; + cnx.send_idle_time = timestamp - std::max(cntxt.m_started, cntxt.m_last_send); cnx.state = get_protocol_state_string(cntxt.m_state); @@ -239,6 +239,8 @@ namespace cryptonote cnx.connection_id = cntxt.m_connection_id; + cnx.height = cntxt.m_remote_blockchain_height; + connections.push_back(cnx); return true; @@ -256,6 +258,16 @@ namespace cryptonote if(context.m_state == cryptonote_connection_context::state_synchronizing) return true; + // from v6, if the peer advertises a top block version, reject if it's not what it should be (will only work if no voting) + const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1); + if (version >= 6 && version != hshd.top_version) + { + LOG_DEBUG_CC(context, "Ignoring due to wrong top version " << (unsigned)hshd.top_version << ", expected " << (unsigned)version); + return false; + } + + context.m_remote_blockchain_height = hshd.current_height; + uint64_t target = m_core.get_target_blockchain_height(); if (target == 0) target = m_core.get_current_blockchain_height(); @@ -285,7 +297,6 @@ namespace cryptonote } LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; - context.m_remote_blockchain_height = hshd.current_height; //let the socket to send response to handshake, but request callback, to let send request data after response LOG_PRINT_CCONTEXT_L2("requesting callback"); ++context.m_callback_request_count; @@ -297,6 +308,7 @@ namespace cryptonote bool t_cryptonote_protocol_handler<t_core>::get_payload_sync_data(CORE_SYNC_DATA& hshd) { m_core.get_blockchain_top(hshd.current_height, hshd.top_id); + hshd.top_version = m_core.get_hard_fork_version(hshd.current_height); hshd.current_height +=1; return true; } @@ -332,10 +344,9 @@ namespace cryptonote if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); - m_p2p->drop_connection(context); + drop_connection(context, false, false); m_core.cleanup_handle_incoming_blocks(); m_core.resume_mine(); - m_block_queue.flush_spans(context.m_connection_id); return 1; } } @@ -347,8 +358,7 @@ namespace cryptonote if(bvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, true, false); return 1; } if(bvc.m_added_to_main_chain) @@ -401,8 +411,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -435,8 +444,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -450,8 +458,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -475,8 +482,7 @@ namespace cryptonote << "dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -493,8 +499,7 @@ namespace cryptonote if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -516,8 +521,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -537,8 +541,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -615,8 +618,7 @@ namespace cryptonote if( bvc.m_verifivation_failed ) { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, true, false); return 1; } if( bvc.m_added_to_main_chain ) @@ -649,8 +651,7 @@ namespace cryptonote ); m_core.resume_mine(); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -670,8 +671,7 @@ namespace cryptonote if (!m_core.get_block_by_hash(arg.block_hash, b)) { LOG_ERROR_CCONTEXT("failed to find block: " << arg.block_hash << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -698,8 +698,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } } @@ -710,16 +709,14 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " << "failed to get requested transactions"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if (!missed.empty() || txs.size() != txids.size()) { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " << missed.size() << " requested transactions not found" << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -762,8 +759,7 @@ namespace cryptonote if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if(tvc.m_should_be_relayed) @@ -789,8 +785,7 @@ namespace cryptonote if(!m_core.handle_get_objects(arg, rsp, context)) { LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() @@ -857,8 +852,7 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -880,16 +874,14 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: " << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen)) { LOG_ERROR_CCONTEXT("sent wrong block: block: miner tx does not have exactly one txin_gen input" << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if (start_height == std::numeric_limits<uint64_t>::max()) @@ -901,16 +893,14 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) << " wasn't requested, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if(b.tx_hashes.size() != block_entry.txs.size()) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -922,8 +912,7 @@ namespace cryptonote { MERROR("returned not all requested objects (context.m_requested_objects.size()=" << context.m_requested_objects.size() << "), dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -936,7 +925,8 @@ namespace cryptonote } { - MLOG_YELLOW(el::Level::Debug, "Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size()); + MLOG_YELLOW(el::Level::Debug, context << " Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size() + << ", blocks: " << start_height << " - " << (start_height + arg.blocks.size() - 1)); // add that new span to the block queue const boost::posix_time::time_duration dt = now - context.m_last_request_time; @@ -944,6 +934,8 @@ namespace cryptonote MDEBUG(context << " adding span: " << arg.blocks.size() << " at height " << start_height << ", " << dt.total_microseconds()/1e6 << " seconds, " << (rate/1e3) << " 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); + context.m_last_known_hash = cryptonote::get_blob_hash(arg.blocks.back().block); + if (m_core.get_test_drop_download() && m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing // We try to lock the sync lock. If we can, it means no other thread is @@ -967,7 +959,6 @@ namespace cryptonote uint64_t start_height; std::list<cryptonote::block_complete_entry> blocks; boost::uuids::uuid span_connection_id; - m_block_queue.mark_last_block(previous_height - 1); if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id)) { MDEBUG(context << " no next span found, going back to download"); @@ -975,9 +966,41 @@ namespace cryptonote } MDEBUG(context << " next span in the queue has blocks " << start_height << "-" << (start_height + blocks.size() - 1) << ", we need " << previous_height); - if (previous_height < start_height || previous_height >= start_height + blocks.size()) + + + block new_block; + if (!parse_and_validate_block_from_blob(blocks.front().block, new_block)) { - MDEBUG(context << " this span is not what we need, going back to download"); + MERROR("Failed to parse block, but it should already have been parsed"); + break; + } + bool parent_known = m_core.have_block(new_block.prev_id); + if (!parent_known) + { + // it could be: + // - later in the current chain + // - later in an alt chain + // - orphan + // if it was requested, then it'll be resolved later, otherwise it's an orphan + bool parent_requested = false; + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ + if (context.m_requested_objects.find(new_block.prev_id) != context.m_requested_objects.end()) + { + parent_requested = true; + return false; + } + return true; + }); + if (!parent_requested) + { + LOG_ERROR_CCONTEXT("Got block with unknown parent which was not requested - dropping connection"); + // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it + m_block_queue.remove_spans(span_connection_id, start_height); + return 1; + } + + // parent was requested, so we wait for it to be retrieved + MINFO(context << " parent was requested, we'll get back to it"); break; } @@ -1008,8 +1031,7 @@ namespace cryptonote if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = " << epee::string_tools::pod_to_hex(get_blob_hash(*it)) << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id, true); + drop_connection(context, false, true); return true; })) LOG_ERROR_CCONTEXT("span connection id not found"); @@ -1034,9 +1056,7 @@ namespace cryptonote { 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"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id, true); + drop_connection(context, true, true); return true; })) LOG_ERROR_CCONTEXT("span connection id not found"); @@ -1050,9 +1070,7 @@ namespace cryptonote { 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"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id, true); + drop_connection(context, true, true); return true; })) LOG_ERROR_CCONTEXT("span connection id not found"); @@ -1072,7 +1090,7 @@ namespace cryptonote m_core.cleanup_handle_incoming_blocks(); - m_block_queue.mark_last_block(m_core.get_current_blockchain_height() - 1); + m_block_queue.remove_spans(span_connection_id, start_height); if (m_core.get_current_blockchain_height() > previous_height) { @@ -1102,8 +1120,7 @@ skip: if (!request_missing_objects(context, true, force_next_span)) { LOG_ERROR_CCONTEXT("Failed to request missing objects, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } return 1; @@ -1123,8 +1140,7 @@ skip: if(!m_core.find_blockchain_supplement(arg.block_ids, r)) { LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } LOG_PRINT_CCONTEXT_L2("-->>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()); @@ -1190,8 +1206,6 @@ skip: template<class t_core> bool t_cryptonote_protocol_handler<t_core>::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks, bool force_next_span) { - m_block_queue.mark_last_block(m_core.get_current_blockchain_height() - 1); - // flush stale spans std::set<boost::uuids::uuid> live_connections; m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ @@ -1297,8 +1311,13 @@ skip: context.m_last_response_height = 0; goto skip; } + // take out blocks we already have + while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front())) + { + context.m_needed_objects.pop_front(); + } const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; - span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id); + span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_needed_objects); MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); } if (span.second == 0 && !force_next_span) @@ -1349,8 +1368,6 @@ skip: auto j = it++; context.m_needed_objects.erase(j); } - - m_block_queue.set_span_hashes(span.first, context.m_connection_id, hashes); } context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); @@ -1373,10 +1390,9 @@ skip: if (!start_from_current_chain) { - // we'll want to start off from where we are on the download side, which may not be added yet - crypto::hash last_known_hash = m_block_queue.get_last_known_hash(); - if (last_known_hash != cryptonote::null_hash && r.block_ids.front() != last_known_hash) - r.block_ids.push_front(last_known_hash); + // 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 != cryptonote::null_hash && r.block_ids.front() != context.m_last_known_hash) + r.block_ids.push_front(context.m_last_known_hash); } handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) @@ -1453,9 +1469,7 @@ skip: if(!arg.m_block_ids.size()) { LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, true, false); return 1; } @@ -1466,8 +1480,7 @@ skip: LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with m_total_height=" << arg.total_height << ", m_start_height=" << arg.start_height << ", m_block_ids.size()=" << arg.m_block_ids.size()); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -1479,8 +1492,7 @@ skip: if (!request_missing_objects(context, false)) { LOG_ERROR_CCONTEXT("Failed to request missing objects, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } return 1; @@ -1536,6 +1548,29 @@ skip: m_core.on_transaction_relayed(*tx_blob_it); return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context); } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + void t_cryptonote_protocol_handler<t_core>::drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans) + { + if (add_fail) + m_p2p->add_host_fail(context.m_remote_address); + m_p2p->drop_connection(context); + + uint64_t target = 0; + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { + if (cntxt.m_state >= cryptonote_connection_context::state_synchronizing && cntxt.m_connection_id != context.m_connection_id) + target = std::max(target, cntxt.m_remote_blockchain_height); + return true; + }); + const uint64_t previous_target = m_core.get_target_blockchain_height(); + if (target < previous_target) + { + MINFO("Target height decreasing from " << previous_target << " to " << target); + m_core.set_target_blockchain_height(target); + } + + m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> |