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 xrpl {
17
19{
20 auto static ledgerConfig(
22 std::string const& dbPath,
23 std::string const& ledger,
24 StartUpType type,
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
36 struct SetupData
37 {
39 // NOLINTBEGIN(readability-redundant-member-init)
44 // NOLINTEND(readability-redundant-member-init)
45 };
46
47 SetupData
49 {
50 using namespace test::jtx;
51 SetupData retval = {.dbPath = td.path()};
52
53 retval.ledgerFile = td.file("ledgerdata.json");
54
55 Env env{*this};
57
58 for (auto i = 0; i < 20; ++i)
59 {
60 Account acct{"A" + std::to_string(i)};
61 env.fund(XRP(10000), acct);
62 env.close();
63 if (i > 0 && BEAST_EXPECT(prev.has_value()))
64 {
65 env.trust(acct["USD"](1000), *prev); // NOLINT(bugprone-unchecked-optional-access)
66 env(pay(
67 acct, *prev, acct["USD"](5))); // NOLINT(bugprone-unchecked-optional-access)
68 }
69 env(offer(acct, XRP(100), acct["USD"](1)));
70 env.close();
71 prev.emplace(std::move(acct));
72 }
73
74 retval.ledger = env.rpc("ledger", "current", "full")[jss::result];
75 BEAST_EXPECT(retval.ledger[jss::ledger][jss::accountState].size() == 102);
76
77 retval.hashes = [&] {
78 for (auto const& it : retval.ledger[jss::ledger][jss::accountState])
79 {
80 if (it[sfLedgerEntryType.fieldName] == jss::LedgerHashes)
81 return it[sfHashes.fieldName];
82 }
83 return Json::Value{};
84 }();
85
86 BEAST_EXPECT(retval.hashes.size() == 41);
87 retval.trapTxHash = [&]() {
88 auto const txs = env.rpc(
89 "ledger", std::to_string(41), "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,
113 nullptr,
115 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
116 BEAST_EXPECT(
117 sd.ledger[jss::ledger][jss::accountState].size() ==
118 jrb[jss::ledger][jss::accountState].size());
119 }
120
121 void
123 {
124 testcase("Load ledger: Bad Files");
125 using namespace test::jtx;
126 using namespace boost::filesystem;
127
128 // empty path
129 except([&] {
130 Env const env(
131 *this,
133 nullptr,
135 });
136
137 // file does not exist
138 except([&] {
139 Env const env(
140 *this,
141 envconfig(
143 nullptr,
145 });
146
147 // make a corrupted version of the ledger file (last 10 bytes removed).
148 boost::system::error_code ec;
149 auto ledgerFileCorrupt = boost::filesystem::path{sd.dbPath} / "ledgerdata_bad.json";
150 copy_file(sd.ledgerFile, ledgerFileCorrupt, copy_options::overwrite_existing, ec);
151 if (!BEAST_EXPECTS(!ec, ec.message()))
152 return;
153 auto filesize = file_size(ledgerFileCorrupt, ec);
154 if (!BEAST_EXPECTS(!ec, ec.message()))
155 return;
156 resize_file(ledgerFileCorrupt, filesize - 10, ec);
157 if (!BEAST_EXPECTS(!ec, ec.message()))
158 return;
159
160 except([&] {
161 Env const env(
162 *this,
163 envconfig(
165 sd.dbPath,
166 ledgerFileCorrupt.string(),
169 nullptr,
171 });
172 }
173
174 void
176 {
177 testcase("Load by hash");
178 using namespace test::jtx;
179
180 // create a new env with the ledger hash specified for startup
181 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
182 boost::erase_all(ledgerHash, "\"");
183 Env env(
184 *this,
185 envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Load, std::nullopt),
186 nullptr,
188 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
189 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98);
190 BEAST_EXPECT(
191 jrb[jss::ledger][jss::accountState].size() <=
192 sd.ledger[jss::ledger][jss::accountState].size());
193 }
194
195 void
197 {
198 testcase("Load and replay by hash");
199 using namespace test::jtx;
200
201 // create a new env with the ledger hash specified for startup
202 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
203 boost::erase_all(ledgerHash, "\"");
204 Env env(
205 *this,
206 envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Replay, std::nullopt),
207 nullptr,
209 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
210 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
211 // in replace mode do not automatically accept the ledger being replayed
212
213 env.close();
214 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
215 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
216 BEAST_EXPECT(
217 closed[jss::ledger][jss::accountState].size() <=
218 sd.ledger[jss::ledger][jss::accountState].size());
219 }
220
221 void
223 {
224 testcase("Load and replay transaction by hash");
225 using namespace test::jtx;
226
227 // create a new env with the ledger hash specified for startup
228 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
229 boost::erase_all(ledgerHash, "\"");
230 Env env(
231 *this,
232 envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Replay, sd.trapTxHash),
233 nullptr,
235 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
236 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
237 // in replace mode do not automatically accept the ledger being replayed
238
239 env.close();
240 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
241 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
242 BEAST_EXPECT(
243 closed[jss::ledger][jss::accountState].size() <=
244 sd.ledger[jss::ledger][jss::accountState].size());
245 }
246
247 void
249 {
250 testcase("Load and replay transaction by hash failure");
251 using namespace test::jtx;
252
253 // create a new env with the ledger hash specified for startup
254 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
255 boost::erase_all(ledgerHash, "\"");
256 try
257 {
258 // will throw an exception, because we cannot load a ledger for
259 // replay when trapTxHash is set to an invalid transaction
260 Env const env(
261 *this,
262 envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::Replay, ~sd.trapTxHash),
263 nullptr,
265 BEAST_EXPECT(false);
266 }
267 catch (std::runtime_error const&)
268 {
269 BEAST_EXPECT(true);
270 }
271 catch (...)
272 {
273 BEAST_EXPECT(false);
274 }
275 }
276
277 void
279 {
280 testcase("Load by keyword");
281 using namespace test::jtx;
282
283 // create a new env with the ledger "latest" specified for startup
284 Env env(
285 *this,
286 envconfig(ledgerConfig, sd.dbPath, "latest", StartUpType::Load, std::nullopt),
287 nullptr,
289 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
290 BEAST_EXPECT(
291 sd.ledger[jss::ledger][jss::accountState].size() ==
292 jrb[jss::ledger][jss::accountState].size());
293 }
294
295 void
297 {
298 testcase("Load by index");
299 using namespace test::jtx;
300
301 // create a new env with specific ledger index at startup
302 Env env(
303 *this,
304 envconfig(ledgerConfig, sd.dbPath, "43", StartUpType::Load, std::nullopt),
305 nullptr,
307 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
308 BEAST_EXPECT(
309 sd.ledger[jss::ledger][jss::accountState].size() ==
310 jrb[jss::ledger][jss::accountState].size());
311 }
312
313public:
314 void
315 run() override
316 {
317 beast::temp_dir const td;
318 auto sd = setupLedger(td);
319
320 // test cases
321 testLoad(sd);
322 testBadFiles(sd);
323 testLoadByHash(sd);
324 testReplay(sd);
325 testReplayTx(sd);
327 testLoadLatest(sd);
328 testLoadIndex(sd);
329 }
330};
331
332BEAST_DEFINE_TESTSUITE(LedgerLoad, app, xrpl);
333
334} // namespace xrpl
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:15
std::string path() const
Get the native path for the temporary directory.
Definition temp_dir.h:47
std::string file(std::string const &name) const
Get the native path for the a file.
Definition temp_dir.h:57
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
bool except(F &&f, String const &reason)
Definition suite.h:434
void testLoadLatest(SetupData const &sd)
void testReplay(SetupData const &sd)
void testReplayTxFail(SetupData const &sd)
void run() override
Runs the suite.
static auto ledgerConfig(std::unique_ptr< Config > cfg, std::string const &dbPath, std::string const &ledger, StartUpType type, std::optional< uint256 > trapTxHash)
void testBadFiles(SetupData const &sd)
void testReplayTx(SetupData const &sd)
SetupData setupLedger(beast::temp_dir const &td)
void testLoadByHash(SetupData const &sd)
void testLoadIndex(SetupData const &sd)
void testLoad(SetupData const &sd)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:476
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:5
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
StartUpType
Definition StartUpType.h:8
T to_string(T... args)