xrpld
Loading...
Searching...
No Matches
Logic.cpp
1#include <xrpl/resource/detail/Logic.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/chrono.h>
5#include <xrpl/basics/random.h>
6#include <xrpl/beast/insight/NullCollector.h>
7#include <xrpl/beast/net/IPAddressV4.h>
8#include <xrpl/beast/utility/Journal.h>
9#include <xrpl/resource/Charge.h>
10#include <xrpl/resource/Consumer.h>
11#include <xrpl/resource/Disposition.h>
12#include <xrpl/resource/Gossip.h>
13#include <xrpl/resource/detail/Tuning.h>
14
15#include <boost/utility/base_from_member.hpp>
16
17#include <gtest/gtest.h>
18#include <helpers/TestSink.h>
19
20#include <algorithm>
21#include <chrono>
22#include <cstdint>
23#include <ranges>
24#include <string>
25#include <utility>
26
27namespace xrpl::resource {
28
29class ResourceManagerTest : public ::testing::Test
30{
31protected:
33
34 class TestLogic : private boost::base_from_member<TestStopwatch>, public Logic
35 {
36 private:
37 using clock_type = boost::base_from_member<TestStopwatch>;
38
39 public:
40 explicit TestLogic(beast::Journal journal)
41 : Logic(beast::insight::NullCollector::make(), member, journal)
42 {
43 }
44
45 void
47 {
48 ++member;
49 }
50
53 {
54 return member;
55 }
56 };
57
58 //--------------------------------------------------------------------------
59
60 static Gossip
62 {
63 Gossip gossip;
64 std::uint8_t const v(10 + randInt(9));
65 std::uint8_t const n(10 + randInt(9));
66 gossip.items.reserve(n);
67 for (std::uint8_t i = 0; i < n; ++i)
68 {
69 Gossip::Item item;
70 item.balance = 100 + randInt(499);
71 beast::ip::AddressV4::bytes_type const d = {{
72 192,
73 0,
74 2,
75 static_cast<std::uint8_t>(v + i),
76 }};
78 gossip.items.push_back(std::move(item));
79 }
80 return gossip;
81 }
82};
83
84TEST_F(ResourceManagerTest, limited_warn_drop)
85{
86 TestLogic logic{j_};
87
88 Charge const fee{kDropThreshold + 1};
90
91 {
92 Consumer c{logic.newInboundEndpoint(addr)};
93
94 // Create load until we get a warning
95 auto n = 10000;
96 auto warned = false;
97
98 while (--n >= 0)
99 {
100 if (c.charge(fee) == Disposition::Warn)
101 {
102 warned = true;
103 break;
104 }
105 logic.advance();
106 }
107
108 ASSERT_TRUE(warned) << "Loop count exceeded without warning";
109
110 // Create load until we get dropped
111 bool dropped = false;
112 while (--n >= 0)
113 {
114 if (c.charge(fee) == Disposition::Drop)
115 {
116 dropped = true;
117 // Disconnect abusive Consumer
118 EXPECT_TRUE(c.disconnect(j_));
119 break;
120 }
121 logic.advance();
122 }
123
124 ASSERT_TRUE(dropped) << "Loop count exceeded without dropping";
125 }
126
127 // Make sure the consumer is on the blacklist for a while.
128 {
129 Consumer const c{logic.newInboundEndpoint(addr)};
130 logic.periodicActivity();
131 EXPECT_EQ(c.disposition(), Disposition::Drop) << "Dropped consumer not put on blacklist";
132 }
133
134 // Makes sure the Consumer is eventually removed from blacklist
135 bool readmitted = false;
136 {
137 using namespace std::chrono_literals;
138 // Give Consumer time to become readmitted. Should never
139 // exceed expiration time.
140 auto n = kSecondsUntilExpiration + 1s;
141 while (--n > 0s)
142 {
143 logic.advance();
144 logic.periodicActivity();
145 Consumer const c{logic.newInboundEndpoint(addr)};
147 {
148 readmitted = true;
149 break;
150 }
151 }
152 }
153 EXPECT_TRUE(readmitted) << "Dropped Consumer left on blacklist too long";
154}
155
156TEST_F(ResourceManagerTest, unlimited_warn_drop)
157{
158 TestLogic logic{j_};
159
160 Charge const fee{kDropThreshold + 1};
162 Consumer c{logic.newUnlimitedEndpoint(addr)};
163
164 // Create load until we get a warning
165 int n = 10000;
166 bool warned = false;
167
168 while (--n >= 0)
169 {
170 if (c.charge(fee) == Disposition::Warn)
171 {
172 warned = true;
173 break;
174 }
175 logic.advance();
176 }
177
178 EXPECT_FALSE(warned) << "Should loop forever with no warning";
179}
180
182{
183 static constexpr auto kDecayTicks = 128uz;
184
185 TestLogic logic{j_};
186
187 {
188 beast::ip::Endpoint const address{beast::ip::Endpoint::fromString("192.0.2.1")};
189 Consumer c{logic.newInboundEndpoint(address)};
190 Charge const fee{1000};
191 JLOG(j_.info()) << "Charging " << c.toString() << " " << fee << " per second";
192 c.charge(fee);
193 for (auto tick = 0uz; tick < kDecayTicks; ++tick)
194 {
195 JLOG(j_.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
196 << ", Balance = " << c.balance();
197 logic.advance();
198 }
199 }
200
201 {
202 beast::ip::Endpoint const address{beast::ip::Endpoint::fromString("192.0.2.2")};
203 Consumer c{logic.newInboundEndpoint(address)};
204 Charge const fee{1000};
205 JLOG(j_.info()) << "Charging " << c.toString() << " " << fee << " per second";
206 for (auto tick = 0uz; tick < kDecayTicks; ++tick)
207 {
208 c.charge(fee);
209 JLOG(j_.info()) << "Time= " << logic.clock().now().time_since_epoch().count()
210 << ", Balance = " << c.balance();
211 logic.advance();
212 }
213 }
214}
215
217{
218 TestLogic logic{j_};
219
220 static constexpr auto kGossipSources = 5uz;
221 std::ranges::for_each(std::views::iota(0uz, kGossipSources), [&](auto const i) {
222 logic.importConsumers(std::to_string(i), makeGossip());
223 });
224}
225
227{
228 TestLogic logic{j_};
229
230 Gossip g;
231 Gossip::Item item;
232 item.balance = 100;
233 beast::ip::AddressV4::bytes_type const d = {{
234 192,
235 0,
236 2,
237 1,
238 }};
240 g.items.push_back(std::move(item));
241
242 logic.importConsumers("g", g);
243}
244
245} // namespace xrpl::resource
A generic endpoint for log messages.
Definition Journal.h:44
A version-independent IP address and port combination.
Definition IPEndpoint.h:24
static Endpoint fromString(std::string const &s)
static TestSink & instance()
Definition TestSink.h:12
A consumption charge.
Definition Charge.h:13
An endpoint that consumes resources.
Definition Consumer.h:20
bool disconnect(beast::Journal const &j)
Returns true if the consumer should be disconnected.
Definition Consumer.cpp:107
int balance()
Returns the credit balance representing consumption.
Definition Consumer.cpp:119
std::string toString() const
Return a human readable string uniquely identifying this consumer.
Definition Consumer.cpp:61
Disposition charge(Charge const &fee, std::string const &context={})
Apply a load charge to the consumer.
Definition Consumer.cpp:89
Disposition disposition() const
Returns the current disposition of this consumer.
Definition Consumer.cpp:79
Logic(beast::insight::Collector::ptr const &collector, clock_type &clock, beast::Journal journal)
boost::base_from_member< TestStopwatch > clock_type
Definition Logic.cpp:37
beast::Journal const j_
Definition Logic.cpp:32
T for_each(T... args)
boost::asio::ip::address_v4 AddressV4
Definition IPAddressV4.h:7
static constexpr std::chrono::seconds kSecondsUntilExpiration
@ Warn
Consumer should be disconnected for excess consumption.
Definition Disposition.h:18
static constexpr auto kDropThreshold
TEST_F(ResourceManagerTest, limited_warn_drop)
Definition Logic.cpp:84
beast::ManualClock< std::chrono::steady_clock > TestStopwatch
A manual Stopwatch for unit tests.
Definition chrono.h:95
Describes a single consumer.
Definition Gossip.h:20
beast::ip::Endpoint address
Definition Gossip.h:24
Data format for exchanging consumption information across peers.
Definition Gossip.h:13
std::vector< Item > items
Definition Gossip.h:27
T to_string(T... args)