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#include <fstream>
32
33namespace ripple {
34
36{
37 auto static ledgerConfig(
39 std::string const& dbPath,
40 std::string const& ledger,
42 std::optional<uint256> trapTxHash)
43 {
44 cfg->START_LEDGER = ledger;
45 cfg->START_UP = type;
46 cfg->TRAP_TX_HASH = trapTxHash;
47 assert(!dbPath.empty());
48 cfg->legacy("database_path", dbPath);
49 return cfg;
50 }
51
52 // setup for test cases
53 struct SetupData
54 {
60 };
61
62 SetupData
64 {
65 using namespace test::jtx;
66 SetupData retval = {td.path()};
67
68 retval.ledgerFile = td.file("ledgerdata.json");
69
70 Env env{*this};
72
73 for (auto i = 0; i < 20; ++i)
74 {
75 Account acct{"A" + std::to_string(i)};
76 env.fund(XRP(10000), acct);
77 env.close();
78 if (i > 0 && BEAST_EXPECT(prev))
79 {
80 env.trust(acct["USD"](1000), *prev);
81 env(pay(acct, *prev, acct["USD"](5)));
82 }
83 env(offer(acct, XRP(100), acct["USD"](1)));
84 env.close();
85 prev.emplace(std::move(acct));
86 }
87
88 retval.ledger = env.rpc("ledger", "current", "full")[jss::result];
89 BEAST_EXPECT(
90 retval.ledger[jss::ledger][jss::accountState].size() == 102);
91
92 retval.hashes = [&] {
93 for (auto const& it : retval.ledger[jss::ledger][jss::accountState])
94 {
95 if (it[sfLedgerEntryType.fieldName] == jss::LedgerHashes)
96 return it[sfHashes.fieldName];
97 }
98 return Json::Value{};
99 }();
100
101 BEAST_EXPECT(retval.hashes.size() == 41);
102 retval.trapTxHash = [&]() {
103 auto const txs = env.rpc(
104 "ledger",
105 std::to_string(41),
106 "tx")[jss::result][jss::ledger][jss::transactions];
107 BEAST_EXPECT(txs.isArray() && txs.size() > 0);
108 uint256 tmp;
109 BEAST_EXPECT(tmp.parseHex(txs[0u][jss::hash].asString()));
110 return tmp;
111 }();
112
113 // write this ledger data to a file.
114 std::ofstream o(retval.ledgerFile, std::ios::out | std::ios::trunc);
115 o << to_string(retval.ledger);
116 o.close();
117 return retval;
118 }
119
120 void
122 {
123 testcase("Load a saved ledger");
124 using namespace test::jtx;
125
126 // create a new env with the ledger file specified for startup
127 Env env(
128 *this,
129 envconfig(
131 sd.dbPath,
132 sd.ledgerFile,
134 std::nullopt),
135 nullptr,
137 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
138 BEAST_EXPECT(
139 sd.ledger[jss::ledger][jss::accountState].size() ==
140 jrb[jss::ledger][jss::accountState].size());
141 }
142
143 void
145 {
146 testcase("Load ledger: Bad Files");
147 using namespace test::jtx;
148 using namespace boost::filesystem;
149
150 // empty path
151 except([&] {
152 Env env(
153 *this,
154 envconfig(
156 sd.dbPath,
157 "",
159 std::nullopt),
160 nullptr,
162 });
163
164 // file does not exist
165 except([&] {
166 Env env(
167 *this,
168 envconfig(
170 sd.dbPath,
171 "badfile.json",
173 std::nullopt),
174 nullptr,
176 });
177
178 // make a corrupted version of the ledger file (last 10 bytes removed).
179 boost::system::error_code ec;
180 auto ledgerFileCorrupt =
181 boost::filesystem::path{sd.dbPath} / "ledgerdata_bad.json";
182 copy_file(
183 sd.ledgerFile,
184 ledgerFileCorrupt,
185 copy_options::overwrite_existing,
186 ec);
187 if (!BEAST_EXPECTS(!ec, ec.message()))
188 return;
189 auto filesize = file_size(ledgerFileCorrupt, ec);
190 if (!BEAST_EXPECTS(!ec, ec.message()))
191 return;
192 resize_file(ledgerFileCorrupt, filesize - 10, ec);
193 if (!BEAST_EXPECTS(!ec, ec.message()))
194 return;
195
196 except([&] {
197 Env env(
198 *this,
199 envconfig(
201 sd.dbPath,
202 ledgerFileCorrupt.string(),
204 std::nullopt),
205 nullptr,
207 });
208 }
209
210 void
212 {
213 testcase("Load by hash");
214 using namespace test::jtx;
215
216 // create a new env with the ledger hash specified for startup
217 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
218 boost::erase_all(ledgerHash, "\"");
219 Env env(
220 *this,
221 envconfig(
223 sd.dbPath,
224 ledgerHash,
226 std::nullopt),
227 nullptr,
229 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
230 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98);
231 BEAST_EXPECT(
232 jrb[jss::ledger][jss::accountState].size() <=
233 sd.ledger[jss::ledger][jss::accountState].size());
234 }
235
236 void
238 {
239 testcase("Load and replay by hash");
240 using namespace test::jtx;
241
242 // create a new env with the ledger hash specified for startup
243 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
244 boost::erase_all(ledgerHash, "\"");
245 Env env(
246 *this,
247 envconfig(
249 sd.dbPath,
250 ledgerHash,
252 std::nullopt),
253 nullptr,
255 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
256 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
257 // in replace mode do not automatically accept the ledger being replayed
258
259 env.close();
260 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
261 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
262 BEAST_EXPECT(
263 closed[jss::ledger][jss::accountState].size() <=
264 sd.ledger[jss::ledger][jss::accountState].size());
265 }
266
267 void
269 {
270 testcase("Load and replay transaction by hash");
271 using namespace test::jtx;
272
273 // create a new env with the ledger hash specified for startup
274 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
275 boost::erase_all(ledgerHash, "\"");
276 Env env(
277 *this,
278 envconfig(
280 sd.dbPath,
281 ledgerHash,
283 sd.trapTxHash),
284 nullptr,
286 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
287 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
288 // in replace mode do not automatically accept the ledger being replayed
289
290 env.close();
291 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
292 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
293 BEAST_EXPECT(
294 closed[jss::ledger][jss::accountState].size() <=
295 sd.ledger[jss::ledger][jss::accountState].size());
296 }
297
298 void
300 {
301 testcase("Load and replay transaction by hash failure");
302 using namespace test::jtx;
303
304 // create a new env with the ledger hash specified for startup
305 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
306 boost::erase_all(ledgerHash, "\"");
307 try
308 {
309 // will throw an exception, because we cannot load a ledger for
310 // replay when trapTxHash is set to an invalid transaction
311 Env env(
312 *this,
313 envconfig(
315 sd.dbPath,
316 ledgerHash,
318 ~sd.trapTxHash),
319 nullptr,
321 BEAST_EXPECT(false);
322 }
323 catch (std::runtime_error const&)
324 {
325 BEAST_EXPECT(true);
326 }
327 catch (...)
328 {
329 BEAST_EXPECT(false);
330 }
331 }
332
333 void
335 {
336 testcase("Load by keyword");
337 using namespace test::jtx;
338
339 // create a new env with the ledger "latest" specified for startup
340 Env env(
341 *this,
342 envconfig(
343 ledgerConfig, sd.dbPath, "latest", Config::LOAD, std::nullopt),
344 nullptr,
346 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
347 BEAST_EXPECT(
348 sd.ledger[jss::ledger][jss::accountState].size() ==
349 jrb[jss::ledger][jss::accountState].size());
350 }
351
352 void
354 {
355 testcase("Load by index");
356 using namespace test::jtx;
357
358 // create a new env with specific ledger index at startup
359 Env env(
360 *this,
361 envconfig(
362 ledgerConfig, sd.dbPath, "43", Config::LOAD, std::nullopt),
363 nullptr,
365 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
366 BEAST_EXPECT(
367 sd.ledger[jss::ledger][jss::accountState].size() ==
368 jrb[jss::ledger][jss::accountState].size());
369 }
370
371public:
372 void
373 run() override
374 {
376 auto sd = setupLedger(td);
377
378 // test cases
379 testLoad(sd);
380 testBadFiles(sd);
381 testLoadByHash(sd);
382 testReplay(sd);
383 testReplayTx(sd);
385 testLoadLatest(sd);
386 testLoadIndex(sd);
387 }
388};
389
390BEAST_DEFINE_TESTSUITE(LedgerLoad, app, ripple);
391
392} // namespace ripple
Represents a JSON value.
Definition: json_value.h:147
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
RAII temporary directory.
Definition: temp_dir.h:34
std::string path() const
Get the native path for the temporary directory.
Definition: temp_dir.h:66
std::string file(std::string const &name) const
Get the native path for the a file.
Definition: temp_dir.h:76
A testsuite class.
Definition: suite.h:53
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
bool except(F &&f, String const &reason)
Definition: suite.h:446
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:502
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:629
T to_string(T... args)