Files
rippled/src/tests/libxrpl/resource/Logic.cpp
2026-07-06 17:26:19 +00:00

242 lines
6.2 KiB
C++

#include <xrpl/resource/detail/Logic.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/random.h>
#include <xrpl/beast/insight/NullCollector.h>
#include <xrpl/beast/net/IPAddressV4.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/resource/Charge.h>
#include <xrpl/resource/Consumer.h>
#include <xrpl/resource/Disposition.h>
#include <xrpl/resource/Gossip.h>
#include <xrpl/resource/detail/Tuning.h>
#include <boost/utility/base_from_member.hpp>
#include <gtest/gtest.h>
#include <helpers/TestSink.h>
#include <chrono>
#include <cstdint>
#include <string>
namespace xrpl::Resource {
class ResourceManagerTest : public ::testing::Test
{
protected:
beast::Journal const j_{TestSink::instance()};
class TestLogic : private boost::base_from_member<TestStopwatch>, public Logic
{
private:
using clock_type = boost::base_from_member<TestStopwatch>;
public:
explicit TestLogic(beast::Journal journal)
: Logic(beast::insight::NullCollector::make(), member, journal)
{
}
void
advance()
{
++member;
}
TestStopwatch&
clock()
{
return member;
}
};
//--------------------------------------------------------------------------
static void
populateGossip(Gossip& gossip)
{
std::uint8_t const v(10 + randInt(9));
std::uint8_t const n(10 + randInt(9));
gossip.items.reserve(n);
for (std::uint8_t i = 0; i < n; ++i)
{
Gossip::Item item;
item.balance = 100 + randInt(499);
beast::IP::AddressV4::bytes_type const d = {{
192,
0,
2,
static_cast<std::uint8_t>(v + i),
}};
item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}};
gossip.items.push_back(item);
}
}
};
TEST_F(ResourceManagerTest, limited_warn_drop)
{
TestLogic logic{j_};
Charge const fee{kDropThreshold + 1};
beast::IP::Endpoint const addr{beast::IP::Endpoint::fromString("192.0.2.2")};
{
Consumer c{logic.newInboundEndpoint(addr)};
// Create load until we get a warning
int n = 10000;
bool warned = false;
while (--n >= 0)
{
if (c.charge(fee) == Disposition::Warn)
{
warned = true;
break;
}
++logic.clock();
}
ASSERT_TRUE(warned) << "Loop count exceeded without warning";
// Create load until we get dropped
bool dropped = false;
while (--n >= 0)
{
if (c.charge(fee) == Disposition::Drop)
{
dropped = true;
// Disconnect abusive Consumer
EXPECT_TRUE(c.disconnect(j_));
break;
}
++logic.clock();
}
ASSERT_TRUE(dropped) << "Loop count exceeded without dropping";
}
// Make sure the consumer is on the blacklist for a while.
{
Consumer const c{logic.newInboundEndpoint(addr)};
logic.periodicActivity();
EXPECT_EQ(c.disposition(), Disposition::Drop) << "Dropped consumer not put on blacklist";
}
// Makes sure the Consumer is eventually removed from blacklist
bool readmitted = false;
{
using namespace std::chrono_literals;
// Give Consumer time to become readmitted. Should never
// exceed expiration time.
auto n = kSecondsUntilExpiration + 1s;
while (--n > 0s)
{
++logic.clock();
logic.periodicActivity();
Consumer const c{logic.newInboundEndpoint(addr)};
if (c.disposition() != Disposition::Drop)
{
readmitted = true;
break;
}
}
}
EXPECT_TRUE(readmitted) << "Dropped Consumer left on blacklist too long";
}
TEST_F(ResourceManagerTest, unlimited_warn_drop)
{
TestLogic logic{j_};
Charge const fee{kDropThreshold + 1};
beast::IP::Endpoint const addr{beast::IP::Endpoint::fromString("192.0.2.2")};
Consumer c{logic.newUnlimitedEndpoint(addr)};
// Create load until we get a warning
int n = 10000;
bool warned = false;
while (--n >= 0)
{
if (c.charge(fee) == Disposition::Warn)
{
warned = true;
break;
}
++logic.clock();
}
EXPECT_FALSE(warned) << "Should loop forever with no warning";
}
TEST_F(ResourceManagerTest, charges)
{
TestLogic logic{j_};
{
beast::IP::Endpoint const address{beast::IP::Endpoint::fromString("192.0.2.1")};
Consumer c{logic.newInboundEndpoint(address)};
Charge const fee{1000};
JLOG(j_.info()) << "Charging " << c.toString() << " " << fee << " per second";
c.charge(fee);
for (int i = 0; i < 128; ++i)
{
JLOG(j_.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
<< ", Balance = " << c.balance();
logic.advance();
}
}
{
beast::IP::Endpoint const address{beast::IP::Endpoint::fromString("192.0.2.2")};
Consumer c{logic.newInboundEndpoint(address)};
Charge const fee{1000};
JLOG(j_.info()) << "Charging " << c.toString() << " " << fee << " per second";
for (int i = 0; i < 128; ++i)
{
c.charge(fee);
JLOG(j_.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
<< ", Balance = " << c.balance();
logic.advance();
}
}
}
TEST_F(ResourceManagerTest, imports)
{
TestLogic logic{j_};
Gossip g[5];
for (auto& i : g)
populateGossip(i);
for (int i = 0; i < 5; ++i)
logic.importConsumers(std::to_string(i), g[i]);
}
TEST_F(ResourceManagerTest, import)
{
TestLogic logic{j_};
Gossip g;
Gossip::Item item;
item.balance = 100;
beast::IP::AddressV4::bytes_type const d = {{
192,
0,
2,
1,
}};
item.address = beast::IP::Endpoint{beast::IP::AddressV4{d}};
g.items.push_back(item);
logic.importConsumers("g", g);
}
} // namespace xrpl::Resource