rippled
Loading...
Searching...
No Matches
LedgerLoad_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-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 <test/jtx.h>
21#include <test/jtx/Env.h>
22
23#include <xrpl/beast/unit_test.h>
24#include <xrpl/beast/unit_test/suite.h>
25#include <xrpl/beast/utility/Journal.h>
26#include <xrpl/beast/utility/temp_dir.h>
27#include <xrpl/protocol/SField.h>
28#include <xrpl/protocol/jss.h>
29
30#include <boost/algorithm/string.hpp>
31#include <boost/filesystem.hpp>
32
33#include <fstream>
34
35namespace ripple {
36
38{
39 auto static ledgerConfig(
41 std::string const& dbPath,
42 std::string const& ledger,
44 std::optional<uint256> trapTxHash)
45 {
46 cfg->START_LEDGER = ledger;
47 cfg->START_UP = type;
48 cfg->TRAP_TX_HASH = trapTxHash;
49 assert(!dbPath.empty());
50 cfg->legacy("database_path", dbPath);
51 return cfg;
52 }
53
54 // setup for test cases
63
64 SetupData
66 {
67 using namespace test::jtx;
68 SetupData retval = {td.path()};
69
70 retval.ledgerFile = td.file("ledgerdata.json");
71
72 Env env{*this};
74
75 for (auto i = 0; i < 20; ++i)
76 {
77 Account acct{"A" + std::to_string(i)};
78 env.fund(XRP(10000), acct);
79 env.close();
80 if (i > 0 && BEAST_EXPECT(prev))
81 {
82 env.trust(acct["USD"](1000), *prev);
83 env(pay(acct, *prev, acct["USD"](5)));
84 }
85 env(offer(acct, XRP(100), acct["USD"](1)));
86 env.close();
87 prev.emplace(std::move(acct));
88 }
89
90 retval.ledger = env.rpc("ledger", "current", "full")[jss::result];
91 BEAST_EXPECT(
92 retval.ledger[jss::ledger][jss::accountState].size() == 102);
93
94 retval.hashes = [&] {
95 for (auto const& it : retval.ledger[jss::ledger][jss::accountState])
96 {
97 if (it[sfLedgerEntryType.fieldName] == jss::LedgerHashes)
98 return it[sfHashes.fieldName];
99 }
100 return Json::Value{};
101 }();
102
103 BEAST_EXPECT(retval.hashes.size() == 41);
104 retval.trapTxHash = [&]() {
105 auto const txs = env.rpc(
106 "ledger",
107 std::to_string(41),
108 "tx")[jss::result][jss::ledger][jss::transactions];
109 BEAST_EXPECT(txs.isArray() && txs.size() > 0);
110 uint256 tmp;
111 BEAST_EXPECT(tmp.parseHex(txs[0u][jss::hash].asString()));
112 return tmp;
113 }();
114
115 // write this ledger data to a file.
116 std::ofstream o(retval.ledgerFile, std::ios::out | std::ios::trunc);
117 o << to_string(retval.ledger);
118 o.close();
119 return retval;
120 }
121
122 void
124 {
125 testcase("Load a saved ledger");
126 using namespace test::jtx;
127
128 // create a new env with the ledger file specified for startup
129 Env env(
130 *this,
131 envconfig(
133 sd.dbPath,
134 sd.ledgerFile,
137 nullptr,
139 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
140 BEAST_EXPECT(
141 sd.ledger[jss::ledger][jss::accountState].size() ==
142 jrb[jss::ledger][jss::accountState].size());
143 }
144
145 void
147 {
148 testcase("Load ledger: Bad Files");
149 using namespace test::jtx;
150 using namespace boost::filesystem;
151
152 // empty path
153 except([&] {
154 Env env(
155 *this,
156 envconfig(
158 sd.dbPath,
159 "",
162 nullptr,
164 });
165
166 // file does not exist
167 except([&] {
168 Env env(
169 *this,
170 envconfig(
172 sd.dbPath,
173 "badfile.json",
176 nullptr,
178 });
179
180 // make a corrupted version of the ledger file (last 10 bytes removed).
181 boost::system::error_code ec;
182 auto ledgerFileCorrupt =
183 boost::filesystem::path{sd.dbPath} / "ledgerdata_bad.json";
184 copy_file(
185 sd.ledgerFile,
186 ledgerFileCorrupt,
187 copy_options::overwrite_existing,
188 ec);
189 if (!BEAST_EXPECTS(!ec, ec.message()))
190 return;
191 auto filesize = file_size(ledgerFileCorrupt, ec);
192 if (!BEAST_EXPECTS(!ec, ec.message()))
193 return;
194 resize_file(ledgerFileCorrupt, filesize - 10, ec);
195 if (!BEAST_EXPECTS(!ec, ec.message()))
196 return;
197
198 except([&] {
199 Env env(
200 *this,
201 envconfig(
203 sd.dbPath,
204 ledgerFileCorrupt.string(),
207 nullptr,
209 });
210 }
211
212 void
214 {
215 testcase("Load by hash");
216 using namespace test::jtx;
217
218 // create a new env with the ledger hash specified for startup
219 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
220 boost::erase_all(ledgerHash, "\"");
221 Env env(
222 *this,
223 envconfig(
225 sd.dbPath,
226 ledgerHash,
229 nullptr,
231 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
232 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98);
233 BEAST_EXPECT(
234 jrb[jss::ledger][jss::accountState].size() <=
235 sd.ledger[jss::ledger][jss::accountState].size());
236 }
237
238 void
240 {
241 testcase("Load and replay by hash");
242 using namespace test::jtx;
243
244 // create a new env with the ledger hash specified for startup
245 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
246 boost::erase_all(ledgerHash, "\"");
247 Env env(
248 *this,
249 envconfig(
251 sd.dbPath,
252 ledgerHash,
255 nullptr,
257 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
258 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
259 // in replace mode do not automatically accept the ledger being replayed
260
261 env.close();
262 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
263 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
264 BEAST_EXPECT(
265 closed[jss::ledger][jss::accountState].size() <=
266 sd.ledger[jss::ledger][jss::accountState].size());
267 }
268
269 void
271 {
272 testcase("Load and replay transaction by hash");
273 using namespace test::jtx;
274
275 // create a new env with the ledger hash specified for startup
276 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
277 boost::erase_all(ledgerHash, "\"");
278 Env env(
279 *this,
280 envconfig(
282 sd.dbPath,
283 ledgerHash,
285 sd.trapTxHash),
286 nullptr,
288 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
289 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
290 // in replace mode do not automatically accept the ledger being replayed
291
292 env.close();
293 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
294 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
295 BEAST_EXPECT(
296 closed[jss::ledger][jss::accountState].size() <=
297 sd.ledger[jss::ledger][jss::accountState].size());
298 }
299
300 void
302 {
303 testcase("Load and replay transaction by hash failure");
304 using namespace test::jtx;
305
306 // create a new env with the ledger hash specified for startup
307 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
308 boost::erase_all(ledgerHash, "\"");
309 try
310 {
311 // will throw an exception, because we cannot load a ledger for
312 // replay when trapTxHash is set to an invalid transaction
313 Env env(
314 *this,
315 envconfig(
317 sd.dbPath,
318 ledgerHash,
320 ~sd.trapTxHash),
321 nullptr,
323 BEAST_EXPECT(false);
324 }
325 catch (std::runtime_error const&)
326 {
327 BEAST_EXPECT(true);
328 }
329 catch (...)
330 {
331 BEAST_EXPECT(false);
332 }
333 }
334
335 void
337 {
338 testcase("Load by keyword");
339 using namespace test::jtx;
340
341 // create a new env with the ledger "latest" specified for startup
342 Env env(
343 *this,
344 envconfig(
346 nullptr,
348 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
349 BEAST_EXPECT(
350 sd.ledger[jss::ledger][jss::accountState].size() ==
351 jrb[jss::ledger][jss::accountState].size());
352 }
353
354 void
356 {
357 testcase("Load by index");
358 using namespace test::jtx;
359
360 // create a new env with specific ledger index at startup
361 Env env(
362 *this,
363 envconfig(
365 nullptr,
367 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
368 BEAST_EXPECT(
369 sd.ledger[jss::ledger][jss::accountState].size() ==
370 jrb[jss::ledger][jss::accountState].size());
371 }
372
373public:
374 void
375 run() override
376 {
378 auto sd = setupLedger(td);
379
380 // test cases
381 testLoad(sd);
382 testBadFiles(sd);
383 testLoadByHash(sd);
384 testReplay(sd);
385 testReplayTx(sd);
387 testLoadLatest(sd);
388 testLoadIndex(sd);
389 }
390};
391
392BEAST_DEFINE_TESTSUITE(LedgerLoad, app, ripple);
393
394} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
UInt size() const
Number of values in array or object.
RAII temporary directory.
Definition temp_dir.h:35
std::string path() const
Get the native path for the temporary directory.
Definition temp_dir.h:67
std::string file(std::string const &name) const
Get the native path for the a file.
Definition temp_dir.h:77
A testsuite class.
Definition suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
bool except(F &&f, String const &reason)
Definition suite.h:448
SetupData setupLedger(beast::temp_dir const &td)
void testReplayTx(SetupData const &sd)
void testLoadByHash(SetupData const &sd)
void testLoadIndex(SetupData const &sd)
void run() override
Runs the suite.
void testReplayTxFail(SetupData const &sd)
static auto ledgerConfig(std::unique_ptr< Config > cfg, std::string const &dbPath, std::string const &ledger, Config::StartUpType type, std::optional< uint256 > trapTxHash)
void testBadFiles(SetupData const &sd)
void testLoad(SetupData const &sd)
void testReplay(SetupData const &sd)
void testLoadLatest(SetupData const &sd)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:503
T close(T... args)
T empty(T... args)
T is_same_v
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
T to_string(T... args)