mirror of
https://github.com/monero-project/monero.git
synced 2024-11-17 16:27:39 +00:00
Change to Tx diffusion (Dandelion++ fluff) instead of flooding
This commit is contained in:
parent
b4e1dc83d2
commit
70c9cd3c9c
15 changed files with 280 additions and 97 deletions
|
@ -67,7 +67,7 @@ bool matches_category(relay_method method, relay_category category) noexcept
|
||||||
case relay_method::local:
|
case relay_method::local:
|
||||||
return false;
|
return false;
|
||||||
case relay_method::block:
|
case relay_method::block:
|
||||||
case relay_method::flood:
|
case relay_method::fluff:
|
||||||
return true;
|
return true;
|
||||||
case relay_method::none:
|
case relay_method::none:
|
||||||
break;
|
break;
|
||||||
|
@ -90,7 +90,7 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept
|
||||||
is_local = 1;
|
is_local = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case relay_method::flood:
|
case relay_method::fluff:
|
||||||
break;
|
break;
|
||||||
case relay_method::block:
|
case relay_method::block:
|
||||||
kept_by_block = 1;
|
kept_by_block = 1;
|
||||||
|
@ -106,7 +106,7 @@ relay_method txpool_tx_meta_t::get_relay_method() const noexcept
|
||||||
return relay_method::none;
|
return relay_method::none;
|
||||||
if (is_local)
|
if (is_local)
|
||||||
return relay_method::local;
|
return relay_method::local;
|
||||||
return relay_method::flood;
|
return relay_method::fluff;
|
||||||
}
|
}
|
||||||
|
|
||||||
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
|
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
|
||||||
|
|
|
@ -108,7 +108,7 @@ extern const command_line::arg_descriptor<bool, false> arg_db_salvage;
|
||||||
|
|
||||||
enum class relay_category : uint8_t
|
enum class relay_category : uint8_t
|
||||||
{
|
{
|
||||||
broadcasted = 0,//!< Public txes received via block/flooding/fluff
|
broadcasted = 0,//!< Public txes received via block/fluff
|
||||||
relayable, //!< Every tx not marked `relay_method::none`
|
relayable, //!< Every tx not marked `relay_method::none`
|
||||||
legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons
|
legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons
|
||||||
all //!< Everything in the db
|
all //!< Everything in the db
|
||||||
|
|
|
@ -101,6 +101,9 @@
|
||||||
#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days
|
#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days
|
||||||
#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week
|
#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week
|
||||||
|
|
||||||
|
|
||||||
|
#define CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE 5 // seconds
|
||||||
|
|
||||||
// see src/cryptonote_protocol/levin_notify.cpp
|
// see src/cryptonote_protocol/levin_notify.cpp
|
||||||
#define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes
|
#define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes
|
||||||
#define CRYPTONOTE_NOISE_EPOCH_RANGE 30 // seconds
|
#define CRYPTONOTE_NOISE_EPOCH_RANGE 30 // seconds
|
||||||
|
|
|
@ -174,11 +174,6 @@ namespace cryptonote
|
||||||
, "Relay blocks as normal blocks"
|
, "Relay blocks as normal blocks"
|
||||||
, false
|
, false
|
||||||
};
|
};
|
||||||
static const command_line::arg_descriptor<bool> arg_pad_transactions = {
|
|
||||||
"pad-transactions"
|
|
||||||
, "Pad relayed transactions to help defend against traffic volume analysis"
|
|
||||||
, false
|
|
||||||
};
|
|
||||||
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
|
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
|
||||||
"max-txpool-weight"
|
"max-txpool-weight"
|
||||||
, "Set maximum txpool weight in bytes."
|
, "Set maximum txpool weight in bytes."
|
||||||
|
@ -235,8 +230,7 @@ namespace cryptonote
|
||||||
m_disable_dns_checkpoints(false),
|
m_disable_dns_checkpoints(false),
|
||||||
m_update_download(0),
|
m_update_download(0),
|
||||||
m_nettype(UNDEFINED),
|
m_nettype(UNDEFINED),
|
||||||
m_update_available(false),
|
m_update_available(false)
|
||||||
m_pad_transactions(false)
|
|
||||||
{
|
{
|
||||||
m_checkpoints_updating.clear();
|
m_checkpoints_updating.clear();
|
||||||
set_cryptonote_protocol(pprotocol);
|
set_cryptonote_protocol(pprotocol);
|
||||||
|
@ -333,7 +327,6 @@ namespace cryptonote
|
||||||
command_line::add_arg(desc, arg_block_download_max_size);
|
command_line::add_arg(desc, arg_block_download_max_size);
|
||||||
command_line::add_arg(desc, arg_sync_pruned_blocks);
|
command_line::add_arg(desc, arg_sync_pruned_blocks);
|
||||||
command_line::add_arg(desc, arg_max_txpool_weight);
|
command_line::add_arg(desc, arg_max_txpool_weight);
|
||||||
command_line::add_arg(desc, arg_pad_transactions);
|
|
||||||
command_line::add_arg(desc, arg_block_notify);
|
command_line::add_arg(desc, arg_block_notify);
|
||||||
command_line::add_arg(desc, arg_prune_blockchain);
|
command_line::add_arg(desc, arg_prune_blockchain);
|
||||||
command_line::add_arg(desc, arg_reorg_notify);
|
command_line::add_arg(desc, arg_reorg_notify);
|
||||||
|
@ -376,7 +369,6 @@ namespace cryptonote
|
||||||
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
|
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
|
||||||
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
|
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
|
||||||
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
|
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
|
||||||
m_pad_transactions = get_arg(vm, arg_pad_transactions);
|
|
||||||
m_offline = get_arg(vm, arg_offline);
|
m_offline = get_arg(vm, arg_offline);
|
||||||
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
|
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
|
||||||
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
|
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
|
||||||
|
@ -1295,7 +1287,7 @@ namespace cryptonote
|
||||||
private_req.txs.push_back(std::move(std::get<1>(tx)));
|
private_req.txs.push_back(std::move(std::get<1>(tx)));
|
||||||
break;
|
break;
|
||||||
case relay_method::block:
|
case relay_method::block:
|
||||||
case relay_method::flood:
|
case relay_method::fluff:
|
||||||
public_req.txs.push_back(std::move(std::get<1>(tx)));
|
public_req.txs.push_back(std::move(std::get<1>(tx)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -791,13 +791,6 @@ namespace cryptonote
|
||||||
*/
|
*/
|
||||||
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
|
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get whether transaction relay should be padded
|
|
||||||
*
|
|
||||||
* @return whether transaction relay should be padded
|
|
||||||
*/
|
|
||||||
bool pad_transactions() const { return m_pad_transactions; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief check a set of hashes against the precompiled hash set
|
* @brief check a set of hashes against the precompiled hash set
|
||||||
*
|
*
|
||||||
|
@ -1102,7 +1095,6 @@ namespace cryptonote
|
||||||
|
|
||||||
bool m_fluffy_blocks_enabled;
|
bool m_fluffy_blocks_enabled;
|
||||||
bool m_offline;
|
bool m_offline;
|
||||||
bool m_pad_transactions;
|
|
||||||
|
|
||||||
std::shared_ptr<tools::Notify> m_block_rate_notify;
|
std::shared_ptr<tools::Notify> m_block_rate_notify;
|
||||||
};
|
};
|
||||||
|
|
|
@ -910,7 +910,7 @@ namespace cryptonote
|
||||||
for (size_t i = 0; i < arg.txs.size(); ++i)
|
for (size_t i = 0; i < arg.txs.size(); ++i)
|
||||||
{
|
{
|
||||||
cryptonote::tx_verification_context tvc{};
|
cryptonote::tx_verification_context tvc{};
|
||||||
m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::flood, true);
|
m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::fluff, true);
|
||||||
if(tvc.m_verifivation_failed)
|
if(tvc.m_verifivation_failed)
|
||||||
{
|
{
|
||||||
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
|
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
|
||||||
|
@ -2351,7 +2351,7 @@ skip:
|
||||||
local mempool before doing the relay. The code was already updating the
|
local mempool before doing the relay. The code was already updating the
|
||||||
DB twice on received transactions - it is difficult to workaround this
|
DB twice on received transactions - it is difficult to workaround this
|
||||||
due to the internal design. */
|
due to the internal design. */
|
||||||
return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core, m_core.pad_transactions()) != epee::net_utils::zone::invalid;
|
return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core) != epee::net_utils::zone::invalid;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------
|
||||||
template<class t_core>
|
template<class t_core>
|
||||||
|
|
|
@ -38,6 +38,6 @@ namespace cryptonote
|
||||||
none = 0, //!< Received via RPC with `do_not_relay` set
|
none = 0, //!< Received via RPC with `do_not_relay` set
|
||||||
local, //!< Received via RPC; trying to send over i2p/tor, etc.
|
local, //!< Received via RPC; trying to send over i2p/tor, etc.
|
||||||
block, //!< Received in block, takes precedence over others
|
block, //!< Received in block, takes precedence over others
|
||||||
flood //!< Received/sent over public networks
|
fluff //!< Received/sent over public networks
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "common/expect.h"
|
#include "common/expect.h"
|
||||||
#include "common/varint.h"
|
#include "common/varint.h"
|
||||||
|
@ -57,6 +58,37 @@ namespace levin
|
||||||
constexpr const std::chrono::seconds noise_min_delay{CRYPTONOTE_NOISE_MIN_DELAY};
|
constexpr const std::chrono::seconds noise_min_delay{CRYPTONOTE_NOISE_MIN_DELAY};
|
||||||
constexpr const std::chrono::seconds noise_delay_range{CRYPTONOTE_NOISE_DELAY_RANGE};
|
constexpr const std::chrono::seconds noise_delay_range{CRYPTONOTE_NOISE_DELAY_RANGE};
|
||||||
|
|
||||||
|
/* A custom duration is used for the poisson distribution because of the
|
||||||
|
variance. If 5 seconds is given to `std::poisson_distribution`, 95% of
|
||||||
|
the values fall between 1-9s in 1s increments (not granular enough). If
|
||||||
|
5000 milliseconds is given, 95% of the values fall between 4859ms-5141ms
|
||||||
|
in 1ms increments (not enough time variance). Providing 20 quarter
|
||||||
|
seconds yields 95% of the values between 3s-7.25s in 1/4s increments. */
|
||||||
|
using fluff_stepsize = std::chrono::duration<std::chrono::milliseconds::rep, std::ratio<1, 4>>;
|
||||||
|
constexpr const std::chrono::seconds fluff_average_in{CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE};
|
||||||
|
|
||||||
|
/*! Bitcoin Core is using 1/2 average seconds for outgoing connections
|
||||||
|
compared to incoming. The thinking is that the user controls outgoing
|
||||||
|
connections (Dandelion++ makes similar assumptions in its stem
|
||||||
|
algorithm). The randomization yields 95% values between 1s-4s in
|
||||||
|
1/4s increments. */
|
||||||
|
constexpr const fluff_stepsize fluff_average_out{fluff_stepsize{fluff_average_in} / 2};
|
||||||
|
|
||||||
|
class random_poisson
|
||||||
|
{
|
||||||
|
std::poisson_distribution<fluff_stepsize::rep> dist;
|
||||||
|
public:
|
||||||
|
explicit random_poisson(fluff_stepsize average)
|
||||||
|
: dist(average.count() < 0 ? 0 : average.count())
|
||||||
|
{}
|
||||||
|
|
||||||
|
fluff_stepsize operator()()
|
||||||
|
{
|
||||||
|
crypto::random_device rand{};
|
||||||
|
return fluff_stepsize{dist(rand)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*! Select a randomized duration from 0 to `range`. The precision will be to
|
/*! Select a randomized duration from 0 to `range`. The precision will be to
|
||||||
the systems `steady_clock`. As an example, supplying 3 seconds to this
|
the systems `steady_clock`. As an example, supplying 3 seconds to this
|
||||||
function will select a duration from [0, 3] seconds, and the increments
|
function will select a duration from [0, 3] seconds, and the increments
|
||||||
|
@ -129,6 +161,12 @@ namespace levin
|
||||||
return fullBlob;
|
return fullBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool make_payload_send_txs(connections& p2p, std::vector<blobdata>&& txs, const boost::uuids::uuid& destination, const bool pad)
|
||||||
|
{
|
||||||
|
const cryptonote::blobdata blob = make_tx_payload(std::move(txs), pad);
|
||||||
|
return p2p.notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<std::uint8_t>(blob), destination);
|
||||||
|
}
|
||||||
|
|
||||||
/* The current design uses `asio::strand`s. The documentation isn't as clear
|
/* The current design uses `asio::strand`s. The documentation isn't as clear
|
||||||
as it should be - a `strand` has an internal `mutex` and `bool`. The
|
as it should be - a `strand` has an internal `mutex` and `bool`. The
|
||||||
`mutex` synchronizes thread access and the `bool` is set when a thread is
|
`mutex` synchronizes thread access and the `bool` is set when a thread is
|
||||||
|
@ -187,15 +225,18 @@ namespace levin
|
||||||
{
|
{
|
||||||
struct zone
|
struct zone
|
||||||
{
|
{
|
||||||
explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, bool is_public)
|
explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, bool is_public, bool pad_txs)
|
||||||
: p2p(std::move(p2p)),
|
: p2p(std::move(p2p)),
|
||||||
noise(std::move(noise_in)),
|
noise(std::move(noise_in)),
|
||||||
next_epoch(io_service),
|
next_epoch(io_service),
|
||||||
|
flush_txs(io_service),
|
||||||
strand(io_service),
|
strand(io_service),
|
||||||
map(),
|
map(),
|
||||||
channels(),
|
channels(),
|
||||||
|
flush_time(std::chrono::steady_clock::time_point::max()),
|
||||||
connection_count(0),
|
connection_count(0),
|
||||||
is_public(is_public)
|
is_public(is_public),
|
||||||
|
pad_txs(pad_txs)
|
||||||
{
|
{
|
||||||
for (std::size_t count = 0; !noise.empty() && count < CRYPTONOTE_NOISE_CHANNELS; ++count)
|
for (std::size_t count = 0; !noise.empty() && count < CRYPTONOTE_NOISE_CHANNELS; ++count)
|
||||||
channels.emplace_back(io_service);
|
channels.emplace_back(io_service);
|
||||||
|
@ -204,11 +245,14 @@ namespace levin
|
||||||
const std::shared_ptr<connections> p2p;
|
const std::shared_ptr<connections> p2p;
|
||||||
const epee::byte_slice noise; //!< `!empty()` means zone is using noise channels
|
const epee::byte_slice noise; //!< `!empty()` means zone is using noise channels
|
||||||
boost::asio::steady_timer next_epoch;
|
boost::asio::steady_timer next_epoch;
|
||||||
|
boost::asio::steady_timer flush_txs;
|
||||||
boost::asio::io_service::strand strand;
|
boost::asio::io_service::strand strand;
|
||||||
net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems
|
net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems
|
||||||
std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand`
|
std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand`
|
||||||
|
std::chrono::steady_clock::time_point flush_time; //!< Next expected Dandelion++ fluff flush
|
||||||
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
|
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
|
||||||
const bool is_public; //!< Zone is public ipv4/ipv6 connections
|
const bool is_public; //!< Zone is public ipv4/ipv6 connections
|
||||||
|
const bool pad_txs; //!< Pad txs to the next boundary for privacy
|
||||||
};
|
};
|
||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
|
@ -245,49 +289,112 @@ namespace levin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Sends a message to every active connection
|
//! Sends txs on connections with expired timers, and queues callback for next timer expiration (if any).
|
||||||
class flood_notify
|
struct fluff_flush
|
||||||
{
|
{
|
||||||
std::shared_ptr<detail::zone> zone_;
|
std::shared_ptr<detail::zone> zone_;
|
||||||
epee::byte_slice message_; // Requires manual copy
|
std::chrono::steady_clock::time_point flush_time_;
|
||||||
boost::uuids::uuid source_;
|
|
||||||
|
|
||||||
public:
|
static void queue(std::shared_ptr<detail::zone> zone, const std::chrono::steady_clock::time_point flush_time)
|
||||||
explicit flood_notify(std::shared_ptr<detail::zone> zone, epee::byte_slice message, const boost::uuids::uuid& source)
|
{
|
||||||
: zone_(std::move(zone)), message_(message.clone()), source_(source)
|
assert(zone != nullptr);
|
||||||
{}
|
assert(zone->strand.running_in_this_thread());
|
||||||
|
|
||||||
flood_notify(flood_notify&&) = default;
|
detail::zone& this_zone = *zone;
|
||||||
flood_notify(const flood_notify& source)
|
this_zone.flush_time = flush_time;
|
||||||
: zone_(source.zone_), message_(source.message_.clone()), source_(source.source_)
|
this_zone.flush_txs.expires_at(flush_time);
|
||||||
{}
|
this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone), flush_time}));
|
||||||
|
}
|
||||||
|
|
||||||
void operator()() const
|
void operator()(const boost::system::error_code error)
|
||||||
{
|
{
|
||||||
if (!zone_ || !zone_->p2p)
|
if (!zone_ || !zone_->p2p)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(zone_->strand.running_in_this_thread());
|
assert(zone_->strand.running_in_this_thread());
|
||||||
|
|
||||||
/* The foreach should be quick, but then it iterates and acquires the
|
const bool timer_error = bool(error);
|
||||||
same lock for every connection. So do in a strand because two threads
|
if (timer_error)
|
||||||
will ping-pong each other with cacheline invalidations. Revisit if
|
{
|
||||||
algorithm changes or the locking strategy within the levin config
|
if (error != boost::system::errc::operation_canceled)
|
||||||
class changes. */
|
throw boost::system::system_error{error, "fluff_flush timer failed"};
|
||||||
|
|
||||||
std::vector<boost::uuids::uuid> connections;
|
// new timer canceled this one set in future
|
||||||
connections.reserve(connection_id_reserve_size);
|
if (zone_->flush_time < flush_time_)
|
||||||
zone_->p2p->foreach_connection([this, &connections] (detail::p2p_context& context) {
|
return;
|
||||||
/* Only send to outgoing connections when "flooding" over i2p/tor.
|
}
|
||||||
Otherwise this makes the tx linkable to a hidden service address,
|
|
||||||
making things linkable across connections. */
|
const auto now = std::chrono::steady_clock::now();
|
||||||
if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income))
|
auto next_flush = std::chrono::steady_clock::time_point::max();
|
||||||
connections.emplace_back(context.m_connection_id);
|
std::vector<std::pair<std::vector<blobdata>, boost::uuids::uuid>> connections{};
|
||||||
|
zone_->p2p->foreach_connection([timer_error, now, &next_flush, &connections] (detail::p2p_context& context)
|
||||||
|
{
|
||||||
|
if (!context.fluff_txs.empty())
|
||||||
|
{
|
||||||
|
if (context.flush_time <= now || timer_error) // flush on canceled timer
|
||||||
|
{
|
||||||
|
context.flush_time = std::chrono::steady_clock::time_point::max();
|
||||||
|
connections.emplace_back(std::move(context.fluff_txs), context.m_connection_id);
|
||||||
|
context.fluff_txs.clear();
|
||||||
|
}
|
||||||
|
else // not flushing yet
|
||||||
|
next_flush = std::min(next_flush, context.flush_time);
|
||||||
|
}
|
||||||
|
else // nothing to flush
|
||||||
|
context.flush_time = std::chrono::steady_clock::time_point::max();
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const boost::uuids::uuid& connection : connections)
|
for (auto& connection : connections)
|
||||||
zone_->p2p->send(message_.clone(), connection);
|
make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs);
|
||||||
|
|
||||||
|
if (next_flush != std::chrono::steady_clock::time_point::max())
|
||||||
|
fluff_flush::queue(std::move(zone_), next_flush);
|
||||||
|
else
|
||||||
|
zone_->flush_time = next_flush; // signal that no timer is set
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! The "fluff" portion of the Dandelion++ algorithm. Every tx is queued
|
||||||
|
per-connection and flushed with a randomized poisson timer. This
|
||||||
|
implementation only has one system timer per-zone, and instead tracks
|
||||||
|
the lowest flush time. */
|
||||||
|
struct fluff_notify
|
||||||
|
{
|
||||||
|
std::shared_ptr<detail::zone> zone_;
|
||||||
|
std::vector<blobdata> txs_;
|
||||||
|
boost::uuids::uuid source_;
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
if (!zone_ || !zone_->p2p || txs_.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(zone_->strand.running_in_this_thread());
|
||||||
|
|
||||||
|
const auto now = std::chrono::steady_clock::now();
|
||||||
|
auto next_flush = std::chrono::steady_clock::time_point::max();
|
||||||
|
|
||||||
|
random_poisson in_duration(fluff_average_in);
|
||||||
|
random_poisson out_duration(fluff_average_out);
|
||||||
|
|
||||||
|
zone_->p2p->foreach_connection([this, now, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
|
||||||
|
{
|
||||||
|
if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income))
|
||||||
|
{
|
||||||
|
if (context.fluff_txs.empty())
|
||||||
|
context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
|
||||||
|
|
||||||
|
next_flush = std::min(next_flush, context.flush_time);
|
||||||
|
context.fluff_txs.reserve(context.fluff_txs.size() + this->txs_.size());
|
||||||
|
for (const blobdata& tx : this->txs_)
|
||||||
|
context.fluff_txs.push_back(tx); // must copy instead of move (multiple conns)
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (next_flush < zone_->flush_time)
|
||||||
|
fluff_flush::queue(std::move(zone_), next_flush);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -451,7 +558,7 @@ namespace levin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Prepares connections for new channel epoch and sets timer for next epoch
|
//! Prepares connections for new channel/dandelionpp epoch and sets timer for next epoch
|
||||||
struct start_epoch
|
struct start_epoch
|
||||||
{
|
{
|
||||||
// Variables allow for Dandelion++ extension
|
// Variables allow for Dandelion++ extension
|
||||||
|
@ -481,8 +588,8 @@ namespace levin
|
||||||
};
|
};
|
||||||
} // anonymous
|
} // anonymous
|
||||||
|
|
||||||
notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public)
|
notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, const bool is_public, const bool pad_txs)
|
||||||
: zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), is_public))
|
: zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), is_public, pad_txs))
|
||||||
{
|
{
|
||||||
if (!zone_->p2p)
|
if (!zone_->p2p)
|
||||||
throw std::logic_error{"cryptonote::levin::notify cannot have nullptr p2p argument"};
|
throw std::logic_error{"cryptonote::levin::notify cannot have nullptr p2p argument"};
|
||||||
|
@ -533,8 +640,18 @@ namespace levin
|
||||||
channel.next_noise.cancel();
|
channel.next_noise.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notify::send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source, const bool pad_txs)
|
void notify::run_fluff()
|
||||||
{
|
{
|
||||||
|
if (!zone_)
|
||||||
|
return;
|
||||||
|
zone_->flush_txs.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool notify::send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source)
|
||||||
|
{
|
||||||
|
if (txs.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!zone_)
|
if (!zone_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -565,12 +682,7 @@ namespace levin
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const std::string payload = make_tx_payload(std::move(txs), pad_txs);
|
zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source});
|
||||||
epee::byte_slice message =
|
|
||||||
epee::levin::make_notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<std::uint8_t>(payload));
|
|
||||||
|
|
||||||
// traditional monero send technique
|
|
||||||
zone_->strand.dispatch(flood_notify{zone_, std::move(message), source});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace levin
|
||||||
{}
|
{}
|
||||||
|
|
||||||
//! Construct an instance with available notification `zones`.
|
//! 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);
|
explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public, bool pad_txs);
|
||||||
|
|
||||||
notify(const notify&) = delete;
|
notify(const notify&) = delete;
|
||||||
notify(notify&&) = default;
|
notify(notify&&) = default;
|
||||||
|
@ -104,11 +104,14 @@ namespace levin
|
||||||
//! Run the logic for the next stem timeout imemdiately. Only use in testing.
|
//! Run the logic for the next stem timeout imemdiately. Only use in testing.
|
||||||
void run_stems();
|
void run_stems();
|
||||||
|
|
||||||
|
//! Run the logic for flushing all Dandelion++ fluff queued txs. Only use in testing.
|
||||||
|
void run_fluff();
|
||||||
|
|
||||||
/*! Send txs using `cryptonote_protocol_defs.h` payload format wrapped in a
|
/*! Send txs using `cryptonote_protocol_defs.h` payload format wrapped in a
|
||||||
levin header. The message will be sent in a "discreet" manner if
|
levin header. The message will be sent in a "discreet" manner if
|
||||||
enabled - if `!noise.empty()` then the `command`/`payload` will be
|
enabled - if `!noise.empty()` then the `command`/`payload` will be
|
||||||
queued to send at the next available noise interval. Otherwise, a
|
queued to send at the next available noise interval. Otherwise, a
|
||||||
standard Monero flood notification will be used.
|
Dandelion++ fluff algorithm will be used.
|
||||||
|
|
||||||
\note Eventually Dandelion++ stem sending will be used here when
|
\note Eventually Dandelion++ stem sending will be used here when
|
||||||
enabled.
|
enabled.
|
||||||
|
@ -117,12 +120,9 @@ namespace levin
|
||||||
\param source The source of the notification. `is_nil()` indicates this
|
\param source The source of the notification. `is_nil()` indicates this
|
||||||
node is the source. Dandelion++ will use this to map a source to a
|
node is the source. Dandelion++ will use this to map a source to a
|
||||||
particular stem.
|
particular stem.
|
||||||
\param pad_txs A request to pad txs to help conceal origin via
|
|
||||||
statistical analysis. Ignored if noise was enabled during
|
|
||||||
construction.
|
|
||||||
|
|
||||||
\return True iff the notification is queued for sending. */
|
\return True iff the notification is queued for sending. */
|
||||||
bool send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source, bool pad_txs);
|
bool send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source);
|
||||||
};
|
};
|
||||||
} // levin
|
} // levin
|
||||||
} // net
|
} // net
|
||||||
|
|
|
@ -161,6 +161,10 @@ namespace nodetool
|
||||||
const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN};
|
const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN};
|
||||||
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
|
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
|
||||||
|
|
||||||
|
const command_line::arg_descriptor<bool> arg_pad_transactions = {
|
||||||
|
"pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
|
||||||
|
};
|
||||||
|
|
||||||
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
|
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
|
||||||
{
|
{
|
||||||
namespace ip = boost::asio::ip;
|
namespace ip = boost::asio::ip;
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <boost/program_options/options_description.hpp>
|
#include <boost/program_options/options_description.hpp>
|
||||||
#include <boost/program_options/variables_map.hpp>
|
#include <boost/program_options/variables_map.hpp>
|
||||||
#include <boost/uuid/uuid.hpp>
|
#include <boost/uuid/uuid.hpp>
|
||||||
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -109,8 +110,16 @@ namespace nodetool
|
||||||
template<class base_type>
|
template<class base_type>
|
||||||
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
|
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
|
||||||
{
|
{
|
||||||
p2p_connection_context_t(): peer_id(0), support_flags(0), m_in_timedsync(false) {}
|
p2p_connection_context_t()
|
||||||
|
: fluff_txs(),
|
||||||
|
flush_time(std::chrono::steady_clock::time_point::max()),
|
||||||
|
peer_id(0),
|
||||||
|
support_flags(0),
|
||||||
|
m_in_timedsync(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<cryptonote::blobdata> fluff_txs;
|
||||||
|
std::chrono::steady_clock::time_point flush_time;
|
||||||
peerid_type peer_id;
|
peerid_type peer_id;
|
||||||
uint32_t support_flags;
|
uint32_t support_flags;
|
||||||
bool m_in_timedsync;
|
bool m_in_timedsync;
|
||||||
|
@ -337,7 +346,7 @@ namespace nodetool
|
||||||
virtual void callback(p2p_connection_context& context);
|
virtual void callback(p2p_connection_context& context);
|
||||||
//----------------- i_p2p_endpoint -------------------------------------------------------------
|
//----------------- i_p2p_endpoint -------------------------------------------------------------
|
||||||
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections);
|
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections);
|
||||||
virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs);
|
virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core);
|
||||||
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context);
|
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context);
|
||||||
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context);
|
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context);
|
||||||
virtual bool drop_connection(const epee::net_utils::connection_context_base& context);
|
virtual bool drop_connection(const epee::net_utils::connection_context_base& context);
|
||||||
|
@ -540,6 +549,7 @@ namespace nodetool
|
||||||
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up;
|
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up;
|
||||||
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down;
|
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down;
|
||||||
extern const command_line::arg_descriptor<int64_t> arg_limit_rate;
|
extern const command_line::arg_descriptor<int64_t> arg_limit_rate;
|
||||||
|
extern const command_line::arg_descriptor<bool> arg_pad_transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
POP_WARNINGS
|
POP_WARNINGS
|
||||||
|
|
|
@ -116,6 +116,7 @@ namespace nodetool
|
||||||
command_line::add_arg(desc, arg_limit_rate_up);
|
command_line::add_arg(desc, arg_limit_rate_up);
|
||||||
command_line::add_arg(desc, arg_limit_rate_down);
|
command_line::add_arg(desc, arg_limit_rate_down);
|
||||||
command_line::add_arg(desc, arg_limit_rate);
|
command_line::add_arg(desc, arg_limit_rate);
|
||||||
|
command_line::add_arg(desc, arg_pad_transactions);
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
template<class t_payload_net_handler>
|
template<class t_payload_net_handler>
|
||||||
|
@ -340,6 +341,7 @@ namespace nodetool
|
||||||
{
|
{
|
||||||
bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
|
||||||
bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
|
bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
|
||||||
|
const bool pad_txs = command_line::get_arg(vm, arg_pad_transactions);
|
||||||
m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET;
|
m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET;
|
||||||
|
|
||||||
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
|
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
|
||||||
|
@ -384,7 +386,7 @@ namespace nodetool
|
||||||
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
|
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
|
||||||
m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
|
m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
|
||||||
public_zone.m_notifier = cryptonote::levin::notify{
|
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
|
public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true, pad_txs
|
||||||
};
|
};
|
||||||
|
|
||||||
if (command_line::has_arg(vm, arg_p2p_add_peer))
|
if (command_line::has_arg(vm, arg_p2p_add_peer))
|
||||||
|
@ -495,7 +497,7 @@ namespace nodetool
|
||||||
}
|
}
|
||||||
|
|
||||||
zone.m_notifier = cryptonote::levin::notify{
|
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
|
zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false, pad_txs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2053,18 +2055,18 @@ namespace nodetool
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
template<class t_payload_net_handler>
|
template<class t_payload_net_handler>
|
||||||
epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs)
|
epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)
|
||||||
{
|
{
|
||||||
namespace enet = epee::net_utils;
|
namespace enet = epee::net_utils;
|
||||||
|
|
||||||
const auto send = [&txs, &source, &core, pad_txs] (std::pair<const enet::zone, network_zone>& network)
|
const auto send = [&txs, &source, &core] (std::pair<const enet::zone, network_zone>& network)
|
||||||
{
|
{
|
||||||
const bool is_public = (network.first == enet::zone::public_);
|
const bool is_public = (network.first == enet::zone::public_);
|
||||||
const cryptonote::relay_method tx_relay = is_public ?
|
const cryptonote::relay_method tx_relay = is_public ?
|
||||||
cryptonote::relay_method::flood : cryptonote::relay_method::local;
|
cryptonote::relay_method::fluff : cryptonote::relay_method::local;
|
||||||
|
|
||||||
core.on_transactions_relayed(epee::to_span(txs), tx_relay);
|
core.on_transactions_relayed(epee::to_span(txs), tx_relay);
|
||||||
if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || !is_public)))
|
if (network.second.m_notifier.send_txs(std::move(txs), source))
|
||||||
return network.first;
|
return network.first;
|
||||||
return enet::zone::invalid;
|
return enet::zone::invalid;
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace nodetool
|
||||||
struct i_p2p_endpoint
|
struct i_p2p_endpoint
|
||||||
{
|
{
|
||||||
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0;
|
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0;
|
||||||
virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, bool pad_txs)=0;
|
virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)=0;
|
||||||
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0;
|
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0;
|
||||||
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0;
|
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0;
|
||||||
virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0;
|
virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0;
|
||||||
|
@ -75,7 +75,7 @@ namespace nodetool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core, const bool pad_txs)
|
virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)
|
||||||
{
|
{
|
||||||
return epee::net_utils::zone::invalid;
|
return epee::net_utils::zone::invalid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -511,7 +511,7 @@ public:
|
||||||
, m_events(events)
|
, m_events(events)
|
||||||
, m_validator(validator)
|
, m_validator(validator)
|
||||||
, m_ev_index(0)
|
, m_ev_index(0)
|
||||||
, m_tx_relay(cryptonote::relay_method::flood)
|
, m_tx_relay(cryptonote::relay_method::fluff)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,7 +544,7 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_tx_relay = cryptonote::relay_method::flood;
|
m_tx_relay = cryptonote::relay_method::fluff;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -271,12 +271,12 @@ namespace
|
||||||
EXPECT_EQ(connection_ids_.size(), connections_->get_connections_count());
|
EXPECT_EQ(connection_ids_.size(), connections_->get_connections_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public)
|
cryptonote::levin::notify make_notifier(const std::size_t noise_size, bool is_public, bool pad_txs)
|
||||||
{
|
{
|
||||||
epee::byte_slice noise = nullptr;
|
epee::byte_slice noise = nullptr;
|
||||||
if (noise_size)
|
if (noise_size)
|
||||||
noise = epee::levin::make_noise_notify(noise_size);
|
noise = epee::levin::make_noise_notify(noise_size);
|
||||||
return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public};
|
return cryptonote::levin::notify{io_service_, connections_, std::move(noise), is_public, pad_txs};
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::uuids::random_generator random_generator_;
|
boost::uuids::random_generator random_generator_;
|
||||||
|
@ -434,12 +434,16 @@ TEST_F(levin_notify, defaulted)
|
||||||
EXPECT_FALSE(status.has_noise);
|
EXPECT_FALSE(status.has_noise);
|
||||||
EXPECT_FALSE(status.connections_filled);
|
EXPECT_FALSE(status.connections_filled);
|
||||||
}
|
}
|
||||||
EXPECT_FALSE(notifier.send_txs({}, random_generator_(), false));
|
EXPECT_TRUE(notifier.send_txs({}, random_generator_()));
|
||||||
|
|
||||||
|
std::vector<cryptonote::blobdata> txs(2);
|
||||||
|
txs[0].resize(100, 'e');
|
||||||
|
EXPECT_FALSE(notifier.send_txs(std::move(txs), random_generator_()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(levin_notify, flood)
|
TEST_F(levin_notify, fluff_without_padding)
|
||||||
{
|
{
|
||||||
cryptonote::levin::notify notifier = make_notifier(0, true);
|
cryptonote::levin::notify notifier = make_notifier(0, true, false);
|
||||||
|
|
||||||
for (unsigned count = 0; count < 10; ++count)
|
for (unsigned count = 0; count < 10; ++count)
|
||||||
add_connection(count % 2 == 0);
|
add_connection(count % 2 == 0);
|
||||||
|
@ -464,10 +468,13 @@ TEST_F(levin_notify, flood)
|
||||||
ASSERT_EQ(10u, contexts_.size());
|
ASSERT_EQ(10u, contexts_.size());
|
||||||
{
|
{
|
||||||
auto context = contexts_.begin();
|
auto context = contexts_.begin();
|
||||||
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false));
|
EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
|
||||||
|
|
||||||
io_service_.reset();
|
io_service_.reset();
|
||||||
ASSERT_LT(0u, io_service_.poll());
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
notifier.run_fluff();
|
||||||
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
|
||||||
EXPECT_EQ(0u, context->process_send_queue());
|
EXPECT_EQ(0u, context->process_send_queue());
|
||||||
for (++context; context != contexts_.end(); ++context)
|
for (++context; context != contexts_.end(); ++context)
|
||||||
EXPECT_EQ(1u, context->process_send_queue());
|
EXPECT_EQ(1u, context->process_send_queue());
|
||||||
|
@ -480,14 +487,42 @@ TEST_F(levin_notify, flood)
|
||||||
EXPECT_TRUE(notification._.empty());
|
EXPECT_TRUE(notification._.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(levin_notify, fluff_with_padding)
|
||||||
|
{
|
||||||
|
cryptonote::levin::notify notifier = make_notifier(0, true, true);
|
||||||
|
|
||||||
|
for (unsigned count = 0; count < 10; ++count)
|
||||||
|
add_connection(count % 2 == 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto status = notifier.get_status();
|
||||||
|
EXPECT_FALSE(status.has_noise);
|
||||||
|
EXPECT_FALSE(status.connections_filled);
|
||||||
|
}
|
||||||
|
notifier.new_out_connection();
|
||||||
|
io_service_.poll();
|
||||||
|
{
|
||||||
|
const auto status = notifier.get_status();
|
||||||
|
EXPECT_FALSE(status.has_noise);
|
||||||
|
EXPECT_FALSE(status.connections_filled); // not tracked
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cryptonote::blobdata> txs(2);
|
||||||
|
txs[0].resize(100, 'e');
|
||||||
|
txs[1].resize(200, 'f');
|
||||||
|
|
||||||
ASSERT_EQ(10u, contexts_.size());
|
ASSERT_EQ(10u, contexts_.size());
|
||||||
{
|
{
|
||||||
auto context = contexts_.begin();
|
auto context = contexts_.begin();
|
||||||
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true));
|
EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
|
||||||
|
|
||||||
io_service_.reset();
|
io_service_.reset();
|
||||||
ASSERT_LT(0u, io_service_.poll());
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
notifier.run_fluff();
|
||||||
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
|
||||||
EXPECT_EQ(0u, context->process_send_queue());
|
EXPECT_EQ(0u, context->process_send_queue());
|
||||||
for (++context; context != contexts_.end(); ++context)
|
for (++context; context != contexts_.end(); ++context)
|
||||||
EXPECT_EQ(1u, context->process_send_queue());
|
EXPECT_EQ(1u, context->process_send_queue());
|
||||||
|
@ -502,9 +537,9 @@ TEST_F(levin_notify, flood)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(levin_notify, private_flood)
|
TEST_F(levin_notify, private_fluff_without_padding)
|
||||||
{
|
{
|
||||||
cryptonote::levin::notify notifier = make_notifier(0, false);
|
cryptonote::levin::notify notifier = make_notifier(0, false, false);
|
||||||
|
|
||||||
for (unsigned count = 0; count < 10; ++count)
|
for (unsigned count = 0; count < 10; ++count)
|
||||||
add_connection(count % 2 == 0);
|
add_connection(count % 2 == 0);
|
||||||
|
@ -529,10 +564,14 @@ TEST_F(levin_notify, private_flood)
|
||||||
ASSERT_EQ(10u, contexts_.size());
|
ASSERT_EQ(10u, contexts_.size());
|
||||||
{
|
{
|
||||||
auto context = contexts_.begin();
|
auto context = contexts_.begin();
|
||||||
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), false));
|
EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
|
||||||
|
|
||||||
io_service_.reset();
|
io_service_.reset();
|
||||||
ASSERT_LT(0u, io_service_.poll());
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
notifier.run_fluff();
|
||||||
|
io_service_.reset();
|
||||||
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
|
||||||
EXPECT_EQ(0u, context->process_send_queue());
|
EXPECT_EQ(0u, context->process_send_queue());
|
||||||
for (++context; context != contexts_.end(); ++context)
|
for (++context; context != contexts_.end(); ++context)
|
||||||
{
|
{
|
||||||
|
@ -548,14 +587,43 @@ TEST_F(levin_notify, private_flood)
|
||||||
EXPECT_TRUE(notification._.empty());
|
EXPECT_TRUE(notification._.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(levin_notify, private_fluff_with_padding)
|
||||||
|
{
|
||||||
|
cryptonote::levin::notify notifier = make_notifier(0, false, true);
|
||||||
|
|
||||||
|
for (unsigned count = 0; count < 10; ++count)
|
||||||
|
add_connection(count % 2 == 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto status = notifier.get_status();
|
||||||
|
EXPECT_FALSE(status.has_noise);
|
||||||
|
EXPECT_FALSE(status.connections_filled);
|
||||||
|
}
|
||||||
|
notifier.new_out_connection();
|
||||||
|
io_service_.poll();
|
||||||
|
{
|
||||||
|
const auto status = notifier.get_status();
|
||||||
|
EXPECT_FALSE(status.has_noise);
|
||||||
|
EXPECT_FALSE(status.connections_filled); // not tracked
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cryptonote::blobdata> txs(2);
|
||||||
|
txs[0].resize(100, 'e');
|
||||||
|
txs[1].resize(200, 'f');
|
||||||
|
|
||||||
ASSERT_EQ(10u, contexts_.size());
|
ASSERT_EQ(10u, contexts_.size());
|
||||||
{
|
{
|
||||||
auto context = contexts_.begin();
|
auto context = contexts_.begin();
|
||||||
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), true));
|
EXPECT_TRUE(notifier.send_txs(txs, context->get_id()));
|
||||||
|
|
||||||
io_service_.reset();
|
io_service_.reset();
|
||||||
ASSERT_LT(0u, io_service_.poll());
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
notifier.run_fluff();
|
||||||
|
io_service_.reset();
|
||||||
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
|
||||||
EXPECT_EQ(0u, context->process_send_queue());
|
EXPECT_EQ(0u, context->process_send_queue());
|
||||||
for (++context; context != contexts_.end(); ++context)
|
for (++context; context != contexts_.end(); ++context)
|
||||||
{
|
{
|
||||||
|
@ -582,7 +650,7 @@ TEST_F(levin_notify, noise)
|
||||||
txs[0].resize(1900, 'h');
|
txs[0].resize(1900, 'h');
|
||||||
|
|
||||||
const boost::uuids::uuid incoming_id = random_generator_();
|
const boost::uuids::uuid incoming_id = random_generator_();
|
||||||
cryptonote::levin::notify notifier = make_notifier(2048, false);
|
cryptonote::levin::notify notifier = make_notifier(2048, false, true);
|
||||||
|
|
||||||
{
|
{
|
||||||
const auto status = notifier.get_status();
|
const auto status = notifier.get_status();
|
||||||
|
@ -608,7 +676,7 @@ TEST_F(levin_notify, noise)
|
||||||
EXPECT_EQ(0u, receiver_.notified_size());
|
EXPECT_EQ(0u, receiver_.notified_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_TRUE(notifier.send_txs(txs, incoming_id, false));
|
EXPECT_TRUE(notifier.send_txs(txs, incoming_id));
|
||||||
notifier.run_stems();
|
notifier.run_stems();
|
||||||
io_service_.reset();
|
io_service_.reset();
|
||||||
ASSERT_LT(0u, io_service_.poll());
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
@ -627,7 +695,7 @@ TEST_F(levin_notify, noise)
|
||||||
}
|
}
|
||||||
|
|
||||||
txs[0].resize(3000, 'r');
|
txs[0].resize(3000, 'r');
|
||||||
EXPECT_TRUE(notifier.send_txs(txs, incoming_id, true));
|
EXPECT_TRUE(notifier.send_txs(txs, incoming_id));
|
||||||
notifier.run_stems();
|
notifier.run_stems();
|
||||||
io_service_.reset();
|
io_service_.reset();
|
||||||
ASSERT_LT(0u, io_service_.poll());
|
ASSERT_LT(0u, io_service_.poll());
|
||||||
|
|
Loading…
Reference in a new issue