What changed, and why it matters
This Monero update improves how nodes download and queue blocks during blockchain synchronization. It changes the way pruned block sizes are counted, adds a check that pruned block weights match known values, and switches the download queue limit from a fixed number of spans to a dynamic number of blocks based on download speed. The changes are defensive: they reduce the chance that a misbehaving peer can feed incorrect block-size data that would make a node request too much or too little data, and they fix arithmetic overflow/underflow risks in the sync-size calculations. There is no direct evidence in the commit that these flaws were exploited or that the project calls this a security fix.
Treat as a hardening/sync-correctness patch. Deploy in the normal release cycle. Node operators should upgrade to benefit from more robust synchronization limits and pruned-block validation. No emergency response is indicated by the diff alone, but downstream teams may want to monitor for related disclosure or CVE assignment.
Security signals we found
Replaced simple zero-weight check with cryptographic-weight validation against prevalidated chain data
Added overflow/underflow guards in sync-size arithmetic (division instead of multiplication, NaN/inf checks)
Changed queue limit from span-based to block-count-based with dynamic recalculation
Pruned block weights now included in adaptive sync sizing, closing a sizing inconsistency
No CVE, advisory, or vendor security disclosure present in the commit or supplied references
Evidence from the diff
The merge refactors the block-queue sizing logic. Key changes: (1) block_queue spans now carry a sync_size field that includes prevalidated pruned-block weights, so adaptive sync sizing accounts for pruned data; (2) a new Blockchain::check_block_weights() verifies pruned block weights against m_blocks_hash_check under one lock, replacing a simple zero-weight check; (3) calculate_dynamic_span is renamed calculate_block_queue_limit and now computes a block count limit from blocks_per_second * queue_time_minutes, with NaN/inf and overflow guards; (4) get_block_sync_size avoids a multiplication overflow by using (batch_max_weight - 1) / BLOCKS_MAX_WINDOW; (5) queue-proceed logic now checks both span count and filled block count against the dynamic limit. Unit tests cover the new queue counters.
Changed components
src/cryptonote_core/blockchain.cppsrc/cryptonote_core/blockchain.hsrc/cryptonote_core/cryptonote_core.cppsrc/cryptonote_core/cryptonote_core.hsrc/cryptonote_protocol/block_queue.cppsrc/cryptonote_protocol/block_queue.hsrc/cryptonote_protocol/cryptonote_protocol_handler.hsrc/cryptonote_protocol/cryptonote_protocol_handler.inltests/unit_tests/block_queue.cpptests/unit_tests/node_server.cppInspect captured patch +172 / −57
### src/cryptonote_core/blockchain.cpp
@@ -4877,6 +4877,22 @@ bool Blockchain::has_block_weights(uint64_t height, uint64_t nblocks) const
return true;
}
+bool Blockchain::check_block_weights(uint64_t height, const std::vector<block_complete_entry> &blocks) const
+{
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ const uint64_t available = height < m_blocks_hash_check.size() ? m_blocks_hash_check.size() - height : 0;
+ for (size_t i = 0; i < blocks.size(); ++i)
+ {
+ const block_complete_entry &entry = blocks[i];
+ if (!entry.pruned)
+ continue;
+ // Check the remaining range before adding to height.
+ if (i >= available || entry.block_weight == 0 || entry.block_weight != m_blocks_hash_check[height + i].second)
+ return false;
+ }
+ return true;
+}
+
//------------------------------------------------------------------
// ND: Speedups:
// 1. Thread long_hash computations if possible (m_max_prepare_blocks_threads = nthreads, default = 4)
### src/cryptonote_core/blockchain.h
@@ -1138,6 +1138,14 @@ namespace cryptonote
*/
bool has_block_weights(uint64_t height, uint64_t nblocks) const;
+ /**
+ * @brief checks pruned block weights against prevalidated chain data under one lock
+ * @param height the height of the first block
+ * @param blocks consecutive blocks; full blocks are ignored
+ * @return false if a pruned block has a zero, unavailable, or mismatched weight
+ */
+ bool check_block_weights(uint64_t height, const std::vector<block_complete_entry> &blocks) const;
+
/**
* @brief flush the invalid blocks set
*/
### src/cryptonote_core/cryptonote_core.cpp
@@ -124,9 +124,9 @@ namespace cryptonote
, "Set maximum size of block download queue in bytes (0 for default)"
, 0
};
- const command_line::arg_descriptor<size_t> arg_span_limit = {
- "span-limit"
- , "Defines how many minutes of block synchronization data to request at a time (default is 2 minutes)"
+ const command_line::arg_descriptor<size_t> arg_block_sync_queue_time = {
+ "block-sync-queue-time"
+ , "Target duration of block synchronization data to keep queued, in minutes (default is 2 minutes)"
, 2
};
const command_line::arg_descriptor<bool> arg_sync_pruned_blocks = {
@@ -326,7 +326,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_offline);
command_line::add_arg(desc, arg_disable_dns_checkpoints);
command_line::add_arg(desc, arg_block_download_max_size);
- command_line::add_arg(desc, arg_span_limit);
+ command_line::add_arg(desc, arg_block_sync_queue_time);
command_line::add_arg(desc, arg_sync_pruned_blocks);
command_line::add_arg(desc, arg_max_txpool_weight);
command_line::add_arg(desc, arg_block_notify);
@@ -941,7 +941,8 @@ namespace cryptonote
<< " bytes and the max average blocksize in the queue is " << max_average_of_blocksize_in_queue << " bytes");
uint64_t projected_blocksize = std::max(max_average_of_blocksize_in_queue, max_weight);
uint64_t blocks_huge_threshold = (batch_max_weight / 2);
- if ((projected_blocksize * BLOCKS_MAX_WINDOW) < batch_max_weight)
+ // batch_max_weight is positive; compare without overflowing the projected batch weight.
+ if (projected_blocksize <= (batch_max_weight - 1) / BLOCKS_MAX_WINDOW)
{
res = BLOCKS_MAX_WINDOW;
MINFO("blocks are tiny, " << projected_blocksize << " bytes, sync " << res << " blocks in next batch");
@@ -1905,6 +1906,11 @@ namespace cryptonote
return get_blockchain_storage().has_block_weights(height, nblocks);
}
//-----------------------------------------------------------------------------------------------
+ bool core::check_block_weights(uint64_t height, const std::vector<block_complete_entry> &blocks) const
+ {
+ return get_blockchain_storage().check_block_weights(height, blocks);
+ }
+ //-----------------------------------------------------------------------------------------------
std::time_t core::get_start_time() const
{
return start_time;
### src/cryptonote_core/cryptonote_core.h
@@ -69,7 +69,7 @@ namespace cryptonote
extern const command_line::arg_descriptor<difficulty_type> arg_fixed_difficulty;
extern const command_line::arg_descriptor<bool> arg_offline;
extern const command_line::arg_descriptor<size_t> arg_block_download_max_size;
- extern const command_line::arg_descriptor<size_t> arg_span_limit;
+ extern const command_line::arg_descriptor<size_t> arg_block_sync_queue_time;
extern const command_line::arg_descriptor<bool> arg_sync_pruned_blocks;
/************************************************************************/
@@ -765,6 +765,7 @@ namespace cryptonote
* @return the number of blocks to sync in one go
*/
size_t get_block_sync_size(uint64_t height, const uint64_t max_average_of_blocksize_in_queue = 0) const;
+ bool is_block_sync_size_adaptive() const { return block_sync_size == 0; }
/**
* @brief get the sum of coinbase tx amounts between blocks
@@ -853,6 +854,14 @@ namespace cryptonote
*/
bool has_block_weights(uint64_t height, uint64_t nblocks) const;
+ /**
+ * @brief checks pruned block weights against prevalidated chain data
+ * @param height the height of the first block
+ * @param blocks consecutive blocks; full blocks are ignored
+ * @return false if a pruned block has a zero, unavailable, or mismatched weight
+ */
+ bool check_block_weights(uint64_t height, const std::vector<block_complete_entry> &blocks) const;
+
/**
* @brief flushes the invalid block cache
*/
### src/cryptonote_protocol/block_queue.cpp
@@ -45,12 +45,12 @@
namespace cryptonote
{
-void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size)
+void block_queue::add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size, uint64_t sync_size)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
std::vector<crypto::hash> hashes;
bool has_hashes = remove_span(height, &hashes);
- blocks.insert(span(height, std::move(bcel), connection_id, addr, rate, size));
+ blocks.insert(span(height, std::move(bcel), connection_id, addr, rate, size, sync_size));
if (has_hashes)
{
for (std::size_t i = 0; i < hashes.size(); ++i)
@@ -439,6 +439,32 @@ size_t block_queue::get_num_filled_spans() const
return size;
}
+uint64_t block_queue::get_num_filled_blocks() const
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ uint64_t size = 0;
+ for (const auto &span: blocks)
+ {
+ if (!span.blocks.empty())
+ size += span.nblocks;
+ }
+ return size;
+}
+
+uint64_t block_queue::get_max_block_size_average() const
+{
+ boost::unique_lock<boost::recursive_mutex> lock(mutex);
+ uint64_t max_average = 0;
+ for (const auto &span: blocks)
+ {
+ if (span.blocks.empty())
+ continue;
+
+ max_average = std::max(max_average, span.sync_size / span.nblocks);
+ }
+ return max_average;
+}
+
float block_queue::get_speed(const boost::uuids::uuid &connection_id) const
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
### src/cryptonote_protocol/block_queue.h
@@ -58,20 +58,21 @@ namespace cryptonote
uint64_t nblocks;
float rate;
size_t size;
+ uint64_t sync_size; // includes prevalidated weights for pruned blocks
boost::posix_time::ptime time;
epee::net_utils::network_address origin{};
- span(uint64_t start_block_height, std::vector<cryptonote::block_complete_entry> blocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size):
- start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), time(boost::date_time::min_date_time), origin(addr) {}
+ span(uint64_t start_block_height, std::vector<cryptonote::block_complete_entry> blocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size, uint64_t sync_size):
+ start_block_height(start_block_height), blocks(std::move(blocks)), connection_id(connection_id), nblocks(this->blocks.size()), rate(rate), size(size), sync_size(sync_size), time(boost::date_time::min_date_time), origin(addr) {}
span(uint64_t start_block_height, uint64_t nblocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, boost::posix_time::ptime time):
- start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), time(time), origin(addr) {}
+ start_block_height(start_block_height), connection_id(connection_id), nblocks(nblocks), rate(0.0f), size(0), sync_size(0), time(time), origin(addr) {}
bool operator<(const span &s) const { return start_block_height < s.start_block_height; }
};
typedef std::set<span> block_map;
public:
- void add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size);
+ void add_blocks(uint64_t height, std::vector<cryptonote::block_complete_entry> bcel, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, float rate, size_t size, uint64_t sync_size);
bool add_blocks(uint64_t height, uint64_t nblocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, boost::posix_time::ptime time = boost::date_time::min_date_time);
void flush_spans(const boost::uuids::uuid &connection_id, bool all = false);
void flush_stale_spans(const std::set<boost::uuids::uuid> &live_connections);
@@ -89,6 +90,8 @@ namespace cryptonote
bool has_next_span(uint64_t height, bool &filled, boost::posix_time::ptime &time, boost::uuids::uuid &connection_id) const;
size_t get_data_size() const;
size_t get_num_filled_spans() const;
+ uint64_t get_num_filled_blocks() const;
+ uint64_t get_max_block_size_average() const;
float get_speed(const boost::uuids::uuid &connection_id) const;
bool foreach(std::function<bool(const span&)> f) const;
bool requested(const crypto::hash &hash) const;
### src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -114,14 +114,9 @@ namespace cryptonote
const block_queue &get_block_queue() const { return m_block_queue; }
std::uint64_t max_average_of_blocksize_in_queue()
{
- std::vector<std::uint64_t> average_blocksize{0};
- m_block_queue.foreach([&](const cryptonote::block_queue::span &span)
- {
- average_blocksize.push_back(span.size / span.nblocks);
- return true; // we don't care about the return value
- });
- MINFO("Maximum average of blocksize for current batches : " << *std::max_element(average_blocksize.begin(), average_blocksize.end()));
- return *std::max_element(average_blocksize.begin(), average_blocksize.end());
+ const uint64_t max_average = m_block_queue.get_max_block_size_average();
+ MINFO("Maximum average of blocksize for current batches : " << max_average);
+ return max_average;
}
void stop();
void on_connection_close(cryptonote_connection_context &context);
@@ -178,7 +173,7 @@ namespace cryptonote
size_t skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const;
bool request_txpool_complement(cryptonote_connection_context &context);
void hit_score(cryptonote_connection_context &context, int32_t score);
- void calculate_dynamic_span(const double blocks_per_seconds);
+ void calculate_block_queue_limit(double blocks_per_second);
t_core& m_core;
@@ -204,9 +199,8 @@ namespace cryptonote
uint64_t m_sync_download_chain_size, m_sync_download_objects_size;
size_t m_block_download_max_size;
bool m_sync_pruned_blocks;
- size_t m_span_time;
- std::atomic<size_t> m_span_limit;
- std::atomic<size_t> m_bss;
+ size_t m_block_sync_queue_time;
+ std::atomic<uint64_t> m_block_queue_limit;
// Values for sync time estimates
boost::posix_time::ptime m_sync_start_time;
### src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -38,7 +38,9 @@
#include <boost/optional/optional.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
+#include <cmath>
#include <list>
+#include <limits>
#include <ctime>
#include <cryptonote_core/cryptonote_core.h>
@@ -182,9 +184,8 @@ namespace cryptonote
m_ask_for_txpool_complement(true),
m_stopping(false),
m_no_sync(false),
- m_span_limit(BLOCK_QUEUE_NSPANS_MINIMUM),
- m_span_time(0),
- m_bss(0)
+ m_block_sync_queue_time(0),
+ m_block_queue_limit(0)
{
if(!m_p2p)
@@ -207,22 +208,29 @@ namespace cryptonote
m_block_download_max_size = command_line::get_arg(vm, cryptonote::arg_block_download_max_size);
m_sync_pruned_blocks = command_line::get_arg(vm, cryptonote::arg_sync_pruned_blocks);
- m_span_time = command_line::get_arg(vm, cryptonote::arg_span_limit);
+ m_block_sync_queue_time = command_line::get_arg(vm, cryptonote::arg_block_sync_queue_time);
return true;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
- void t_cryptonote_protocol_handler<t_core>::calculate_dynamic_span(const double blocks_per_seconds)
+ void t_cryptonote_protocol_handler<t_core>::calculate_block_queue_limit(double blocks_per_second)
{
- size_t current_bss = m_bss.load();
- size_t current_span_limit = m_span_limit.load();
- MINFO("m_bss : " << current_bss << ", blocks_per_seconds : " << blocks_per_seconds << ", current_span_limit : " << current_span_limit);
- current_span_limit = (current_bss && blocks_per_seconds) ? (( blocks_per_seconds * 60 * m_span_time ) / current_bss) : BLOCK_QUEUE_NSPANS_MINIMUM;
- if (current_span_limit < BLOCK_QUEUE_NSPANS_MINIMUM)
- current_span_limit = BLOCK_QUEUE_NSPANS_MINIMUM;
- m_span_limit = current_span_limit;
- MINFO("calculated dynamic span limit is span_limit : " << m_span_limit);
+ if (!(blocks_per_second > 0.0) || !std::isfinite(blocks_per_second))
+ {
+ MWARNING("Not updating block queue limit from invalid sync rate " << blocks_per_second);
+ return;
+ }
+
+ // Multiply the duration first so a zero target stays zero even for very large rates.
+ const long double requested_blocks = blocks_per_second * (60.0L * m_block_sync_queue_time);
+ const long double max_block_queue_limit = static_cast<long double>(std::numeric_limits<uint64_t>::max());
+ const uint64_t block_queue_limit = requested_blocks >= max_block_queue_limit
+ ? std::numeric_limits<uint64_t>::max()
+ : static_cast<uint64_t>(requested_blocks);
+ m_block_queue_limit = block_queue_limit;
+ MINFO("Calculated dynamic block queue limit: " << block_queue_limit << " blocks at "
+ << blocks_per_second << " blocks per second");
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@@ -1079,10 +1087,15 @@ namespace cryptonote
// calculate size of request
size_t size = 0;
size_t blocks_size = 0;
+ uint64_t blocks_sync_size = 0;
for (const auto &element : arg.blocks) {
- blocks_size += element.block.size();
+ uint64_t block_size = element.block.size();
for (const auto &tx : element.txs)
- blocks_size += tx.blob.size();
+ block_size += tx.blob.size();
+ blocks_size += block_size;
+ if (element.pruned)
+ block_size = std::max(block_size, element.block_weight);
+ blocks_sync_size += std::min(block_size, std::numeric_limits<uint64_t>::max() - blocks_sync_size);
}
size += blocks_size;
@@ -1265,19 +1278,13 @@ namespace cryptonote
}
}
}
- else
+ else if (!m_core.check_block_weights(start_height, arg.blocks))
{
- // we accept pruned data, check that if we got some, then no weights are zero
- for (block_complete_entry& block_entry: arg.blocks)
- {
- if (block_entry.block_weight == 0 && block_entry.pruned)
- {
- MERROR(context << "returned at least one pruned block with 0 weight, dropping connection");
- drop_connection(context, false, false);
- ++m_sync_bad_spans_downloaded;
- return LEVIN_ERROR_CONNECTION;
- }
- }
+ // Weights used for adaptive sync sizing must match the prevalidated chain data.
+ MERROR(context << "returned an incorrect weight for a pruned block in span starting at height " << start_height << ", dropping connection");
+ drop_connection(context, false, false);
+ ++m_sync_bad_spans_downloaded;
+ return LEVIN_ERROR_CONNECTION;
}
{
@@ -1289,7 +1296,7 @@ namespace cryptonote
const boost::posix_time::time_duration dt = now - request_time;
const float rate = size * 1e6 / (dt.total_microseconds() + 1);
MDEBUG(context << " adding span: " << arg.blocks.size() << " at height " << start_height << ", " << dt.total_microseconds()/1e6 << " seconds, " << (rate/1024) << " kB/s, size now " << (m_block_queue.get_data_size() + blocks_size) / 1048576.f << " MB");
- m_block_queue.add_blocks(start_height, std::move(arg.blocks), context.m_connection_id, context.m_remote_address, rate, blocks_size);
+ m_block_queue.add_blocks(start_height, std::move(arg.blocks), context.m_connection_id, context.m_remote_address, rate, blocks_size, blocks_sync_size);
const crypto::hash last_block_hash = cryptonote::get_block_hash(b);
context.m_last_known_hash = last_block_hash;
@@ -1607,8 +1614,11 @@ namespace cryptonote
{
const uint64_t target_blockchain_height = m_core.get_target_blockchain_height();
const boost::posix_time::time_duration dt = boost::posix_time::microsec_clock::universal_time() - start;
- const double blocks_per_seconds = (((current_blockchain_height - previous_height) * 1e6) / dt.total_microseconds());
- calculate_dynamic_span(blocks_per_seconds);
+ const int64_t elapsed_us = dt.total_microseconds();
+ const double blocks_per_seconds = elapsed_us > 0
+ ? ((current_blockchain_height - previous_height) * 1e6) / elapsed_us
+ : 0.0;
+ calculate_block_queue_limit(blocks_per_seconds);
std::string progress_message = "";
if (current_blockchain_height < target_blockchain_height)
{
@@ -2055,14 +2065,16 @@ skip:
boost::unique_lock<boost::mutex> check_span_lock{m_check_span_queue_mutex};
const size_t nspans = m_block_queue.get_num_filled_spans();
+ const uint64_t nblocks = m_block_queue.get_num_filled_blocks();
const size_t size = m_block_queue.get_data_size();
const uint64_t bc_height = m_core.get_current_blockchain_height();
const auto next_needed_pruning_stripe = get_next_needed_pruning_stripe();
const uint32_t add_stripe = tools::get_pruning_stripe(bc_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES);
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());
const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD;
- const bool queue_proceed_init = (nspans < m_span_limit.load()) && (size < block_queue_size_threshold);
+ const bool queue_proceed_init = (nspans < BLOCK_QUEUE_NSPANS_MINIMUM || nblocks < m_block_queue_limit.load()) &&
+ size < block_queue_size_threshold;
// get rid of blocks we already requested, or already have
if (skip_unneeded_hashes(context, true) && context.m_needed_objects.empty() && context.m_num_requested == 0)
{
@@ -2108,7 +2120,8 @@ skip:
<< ", stripe_proceed_secondary : " << stripe_proceed_secondary
<< ", next_height_proceed : " << next_height_proceed
<< ", next_block_height/next_needed_height/bc_height : " << next_block_height << "/" << next_needed_height << "/" << bc_height
- << ", nspans/span_limit : " << nspans << "/" << m_span_limit
+ << ", nspans/minimum : " << nspans << "/" << BLOCK_QUEUE_NSPANS_MINIMUM
+ << ", nblocks/block_queue_limit : " << nblocks << "/" << m_block_queue_limit
<< ", queue size/size_limit : " << size << "/" << block_queue_size_threshold);
// if we're waiting for next span, try to get it before unblocking threads below,
@@ -2192,7 +2205,8 @@ skip:
NOTIFY_REQUEST_GET_OBJECTS::request req;
bool is_next = false;
size_t count = 0;
- size_t l_m_bss = m_bss = m_core.get_block_sync_size(m_core.get_current_blockchain_height(), max_average_of_blocksize_in_queue());
+ const uint64_t max_average = m_core.is_block_sync_size_adaptive() ? max_average_of_blocksize_in_queue() : 0;
+ const size_t l_m_bss = m_core.get_block_sync_size(m_core.get_current_blockchain_height(), max_average);
std::pair<uint64_t, uint64_t> span = std::make_pair(0, 0);
if (force_next_span)
{
### tests/unit_tests/block_queue.cpp
@@ -164,3 +164,40 @@ TEST(block_queue, reserve_span_skips_requested_prefix)
ASSERT_TRUE(bq.requested(hashes[2].first));
ASSERT_TRUE(bq.requested(hashes[3].first));
}
+
+TEST(block_queue, count_filled_blocks)
+{
+ cryptonote::block_queue bq;
+ epee::net_utils::network_address na;
+
+ bq.add_blocks(0, std::vector<cryptonote::block_complete_entry>(3), uuid1(), na, 0.0f, 0, 0);
+ bq.add_blocks(3, 2, uuid2(), na);
+ bq.add_blocks(5, std::vector<cryptonote::block_complete_entry>(4), uuid2(), na, 0.0f, 0, 0);
+
+ ASSERT_EQ(bq.get_num_filled_spans(), 2);
+ ASSERT_EQ(bq.get_num_filled_blocks(), 7);
+}
+
+TEST(block_queue, cached_sync_size_tracks_span_changes)
+{
+ cryptonote::block_queue bq;
+ epee::net_utils::network_address na;
+
+ bq.add_blocks(0, 2, uuid1(), na);
+ ASSERT_EQ(bq.get_max_block_size_average(), 0);
+ bq.add_blocks(0, std::vector<cryptonote::block_complete_entry>(2), uuid1(), na, 0.0f, 100, 1000);
+ bq.set_span_hashes(0, uuid1(), std::vector<crypto::hash>(2));
+ bq.add_blocks(2, std::vector<cryptonote::block_complete_entry>(1), uuid2(), na, 0.0f, 200, 900);
+ ASSERT_EQ(bq.get_max_block_size_average(), 900);
+ ASSERT_EQ(bq.get_data_size(), 300);
+
+ bq.add_blocks(2, std::vector<cryptonote::block_complete_entry>(1), uuid2(), na, 0.0f, 150, 300);
+ ASSERT_EQ(bq.get_max_block_size_average(), 500);
+ ASSERT_EQ(bq.get_data_size(), 250);
+
+ bq.flush_spans(uuid1(), true);
+ ASSERT_EQ(bq.get_max_block_size_average(), 300);
+ ASSERT_TRUE(bq.remove_span(2));
+ ASSERT_EQ(bq.get_max_block_size_average(), 0);
+ ASSERT_EQ(bq.get_data_size(), 0);
+}
### tests/unit_tests/node_server.cpp
@@ -93,6 +93,7 @@ class test_core : public cryptonote::i_core_events
bool update_checkpoints(const bool skip_dns = false) { return true; }
uint64_t get_target_blockchain_height() const { return 1; }
size_t get_block_sync_size(uint64_t height, const uint64_t max_average_of_blocksize_in_queue = 0) const { return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; }
+ bool is_block_sync_size_adaptive() const { return false; }
virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, cryptonote::relay_method tx_relay) {}
cryptonote::network_type get_nettype() const { return cryptonote::MAINNET; }
bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx_blob, cryptonote::relay_category tx_category) const { return false; }
@@ -112,6 +113,7 @@ class test_core : public cryptonote::i_core_events
bool prune_blockchain(uint32_t pruning_seed = 0) { return true; }
bool is_within_compiled_block_hash_area(uint64_t height) const { return false; }
bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; }
+ bool check_block_weights(uint64_t height, const std::vector<cryptonote::block_complete_entry> &blocks) const { return false; }
bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) { return false; }
bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const { return false; }
crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; }Why this scored 46/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.