rippled
Loading...
Searching...
No Matches
LedgerLoad_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/Env.h>
3
4#include <xrpl/beast/unit_test.h>
5#include <xrpl/beast/unit_test/suite.h>
6#include <xrpl/beast/utility/Journal.h>
7#include <xrpl/beast/utility/temp_dir.h>
8#include <xrpl/protocol/SField.h>
9#include <xrpl/protocol/jss.h>
10
11#include <boost/algorithm/string.hpp>
12#include <boost/filesystem.hpp>
13
14#include <fstream>
15
16namespace ripple {
17
19{
20 auto static ledgerConfig(
22 std::string const& dbPath,
23 std::string const& ledger,
25 std::optional<uint256> trapTxHash)
26 {
27 cfg->START_LEDGER = ledger;
28 cfg->START_UP = type;
29 cfg->TRAP_TX_HASH = trapTxHash;
30 assert(!dbPath.empty());
31 cfg->legacy("database_path", dbPath);
32 return cfg;
33 }
34
35 // setup for test cases
44
45 SetupData
47 {
48 using namespace test::jtx;
49 SetupData retval = {td.path()};
50
51 retval.ledgerFile = td.file("ledgerdata.json");
52
53 Env env{*this};
55
56 for (auto i = 0; i < 20; ++i)
57 {
58 Account acct{"A" + std::to_string(i)};
59 env.fund(XRP(10000), acct);
60 env.close();
61 if (i > 0 && BEAST_EXPECT(prev))
62 {
63 env.trust(acct["USD"](1000), *prev);
64 env(pay(acct, *prev, acct["USD"](5)));
65 }
66 env(offer(acct, XRP(100), acct["USD"](1)));
67 env.close();
68 prev.emplace(std::move(acct));
69 }
70
71 retval.ledger = env.rpc("ledger", "current", "full")[jss::result];
72 BEAST_EXPECT(
73 retval.ledger[jss::ledger][jss::accountState].size() == 102);
74
75 retval.hashes = [&] {
76 for (auto const& it : retval.ledger[jss::ledger][jss::accountState])
77 {
78 if (it[sfLedgerEntryType.fieldName] == jss::LedgerHashes)
79 return it[sfHashes.fieldName];
80 }
81 return Json::Value{};
82 }();
83
84 BEAST_EXPECT(retval.hashes.size() == 41);
85 retval.trapTxHash = [&]() {
86 auto const txs = env.rpc(
87 "ledger",
89 "tx")[jss::result][jss::ledger][jss::transactions];
90 BEAST_EXPECT(txs.isArray() && txs.size() > 0);
91 uint256 tmp;
92 BEAST_EXPECT(tmp.parseHex(txs[0u][jss::hash].asString()));
93 return tmp;
94 }();
95
96 // write this ledger data to a file.
97 std::ofstream o(retval.ledgerFile, std::ios::out | std::ios::trunc);
98 o << to_string(retval.ledger);
99 o.close();
100 return retval;
101 }
102
103 void
105 {
106 testcase("Load a saved ledger");
107 using namespace test::jtx;
108
109 // create a new env with the ledger file specified for startup
110 Env env(
111 *this,
112 envconfig(
114 sd.dbPath,
115 sd.ledgerFile,
118 nullptr,
120 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
121 BEAST_EXPECT(
122 sd.ledger[jss::ledger][jss::accountState].size() ==
123 jrb[jss::ledger][jss::accountState].size());
124 }
125
126 void
128 {
129 testcase("Load ledger: Bad Files");
130 using namespace test::jtx;
131 using namespace boost::filesystem;
132
133 // empty path
134 except([&] {
135 Env env(
136 *this,
137 envconfig(
139 sd.dbPath,
140 "",
143 nullptr,
145 });
146
147 // file does not exist
148 except([&] {
149 Env env(
150 *this,
151 envconfig(
153 sd.dbPath,
154 "badfile.json",
157 nullptr,
159 });
160
161 // make a corrupted version of the ledger file (last 10 bytes removed).
162 boost::system::error_code ec;
163 auto ledgerFileCorrupt =
164 boost::filesystem::path{sd.dbPath} / "ledgerdata_bad.json";
165 copy_file(
166 sd.ledgerFile,
167 ledgerFileCorrupt,
168 copy_options::overwrite_existing,
169 ec);
170 if (!BEAST_EXPECTS(!ec, ec.message()))
171 return;
172 auto filesize = file_size(ledgerFileCorrupt, ec);
173 if (!BEAST_EXPECTS(!ec, ec.message()))
174 return;
175 resize_file(ledgerFileCorrupt, filesize - 10, ec);
176 if (!BEAST_EXPECTS(!ec, ec.message()))
177 return;
178
179 except([&] {
180 Env env(
181 *this,
182 envconfig(
184 sd.dbPath,
185 ledgerFileCorrupt.string(),
188 nullptr,
190 });
191 }
192
193 void
195 {
196 testcase("Load by hash");
197 using namespace test::jtx;
198
199 // create a new env with the ledger hash specified for startup
200 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
201 boost::erase_all(ledgerHash, "\"");
202 Env env(
203 *this,
204 envconfig(
206 sd.dbPath,
207 ledgerHash,
210 nullptr,
212 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
213 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98);
214 BEAST_EXPECT(
215 jrb[jss::ledger][jss::accountState].size() <=
216 sd.ledger[jss::ledger][jss::accountState].size());
217 }
218
219 void
221 {
222 testcase("Load and replay by hash");
223 using namespace test::jtx;
224
225 // create a new env with the ledger hash specified for startup
226 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
227 boost::erase_all(ledgerHash, "\"");
228 Env env(
229 *this,
230 envconfig(
232 sd.dbPath,
233 ledgerHash,
236 nullptr,
238 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
239 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
240 // in replace mode do not automatically accept the ledger being replayed
241
242 env.close();
243 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
244 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
245 BEAST_EXPECT(
246 closed[jss::ledger][jss::accountState].size() <=
247 sd.ledger[jss::ledger][jss::accountState].size());
248 }
249
250 void
252 {
253 testcase("Load and replay transaction by hash");
254 using namespace test::jtx;
255
256 // create a new env with the ledger hash specified for startup
257 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
258 boost::erase_all(ledgerHash, "\"");
259 Env env(
260 *this,
261 envconfig(
263 sd.dbPath,
264 ledgerHash,
266 sd.trapTxHash),
267 nullptr,
269 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
270 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
271 // in replace mode do not automatically accept the ledger being replayed
272
273 env.close();
274 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
275 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
276 BEAST_EXPECT(
277 closed[jss::ledger][jss::accountState].size() <=
278 sd.ledger[jss::ledger][jss::accountState].size());
279 }
280
281 void
283 {
284 testcase("Load and replay transaction by hash failure");
285 using namespace test::jtx;
286
287 // create a new env with the ledger hash specified for startup
288 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
289 boost::erase_all(ledgerHash, "\"");
290 try
291 {
292 // will throw an exception, because we cannot load a ledger for
293 // replay when trapTxHash is set to an invalid transaction
294 Env env(
295 *this,
296 envconfig(
298 sd.dbPath,
299 ledgerHash,
301 ~sd.trapTxHash),
302 nullptr,
304 BEAST_EXPECT(false);
305 }
306 catch (std::runtime_error const&)
307 {
308 BEAST_EXPECT(true);
309 }
310 catch (...)
311 {
312 BEAST_EXPECT(false);
313 }
314 }
315
316 void
318 {
319 testcase("Load by keyword");
320 using namespace test::jtx;
321
322 // create a new env with the ledger "latest" specified for startup
323 Env env(
324 *this,
325 envconfig(
327 nullptr,
329 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
330 BEAST_EXPECT(
331 sd.ledger[jss::ledger][jss::accountState].size() ==
332 jrb[jss::ledger][jss::accountState].size());
333 }
334
335 void
337 {
338 testcase("Load by index");
339 using namespace test::jtx;
340
341 // create a new env with specific ledger index at 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
354public:
355 void
356 run() override
357 {
359 auto sd = setupLedger(td);
360
361 // test cases
362 testLoad(sd);
363 testBadFiles(sd);
364 testLoadByHash(sd);
365 testReplay(sd);
366 testReplayTx(sd);
368 testLoadLatest(sd);
369 testLoadIndex(sd);
370 }
371};
372
373BEAST_DEFINE_TESTSUITE(LedgerLoad, app, ripple);
374
375} // namespace ripple
Represents a JSON value.
Definition json_value.h:130
UInt size() const
Number of values in array or object.
RAII temporary directory.
Definition temp_dir.h:16
std::string path() const
Get the native path for the temporary directory.
Definition temp_dir.h:48
std::string file(std::string const &name) const
Get the native path for the a file.
Definition temp_dir.h:58
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
bool except(F &&f, String const &reason)
Definition suite.h:445
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:484
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:6
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
T to_string(T... args)