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