mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-22 23:00:55 +00:00
256 lines
7.0 KiB
C++
256 lines
7.0 KiB
C++
#include <test/jtx/TestSuite.h>
|
|
#include <test/unit_test/SuiteJournal.h>
|
|
|
|
#include <xrpld/overlay/Cluster.h>
|
|
|
|
#include <xrpl/basics/chrono.h>
|
|
#include <xrpl/beast/unit_test/suite.h>
|
|
#include <xrpl/config/BasicConfig.h>
|
|
#include <xrpl/protocol/KeyType.h>
|
|
#include <xrpl/protocol/PublicKey.h>
|
|
#include <xrpl/protocol/SecretKey.h>
|
|
#include <xrpl/protocol/tokens.h>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
namespace xrpl::tests {
|
|
|
|
class cluster_test : public xrpl::TestSuite
|
|
{
|
|
test::SuiteJournal journal_;
|
|
|
|
public:
|
|
cluster_test() : journal_("cluster_test", *this)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<Cluster>
|
|
create(std::vector<PublicKey> const& nodes)
|
|
{
|
|
auto cluster = std::make_unique<Cluster>(journal_);
|
|
|
|
for (auto const& n : nodes)
|
|
cluster->update(n, "Test");
|
|
|
|
return cluster;
|
|
}
|
|
|
|
static PublicKey
|
|
randomNode()
|
|
{
|
|
return derivePublicKey(KeyType::Secp256k1, randomSecretKey());
|
|
}
|
|
|
|
void
|
|
testMembership()
|
|
{
|
|
// The servers on the network
|
|
std::vector<PublicKey> network;
|
|
|
|
while (network.size() != 128)
|
|
network.push_back(randomNode());
|
|
|
|
{
|
|
testcase("Membership: Empty cluster");
|
|
|
|
auto c = create({});
|
|
|
|
for (auto const& n : network)
|
|
BEAST_EXPECT(!c->member(n));
|
|
}
|
|
|
|
{
|
|
testcase("Membership: Non-empty cluster and none present");
|
|
|
|
std::vector<PublicKey> cluster;
|
|
while (cluster.size() != 32)
|
|
cluster.push_back(randomNode());
|
|
|
|
auto c = create(cluster);
|
|
|
|
for (auto const& n : network)
|
|
BEAST_EXPECT(!c->member(n));
|
|
}
|
|
|
|
{
|
|
testcase("Membership: Non-empty cluster and some present");
|
|
|
|
std::vector<PublicKey> cluster(network.begin(), network.begin() + 16);
|
|
|
|
while (cluster.size() != 32)
|
|
cluster.push_back(randomNode());
|
|
|
|
auto c = create(cluster);
|
|
|
|
for (auto const& n : cluster)
|
|
BEAST_EXPECT(c->member(n));
|
|
|
|
for (auto const& n : network)
|
|
{
|
|
auto found = std::ranges::find(cluster, n);
|
|
BEAST_EXPECT(static_cast<bool>(c->member(n)) == (found != cluster.end()));
|
|
}
|
|
}
|
|
|
|
{
|
|
testcase("Membership: Non-empty cluster and all present");
|
|
|
|
std::vector<PublicKey> cluster(network.begin(), network.begin() + 32);
|
|
|
|
auto c = create(cluster);
|
|
|
|
for (auto const& n : cluster)
|
|
BEAST_EXPECT(c->member(n));
|
|
|
|
for (auto const& n : network)
|
|
{
|
|
auto found = std::ranges::find(cluster, n);
|
|
BEAST_EXPECT(static_cast<bool>(c->member(n)) == (found != cluster.end()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testUpdating()
|
|
{
|
|
testcase("Updating");
|
|
|
|
auto c = create({});
|
|
|
|
auto const node = randomNode();
|
|
auto const name = toBase58(TokenType::NodePublic, node);
|
|
std::uint32_t const load = 0;
|
|
NetClock::time_point tick = {};
|
|
|
|
// Initial update
|
|
BEAST_EXPECT(c->update(node, "", load, tick));
|
|
{
|
|
auto member = c->member(node);
|
|
BEAST_EXPECT(static_cast<bool>(member));
|
|
BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access)
|
|
}
|
|
|
|
// Updating too quickly: should fail
|
|
BEAST_EXPECT(!c->update(node, name, load, tick));
|
|
{
|
|
auto member = c->member(node);
|
|
BEAST_EXPECT(static_cast<bool>(member));
|
|
BEAST_EXPECT(member->empty()); // NOLINT(bugprone-unchecked-optional-access)
|
|
}
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
// Updating the name (empty updates to non-empty)
|
|
tick += 1s;
|
|
BEAST_EXPECT(c->update(node, name, load, tick));
|
|
{
|
|
auto member = c->member(node);
|
|
BEAST_EXPECT(static_cast<bool>(member));
|
|
BEAST_EXPECT(*member == name); // NOLINT(bugprone-unchecked-optional-access)
|
|
}
|
|
|
|
// Updating the name (non-empty doesn't go to empty)
|
|
tick += 1s;
|
|
BEAST_EXPECT(c->update(node, "", load, tick));
|
|
{
|
|
auto member = c->member(node);
|
|
BEAST_EXPECT(static_cast<bool>(member));
|
|
BEAST_EXPECT(*member == name); // NOLINT(bugprone-unchecked-optional-access)
|
|
}
|
|
|
|
// Updating the name (non-empty updates to new non-empty)
|
|
tick += 1s;
|
|
BEAST_EXPECT(c->update(node, "test", load, tick));
|
|
{
|
|
auto member = c->member(node);
|
|
BEAST_EXPECT(static_cast<bool>(member));
|
|
BEAST_EXPECT(*member == "test"); // NOLINT(bugprone-unchecked-optional-access)
|
|
}
|
|
}
|
|
|
|
void
|
|
testConfigLoad()
|
|
{
|
|
testcase("Config Load");
|
|
|
|
auto c = std::make_unique<Cluster>(journal_);
|
|
|
|
// The servers on the network
|
|
std::vector<PublicKey> network;
|
|
|
|
while (network.size() != 8)
|
|
network.push_back(randomNode());
|
|
|
|
auto format = [](PublicKey const& publicKey, char const* comment = nullptr) {
|
|
auto ret = toBase58(TokenType::NodePublic, publicKey);
|
|
|
|
if (comment)
|
|
ret += comment;
|
|
|
|
return ret;
|
|
};
|
|
|
|
Section s1;
|
|
|
|
// Correct (empty) configuration
|
|
BEAST_EXPECT(c->load(s1));
|
|
BEAST_EXPECT(c->size() == 0);
|
|
|
|
// Correct configuration
|
|
s1.append(format(network[0]));
|
|
s1.append(format(network[1], " "));
|
|
s1.append(format(network[2], " Comment"));
|
|
s1.append(format(network[3], " Multi Word Comment"));
|
|
s1.append(format(network[4], " Leading Whitespace"));
|
|
s1.append(format(network[5], " Trailing Whitespace "));
|
|
s1.append(format(network[6], " Leading & Trailing Whitespace "));
|
|
s1.append(format(network[7], " Leading, Trailing & Internal Whitespace "));
|
|
|
|
BEAST_EXPECT(c->load(s1));
|
|
|
|
for (auto const& n : network)
|
|
BEAST_EXPECT(c->member(n));
|
|
|
|
// Incorrect configurations
|
|
Section s2;
|
|
s2.append("NotAPublicKey");
|
|
BEAST_EXPECT(!c->load(s2));
|
|
|
|
Section s3;
|
|
s3.append(format(network[0], "!"));
|
|
BEAST_EXPECT(!c->load(s3));
|
|
|
|
Section s4;
|
|
s4.append(format(network[0], "! Comment"));
|
|
BEAST_EXPECT(!c->load(s4));
|
|
|
|
// Check if we properly terminate when we encounter
|
|
// a malformed or unparsable entry:
|
|
auto const node1 = randomNode();
|
|
auto const node2 = randomNode();
|
|
|
|
Section s5;
|
|
s5.append(format(node1, "XXX"));
|
|
s5.append(format(node2));
|
|
BEAST_EXPECT(!c->load(s5));
|
|
BEAST_EXPECT(!c->member(node1));
|
|
BEAST_EXPECT(!c->member(node2));
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
testMembership();
|
|
testUpdating();
|
|
testConfigLoad();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(cluster, overlay, xrpl);
|
|
|
|
} // namespace xrpl::tests
|