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#include <xrpl/beast/unit_test.h>
23#include <xrpl/beast/unit_test/suite.h>
24#include <xrpl/beast/utility/Journal.h>
25#include <xrpl/beast/utility/temp_dir.h>
26#include <xrpl/protocol/SField.h>
27#include <xrpl/protocol/jss.h>
28
29#include <boost/algorithm/string.hpp>
30#include <boost/filesystem.hpp>
31
32#include <fstream>
33
34namespace ripple {
35
37{
38 auto static ledgerConfig(
40 std::string const& dbPath,
41 std::string const& ledger,
43 std::optional<uint256> trapTxHash)
44 {
45 cfg->START_LEDGER = ledger;
46 cfg->START_UP = type;
47 cfg->TRAP_TX_HASH = trapTxHash;
48 assert(!dbPath.empty());
49 cfg->legacy("database_path", dbPath);
50 return cfg;
51 }
52
53 // setup for test cases
54 struct SetupData
55 {
61 };
62
63 SetupData
65 {
66 using namespace test::jtx;
67 SetupData retval = {td.path()};
68
69 retval.ledgerFile = td.file("ledgerdata.json");
70
71 Env env{*this};
73
74 for (auto i = 0; i < 20; ++i)
75 {
76 Account acct{"A" + std::to_string(i)};
77 env.fund(XRP(10000), acct);
78 env.close();
79 if (i > 0 && BEAST_EXPECT(prev))
80 {
81 env.trust(acct["USD"](1000), *prev);
82 env(pay(acct, *prev, acct["USD"](5)));
83 }
84 env(offer(acct, XRP(100), acct["USD"](1)));
85 env.close();
86 prev.emplace(std::move(acct));
87 }
88
89 retval.ledger = env.rpc("ledger", "current", "full")[jss::result];
90 BEAST_EXPECT(
91 retval.ledger[jss::ledger][jss::accountState].size() == 102);
92
93 retval.hashes = [&] {
94 for (auto const& it : retval.ledger[jss::ledger][jss::accountState])
95 {
96 if (it[sfLedgerEntryType.fieldName] == jss::LedgerHashes)
97 return it[sfHashes.fieldName];
98 }
99 return Json::Value{};
100 }();
101
102 BEAST_EXPECT(retval.hashes.size() == 41);
103 retval.trapTxHash = [&]() {
104 auto const txs = env.rpc(
105 "ledger",
106 std::to_string(41),
107 "tx")[jss::result][jss::ledger][jss::transactions];
108 BEAST_EXPECT(txs.isArray() && txs.size() > 0);
109 uint256 tmp;
110 BEAST_EXPECT(tmp.parseHex(txs[0u][jss::hash].asString()));
111 return tmp;
112 }();
113
114 // write this ledger data to a file.
115 std::ofstream o(retval.ledgerFile, std::ios::out | std::ios::trunc);
116 o << to_string(retval.ledger);
117 o.close();
118 return retval;
119 }
120
121 void
123 {
124 testcase("Load a saved ledger");
125 using namespace test::jtx;
126
127 // create a new env with the ledger file specified for startup
128 Env env(
129 *this,
130 envconfig(
132 sd.dbPath,
133 sd.ledgerFile,
135 std::nullopt),
136 nullptr,
138 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
139 BEAST_EXPECT(
140 sd.ledger[jss::ledger][jss::accountState].size() ==
141 jrb[jss::ledger][jss::accountState].size());
142 }
143
144 void
146 {
147 testcase("Load ledger: Bad Files");
148 using namespace test::jtx;
149 using namespace boost::filesystem;
150
151 // empty path
152 except([&] {
153 Env env(
154 *this,
155 envconfig(
157 sd.dbPath,
158 "",
160 std::nullopt),
161 nullptr,
163 });
164
165 // file does not exist
166 except([&] {
167 Env env(
168 *this,
169 envconfig(
171 sd.dbPath,
172 "badfile.json",
174 std::nullopt),
175 nullptr,
177 });
178
179 // make a corrupted version of the ledger file (last 10 bytes removed).
180 boost::system::error_code ec;
181 auto ledgerFileCorrupt =
182 boost::filesystem::path{sd.dbPath} / "ledgerdata_bad.json";
183 copy_file(
184 sd.ledgerFile,
185 ledgerFileCorrupt,
186 copy_options::overwrite_existing,
187 ec);
188 if (!BEAST_EXPECTS(!ec, ec.message()))
189 return;
190 auto filesize = file_size(ledgerFileCorrupt, ec);
191 if (!BEAST_EXPECTS(!ec, ec.message()))
192 return;
193 resize_file(ledgerFileCorrupt, filesize - 10, ec);
194 if (!BEAST_EXPECTS(!ec, ec.message()))
195 return;
196
197 except([&] {
198 Env env(
199 *this,
200 envconfig(
202 sd.dbPath,
203 ledgerFileCorrupt.string(),
205 std::nullopt),
206 nullptr,
208 });
209 }
210
211 void
213 {
214 testcase("Load by hash");
215 using namespace test::jtx;
216
217 // create a new env with the ledger hash specified for startup
218 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
219 boost::erase_all(ledgerHash, "\"");
220 Env env(
221 *this,
222 envconfig(
224 sd.dbPath,
225 ledgerHash,
227 std::nullopt),
228 nullptr,
230 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
231 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98);
232 BEAST_EXPECT(
233 jrb[jss::ledger][jss::accountState].size() <=
234 sd.ledger[jss::ledger][jss::accountState].size());
235 }
236
237 void
239 {
240 testcase("Load and replay by hash");
241 using namespace test::jtx;
242
243 // create a new env with the ledger hash specified for startup
244 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
245 boost::erase_all(ledgerHash, "\"");
246 Env env(
247 *this,
248 envconfig(
250 sd.dbPath,
251 ledgerHash,
253 std::nullopt),
254 nullptr,
256 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
257 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
258 // in replace mode do not automatically accept the ledger being replayed
259
260 env.close();
261 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
262 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
263 BEAST_EXPECT(
264 closed[jss::ledger][jss::accountState].size() <=
265 sd.ledger[jss::ledger][jss::accountState].size());
266 }
267
268 void
270 {
271 testcase("Load and replay transaction by hash");
272 using namespace test::jtx;
273
274 // create a new env with the ledger hash specified for startup
275 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
276 boost::erase_all(ledgerHash, "\"");
277 Env env(
278 *this,
279 envconfig(
281 sd.dbPath,
282 ledgerHash,
284 sd.trapTxHash),
285 nullptr,
287 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
288 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
289 // in replace mode do not automatically accept the ledger being replayed
290
291 env.close();
292 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
293 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
294 BEAST_EXPECT(
295 closed[jss::ledger][jss::accountState].size() <=
296 sd.ledger[jss::ledger][jss::accountState].size());
297 }
298
299 void
301 {
302 testcase("Load and replay transaction by hash failure");
303 using namespace test::jtx;
304
305 // create a new env with the ledger hash specified for startup
306 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
307 boost::erase_all(ledgerHash, "\"");
308 try
309 {
310 // will throw an exception, because we cannot load a ledger for
311 // replay when trapTxHash is set to an invalid transaction
312 Env env(
313 *this,
314 envconfig(
316 sd.dbPath,
317 ledgerHash,
319 ~sd.trapTxHash),
320 nullptr,
322 BEAST_EXPECT(false);
323 }
324 catch (std::runtime_error const&)
325 {
326 BEAST_EXPECT(true);
327 }
328 catch (...)
329 {
330 BEAST_EXPECT(false);
331 }
332 }
333
334 void
336 {
337 testcase("Load by keyword");
338 using namespace test::jtx;
339
340 // create a new env with the ledger "latest" specified for startup
341 Env env(
342 *this,
343 envconfig(
344 ledgerConfig, sd.dbPath, "latest", Config::LOAD, std::nullopt),
345 nullptr,
347 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
348 BEAST_EXPECT(
349 sd.ledger[jss::ledger][jss::accountState].size() ==
350 jrb[jss::ledger][jss::accountState].size());
351 }
352
353 void
355 {
356 testcase("Load by index");
357 using namespace test::jtx;
358
359 // create a new env with specific ledger index at startup
360 Env env(
361 *this,
362 envconfig(
363 ledgerConfig, sd.dbPath, "43", Config::LOAD, std::nullopt),
364 nullptr,
366 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
367 BEAST_EXPECT(
368 sd.ledger[jss::ledger][jss::accountState].size() ==
369 jrb[jss::ledger][jss::accountState].size());
370 }
371
372public:
373 void
374 run() override
375 {
377 auto sd = setupLedger(td);
378
379 // test cases
380 testLoad(sd);
381 testBadFiles(sd);
382 testLoadByHash(sd);
383 testReplay(sd);
384 testReplayTx(sd);
386 testLoadLatest(sd);
387 testLoadIndex(sd);
388 }
389};
390
391BEAST_DEFINE_TESTSUITE(LedgerLoad, app, ripple);
392
393} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:712
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)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
T to_string(T... args)