From f9b9bbbb401580799bfd4431f90f1505a57c8299 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 14 Feb 2023 20:59:52 -0800 Subject: [PATCH 01/14] Address filtering WIP. --- .../protocols/protocol_address_in_31402.hpp | 3 +++ src/protocols/protocol_address_in_31402.cpp | 22 +++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/bitcoin/network/protocols/protocol_address_in_31402.hpp b/include/bitcoin/network/protocols/protocol_address_in_31402.hpp index 8198c898c..728e0d4f0 100644 --- a/include/bitcoin/network/protocols/protocol_address_in_31402.hpp +++ b/include/bitcoin/network/protocols/protocol_address_in_31402.hpp @@ -45,6 +45,9 @@ class BCT_API protocol_address_in_31402 void start() NOEXCEPT override; protected: + messages::address::cptr filter( + const messages::address_items& message) const NOEXCEPT; + virtual void handle_receive_address(const code& ec, const messages::address::cptr& message) NOEXCEPT; virtual void handle_save_address(const code& ec, diff --git a/src/protocols/protocol_address_in_31402.cpp b/src/protocols/protocol_address_in_31402.cpp index e62c3c6d6..4fbaf6c6d 100644 --- a/src/protocols/protocol_address_in_31402.cpp +++ b/src/protocols/protocol_address_in_31402.cpp @@ -70,6 +70,14 @@ void protocol_address_in_31402::start() NOEXCEPT // Inbound (store addresses). // ---------------------------------------------------------------------------- +address::cptr protocol_address_in_31402::filter( + const address_items& items) const NOEXCEPT +{ + // TODO: disabled, insufficient, unsupported (excludes whitelisted). + // TODO: prove the ptr copy to ptr avoids copy. + return std::make_shared
(difference(items, settings().blacklists)); +} + void protocol_address_in_31402::handle_receive_address(const code& ec, const address::cptr& message) NOEXCEPT { @@ -79,7 +87,8 @@ void protocol_address_in_31402::handle_receive_address(const code& ec, return; // Do not accept multiple addresses from inbound channels. - if (inbound_ && (received_ || !is_one(message->addresses.size()))) + const auto size = message->addresses.size(); + if (inbound_ && (received_ || !is_one(size))) { LOG("Unsolicited addresses from [" << authority() << "]"); stop(error::protocol_violation); @@ -87,22 +96,17 @@ void protocol_address_in_31402::handle_receive_address(const code& ec, } received_ = true; - if (message->addresses.front() == outbound()) { LOG("Dropping redundant address from [" << authority() << "]"); return; } - // TODO: filter items --> keep. - const auto keep = to_shared(new address - { - difference(message->addresses, settings().blacklists) - }); + const auto filtered = filter(message->addresses); // This allows previously-rejected addresses. - save(keep, BIND4(handle_save_address, _1, _2, - keep->addresses.size(), message->addresses.size())); + save(message, + BIND4(handle_save_address, _1, _2, filtered->addresses.size(), size)); } void protocol_address_in_31402::handle_save_address(const code& ec, From f8913416387d07eac61d26d25621c5002a5a382d Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 14 Feb 2023 21:44:56 -0800 Subject: [PATCH 02/14] Denormalize hosts from from ip6.is_v4_compatible(). --- include/bitcoin/network/config/utilities.hpp | 19 +++++++++----- src/config/utilities.cpp | 27 ++++++++++++-------- test/config/authority.cpp | 16 +++++++++--- test/config/utilities.cpp | 12 +++++++++ 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/include/bitcoin/network/config/utilities.hpp b/include/bitcoin/network/config/utilities.hpp index edc3435e0..a0daf4246 100644 --- a/include/bitcoin/network/config/utilities.hpp +++ b/include/bitcoin/network/config/utilities.hpp @@ -29,14 +29,21 @@ namespace libbitcoin { namespace network { namespace config { -/// IPv6 normalizes IPv4 addresses as "mapped" addresses, i.e. mapped into the -/// IPv6 address space. P2P protocol encodes all addresses in this normal form. -/// For serialization purposes we encode/decode only to/from denormalized form. -/// IPv6 "host names" are not bracketed, however IPv6 addresses are bracked. +/// IPv6 supports embedding of an IPv4 address (4 bytes) into IPv6 encodings +/// (16 bytes). The two formats, "compatible" and "mapped" embed the same +/// address differently, with the distinction being the level of support of the +/// device. This is problematic for addresses, as they are device independent. +/// P2P protocol is 16 bytes and allows for either encoding, however the +/// "compatible" concoding is deprecated, so we produce only mapped encoding +/// for P2P serialization. However, as both formats are send via P2P we decode +/// from all three IPv6 encodings (native, compatible, mapped). For human +/// readability we serialize adresses as text, for both logging and shutdown +/// persistence. We refer to this format as denormalized, as it supports only +/// native IPv4 and native IPv6 serialization. IPv6 host names are "bracketed". /// This provides distinction from the port number (otherwise conflating ":"). /// This form is referred to as "literal" IPv6 encoding (from IPv6 URIs). All -/// addresses must be literal encodings, all host names are serialized as non- -/// literal, and deserialized as either literal or non-literal. +/// text addresses are literal encodings, and all host names are serialized as +/// non-literal, and deserialized as either literal or non-literal. /// datatracker.ietf.org/doc/html/rfc4291 constexpr size_t ipv4_size = 4; diff --git a/src/config/utilities.cpp b/src/config/utilities.cpp index 6ef1f0059..08b1aef93 100644 --- a/src/config/utilities.cpp +++ b/src/config/utilities.cpp @@ -37,10 +37,10 @@ static_assert(array_count == ipv4_size); static_assert(array_count == ipv6_size); static_assert(is_same_type); +// Because system::deserialize doesn't convert empty to zero. template inline bool to_integer(Integer& out, const std::string& in) NOEXCEPT { - // system::deserialize doesn't convert empty to zero. if (in.empty()) { out = Integer{}; @@ -50,6 +50,7 @@ inline bool to_integer(Integer& out, const std::string& in) NOEXCEPT return system::deserialize(out, in); } +// For calling consistency. inline bool to_string(std::string& to, std::string&& from) NOEXCEPT { to = std::move(from); @@ -85,10 +86,11 @@ using namespace boost; #define HOST "([^:?/\\\\]+)" #define PORT ":([1-9][0-9]{0,4})" #define CIDR "\\/([1-9][0-9]{0,2})" +////#define IPV6E4 "\\[([0-9a-f:.]+)]" BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) -// Excludes ipv4 mapped, unbracketed, and ports allowed by make_address. +// Excludes ipv4 mapped/compat, unbracketed, and ports allowed by make_address. bool parse_host(asio::address& ip, const std::string& value) NOEXCEPT { static const regex regular @@ -101,7 +103,7 @@ bool parse_host(asio::address& ip, const std::string& value) NOEXCEPT && make_address(ip, (*token)[1]); } -// Excludes ipv4 mapped to ipv6. +// Excludes ipv4 mapped/compat to ipv6. bool parse_authority(asio::address& ip, uint16_t& port, uint8_t& cidr, const std::string& value) NOEXCEPT { @@ -119,7 +121,7 @@ bool parse_authority(asio::address& ip, uint16_t& port, uint8_t& cidr, (ip.is_v6() && cidr <= maximum_cidr_ip6)); } -// Excludes ipv4 mapped to ipv6. +// Excludes ipv4 mapped/compat to ipv6. bool parse_endpoint(std::string& scheme, std::string& host, uint16_t& port, const std::string& value) NOEXCEPT { @@ -140,6 +142,11 @@ BC_POP_WARNING() // asio/asio conversions. // ---------------------------------------------------------------------------- +inline bool is_embedded_v4(const asio::ipv6& ip6) NOEXCEPT +{ + return ip6.is_v4_mapped() || ip6.is_v4_compatible(); +} + static asio::ipv6 to_v6(const asio::ipv4& ip4) NOEXCEPT { try @@ -152,16 +159,16 @@ static asio::ipv6 to_v6(const asio::ipv4& ip4) NOEXCEPT } } -// Convert IPv6-mapped to IPV4 (ensures consistent matching). +// Convert IPv6-mapped to IPV4 (ensures consistent internal matching). +// Reduce 4 encodings (IPv6, IPv6-mapped, IPv6-compat, IPv4) to 2 (IPv6, IPv4). asio::address denormalize(const asio::address& ip) NOEXCEPT { if (ip.is_v6()) { try { - // Must extract the ipv6 object before calling to_ipv4(). const auto ip6 = ip.to_v6(); - if (ip6.is_v4_mapped()) return { ip6.to_v4() }; + if (is_embedded_v4(ip6)) return { ip6.to_v4() }; } catch (std::exception) { @@ -173,14 +180,12 @@ asio::address denormalize(const asio::address& ip) NOEXCEPT // asio/string host conversions. // ---------------------------------------------------------------------------- -// IPv4-Compatible IPv6 address are deprecated, support only IPv4-Mapped. -// rfc-editor.org/rfc/rfc4291 inline std::string to_host(const asio::ipv6& ip6) NOEXCEPT { try { - return ip6.to_string(); + return is_embedded_v4(ip6) ? to_host(ip6.to_v4()) : ip6.to_string(); } catch (std::exception) { @@ -221,7 +226,7 @@ std::string to_literal(const asio::address& ip) NOEXCEPT return (host.find(":") == std::string::npos) ? host : ("[" + host + "]"); } -// Rejects ipv6 mapped to ipv4 and unbracketed ipv6. +// Rejects ipv6 mapped/compat to ipv4 and unbracketed ipv6. asio::address from_host(const std::string& host) NOEXCEPT(false) { asio::address out{}; diff --git a/test/config/authority.cpp b/test/config/authority.cpp index 040f9133b..ebe162137 100644 --- a/test/config/authority.cpp +++ b/test/config/authority.cpp @@ -434,15 +434,23 @@ BOOST_AUTO_TEST_CASE(authority__to_address_item1__ipv4_mapped_ip_address__ipv4) BOOST_REQUIRE(net_equal(host.to_address_item(), expected)); } -BOOST_AUTO_TEST_CASE(authority__to_address_item1__ipv4_compatible_ip_address__ipv6_alternative) +// IPv6 compatible addresses are deprecated, use mapped. +// datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1 +BOOST_AUTO_TEST_CASE(authority__to_address_item1__ipv4_compatible_ip_address__mapped_not_compatible) { - const messages::address_item expected + const messages::address_item compatible { 0, 0, test_compatible_ip_address, 42, }; - const authority host(from_address(expected.ip), expected.port); - BOOST_REQUIRE(net_equal(host.to_address_item(), expected)); + const messages::address_item mapped + { + 0, 0, test_mapped_ip_address, 42, + }; + + const authority host(from_address(compatible.ip), compatible.port); + BOOST_REQUIRE(!net_equal(host.to_address_item(), compatible)); + BOOST_REQUIRE(net_equal(host.to_address_item(), mapped)); } BOOST_AUTO_TEST_CASE(authority__to_address_item1__ipv6_address__ipv6_compressed) diff --git a/test/config/utilities.cpp b/test/config/utilities.cpp index adf57d5a0..b737393da 100644 --- a/test/config/utilities.cpp +++ b/test/config/utilities.cpp @@ -284,6 +284,18 @@ BOOST_AUTO_TEST_CASE(utilities__from_host__v6__expected_v6) BOOST_REQUIRE_EQUAL(from_host("[4242::4242]"), expected); } +////BOOST_AUTO_TEST_CASE(utilities__from_host__compatible__expected_v4) +////{ +//// const asio::address expected{ asio::ipv4{ asio::ipv4::bytes_type{ 127, 0, 1, 1 } } }; +//// BOOST_REQUIRE_EQUAL(make_address_v6("[::127.0.0.1]:8333"), expected); +////} +//// +////BOOST_AUTO_TEST_CASE(utilities__from_host__mapped__expected_v4) +////{ +//// const asio::address expected{ asio::ipv4{ asio::ipv4::bytes_type{ 127, 0, 1, 1 } } }; +//// BOOST_REQUIRE_EQUAL(make_address_v6("[::ffff.127.0.0.1]:8333"), expected); +////} + // to_literal BOOST_AUTO_TEST_CASE(utilities__to_literal__default__unspecified_v4) From 8c9ee3f325f7bef519acd821827f8b6972d9f8d5 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 14 Feb 2023 21:48:53 -0800 Subject: [PATCH 03/14] Allow individual line failures in hosts file. --- include/bitcoin/network/error.hpp | 2 +- include/bitcoin/network/net/hosts.hpp | 3 +++ src/error.cpp | 2 +- src/net/hosts.cpp | 26 ++++++++++++++++++-------- test/error.cpp | 6 +++--- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/include/bitcoin/network/error.hpp b/include/bitcoin/network/error.hpp index ce1bc8a86..77e973419 100644 --- a/include/bitcoin/network/error.hpp +++ b/include/bitcoin/network/error.hpp @@ -66,7 +66,7 @@ enum error_t file_load, file_save, file_system, - file_payload, + file_exception, // general I/O failures bad_stream, diff --git a/include/bitcoin/network/net/hosts.hpp b/include/bitcoin/network/net/hosts.hpp index 8742541d8..a40dd4ef5 100644 --- a/include/bitcoin/network/net/hosts.hpp +++ b/include/bitcoin/network/net/hosts.hpp @@ -93,6 +93,9 @@ class BCT_API hosts BC_POP_WARNING() } + // Push a buffer entry if the line is valid. + void push_valid(const std::string& line) NOEXCEPT; + // These are thread safe. const std::filesystem::path file_path_; std::atomic count_{}; diff --git a/src/error.cpp b/src/error.cpp index 8ad4f0f5c..921b9fd10 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -44,7 +44,7 @@ DEFINE_ERROR_T_MESSAGE_MAP(error) { file_load, "failed to load file" }, { file_save, "failed to save file" }, { file_system, "file system error" }, - { file_payload, "file payload error" }, + { file_exception, "file exception" }, // general I/O failures { bad_stream, "bad data stream" }, diff --git a/src/net/hosts.cpp b/src/net/hosts.cpp index 44516a885..f047c8480 100644 --- a/src/net/hosts.cpp +++ b/src/net/hosts.cpp @@ -48,6 +48,18 @@ size_t hosts::count() const NOEXCEPT return count_.load(std::memory_order_relaxed); } +// private +inline void hosts::push_valid(const std::string& line) NOEXCEPT +{ + try + { + buffer_.push_back(config::address{ line }.item()); + } + catch (std::exception&) + { + } +} + code hosts::start() NOEXCEPT { if (disabled_) @@ -55,20 +67,18 @@ code hosts::start() NOEXCEPT try { - ifstream file(file_path_, ifstream::in); + ifstream file{ file_path_, ifstream::in }; if (!file.good()) return error::success; - std::string line; - while (std::getline(file, line)) - buffer_.push_back(config::address(line).item()); + for (std::string line{}; std::getline(file, line); push_valid(line)); if (file.bad()) return error::file_load; } catch (const std::exception&) { - return error::file_payload; + return error::file_exception; } if (buffer_.empty()) @@ -98,19 +108,19 @@ code hosts::stop() NOEXCEPT try { - ofstream file(file_path_, ofstream::out); + ofstream file{ file_path_, ofstream::out }; if (!file.good()) return error::file_save; for (const auto& entry: buffer_) - file << config::address(entry) << std::endl; + file << config::address{ entry } << std::endl; if (file.bad()) return error::file_save; } catch (const std::exception&) { - return error::file_save; + return error::file_exception; } buffer_.clear(); diff --git a/test/error.cpp b/test/error.cpp index ebc03f981..b9144acbc 100644 --- a/test/error.cpp +++ b/test/error.cpp @@ -135,13 +135,13 @@ BOOST_AUTO_TEST_CASE(error_t__code__file_system__true_exected_message) BOOST_REQUIRE_EQUAL(ec.message(), "file system error"); } -BOOST_AUTO_TEST_CASE(error_t__code__file_payload__true_exected_message) +BOOST_AUTO_TEST_CASE(error_t__code__file_exception__true_exected_message) { - constexpr auto value = error::file_payload; + constexpr auto value = error::file_exception; const auto ec = code(value); BOOST_REQUIRE(ec); BOOST_REQUIRE(ec == value); - BOOST_REQUIRE_EQUAL(ec.message(), "file payload error"); + BOOST_REQUIRE_EQUAL(ec.message(), "file exception"); } // general I/O failures From 11d8287d9800d501066122b0ddd96d12ea59cf73 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 14 Feb 2023 22:26:50 -0800 Subject: [PATCH 04/14] Log verbosity. --- src/protocols/protocol_address_in_31402.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protocols/protocol_address_in_31402.cpp b/src/protocols/protocol_address_in_31402.cpp index 4fbaf6c6d..c6f582b7c 100644 --- a/src/protocols/protocol_address_in_31402.cpp +++ b/src/protocols/protocol_address_in_31402.cpp @@ -98,7 +98,8 @@ void protocol_address_in_31402::handle_receive_address(const code& ec, received_ = true; if (message->addresses.front() == outbound()) { - LOG("Dropping redundant address from [" << authority() << "]"); + // This is very common. + ////LOG("Dropping redundant address from [" << authority() << "]"); return; } From 9483619a7015ef4f2a4c2c0b5ddc13416bdb84f1 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 14 Feb 2023 23:45:18 -0800 Subject: [PATCH 05/14] Randomize connect/retry timeout to 50-100%/10-100% of configured time. --- include/bitcoin/network/settings.hpp | 1 - src/settings.cpp | 10 ++++++++-- test/settings.cpp | 16 ++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/bitcoin/network/settings.hpp b/include/bitcoin/network/settings.hpp index 5b79803bf..b34116461 100644 --- a/include/bitcoin/network/settings.hpp +++ b/include/bitcoin/network/settings.hpp @@ -79,7 +79,6 @@ struct BCT_API settings virtual bool inbound_enabled() const NOEXCEPT; virtual bool outbound_enabled() const NOEXCEPT; virtual bool advertise_enabled() const NOEXCEPT; - virtual size_t maximum_payload() const NOEXCEPT; virtual duration retry_timeout() const NOEXCEPT; virtual duration connect_timeout() const NOEXCEPT; diff --git a/src/settings.cpp b/src/settings.cpp index 914050631..0e19e4dc1 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -142,14 +142,20 @@ size_t settings::maximum_payload() const NOEXCEPT to_bool(services_maximum & service::node_witness)); } +// Randomized from 10% to maximum milliseconds (specified in seconds). duration settings::retry_timeout() const NOEXCEPT { - return seconds(retry_timeout_seconds); + const auto from = retry_timeout_seconds * 100_u64; + const auto to = retry_timeout_seconds * 1'000_u64; + return milliseconds{ system::pseudo_random::next(from, to) }; } +// Randomized from 50% to maximum milliseconds (specified in seconds). duration settings::connect_timeout() const NOEXCEPT { - return seconds(connect_timeout_seconds); + const auto from = connect_timeout_seconds * 500_u64; + const auto to = connect_timeout_seconds * 1'000_u64; + return milliseconds{ system::pseudo_random::next(from, to) }; } duration settings::channel_handshake() const NOEXCEPT diff --git a/test/settings.cpp b/test/settings.cpp index d34812d96..18e893379 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -402,20 +402,20 @@ BOOST_AUTO_TEST_CASE(settings__maximum_payload__maximum_maximum_services__expect BOOST_REQUIRE_EQUAL(instance.maximum_payload(), 4'000'000u); } -BOOST_AUTO_TEST_CASE(settings__retry_timeout__always__connect_timeout_seconds) +BOOST_AUTO_TEST_CASE(settings__retry_timeout__always__between_zero_and_retry_timeout_seconds) { settings instance{}; - const auto expected = 42; - instance.retry_timeout_seconds = expected; - BOOST_REQUIRE(instance.retry_timeout() == seconds(expected)); + instance.retry_timeout_seconds = 42; + BOOST_REQUIRE(instance.retry_timeout() > seconds{ zero }); + BOOST_REQUIRE(instance.retry_timeout() <= seconds{ instance.retry_timeout_seconds }); } -BOOST_AUTO_TEST_CASE(settings__connect_timeout__always__connect_timeout_seconds) +BOOST_AUTO_TEST_CASE(settings__connect_timeout__always__between_zero_and_connect_timeout_seconds) { settings instance{}; - const auto expected = 42; - instance.connect_timeout_seconds = expected; - BOOST_REQUIRE(instance.connect_timeout() == seconds(expected)); + instance.connect_timeout_seconds = 42; + BOOST_REQUIRE(instance.connect_timeout() > seconds{ zero }); + BOOST_REQUIRE(instance.connect_timeout() <= seconds{ instance.connect_timeout_seconds }); } BOOST_AUTO_TEST_CASE(settings__channel_handshake__always__channel_handshake_seconds) From 7d5247ed22f67b2040c791054cea3a67867fbc89 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Tue, 14 Feb 2023 23:47:45 -0800 Subject: [PATCH 06/14] Use randomized timeouts in sessions (retry) and connectors(timeout). --- include/bitcoin/network/sessions/session.hpp | 1 - src/sessions/session.cpp | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/bitcoin/network/sessions/session.hpp b/include/bitcoin/network/sessions/session.hpp index 1e1c55ec4..33386fbd0 100644 --- a/include/bitcoin/network/sessions/session.hpp +++ b/include/bitcoin/network/sessions/session.hpp @@ -214,7 +214,6 @@ class BCT_API session // These are thread safe (mostly). p2p& network_; const size_t key_; - const duration timeout_; std::atomic_bool stopped_{ true }; // These are not thread safe. diff --git a/src/sessions/session.cpp b/src/sessions/session.cpp index d94042ec7..0ea528431 100644 --- a/src/sessions/session.cpp +++ b/src/sessions/session.cpp @@ -49,7 +49,6 @@ BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) session::session(p2p& network, size_t key) NOEXCEPT : network_(network), key_(key), - timeout_(network.network_settings().retry_timeout()), stop_subscriber_(network.strand()), defer_subscriber_(network.strand()), pend_subscriber_(network.strand()), @@ -338,9 +337,11 @@ void session::defer(result_handler&& handler, const uintptr_t& id) NOEXCEPT return; } - // Subscribe completes before handle_timer can be invoked (timer stranded). const auto timer = std::make_shared(log(), network_.strand()); - timer->start(BIND3(handle_timer, _1, id, std::move(handler)), timeout_); + + timer->start(BIND3(handle_timer, _1, id, std::move(handler)), + settings().retry_timeout()); + defer_subscriber_.subscribe(BIND3(handle_defer, _1, id, timer), id); } From 57010eb2c99fc5b8aabda859ce0b3e5197913b43 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 00:16:09 -0800 Subject: [PATCH 07/14] Tweak random timeout baselines. --- src/settings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 0e19e4dc1..601dc81a3 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -142,10 +142,10 @@ size_t settings::maximum_payload() const NOEXCEPT to_bool(services_maximum & service::node_witness)); } -// Randomized from 10% to maximum milliseconds (specified in seconds). +// Randomized from 50% to maximum milliseconds (specified in seconds). duration settings::retry_timeout() const NOEXCEPT { - const auto from = retry_timeout_seconds * 100_u64; + const auto from = retry_timeout_seconds * 500_u64; const auto to = retry_timeout_seconds * 1'000_u64; return milliseconds{ system::pseudo_random::next(from, to) }; } From 867cce59b876cbb88dff749ed435cbf87e12696b Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 00:54:10 -0800 Subject: [PATCH 08/14] Increase address message consumption lower end from 1 to 50%. --- include/bitcoin/network/net/hosts.hpp | 2 +- src/net/hosts.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bitcoin/network/net/hosts.hpp b/include/bitcoin/network/net/hosts.hpp index a40dd4ef5..9e0a7de34 100644 --- a/include/bitcoin/network/net/hosts.hpp +++ b/include/bitcoin/network/net/hosts.hpp @@ -68,7 +68,7 @@ class BCT_API hosts /// Take one random host from the table (non-const). virtual void take(const address_item_handler& handler) NOEXCEPT; - /// Save a random set of hosts (e.g obtained from peer), count of accept. + /// Save random subset of hosts (e.g obtained from peer), count of accept. virtual size_t save(const messages::address_items& hosts) NOEXCEPT; /// Obtain a random set of hosts (e.g for relay to peer). diff --git a/src/net/hosts.cpp b/src/net/hosts.cpp index f047c8480..dbcef7919 100644 --- a/src/net/hosts.cpp +++ b/src/net/hosts.cpp @@ -173,9 +173,9 @@ size_t hosts::save(const address_items& hosts) NOEXCEPT if (disabled_ || hosts.empty()) return zero; - // Accept between 1 and all of the filtered addresses, up to capacity. + // Accept between half and all of the filtered addresses, up to capacity. const auto usable = std::min(hosts.size(), capacity_); - const auto random = pseudo_random::next(one, usable); + const auto random = pseudo_random::next(to_half(usable), usable); // But always accept at least the amount we are short if available. const auto gap = capacity_ - buffer_.size(); From 48f69cd18e4d89c211010e5ddf3c45551870c10a Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 01:39:37 -0800 Subject: [PATCH 09/14] Apply address message filtering to seed, add disabled/insufficient/unsupported. --- .../protocols/protocol_address_in_31402.hpp | 2 +- .../network/protocols/protocol_seed_31402.hpp | 3 ++ src/protocols/protocol_address_in_31402.cpp | 20 ++++++-- src/protocols/protocol_seed_31402.cpp | 50 +++++++++++++------ 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/bitcoin/network/protocols/protocol_address_in_31402.hpp b/include/bitcoin/network/protocols/protocol_address_in_31402.hpp index 728e0d4f0..a248c71f7 100644 --- a/include/bitcoin/network/protocols/protocol_address_in_31402.hpp +++ b/include/bitcoin/network/protocols/protocol_address_in_31402.hpp @@ -45,7 +45,7 @@ class BCT_API protocol_address_in_31402 void start() NOEXCEPT override; protected: - messages::address::cptr filter( + virtual messages::address::cptr filter( const messages::address_items& message) const NOEXCEPT; virtual void handle_receive_address(const code& ec, diff --git a/include/bitcoin/network/protocols/protocol_seed_31402.hpp b/include/bitcoin/network/protocols/protocol_seed_31402.hpp index fd8697caa..f03594dbc 100644 --- a/include/bitcoin/network/protocols/protocol_seed_31402.hpp +++ b/include/bitcoin/network/protocols/protocol_seed_31402.hpp @@ -51,6 +51,9 @@ class BCT_API protocol_seed_31402 virtual bool complete() const NOEXCEPT; virtual void handle_timer(const code& ec) NOEXCEPT; + virtual messages::address::cptr filter( + const messages::address_items& message) const NOEXCEPT; + virtual void handle_send_get_address(const code& ec) NOEXCEPT; virtual void handle_receive_address(const code& ec, const messages::address::cptr& address) NOEXCEPT; diff --git a/src/protocols/protocol_address_in_31402.cpp b/src/protocols/protocol_address_in_31402.cpp index c6f582b7c..564c712ec 100644 --- a/src/protocols/protocol_address_in_31402.cpp +++ b/src/protocols/protocol_address_in_31402.cpp @@ -18,6 +18,8 @@ */ #include +#include +#include #include #include #include @@ -73,9 +75,19 @@ void protocol_address_in_31402::start() NOEXCEPT address::cptr protocol_address_in_31402::filter( const address_items& items) const NOEXCEPT { - // TODO: disabled, insufficient, unsupported (excludes whitelisted). - // TODO: prove the ptr copy to ptr avoids copy. - return std::make_shared
(difference(items, settings().blacklists)); + const auto message = std::make_shared
(address + { + difference(items, settings().blacklists) + }); + + std::erase_if(message->addresses, [&](const auto& address) NOEXCEPT + { + return settings().disabled(address) + || settings().insufficient(address) + || settings().unsupported(address); + }); + + return message; } void protocol_address_in_31402::handle_receive_address(const code& ec, @@ -106,7 +118,7 @@ void protocol_address_in_31402::handle_receive_address(const code& ec, const auto filtered = filter(message->addresses); // This allows previously-rejected addresses. - save(message, + save(filtered, BIND4(handle_save_address, _1, _2, filtered->addresses.size(), size)); } diff --git a/src/protocols/protocol_seed_31402.cpp b/src/protocols/protocol_seed_31402.cpp index b614c6d32..beda08c45 100644 --- a/src/protocols/protocol_seed_31402.cpp +++ b/src/protocols/protocol_seed_31402.cpp @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -35,6 +36,9 @@ using namespace system; using namespace messages; using namespace std::placeholders; +// Bind throws (ok). +BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + protocol_seed_31402::protocol_seed_31402(const session& session, const channel::ptr& channel) NOEXCEPT : protocol(session, channel), @@ -55,8 +59,8 @@ void protocol_seed_31402::start() NOEXCEPT if (started()) return; - SUBSCRIBE2(messages::address, handle_receive_address, _1, _2); - SUBSCRIBE2(messages::address, handle_receive_get_address, _1, _2); + SUBSCRIBE2(address, handle_receive_address, _1, _2); + SUBSCRIBE2(address, handle_receive_get_address, _1, _2); SEND1(get_address{}, handle_send_get_address, _1); protocol::start(); @@ -93,7 +97,7 @@ void protocol_seed_31402::handle_timer(const code& ec) NOEXCEPT stop(error::channel_timeout); } -// Outbound [send_get_address => receive_address (save_addresses)]. +// Inbound (store addresses). // ---------------------------------------------------------------------------- void protocol_seed_31402::handle_send_get_address(const code& ec) NOEXCEPT @@ -110,6 +114,24 @@ void protocol_seed_31402::handle_send_get_address(const code& ec) NOEXCEPT stop(error::success); } +address::cptr protocol_seed_31402::filter( + const address_items& items) const NOEXCEPT +{ + const auto message = std::make_shared
(address + { + difference(items, settings().blacklists) + }); + + std::erase_if(message->addresses, [&](const auto& address) NOEXCEPT + { + return settings().disabled(address) + || settings().insufficient(address) + || settings().unsupported(address); + }); + + return message; +} + void protocol_seed_31402::handle_receive_address(const code& ec, const address::cptr& message) NOEXCEPT { @@ -118,23 +140,19 @@ void protocol_seed_31402::handle_receive_address(const code& ec, if (stopped(ec)) return; - const auto& items = message->addresses; - const auto singleton = is_one(items.size()); + const auto size = message->addresses.size(); // Do not store redundant adresses, outbound() is known address. - if (singleton && (outbound() == items.front())) + if (is_one(size) && (message->addresses.front() == outbound())) { ////LOG("Dropping redundant address from seed [" << authority() << "]"); return; } - // Remove blacklist conflicts. - // Should construct using makes_shared(vargs) overload, but fails on clang. - const auto to = to_shared(messages::address{ difference(items, blacklist_) }); - const auto count = to->addresses.size(); - const auto start = items.size(); + const auto filtered = filter(message->addresses); - save(message, BIND4(handle_save_addresses, _1, _2, count, start)); + save(filtered, + BIND4(handle_save_addresses, _1, _2, filtered->addresses.size(), size)); } void protocol_seed_31402::handle_save_addresses(const code& ec, @@ -162,10 +180,10 @@ void protocol_seed_31402::handle_save_addresses(const code& ec, stop(error::success); } -// Inbound [receive_get_address -> send_address (load_addresses)]. +// Outbound (fetch and send addresses). // ---------------------------------------------------------------------------- -messages::address_item protocol_seed_31402::self() const NOEXCEPT +address_item protocol_seed_31402::self() const NOEXCEPT { return settings().self.to_address_item(unix_time(), settings().services_maximum); @@ -182,7 +200,7 @@ void protocol_seed_31402::handle_receive_get_address(const code& ec, if (settings().advertise_enabled()) { // Only send 0..1 address in response to get_address when seeding. - SEND1(messages::address{ { self() } }, handle_send_address, _1); + SEND1(address{ { self() } }, handle_send_address, _1); return; } @@ -204,5 +222,7 @@ void protocol_seed_31402::handle_send_address(const code& ec) NOEXCEPT stop(error::success); } +BC_POP_WARNING() + } // namespace network } // namespace libbitcoin From 388cca20a4448ae2af432a8dc1ebd08e1699b6a0 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 02:45:20 -0800 Subject: [PATCH 10/14] Rename two timeout settings for consistency. --- include/bitcoin/network/settings.hpp | 4 ++-- src/settings.cpp | 8 ++++---- test/sessions/session_seed.cpp | 2 +- test/settings.cpp | 24 ++++++++++++------------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/bitcoin/network/settings.hpp b/include/bitcoin/network/settings.hpp index b34116461..554027119 100644 --- a/include/bitcoin/network/settings.hpp +++ b/include/bitcoin/network/settings.hpp @@ -59,8 +59,8 @@ struct BCT_API settings uint32_t connect_batch_size; uint32_t retry_timeout_seconds; uint32_t connect_timeout_seconds; - uint32_t channel_handshake_seconds; - uint32_t channel_germination_seconds; + uint32_t handshake_timeout_seconds; + uint32_t seeding_timeout_seconds; uint32_t channel_heartbeat_minutes; uint32_t channel_inactivity_minutes; uint32_t channel_expiration_minutes; diff --git a/src/settings.cpp b/src/settings.cpp index 601dc81a3..6a4b80feb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -58,8 +58,8 @@ settings::settings() NOEXCEPT connect_batch_size(5), retry_timeout_seconds(1), connect_timeout_seconds(5), - channel_handshake_seconds(30), - channel_germination_seconds(30), + handshake_timeout_seconds(30), + seeding_timeout_seconds(30), channel_heartbeat_minutes(5), channel_inactivity_minutes(10), channel_expiration_minutes(1440), @@ -160,12 +160,12 @@ duration settings::connect_timeout() const NOEXCEPT duration settings::channel_handshake() const NOEXCEPT { - return seconds(channel_handshake_seconds); + return seconds(handshake_timeout_seconds); } duration settings::channel_germination() const NOEXCEPT { - return seconds(channel_germination_seconds); + return seconds(seeding_timeout_seconds); } duration settings::channel_heartbeat() const NOEXCEPT diff --git a/test/sessions/session_seed.cpp b/test/sessions/session_seed.cpp index 966ff49ea..ad2ffc881 100644 --- a/test/sessions/session_seed.cpp +++ b/test/sessions/session_seed.cpp @@ -786,7 +786,7 @@ BOOST_AUTO_TEST_CASE(session_seed__start__not_seeded__seeding_unsuccessful) //// const logger log{}; //// settings set(selection::mainnet); //// set.seeds.resize(1); -//// set.channel_germination_seconds = 5; +//// set.seeding_timeout_seconds = 5; //// set.outbound_connections = 1; //// set.host_pool_capacity = 1; //// mock_p2p<> net(set, log); diff --git a/test/settings.cpp b/test/settings.cpp index 18e893379..0c610821b 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -50,8 +50,8 @@ BOOST_AUTO_TEST_CASE(settings__construct__default__expected) BOOST_REQUIRE_EQUAL(instance.connect_batch_size, 5u); BOOST_REQUIRE_EQUAL(instance.retry_timeout_seconds, 1u); BOOST_REQUIRE_EQUAL(instance.connect_timeout_seconds, 5u); - BOOST_REQUIRE_EQUAL(instance.channel_handshake_seconds, 30u); - BOOST_REQUIRE_EQUAL(instance.channel_germination_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.handshake_timeout_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.seeding_timeout_seconds, 30u); BOOST_REQUIRE_EQUAL(instance.channel_heartbeat_minutes, 5u); BOOST_REQUIRE_EQUAL(instance.channel_inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.channel_expiration_minutes, 1440u); @@ -92,8 +92,8 @@ BOOST_AUTO_TEST_CASE(settings__construct__mainnet__expected) BOOST_REQUIRE_EQUAL(instance.connect_batch_size, 5u); BOOST_REQUIRE_EQUAL(instance.retry_timeout_seconds, 1u); BOOST_REQUIRE_EQUAL(instance.connect_timeout_seconds, 5u); - BOOST_REQUIRE_EQUAL(instance.channel_handshake_seconds, 30u); - BOOST_REQUIRE_EQUAL(instance.channel_germination_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.handshake_timeout_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.seeding_timeout_seconds, 30u); BOOST_REQUIRE_EQUAL(instance.channel_heartbeat_minutes, 5u); BOOST_REQUIRE_EQUAL(instance.channel_inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.channel_expiration_minutes, 1440u); @@ -147,8 +147,8 @@ BOOST_AUTO_TEST_CASE(settings__construct__testnet__expected) BOOST_REQUIRE_EQUAL(instance.connect_batch_size, 5u); BOOST_REQUIRE_EQUAL(instance.retry_timeout_seconds, 1u); BOOST_REQUIRE_EQUAL(instance.connect_timeout_seconds, 5u); - BOOST_REQUIRE_EQUAL(instance.channel_handshake_seconds, 30u); - BOOST_REQUIRE_EQUAL(instance.channel_germination_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.handshake_timeout_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.seeding_timeout_seconds, 30u); BOOST_REQUIRE_EQUAL(instance.channel_heartbeat_minutes, 5u); BOOST_REQUIRE_EQUAL(instance.channel_inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.channel_expiration_minutes, 1440u); @@ -202,8 +202,8 @@ BOOST_AUTO_TEST_CASE(settings__construct__regtest__expected) BOOST_REQUIRE_EQUAL(instance.connect_batch_size, 5u); BOOST_REQUIRE_EQUAL(instance.retry_timeout_seconds, 1u); BOOST_REQUIRE_EQUAL(instance.connect_timeout_seconds, 5u); - BOOST_REQUIRE_EQUAL(instance.channel_handshake_seconds, 30u); - BOOST_REQUIRE_EQUAL(instance.channel_germination_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.handshake_timeout_seconds, 30u); + BOOST_REQUIRE_EQUAL(instance.seeding_timeout_seconds, 30u); BOOST_REQUIRE_EQUAL(instance.channel_heartbeat_minutes, 5u); BOOST_REQUIRE_EQUAL(instance.channel_inactivity_minutes, 10u); BOOST_REQUIRE_EQUAL(instance.channel_expiration_minutes, 1440u); @@ -418,11 +418,11 @@ BOOST_AUTO_TEST_CASE(settings__connect_timeout__always__between_zero_and_connect BOOST_REQUIRE(instance.connect_timeout() <= seconds{ instance.connect_timeout_seconds }); } -BOOST_AUTO_TEST_CASE(settings__channel_handshake__always__channel_handshake_seconds) +BOOST_AUTO_TEST_CASE(settings__channel_handshake__always__handshake_timeout_seconds) { settings instance{}; const auto expected = 42u; - instance.channel_handshake_seconds = expected; + instance.handshake_timeout_seconds = expected; BOOST_REQUIRE(instance.channel_handshake() == seconds(expected)); } @@ -450,11 +450,11 @@ BOOST_AUTO_TEST_CASE(settings__channel_expiration__always__channel_expiration_mi BOOST_REQUIRE(instance.channel_expiration() == minutes(expected)); } -BOOST_AUTO_TEST_CASE(settings__channel_germination__always__channel_germination_seconds) +BOOST_AUTO_TEST_CASE(settings__channel_germination__always__seeding_timeout_seconds) { settings instance{}; const auto expected = 42u; - instance.channel_germination_seconds = expected; + instance.seeding_timeout_seconds = expected; BOOST_REQUIRE(instance.channel_germination() == seconds(expected)); } From 64041f8a4cc103b024fb31a81c73f33828dc08aa Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 03:10:55 -0800 Subject: [PATCH 11/14] Set BCT_API on config::utilities. --- include/bitcoin/network/config/utilities.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/bitcoin/network/config/utilities.hpp b/include/bitcoin/network/config/utilities.hpp index a0daf4246..ce6781455 100644 --- a/include/bitcoin/network/config/utilities.hpp +++ b/include/bitcoin/network/config/utilities.hpp @@ -66,25 +66,25 @@ constexpr bool is_v4(const messages::ip_address& ip) NOEXCEPT } /// Member if subnet addresses contain host. -bool is_member(const asio::address& ip, const asio::address& subnet, +BCT_API bool is_member(const asio::address& ip, const asio::address& subnet, uint8_t cidr) NOEXCEPT; /// Unmap IPv6-mapped addresses. asio::address denormalize(const asio::address& ip) NOEXCEPT; /// Denormalizes to IPv4 (unmapped), literal emits unbracketed. -std::string to_host(const asio::address& ip) NOEXCEPT; -std::string to_literal(const asio::address& ip) NOEXCEPT; -asio::address from_host(const std::string& host) NOEXCEPT(false); +BCT_API std::string to_host(const asio::address& ip) NOEXCEPT; +BCT_API std::string to_literal(const asio::address& ip) NOEXCEPT; +BCT_API asio::address from_host(const std::string& host) NOEXCEPT(false); /// Not denormalizing. messages::ip_address to_address(const asio::address& ip) NOEXCEPT; asio::address from_address(const messages::ip_address& address) NOEXCEPT; /// Parsers. -bool parse_authority(asio::address& ip, uint16_t& port, uint8_t& cidr, +BCT_API bool parse_authority(asio::address& ip, uint16_t& port, uint8_t& cidr, const std::string& value) NOEXCEPT; -bool parse_endpoint(std::string& scheme, std::string& host, uint16_t& port, +BCT_API bool parse_endpoint(std::string& scheme, std::string& host, uint16_t& port, const std::string& value) NOEXCEPT; } // namespace config From b4e496998cbd5d89b33c269cf51614755bc53aef Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 03:11:09 -0800 Subject: [PATCH 12/14] Comments on address time/service update. --- src/sessions/session_outbound.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sessions/session_outbound.cpp b/src/sessions/session_outbound.cpp index e414777d1..f3fa99550 100644 --- a/src/sessions/session_outbound.cpp +++ b/src/sessions/session_outbound.cpp @@ -217,7 +217,7 @@ void session_outbound::do_one(const code& ec, const config::address& peer, // Calling connector->stop() either from handle_started or handle_one results // in its timer cancelation, which cancels its handler. In either case the -// address has not been validated, so must be restored to pool here. +// address has not been validated, so restore to pool here - without update. void session_outbound::handle_connector(const code& ec, const channel::ptr& channel, const config::address& peer, size_t, const channel_handler& handler) NOEXCEPT @@ -315,6 +315,7 @@ void session_outbound::handle_connect(const code& ec, if (ec) { BC_ASSERT_MSG(!channel, "unexpected channel instance"); + ////LOG("Failed to connect outbound channel, " << ec.message()); defer(BIND3(start_connect, _1, connectors, id), connectors); return; @@ -335,6 +336,7 @@ void session_outbound::handle_channel_start(const code&, const channel::ptr&, size_t) NOEXCEPT { BC_ASSERT_MSG(stranded(), "strand"); + ////LOG("Outbound channel start [" << channel->authority() << "] " //// "(" << id << ") " << ec.message()); } @@ -350,9 +352,9 @@ void session_outbound::handle_channel_stop(const code& ec, const connectors_ptr& connectors) NOEXCEPT { BC_ASSERT_MSG(stranded(), "strand"); + ////LOG("Outbound channel stop [" << channel->authority() << "] " //// "(" << id << ") " << ec.message()); - untake(ec, id, channel); // The channel stopped following connection, try again without delay. @@ -367,7 +369,9 @@ void session_outbound::untake(const code& ec, size_t, if (!ec || stopped()) { + // Set address to current time and services from peer version message. const auto peer = channel->updated_address(); + ////LOG("Untake [" << config::address(peer) << "] (" << id << ") " //// << ec.message()); restore(peer, BIND1(handle_untake, _1)); From 7844aceca3aa3e5602efc2dbbf1e29079ab45d24 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 03:15:22 -0800 Subject: [PATCH 13/14] Add to_local_time(uint32_t zulu) utility for logging. --- include/bitcoin/network/async/time.hpp | 7 +++++-- src/async/time.cpp | 21 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/include/bitcoin/network/async/time.hpp b/include/bitcoin/network/async/time.hpp index a0d7f41a3..9fcf1dea7 100644 --- a/include/bitcoin/network/async/time.hpp +++ b/include/bitcoin/network/async/time.hpp @@ -43,15 +43,18 @@ typedef steady_clock::time_point time_point; /// C++20: std::chrono::system_clock measures Unix Time. typedef std::chrono::system_clock wall_clock; -/// Current zulu (utc) time using the wall clock. +/// Current zulu (utc) time using the wall clock, as time_t. BCT_API time_t zulu_time() NOEXCEPT; /// Current zulu (utc) time using the wall clock, cast to uint32_t. BCT_API uint32_t unix_time() NOEXCEPT; -/// Standard date-time string, e.g. Sun Oct 17 04:41:13 2010, locale dependent. +/// Current local date-time text, locale dependent (Sun Oct 17 04:41:13 2010). BCT_API std::string local_time() NOEXCEPT; +/// Specified zulu (utc) time as, conversion to local date-time text. +BCT_API std::string BCT_API to_local_time(uint32_t zulu) NOEXCEPT; + } // namespace network } // namespace libbitcoin diff --git a/src/async/time.cpp b/src/async/time.cpp index b394828e1..8cc406c5f 100644 --- a/src/async/time.cpp +++ b/src/async/time.cpp @@ -42,7 +42,7 @@ uint32_t unix_time() NOEXCEPT static bool local_time(tm& out_local, time_t zulu) NOEXCEPT { // localtime not threadsafe due to static buffer return, use localtime_s. -#ifdef _MSC_VER +#ifdef HAVE_MSC // proprietary msvc implemention, parameters swapped, returns errno_t. return localtime_s(&out_local, &zulu) == 0; #else @@ -51,10 +51,11 @@ static bool local_time(tm& out_local, time_t zulu) NOEXCEPT #endif } -std::string local_time() NOEXCEPT +// local +static std::string local_time(time_t zulu) NOEXCEPT { tm out_local{}; - if (!local_time(out_local, zulu_time())) + if (!local_time(out_local, zulu)) return ""; // %c writes standard date and time string, e.g. @@ -65,7 +66,21 @@ std::string local_time() NOEXCEPT // std::strftime is required because gcc doesn't implement std::put_time. // Returns number of characters, zero implies failure and undefined buffer. + BC_PUSH_WARNING(NO_ARRAY_TO_POINTER_DECAY) return is_zero(std::strftime(buffer, size, format, &out_local)) ? "" : buffer; + BC_POP_WARNING() +} + +std::string local_time() NOEXCEPT +{ + return local_time(zulu_time()); +} + +std::string to_local_time(uint32_t zulu) NOEXCEPT +{ + BC_PUSH_WARNING(NO_STATIC_CAST) + return local_time(static_cast(zulu)); + BC_POP_WARNING() } } // namespace network From 2fcb3646f199174d387a67480baea9d91106c229 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Wed, 15 Feb 2023 03:15:46 -0800 Subject: [PATCH 14/14] Use HAVE_MSC macro vs. _MSC_VER. --- src/async/thread.cpp | 4 ++-- test/async/thread.cpp | 8 ++++---- test/test.hpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/async/thread.cpp b/src/async/thread.cpp index e5143745a..69e15d235 100644 --- a/src/async/thread.cpp +++ b/src/async/thread.cpp @@ -21,7 +21,7 @@ #include #include -#ifdef _MSC_VER +#ifdef HAVE_MSC #include #else #include @@ -62,7 +62,7 @@ void set_priority(thread_priority priority) NOEXCEPT { const auto prioritization = get_priority(priority); -#if defined(_MSC_VER) +#if defined(HAVE_MSC) SetThreadPriority(GetCurrentThread(), prioritization); #elif defined(PRIO_THREAD) setpriority(PRIO_THREAD, pthread_self(), prioritization); diff --git a/test/async/thread.cpp b/test/async/thread.cpp index 301451815..3d516ae09 100644 --- a/test/async/thread.cpp +++ b/test/async/thread.cpp @@ -18,7 +18,7 @@ */ #include "../test.hpp" -#ifdef _MSC_VER +#if defined(HAVE_MSC) #include #else #include @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_SUITE(thread_tests) static int get_thread_priority_test() { -#if defined(_MSC_VER) +#if defined(HAVE_MSC) return GetThreadPriority(GetCurrentThread()); #elif defined(PRIO_THREAD) return getpriority(PRIO_LWP, pthread_self()); @@ -49,7 +49,7 @@ static int get_thread_priority_test() void set_thread_priority_test(int priority) { -#if defined(_MSC_VER) +#if defined(HAVE_MSC) SetThreadPriority(GetCurrentThread(), priority); #elif defined(PRIO_THREAD) setpriority(PRIO_THREAD, pthread_self(), priority); @@ -58,7 +58,7 @@ void set_thread_priority_test(int priority) #endif } -#ifdef _MSC_VER +#ifdef HAVE_MSC BOOST_AUTO_TEST_CASE(thread__set_thread_priorites__all__set_as_expected) { diff --git a/test/test.hpp b/test/test.hpp index 50a216bd3..7c1295451 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -39,12 +39,12 @@ #define TEST_PATH \ TEST_DIRECTORY + "/" + TEST_NAME -#ifdef _MSC_VER +#ifdef HAVE_MSC #define NO_GLOBAL_INIT_CALLS 26426 #define NO_UNUSED_LOCAL_SMART_PTR 26414 #endif -#ifdef _MSC_VER +#ifdef HAVE_MSC BC_DISABLE_WARNING(NO_ARRAY_INDEXING) BC_DISABLE_WARNING(NO_GLOBAL_INIT_CALLS) BC_DISABLE_WARNING(NO_UNUSED_LOCAL_SMART_PTR) @@ -90,7 +90,7 @@ namespace test { // Common directory for all test file creations. // Subdirectories and/or files must be differentiated (i.e. by TEST_NAME). -// Total path length cannot exceed MAX_PATH in _MSC_VER builds. +// Total path length cannot exceed MAX_PATH in HAVE_MSC builds. extern const std::string directory; bool clear(const std::filesystem::path& directory) noexcept;