rippled
Loading...
Searching...
No Matches
ProtocolVersion.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2019 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/overlay/detail/ProtocolVersion.h>
21#include <xrpl/beast/core/LexicalCast.h>
22#include <xrpl/beast/rfc2616.h>
23#include <boost/iterator/function_output_iterator.hpp>
24#include <boost/regex.hpp>
25#include <algorithm>
26#include <functional>
27
28namespace ripple {
29
36// clang-format off
38{
39 {2, 1},
40 {2, 2}
41};
42// clang-format on
43
44// This ugly construct ensures that supportedProtocolList is sorted in strictly
45// ascending order and doesn't contain any duplicates.
46// FIXME: With C++20 we can use std::is_sorted with an appropriate comparator
47static_assert(
48 []() constexpr -> bool {
49 auto const len = std::distance(
51
52 // There should be at least one protocol we're willing to speak.
53 if (len == 0)
54 return false;
55
56 // A list with only one entry is, by definition, sorted so we don't
57 // need to check it.
58 if (len != 1)
59 {
60 for (auto i = 0; i != len - 1; ++i)
61 {
63 return false;
64 }
65 }
66
67 return true;
68 }(),
69 "The list of supported protocols isn't properly sorted.");
70
73{
74 return "XRPL/" + std::to_string(p.first) + "." + std::to_string(p.second);
75}
76
78parseProtocolVersions(boost::beast::string_view const& value)
79{
80 static boost::regex re(
81 "^" // start of line
82 "XRPL/" // The string "XRPL/"
83 "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading
84 // zeroes)
85 "\\." // a period
86 "(0|(?:[1-9][0-9]*))" // a number (no leading zeroes unless exactly
87 // zero)
88 "$" // The end of the string
89 ,
90 boost::regex_constants::optimize);
91
93
94 for (auto const& s : beast::rfc2616::split_commas(value))
95 {
96 boost::smatch m;
97
98 if (boost::regex_match(s, m, re))
99 {
100 std::uint16_t major;
101 std::uint16_t minor;
102 if (!beast::lexicalCastChecked(major, std::string(m[1])))
103 continue;
104
105 if (!beast::lexicalCastChecked(minor, std::string(m[2])))
106 continue;
107
108 auto const proto = make_protocol(major, minor);
109
110 // This is an extra sanity check: we check that the protocol we just
111 // decoded corresponds to the token we were parsing.
112 if (to_string(proto) == s)
113 result.push_back(make_protocol(major, minor));
114 }
115 }
116
117 // We guarantee that the returned list is sorted and contains no duplicates:
118 std::sort(result.begin(), result.end());
119 result.erase(std::unique(result.begin(), result.end()), result.end());
120
121 return result;
122}
123
126{
128
129 // The protocol version we want to negotiate is the largest item in the
130 // intersection of the versions supported by us and the peer. Since the
131 // output of std::set_intersection is sorted, that item is always going
132 // to be the last one. So we get a little clever and avoid the need for
133 // a container:
134 std::function<void(ProtocolVersion const&)> pickVersion =
135 [&result](ProtocolVersion const& v) { result = v; };
136
138 std::begin(versions),
139 std::end(versions),
142 boost::make_function_output_iterator(pickVersion));
143
144 return result;
145}
146
148negotiateProtocolVersion(boost::beast::string_view const& versions)
149{
150 auto const them = parseProtocolVersions(versions);
151
152 return negotiateProtocolVersion(them);
153}
154
155std::string const&
157{
158 static std::string const supported = []() {
159 std::string ret;
160 for (auto const& v : supportedProtocolList)
161 {
162 if (!ret.empty())
163 ret += ", ";
164 ret += to_string(v);
165 }
166
167 return ret;
168 }();
169
170 return supported;
171}
172
173bool
175{
177 std::find(
180 v);
181}
182
183} // namespace ripple
T begin(T... args)
T distance(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
Result split_commas(FwdIt first, FwdIt last)
Definition: rfc2616.h:204
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Definition: LexicalCast.h:201
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::optional< ProtocolVersion > negotiateProtocolVersion(std::vector< ProtocolVersion > const &versions)
Given a list of supported protocol versions, choose the one we prefer.
std::vector< ProtocolVersion > parseProtocolVersions(boost::beast::string_view const &value)
Parse a set of protocol versions.
constexpr ProtocolVersion const supportedProtocolList[]
The list of protocol versions we speak and we prefer to use.
constexpr ProtocolVersion make_protocol(std::uint16_t major, std::uint16_t minor)
std::string const & supportedProtocolVersions()
The list of all the protocol versions we support.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
bool isProtocolSupported(ProtocolVersion const &v)
Determine whether we support a specific protocol version.
T push_back(T... args)
T set_intersection(T... args)
T sort(T... args)
T to_string(T... args)
T unique(T... args)