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