xrpld
Loading...
Searching...
No Matches
cluster_test.cpp
1#include <test/jtx/TestSuite.h>
2#include <test/unit_test/SuiteJournal.h>
3
4#include <xrpld/overlay/Cluster.h>
5
6#include <xrpl/basics/chrono.h>
7#include <xrpl/beast/unit_test/suite.h>
8#include <xrpl/config/BasicConfig.h>
9#include <xrpl/protocol/KeyType.h>
10#include <xrpl/protocol/PublicKey.h>
11#include <xrpl/protocol/SecretKey.h>
12#include <xrpl/protocol/tokens.h>
13
14#include <algorithm>
15#include <chrono>
16#include <cstddef>
17#include <cstdint>
18#include <memory>
19#include <vector>
20
21namespace xrpl::tests {
22
24{
26
27public:
28 cluster_test() : journal_("cluster_test", *this)
29 {
30 }
31
34 {
35 auto cluster = std::make_unique<Cluster>(journal_);
36
37 for (auto const& n : nodes)
38 cluster->update(n, "Test");
39
40 return cluster;
41 }
42
43 static PublicKey
48
49 void
51 {
52 // The servers on the network
54
55 while (network.size() != 128)
56 network.push_back(randomNode());
57
58 {
59 testcase("Membership: Empty cluster");
60
61 auto c = create({});
62
63 for (auto const& n : network)
64 BEAST_EXPECT(!c->member(n));
65 }
66
67 {
68 testcase("Membership: Non-empty cluster and none present");
69
71 while (cluster.size() != 32)
72 cluster.push_back(randomNode());
73
74 auto c = create(cluster);
75
76 for (auto const& n : network)
77 BEAST_EXPECT(!c->member(n));
78 }
79
80 {
81 testcase("Membership: Non-empty cluster and some present");
82
83 std::vector<PublicKey> cluster(network.begin(), network.begin() + 16);
84
85 while (cluster.size() != 32)
86 cluster.push_back(randomNode());
87
88 auto c = create(cluster);
89
90 for (auto const& n : cluster)
91 BEAST_EXPECT(c->member(n));
92
93 for (auto const& n : network)
94 {
95 auto found = std::ranges::find(cluster, n);
96 BEAST_EXPECT(static_cast<bool>(c->member(n)) == (found != cluster.end()));
97 }
98 }
99
100 {
101 testcase("Membership: isMember agrees with member");
102
103 // Number of network nodes that also belong to the cluster.
104 std::size_t const overlapCount = 16;
105
106 // Total size of the cluster once padded with non-network nodes.
107 std::size_t const clusterSize = 32;
108
109 std::vector<PublicKey> cluster(network.begin(), network.begin() + overlapCount);
110
111 while (cluster.size() != clusterSize)
112 cluster.push_back(randomNode());
113
114 auto c = create(cluster);
115
116 for (auto const& n : cluster)
117 BEAST_EXPECT(c->isMember(n));
118
119 for (auto const& n : network)
120 BEAST_EXPECT(c->isMember(n) == static_cast<bool>(c->member(n)));
121 }
122
123 {
124 testcase("Membership: Non-empty cluster and all present");
125
126 std::vector<PublicKey> cluster(network.begin(), network.begin() + 32);
127
128 auto c = create(cluster);
129
130 for (auto const& n : cluster)
131 BEAST_EXPECT(c->member(n));
132
133 for (auto const& n : network)
134 {
135 auto found = std::ranges::find(cluster, n);
136 BEAST_EXPECT(static_cast<bool>(c->member(n)) == (found != cluster.end()));
137 }
138 }
139 }
140
141 void
143 {
144 testcase("Updating");
145
146 auto c = create({});
147
148 auto const node = randomNode();
149 auto const name = toBase58(TokenType::NodePublic, node);
150 std::uint32_t const load = 0;
151 NetClock::time_point tick = {};
152
153 // Initial update
154 BEAST_EXPECT(c->update(node, "", load, tick));
155 {
156 auto member = c->member(node);
157 BEAST_EXPECT(static_cast<bool>(member));
158 BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access)
159 }
160
161 // Updating too quickly: should fail
162 BEAST_EXPECT(!c->update(node, name, load, tick));
163 {
164 auto member = c->member(node);
165 BEAST_EXPECT(static_cast<bool>(member));
166 BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access)
167 }
168
169 using namespace std::chrono_literals;
170
171 // Updating the name (empty updates to non-empty)
172 tick += 1s;
173 BEAST_EXPECT(c->update(node, name, load, tick));
174 {
175 auto member = c->member(node);
176 BEAST_EXPECT(static_cast<bool>(member));
177 BEAST_EXPECT(*member == name); // NOLINT(bugprone-unchecked-optional-access)
178 }
179
180 // Updating the name (non-empty doesn't go to empty)
181 tick += 1s;
182 BEAST_EXPECT(c->update(node, "", load, tick));
183 {
184 auto member = c->member(node);
185 BEAST_EXPECT(static_cast<bool>(member));
186 BEAST_EXPECT(*member == name); // NOLINT(bugprone-unchecked-optional-access)
187 }
188
189 // Updating the name (non-empty updates to new non-empty)
190 tick += 1s;
191 BEAST_EXPECT(c->update(node, "test", load, tick));
192 {
193 auto member = c->member(node);
194 BEAST_EXPECT(static_cast<bool>(member));
195 BEAST_EXPECT(*member == "test"); // NOLINT(bugprone-unchecked-optional-access)
196 }
197 }
198
199 void
201 {
202 testcase("Config Load");
203
205
206 // The servers on the network
208
209 while (network.size() != 8)
210 network.push_back(randomNode());
211
212 auto format = [](PublicKey const& publicKey, char const* comment = nullptr) {
213 auto ret = toBase58(TokenType::NodePublic, publicKey);
214
215 if (comment)
216 ret += comment;
217
218 return ret;
219 };
220
221 Section s1;
222
223 // Correct (empty) configuration
224 BEAST_EXPECT(c->load(s1));
225 BEAST_EXPECT(c->size() == 0);
226
227 // Correct configuration
228 s1.append(format(network[0]));
229 s1.append(format(network[1], " "));
230 s1.append(format(network[2], " Comment"));
231 s1.append(format(network[3], " Multi Word Comment"));
232 s1.append(format(network[4], " Leading Whitespace"));
233 s1.append(format(network[5], " Trailing Whitespace "));
234 s1.append(format(network[6], " Leading & Trailing Whitespace "));
235 s1.append(format(network[7], " Leading, Trailing & Internal Whitespace "));
236
237 BEAST_EXPECT(c->load(s1));
238
239 for (auto const& n : network)
240 BEAST_EXPECT(c->member(n));
241
242 // Incorrect configurations
243 Section s2;
244 s2.append("NotAPublicKey");
245 BEAST_EXPECT(!c->load(s2));
246
247 Section s3;
248 s3.append(format(network[0], "!"));
249 BEAST_EXPECT(!c->load(s3));
250
251 Section s4;
252 s4.append(format(network[0], "! Comment"));
253 BEAST_EXPECT(!c->load(s4));
254
255 // Check if we properly terminate when we encounter
256 // a malformed or unparsable entry:
257 auto const node1 = randomNode();
258 auto const node2 = randomNode();
259
260 Section s5;
261 s5.append(format(node1, "XXX"));
262 s5.append(format(node2));
263 BEAST_EXPECT(!c->load(s5));
264 BEAST_EXPECT(!c->member(node1));
265 BEAST_EXPECT(!c->member(node2));
266 }
267
268 void
269 run() override
270 {
272 testUpdating();
274 }
275};
276
277BEAST_DEFINE_TESTSUITE(cluster, overlay, xrpl);
278
279} // namespace xrpl::tests
T begin(T... args)
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
std::chrono::time_point< NetClock > time_point
Definition chrono.h:48
A public key.
Definition PublicKey.h:53
Holds a collection of configuration values.
Definition BasicConfig.h:28
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
static PublicKey randomNode()
test::SuiteJournal journal_
void run() override
Runs the suite.
std::unique_ptr< Cluster > create(std::vector< PublicKey > const &nodes)
T end(T... args)
T find(T... args)
T make_unique(T... args)
BEAST_DEFINE_TESTSUITE(LedgerNodeHelpers, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
T push_back(T... args)
T size(T... args)