Merge pull request 'upstream' (#350) from wowario/wownero:up into master

Reviewed-on: https://git.wownero.com/wownero/wownero/pulls/350
This commit is contained in:
jwinterm 2020-11-07 23:02:36 +00:00
commit 21fa2b944b
9 changed files with 249 additions and 35 deletions

View file

@ -51,6 +51,7 @@ rdln::readline_buffer::readline_buffer()
void rdln::readline_buffer::start() void rdln::readline_buffer::start()
{ {
boost::lock_guard<boost::mutex> lock(sync_mutex);
if(m_cout_buf != NULL) if(m_cout_buf != NULL)
return; return;
m_cout_buf = std::cout.rdbuf(); m_cout_buf = std::cout.rdbuf();
@ -60,6 +61,7 @@ void rdln::readline_buffer::start()
void rdln::readline_buffer::stop() void rdln::readline_buffer::stop()
{ {
boost::lock_guard<boost::mutex> lock(sync_mutex);
if(m_cout_buf == NULL) if(m_cout_buf == NULL)
return; return;
std::cout.rdbuf(m_cout_buf); std::cout.rdbuf(m_cout_buf);
@ -88,9 +90,9 @@ rdln::linestatus rdln::readline_buffer::get_line(std::string& line) const
void rdln::readline_buffer::set_prompt(const std::string& prompt) void rdln::readline_buffer::set_prompt(const std::string& prompt)
{ {
boost::lock_guard<boost::mutex> lock(sync_mutex);
if(m_cout_buf == NULL) if(m_cout_buf == NULL)
return; return;
boost::lock_guard<boost::mutex> lock(sync_mutex);
rl_set_prompt(std::string(m_prompt_length, ' ').c_str()); rl_set_prompt(std::string(m_prompt_length, ' ').c_str());
rl_redisplay(); rl_redisplay();
rl_set_prompt(prompt.c_str()); rl_set_prompt(prompt.c_str());
@ -113,6 +115,12 @@ const std::vector<std::string>& rdln::readline_buffer::get_completions()
int rdln::readline_buffer::sync() int rdln::readline_buffer::sync()
{ {
boost::lock_guard<boost::mutex> lock(sync_mutex); boost::lock_guard<boost::mutex> lock(sync_mutex);
if (m_cout_buf == nullptr)
{
return -1;
}
#if RL_READLINE_VERSION < 0x0700 #if RL_READLINE_VERSION < 0x0700
char lbuf[2] = {0,0}; char lbuf[2] = {0,0};
char *line = NULL; char *line = NULL;

View file

@ -1091,6 +1091,13 @@ namespace cryptonote
//epee::net_utils::network_throttle_manager::get_global_throttle_inreq().logger_handle_net("log/dr-monero/net/req-all.data", sec, get_avg_block_size()); //epee::net_utils::network_throttle_manager::get_global_throttle_inreq().logger_handle_net("log/dr-monero/net/req-all.data", sec, get_avg_block_size());
if(arg.blocks.empty())
{
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: no blocks");
drop_connection(context, true, false);
++m_sync_bad_spans_downloaded;
return 1;
}
if(context.m_last_response_height > arg.current_blockchain_height) if(context.m_last_response_height > arg.current_blockchain_height)
{ {
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height

View file

@ -241,8 +241,8 @@ namespace levin
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),
flush_callbacks(0),
is_public(is_public), is_public(is_public),
pad_txs(pad_txs), pad_txs(pad_txs),
fluffing(false) fluffing(false)
@ -258,8 +258,8 @@ namespace levin
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
std::uint32_t flush_callbacks; //!< Number of active fluff flush callbacks queued
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 const bool pad_txs; //!< Pad txs to the next boundary for privacy
bool fluffing; //!< Zone is in Dandelion++ fluff epoch bool fluffing; //!< Zone is in Dandelion++ fluff epoch
@ -305,7 +305,6 @@ namespace levin
struct fluff_flush struct fluff_flush
{ {
std::shared_ptr<detail::zone> zone_; std::shared_ptr<detail::zone> zone_;
std::chrono::steady_clock::time_point flush_time_;
static void queue(std::shared_ptr<detail::zone> zone, const std::chrono::steady_clock::time_point flush_time) static void queue(std::shared_ptr<detail::zone> zone, const std::chrono::steady_clock::time_point flush_time)
{ {
@ -313,28 +312,21 @@ namespace levin
assert(zone->strand.running_in_this_thread()); assert(zone->strand.running_in_this_thread());
detail::zone& this_zone = *zone; detail::zone& this_zone = *zone;
this_zone.flush_time = flush_time; ++this_zone.flush_callbacks;
this_zone.flush_txs.expires_at(flush_time); 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})); this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone)}));
} }
void operator()(const boost::system::error_code error) void operator()(const boost::system::error_code error)
{ {
if (!zone_ || !zone_->p2p) if (!zone_ || !zone_->flush_callbacks || --zone_->flush_callbacks || !zone_->p2p)
return; return;
assert(zone_->strand.running_in_this_thread()); assert(zone_->strand.running_in_this_thread());
const bool timer_error = bool(error); const bool timer_error = bool(error);
if (timer_error) if (timer_error && error != boost::system::errc::operation_canceled)
{ throw boost::system::system_error{error, "fluff_flush timer failed"};
if (error != boost::system::errc::operation_canceled)
throw boost::system::system_error{error, "fluff_flush timer failed"};
// new timer canceled this one set in future
if (zone_->flush_time < flush_time_)
return;
}
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
auto next_flush = std::chrono::steady_clock::time_point::max(); auto next_flush = std::chrono::steady_clock::time_point::max();
@ -370,8 +362,6 @@ namespace levin
if (next_flush != std::chrono::steady_clock::time_point::max()) if (next_flush != std::chrono::steady_clock::time_point::max())
fluff_flush::queue(std::move(zone_), next_flush); fluff_flush::queue(std::move(zone_), next_flush);
else
zone_->flush_time = next_flush; // signal that no timer is set
} }
}; };
@ -406,13 +396,11 @@ namespace levin
MDEBUG("Queueing " << txs.size() << " transaction(s) for Dandelion++ fluffing"); MDEBUG("Queueing " << txs.size() << " transaction(s) for Dandelion++ fluffing");
bool available = false; zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush, &available] (detail::p2p_context& context)
{ {
// When i2p/tor, only fluff to outbound connections // 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->is_public || !context.m_is_income))
{ {
available = true;
if (context.fluff_txs.empty()) if (context.fluff_txs.empty())
context.flush_time = now + (context.m_is_income ? in_duration() : out_duration()); context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
@ -424,10 +412,9 @@ namespace levin
return true; return true;
}); });
if (!available) if (next_flush == std::chrono::steady_clock::time_point::max())
MWARNING("Unable to send transaction(s), no available connections"); MWARNING("Unable to send transaction(s), no available connections");
else if (!zone->flush_callbacks || next_flush < zone->flush_txs.expires_at())
if (next_flush < zone->flush_time)
fluff_flush::queue(std::move(zone), next_flush); fluff_flush::queue(std::move(zone), next_flush);
} }
}; };
@ -524,12 +511,7 @@ namespace levin
if (!zone_ || !core_ || txs_.empty()) if (!zone_ || !core_ || txs_.empty())
return; return;
if (zone_->fluffing) if (!zone_->fluffing)
{
core_->on_transactions_relayed(epee::to_span(txs_), relay_method::fluff);
fluff_notify::run(std::move(zone_), epee::to_span(txs_), source_);
}
else // forward tx in stem
{ {
core_->on_transactions_relayed(epee::to_span(txs_), relay_method::stem); core_->on_transactions_relayed(epee::to_span(txs_), relay_method::stem);
for (int tries = 2; 0 < tries; tries--) for (int tries = 2; 0 < tries; tries--)
@ -549,6 +531,9 @@ namespace levin
MERROR("Unable to send transaction(s) via Dandelion++ stem"); MERROR("Unable to send transaction(s) via Dandelion++ stem");
} }
core_->on_transactions_relayed(epee::to_span(txs_), relay_method::fluff);
fluff_notify::run(std::move(zone_), epee::to_span(txs_), source_);
} }
}; };

View file

@ -146,6 +146,7 @@ namespace nodetool
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"};
const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""}; const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""};
const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""}; const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""};
const command_line::arg_descriptor<std::string> arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false}; const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false};

View file

@ -520,6 +520,7 @@ namespace nodetool
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node; extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy; extern const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound; extern const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound;
extern const command_line::arg_descriptor<std::string> arg_ban_list;
extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port; extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port;
extern const command_line::arg_descriptor<bool> arg_no_sync; extern const command_line::arg_descriptor<bool> arg_no_sync;

View file

@ -71,6 +71,17 @@
#define MIN_WANTED_SEED_NODES 12 #define MIN_WANTED_SEED_NODES 12
static inline boost::asio::ip::address_v4 make_address_v4_from_v6(const boost::asio::ip::address_v6& a)
{
const auto &bytes = a.to_bytes();
uint32_t v4 = 0;
v4 = (v4 << 8) | bytes[12];
v4 = (v4 << 8) | bytes[13];
v4 = (v4 << 8) | bytes[14];
v4 = (v4 << 8) | bytes[15];
return boost::asio::ip::address_v4(v4);
}
namespace nodetool namespace nodetool
{ {
template<class t_payload_net_handler> template<class t_payload_net_handler>
@ -106,6 +117,7 @@ namespace nodetool
command_line::add_arg(desc, arg_p2p_seed_node); command_line::add_arg(desc, arg_p2p_seed_node);
command_line::add_arg(desc, arg_tx_proxy); command_line::add_arg(desc, arg_tx_proxy);
command_line::add_arg(desc, arg_anonymous_inbound); command_line::add_arg(desc, arg_anonymous_inbound);
command_line::add_arg(desc, arg_ban_list);
command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_p2p_hide_my_port);
command_line::add_arg(desc, arg_no_sync); command_line::add_arg(desc, arg_no_sync);
command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_no_igd);
@ -245,6 +257,10 @@ namespace nodetool
zone.second.m_net_server.get_config_object().close(c); zone.second.m_net_server.get_config_object().close(c);
conns.clear(); conns.clear();
peerlist_entry pe{};
pe.adr = addr;
zone.second.m_peerlist.remove_from_peer_white(pe);
} }
MCLOG_CYAN(el::Level::Info, "global", "Host " << addr.host_str() << " blocked."); MCLOG_CYAN(el::Level::Info, "global", "Host " << addr.host_str() << " blocked.");
@ -441,6 +457,36 @@ namespace nodetool
return false; return false;
} }
if (!command_line::is_arg_defaulted(vm, arg_ban_list))
{
const std::string ban_list = command_line::get_arg(vm, arg_ban_list);
const boost::filesystem::path ban_list_path(ban_list);
boost::system::error_code ec;
if (!boost::filesystem::exists(ban_list_path, ec))
{
throw std::runtime_error("Can't find ban list file " + ban_list + " - " + ec.message());
}
std::string banned_ips;
if (!epee::file_io_utils::load_file_to_string(ban_list_path.string(), banned_ips))
{
throw std::runtime_error("Failed to read ban list file " + ban_list);
}
std::istringstream iss(banned_ips);
for (std::string line; std::getline(iss, line); )
{
const expect<epee::net_utils::network_address> parsed_addr = net::get_network_address(line, 0);
if (!parsed_addr)
{
MERROR("Invalid IP address: " << line << " - " << parsed_addr.error());
continue;
}
block_host(*parsed_addr, std::numeric_limits<time_t>::max());
}
}
if(command_line::has_arg(vm, arg_p2p_hide_my_port)) if(command_line::has_arg(vm, arg_p2p_hide_my_port))
m_hide_my_port = true; m_hide_my_port = true;
@ -1228,7 +1274,10 @@ namespace nodetool
template<class t_payload_net_handler> template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp)
{ {
network_zone& zone = m_network_zones.at(na.get_zone()); const auto i = m_network_zones.find(na.get_zone());
if (i == m_network_zones.end())
return false;
network_zone& zone = i->second;
if (zone.m_connect == nullptr) // outgoing connections in zone not possible if (zone.m_connect == nullptr) // outgoing connections in zone not possible
return false; return false;
@ -1428,17 +1477,44 @@ namespace nodetool
const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
classB.insert(actual_ip & 0x0000ffff); classB.insert(actual_ip & 0x0000ffff);
} }
else if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
{
const epee::net_utils::network_address na = cntxt.m_remote_address;
const boost::asio::ip::address_v6 &actual_ip = na.as<const epee::net_utils::ipv6_network_address>().ip();
if (actual_ip.is_v4_mapped())
{
boost::asio::ip::address_v4 v4ip = make_address_v4_from_v6(actual_ip);
uint32_t actual_ipv4;
memcpy(&actual_ipv4, v4ip.to_bytes().data(), sizeof(actual_ipv4));
classB.insert(actual_ipv4 & ntohl(0xffff0000));
}
}
return true; return true;
}); });
} }
auto get_host_string = [](const epee::net_utils::network_address &address) {
if (address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
{
boost::asio::ip::address_v6 actual_ip = address.as<const epee::net_utils::ipv6_network_address>().ip();
if (actual_ip.is_v4_mapped())
{
boost::asio::ip::address_v4 v4ip = make_address_v4_from_v6(actual_ip);
uint32_t actual_ipv4;
memcpy(&actual_ipv4, v4ip.to_bytes().data(), sizeof(actual_ipv4));
return epee::net_utils::ipv4_network_address(actual_ipv4, 0).host_str();
}
}
return address.host_str();
};
std::unordered_set<std::string> hosts_added;
std::deque<size_t> filtered; std::deque<size_t> filtered;
const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max();
for (int step = 0; step < 2; ++step) for (int step = 0; step < 2; ++step)
{ {
bool skip_duplicate_class_B = step == 0; bool skip_duplicate_class_B = step == 0;
size_t idx = 0, skipped = 0; size_t idx = 0, skipped = 0;
zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe, &hosts_added, &get_host_string](const peerlist_entry &pe){
if (filtered.size() >= limit) if (filtered.size() >= limit)
return false; return false;
bool skip = false; bool skip = false;
@ -1448,6 +1524,27 @@ namespace nodetool
uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
skip = classB.find(actual_ip & 0x0000ffff) != classB.end(); skip = classB.find(actual_ip & 0x0000ffff) != classB.end();
} }
else if (skip_duplicate_class_B && pe.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
{
const epee::net_utils::network_address na = pe.adr;
const boost::asio::ip::address_v6 &actual_ip = na.as<const epee::net_utils::ipv6_network_address>().ip();
if (actual_ip.is_v4_mapped())
{
boost::asio::ip::address_v4 v4ip = make_address_v4_from_v6(actual_ip);
uint32_t actual_ipv4;
memcpy(&actual_ipv4, v4ip.to_bytes().data(), sizeof(actual_ipv4));
skip = classB.find(actual_ipv4 & ntohl(0xffff0000)) != classB.end();
}
}
// consider each host once, to avoid giving undue inflence to hosts running several nodes
if (!skip)
{
const auto i = hosts_added.find(get_host_string(pe.adr));
if (i != hosts_added.end())
skip = true;
}
if (skip) if (skip)
++skipped; ++skipped;
else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0)
@ -1455,16 +1552,17 @@ namespace nodetool
else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed))
filtered.push_front(idx); filtered.push_front(idx);
++idx; ++idx;
hosts_added.insert(get_host_string(pe.adr));
return true; return true;
}); });
if (skipped == 0 || !filtered.empty()) if (skipped == 0 || !filtered.empty())
break; break;
if (skipped) if (skipped)
MINFO("Skipping " << skipped << " possible peers as they share a class B with existing peers"); MDEBUG("Skipping " << skipped << " possible peers as they share a class B with existing peers");
} }
if (filtered.empty()) if (filtered.empty())
{ {
MDEBUG("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe); MINFO("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe);
return false; return false;
} }
if (use_white_list) if (use_white_list)

View file

@ -1131,6 +1131,11 @@ namespace cryptonote
{ {
RPC_TRACKER(send_raw_tx); RPC_TRACKER(send_raw_tx);
{
bool ok;
use_bootstrap_daemon_if_necessary<COMMAND_RPC_SEND_RAW_TX>(invoke_http_mode::JON, "/sendrawtransaction", req, res, ok);
}
const bool restricted = m_restricted && ctx; const bool restricted = m_restricted && ctx;
bool skip_validation = false; bool skip_validation = false;

View file

@ -107,7 +107,7 @@ class MiningTest():
break break
else: else:
assert False, 'Failed to mine successor to block %d (initial block = %d)' % (seen_height, prev_height) assert False, 'Failed to mine successor to block %d (initial block = %d)' % (seen_height, prev_height)
timeout = 5 timeout = 10
if via_daemon: if via_daemon:
res = daemon.stop_mining() res = daemon.stop_mining()

View file

@ -613,6 +613,61 @@ TEST_F(levin_notify, stem_without_padding)
} }
} }
TEST_F(levin_notify, stem_no_outs_without_padding)
{
cryptonote::levin::notify notifier = make_notifier(0, true, false);
for (unsigned count = 0; count < 10; ++count)
add_connection(true);
{
const auto status = notifier.get_status();
EXPECT_FALSE(status.has_noise);
EXPECT_FALSE(status.connections_filled);
}
notifier.new_out_connection();
io_service_.poll();
std::vector<cryptonote::blobdata> txs(2);
txs[0].resize(100, 'f');
txs[1].resize(200, 'e');
std::vector<cryptonote::blobdata> sorted_txs = txs;
std::sort(sorted_txs.begin(), sorted_txs.end());
ASSERT_EQ(10u, contexts_.size());
auto context = contexts_.begin();
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), cryptonote::relay_method::stem));
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
if (events_.has_stem_txes())
EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem));
notifier.run_fluff();
ASSERT_LT(0u, io_service_.poll());
std::size_t send_count = 0;
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
{
send_count += context->process_send_queue();
}
EXPECT_EQ(9u, send_count);
ASSERT_EQ(9u, receiver_.notified_size());
for (unsigned count = 0; count < 9u; ++count)
{
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(sorted_txs, notification.txs);
EXPECT_TRUE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
TEST_F(levin_notify, local_without_padding) TEST_F(levin_notify, local_without_padding)
{ {
cryptonote::levin::notify notifier = make_notifier(0, true, false); cryptonote::levin::notify notifier = make_notifier(0, true, false);
@ -928,6 +983,60 @@ TEST_F(levin_notify, stem_with_padding)
} }
} }
TEST_F(levin_notify, stem_no_outs_with_padding)
{
cryptonote::levin::notify notifier = make_notifier(0, true, true);
for (unsigned count = 0; count < 10; ++count)
add_connection(true);
{
const auto status = notifier.get_status();
EXPECT_FALSE(status.has_noise);
EXPECT_FALSE(status.connections_filled);
}
notifier.new_out_connection();
io_service_.poll();
std::vector<cryptonote::blobdata> txs(2);
txs[0].resize(100, 'f');
txs[1].resize(200, 'e');
std::vector<cryptonote::blobdata> sorted_txs = txs;
std::sort(sorted_txs.begin(), sorted_txs.end());
ASSERT_EQ(10u, contexts_.size());
auto context = contexts_.begin();
EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), cryptonote::relay_method::stem));
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
if (events_.has_stem_txes())
EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem));
notifier.run_fluff();
ASSERT_LT(0u, io_service_.poll());
std::size_t send_count = 0;
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
{
send_count += context->process_send_queue();
}
EXPECT_EQ(9u, send_count);
ASSERT_EQ(9u, receiver_.notified_size());
for (unsigned count = 0; count < 9u; ++count)
{
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(sorted_txs, notification.txs);
EXPECT_FALSE(notification._.empty());
EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
TEST_F(levin_notify, local_with_padding) TEST_F(levin_notify, local_with_padding)
{ {
cryptonote::levin::notify notifier = make_notifier(0, true, true); cryptonote::levin::notify notifier = make_notifier(0, true, true);