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", "noripple_check", to_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", "noripple_check", to_string(params))[jss::result];
94 BEAST_EXPECT(result[jss::error] == "invalidParams");
95 BEAST_EXPECT(result[jss::error_message] == "Invalid field 'role'.");
96 }
97
98 { // invalid limit
99 Json::Value params;
100 params[jss::account] = alice.human();
101 params[jss::role] = "user";
102 params[jss::limit] = -1;
103 auto const result = env.rpc(
104 "json", "noripple_check", to_string(params))[jss::result];
105 BEAST_EXPECT(result[jss::error] == "invalidParams");
106 BEAST_EXPECT(
107 result[jss::error_message] ==
108 "Invalid field 'limit', not unsigned integer.");
109 }
110
111 { // invalid ledger (hash)
112 Json::Value params;
113 params[jss::account] = alice.human();
114 params[jss::role] = "user";
115 params[jss::ledger_hash] = 1;
116 auto const result = env.rpc(
117 "json", "noripple_check", to_string(params))[jss::result];
118 BEAST_EXPECT(result[jss::error] == "invalidParams");
119 BEAST_EXPECT(result[jss::error_message] == "ledgerHashNotString");
120 }
121
122 { // account not found
123 Json::Value params;
124 params[jss::account] = Account{"nobody"}.human();
125 params[jss::role] = "user";
126 params[jss::ledger] = "current";
127 auto const result = env.rpc(
128 "json", "noripple_check", to_string(params))[jss::result];
129 BEAST_EXPECT(result[jss::error] == "actNotFound");
130 BEAST_EXPECT(result[jss::error_message] == "Account not found.");
131 }
132
133 { // passing an account private key will cause
134 // parsing as a seed to fail
135 Json::Value params;
136 params[jss::account] = toBase58(TokenType::NodePrivate, alice.sk());
137 params[jss::role] = "user";
138 params[jss::ledger] = "current";
139 auto const result = env.rpc(
140 "json", "noripple_check", to_string(params))[jss::result];
141 BEAST_EXPECT(result[jss::error] == "actMalformed");
142 BEAST_EXPECT(result[jss::error_message] == "Account malformed.");
143 }
144 }
145
146 void
147 testBasic(bool user, bool problems)
148 {
149 testcase << "Request noripple_check for " << (user ? "user" : "gateway")
150 << " role, expect" << (problems ? "" : " no") << " problems";
151
152 using namespace test::jtx;
153 Env env{*this};
154
155 auto const gw = Account{"gw"};
156 auto const alice = Account{"alice"};
157
158 env.fund(XRP(10000), gw, alice);
159 if ((user && problems) || (!user && !problems))
160 {
161 env(fset(alice, asfDefaultRipple));
162 env(trust(alice, gw["USD"](100)));
163 }
164 else
165 {
166 env(fclear(alice, asfDefaultRipple));
167 env(trust(alice, gw["USD"](100), gw, tfSetNoRipple));
168 }
169 env.close();
170
171 Json::Value params;
172 params[jss::account] = alice.human();
173 params[jss::role] = (user ? "user" : "gateway");
174 params[jss::ledger] = "current";
175 auto result =
176 env.rpc("json", "noripple_check", to_string(params))[jss::result];
177
178 auto const pa = result["problems"];
179 if (!BEAST_EXPECT(pa.isArray()))
180 return;
181
182 if (problems)
183 {
184 if (!BEAST_EXPECT(pa.size() == 2))
185 return;
186
187 if (user)
188 {
189 BEAST_EXPECT(boost::starts_with(
190 pa[0u].asString(), "You appear to have set"));
191 BEAST_EXPECT(boost::starts_with(
192 pa[1u].asString(), "You should probably set"));
193 }
194 else
195 {
196 BEAST_EXPECT(boost::starts_with(
197 pa[0u].asString(), "You should immediately set"));
198 BEAST_EXPECT(
199 boost::starts_with(pa[1u].asString(), "You should clear"));
200 }
201 }
202 else
203 {
204 BEAST_EXPECT(pa.size() == 0);
205 }
206
207 // now make a second request asking for the relevant transactions this
208 // time.
209 params[jss::transactions] = true;
210 result =
211 env.rpc("json", "noripple_check", to_string(params))[jss::result];
212 if (!BEAST_EXPECT(result[jss::transactions].isArray()))
213 return;
214
215 auto const txs = result[jss::transactions];
216 if (problems)
217 {
218 if (!BEAST_EXPECT(txs.size() == (user ? 1 : 2)))
219 return;
220
221 if (!user)
222 {
223 BEAST_EXPECT(txs[0u][jss::Account] == alice.human());
224 BEAST_EXPECT(txs[0u][jss::TransactionType] == jss::AccountSet);
225 }
226
227 BEAST_EXPECT(
228 result[jss::transactions][txs.size() - 1][jss::Account] ==
229 alice.human());
230 BEAST_EXPECT(
231 result[jss::transactions][txs.size() - 1]
232 [jss::TransactionType] == jss::TrustSet);
233 BEAST_EXPECT(
234 result[jss::transactions][txs.size() - 1][jss::LimitAmount] ==
235 gw["USD"](100).value().getJson(JsonOptions::none));
236 }
237 else
238 {
239 BEAST_EXPECT(txs.size() == 0);
240 }
241 }
242
243public:
244 void
245 run() override
246 {
247 testBadInput();
248 for (auto user : {true, false})
249 for (auto problem : {true, false})
250 testBasic(user, problem);
251 }
252};
253
255{
256 void
257 testLimits(bool admin)
258 {
259 testcase << "Check limits in returned data, "
260 << (admin ? "admin" : "non-admin");
261
262 using namespace test::jtx;
263
264 Env env{*this, admin ? envconfig() : envconfig(no_admin)};
265
266 auto const alice = Account{"alice"};
267 env.fund(XRP(100000), alice);
268 env(fset(alice, asfDefaultRipple));
269 env.close();
270
271 auto checkBalance = [&env]() {
272 // this is endpoint drop prevention. Non admin ports will drop
273 // requests if they are coming too fast, so we manipulate the
274 // resource manager here to reset the enpoint balance (for
275 // localhost) if we get too close to the drop limit. It would
276 // be better if we could add this functionality to Env somehow
277 // or otherwise disable endpoint charging for certain test
278 // cases.
279 using namespace ripple::Resource;
280 using namespace std::chrono;
281 using namespace beast::IP;
282 auto c = env.app().getResourceManager().newInboundEndpoint(
283 Endpoint::from_string(test::getEnvLocalhostAddr()));
284
285 // if we go above the warning threshold, reset
286 if (c.balance() > warningThreshold)
287 {
289 c.entry().local_balance =
290 DecayingSample<decayWindowSeconds, ct>{steady_clock::now()};
291 }
292 };
293
294 for (auto i = 0; i < ripple::RPC::Tuning::noRippleCheck.rmax + 5; ++i)
295 {
296 if (!admin)
297 checkBalance();
298
299 auto& txq = env.app().getTxQ();
300 auto const gw = Account{"gw" + std::to_string(i)};
301 env.memoize(gw);
302 auto const baseFee = env.current()->fees().base;
303 env(pay(env.master, gw, XRP(1000)),
304 seq(autofill),
305 fee(toDrops(
306 txq.getMetrics(*env.current()).openLedgerFeeLevel,
307 baseFee) +
308 1),
309 sig(autofill));
310 env(fset(gw, asfDefaultRipple),
311 seq(autofill),
312 fee(toDrops(
313 txq.getMetrics(*env.current()).openLedgerFeeLevel,
314 baseFee) +
315 1),
316 sig(autofill));
317 env(trust(alice, gw["USD"](10)),
318 fee(toDrops(
319 txq.getMetrics(*env.current()).openLedgerFeeLevel,
320 baseFee) +
321 1));
322 env.close();
323 }
324
325 // default limit value
326 Json::Value params;
327 params[jss::account] = alice.human();
328 params[jss::role] = "user";
329 params[jss::ledger] = "current";
330 auto result =
331 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 =
338 env.rpc("json", "noripple_check", to_string(params))[jss::result];
339 BEAST_EXPECT(result["problems"].size() == (admin ? 10 : 11));
340
341 // at minimum
342 params[jss::limit] = 10;
343 result =
344 env.rpc("json", "noripple_check", to_string(params))[jss::result];
345 BEAST_EXPECT(result["problems"].size() == 11);
346
347 // at max
348 params[jss::limit] = 400;
349 result =
350 env.rpc("json", "noripple_check", to_string(params))[jss::result];
351 BEAST_EXPECT(result["problems"].size() == 401);
352
353 // at max+1
354 params[jss::limit] = 401;
355 result =
356 env.rpc("json", "noripple_check", to_string(params))[jss::result];
357 BEAST_EXPECT(result["problems"].size() == (admin ? 402 : 401));
358 }
359
360public:
361 void
362 run() override
363 {
364 for (auto admin : {true, false})
365 testLimits(admin);
366 }
367};
368
369BEAST_DEFINE_TESTSUITE(NoRippleCheck, rpc, ripple);
370
371// These tests that deal with limit amounts are slow because of the
372// offer/account setup, so making them manual -- the additional coverage
373// provided by them is minimal
374
375BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(NoRippleCheckLimits, rpc, ripple, 1);
376
377} // 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)