diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 1a23a9ada..1ed715315 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1496,6 +1496,8 @@ public: */ virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const = 0; + virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const = 0; + /** * @brief is BlockchainDB in read-only mode? * diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index d392e3221..fbf7629c6 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3180,6 +3180,47 @@ std::map> BlockchainLMDB::get return histogram; } +bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(output_amounts); + + distribution.clear(); + const uint64_t db_height = height(); + if (from_height >= db_height) + return false; + distribution.resize(db_height - from_height, 0); + + bool fret = true; + MDB_val_set(k, amount); + MDB_val v; + MDB_cursor_op op = MDB_SET; + while (1) + { + int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op); + op = MDB_NEXT_DUP; + if (ret == MDB_NOTFOUND) + break; + if (ret) + throw0(DB_ERROR("Failed to enumerate outputs")); + const outkey *ok = (const outkey *)v.mv_data; + const uint64_t height = ok->data.height; + if (height >= from_height) + distribution[height - from_height]++; + else + base++; + if (to_height > 0 && height > to_height) + break; + } + + TXN_POSTFIX_RDONLY(); + + return true; +} + void BlockchainLMDB::check_hard_fork_info() { } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 9a20c0f1e..f1773bac8 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -293,6 +293,8 @@ public: */ std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const; + bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const; + private: void do_resize(uint64_t size_increase=0); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index ece8e5d0d..b4fd7a217 100755 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1963,7 +1963,7 @@ void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)); } //------------------------------------------------------------------ -bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const +bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const { // rct outputs don't exist before v3 if (amount == 0) @@ -1984,22 +1984,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, if (from_height > start_height) start_height = from_height; - distribution.clear(); - uint64_t db_height = m_db->height(); - if (start_height >= db_height) - return false; - distribution.resize(db_height - start_height, 0); - bool r = for_all_outputs(amount, [&](uint64_t height) { - CHECK_AND_ASSERT_MES(height >= real_start_height && height <= db_height, false, "Height not in expected range"); - if (height >= start_height) - distribution[height - start_height]++; - else - base++; - return true; - }); - if (!r) - return false; - return true; + return m_db->get_output_distribution(amount, start_height, to_height, distribution, base); } //------------------------------------------------------------------ // This function takes a list of block hashes from another node @@ -3944,7 +3929,7 @@ MWARNING("first_index: " << first_index << " - " << last_index); // add to the known hashes array if (!valid) { - MWARNING("invalid hash for blocks " << n * HASH_OF_HASHES_STEP << " - " << (n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP - 1)); + MDEBUG("invalid hash for blocks " << n * HASH_OF_HASHES_STEP << " - " << (n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP - 1)); break; } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 98f086417..cc0ed8181 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -527,12 +527,13 @@ namespace cryptonote * @brief gets per block distribution of outputs of a given amount * * @param amount the amount to get a ditribution for - * @param return-by-reference from_height the height before which we do not care about the data + * @param from_height the height before which we do not care about the data + * @param to_height the height after which we do not care about the data * @param return-by-reference start_height the height of the first rct output * @param return-by-reference distribution the start offset of the first rct output in this block (same as previous if none) * @param return-by-reference base how many outputs of that amount are before the stated distribution */ - bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const; + bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const; /** * @brief gets the global indices for outputs from a given transaction diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 8a96a528f..ac686ff7f 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1074,9 +1074,9 @@ namespace cryptonote return m_blockchain_storage.get_random_rct_outs(req, res); } //----------------------------------------------------------------------------------------------- - bool core::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const + bool core::get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const { - return m_blockchain_storage.get_output_distribution(amount, from_height, start_height, distribution, base); + return m_blockchain_storage.get_output_distribution(amount, from_height, to_height, start_height, distribution, base); } //----------------------------------------------------------------------------------------------- bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index ff056c2a2..f3b9dddc0 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -576,7 +576,7 @@ namespace cryptonote * * @brief get per block distribution of outputs of a given amount */ - bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const; + bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base) const; /** * @copydoc miner::pause diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 571b33c79..01c383cc4 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1632,10 +1632,10 @@ skip: } uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids); - if (n_use_blocks == 0) + if (n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size()) { - LOG_ERROR_CCONTEXT("Peer yielded no usable blocks, dropping connection"); - drop_connection(context, false, false); + LOG_ERROR_CCONTEXT("Most blocks are invalid, dropping connection"); + drop_connection(context, true, false); return 1; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index b099c63f5..efb5387a1 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -241,6 +241,7 @@ namespace nodetool bool check_connection_and_handshake_with_peer(const epee::net_utils::network_address& na, uint64_t last_seen_stamp); bool gray_peerlist_housekeeping(); + bool check_incoming_connections(); void kill() { ///< will be called e.g. from deinit() _info("Killing the net_node"); @@ -304,6 +305,7 @@ namespace nodetool epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval; epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; + epee::math_helper::once_a_time_seconds<900, false> m_incoming_connections_interval; std::string m_bind_ip; std::string m_port; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index b28d8b36d..2ed7ec891 100755 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1279,6 +1279,20 @@ namespace nodetool m_connections_maker_interval.do_call(boost::bind(&node_server::connections_maker, this)); m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server::gray_peerlist_housekeeping, this)); m_peerlist_store_interval.do_call(boost::bind(&node_server::store_config, this)); + m_incoming_connections_interval.do_call(boost::bind(&node_server::check_incoming_connections, this)); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::check_incoming_connections() + { + if (m_offline || m_hide_my_port) + return true; + if (get_incoming_connections_count() == 0) + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + } return true; } //----------------------------------------------------------------------------------- diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 8b8fdc9c7..3ec67e7e0 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -47,6 +47,7 @@ using namespace epee; #include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" #include "p2p/net_node.h" +#include "get_output_distribution_cache.h" #include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -2110,9 +2111,30 @@ namespace cryptonote continue; } + // this is a slow operation, so we have precomputed caches of common cases + bool found = false; + for (const auto &slot: get_output_distribution_cache) + { + if (slot.amount == amount && slot.from_height == req.from_height && slot.to_height == req.to_height) + { + res.distributions.push_back({amount, slot.start_height, slot.distribution, slot.base}); + found = true; + if (req.cumulative) + { + auto &distribution = res.distributions.back().distribution; + distribution[0] += slot.base; + for (size_t n = 1; n < distribution.size(); ++n) + distribution[n] += distribution[n-1]; + } + break; + } + } + if (found) + continue; + std::vector distribution; uint64_t start_height, base; - if (!m_core.get_output_distribution(amount, req.from_height, start_height, distribution, base)) + if (!m_core.get_output_distribution(amount, req.from_height, req.to_height, start_height, distribution, base)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Failed to get rct distribution"; diff --git a/src/rpc/get_output_distribution_cache.h b/src/rpc/get_output_distribution_cache.h new file mode 100644 index 000000000..055e3a1c0 --- /dev/null +++ b/src/rpc/get_output_distribution_cache.h @@ -0,0 +1,23 @@ +static const struct +{ + uint64_t amount; + uint64_t from_height; + uint64_t to_height; + uint64_t start_height; + uint64_t base; + std::vector distribution; +} +get_output_distribution_cache[] = +{ + { + 0, + { + }, + }, + { + 0, + { + } + } +}; + diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 507cec8cf..f60b18eef 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -113,6 +113,7 @@ public: virtual bool for_all_outputs(uint64_t amount, const std::function &f) const { return true; } virtual bool is_read_only() const { return false; } virtual std::map> get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map>(); } + virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector &distribution, uint64_t &base) const { return false; } virtual void add_txpool_tx(const transaction &tx, const txpool_tx_meta_t& details) {} virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& details) {}