aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_protocol/block_queue.cpp
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2018-04-29 23:30:51 +0100
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-01-22 20:30:51 +0000
commitb750fb27b0f20e9443827732b69a504a76036430 (patch)
tree36fdd03f84ae78dbd217b5bd255bd86e33db1bcc /src/cryptonote_protocol/block_queue.cpp
parentMerge pull request #5008 (diff)
downloadmonero-b750fb27b0f20e9443827732b69a504a76036430.tar.xz
Pruning
The blockchain prunes seven eighths of prunable tx data. This saves about two thirds of the blockchain size, while keeping the node useful as a sync source for an eighth of the blockchain. No other data is currently pruned. There are three ways to prune a blockchain: - run monerod with --prune-blockchain - run "prune_blockchain" in the monerod console - run the monero-blockchain-prune utility The first two will prune in place. Due to how LMDB works, this will not reduce the blockchain size on disk. Instead, it will mark parts of the file as free, so that future data will use that free space, causing the file to not grow until free space grows scarce. The third way will create a second database, a pruned copy of the original one. Since this is a new file, this one will be smaller than the original one. Once the database is pruned, it will stay pruned as it syncs. That is, there is no need to use --prune-blockchain again, etc.
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;