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