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
15 changes: 12 additions & 3 deletions include/bitcoin/server/parsers/electrum_version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#ifndef LIBBITCOIN_SERVER_PARSERS_ELECTRUM_VERSION_HPP
#define LIBBITCOIN_SERVER_PARSERS_ELECTRUM_VERSION_HPP

#include <unordered_map>
#include <bitcoin/server/define.hpp>

namespace libbitcoin {
Expand Down Expand Up @@ -78,8 +77,18 @@ enum class version
v1_7
};

std::string_view version_to_string(version value) NOEXCEPT;
version version_from_string(const std::string_view& value) NOEXCEPT;
/// The numeric form of a defined version (0.0 if undefined).
system::config::version version_to_number(version value) NOEXCEPT;

/// Serialized form of a defined version ("0.0" if undefined).
std::string version_to_string(version value) NOEXCEPT;

/// Parse any dotted numeric version, defined or not (false if malformed).
bool version_from_string(system::config::version& out,
const std::string_view& value) NOEXCEPT;

/// The greatest defined version not exceeding value (v0_0 if none).
version version_floor(const system::config::version& value) NOEXCEPT;

} // namespace electrum
} // namespace server
Expand Down
4 changes: 0 additions & 4 deletions include/bitcoin/server/protocols/protocol_electrum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <bitcoin/server/define.hpp>
#include <bitcoin/server/interfaces/interfaces.hpp>
#include <bitcoin/server/parsers/parsers.hpp>
#include <bitcoin/server/protocols/protocol_electrum_version.hpp>
#include <bitcoin/server/protocols/protocol_rpc.hpp>

namespace libbitcoin {
Expand Down Expand Up @@ -320,9 +319,6 @@ class BCS_API protocol_electrum
// Aliases.
using array_t = network::rpc::array_t;
using object_t = network::rpc::object_t;
using version_t = protocol_electrum_version;
static constexpr electrum::version minimum = version_t::minimum;
static constexpr electrum::version maximum = version_t::maximum;

// Post to notification strand.
template <class Derived, typename Method, typename... Args>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ class BCS_API protocol_electrum_version
typedef std::shared_ptr<protocol_electrum_version> ptr;
using rpc_interface = interface::electrum;

static constexpr electrum::version minimum = electrum::version::v1_0;
static constexpr electrum::version maximum = electrum::version::v1_7;

inline protocol_electrum_version(const auto& session,
const network::channel::ptr& channel,
const options_t& options) NOEXCEPT
Expand All @@ -60,10 +57,10 @@ class BCS_API protocol_electrum_version
rpc_interface::server_version, const std::string& client_name,
const interface::value_t& protocol_version) NOEXCEPT;

electrum::version version() const NOEXCEPT;
std::string_view negotiated_version() const NOEXCEPT;
std::string negotiated_version() const NOEXCEPT;
bool set_version(const interface::value_t& version) NOEXCEPT;
bool get_versions(electrum::version& min, electrum::version& max,
bool get_versions(system::config::version& min,
system::config::version& max,
const interface::value_t& version) NOEXCEPT;

std::string_view server_name() const NOEXCEPT;
Expand Down
116 changes: 77 additions & 39 deletions src/parsers/electrum_version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
*/
#include <bitcoin/server/parsers/electrum_version.hpp>

#include <algorithm>
#include <array>
#include <utility>
#include <bitcoin/server/define.hpp>

namespace libbitcoin {
Expand All @@ -26,52 +29,87 @@ namespace electrum {

BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)

std::string_view version_to_string(version value) NOEXCEPT
// Defined versions with their numeric forms, in ascending order.
static const auto& versions() NOEXCEPT
{
static const std::unordered_map<version, std::string_view> map
{
{ version::v0_0, "0.0" },
{ version::v0_6, "0.6" },
{ version::v0_8, "0.8" },
{ version::v0_9, "0.9" },
{ version::v0_10, "0.10" },
{ version::v1_0, "1.0" },
{ version::v1_1, "1.1" },
{ version::v1_2, "1.2" },
{ version::v1_3, "1.3" },
{ version::v1_4, "1.4" },
{ version::v1_4_1, "1.4.1" },
{ version::v1_4_2, "1.4.2" },
{ version::v1_6, "1.6" },
{ version::v1_7, "1.7" }
};
static const std::array<std::pair<version, system::config::version>, 14>
map
{{
{ version::v0_0, { 0, 0 } },
{ version::v0_6, { 0, 6 } },
{ version::v0_8, { 0, 8 } },
{ version::v0_9, { 0, 9 } },
{ version::v0_10, { 0, 10 } },
{ version::v1_0, { 1, 0 } },
{ version::v1_1, { 1, 1 } },
{ version::v1_2, { 1, 2 } },
{ version::v1_3, { 1, 3 } },
{ version::v1_4, { 1, 4 } },
{ version::v1_4_1, { 1, 4, 1 } },
{ version::v1_4_2, { 1, 4, 2 } },
{ version::v1_6, { 1, 6 } },
{ version::v1_7, { 1, 7 } }
}};

const auto it = map.find(value);
return it != map.end() ? it->second : "0.0";
return map;
}

version version_from_string( const std::string_view& value) NOEXCEPT
system::config::version version_to_number(version value) NOEXCEPT
{
static const std::unordered_map<std::string_view, version> map
const auto& map = versions();
const auto it = std::find_if(map.begin(), map.end(),
[=](const auto& entry) NOEXCEPT
{
return entry.first == value;
});

return it != map.end() ? it->second : system::config::version{};
}

std::string version_to_string(version value) NOEXCEPT
{
return version_to_number(value).to_string();
}

bool version_from_string(system::config::version& out,
const std::string_view& value) NOEXCEPT
{
// Stream extraction tolerates sign, whitespace and trailing garbage, and
// split elides empty segments, so require well-formed dotted digits.
auto result = !value.empty()
&& value.front() != '.'
&& value.back() != '.'
&& value.find("..") == std::string_view::npos
&& std::all_of(value.begin(), value.end(), [](char character) NOEXCEPT
{
return system::is_ascii_number(character) || character == '.';
});

if (result)
{
{ "0.0", version::v0_0 },
{ "0.6", version::v0_6 },
{ "0.8", version::v0_8 },
{ "0.9", version::v0_9 },
{ "0.10", version::v0_10 },
{ "1.0", version::v1_0 },
{ "1.1", version::v1_1 },
{ "1.2", version::v1_2 },
{ "1.3", version::v1_3 },
{ "1.4", version::v1_4 },
{ "1.4.1", version::v1_4_1 },
{ "1.4.2", version::v1_4_2 },
{ "1.6", version::v1_6 },
{ "1.7", version::v1_7 }
};
try
{
out = system::config::version{ std::string{ value } };
}
catch (const std::exception&)
{
result = false;
}
}

return result;
}

version version_floor(const system::config::version& value) NOEXCEPT
{
const auto& map = versions();
const auto it = std::find_if(map.rbegin(), map.rend(),
[&](const auto& entry) NOEXCEPT
{
return entry.second <= value;
});

const auto it = map.find(value);
return it != map.end() ? it->second : version::v0_0;
return it != map.rend() ? it->first : version::v0_0;
}

BC_POP_WARNING()
Expand Down
4 changes: 2 additions & 2 deletions src/protocols/electrum/protocol_electrum_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ void protocol_electrum::handle_server_features(const code& ec,
{ "genesis_hash", encode_hash(hash) },
{ "hosts", self_hosts() },
{ "server_version", options().server_name },
{ "protocol_min", string_t{ version_to_string(minimum) } },
{ "protocol_max", string_t{ version_to_string(maximum) } },
{ "protocol_min", string_t{ options().protocol_minimum.to_string() } },
{ "protocol_max", string_t{ options().protocol_maximum.to_string() } },
{ "pruning", null_t{} }
};

Expand Down
42 changes: 25 additions & 17 deletions src/protocols/electrum/protocol_electrum_version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,32 +154,35 @@ std::string protocol_electrum_version::escape_client(
// Negotiated version.
// ----------------------------------------------------------------------------

std::string_view protocol_electrum_version::negotiated_version() const NOEXCEPT
std::string protocol_electrum_version::negotiated_version() const NOEXCEPT
{
return electrum::version_to_string(channel_->version());
}

bool protocol_electrum_version::set_version(const value_t& version) NOEXCEPT
{
electrum::version client_min{};
electrum::version client_max{};
system::config::version client_min{};
system::config::version client_max{};
if (!get_versions(client_min, client_max, version))
return false;

const auto lower = std::max(client_min, minimum);
const auto upper = std::min(client_max, maximum);
if (lower > upper)
// Clients may specify undefined (e.g. future) versions, negotiation is
// numeric and settles on the greatest defined version in the overlap.
const auto lower = std::max(client_min, options().protocol_minimum);
const auto upper = std::min(client_max, options().protocol_maximum);
const auto floor = electrum::version_floor(upper);
if (electrum::version_to_number(floor) < lower)
return false;

LOGA("Electrum [" << opposite() << "] version ("
<< electrum::version_to_string(client_max) << ") " << client_name());
<< client_max.to_string() << ") " << client_name());

channel_->set_version(upper);
channel_->set_version(floor);
return true;
}

bool protocol_electrum_version::get_versions(electrum::version& min,
electrum::version& max, const interface::value_t& version) NOEXCEPT
bool protocol_electrum_version::get_versions(system::config::version& min,
system::config::version& max, const interface::value_t& version) NOEXCEPT
{
// Optional value_t can be string_t or array_t of two string_t.
const auto& value = version.value();
Expand All @@ -188,16 +191,21 @@ bool protocol_electrum_version::get_versions(electrum::version& min,
if (std::holds_alternative<null_t>(value))
{
// An interface default can't be set for optional<value_t>.
max = min = maximum;
// An unspecified version accepts any, subject to configured limits.
min = {};
max = options().protocol_maximum;
return true;
}

// One version.
if (std::holds_alternative<string_t>(value))
{
// A single value implies minimum is the same as maximum.
max = min = electrum::version_from_string(std::get<string_t>(value));
return min != electrum::version::v0_0;
if (!electrum::version_from_string(min, std::get<string_t>(value)))
return false;

max = min;
return true;
}

// Two versions.
Expand All @@ -214,10 +222,10 @@ bool protocol_electrum_version::get_versions(electrum::version& min,
!std::holds_alternative<string_t>(max_version))
return false;

min = electrum::version_from_string(std::get<string_t>(min_version));
max = electrum::version_from_string(std::get<string_t>(max_version));
return min != electrum::version::v0_0
&& max != electrum::version::v0_0;
return electrum::version_from_string(min,
std::get<string_t>(min_version))
&& electrum::version_from_string(max,
std::get<string_t>(max_version));
}

return false;
Expand Down
Loading
Loading