diff options
Diffstat (limited to '')
-rw-r--r-- | src/cryptonote_config.h | 4 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 2 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 7 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 12 | ||||
-rw-r--r-- | src/cryptonote_core/i_core_events.h | 3 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 45 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.h | 11 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.h | 2 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 6 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler_common.h | 5 | ||||
-rw-r--r-- | src/cryptonote_protocol/levin_notify.cpp | 58 |
11 files changed, 126 insertions, 29 deletions
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index bc17b7e81..826cabe17 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -102,11 +102,11 @@ #define CRYPTONOTE_DANDELIONPP_STEMS 2 // number of outgoing stem connections per epoch -#define CRYPTONOTE_DANDELIONPP_FLUFF_PROBABILITY 10 // out of 100 +#define CRYPTONOTE_DANDELIONPP_FLUFF_PROBABILITY 20 // out of 100 #define CRYPTONOTE_DANDELIONPP_MIN_EPOCH 10 // minutes #define CRYPTONOTE_DANDELIONPP_EPOCH_RANGE 30 // seconds #define CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE 5 // seconds average for poisson distributed fluff flush -#define CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE 173 // seconds (see tx_pool.cpp for more info) +#define CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE 39 // seconds (see tx_pool.cpp for more info) // see src/cryptonote_protocol/levin_notify.cpp #define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d4b844c58..89ecda2cd 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -5376,7 +5376,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "1d166275537bfbb5841d98da298c65089f3006d751776b2dc8a4f0275ef0ce58"; +static const char expected_block_hashes_hash[] = "3a2a0720f7ed3573d2b632f7ba127e2f97edac3af34bf924df57c13a88bf2460"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index fef411a0c..42b1c6c84 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1510,6 +1510,11 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::is_synchronized() const + { + return m_pprotocol != nullptr && m_pprotocol->is_synchronized(); + } + //----------------------------------------------------------------------------------------------- void core::on_synchronized() { m_miner.on_synchronized(); @@ -1725,7 +1730,7 @@ namespace cryptonote m_starter_message_showed = true; } - m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); + relay_txpool_transactions(); // txpool handles periodic DB checking m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this)); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 7c578ac51..da7a21b27 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -329,7 +329,7 @@ namespace cryptonote * * @note see Blockchain::get_current_blockchain_height() */ - uint64_t get_current_blockchain_height() const; + virtual uint64_t get_current_blockchain_height() const final; /** * @brief get the hash and height of the most recent block @@ -638,6 +638,13 @@ namespace cryptonote std::string print_pool(bool short_format) const; /** + * @brief gets the core synchronization status + * + * @return core synchronization status + */ + virtual bool is_synchronized() const final; + + /** * @copydoc miner::on_synchronized * * @note see miner::on_synchronized @@ -663,7 +670,7 @@ namespace cryptonote * * @param target_blockchain_height the target height */ - virtual uint64_t get_target_blockchain_height() const override; + uint64_t get_target_blockchain_height() const; /** * @brief returns the newest hardfork version known to the blockchain @@ -1065,7 +1072,6 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status - epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h index addb659ab..5d00858b5 100644 --- a/src/cryptonote_core/i_core_events.h +++ b/src/cryptonote_core/i_core_events.h @@ -39,7 +39,8 @@ namespace cryptonote virtual ~i_core_events() noexcept {} - virtual uint64_t get_target_blockchain_height() const = 0; + virtual uint64_t get_current_blockchain_height() const = 0; + virtual bool is_synchronized() const = 0; virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, relay_method tx_relay) = 0; }; } diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 28721ee36..d059ab78f 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -75,11 +75,11 @@ namespace cryptonote not ideal since a blackhole is more likely to reveal earlier nodes in the chain. - This value was calculated with k=10, ep=0.10, and hop = 175 ms. A + This value was calculated with k=5, ep=0.10, and hop = 175 ms. A testrun from a recent Intel laptop took ~80ms to receive+parse+proces+send transaction. At least 50ms will be added to the latency if crossing an ocean. So 175ms is the fudge factor for - a single hop with 173s being the embargo timer. */ + a single hop with 39s being the embargo timer. */ constexpr const std::chrono::seconds dandelionpp_embargo_average{CRYPTONOTE_DANDELIONPP_EMBARGO_AVERAGE}; //TODO: constants such as these should at least be in the header, @@ -91,6 +91,9 @@ namespace cryptonote time_t const MAX_RELAY_TIME = (60 * 60 * 4); // at most that many seconds between resends float const ACCEPT_THRESHOLD = 1.0f; + //! Max DB check interval for relayable txes + constexpr const std::chrono::minutes max_relayable_check{2}; + constexpr const std::chrono::seconds forward_delay_average{CRYPTONOTE_FORWARD_DELAY_AVERAGE}; // a kind of increasing backoff within min/max bounds @@ -115,12 +118,21 @@ namespace cryptonote else return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } + + // external lock must be held for the comparison+set to work properly + void set_if_less(std::atomic<time_t>& next_check, const time_t candidate) noexcept + { + if (candidate < next_check.load(std::memory_order_relaxed)) + next_check = candidate; + } } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_cookie(0), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_mine_stem_txes(false) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_cookie(0), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_mine_stem_txes(false), m_next_check(std::time(nullptr)) { - + // class code expects unsigned values throughout + if (m_next_check < time_t(0)) + throw std::runtime_error{"Unexpected time_t (system clock) value"}; } //--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version) @@ -314,7 +326,10 @@ namespace cryptonote using clock = std::chrono::system_clock; auto last_relayed_time = std::numeric_limits<decltype(meta.last_relayed_time)>::max(); if (tx_relay == relay_method::forward) + { last_relayed_time = clock::to_time_t(clock::now() + crypto::random_poisson_seconds{forward_delay_average}()); + set_if_less(m_next_check, time_t(last_relayed_time)); + } // else the `set_relayed` function will adjust the time accordingly later //update transactions container @@ -728,16 +743,22 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> &txs) const + bool tx_memory_pool::get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> &txs) { - std::vector<std::pair<crypto::hash, txpool_tx_meta_t>> change_timestamps; + using clock = std::chrono::system_clock; + const uint64_t now = time(NULL); + if (uint64_t{std::numeric_limits<time_t>::max()} < now || time_t(now) < m_next_check) + return false; + + uint64_t next_check = clock::to_time_t(clock::from_time_t(time_t(now)) + max_relayable_check); + std::vector<std::pair<crypto::hash, txpool_tx_meta_t>> change_timestamps; CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); LockedTXN lock(m_blockchain.get_db()); txs.reserve(m_blockchain.get_txpool_tx_count()); - m_blockchain.for_all_txpool_txes([this, now, &txs, &change_timestamps](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *){ + m_blockchain.for_all_txpool_txes([this, now, &txs, &change_timestamps, &next_check](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref *){ // 0 fee transactions are never relayed if(!meta.pruned && meta.fee > 0 && !meta.do_not_relay) { @@ -747,7 +768,10 @@ namespace cryptonote case relay_method::stem: case relay_method::forward: if (meta.last_relayed_time > now) + { + next_check = std::min(next_check, meta.last_relayed_time); return true; // continue to next tx + } change_timestamps.emplace_back(txid, meta); break; default: @@ -792,6 +816,8 @@ namespace cryptonote elem.second.last_relayed_time = now + get_relay_delay(now, elem.second.receive_time); m_blockchain.update_txpool_tx(elem.first, elem.second); } + + m_next_check = time_t(next_check); return true; } //--------------------------------------------------------------------------------- @@ -799,6 +825,7 @@ namespace cryptonote { crypto::random_poisson_seconds embargo_duration{dandelionpp_embargo_average}; const auto now = std::chrono::system_clock::now(); + uint64_t next_relay = uint64_t{std::numeric_limits<time_t>::max()}; CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -815,7 +842,10 @@ namespace cryptonote meta.relayed = true; if (meta.dandelionpp_stem) + { meta.last_relayed_time = std::chrono::system_clock::to_time_t(now + embargo_duration()); + next_relay = std::min(next_relay, meta.last_relayed_time); + } else meta.last_relayed_time = std::chrono::system_clock::to_time_t(now); @@ -829,6 +859,7 @@ namespace cryptonote } } lock.commit(); + set_if_less(m_next_check, time_t(next_relay)); } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_transactions_count(bool include_sensitive) const diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 8955d7551..ab2a57ea2 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -31,6 +31,7 @@ #pragma once #include "include_base_utils.h" +#include <atomic> #include <set> #include <tuple> #include <unordered_map> @@ -329,11 +330,14 @@ namespace cryptonote * isn't old enough that relaying it is considered harmful * Note a transaction can be "relayable" even if do_not_relay is true * + * This function will skip all DB checks if an insufficient amount of + * time since the last call. + * * @param txs return-by-reference the transactions and their hashes * - * @return true + * @return True if DB was checked, false if DB checks skipped. */ - bool get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>>& txs) const; + bool get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>>& txs); /** * @brief tell the pool that certain transactions were just relayed @@ -609,6 +613,9 @@ private: mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache; std::unordered_map<crypto::hash, transaction> m_parsed_tx_cache; + + //! Next timestamp that a DB check for relayable txes is allowed + std::atomic<time_t> m_next_check; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 89860fe41..61aac6d81 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -104,7 +104,7 @@ namespace cryptonote bool get_payload_sync_data(CORE_SYNC_DATA& hshd); bool on_callback(cryptonote_connection_context& context); t_core& get_core(){return m_core;} - bool is_synchronized(){return m_synchronized;} + virtual bool is_synchronized() const final { return !no_sync() && m_synchronized; } void log_connections(); std::list<connection_info> get_connections(); const block_queue &get_block_queue() const { return m_block_queue; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 20bcd7f3b..a72b7db79 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -430,7 +430,7 @@ namespace cryptonote MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, "Received NOTIFY_NEW_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; - if(!is_synchronized() || m_no_sync) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks + if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks { LOG_DEBUG_CC(context, "Received new block while syncing, ignored"); return 1; @@ -501,7 +501,7 @@ namespace cryptonote MLOGIF_P2P_MESSAGE(crypto::hash hash; cryptonote::block b; bool ret = cryptonote::parse_and_validate_block_from_blob(arg.b.block, b, &hash);, ret, "Received NOTIFY_NEW_FLUFFY_BLOCK " << hash << " (height " << arg.current_blockchain_height << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; - if(!is_synchronized() || m_no_sync) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks + if(!is_synchronized()) // can happen if a peer connection goes to normal but another thread still hasn't finished adding queued blocks { LOG_DEBUG_CC(context, "Received new block while syncing, ignored"); return 1; @@ -929,7 +929,7 @@ namespace cryptonote // while syncing, core will lock for a long time, so we ignore // those txes as they aren't really needed anyway, and avoid a // long block before replying - if(!is_synchronized() || m_no_sync) + if(!is_synchronized()) { LOG_DEBUG_CC(context, "Received new tx while syncing, ignored"); return 1; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 1c7635fd8..79c2edf1d 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -40,6 +40,7 @@ namespace cryptonote /************************************************************************/ struct i_cryptonote_protocol { + virtual bool is_synchronized() const = 0; virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0; virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone, relay_method tx_relay)=0; //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; @@ -50,6 +51,10 @@ namespace cryptonote /************************************************************************/ struct cryptonote_protocol_stub: public i_cryptonote_protocol { + virtual bool is_synchronized() const final + { + return false; + } virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) { return false; diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index bbec2ba9b..69df22a92 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -105,8 +105,44 @@ namespace levin return std::chrono::steady_clock::duration{crypto::rand_range(rep(0), range.count())}; } - //! \return Outgoing connections supporting fragments in `connections` filtered by remote blockchain height. - std::vector<boost::uuids::uuid> get_out_connections(connections& p2p, uint64_t min_blockchain_height) + uint64_t get_median_remote_height(connections& p2p) + { + std::vector<uint64_t> remote_heights; + remote_heights.reserve(connection_id_reserve_size); + p2p.foreach_connection([&remote_heights] (detail::p2p_context& context) { + if (!context.m_is_income) + { + remote_heights.emplace_back(context.m_remote_blockchain_height); + } + return true; + }); + + if (remote_heights.empty()) + { + return 0; + } + + const size_t n = remote_heights.size() / 2; + std::sort(remote_heights.begin(), remote_heights.end()); + if (remote_heights.size() % 2 != 0) + { + return remote_heights[n]; + } + return remote_heights[n-1]; + } + + uint64_t get_blockchain_height(connections& p2p, const i_core_events* core) + { + const uint64_t local_blockchain_height = core->get_current_blockchain_height(); + if (core->is_synchronized()) + { + return local_blockchain_height; + } + return std::max(local_blockchain_height, get_median_remote_height(p2p)); + } + + //! \return Outgoing connections supporting fragments in `connections` filtered by blockchain height. + std::vector<boost::uuids::uuid> get_out_connections(connections& p2p, uint64_t blockchain_height) { std::vector<boost::uuids::uuid> outs; outs.reserve(connection_id_reserve_size); @@ -115,15 +151,21 @@ namespace levin the reserve call so a strand is not used. Investigate if there is lots of waiting in here. */ - p2p.foreach_connection([&outs, min_blockchain_height] (detail::p2p_context& context) { - if (!context.m_is_income && context.m_remote_blockchain_height >= min_blockchain_height) + p2p.foreach_connection([&outs, blockchain_height] (detail::p2p_context& context) { + if (!context.m_is_income && context.m_remote_blockchain_height >= blockchain_height) outs.emplace_back(context.m_connection_id); return true; }); + MDEBUG("Found " << outs.size() << " out connections having height >= " << blockchain_height); return outs; } + std::vector<boost::uuids::uuid> get_out_connections(connections& p2p, const i_core_events* core) + { + return get_out_connections(p2p, get_blockchain_height(p2p, core)); + } + std::string make_tx_payload(std::vector<blobdata>&& txs, const bool pad, const bool fluff) { NOTIFY_NEW_TRANSACTIONS::request request{}; @@ -527,7 +569,7 @@ namespace levin } // connection list may be outdated, try again - update_channels::run(zone_, get_out_connections(*zone_->p2p, core_->get_target_blockchain_height())); + update_channels::run(zone_, get_out_connections(*zone_->p2p, core_)); } MERROR("Unable to send transaction(s) via Dandelion++ stem"); @@ -631,7 +673,7 @@ namespace levin { channel.active = nullptr; channel.connection = boost::uuids::nil_uuid(); - auto height = core_->get_target_blockchain_height(); + auto height = get_blockchain_height(*zone_->p2p, core_); auto connections = get_out_connections(*zone_->p2p, height); if (connections.empty()) @@ -667,7 +709,7 @@ namespace levin const bool fluffing = crypto::rand_idx(unsigned(100)) < CRYPTONOTE_DANDELIONPP_FLUFF_PROBABILITY; const auto start = std::chrono::steady_clock::now(); - auto connections = get_out_connections(*(zone_->p2p), core_->get_target_blockchain_height()); + auto connections = get_out_connections(*(zone_->p2p), core_); zone_->strand.dispatch( change_channels{zone_, net::dandelionpp::connection_map{std::move(connections), count_}, fluffing} ); @@ -718,7 +760,7 @@ namespace levin return; zone_->strand.dispatch( - update_channels{zone_, get_out_connections(*(zone_->p2p), core_->get_target_blockchain_height())} + update_channels{zone_, get_out_connections(*(zone_->p2p), core_)} ); } |