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