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