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