Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion builds/cmake/install-cmake.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ if [[ -z ${secp256k1_OWNER} ]]; then
secp256k1_OWNER="bitcoin-core"
fi
if [[ -z ${secp256k1_TAG} ]]; then
secp256k1_TAG="v0.7.0"
secp256k1_TAG="v0.8.0"
fi

if [[ -z ${UltrafastSecp256k1_OWNER} ]]; then
Expand Down
2 changes: 1 addition & 1 deletion builds/cmake/install-presets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ if [[ -z ${secp256k1_OWNER} ]]; then
secp256k1_OWNER="bitcoin-core"
fi
if [[ -z ${secp256k1_TAG} ]]; then
secp256k1_TAG="v0.7.0"
secp256k1_TAG="v0.8.0"
fi

if [[ -z ${UltrafastSecp256k1_OWNER} ]]; then
Expand Down
2 changes: 1 addition & 1 deletion builds/gnu/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ AC_SUBST([pkgconfigdir],[${with_pkgconfigdir}])
AC_MSG_CHECKING([--enable-ndebug option])
AC_ARG_ENABLE([ndebug],
AS_HELP_STRING([--enable-ndebug],
[Compile with NDEBUG assertion. @<:@default=yes@:>@]),
[Compile with NDEBUG (no debug assertions). @<:@default=yes@:>@]),
[enable_ndebug=$enableval],
[enable_ndebug=yes])
AC_MSG_RESULT([$enable_ndebug])
Expand Down
2 changes: 1 addition & 1 deletion builds/gnu/install-gnu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ if [[ -z ${secp256k1_OWNER} ]]; then
secp256k1_OWNER="bitcoin-core"
fi
if [[ -z ${secp256k1_TAG} ]]; then
secp256k1_TAG="v0.7.0"
secp256k1_TAG="v0.8.0"
fi

if [[ -z ${UltrafastSecp256k1_OWNER} ]]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@
<Import Project="$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets" Condition="Exists('$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets')" />
<Import Project="$(NuGetPackageRoot)libbitcoin-boost_unit_test_framework-vc145.1.91.0\build\libbitcoin-boost_unit_test_framework-vc145.targets" Condition="Exists('$(NuGetPackageRoot)libbitcoin-boost_unit_test_framework-vc145.1.91.0\build\libbitcoin-boost_unit_test_framework-vc145.targets')" />
<Import Project="$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets" Condition="Exists('$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets')" />
<Import Project="$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets" Condition="Exists('$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets')" />
<Import Project="$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets" Condition="Exists('$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets')" />
<Import Project="$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets" Condition="Exists('$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
Expand All @@ -438,7 +438,7 @@
<Error Condition="!Exists('$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)libbitcoin-boost_unit_test_framework-vc145.1.91.0\build\libbitcoin-boost_unit_test_framework-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)libbitcoin-boost_unit_test_framework-vc145.1.91.0\build\libbitcoin-boost_unit_test_framework-vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets'))" />
</Target>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion builds/msvc/vs2026/libbitcoin-network-test/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
<package id="libbitcoin-boost_thread-vc145" version="1.91.0" targetFramework="Native" />
<package id="libbitcoin-boost_unit_test_framework-vc145" version="1.91.0" targetFramework="Native" />
<package id="libbitcoin-boost_url-vc145" version="1.91.0" targetFramework="Native" />
<package id="secp256k1_vc145" version="0.6.1.2" targetFramework="Native" />
<package id="secp256k1_vc145" version="0.8.0" targetFramework="Native" />
</packages>
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@
<Import Project="$(NuGetPackageRoot)libbitcoin-boost_program_options-vc145.1.91.0\build\libbitcoin-boost_program_options-vc145.targets" Condition="Exists('$(NuGetPackageRoot)libbitcoin-boost_program_options-vc145.1.91.0\build\libbitcoin-boost_program_options-vc145.targets')" />
<Import Project="$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets" Condition="Exists('$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets')" />
<Import Project="$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets" Condition="Exists('$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets')" />
<Import Project="$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets" Condition="Exists('$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets')" />
<Import Project="$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets" Condition="Exists('$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets')" />
<Import Project="$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets" Condition="Exists('$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
Expand All @@ -613,7 +613,7 @@
<Error Condition="!Exists('$(NuGetPackageRoot)libbitcoin-boost_program_options-vc145.1.91.0\build\libbitcoin-boost_program_options-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)libbitcoin-boost_program_options-vc145.1.91.0\build\libbitcoin-boost_program_options-vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)libbitcoin-boost_thread-vc145.1.91.0\build\libbitcoin-boost_thread-vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)libbitcoin-boost_url-vc145.1.91.0\build\libbitcoin-boost_url-vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)secp256k1_vc145.0.6.1.2\build\native\secp256k1_vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)secp256k1_vc145.0.8.0\build\native\secp256k1_vc145.targets'))" />
<Error Condition="!Exists('$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(NuGetPackageRoot)UltrafastSecp256k1-vc145.4.5.0.0\build\native\UltrafastSecp256k1-vc145.targets'))" />
</Target>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion builds/msvc/vs2026/libbitcoin-network/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
<package id="libbitcoin-boost_program_options-vc145" version="1.91.0" targetFramework="Native" />
<package id="libbitcoin-boost_thread-vc145" version="1.91.0" targetFramework="Native" />
<package id="libbitcoin-boost_url-vc145" version="1.91.0" targetFramework="Native" />
<package id="secp256k1_vc145" version="0.6.1.2" targetFramework="Native" />
<package id="secp256k1_vc145" version="0.8.0" targetFramework="Native" />
</packages>
6 changes: 6 additions & 0 deletions include/bitcoin/network/channels/channel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ class BCT_API channel
const options_t& options() const NOEXCEPT;

protected:
/// The network and service rate limits overlap, so the more restrictive
/// applies. Zero is unlimited, and therefore the less restrictive, so the
/// maximum applies if either is zero (zero only if both are zero).
static uint32_t rate_limited(const settings_t& settings,
const options_t& options) NOEXCEPT;

/// Construct a channel to encapsulated and communicate on the socket.
channel(const logger& log, const socket::ptr& socket, uint64_t identifier,
const settings_t& settings, const options_t& options) NOEXCEPT;
Expand Down
27 changes: 26 additions & 1 deletion include/bitcoin/network/net/proxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <bitcoin/network/define.hpp>
#include <bitcoin/network/memory.hpp>
#include <bitcoin/network/messages/messages.hpp>
#include <bitcoin/network/net/deadline.hpp>
#include <bitcoin/network/net/socket.hpp>

namespace libbitcoin {
Expand All @@ -40,6 +41,11 @@ namespace network {
/// Completion handler is invoked once write is complete, at which point the
/// next queued write is invoked. When a channel stops with pending writes the
/// write queue is purged without invoke of the purged handlers.
/// Each send is allocated (bytes/rate_limit) of time, and its completion is
/// deferred by whatever portion of that allocation the write did not consume.
/// Since nothing is produced until the completion handler is invoked, this
/// throttles the channel without queueing. Reads are not metered, as they are
/// bounded by protocol correctness. Zero rate_limit disables the throttle.
class BCT_API proxy
: public enable_shared_from_base<proxy>, public reporter
{
Expand Down Expand Up @@ -111,7 +117,7 @@ class BCT_API proxy
const config::endpoint& endpoint() const NOEXCEPT;

protected:
proxy(const socket::ptr& socket) NOEXCEPT;
proxy(const socket::ptr& socket, uint32_t rate_limit) NOEXCEPT;

/// Stranded event, allows timer reset.
virtual void reading() NOEXCEPT;
Expand All @@ -122,6 +128,15 @@ class BCT_API proxy
/// Subscribe to stop notification (requires strand).
void subscribe_stop(result_handler&& handler) NOEXCEPT;

/// Throttle.
/// -----------------------------------------------------------------------

/// Unconsumed portion of the byte allocation, by which a send completing
/// now is deferred. Zero if unlimited, stopped, or fully consumed by the
/// transmission (requires strand).
steady_clock::duration unconsumed(size_t bytes,
const steady_clock::time_point& start) const NOEXCEPT;

/// Wait.
/// -----------------------------------------------------------------------

Expand Down Expand Up @@ -243,15 +258,25 @@ class BCT_API proxy
void handle_write(const code& ec, size_t bytes,
const count_handler& handler) NOEXCEPT;

// Meter sent bytes and defer the completion by the unconsumed allocation.
count_handler metered(count_handler&& handler) NOEXCEPT;
void handle_metered(const code& ec, size_t bytes,
const steady_clock::time_point& start,
const count_handler& handler) NOEXCEPT;
void handle_charge(const code&, const code& ec, size_t bytes,
const count_handler& handler) NOEXCEPT;

// Invoke reading() on strand.
void do_reading() NOEXCEPT;

// These are thread safe.
std::atomic_bool paused_{ true };
std::atomic<uint64_t> total_{};
const uint32_t rate_limit_;
socket::ptr socket_;

// These are protected by strand.
deadline::ptr throttle_;
stop_subscriber stop_subscriber_{};
socket::http_parser_ptr parser_{};
queue deferred_{};
Expand Down
11 changes: 10 additions & 1 deletion include/bitcoin/network/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ struct BCT_API settings
uint32_t maximum_request{ maximum_request_default };
uint32_t minimum_buffer{ maximum_request_default };

/// Service send rate limit, overlapping the network rate limit (see
/// settings::rate_limited). Zero is unlimited.
uint32_t rate_limit{ 0 };

/// Helpers.
virtual bool enabled() const NOEXCEPT;
virtual steady_clock::duration inactivity() const NOEXCEPT;
Expand Down Expand Up @@ -297,7 +301,12 @@ struct BCT_API settings
uint32_t handshake_timeout_seconds{ 15 };
uint32_t channel_heartbeat_minutes{ 5 };
uint32_t maximum_skew_minutes{ 120 };
uint32_t rate_limit{ 1024 };

/// Bytes/second allocated to each channel for sending, zero is unlimited.
/// A send is deferred by the unconsumed portion of its byte allocation,
/// which the next send of the channel cannot start until it expires.
/// Overlaps tcp_server::rate_limit (see settings::rate_limited).
uint32_t rate_limit{ 0 };
std::string user_agent{ BC_USER_AGENT };
std::filesystem::path path{};
config::authorities blacklists{};
Expand Down
16 changes: 14 additions & 2 deletions src/channels/channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
#include <bitcoin/network/channels/channel.hpp>

#include <algorithm>
#include <bitcoin/network/config/config.hpp>
#include <bitcoin/network/define.hpp>
#include <bitcoin/network/log/log.hpp>
Expand All @@ -41,12 +42,21 @@ inline deadline::ptr make_timer(const logger& log, asio::strand& strand,
emplace_shared<deadline>(log, strand, span) : nullptr;
}

// protected/static
uint32_t channel::rate_limited(const settings_t& settings,
const options_t& options) NOEXCEPT
{
return to_bool(settings.rate_limit) && to_bool(options.rate_limit) ?
std::min(settings.rate_limit, options.rate_limit) :
std::max(settings.rate_limit, options.rate_limit);
}

// Protocols invoke channel stop for application layer protocol violations.
// Channels invoke channel stop for channel timouts and communcation failures.
channel::channel(const logger& log, const socket::ptr& socket,
uint64_t identifier, const settings_t& settings,
const options_t& options) NOEXCEPT
: proxy(socket),
: proxy(socket, rate_limited(settings, options)),
options_(options),
settings_(settings),
identifier_(identifier),
Expand Down Expand Up @@ -112,7 +122,9 @@ void channel::handle_monitor(const code& ec) NOEXCEPT

// Timers.
// ----------------------------------------------------------------------------
// TODO: build DoS protection around rate_limit_, total(), and time.
// Send throttling (settings.rate_limit) is implemented by the proxy. A channel
// whose accrued deferral exceeds inactivity is dropped by that timer, which is
// the intended outcome (the throttle degrades to disconnection under abuse).
// A restarted timer invokes completion handler with error::operation_canceled.
// Called from start or strand.

Expand Down
21 changes: 19 additions & 2 deletions src/net/proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,21 @@ namespace network {

BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)

// Factory for variable deadline timer pointer construction (or null).
inline deadline::ptr make_throttle(const logger& log, asio::strand& strand,
uint32_t rate_limit) NOEXCEPT
{
return to_bool(rate_limit) ?
system::emplace_shared<deadline>(log, strand) : nullptr;
}

// This is created in a started state and must be stopped, as the subscribers
// assert if not stopped. Subscribers may hold protocols even if the service
// is not started.
proxy::proxy(const socket::ptr& socket) NOEXCEPT
: socket_(socket),
proxy::proxy(const socket::ptr& socket, uint32_t rate_limit) NOEXCEPT
: rate_limit_(rate_limit),
socket_(socket),
throttle_(make_throttle(socket->log, socket->strand(), rate_limit)),
reporter(socket->log)
{
}
Expand Down Expand Up @@ -75,6 +85,10 @@ void proxy::do_stop(const code& ec) NOEXCEPT
BC_ASSERT(stranded());
using namespace std::placeholders;

// The socket is not yet stopped, so a deferred send still holds the queue.
// Release it here so that the close part is not delayed by the throttle.
if (throttle_) throttle_->stop();

batched_ = false;
parted_ = false;

Expand Down Expand Up @@ -138,6 +152,9 @@ void proxy::stopping(const code& ec) NOEXCEPT
{
BC_ASSERT(stranded());

// Release any deferred send (fires pending charge with canceled).
if (throttle_) throttle_->stop();

// Release any http message parse in progress.
parser_.reset();

Expand Down
31 changes: 16 additions & 15 deletions src/net/proxy_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ void proxy::do_ws_write(const asio::const_buffer& payload, bool binary,
const count_handler& handler) NOEXCEPT
{
socket_->ws_write({ payload.data(), payload.size() }, binary,
std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler));
metered(std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler)));
}

// TCP (generic, fixed size).
Expand Down Expand Up @@ -103,8 +103,8 @@ void proxy::do_tcp_write(const asio::const_buffer& payload,
const count_handler& handler) NOEXCEPT
{
socket_->tcp_write({ payload.data(), payload.size() },
std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler));
metered(std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler)));
}

// RPC (TCP: electrum/stratum_v1, WS: btcd).
Expand Down Expand Up @@ -256,17 +256,17 @@ void proxy::do_response_write(const rpc::response_ptr& response,
}

socket_->rpc_write(std::move(*response),
std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler));
metered(std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler)));
}

// private
void proxy::do_notification_write(const rpc::request_ptr& notification,
const count_handler& handler) NOEXCEPT
{
socket_->rpc_notify(std::move(*notification),
std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler));
metered(std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler)));
}

// HTTP/WS (generic/rpc).
Expand Down Expand Up @@ -466,7 +466,8 @@ void proxy::write(http::response&& response,

if (parted_)
{
socket_->rpc_write_chunk(std::move(part), std::move(handler));
socket_->rpc_write_chunk(std::move(part),
metered(std::move(handler)));
return;
}

Expand All @@ -477,13 +478,13 @@ void proxy::write(http::response&& response,

const auto out = move_shared(std::move(part));
socket_->http_write_header(std::move(response),
std::bind(&proxy::handle_http_header_write,
shared_from_this(), _1, _2, out, std::move(handler)));
metered(std::bind(&proxy::handle_http_header_write,
shared_from_this(), _1, _2, out, std::move(handler))));
return;
}

// http is half duplex so there is no interleave risk.
socket_->http_write(std::move(response), std::move(handler));
socket_->http_write(std::move(response), metered(std::move(handler)));
}

// private
Expand All @@ -498,16 +499,16 @@ void proxy::handle_http_header_write(const code& ec, size_t bytes,
return;
}

socket_->rpc_write_chunk(std::move(*part), move_copy(handler));
socket_->rpc_write_chunk(std::move(*part), metered(move_copy(handler)));
}

// private
void proxy::do_http_write(const http::response_ptr& response,
const count_handler& handler) NOEXCEPT
{
socket_->http_write(std::move(*response),
std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler));
metered(std::bind(&proxy::handle_write,
shared_from_this(), _1, _2, handler)));
}

BC_POP_WARNING()
Expand Down
Loading
Loading