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.inl174
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>