aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_config.h4
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp2
-rw-r--r--src/cryptonote_core/cryptonote_core.h1
-rw-r--r--src/cryptonote_core/tx_pool.cpp45
-rw-r--r--src/cryptonote_core/tx_pool.h11
-rw-r--r--src/cryptonote_protocol/levin_notify.cpp27
-rw-r--r--src/cryptonote_protocol/levin_notify.h2
-rw-r--r--src/p2p/net_node.inl4
-rw-r--r--tests/unit_tests/levin.cpp3
9 files changed, 70 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/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index fef411a0c..bd138038d 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1725,7 +1725,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..0e4318256 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -1065,7 +1065,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/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/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index 00855ac26..bbec2ba9b 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -233,7 +233,7 @@ namespace levin
{
struct zone
{
- explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, bool is_public, bool pad_txs)
+ explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, epee::net_utils::zone zone, bool pad_txs)
: p2p(std::move(p2p)),
noise(std::move(noise_in)),
next_epoch(io_service),
@@ -243,7 +243,7 @@ namespace levin
channels(),
connection_count(0),
flush_callbacks(0),
- is_public(is_public),
+ nzone(zone),
pad_txs(pad_txs),
fluffing(false)
{
@@ -260,7 +260,7 @@ namespace levin
std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand`
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
std::uint32_t flush_callbacks; //!< Number of active fluff flush callbacks queued
- const bool is_public; //!< Zone is public ipv4/ipv6 connections
+ const epee::net_utils::zone nzone; //!< Zone is public ipv4/ipv6 connections, or i2p or tor
const bool pad_txs; //!< Pad txs to the next boundary for privacy
bool fluffing; //!< Zone is in Dandelion++ fluff epoch
};
@@ -297,7 +297,8 @@ namespace levin
if (!channel.connection.is_nil())
channel.queue.push_back(std::move(message_));
else if (destination_ == 0 && zone_->connection_count == 0)
- MWARNING("Unable to send transaction(s) over anonymity network - no available outbound connections");
+ MWARNING("Unable to send transaction(s) to " << epee::net_utils::zone_to_string(zone_->nzone) <<
+ " - no available outbound connections");
}
};
@@ -399,7 +400,7 @@ namespace levin
zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
{
// When i2p/tor, only fluff to outbound connections
- if (source != context.m_connection_id && (zone->is_public || !context.m_is_income))
+ if (source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income))
{
if (context.fluff_txs.empty())
context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
@@ -562,7 +563,7 @@ namespace levin
assert(zone_->strand.running_in_this_thread());
- if (zone_->is_public)
+ if (zone_->nzone == epee::net_utils::zone::public_)
MDEBUG("Starting new Dandelion++ epoch: " << (fluffing_ ? "fluff" : "stem"));
zone_->map = std::move(map_);
@@ -630,10 +631,12 @@ namespace levin
{
channel.active = nullptr;
channel.connection = boost::uuids::nil_uuid();
+ auto height = core_->get_target_blockchain_height();
- auto connections = get_out_connections(*zone_->p2p, core_->get_target_blockchain_height());
+ auto connections = get_out_connections(*zone_->p2p, height);
if (connections.empty())
- MWARNING("Lost all outbound connections to anonymity network - currently unable to send transaction(s)");
+ MWARNING("Unable to send transaction(s) to " << epee::net_utils::zone_to_string(zone_->nzone) <<
+ " - no suitable outbound connections at height " << height);
zone_->strand.post(update_channels{zone_, std::move(connections)});
}
@@ -676,15 +679,15 @@ namespace levin
};
} // anonymous
- notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, const bool is_public, const bool pad_txs, i_core_events& core)
- : zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), is_public, pad_txs))
+ notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, epee::net_utils::zone zone, const bool pad_txs, i_core_events& core)
+ : zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), zone, pad_txs))
, core_(std::addressof(core))
{
if (!zone_->p2p)
throw std::logic_error{"cryptonote::levin::notify cannot have nullptr p2p argument"};
const bool noise_enabled = !zone_->noise.empty();
- if (noise_enabled || is_public)
+ if (noise_enabled || zone == epee::net_utils::zone::public_)
{
const auto now = std::chrono::steady_clock::now();
const auto min_epoch = noise_enabled ? noise_min_epoch : dandelionpp_min_epoch;
@@ -806,7 +809,7 @@ namespace levin
case relay_method::stem:
case relay_method::forward:
case relay_method::local:
- if (zone_->is_public)
+ if (zone_->nzone == epee::net_utils::zone::public_)
{
// this will change a local/forward tx to stem or fluff ...
zone_->strand.dispatch(
diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h
index 43b61aead..abbf9d461 100644
--- a/src/cryptonote_protocol/levin_notify.h
+++ b/src/cryptonote_protocol/levin_notify.h
@@ -85,7 +85,7 @@ namespace levin
{}
//! Construct an instance with available notification `zones`.
- explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public, bool pad_txs, i_core_events& core);
+ explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, epee::net_utils::zone zone, bool pad_txs, i_core_events& core);
notify(const notify&) = delete;
notify(notify&&) = default;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index a7033f1fa..c71dfc2de 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -402,7 +402,7 @@ namespace nodetool
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
public_zone.m_notifier = cryptonote::levin::notify{
- public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true, pad_txs, m_payload_handler.get_core()
+ public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core()
};
if (command_line::has_arg(vm, arg_p2p_add_peer))
@@ -545,7 +545,7 @@ namespace nodetool
}
zone.m_notifier = cryptonote::levin::notify{
- zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false, pad_txs, m_payload_handler.get_core()
+ zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core()
};
}
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index 28032fa50..128d51fb3 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -329,7 +329,8 @@ namespace
epee::byte_slice noise = nullptr;
if (noise_size)
noise = epee::levin::make_noise_notify(noise_size);
- return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public, pad_txs, events_};
+ epee::net_utils::zone zone = is_public ? epee::net_utils::zone::public_ : epee::net_utils::zone::i2p;
+ return cryptonote::levin::notify{io_service_, connections_, std::move(noise), zone, pad_txs, events_};
}
boost::uuids::random_generator random_generator_;