rippled
LedgerRequestRPC_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2016 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/ledger/LedgerMaster.h>
21 #include <ripple/beast/unit_test.h>
22 #include <ripple/protocol/ErrorCodes.h>
23 #include <ripple/protocol/jss.h>
24 #include <ripple/rpc/impl/RPCHelpers.h>
25 #include <test/jtx.h>
26 
27 #include <functional>
28 
29 namespace ripple {
30 
31 namespace RPC {
32 
33 class LedgerRequestRPC_test : public beast::unit_test::suite
34 {
35  static constexpr char const* hash1 =
36  "3020EB9E7BE24EF7D7A060CB051583EC117384636D1781AFB5B87F3E348DA489";
37  static constexpr char const* accounthash1 =
38  "BD8A3D72CA73DDE887AD63666EC2BAD07875CBA997A102579B5B95ECDFFEAED8";
39 
40  static constexpr char const* zerohash =
41  "0000000000000000000000000000000000000000000000000000000000000000";
42 
43 public:
44  void
46  {
47  using namespace test::jtx;
48 
49  Env env(*this);
50 
51  env.close();
52  env.close();
53  BEAST_EXPECT(env.current()->info().seq == 5);
54 
55  {
56  // arbitrary text is converted to 0.
57  auto const result = env.rpc("ledger_request", "arbitrary_text");
58  BEAST_EXPECT(
59  RPC::contains_error(result[jss::result]) &&
60  result[jss::result][jss::error_message] ==
61  "Ledger index too small");
62  }
63 
64  {
65  auto const result = env.rpc("ledger_request", "-1");
66  BEAST_EXPECT(
67  RPC::contains_error(result[jss::result]) &&
68  result[jss::result][jss::error_message] ==
69  "Ledger index too small");
70  }
71 
72  {
73  auto const result = env.rpc("ledger_request", "0");
74  BEAST_EXPECT(
75  RPC::contains_error(result[jss::result]) &&
76  result[jss::result][jss::error_message] ==
77  "Ledger index too small");
78  }
79 
80  {
81  auto const result = env.rpc("ledger_request", "1");
82  BEAST_EXPECT(
83  !RPC::contains_error(result[jss::result]) &&
84  result[jss::result][jss::ledger_index] == 1 &&
85  result[jss::result].isMember(jss::ledger));
86  BEAST_EXPECT(
87  result[jss::result][jss::ledger].isMember(jss::ledger_hash) &&
88  result[jss::result][jss::ledger][jss::ledger_hash].isString());
89  }
90 
91  {
92  auto const result = env.rpc("ledger_request", "2");
93  BEAST_EXPECT(
94  !RPC::contains_error(result[jss::result]) &&
95  result[jss::result][jss::ledger_index] == 2 &&
96  result[jss::result].isMember(jss::ledger));
97  BEAST_EXPECT(
98  result[jss::result][jss::ledger].isMember(jss::ledger_hash) &&
99  result[jss::result][jss::ledger][jss::ledger_hash].isString());
100  }
101 
102  {
103  auto const result = env.rpc("ledger_request", "3");
104  BEAST_EXPECT(
105  !RPC::contains_error(result[jss::result]) &&
106  result[jss::result][jss::ledger_index] == 3 &&
107  result[jss::result].isMember(jss::ledger));
108  BEAST_EXPECT(
109  result[jss::result][jss::ledger].isMember(jss::ledger_hash) &&
110  result[jss::result][jss::ledger][jss::ledger_hash].isString());
111 
112  auto const ledgerHash =
113  result[jss::result][jss::ledger][jss::ledger_hash].asString();
114 
115  {
116  auto const r = env.rpc("ledger_request", ledgerHash);
117  BEAST_EXPECT(
118  !RPC::contains_error(r[jss::result]) &&
119  r[jss::result][jss::ledger_index] == 3 &&
120  r[jss::result].isMember(jss::ledger));
121  BEAST_EXPECT(
122  r[jss::result][jss::ledger].isMember(jss::ledger_hash) &&
123  r[jss::result][jss::ledger][jss::ledger_hash] ==
124  ledgerHash);
125  }
126  }
127 
128  {
129  std::string ledgerHash(64, 'q');
130 
131  auto const result = env.rpc("ledger_request", ledgerHash);
132 
133  BEAST_EXPECT(
134  RPC::contains_error(result[jss::result]) &&
135  result[jss::result][jss::error_message] ==
136  "Invalid field 'ledger_hash'.");
137  }
138 
139  {
140  std::string ledgerHash(64, '1');
141 
142  auto const result = env.rpc("ledger_request", ledgerHash);
143 
144  BEAST_EXPECT(
145  !RPC::contains_error(result[jss::result]) &&
146  result[jss::result][jss::have_header] == false);
147  }
148 
149  {
150  auto const result = env.rpc("ledger_request", "4");
151  BEAST_EXPECT(
152  RPC::contains_error(result[jss::result]) &&
153  result[jss::result][jss::error_message] ==
154  "Ledger index too large");
155  }
156 
157  {
158  auto const result = env.rpc("ledger_request", "5");
159  BEAST_EXPECT(
160  RPC::contains_error(result[jss::result]) &&
161  result[jss::result][jss::error_message] ==
162  "Ledger index too large");
163  }
164  }
165 
166  void
168  {
169  using namespace test::jtx;
170  Env env{*this, FeatureBitset{}}; // the hashes being checked below
171  // assume no amendments
172  Account const gw{"gateway"};
173  auto const USD = gw["USD"];
174  env.fund(XRP(100000), gw);
175  env.close();
176 
177  env.memoize("bob");
178  env.fund(XRP(1000), "bob");
179  env.close();
180 
181  env.memoize("alice");
182  env.fund(XRP(1000), "alice");
183  env.close();
184 
185  env.memoize("carol");
186  env.fund(XRP(1000), "carol");
187  env.close();
188 
189  auto result = env.rpc("ledger_request", "1")[jss::result];
190  BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "1");
191  BEAST_EXPECT(
192  result[jss::ledger][jss::total_coins] == "100000000000000000");
193  BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
194  BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == hash1);
195  BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == zerohash);
196  BEAST_EXPECT(result[jss::ledger][jss::account_hash] == accounthash1);
197  BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == zerohash);
198 
199  result = env.rpc("ledger_request", "2")[jss::result];
200  constexpr char const* hash2 =
201  "CCC3B3E88CCAC17F1BE6B4A648A55999411F19E3FE55EB721960EB0DF28EDDA5";
202  BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "2");
203  BEAST_EXPECT(
204  result[jss::ledger][jss::total_coins] == "100000000000000000");
205  BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
206  BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == hash2);
207  BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == hash1);
208  BEAST_EXPECT(
209  result[jss::ledger][jss::account_hash] ==
210  "3C834285F7F464FBE99AFEB84D354A968EB2CAA24523FF26797A973D906A3D29");
211  BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == zerohash);
212 
213  result = env.rpc("ledger_request", "3")[jss::result];
214  constexpr char const* hash3 =
215  "8D631B20BC989AF568FBA97375290544B0703A5ADC1CF9E9053580461690C9EE";
216  BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "3");
217  BEAST_EXPECT(
218  result[jss::ledger][jss::total_coins] == "99999999999999980");
219  BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
220  BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == hash3);
221  BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == hash2);
222  BEAST_EXPECT(
223  result[jss::ledger][jss::account_hash] ==
224  "BC9EF2A16BFF80BCFABA6FA84688D858D33BD0FA0435CAA9DF6DA4105A39A29E");
225  BEAST_EXPECT(
226  result[jss::ledger][jss::transaction_hash] ==
227  "0213EC486C058B3942FBE3DAC6839949A5C5B02B8B4244C8998EFDF04DBD8222");
228 
229  result = env.rpc("ledger_request", "4")[jss::result];
230  constexpr char const* hash4 =
231  "1A8E7098B23597E73094DADA58C9D62F3AB93A12C6F7666D56CA85A6CFDE530F";
232  BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "4");
233  BEAST_EXPECT(
234  result[jss::ledger][jss::total_coins] == "99999999999999960");
235  BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
236  BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == hash4);
237  BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == hash3);
238  BEAST_EXPECT(
239  result[jss::ledger][jss::account_hash] ==
240  "C690188F123C91355ADA8BDF4AC5B5C927076D3590C215096868A5255264C6DD");
241  BEAST_EXPECT(
242  result[jss::ledger][jss::transaction_hash] ==
243  "3CBDB8F42E04333E1642166BFB93AC9A7E1C6C067092CD5D881D6F3AB3D67E76");
244 
245  result = env.rpc("ledger_request", "5")[jss::result];
246  constexpr char const* hash5 =
247  "C6A222D71AE65D7B4F240009EAD5DEB20D7EEDE5A4064F28BBDBFEEB6FBE48E5";
248  BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "5");
249  BEAST_EXPECT(
250  result[jss::ledger][jss::total_coins] == "99999999999999940");
251  BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
252  BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == hash5);
253  BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == hash4);
254  BEAST_EXPECT(
255  result[jss::ledger][jss::account_hash] ==
256  "EA81CD9D36740736F00CB747E0D0E32D3C10B695823D961F0FB9A1CE7133DD4D");
257  BEAST_EXPECT(
258  result[jss::ledger][jss::transaction_hash] ==
259  "C3D086CD6BDB9E97AD1D513B2C049EF2840BD21D0B3E22D84EBBB89B6D2EF59D");
260 
261  result = env.rpc("ledger_request", "6")[jss::result];
262  BEAST_EXPECT(result[jss::error] == "invalidParams");
263  BEAST_EXPECT(result[jss::status] == "error");
264  BEAST_EXPECT(result[jss::error_message] == "Ledger index too large");
265  }
266 
267  void
268  testBadInput(unsigned apiVersion)
269  {
270  using namespace test::jtx;
271  Env env{*this};
272  Account const gw{"gateway"};
273  auto const USD = gw["USD"];
274  env.fund(XRP(100000), gw);
275  env.close();
276 
277  Json::Value jvParams;
278  jvParams[jss::ledger_hash] =
279  "AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936C7";
280  jvParams[jss::ledger_index] = "1";
281  auto result = env.rpc(
282  "json", "ledger_request", jvParams.toStyledString())[jss::result];
283  BEAST_EXPECT(result[jss::error] == "invalidParams");
284  BEAST_EXPECT(result[jss::status] == "error");
285  BEAST_EXPECT(
286  result[jss::error_message] ==
287  "Exactly one of ledger_hash and ledger_index can be set.");
288 
289  // the purpose in this test is to force the ledger expiration/out of
290  // date check to trigger
291  env.timeKeeper().adjustCloseTime(weeks{3});
292  result = env.rpc(apiVersion, "ledger_request", "1")[jss::result];
293  BEAST_EXPECT(result[jss::status] == "error");
294  if (apiVersion == 1)
295  {
296  BEAST_EXPECT(result[jss::error] == "noCurrent");
297  BEAST_EXPECT(
298  result[jss::error_message] == "Current ledger is unavailable.");
299  }
300  else
301  {
302  BEAST_EXPECT(result[jss::error] == "notSynced");
303  BEAST_EXPECT(
304  result[jss::error_message] == "Not synced to the network.");
305  }
306  }
307 
308  void
310  {
311  using namespace test::jtx;
312  using namespace std::chrono_literals;
313  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
314  cfg->NODE_SIZE = 0;
315  return cfg;
316  })};
317  Account const gw{"gateway"};
318  auto const USD = gw["USD"];
319  env.fund(XRP(100000), gw);
320 
321  int const max_limit = 256;
322 
323  for (auto i = 0; i < max_limit + 10; i++)
324  {
325  Account const bob{std::string("bob") + std::to_string(i)};
326  env.fund(XRP(1000), bob);
327  env.close();
328  }
329 
330  auto result = env.rpc("ledger_request", "1")[jss::result];
331  BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "1");
332  BEAST_EXPECT(
333  result[jss::ledger][jss::total_coins] == "100000000000000000");
334  BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
335  BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == hash1);
336  BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == zerohash);
337  BEAST_EXPECT(result[jss::ledger][jss::account_hash] == accounthash1);
338  BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == zerohash);
339  }
340 
341  void
343  {
344  using namespace test::jtx;
345  Env env{*this, envconfig(no_admin)};
346  Account const gw{"gateway"};
347  auto const USD = gw["USD"];
348  env.fund(XRP(100000), gw);
349 
350  auto const result = env.rpc("ledger_request", "1")[jss::result];
351  // The current HTTP/S ServerHandler returns an HTTP 403 error code here
352  // rather than a noPermission JSON error. The JSONRPCClient just eats
353  // that error and returns an null result.
354  BEAST_EXPECT(result.type() == Json::nullValue);
355  }
356 
357  void
358  run() override
359  {
361  testEvolution();
365  testNonAdmin();
366  }
367 };
368 
369 BEAST_DEFINE_TESTSUITE(LedgerRequestRPC, app, ripple);
370 
371 } // namespace RPC
372 } // namespace ripple
ripple::RPC::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountLinesRPC, app, ripple)
std::string
STL class.
functional
ripple::RPC::LedgerRequestRPC_test::zerohash
static constexpr char const * zerohash
Definition: LedgerRequestRPC_test.cpp:40
ripple::RPC::LedgerRequestRPC_test::hash1
static constexpr char const * hash1
Definition: LedgerRequestRPC_test.cpp:35
std::chrono::duration
ripple::RPC::LedgerRequestRPC_test
Definition: LedgerRequestRPC_test.cpp:33
ripple::test::jtx::forAllApiVersions
void forAllApiVersions(VersionedTestCallable auto... testCallable)
Definition: Env.h:781
std::bind_front
T bind_front(T... args)
Json::Value::toStyledString
std::string toStyledString() const
Definition: json_value.cpp:1039
ripple::RPC::LedgerRequestRPC_test::testNonAdmin
void testNonAdmin()
Definition: LedgerRequestRPC_test.cpp:342
ripple::RPC::contains_error
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:196
std::to_string
T to_string(T... args)
ripple::RPC::LedgerRequestRPC_test::accounthash1
static constexpr char const * accounthash1
Definition: LedgerRequestRPC_test.cpp:37
ripple::RPC::LedgerRequestRPC_test::testEvolution
void testEvolution()
Definition: LedgerRequestRPC_test.cpp:167
ripple::RPC::LedgerRequestRPC_test::run
void run() override
Definition: LedgerRequestRPC_test.cpp:358
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::RPC::LedgerRequestRPC_test::testBadInput
void testBadInput(unsigned apiVersion)
Definition: LedgerRequestRPC_test.cpp:268
ripple::RPC::LedgerRequestRPC_test::testLedgerRequest
void testLedgerRequest()
Definition: LedgerRequestRPC_test.cpp:45
Json::nullValue
@ nullValue
'null' value
Definition: json_value.h:36
ripple::FeatureBitset
Definition: Feature.h:113
ripple::RPC::LedgerRequestRPC_test::testMoreThan256Closed
void testMoreThan256Closed()
Definition: LedgerRequestRPC_test.cpp:309
std::unique_ptr
STL class.
Json::Value
Represents a JSON value.
Definition: json_value.h:145