rippled
Loading...
Searching...
No Matches
NoRippleCheck_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/envconfig.h>
3
4#include <xrpld/app/misc/TxQ.h>
5#include <xrpld/rpc/detail/Tuning.h>
6
7#include <xrpl/beast/utility/temp_dir.h>
8#include <xrpl/protocol/jss.h>
9#include <xrpl/resource/ResourceManager.h>
10#include <xrpl/resource/detail/Entry.h>
11#include <xrpl/resource/detail/Tuning.h>
12
13#include <boost/algorithm/string/predicate.hpp>
14
15namespace xrpl {
16
18{
19 void
21 {
22 testcase("Bad input to noripple_check");
23
24 using namespace test::jtx;
25 Env env{*this};
26
27 auto const alice = Account{"alice"};
28 env.fund(XRP(10000), alice);
29 env.close();
30
31 { // missing account field
32 auto const result =
33 env.rpc("json", "noripple_check", "{}")[jss::result];
34 BEAST_EXPECT(result[jss::error] == "invalidParams");
35 BEAST_EXPECT(
36 result[jss::error_message] == "Missing field 'account'.");
37 }
38
39 { // missing role field
40 Json::Value params;
41 params[jss::account] = alice.human();
42 auto const result = env.rpc(
43 "json", "noripple_check", to_string(params))[jss::result];
44 BEAST_EXPECT(result[jss::error] == "invalidParams");
45 BEAST_EXPECT(result[jss::error_message] == "Missing field 'role'.");
46 }
47
48 // test account non-string
49 {
50 auto testInvalidAccountParam = [&](auto const& param) {
51 Json::Value params;
52 params[jss::account] = param;
53 params[jss::role] = "user";
54 auto jrr = env.rpc(
55 "json", "noripple_check", to_string(params))[jss::result];
56 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
57 BEAST_EXPECT(
58 jrr[jss::error_message] == "Invalid field 'account'.");
59 };
60
61 testInvalidAccountParam(1);
62 testInvalidAccountParam(1.1);
63 testInvalidAccountParam(true);
64 testInvalidAccountParam(Json::Value(Json::nullValue));
65 testInvalidAccountParam(Json::Value(Json::objectValue));
66 testInvalidAccountParam(Json::Value(Json::arrayValue));
67 }
68
69 { // invalid role field
70 Json::Value params;
71 params[jss::account] = alice.human();
72 params[jss::role] = "not_a_role";
73 auto const result = env.rpc(
74 "json", "noripple_check", to_string(params))[jss::result];
75 BEAST_EXPECT(result[jss::error] == "invalidParams");
76 BEAST_EXPECT(result[jss::error_message] == "Invalid field 'role'.");
77 }
78
79 { // invalid limit
80 Json::Value params;
81 params[jss::account] = alice.human();
82 params[jss::role] = "user";
83 params[jss::limit] = -1;
84 auto const result = env.rpc(
85 "json", "noripple_check", to_string(params))[jss::result];
86 BEAST_EXPECT(result[jss::error] == "invalidParams");
87 BEAST_EXPECT(
88 result[jss::error_message] ==
89 "Invalid field 'limit', not unsigned integer.");
90 }
91
92 { // invalid ledger (hash)
93 Json::Value params;
94 params[jss::account] = alice.human();
95 params[jss::role] = "user";
96 params[jss::ledger_hash] = 1;
97 auto const result = env.rpc(
98 "json", "noripple_check", to_string(params))[jss::result];
99 BEAST_EXPECT(result[jss::error] == "invalidParams");
100 BEAST_EXPECT(
101 result[jss::error_message] ==
102 "Invalid field 'ledger_hash', not hex string.");
103 }
104
105 { // account not found
106 Json::Value params;
107 params[jss::account] = Account{"nobody"}.human();
108 params[jss::role] = "user";
109 params[jss::ledger] = "current";
110 auto const result = env.rpc(
111 "json", "noripple_check", to_string(params))[jss::result];
112 BEAST_EXPECT(result[jss::error] == "actNotFound");
113 BEAST_EXPECT(result[jss::error_message] == "Account not found.");
114 }
115
116 { // passing an account private key will cause
117 // parsing as a seed to fail
118 Json::Value params;
119 params[jss::account] = toBase58(TokenType::NodePrivate, alice.sk());
120 params[jss::role] = "user";
121 params[jss::ledger] = "current";
122 auto const result = env.rpc(
123 "json", "noripple_check", to_string(params))[jss::result];
124 BEAST_EXPECT(result[jss::error] == "actMalformed");
125 BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
126 }
127
128 {
129 // ledger and ledger_hash are included
130 Json::Value params;
131 params[jss::account] = Account{"nobody"}.human();
132 params[jss::role] = "user";
133 params[jss::ledger] = "current";
134 params[jss::ledger_hash] = "ABCDEF";
135 auto const result = env.rpc(
136 "json", "noripple_check", to_string(params))[jss::result];
137 BEAST_EXPECT(result[jss::error] == "invalidParams");
138 BEAST_EXPECT(
139 result[jss::error_message] ==
140 "Exactly one of 'ledger', 'ledger_hash', or 'ledger_index' can "
141 "be specified.");
142 }
143
144 {
145 // invalid ledger
146 Json::Value params;
147 params[jss::account] = Account{"nobody"}.human();
148 params[jss::role] = "user";
149 params[jss::ledger] = Json::objectValue;
150 auto const result = env.rpc(
151 "json", "noripple_check", to_string(params))[jss::result];
152 BEAST_EXPECT(result[jss::error] == "invalidParams");
153 BEAST_EXPECT(
154 result[jss::error_message] ==
155 "Invalid field 'ledger', not string or number.");
156 }
157 }
158
159 void
160 testBasic(bool user, bool problems)
161 {
162 testcase << "Request noripple_check for " << (user ? "user" : "gateway")
163 << " role, expect" << (problems ? "" : " no") << " problems";
164
165 using namespace test::jtx;
166 Env env{*this};
167
168 auto const gw = Account{"gw"};
169 auto const alice = Account{"alice"};
170
171 env.fund(XRP(10000), gw, alice);
172 if ((user && problems) || (!user && !problems))
173 {
174 env(fset(alice, asfDefaultRipple));
175 env(trust(alice, gw["USD"](100)));
176 }
177 else
178 {
179 env(fclear(alice, asfDefaultRipple));
180 env(trust(alice, gw["USD"](100), gw, tfSetNoRipple));
181 }
182 env.close();
183
184 Json::Value params;
185 params[jss::account] = alice.human();
186 params[jss::role] = (user ? "user" : "gateway");
187 params[jss::ledger] = "current";
188 auto result =
189 env.rpc("json", "noripple_check", to_string(params))[jss::result];
190
191 auto const pa = result["problems"];
192 if (!BEAST_EXPECT(pa.isArray()))
193 return;
194
195 if (problems)
196 {
197 if (!BEAST_EXPECT(pa.size() == 2))
198 return;
199
200 if (user)
201 {
202 BEAST_EXPECT(boost::starts_with(
203 pa[0u].asString(), "You appear to have set"));
204 BEAST_EXPECT(boost::starts_with(
205 pa[1u].asString(), "You should probably set"));
206 }
207 else
208 {
209 BEAST_EXPECT(boost::starts_with(
210 pa[0u].asString(), "You should immediately set"));
211 BEAST_EXPECT(
212 boost::starts_with(pa[1u].asString(), "You should clear"));
213 }
214 }
215 else
216 {
217 BEAST_EXPECT(pa.size() == 0);
218 }
219
220 // now make a second request asking for the relevant transactions this
221 // time.
222 params[jss::transactions] = true;
223 result =
224 env.rpc("json", "noripple_check", to_string(params))[jss::result];
225 if (!BEAST_EXPECT(result[jss::transactions].isArray()))
226 return;
227
228 auto const txs = result[jss::transactions];
229 if (problems)
230 {
231 if (!BEAST_EXPECT(txs.size() == (user ? 1 : 2)))
232 return;
233
234 if (!user)
235 {
236 BEAST_EXPECT(txs[0u][jss::Account] == alice.human());
237 BEAST_EXPECT(txs[0u][jss::TransactionType] == jss::AccountSet);
238 }
239
240 BEAST_EXPECT(
241 result[jss::transactions][txs.size() - 1][jss::Account] ==
242 alice.human());
243 BEAST_EXPECT(
244 result[jss::transactions][txs.size() - 1]
245 [jss::TransactionType] == jss::TrustSet);
246 BEAST_EXPECT(
247 result[jss::transactions][txs.size() - 1][jss::LimitAmount] ==
248 gw["USD"](100).value().getJson(JsonOptions::none));
249 }
250 else
251 {
252 BEAST_EXPECT(txs.size() == 0);
253 }
254 }
255
256public:
257 void
258 run() override
259 {
260 testBadInput();
261 for (auto user : {true, false})
262 for (auto problem : {true, false})
263 testBasic(user, problem);
264 }
265};
266
268{
269 void
270 testLimits(bool admin)
271 {
272 testcase << "Check limits in returned data, "
273 << (admin ? "admin" : "non-admin");
274
275 using namespace test::jtx;
276
277 Env env{*this, admin ? envconfig() : envconfig(no_admin)};
278
279 auto const alice = Account{"alice"};
280 env.fund(XRP(100000), alice);
281 env(fset(alice, asfDefaultRipple));
282 env.close();
283
284 auto checkBalance = [&env]() {
285 // this is endpoint drop prevention. Non admin ports will drop
286 // requests if they are coming too fast, so we manipulate the
287 // resource manager here to reset the endpoint balance (for
288 // localhost) if we get too close to the drop limit. It would
289 // be better if we could add this functionality to Env somehow
290 // or otherwise disable endpoint charging for certain test
291 // cases.
292 using namespace xrpl::Resource;
293 using namespace std::chrono;
294 using namespace beast::IP;
295 auto c = env.app().getResourceManager().newInboundEndpoint(
296 Endpoint::from_string(test::getEnvLocalhostAddr()));
297
298 // if we go above the warning threshold, reset
299 if (c.balance() > warningThreshold)
300 {
302 c.entry().local_balance =
303 DecayingSample<decayWindowSeconds, ct>{steady_clock::now()};
304 }
305 };
306
307 for (auto i = 0; i < xrpl::RPC::Tuning::noRippleCheck.rmax + 5; ++i)
308 {
309 if (!admin)
310 checkBalance();
311
312 auto& txq = env.app().getTxQ();
313 auto const gw = Account{"gw" + std::to_string(i)};
314 env.memoize(gw);
315 auto const baseFee = env.current()->fees().base;
316 env(pay(env.master, gw, XRP(1000)),
317 seq(autofill),
318 fee(toDrops(
319 txq.getMetrics(*env.current()).openLedgerFeeLevel,
320 baseFee) +
321 1),
322 sig(autofill));
323 env(fset(gw, asfDefaultRipple),
324 seq(autofill),
325 fee(toDrops(
326 txq.getMetrics(*env.current()).openLedgerFeeLevel,
327 baseFee) +
328 1),
329 sig(autofill));
330 env(trust(alice, gw["USD"](10)),
331 fee(toDrops(
332 txq.getMetrics(*env.current()).openLedgerFeeLevel,
333 baseFee) +
334 1));
335 env.close();
336 }
337
338 // default limit value
339 Json::Value params;
340 params[jss::account] = alice.human();
341 params[jss::role] = "user";
342 params[jss::ledger] = "current";
343 auto result =
344 env.rpc("json", "noripple_check", to_string(params))[jss::result];
345
346 BEAST_EXPECT(result["problems"].size() == 301);
347
348 // one below minimum
349 params[jss::limit] = 9;
350 result =
351 env.rpc("json", "noripple_check", to_string(params))[jss::result];
352 BEAST_EXPECT(result["problems"].size() == (admin ? 10 : 11));
353
354 // at minimum
355 params[jss::limit] = 10;
356 result =
357 env.rpc("json", "noripple_check", to_string(params))[jss::result];
358 BEAST_EXPECT(result["problems"].size() == 11);
359
360 // at max
361 params[jss::limit] = 400;
362 result =
363 env.rpc("json", "noripple_check", to_string(params))[jss::result];
364 BEAST_EXPECT(result["problems"].size() == 401);
365
366 // at max+1
367 params[jss::limit] = 401;
368 result =
369 env.rpc("json", "noripple_check", to_string(params))[jss::result];
370 BEAST_EXPECT(result["problems"].size() == (admin ? 402 : 401));
371 }
372
373public:
374 void
375 run() override
376 {
377 for (auto admin : {true, false})
378 testLimits(admin);
379 }
380};
381
382BEAST_DEFINE_TESTSUITE(NoRippleCheck, rpc, xrpl);
383
384// These tests that deal with limit amounts are slow because of the
385// offer/account setup, so making them manual -- the additional coverage
386// provided by them is minimal
387
388BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(NoRippleCheckLimits, rpc, xrpl, 1);
389
390} // namespace xrpl
Represents a JSON value.
Definition json_value.h:131
Abstract interface to a clock.
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
Sampling function using exponential decay to provide a continuous value.
void run() override
Runs the suite.
void run() override
Runs the suite.
void testBasic(bool user, bool problems)
@ nullValue
'null' value
Definition json_value.h:20
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
static LimitRange constexpr noRippleCheck
Limits for the no_ripple_check command.
char const * getEnvLocalhostAddr()
Definition envconfig.h:17
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t tfSetNoRipple
Definition TxFlags.h:97
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
Definition TxQ.h:844
constexpr std::uint32_t asfDefaultRipple
Definition TxFlags.h:65
T to_string(T... args)