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