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