aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_protocol/block_queue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_protocol/block_queue.cpp')
-rw-r--r--src/cryptonote_protocol/block_queue.cpp193
1 files changed, 146 insertions, 47 deletions
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index c1989f093..672696944 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -34,6 +34,7 @@
#include <boost/uuid/uuid_io.hpp>
#include "string_tools.h"
#include "cryptonote_protocol_defs.h"
+#include "common/pruning.h"
#include "block_queue.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -60,7 +61,10 @@ void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_comp
if (has_hashes)
{
for (const crypto::hash &h: hashes)
+ {
requested_hashes.insert(h);
+ have_blocks.insert(h);
+ }
set_span_hashes(height, connection_id, hashes);
}
}
@@ -90,7 +94,10 @@ void block_queue::erase_block(block_map::iterator j)
{
CHECK_AND_ASSERT_THROW_MES(j != blocks.end(), "Invalid iterator");
for (const crypto::hash &h: j->hashes)
+ {
requested_hashes.erase(h);
+ have_blocks.erase(h);
+ }
blocks.erase(j);
}
@@ -98,12 +105,10 @@ void block_queue::flush_stale_spans(const std::set<boost::uuids::uuid> &live_con
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
block_map::iterator i = blocks.begin();
- if (i != blocks.end() && is_blockchain_placeholder(*i))
- ++i;
while (i != blocks.end())
{
block_map::iterator j = i++;
- if (live_connections.find(j->connection_id) == live_connections.end() && j->blocks.size() == 0)
+ if (j->blocks.empty() && live_connections.find(j->connection_id) == live_connections.end())
{
erase_block(j);
}
@@ -152,23 +157,56 @@ uint64_t block_queue::get_max_block_height() const
return height;
}
+uint64_t block_queue::get_next_needed_height(uint64_t blockchain_height) const
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ if (blocks.empty())
+ return blockchain_height;
+ uint64_t last_needed_height = blockchain_height;
+ bool first = true;
+ for (const auto &span: blocks)
+ {
+ if (span.start_block_height + span.nblocks - 1 < blockchain_height)
+ continue;
+ if (span.start_block_height != last_needed_height || (first && span.blocks.empty()))
+ return last_needed_height;
+ last_needed_height = span.start_block_height + span.nblocks;
+ first = false;
+ }
+ return last_needed_height;
+}
+
void block_queue::print() const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
MDEBUG("Block queue has " << blocks.size() << " spans");
for (const auto &span: blocks)
- MDEBUG(" " << span.start_block_height << " - " << (span.start_block_height+span.nblocks-1) << " (" << span.nblocks << ") - " << (is_blockchain_placeholder(span) ? "blockchain" : span.blocks.empty() ? "scheduled" : "filled ") << " " << span.connection_id << " (" << ((unsigned)(span.rate*10/1024.f))/10.f << " kB/s)");
+ MDEBUG(" " << span.start_block_height << " - " << (span.start_block_height+span.nblocks-1) << " (" << span.nblocks << ") - " << (span.blocks.empty() ? "scheduled" : "filled ") << " " << span.connection_id << " (" << ((unsigned)(span.rate*10/1024.f))/10.f << " kB/s)");
}
-std::string block_queue::get_overview() const
+std::string block_queue::get_overview(uint64_t blockchain_height) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
if (blocks.empty())
return "[]";
block_map::const_iterator i = blocks.begin();
- std::string s = std::string("[") + std::to_string(i->start_block_height + i->nblocks - 1) + ":";
- while (++i != blocks.end())
- s += i->blocks.empty() ? "." : "o";
+ std::string s = std::string("[");
+ uint64_t expected = blockchain_height;
+ while (i != blocks.end())
+ {
+ if (expected > i->start_block_height)
+ {
+ s += "<";
+ }
+ else
+ {
+ if (expected < i->start_block_height)
+ s += std::string(std::max((uint64_t)1, (i->start_block_height - expected) / (i->nblocks ? i->nblocks : 1)), '_');
+ s += i->blocks.empty() ? "." : i->start_block_height == blockchain_height ? "m" : "o";
+ expected = i->start_block_height + i->nblocks;
+ }
+ ++i;
+ }
s += "]";
return s;
}
@@ -184,16 +222,31 @@ bool block_queue::requested(const crypto::hash &hash) const
return requested_internal(hash);
}
-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 std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time)
+bool block_queue::have(const crypto::hash &hash) const
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ 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, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ MDEBUG("reserve_span: first_block_height " << first_block_height << ", last_block_height " << last_block_height
+ << ", max " << max_blocks << ", seed " << epee::string_tools::to_string_hex(pruning_seed) << ", blockchain_height " <<
+ blockchain_height << ", block hashes size " << block_hashes.size());
if (last_block_height < first_block_height || max_blocks == 0)
{
MDEBUG("reserve_span: early out: first_block_height " << first_block_height << ", last_block_height " << last_block_height << ", max_blocks " << max_blocks);
return std::make_pair(0, 0);
}
+ if (block_hashes.size() >= last_block_height)
+ {
+ MDEBUG("reserve_span: more block hashes than fit within last_block_height: " << block_hashes.size() << " and " << last_block_height);
+ return std::make_pair(0, 0);
+ }
+ // skip everything we've already requested
uint64_t span_start_height = last_block_height - block_hashes.size() + 1;
std::vector<crypto::hash>::const_iterator i = block_hashes.begin();
while (i != block_hashes.end() && requested_internal(*i))
@@ -201,55 +254,57 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei
++i;
++span_start_height;
}
+
+ // if the peer's pruned for the starting block and its unpruned stripe comes next, start downloading from there
+ const uint32_t next_unpruned_height = tools::get_next_unpruned_block_height(span_start_height, blockchain_height, pruning_seed);
+ MDEBUG("reserve_span: next_unpruned_height " << next_unpruned_height << " from " << span_start_height << " and seed "
+ << epee::string_tools::to_string_hex(pruning_seed) << ", limit " << span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE);
+ if (next_unpruned_height > span_start_height && next_unpruned_height < span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE)
+ {
+ MDEBUG("We can download from next span: ideal height " << span_start_height << ", next unpruned height " << next_unpruned_height <<
+ "(+" << next_unpruned_height - span_start_height << "), current seed " << pruning_seed);
+ span_start_height = next_unpruned_height;
+ }
+ MDEBUG("span_start_height: " <<span_start_height);
+ const uint64_t block_hashes_start_height = last_block_height - block_hashes.size() + 1;
+ if (span_start_height >= block_hashes.size() + block_hashes_start_height)
+ {
+ MDEBUG("Out of hashes, cannot reserve");
+ return std::make_pair(0, 0);
+ }
+
+ i = block_hashes.begin() + span_start_height - block_hashes_start_height;
+ while (i != block_hashes.end() && requested_internal(*i))
+ {
+ ++i;
+ ++span_start_height;
+ }
+
uint64_t span_length = 0;
std::vector<crypto::hash> hashes;
- while (i != block_hashes.end() && span_length < max_blocks)
+ while (i != block_hashes.end() && span_length < max_blocks && tools::has_unpruned_block(span_start_height + span_length, blockchain_height, pruning_seed))
{
hashes.push_back(*i);
++i;
++span_length;
}
if (span_length == 0)
+ {
+ MDEBUG("span_length 0, cannot reserve");
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);
set_span_hashes(span_start_height, connection_id, hashes);
return std::make_pair(span_start_height, span_length);
}
-bool block_queue::is_blockchain_placeholder(const span &span) const
-{
- return span.connection_id == boost::uuids::nil_uuid();
-}
-
-std::pair<uint64_t, uint64_t> block_queue::get_start_gap_span() const
-{
- boost::unique_lock<boost::recursive_mutex> lock(mutex);
- if (blocks.empty())
- return std::make_pair(0, 0);
- block_map::const_iterator i = blocks.begin();
- if (!is_blockchain_placeholder(*i))
- return std::make_pair(0, 0);
- uint64_t current_height = i->start_block_height + i->nblocks - 1;
- ++i;
- if (i == blocks.end())
- return std::make_pair(0, 0);
- uint64_t first_span_height = i->start_block_height;
- if (first_span_height <= current_height + 1)
- return std::make_pair(0, 0);
- MDEBUG("Found gap at start of spans: last blockchain block height " << current_height << ", first span's block height " << first_span_height);
- print();
- return std::make_pair(current_height + 1, first_span_height - current_height - 1);
-}
-
std::pair<uint64_t, uint64_t> block_queue::get_next_span_if_scheduled(std::vector<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
if (blocks.empty())
return std::make_pair(0, 0);
block_map::const_iterator i = blocks.begin();
- if (is_blockchain_placeholder(*i))
- ++i;
if (i == blocks.end())
return std::make_pair(0, 0);
if (!i->blocks.empty())
@@ -260,6 +315,16 @@ std::pair<uint64_t, uint64_t> block_queue::get_next_span_if_scheduled(std::vecto
return std::make_pair(i->start_block_height, i->nblocks);
}
+void block_queue::reset_next_span_time(boost::posix_time::ptime t)
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ CHECK_AND_ASSERT_THROW_MES(!blocks.empty(), "No next span to reset time");
+ block_map::iterator i = blocks.begin();
+ CHECK_AND_ASSERT_THROW_MES(i != blocks.end(), "No next span to reset time");
+ CHECK_AND_ASSERT_THROW_MES(i->blocks.empty(), "Next span is not empty");
+ (boost::posix_time::ptime&)i->time = t; // sod off, time doesn't influence sorting
+}
+
void block_queue::set_span_hashes(uint64_t start_height, const boost::uuids::uuid &connection_id, std::vector<crypto::hash> hashes)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
@@ -284,8 +349,6 @@ bool block_queue::get_next_span(uint64_t &height, std::vector<cryptonote::block_
if (blocks.empty())
return false;
block_map::const_iterator i = blocks.begin();
- if (is_blockchain_placeholder(*i))
- ++i;
for (; i != blocks.end(); ++i)
{
if (!filled || !i->blocks.empty())
@@ -299,19 +362,34 @@ bool block_queue::get_next_span(uint64_t &height, std::vector<cryptonote::block_
return false;
}
-bool block_queue::has_next_span(const boost::uuids::uuid &connection_id, bool &filled) const
+bool block_queue::has_next_span(const boost::uuids::uuid &connection_id, bool &filled, boost::posix_time::ptime &time) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
if (blocks.empty())
return false;
block_map::const_iterator i = blocks.begin();
- if (is_blockchain_placeholder(*i))
- ++i;
if (i == blocks.end())
return false;
if (i->connection_id != connection_id)
return false;
filled = !i->blocks.empty();
+ time = i->time;
+ return true;
+}
+
+bool block_queue::has_next_span(uint64_t height, bool &filled, boost::posix_time::ptime &time, boost::uuids::uuid &connection_id) const
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ if (blocks.empty())
+ return false;
+ block_map::const_iterator i = blocks.begin();
+ if (i == blocks.end())
+ return false;
+ if (i->start_block_height > height)
+ return false;
+ filled = !i->blocks.empty();
+ time = i->time;
+ connection_id = i->connection_id;
return true;
}
@@ -331,8 +409,6 @@ size_t block_queue::get_num_filled_spans_prefix() const
if (blocks.empty())
return 0;
block_map::const_iterator i = blocks.begin();
- if (is_blockchain_placeholder(*i))
- ++i;
size_t size = 0;
while (i != blocks.end() && !i->blocks.empty())
{
@@ -417,12 +493,35 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const
return speed;
}
-bool block_queue::foreach(std::function<bool(const span&)> f, bool include_blockchain_placeholder) const
+float block_queue::get_download_rate(const boost::uuids::uuid &connection_id) const
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ float conn_rate = -1.f;
+ for (const auto &span: blocks)
+ {
+ if (span.blocks.empty())
+ continue;
+ if (span.connection_id != connection_id)
+ continue;
+ // note that the average below does not average over the whole set, but over the
+ // previous pseudo average and the latest rate: this gives much more importance
+ // to the latest measurements, which is fine here
+ if (conn_rate < 0.f)
+ conn_rate = span.rate;
+ else
+ conn_rate = (conn_rate + span.rate) / 2;
+ }
+
+ if (conn_rate < 0)
+ conn_rate = 0.0f;
+ MTRACE("Download rate for " << connection_id << ": " << conn_rate << " b/s");
+ return conn_rate;
+}
+
+bool block_queue::foreach(std::function<bool(const span&)> f) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
block_map::const_iterator i = blocks.begin();
- if (!include_blockchain_placeholder && i != blocks.end() && is_blockchain_placeholder(*i))
- ++i;
while (i != blocks.end())
if (!f(*i++))
return false;