From 8547473c83e1b081c29df32bc8d79310841ac436 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Dec 2020 23:12:19 +0000 Subject: [PATCH 01/44] p2p: ignore incoming peer list entries when we have them blocked --- src/p2p/net_node.inl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 07fc64952..6215f9396 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -2049,7 +2049,9 @@ namespace nodetool LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size()); LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); - return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); }); + return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { + return !is_addr_recently_failed(pe.adr) && is_remote_host_allowed(pe.adr); + }); } //----------------------------------------------------------------------------------- template From 8088f8d894ac30ce1bfffa0478ba1e3cd81e5446 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Mon, 14 Dec 2020 14:45:24 -0500 Subject: [PATCH 02/44] Fix byte_stream::put_n --- contrib/epee/include/byte_stream.h | 2 +- tests/unit_tests/epee_utils.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/byte_stream.h b/contrib/epee/include/byte_stream.h index 30abd050d..93f9ac85c 100644 --- a/contrib/epee/include/byte_stream.h +++ b/contrib/epee/include/byte_stream.h @@ -175,7 +175,7 @@ namespace epee void put_n(const std::uint8_t ch, const std::size_t count) { check(count); - std::memset(tellp(), count, ch); + std::memset(tellp(), ch, count); next_write_ += count; } diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 207c4a7dc..256f8c3c2 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -982,6 +982,23 @@ TEST(ByteStream, Put) EXPECT_TRUE(equal(bytes, byte_span{stream.data(), stream.size()})); } +TEST(ByteStream, PutN) +{ + using boost::range::equal; + using byte_span = epee::span; + + std::vector bytes; + bytes.resize(1000, 'f'); + + epee::byte_stream stream; + stream.put_n('f', 1000); + + EXPECT_EQ(1000u, stream.size()); + EXPECT_LE(1000u, stream.capacity()); + EXPECT_EQ(stream.available(), stream.capacity() - stream.size()); + EXPECT_TRUE(equal(bytes, byte_span{stream.data(), stream.size()})); +} + TEST(ByteStream, Reserve) { using boost::range::equal; From ffa987003ab1f3efb7dfc2b6184387fba97d422c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Dec 2020 16:59:08 +0000 Subject: [PATCH 03/44] daemon: the ban command can now load IPs from a file (ban @filename) --- src/daemon/command_parser_executor.cpp | 42 ++++++++++++++++++++++++-- src/daemon/command_server.cpp | 4 +-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 89478e831..37b8b7cdb 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -28,6 +28,7 @@ #include "common/dns_utils.h" #include "common/command_line.h" +#include "net/parse.h" #include "daemon/command_parser_executor.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -657,7 +658,6 @@ bool t_command_parser_executor::ban(const std::vector& args) std::cout << "Invalid syntax: Expects one or two parameters. For more details, use the help command." << std::endl; return true; } - std::string ip = args[0]; time_t seconds = P2P_IP_BLOCKTIME; if (args.size() > 1) { @@ -676,7 +676,45 @@ bool t_command_parser_executor::ban(const std::vector& args) return true; } } - return m_executor.ban(ip, seconds); + if (boost::starts_with(args[0], "@")) + { + const std::string ban_list = args[0].substr(1); + + try + { + const boost::filesystem::path ban_list_path(ban_list); + boost::system::error_code ec; + if (!boost::filesystem::exists(ban_list_path, ec)) + { + std::cout << "Can't find ban list file " + ban_list + " - " + ec.message() << std::endl; + return true; + } + + bool ret = true; + std::ifstream ifs(ban_list_path.string()); + for (std::string line; std::getline(ifs, line); ) + { + const expect parsed_addr = net::get_network_address(line, 0); + if (!parsed_addr) + { + std::cout << "Invalid IP address: " << line << " - " << parsed_addr.error() << std::endl; + continue; + } + ret &= m_executor.ban(parsed_addr->host_str(), seconds); + } + return ret; + } + catch (const std::exception &e) + { + std::cout << "Error loading ban list: " << e.what() << std::endl; + return false; + } + } + else + { + const std::string ip = args[0]; + return m_executor.ban(ip, seconds); + } } bool t_command_parser_executor::unban(const std::vector& args) diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 8d7d1b885..938a7d8af 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -234,8 +234,8 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "ban" , std::bind(&t_command_parser_executor::ban, &m_parser, p::_1) - , "ban []" - , "Ban a given for a given amount of ." + , "ban [|@] []" + , "Ban a given or list of IPs from a file for a given amount of ." ); m_command_lookup.set_handler( "unban" From a88448499ba5e9c8289c8f3b152dd17f2c3adf83 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Dec 2020 14:16:03 +0000 Subject: [PATCH 04/44] Optional DNS based blocklist If enabled, pulls IPs to block on blocklist.moneropulse.*, and blocks then for 8 days (so IPs dropping from the list will eventually get unblocked, and DNS failures don't result in instant clearing of the blocklist). Enable with --enable-dns-blocklist --- src/cryptonote_config.h | 2 ++ src/p2p/net_node.cpp | 1 + src/p2p/net_node.h | 7 ++++- src/p2p/net_node.inl | 58 +++++++++++++++++++++++++++++++++++++-- src/p2p/net_node_common.h | 4 +-- 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 9b1f9ef70..8426ebb32 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -203,6 +203,8 @@ #define RPC_CREDITS_PER_HASH_SCALE ((float)(1<<24)) +#define DNS_BLOCKLIST_LIFETIME (86400 * 8) + // New constants are intended to go here namespace config { diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index aee1fa593..8dd551d1e 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -149,6 +149,7 @@ namespace nodetool const command_line::arg_descriptor arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; const command_line::arg_descriptor arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false}; + const command_line::arg_descriptor arg_enable_dns_blocklist = {"enable-dns-blocklist", "Apply realtime blocklist from DNS", false}; const command_line::arg_descriptor arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index c3595787a..915ee4ea7 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -278,7 +278,7 @@ namespace nodetool uint32_t get_max_out_public_peers() const; void change_max_in_public_peers(size_t count); uint32_t get_max_in_public_peers() const; - virtual bool block_host(epee::net_utils::network_address address, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool block_host(epee::net_utils::network_address address, time_t seconds = P2P_IP_BLOCKTIME, bool add_only = false); virtual bool unblock_host(const epee::net_utils::network_address &address); virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet); @@ -357,6 +357,7 @@ namespace nodetool bool peer_sync_idle_maker(); bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id); + bool update_dns_blocklist(); bool make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist); bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list); @@ -461,6 +462,7 @@ namespace nodetool epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval; + epee::math_helper::once_a_time_seconds<7000> m_dns_blocklist_interval; std::list m_priority_peers; std::vector m_exclusive_peers; @@ -502,6 +504,8 @@ namespace nodetool cryptonote::network_type m_nettype; epee::net_utils::ssl_support_t m_ssl_support; + + bool m_enable_dns_blocklist; }; const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s @@ -523,6 +527,7 @@ namespace nodetool extern const command_line::arg_descriptor arg_ban_list; extern const command_line::arg_descriptor arg_p2p_hide_my_port; extern const command_line::arg_descriptor arg_no_sync; + extern const command_line::arg_descriptor arg_enable_dns_blocklist; extern const command_line::arg_descriptor arg_no_igd; extern const command_line::arg_descriptor arg_igd; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 6215f9396..1e2d0f9f3 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,7 @@ namespace nodetool command_line::add_arg(desc, arg_ban_list); command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_sync); + command_line::add_arg(desc, arg_enable_dns_blocklist); command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_igd); command_line::add_arg(desc, arg_out_peers); @@ -226,7 +228,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::block_host(epee::net_utils::network_address addr, time_t seconds) + bool node_server::block_host(epee::net_utils::network_address addr, time_t seconds, bool add_only) { if(!addr.is_blockable()) return false; @@ -240,7 +242,11 @@ namespace nodetool else limit = now + seconds; const std::string host_str = addr.host_str(); - m_blocked_hosts[host_str] = limit; + auto it = m_blocked_hosts.find(host_str); + if (it == m_blocked_hosts.end()) + m_blocked_hosts[host_str] = limit; + else if (it->second < limit || !add_only) + it->second = limit; // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is @@ -497,6 +503,8 @@ namespace nodetool if (command_line::has_arg(vm, arg_no_sync)) m_payload_handler.set_no_sync(true); + m_enable_dns_blocklist = command_line::get_arg(vm, arg_enable_dns_blocklist); + if ( !set_max_out_peers(public_zone, command_line::get_arg(vm, arg_out_peers) ) ) return false; else @@ -1927,6 +1935,52 @@ namespace nodetool m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server::gray_peerlist_housekeeping, this)); m_peerlist_store_interval.do_call(boost::bind(&node_server::store_config, this)); m_incoming_connections_interval.do_call(boost::bind(&node_server::check_incoming_connections, this)); + m_dns_blocklist_interval.do_call(boost::bind(&node_server::update_dns_blocklist, this)); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::update_dns_blocklist() + { + if (!m_enable_dns_blocklist) + return true; + if (m_nettype != cryptonote::MAINNET) + return true; + + static const std::vector dns_urls = { + "blocklist.moneropulse.se" + , "blocklist.moneropulse.org" + , "blocklist.moneropulse.net" + , "blocklist.moneropulse.no" + , "blocklist.moneropulse.fr" + , "blocklist.moneropulse.de" + , "blocklist.moneropulse.ch" + }; + + std::vector records; + if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls)) + return true; + + unsigned good = 0, bad = 0; + for (const auto& record : records) + { + std::vector ips; + boost::split(ips, record, boost::is_any_of(";")); + for (const auto &ip: ips) + { + const expect parsed_addr = net::get_network_address(ip, 0); + if (!parsed_addr) + { + MWARNING("Invalid IP address from DNS blocklist: " << ip << " - " << parsed_addr.error()); + ++bad; + continue; + } + block_host(*parsed_addr, DNS_BLOCKLIST_LIFETIME, true); + ++good; + } + } + if (good > 0) + MINFO(good << " addresses added to the blocklist"); return true; } //----------------------------------------------------------------------------------- diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index f1490a0db..0da758ad4 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -58,7 +58,7 @@ namespace nodetool virtual uint64_t get_public_connections_count()=0; virtual void for_each_connection(std::function f)=0; virtual bool for_connection(const boost::uuids::uuid&, std::function f)=0; - virtual bool block_host(epee::net_utils::network_address address, time_t seconds = 0)=0; + virtual bool block_host(epee::net_utils::network_address address, time_t seconds = 0, bool add_only = false)=0; virtual bool unblock_host(const epee::net_utils::network_address &address)=0; virtual std::map get_blocked_hosts()=0; virtual std::map get_blocked_subnets()=0; @@ -108,7 +108,7 @@ namespace nodetool { return false; } - virtual bool block_host(epee::net_utils::network_address address, time_t seconds) + virtual bool block_host(epee::net_utils::network_address address, time_t seconds, bool add_only) { return true; } From 819d40fc7e5c426893bb45be85974b411f4e5a7f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 13 Dec 2020 23:27:07 +0000 Subject: [PATCH 05/44] p2p: remove peers from grey and anchors lists when blocked --- src/p2p/net_node.inl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 1e2d0f9f3..ec7dc0472 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -266,6 +266,8 @@ namespace nodetool peerlist_entry pe{}; pe.adr = addr; zone.second.m_peerlist.remove_from_peer_white(pe); + zone.second.m_peerlist.remove_from_peer_gray(pe); + zone.second.m_peerlist.remove_from_peer_anchor(addr); for (const auto &c: conns) zone.second.m_net_server.get_config_object().close(c); From 879715610d9b061043d14f064f77aa37b2cd8f2c Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Dec 2020 12:49:37 +0000 Subject: [PATCH 06/44] protocol: drop peers we can't download anything from in sync mode instead of the (incorrect) check for whether we think we might have a user for it at some point in the near future. We can reconnect. --- .../cryptonote_protocol_handler.inl | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index b4e1fb408..8a81e1d63 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2298,30 +2298,16 @@ skip: return true; } - // if we're still around, we might be at a point where the peer is pruned, so we could either - // drop it to make space for other peers, or ask for a span further down the line - const uint32_t next_stripe = get_next_needed_pruning_stripe().first; - const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); - const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()); - if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe) + // we can do nothing, so drop this peer to make room for others unless we think we've downloaded all we need + const uint64_t blockchain_height = m_core.get_current_blockchain_height(); + if (std::max(blockchain_height, m_block_queue.get_next_needed_height(blockchain_height)) >= m_core.get_target_blockchain_height()) { - // at this point, we have to either close the connection, or start getting blocks past the - // current point, or become dormant - MDEBUG(context << "this peer is pruned at seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << - ", next stripe needed is " << next_stripe); - if (!context.m_is_income) - { - if (should_drop_connection(context, next_stripe)) - { - m_p2p->add_used_stripe_peer(context); - return false; // drop outgoing connections - } - } - // we'll get back stuck waiting for the go ahead context.m_state = cryptonote_connection_context::state_normal; MLOG_PEER_STATE("Nothing to do for now, switching to normal state"); return true; } + MLOG_PEER_STATE("We can download nothing from this peer, dropping"); + return false; } skip: From b042506baac3106057f9065bc996d9de69c9282d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 15 Dec 2020 12:50:38 +0000 Subject: [PATCH 07/44] protocol: reject claimed block hashes that already are in the chain --- .../cryptonote_protocol_handler.inl | 13 +++++++++++++ tests/core_proxy/core_proxy.h | 1 + tests/unit_tests/node_server.cpp | 1 + 3 files changed, 15 insertions(+) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 8a81e1d63..dc61dcefc 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2548,6 +2548,8 @@ skip: } std::unordered_set hashes; + uint64_t height = arg.start_height; + const uint64_t blockchain_height = m_core.get_current_blockchain_height(); for (const auto &h: arg.m_block_ids) { if (!hashes.insert(h).second) @@ -2556,6 +2558,17 @@ skip: drop_connection(context, true, false); return 1; } + if (height < blockchain_height) + { + const crypto::hash block_in_chain = m_core.get_block_id_by_height(height); + if ((height < context.m_expect_height - 1 && block_in_chain == h) || (height == context.m_expect_height - 1 && block_in_chain != h)) + { + LOG_ERROR_CCONTEXT("sent existing block " << h << " at height " << height << ", expected height was " << context.m_expect_height << ", dropping connection"); + drop_connection(context, true, false); + return 1; + } + } + ++height; } uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights); diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index ecfcc18ae..ebc3a89c2 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -112,5 +112,6 @@ namespace tests bool prune_blockchain(uint32_t pruning_seed) const { return true; } bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } + crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; } }; } diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 0191e5aa4..0569d3748 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -93,6 +93,7 @@ public: bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; } bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } + crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; } void stop() {} }; From 10f9008b9cd656f8543aee8cbcc09bf2557421e7 Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 19 Nov 2020 12:51:31 +0000 Subject: [PATCH 08/44] rpc: get_info - add 'synchronized' field --- src/rpc/core_rpc_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 1a4fd86f0..3e6e43025 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -500,6 +500,7 @@ namespace cryptonote res.version = restricted ? "" : MONERO_VERSION_FULL; res.synchronized = check_core_ready(); res.busy_syncing = m_p2p.get_payload_object().is_busy_syncing(); + res.synchronized = check_core_ready(); res.status = CORE_RPC_STATUS_OK; return true; From 295d46a1fc5a41fcbfa5bc419315ef7201613e32 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 20 Dec 2020 12:48:34 +0000 Subject: [PATCH 09/44] restrict public node checks a little do not include blocked hosts in peer lists or public node lists by default, warn about no https on clearnet and about untrusted peers likely being spies --- src/common/util.cpp | 11 +++++- src/common/util.h | 1 + src/daemon/rpc_command_executor.cpp | 4 +++ src/rpc/core_rpc_server.cpp | 9 ++++- src/rpc/core_rpc_server_commands_defs.h | 6 +++- src/simplewallet/simplewallet.cpp | 47 ++++++++++++++++++++----- src/wallet/wallet2.cpp | 1 + 7 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index f8707d65c..445a11a75 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -871,10 +871,19 @@ std::string get_nix_version_display_string() return max_concurrency; } + bool is_privacy_preserving_network(const std::string &address) + { + if (boost::ends_with(address, ".onion")) + return true; + if (boost::ends_with(address, ".i2p")) + return true; + return false; + } + bool is_local_address(const std::string &address) { // always assume Tor/I2P addresses to be untrusted by default - if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p")) + if (is_privacy_preserving_network(address)) { MDEBUG("Address '" << address << "' is Tor/I2P, non local"); return false; diff --git a/src/common/util.h b/src/common/util.h index 0c3409ea1..6366e5cb4 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -228,6 +228,7 @@ namespace tools unsigned get_max_concurrency(); bool is_local_address(const std::string &address); + bool is_privacy_preserving_network(const std::string &address); int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ee88e6ce8..b99500a88 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -191,6 +191,9 @@ bool t_rpc_command_executor::print_peer_list(bool white, bool gray, size_t limit cryptonote::COMMAND_RPC_GET_PEER_LIST::response res; std::string failure_message = "Couldn't retrieve peer list"; + + req.include_blocked = true; + if (m_is_rpc) { if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str())) @@ -237,6 +240,7 @@ bool t_rpc_command_executor::print_peer_list_stats() { std::string failure_message = "Couldn't retrieve peer list"; req.public_only = false; + req.include_blocked = true; if (m_is_rpc) { diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 3e6e43025..69a228916 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -190,6 +190,7 @@ namespace cryptonote request.gray = true; request.white = true; + request.include_blocked = false; if (!on_get_public_nodes(request, response) || response.status != CORE_RPC_STATUS_OK) { return {}; @@ -1384,6 +1385,8 @@ namespace cryptonote for (auto & entry : white_list) { + if (!req.include_blocked && m_p2p.is_host_blocked(entry.adr, NULL)) + continue; if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) res.white_list.emplace_back(entry.id, entry.adr.as().ip(), entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash); @@ -1396,6 +1399,8 @@ namespace cryptonote for (auto & entry : gray_list) { + if (!req.include_blocked && m_p2p.is_host_blocked(entry.adr, NULL)) + continue; if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) res.gray_list.emplace_back(entry.id, entry.adr.as().ip(), entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash); @@ -1414,8 +1419,10 @@ namespace cryptonote { RPC_TRACKER(get_public_nodes); + COMMAND_RPC_GET_PEER_LIST::request peer_list_req; COMMAND_RPC_GET_PEER_LIST::response peer_list_res; - const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx); + peer_list_req.include_blocked = req.include_blocked; + const bool success = on_get_peer_list(peer_list_req, peer_list_res, ctx); res.status = peer_list_res.status; if (!success) { diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index f2d8467f0..9f7a6bf9e 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 3 -#define CORE_RPC_VERSION_MINOR 4 +#define CORE_RPC_VERSION_MINOR 5 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -1197,10 +1197,12 @@ namespace cryptonote struct request_t: public rpc_request_base { bool public_only; + bool include_blocked; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(public_only, true) + KV_SERIALIZE_OPT(include_blocked, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; @@ -1246,11 +1248,13 @@ namespace cryptonote { bool gray; bool white; + bool include_blocked; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(gray, false) KV_SERIALIZE_OPT(white, true) + KV_SERIALIZE_OPT(include_blocked, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ec51907f3..e69538f75 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -181,7 +181,7 @@ namespace const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; const char* USAGE_START_MINING("start_mining [] [bg_mining] [ignore_battery]"); - const char* USAGE_SET_DAEMON("set_daemon [:] [trusted|untrusted]"); + const char* USAGE_SET_DAEMON("set_daemon [:] [trusted|untrusted|this-is-probably-a-spy-node]"); const char* USAGE_SHOW_BALANCE("balance [detail]"); const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=[,[,...]]]"); const char* USAGE_PAYMENTS("payments [ ... ]"); @@ -2286,6 +2286,7 @@ bool simple_wallet::public_nodes(const std::vector &args) { fail_msg_writer() << tr("Error retrieving public node list: ") << e.what(); } + message_writer(console_color_red, true) << tr("Most of these nodes are probably spies. You should not use them unless connecting via Tor or I2P"); return true; } @@ -3222,7 +3223,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("set_daemon", boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_daemon, _1), tr(USAGE_SET_DAEMON), - tr("Set another daemon to connect to.")); + tr("Set another daemon to connect to. If it's not yours, it'll probably spy on you.")); m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::on_command, this, &simple_wallet::save_bc, _1), tr("Save the current blockchain data.")); @@ -5507,21 +5508,51 @@ bool simple_wallet::set_daemon(const std::vector& args) } else { daemon_url = args[0]; } - LOCK_IDLE_SCOPE(); - m_wallet->init(daemon_url); + epee::net_utils::http::url_content parsed{}; + const bool r = epee::net_utils::parse_url(daemon_url, parsed); + if (!r) + { + fail_msg_writer() << tr("Failed to parse address"); + return true; + } + + std::string trusted; if (args.size() == 2) { if (args[1] == "trusted") - m_wallet->set_trusted_daemon(true); + trusted = "trusted"; else if (args[1] == "untrusted") - m_wallet->set_trusted_daemon(false); + trusted = "untrusted"; + else if (args[1] == "this-is-probably-a-spy-node") + trusted = "this-is-probably-a-spy-node"; else { - fail_msg_writer() << tr("Expected trusted or untrusted, got ") << args[1] << ": assuming untrusted"; - m_wallet->set_trusted_daemon(false); + fail_msg_writer() << tr("Expected trusted, untrusted or this-is-probably-a-spy-node got ") << args[1]; + return true; } } + + if (!tools::is_privacy_preserving_network(parsed.host) && !tools::is_local_address(parsed.host)) + { + if (trusted == "untrusted" || trusted == "") + { + fail_msg_writer() << tr("This is not Tor/I2P address, and is not a trusted daemon."); + fail_msg_writer() << tr("Either use your own trusted node, connect via Tor or I2P, or pass this-is-probably-a-spy-node and be spied on."); + return true; + } + + if (parsed.schema != "https") + message_writer(console_color_red) << tr("Warning: connecting to a non-local daemon without SSL, passive adversaries will be able to spy on you."); + } + + LOCK_IDLE_SCOPE(); + m_wallet->init(daemon_url); + + if (!trusted.empty()) + { + m_wallet->set_trusted_daemon(trusted == "trusted"); + } else { m_wallet->set_trusted_daemon(false); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 020e888f1..3bcfa0ce8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -14149,6 +14149,7 @@ std::vector wallet2::get_public_nodes(bool white_only) req.white = true; req.gray = !white_only; + req.include_blocked = false; { const boost::lock_guard lock{m_daemon_rpc_mutex}; From 9e4e28b25c105a3437fcd0a8bc3bd62fde8b21bb Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 22 Dec 2020 02:11:52 +0000 Subject: [PATCH 10/44] ban lists may now include subnets --- src/daemon/command_parser_executor.cpp | 12 +++++++++--- src/p2p/net_node.inl | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 37b8b7cdb..92af920cc 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -694,13 +694,19 @@ bool t_command_parser_executor::ban(const std::vector& args) std::ifstream ifs(ban_list_path.string()); for (std::string line; std::getline(ifs, line); ) { + auto subnet = net::get_ipv4_subnet_address(line); + if (subnet) + { + ret &= m_executor.ban(subnet->str(), seconds); + continue; + } const expect parsed_addr = net::get_network_address(line, 0); - if (!parsed_addr) + if (parsed_addr) { - std::cout << "Invalid IP address: " << line << " - " << parsed_addr.error() << std::endl; + ret &= m_executor.ban(parsed_addr->host_str(), seconds); continue; } - ret &= m_executor.ban(parsed_addr->host_str(), seconds); + std::cout << "Invalid IP address or IPv4 subnet: " << line << std::endl; } return ret; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index ec7dc0472..4f47cf2b5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -489,13 +489,19 @@ namespace nodetool std::istringstream iss(banned_ips); for (std::string line; std::getline(iss, line); ) { + auto subnet = net::get_ipv4_subnet_address(line); + if (subnet) + { + block_subnet(*subnet, std::numeric_limits::max()); + continue; + } const expect parsed_addr = net::get_network_address(line, 0); - if (!parsed_addr) + if (parsed_addr) { - MERROR("Invalid IP address: " << line << " - " << parsed_addr.error()); + block_host(*parsed_addr, std::numeric_limits::max()); continue; } - block_host(*parsed_addr, std::numeric_limits::max()); + MERROR("Invalid IP address or IPv4 subnet: " << line); } } From 0afd50384f4a78b04613672ea82bd3751ebc2a64 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 25 Dec 2020 20:29:11 +0000 Subject: [PATCH 11/44] protocol: drop nodes if they claim new data but only give stale data Some joker is spending time actually doing this --- src/cryptonote_basic/connection_context.h | 3 ++- .../cryptonote_protocol_handler.h | 2 +- .../cryptonote_protocol_handler.inl | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index dfc59631f..a735ae06b 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -44,7 +44,7 @@ namespace cryptonote cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0), m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_rpc_credits_per_hash(0), m_anchor(false), m_score(0), - m_expect_response(0) {} + m_expect_response(0), m_num_requested(0) {} enum state { @@ -70,6 +70,7 @@ namespace cryptonote int32_t m_score; int m_expect_response; uint64_t m_expect_height; + size_t m_num_requested; }; inline std::string get_protocol_state_string(cryptonote_connection_context::state s) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 8f867eee6..28530f3e7 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -150,7 +150,7 @@ namespace cryptonote bool update_sync_search(); int try_add_next_blocks(cryptonote_connection_context &context); void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe); - void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; + size_t skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; bool request_txpool_complement(cryptonote_connection_context &context); void hit_score(cryptonote_connection_context &context, int32_t score); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index dc61dcefc..1b421abd4 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -409,6 +409,7 @@ namespace cryptonote ++context.m_callback_request_count; m_p2p->request_callback(context); MLOG_PEER_STATE("requesting callback"); + context.m_num_requested = 0; return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -1990,7 +1991,7 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template - void t_cryptonote_protocol_handler::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const + size_t t_cryptonote_protocol_handler::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const { // take out blocks we already have size_t skip = 0; @@ -2007,6 +2008,7 @@ skip: MDEBUG(context << "skipping " << skip << "/" << context.m_needed_objects.size() << " blocks"); context.m_needed_objects = std::vector>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); } + return skip; } //------------------------------------------------------------------------------------------------------------------------ template @@ -2060,7 +2062,11 @@ skip: const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD; bool queue_proceed = nspans < BLOCK_QUEUE_NSPANS_THRESHOLD || size < block_queue_size_threshold; // get rid of blocks we already requested, or already have - skip_unneeded_hashes(context, true); + if (skip_unneeded_hashes(context, true) && context.m_needed_objects.empty() && context.m_num_requested == 0) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } uint64_t next_needed_height = m_block_queue.get_next_needed_height(bc_height); uint64_t next_block_height; if (context.m_needed_objects.empty()) @@ -2196,7 +2202,11 @@ skip: context.m_last_response_height = 0; goto skip; } - skip_unneeded_hashes(context, false); + if (skip_unneeded_hashes(context, false) && context.m_needed_objects.empty() && context.m_num_requested == 0) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; static const uint64_t bp_fork_height = m_core.get_earliest_ideal_height_for_version(HF_VERSION_SMALLER_BP); @@ -2293,6 +2303,7 @@ skip: << "/" << tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) << ", ours " << tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()) << ", peer stripe " << tools::get_pruning_stripe(context.m_pruning_seed)); + context.m_num_requested += req.blocks.size(); post_notify(req, context); MLOG_PEER_STATE("requesting objects"); return true; @@ -2607,6 +2618,7 @@ skip: if (arg.total_height > m_core.get_target_blockchain_height()) m_core.set_target_blockchain_height(arg.total_height); + context.m_num_requested = 0; return 1; } //------------------------------------------------------------------------------------------------------------------------ From a7e395577231ebc13ce8e6aa826bbcea15db50ab Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 25 Dec 2020 17:20:24 +0000 Subject: [PATCH 12/44] portable_storage: add some sanity checks on data size especially when allocated size is >> serialized data size --- .../storages/portable_storage_from_bin.h | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index b39dc7c92..20c2bc124 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -37,11 +37,32 @@ #else #define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 #endif +#define EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL 65536 +#define EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL 65536 namespace epee { namespace serialization { + template + struct ps_min_bytes { + static constexpr const size_t strict = 4096; // actual low bound + static constexpr const size_t rough = 4096; // when we want to be stricter for DoS prevention + }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 8, rough = 8; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 8, rough = 8; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 4, rough = 4; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 4, rough = 4; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 2, rough = 2; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 2, rough = 2; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 8, rough = 8; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 2, rough = 16; }; + template<> struct ps_min_bytes
{ static constexpr const size_t strict = 1, rough = 256; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 128; }; + struct throwable_buffer_reader { throwable_buffer_reader(const void* ptr, size_t sz); @@ -61,6 +82,8 @@ namespace epee void read(section& sec); void read(std::string& str); void read(array_entry &ae); + template + size_t min_bytes() const; private: struct recursuion_limitation_guard { @@ -81,6 +104,8 @@ namespace epee const uint8_t* m_ptr; size_t m_count; size_t m_recursion_count; + size_t m_objects; + size_t m_array_elements; }; inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) @@ -92,6 +117,8 @@ namespace epee m_ptr = (uint8_t*)ptr; m_count = sz; m_recursion_count = 0; + m_objects = 0; + m_array_elements = 0; } inline void throwable_buffer_reader::read(void* target, size_t count) @@ -138,7 +165,12 @@ namespace epee //for pod types array_entry_t sa; size_t size = read_varint(); - CHECK_AND_ASSERT_THROW_MES(size <= m_count, "Size sanity check failed"); + CHECK_AND_ASSERT_THROW_MES(size < EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL - m_array_elements, "Too many array elements"); + m_array_elements += size; + CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes::strict, "Size sanity check failed"); + const size_t threshold = 16384 - std::min(m_array_elements, 16384); + CHECK_AND_ASSERT_THROW_MES(size <= threshold || size <= m_count / ps_min_bytes::rough, "Large array stricter size sanity check failed"); + sa.reserve(size); //TODO: add some optimization here later while(size--) @@ -263,6 +295,8 @@ namespace epee RECURSION_LIMITATION(); sec.m_entries.clear(); size_t count = read_varint(); + CHECK_AND_ASSERT_THROW_MES(count < EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL - m_objects, "Too many objects"); + m_objects += count; while(count--) { //read section name string From 4efba60bc108acffe572f61fc0d1b7e74811af51 Mon Sep 17 00:00:00 2001 From: luigi1111 Date: Sun, 27 Dec 2020 00:16:56 -0500 Subject: [PATCH 13/44] Revert "Reject existing claimed blocks in sync mode" --- .../cryptonote_protocol_handler.inl | 37 ++++++++++--------- tests/core_proxy/core_proxy.h | 1 - tests/unit_tests/node_server.cpp | 1 - 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 1b421abd4..f9606ac9b 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2309,16 +2309,30 @@ skip: return true; } - // we can do nothing, so drop this peer to make room for others unless we think we've downloaded all we need - const uint64_t blockchain_height = m_core.get_current_blockchain_height(); - if (std::max(blockchain_height, m_block_queue.get_next_needed_height(blockchain_height)) >= m_core.get_target_blockchain_height()) + // if we're still around, we might be at a point where the peer is pruned, so we could either + // drop it to make space for other peers, or ask for a span further down the line + const uint32_t next_stripe = get_next_needed_pruning_stripe().first; + const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); + const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()); + if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe) { + // at this point, we have to either close the connection, or start getting blocks past the + // current point, or become dormant + MDEBUG(context << "this peer is pruned at seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << + ", next stripe needed is " << next_stripe); + if (!context.m_is_income) + { + if (should_drop_connection(context, next_stripe)) + { + m_p2p->add_used_stripe_peer(context); + return false; // drop outgoing connections + } + } + // we'll get back stuck waiting for the go ahead context.m_state = cryptonote_connection_context::state_normal; MLOG_PEER_STATE("Nothing to do for now, switching to normal state"); return true; } - MLOG_PEER_STATE("We can download nothing from this peer, dropping"); - return false; } skip: @@ -2559,8 +2573,6 @@ skip: } std::unordered_set hashes; - uint64_t height = arg.start_height; - const uint64_t blockchain_height = m_core.get_current_blockchain_height(); for (const auto &h: arg.m_block_ids) { if (!hashes.insert(h).second) @@ -2569,17 +2581,6 @@ skip: drop_connection(context, true, false); return 1; } - if (height < blockchain_height) - { - const crypto::hash block_in_chain = m_core.get_block_id_by_height(height); - if ((height < context.m_expect_height - 1 && block_in_chain == h) || (height == context.m_expect_height - 1 && block_in_chain != h)) - { - LOG_ERROR_CCONTEXT("sent existing block " << h << " at height " << height << ", expected height was " << context.m_expect_height << ", dropping connection"); - drop_connection(context, true, false); - return 1; - } - } - ++height; } uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights); diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index ebc3a89c2..ecfcc18ae 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -112,6 +112,5 @@ namespace tests bool prune_blockchain(uint32_t pruning_seed) const { return true; } bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } - crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; } }; } diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 0569d3748..0191e5aa4 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -93,7 +93,6 @@ public: bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; } bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } - crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; } void stop() {} }; From 50e25e681c97ec684928d1faaf06b8dba665143e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Dec 2020 21:28:55 +0000 Subject: [PATCH 14/44] cryptonote_basic: guess what got lost porting patches to branches again --- src/cryptonote_basic/connection_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index a735ae06b..9e012f8f5 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -44,7 +44,7 @@ namespace cryptonote cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0), m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_rpc_credits_per_hash(0), m_anchor(false), m_score(0), - m_expect_response(0), m_num_requested(0) {} + m_expect_response(0), m_expect_height(0), m_num_requested(0) {} enum state { From 63381702d84c1dae3372f1740daf493aca87eaa6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Dec 2020 12:44:38 +0000 Subject: [PATCH 15/44] portable_storage: remove overly aggressive cutoff --- contrib/epee/include/storages/portable_storage_from_bin.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 20c2bc124..f92546823 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -168,8 +168,6 @@ namespace epee CHECK_AND_ASSERT_THROW_MES(size < EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL - m_array_elements, "Too many array elements"); m_array_elements += size; CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes::strict, "Size sanity check failed"); - const size_t threshold = 16384 - std::min(m_array_elements, 16384); - CHECK_AND_ASSERT_THROW_MES(size <= threshold || size <= m_count / ps_min_bytes::rough, "Large array stricter size sanity check failed"); sa.reserve(size); //TODO: add some optimization here later From b56a9f5bade255593fca5f0459c5e0823260efda Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 27 Dec 2020 14:13:26 +0000 Subject: [PATCH 16/44] protocol: fix false positives dropping peers it'd trigger on reorgs --- .../cryptonote_protocol_handler.inl | 44 +++++++++---------- tests/core_proxy/core_proxy.h | 1 + tests/unit_tests/node_server.cpp | 1 + 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index f9606ac9b..32882471d 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2064,8 +2064,14 @@ skip: // get rid of blocks we already requested, or already have if (skip_unneeded_hashes(context, true) && context.m_needed_objects.empty() && context.m_num_requested == 0) { - MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); - return false; + if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(bc_height)) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } + MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done"); + context.m_state = cryptonote_connection_context::state_normal; + return true; } uint64_t next_needed_height = m_block_queue.get_next_needed_height(bc_height); uint64_t next_block_height; @@ -2204,8 +2210,14 @@ skip: } if (skip_unneeded_hashes(context, false) && context.m_needed_objects.empty() && context.m_num_requested == 0) { - MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); - return false; + if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(m_core.get_current_blockchain_height())) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } + MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done"); + context.m_state = cryptonote_connection_context::state_normal; + return true; } const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; @@ -2309,30 +2321,16 @@ skip: return true; } - // if we're still around, we might be at a point where the peer is pruned, so we could either - // drop it to make space for other peers, or ask for a span further down the line - const uint32_t next_stripe = get_next_needed_pruning_stripe().first; - const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); - const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()); - if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe) + // we can do nothing, so drop this peer to make room for others unless we think we've downloaded all we need + const uint64_t blockchain_height = m_core.get_current_blockchain_height(); + if (std::max(blockchain_height, m_block_queue.get_next_needed_height(blockchain_height)) >= m_core.get_target_blockchain_height()) { - // at this point, we have to either close the connection, or start getting blocks past the - // current point, or become dormant - MDEBUG(context << "this peer is pruned at seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << - ", next stripe needed is " << next_stripe); - if (!context.m_is_income) - { - if (should_drop_connection(context, next_stripe)) - { - m_p2p->add_used_stripe_peer(context); - return false; // drop outgoing connections - } - } - // we'll get back stuck waiting for the go ahead context.m_state = cryptonote_connection_context::state_normal; MLOG_PEER_STATE("Nothing to do for now, switching to normal state"); return true; } + MLOG_PEER_STATE("We can download nothing from this peer, dropping"); + return false; } skip: diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index ecfcc18ae..ebc3a89c2 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -112,5 +112,6 @@ namespace tests bool prune_blockchain(uint32_t pruning_seed) const { return true; } bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } + crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; } }; } diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 0191e5aa4..0569d3748 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -93,6 +93,7 @@ public: bool has_block_weights(uint64_t height, uint64_t nblocks) const { return false; } bool get_txpool_complement(const std::vector &hashes, std::vector &txes) { return false; } bool get_pool_transaction_hashes(std::vector& txs, bool include_unrelayed_txes = true) const { return false; } + crypto::hash get_block_id_by_height(uint64_t height) const { return crypto::null_hash; } void stop() {} }; From 6de11d071802ff8b1091afe4c29c70535da66fce Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 26 Dec 2020 14:14:09 +0000 Subject: [PATCH 17/44] epee: fix some issues using connections after shutdown --- .../epee/include/net/abstract_tcp_server2.inl | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index cbacd118c..44a7899f9 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -334,6 +334,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) TRY_ENTRY(); //_info("[sock " << socket().native_handle() << "] Async read calledback."); + if (m_was_shutdown) + return; + if (!e) { double current_speed_down; @@ -360,6 +363,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in ); delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred ); } + + if (m_was_shutdown) + return; delay *= 0.5; long int ms = (long int)(delay * 100); @@ -431,6 +437,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) std::size_t bytes_transferred) { TRY_ENTRY(); + + if (m_was_shutdown) return; + if (e) { // offload the error case @@ -438,12 +447,15 @@ PRAGMA_WARNING_DISABLE_VS(4355) return; } - reset_timer(get_timeout_from_bytes_read(bytes_transferred), false); - - buffer_ssl_init_fill += bytes_transferred; - if (buffer_ssl_init_fill <= get_ssl_magic_size()) + buffer_ssl_init_fill = bytes_transferred; + MTRACE("we now have " << buffer_ssl_init_fill << "/" << get_ssl_magic_size() << " bytes needed to detect SSL"); + if (buffer_ssl_init_fill < get_ssl_magic_size()) { - socket().async_receive(boost::asio::buffer(buffer_.data() + buffer_ssl_init_fill, buffer_.size() - buffer_ssl_init_fill), + // don't busy loop on this, ideally we'd want to queue a "async_receive_if_new_data" but there doesn't + // seem to be something like that in boost if we want to just peek at the data, so we'd need to copy and + // have a bit more code just for this. Though I'm just seeing async_read_until which might just work. + epee::misc_utils::sleep_no_w(100); + socket().async_receive(boost::asio::buffer(buffer_.data(), buffer_.size()), boost::asio::socket_base::message_peek, strand_.wrap( boost::bind(&connection::handle_receive, connection::shared_from_this(), @@ -651,6 +663,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) ); m_send_que_lock.lock(); _dbg1("sleep for queue: " << ms); + if (m_was_shutdown) + return false; if (retry > retry_limit) { MWARNING("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); @@ -748,7 +762,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) template void connection::reset_timer(boost::posix_time::milliseconds ms, bool add) { - if (ms.total_milliseconds() < 0) + const auto tms = ms.total_milliseconds(); + if (tms < 0 || (add && tms == 0)) { MWARNING("Ignoring negative timeout " << ms); return; From 1ec6d5ccda31747f76c4a144615bada0e3dbfa28 Mon Sep 17 00:00:00 2001 From: anon Date: Sun, 27 Dec 2020 01:55:12 +0000 Subject: [PATCH 18/44] ssl: buffered handshake detection --- .../epee/include/net/abstract_tcp_server2.inl | 17 ++++++++--------- contrib/epee/include/net/connection_basic.hpp | 4 ++-- contrib/epee/include/net/net_helper.h | 2 +- contrib/epee/include/net/net_ssl.h | 1 + contrib/epee/src/net_ssl.cpp | 3 ++- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 44a7899f9..cb1388f3b 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -207,7 +207,6 @@ PRAGMA_WARNING_DISABLE_VS(4355) buffer_ssl_init_fill = 0; if (is_income && m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) socket().async_receive(boost::asio::buffer(buffer_), - boost::asio::socket_base::message_peek, strand_.wrap( std::bind(&connection::handle_receive, self, std::placeholders::_1, @@ -447,16 +446,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) return; } - buffer_ssl_init_fill = bytes_transferred; + buffer_ssl_init_fill += bytes_transferred; MTRACE("we now have " << buffer_ssl_init_fill << "/" << get_ssl_magic_size() << " bytes needed to detect SSL"); if (buffer_ssl_init_fill < get_ssl_magic_size()) { - // don't busy loop on this, ideally we'd want to queue a "async_receive_if_new_data" but there doesn't - // seem to be something like that in boost if we want to just peek at the data, so we'd need to copy and - // have a bit more code just for this. Though I'm just seeing async_read_until which might just work. - epee::misc_utils::sleep_no_w(100); - socket().async_receive(boost::asio::buffer(buffer_.data(), buffer_.size()), - boost::asio::socket_base::message_peek, + socket().async_receive(boost::asio::buffer(buffer_.data() + buffer_ssl_init_fill, buffer_.size() - buffer_ssl_init_fill), strand_.wrap( boost::bind(&connection::handle_receive, connection::shared_from_this(), boost::asio::placeholders::error, @@ -482,7 +476,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) { // Handshake - if (!handshake(boost::asio::ssl::stream_base::server)) + if (!handshake(boost::asio::ssl::stream_base::server, boost::asio::const_buffer(buffer_.data(), buffer_ssl_init_fill))) { MERROR("SSL handshake failed"); boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); @@ -497,6 +491,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) return; } } + else + { + handle_read(e, buffer_ssl_init_fill); + return; + } async_read_some(boost::asio::buffer(buffer_), strand_.wrap( diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 90303a785..23873f65b 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -132,10 +132,10 @@ class connection_basic { // not-templated base class for rapid developmet of som ssl_support_t get_ssl_support() const { return m_ssl_support; } void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; } - bool handshake(boost::asio::ssl::stream_base::handshake_type type) + bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {}) { //m_state != nullptr verified in constructor - return m_state->ssl_options().handshake(socket_, type); + return m_state->ssl_options().handshake(socket_, type, buffer); } template diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 508c79d76..a1f44cab0 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -179,7 +179,7 @@ namespace net_utils // SSL Options if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { - if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, addr, timeout)) + if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout)) { if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h index 643b2c486..1b1577e77 100644 --- a/contrib/epee/include/net/net_ssl.h +++ b/contrib/epee/include/net/net_ssl.h @@ -132,6 +132,7 @@ namespace net_utils bool handshake( boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, + boost::asio::const_buffer buffer = {}, const std::string& host = {}, std::chrono::milliseconds timeout = std::chrono::seconds(15)) const; }; diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index a09e82771..6ed27efa9 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -473,6 +473,7 @@ bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const bool ssl_options_t::handshake( boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, + boost::asio::const_buffer buffer, const std::string& host, std::chrono::milliseconds timeout) const { @@ -530,7 +531,7 @@ bool ssl_options_t::handshake( }); boost::system::error_code ec = boost::asio::error::would_block; - socket.async_handshake(type, boost::lambda::var(ec) = boost::lambda::_1); + socket.async_handshake(type, boost::asio::buffer(buffer), boost::lambda::var(ec) = boost::lambda::_1); if (io_service.stopped()) { io_service.reset(); From 58404d389ca95c18c51d8855e7cfce3b95e76e04 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 28 Dec 2020 21:04:11 +0000 Subject: [PATCH 19/44] portable_storage: remove array element limit some people don't want it --- contrib/epee/include/storages/portable_storage_from_bin.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index f92546823..eb0eed235 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -38,7 +38,6 @@ #define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 #endif #define EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL 65536 -#define EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL 65536 namespace epee { @@ -105,7 +104,6 @@ namespace epee size_t m_count; size_t m_recursion_count; size_t m_objects; - size_t m_array_elements; }; inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) @@ -118,7 +116,6 @@ namespace epee m_count = sz; m_recursion_count = 0; m_objects = 0; - m_array_elements = 0; } inline void throwable_buffer_reader::read(void* target, size_t count) @@ -165,8 +162,6 @@ namespace epee //for pod types array_entry_t sa; size_t size = read_varint(); - CHECK_AND_ASSERT_THROW_MES(size < EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL - m_array_elements, "Too many array elements"); - m_array_elements += size; CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes::strict, "Size sanity check failed"); sa.reserve(size); From 03b8228b355f26e339eb3c3ea4f66d231faf2a07 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Dec 2020 04:31:58 +0000 Subject: [PATCH 20/44] rpc: limit the number of txes for get_blocks.bin --- src/blockchain_db/blockchain_db.h | 9 ++++++--- src/blockchain_db/lmdb/db_lmdb.cpp | 11 ++++++++--- src/blockchain_db/lmdb/db_lmdb.h | 2 +- src/blockchain_db/testdb.h | 2 +- src/cryptonote_config.h | 3 ++- src/cryptonote_core/blockchain.cpp | 6 +++--- src/cryptonote_core/blockchain.h | 5 +++-- src/cryptonote_core/cryptonote_core.cpp | 4 ++-- src/cryptonote_core/cryptonote_core.h | 2 +- src/rpc/core_rpc_server.cpp | 8 ++++---- src/rpc/daemon_handler.cpp | 2 +- 11 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index abebb52b4..74bd72332 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1312,17 +1312,20 @@ public: * height. The number of blocks returned is variable, based on the max_size passed. * * @param start_height the height of the first block - * @param min_count the minimum number of blocks to return, if they exist - * @param max_count the maximum number of blocks to return + * @param min_block_count the minimum number of blocks to return, if they exist + * @param max_block_count the maximum number of blocks to return + * @param max_tx_count the maximum number of txes to return * @param max_size the maximum size of block/transaction data to return (will be exceeded by one blocks's worth at most, if min_count is met) * @param blocks the returned block/transaction data * @param pruned whether to return full or pruned tx data * @param skip_coinbase whether to return or skip coinbase transactions (they're in blocks regardless) * @param get_miner_tx_hash whether to calculate and return the miner (coinbase) tx hash * + * The call will return at least min_block_count if possible, even if this contravenes max_tx_count + * * @return true iff the blocks and transactions were found */ - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0; + virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0; /** * @brief fetches the prunable transaction blob with the given hash diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 6a02b3bdb..e279eb0a4 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3171,7 +3171,7 @@ bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t coun return true; } -bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const +bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -3185,14 +3185,15 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si RCURSOR(txs_prunable); } - blocks.reserve(std::min(max_count, 10000)); // guard against very large max count if only checking bytes + blocks.reserve(std::min(max_block_count, 10000)); // guard against very large max count if only checking bytes const uint64_t blockchain_height = height(); uint64_t size = 0; + size_t num_txes = 0; MDB_val_copy key(start_height); MDB_val k, v, val_tx_id; uint64_t tx_id = ~0; MDB_cursor_op op = MDB_SET; - for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_count && (size < max_size || blocks.size() < min_count); ++h) + for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_block_count && (size < max_size || blocks.size() < min_block_count); ++h) { MDB_cursor_op op = h == start_height ? MDB_SET : MDB_NEXT; int result = mdb_cursor_get(m_cur_blocks, &key, &v, op); @@ -3243,6 +3244,7 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si op = MDB_NEXT; current_block.second.reserve(b.tx_hashes.size()); + num_txes += b.tx_hashes.size() + (skip_coinbase ? 0 : 1); for (const auto &tx_hash: b.tx_hashes) { // get pruned data @@ -3262,6 +3264,9 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si current_block.second.push_back(std::make_pair(tx_hash, std::move(tx_blob))); size += current_block.second.back().second.size(); } + + if (blocks.size() >= min_block_count && num_txes >= max_tx_count) + break; } TXN_POSTFIX_RDONLY(); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 568882ae5..0e6d70039 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -257,7 +257,7 @@ public: virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const; - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const; + virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const; virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 9e74b33f1..e36026e07 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -70,7 +70,7 @@ public: virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const override { return false; } - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; } + virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; } virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; } virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 8426ebb32..7d7fb84c6 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -128,7 +128,8 @@ #define CRYPTONOTE_MAX_FRAGMENTS 20 // ~20 * NOISE_BYTES max payload size for covert/noise send -#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 +#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000 +#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000 #define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 #define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e470b39bb..d6fd2ae17 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2780,7 +2780,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height ), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2805,8 +2805,8 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons db_rtxn_guard rtxn_guard(m_db); total_height = get_current_blockchain_height(); - blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); - CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), + blocks.reserve(std::min(std::min(max_block_count, (size_t)10000), (size_t)(total_height - start_height))); + CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_block_count, max_tx_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), false, "Error getting blocks"); return true; diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index a8e251c71..07238b719 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -465,11 +465,12 @@ namespace cryptonote * @param total_height return-by-reference our current blockchain height * @param start_height return-by-reference the height of the first block returned * @param pruned whether to return full or pruned tx blobs - * @param max_count the max number of blocks to get + * @param max_block_count the max number of blocks to get + * @param max_tx_count the max number of txes to get (it can get overshot by the last block's number of txes minus 1) * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index bbdaf7efb..66fa7de54 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1446,9 +1446,9 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const { - return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count); + return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_block_count, max_tx_count); } //----------------------------------------------------------------------------------------------- bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 37981478c..24d0f9e76 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -564,7 +564,7 @@ namespace cryptonote * * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const; /** * @copydoc Blockchain::get_tx_outputs_gindexs diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 69a228916..9faab0460 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -566,12 +566,12 @@ namespace cryptonote } } - size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT; + size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT; if (m_rpc_payment) { max_blocks = res.credits / COST_PER_BLOCK; - if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT) - max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT; + if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT) + max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT; if (max_blocks == 0) { res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED; @@ -580,7 +580,7 @@ namespace cryptonote } std::vector, std::vector > > > bs; - if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks)) + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT)) { res.status = "Failed"; add_host_fail(ctx); diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index e256322cb..cdfc363a9 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -128,7 +128,7 @@ namespace rpc { std::vector, std::vector > > > blocks; - if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT)) { res.status = Message::STATUS_FAILED; res.error_details = "core::find_blockchain_supplement() returned false"; From b4206cea5aae0caac940b33d8afc8c0a2ae5915b Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Tue, 29 Dec 2020 19:58:53 -0500 Subject: [PATCH 21/44] Add aggressive restrictions to pre-handshake p2p buffer limit --- contrib/epee/include/net/levin_base.h | 3 +- .../net/levin_protocol_handler_async.h | 32 +++++++++++++------ src/cryptonote_basic/connection_context.h | 2 ++ .../cryptonote_protocol_handler.inl | 1 + src/cryptonote_protocol/levin_notify.cpp | 2 +- src/p2p/net_node.h | 3 ++ tests/fuzz/levin.cpp | 2 ++ tests/net_load_tests/net_load_tests.h | 2 ++ .../epee_levin_protocol_handler_async.cpp | 3 ++ tests/unit_tests/levin.cpp | 1 + 10 files changed, 40 insertions(+), 11 deletions(-) diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h index ad561c5b6..fce6d4b7e 100644 --- a/contrib/epee/include/net/levin_base.h +++ b/contrib/epee/include/net/levin_base.h @@ -72,7 +72,8 @@ namespace levin #define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0 -#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default +#define LEVIN_INITIAL_MAX_PACKET_SIZE 256*1024 // 256 KiB before handshake +#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default after handshake #define LEVIN_PACKET_REQUEST 0x00000001 #define LEVIN_PACKET_RESPONSE 0x00000002 diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 8cb2be3e1..9e10747c2 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -84,7 +84,8 @@ class async_protocol_handler_config public: typedef t_connection_context connection_context; - uint64_t m_max_packet_size; + uint64_t m_initial_max_packet_size; + uint64_t m_max_packet_size; uint64_t m_invoke_timeout; int invoke(int command, const epee::span in_buff, std::string& buff_out, boost::uuids::uuid connection_id); @@ -105,7 +106,7 @@ public: size_t get_in_connections_count(); void set_handler(levin_commands_handler* handler, void (*destroy)(levin_commands_handler*) = NULL); - async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_initial_max_packet_size(LEVIN_INITIAL_MAX_PACKET_SIZE), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) {} ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); @@ -162,6 +163,7 @@ public: net_utils::i_service_endpoint* m_pservice_endpoint; config_type& m_config; t_connection_context& m_connection_context; + std::atomic m_max_packet_size; net_utils::buffer m_cache_in_buffer; stream_state m_state; @@ -289,7 +291,8 @@ public: m_current_head(bucket_head2()), m_pservice_endpoint(psnd_hndlr), m_config(config), - m_connection_context(conn_context), + m_connection_context(conn_context), + m_max_packet_size(config.m_initial_max_packet_size), m_cache_in_buffer(4 * 1024), m_state(stream_state_head) { @@ -399,13 +402,14 @@ public: } // these should never fail, but do runtime check for safety - CHECK_AND_ASSERT_MES(m_config.m_max_packet_size >= m_cache_in_buffer.size(), false, "Bad m_cache_in_buffer.size()"); - CHECK_AND_ASSERT_MES(m_config.m_max_packet_size - m_cache_in_buffer.size() >= m_fragment_buffer.size(), false, "Bad m_cache_in_buffer.size() + m_fragment_buffer.size()"); + const uint64_t max_packet_size = m_max_packet_size; + CHECK_AND_ASSERT_MES(max_packet_size >= m_cache_in_buffer.size(), false, "Bad m_cache_in_buffer.size()"); + CHECK_AND_ASSERT_MES(max_packet_size - m_cache_in_buffer.size() >= m_fragment_buffer.size(), false, "Bad m_cache_in_buffer.size() + m_fragment_buffer.size()"); // flipped to subtraction; prevent overflow since m_max_packet_size is variable and public - if(cb > m_config.m_max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size()) + if(cb > max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size()) { - MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << max_packet_size << ", packet received " << m_cache_in_buffer.size() + cb << ", connection will be closed."); return false; @@ -519,6 +523,10 @@ public: m_current_head.m_command, buff_to_invoke, return_buff, m_connection_context ); + // peer_id remains unset if dropped + if (m_current_head.m_command == m_connection_context.handshake_command() && m_connection_context.handshake_complete()) + m_max_packet_size = m_config.m_max_packet_size; + bucket_head2 head = make_header(m_current_head.m_command, return_buff.size(), LEVIN_PACKET_RESPONSE, false); head.m_return_code = SWAP32LE(return_code); @@ -576,9 +584,9 @@ public: m_cache_in_buffer.erase(sizeof(bucket_head2)); m_state = stream_state_body; m_oponent_protocol_ver = m_current_head.m_protocol_version; - if(m_current_head.m_cb > m_config.m_max_packet_size) + if(m_current_head.m_cb > max_packet_size) { - LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << max_packet_size << ", packet header received " << m_current_head.m_cb << ", connection will be closed."); return false; @@ -633,6 +641,9 @@ public: boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock); + if (command == m_connection_context.handshake_command()) + m_max_packet_size = m_config.m_max_packet_size; + if(!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true)) { LOG_ERROR_CC(m_connection_context, "Failed to do_send"); @@ -674,6 +685,9 @@ public: boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); + if (command == m_connection_context.handshake_command()) + m_max_packet_size = m_config.m_max_packet_size; + if (!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true)) { LOG_ERROR_CC(m_connection_context, "Failed to send request"); diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 9e012f8f5..e5c00d4f3 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -55,6 +55,8 @@ namespace cryptonote state_normal }; + bool handshake_complete() const noexcept { return m_state != state_before_handshake; } + state m_state; std::vector> m_needed_objects; std::unordered_set m_requested_objects; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 32882471d..118578d44 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2634,6 +2634,7 @@ skip: std::vector> fullConnections, fluffyConnections; m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags) { + // peer_id also filters out connections before handshake if (peer_id && exclude_context.m_connection_id != context.m_connection_id && context.m_remote_address.get_zone() == epee::net_utils::zone::public_) { if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS)) diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 318bcf4e1..aca3acd78 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -445,7 +445,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->nzone == epee::net_utils::zone::public_ || !context.m_is_income)) + if (context.handshake_complete() && 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()); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 915ee4ea7..6e8cbe042 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -118,6 +118,8 @@ namespace nodetool m_in_timedsync(false) {} + static constexpr int handshake_command() noexcept { return 1001; } + std::vector fluff_txs; std::chrono::steady_clock::time_point flush_time; peerid_type peer_id; @@ -139,6 +141,7 @@ namespace nodetool typedef COMMAND_HANDSHAKE_T COMMAND_HANDSHAKE; typedef COMMAND_TIMED_SYNC_T COMMAND_TIMED_SYNC; + static_assert(p2p_connection_context::handshake_command() == COMMAND_HANDSHAKE::ID, "invalid handshake command id"); typedef epee::net_utils::boosted_tcp_server> net_server; diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index b090c350b..079ea7dae 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -52,6 +52,8 @@ namespace struct test_levin_connection_context : public epee::net_utils::connection_context_base { + static constexpr int handshake_command() noexcept { return 1001; } + static constexpr bool handshake_complete() noexcept { return true; } }; typedef epee::levin::async_protocol_handler_config test_levin_protocol_handler_config; diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index e7e0ee247..532017efd 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -48,6 +48,8 @@ namespace net_load_tests struct test_connection_context : epee::net_utils::connection_context_base { test_connection_context(): epee::net_utils::connection_context_base(boost::uuids::nil_uuid(), {}, false, false), m_closed(false) {} + static constexpr int handshake_command() noexcept { return 1001; } + static constexpr bool handshake_complete() noexcept { return true; } volatile bool m_closed; }; diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index ab6791324..3dbb36171 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -43,6 +43,8 @@ namespace { struct test_levin_connection_context : public epee::net_utils::connection_context_base { + static constexpr int handshake_command() noexcept { return 1001; } + static constexpr bool handshake_complete() noexcept { return true; } }; typedef epee::levin::async_protocol_handler_config test_levin_protocol_handler_config; @@ -193,6 +195,7 @@ namespace { m_handler_config.set_handler(m_pcommands_handler, [](epee::levin::levin_commands_handler *handler) { delete handler; }); m_handler_config.m_invoke_timeout = invoke_timeout; + m_handler_config.m_initial_max_packet_size = max_packet_size; m_handler_config.m_max_packet_size = max_packet_size; } diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp index 2ad149afe..dc854ef0c 100644 --- a/tests/unit_tests/levin.cpp +++ b/tests/unit_tests/levin.cpp @@ -178,6 +178,7 @@ namespace { using base_type = epee::net_utils::connection_context_base; static_cast(context_) = base_type{random_generator(), {}, is_incoming, false}; + context_.m_state = cryptonote::cryptonote_connection_context::state_normal; handler_.after_init_connection(); } From 45a462eb91a16bb2ba39bd6bf10b2d5db1dae068 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 30 Dec 2020 14:14:52 +0000 Subject: [PATCH 22/44] protocol: don't reset last request time on an idle timer this prevents losing time of "idle time" for a peer, which could otherwise be reset by another timed sync command from the peer --- src/cryptonote_protocol/cryptonote_protocol_handler.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 118578d44..318d2638b 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -137,7 +137,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); --context.m_callback_request_count; - if(context.m_state == cryptonote_connection_context::state_synchronizing) + if(context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time == boost::posix_time::not_a_date_time) { NOTIFY_REQUEST_CHAIN::request r = {}; context.m_needed_objects.clear(); From 31dc71afafe0e0ae7c0a5248a76ef9248b1de966 Mon Sep 17 00:00:00 2001 From: moneromooo Date: Thu, 31 Dec 2020 02:05:39 +0000 Subject: [PATCH 23/44] p2p: fix deadlock banning while updating peer lists --- src/p2p/net_node.inl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 4f47cf2b5..ef6e2058c 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -2111,6 +2111,7 @@ namespace nodetool LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size()); LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr) && is_remote_host_allowed(pe.adr); }); From 6375b913d9409b110eb1b4a509bcc968211e823d Mon Sep 17 00:00:00 2001 From: moneromooo Date: Thu, 31 Dec 2020 16:27:27 +0000 Subject: [PATCH 24/44] portable_storage: check object limit where appropriate also fix pedantic off by one in check --- contrib/epee/include/storages/portable_storage_from_bin.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index eb0eed235..1d6a09c41 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -163,6 +163,11 @@ namespace epee array_entry_t sa; size_t size = read_varint(); CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes::strict, "Size sanity check failed"); + if (std::is_same()) + { + CHECK_AND_ASSERT_THROW_MES(size <= EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL - m_objects, "Too many objects"); + m_objects += size; + } sa.reserve(size); //TODO: add some optimization here later @@ -288,7 +293,7 @@ namespace epee RECURSION_LIMITATION(); sec.m_entries.clear(); size_t count = read_varint(); - CHECK_AND_ASSERT_THROW_MES(count < EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL - m_objects, "Too many objects"); + CHECK_AND_ASSERT_THROW_MES(count <= EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL - m_objects, "Too many objects"); m_objects += count; while(count--) { From c44dbc582f179ba69d53e7cb81973fcd4ded86e9 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 31 Dec 2020 21:12:30 +0000 Subject: [PATCH 25/44] protocol: more sanity checks in new chain block hashes --- src/cryptonote_core/blockchain.cpp | 16 ++++- src/cryptonote_core/blockchain.h | 4 +- src/cryptonote_core/cryptonote_core.cpp | 9 ++- src/cryptonote_core/cryptonote_core.h | 5 +- .../cryptonote_protocol_handler.inl | 64 +++++++++++++++---- tests/core_proxy/core_proxy.cpp | 7 +- tests/core_proxy/core_proxy.h | 3 +- tests/unit_tests/node_server.cpp | 3 +- 8 files changed, 90 insertions(+), 21 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d6fd2ae17..b3cc0244d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2837,32 +2837,44 @@ void Blockchain::flush_invalid_blocks() m_invalid_blocks.clear(); } //------------------------------------------------------------------ -bool Blockchain::have_block(const crypto::hash& id) const +bool Blockchain::have_block_unlocked(const crypto::hash& id, int *where) const { + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); if(m_db->block_exists(id)) { LOG_PRINT_L2("block " << id << " found in main chain"); + if (where) *where = HAVE_BLOCK_MAIN_CHAIN; return true; } if(m_db->get_alt_block(id, NULL, NULL)) { LOG_PRINT_L2("block " << id << " found in alternative chains"); + if (where) *where = HAVE_BLOCK_ALT_CHAIN; return true; } if(m_invalid_blocks.count(id)) { LOG_PRINT_L2("block " << id << " found in m_invalid_blocks"); + if (where) *where = HAVE_BLOCK_INVALID; return true; } return false; } //------------------------------------------------------------------ +bool Blockchain::have_block(const crypto::hash& id, int *where) const +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return have_block_unlocked(id, where); +} +//------------------------------------------------------------------ bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc, bool notify/* = true*/) { LOG_PRINT_L3("Blockchain::" << __func__); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 07238b719..5291f1338 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -377,10 +377,12 @@ namespace cryptonote * for a block with the given hash * * @param id the hash to search for + * @param where the type of block, if non NULL * * @return true if the block is known, else false */ - bool have_block(const crypto::hash& id) const; + bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const; + bool have_block(const crypto::hash& id, int *where = NULL) const; /** * @brief gets the total number of transactions on the main chain diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 66fa7de54..db4219e1a 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1665,9 +1665,14 @@ namespace cryptonote return m_mempool.get_transactions_count(include_sensitive_txes); } //----------------------------------------------------------------------------------------------- - bool core::have_block(const crypto::hash& id) const + bool core::have_block_unlocked(const crypto::hash& id, int *where) const { - return m_blockchain_storage.have_block(id); + return m_blockchain_storage.have_block_unlocked(id, where); + } + //----------------------------------------------------------------------------------------------- + bool core::have_block(const crypto::hash& id, int *where) const + { + return m_blockchain_storage.have_block(id, where); } //----------------------------------------------------------------------------------------------- bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 24d0f9e76..8891540a9 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -55,6 +55,8 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) +enum { HAVE_BLOCK_MAIN_CHAIN, HAVE_BLOCK_ALT_CHAIN, HAVE_BLOCK_INVALID }; + namespace cryptonote { struct test_options { @@ -543,7 +545,8 @@ namespace cryptonote * * @note see Blockchain::have_block */ - bool have_block(const crypto::hash& id) const; + bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const; + bool have_block(const crypto::hash& id, int *where = NULL) const; /** * @copydoc Blockchain::get_short_chain_history diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 318d2638b..87d22539f 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2570,17 +2570,6 @@ skip: return 1; } - std::unordered_set hashes; - for (const auto &h: arg.m_block_ids) - { - if (!hashes.insert(h).second) - { - LOG_ERROR_CCONTEXT("sent duplicate block, dropping connection"); - drop_connection(context, true, false); - return 1; - } - } - uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights); if (n_use_blocks == 0 || n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size()) { @@ -2592,18 +2581,69 @@ skip: context.m_needed_objects.clear(); uint64_t added = 0; std::unordered_set blocks_found; + bool first = true; + bool expect_unknown = false; for (size_t i = 0; i < arg.m_block_ids.size(); ++i) { if (!blocks_found.insert(arg.m_block_ids[i]).second) { LOG_ERROR_CCONTEXT("Duplicate blocks in chain entry response, dropping connection"); - drop_connection(context, true, false); + drop_connection_with_score(context, 5, false); + return 1; + } + int where; + const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where); + if (first && !have_block) + { + LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection"); + drop_connection_with_score(context, 5, false); return 1; } + if (!first) + { + // after the first, blocks may be known or unknown, but if they are known, + // they should be at the same height if on the main chain + if (have_block) + { + switch (where) + { + default: + case HAVE_BLOCK_INVALID: + LOG_ERROR_CCONTEXT("Block is invalid or known without known type, dropping connection"); + drop_connection(context, true, false); + return 1; + case HAVE_BLOCK_MAIN_CHAIN: + if (expect_unknown) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + if (m_core.get_block_id_by_height(arg.start_height + i) != arg.m_block_ids[i]) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but not at the expected height, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + break; + case HAVE_BLOCK_ALT_CHAIN: + if (expect_unknown) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + break; + } + } + else + expect_unknown = true; + } const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i]; context.m_needed_objects.push_back(std::make_pair(arg.m_block_ids[i], block_weight)); if (++added == n_use_blocks) break; + first = false; } context.m_last_response_height -= arg.m_block_ids.size() - n_use_blocks; diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 8095f03e3..09be8758b 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -245,12 +245,17 @@ bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/ return true; } -bool tests::proxy_core::have_block(const crypto::hash& id) { +bool tests::proxy_core::have_block_unlocked(const crypto::hash& id, int *where) { if (m_hash2blkidx.end() == m_hash2blkidx.find(id)) return false; + if (where) *where = HAVE_BLOCK_MAIN_CHAIN; return true; } +bool tests::proxy_core::have_block(const crypto::hash& id, int *where) { + return have_block_unlocked(id, where); +} + void tests::proxy_core::build_short_history(std::list &m_history, const crypto::hash &m_start) { m_history.push_front(get_block_hash(m_genesis)); /*std::unordered_map::const_iterator cit = m_hash2blkidx.find(m_lastblk); diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index ebc3a89c2..94f148e8c 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -74,7 +74,8 @@ namespace tests bool init(const boost::program_options::variables_map& vm); bool deinit(){return true;} bool get_short_chain_history(std::list& ids); - bool have_block(const crypto::hash& id); + bool have_block(const crypto::hash& id, int *where = NULL); + bool have_block_unlocked(const crypto::hash& id, int *where = NULL); void get_blockchain_top(uint64_t& height, crypto::hash& top_id); bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed); bool handle_incoming_txs(const std::vector& tx_blobs, std::vector& tvc, cryptonote::relay_method tx_relay, bool relayed); diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index 0569d3748..4d6f09e69 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -55,7 +55,8 @@ public: bool init(const boost::program_options::variables_map& vm) {return true ;} bool deinit(){return true;} bool get_short_chain_history(std::list& ids) const { return true; } - bool have_block(const crypto::hash& id) const {return true;} + bool have_block(const crypto::hash& id, int *where = NULL) const {return false;} + bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const {return false;} void get_blockchain_top(uint64_t& height, crypto::hash& top_id)const{height=0;top_id=crypto::null_hash;} bool handle_incoming_tx(const cryptonote::tx_blob_entry& tx_blob, cryptonote::tx_verification_context& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; } bool handle_incoming_txs(const std::vector& tx_blob, std::vector& tvc, cryptonote::relay_method tx_relay, bool relayed) { return true; } From 6675069dcfff0faa4988381e2fb1cb70473be41c Mon Sep 17 00:00:00 2001 From: xiphon Date: Fri, 1 Jan 2021 02:04:01 +0100 Subject: [PATCH 26/44] portable_storage: forbid unnamed sections --- contrib/epee/include/storages/portable_storage.h | 1 + contrib/epee/include/storages/portable_storage_from_bin.h | 1 + contrib/epee/include/storages/portable_storage_to_bin.h | 1 + 3 files changed, 3 insertions(+) diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index 589e6ad63..2aeadf72c 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -266,6 +266,7 @@ namespace epee static_assert(std::is_rvalue_reference(), "unexpected copy of value"); TRY_ENTRY(); CHECK_AND_ASSERT(psection, nullptr); + CHECK_AND_ASSERT(!pentry_name.empty(), nullptr); auto ins_res = psection->m_entries.emplace(pentry_name, std::forward(entry)); return &ins_res.first->second; CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 1d6a09c41..631ad307b 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -132,6 +132,7 @@ namespace epee RECURSION_LIMITATION(); uint8_t name_len = 0; read(name_len); + CHECK_AND_ASSERT_THROW_MES(name_len > 0, "Section name is missing"); sce_name.resize(name_len); read((void*)sce_name.data(), name_len); } diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h index 137497e19..49a7be185 100644 --- a/contrib/epee/include/storages/portable_storage_to_bin.h +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -211,6 +211,7 @@ namespace epee for(const section_pair& se: sec.m_entries) { CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first); + CHECK_AND_ASSERT_THROW_MES(!se.first.empty(), "storage_entry_name is empty"); uint8_t len = static_cast(se.first.size()); strm.write((const char*)&len, sizeof(len)); strm.write(se.first.data(), size_t(len)); From 6a2abebb940b06feb43527f7c687af223bf67f86 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Thu, 31 Dec 2020 20:40:50 -0500 Subject: [PATCH 27/44] Restrict duplicate keys in epee binary format --- contrib/epee/include/storages/portable_storage_from_bin.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 631ad307b..3dc60ba71 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -301,7 +301,9 @@ namespace epee //read section name string std::string sec_name; read_sec_name(sec_name); - sec.m_entries.emplace(std::move(sec_name), load_storage_entry()); + const auto insert_loc = sec.m_entries.lower_bound(sec_name); + CHECK_AND_ASSERT_THROW_MES(insert_loc == sec.m_entries.end() || insert_loc->first != sec_name, "duplicate key: " << sec_name); + sec.m_entries.emplace_hint(insert_loc, std::move(sec_name), load_storage_entry()); } } inline From 3e36df73ace248ae2558287fef88eb6dcdefe6e6 Mon Sep 17 00:00:00 2001 From: codesoap Date: Fri, 23 Oct 2020 22:39:45 +0200 Subject: [PATCH 28/44] simplewallet: add "address mnew" command --- src/simplewallet/simplewallet.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e69538f75..96389b109 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -143,6 +143,7 @@ typedef cryptonote::simple_wallet sw; #define CREDITS_TARGET 50000 #define MAX_PAYMENT_DIFF 10000 #define MIN_PAYMENT_RATE 0.01f // per hash +#define MAX_MNEW_ADDRESSES 1000 enum TransferType { Transfer, @@ -203,7 +204,7 @@ namespace " account tag [ ...]\n" " account untag [ ...]\n" " account tag_description "); - const char* USAGE_ADDRESS("address [ new