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,
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(retval.ledger[jss::ledger][jss::accountState].size() == 102);
73
74 retval.hashes = [&] {
75 for (auto const& it : retval.ledger[jss::ledger][jss::accountState])
76 {
77 if (it[sfLedgerEntryType.fieldName] == jss::LedgerHashes)
78 return it[sfHashes.fieldName];
79 }
80 return Json::Value{};
81 }();
82
83 BEAST_EXPECT(retval.hashes.size() == 41);
84 retval.trapTxHash = [&]() {
85 auto const txs = env.rpc("ledger", std::to_string(41), "tx")[jss::result][jss::ledger][jss::transactions];
86 BEAST_EXPECT(txs.isArray() && txs.size() > 0);
87 uint256 tmp;
88 BEAST_EXPECT(tmp.parseHex(txs[0u][jss::hash].asString()));
89 return tmp;
90 }();
91
92 // write this ledger data to a file.
93 std::ofstream o(retval.ledgerFile, std::ios::out | std::ios::trunc);
94 o << to_string(retval.ledger);
95 o.close();
96 return retval;
97 }
98
99 void
101 {
102 testcase("Load a saved ledger");
103 using namespace test::jtx;
104
105 // create a new env with the ledger file specified for startup
106 Env env(
107 *this,
109 nullptr,
111 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
112 BEAST_EXPECT(sd.ledger[jss::ledger][jss::accountState].size() == jrb[jss::ledger][jss::accountState].size());
113 }
114
115 void
117 {
118 testcase("Load ledger: Bad Files");
119 using namespace test::jtx;
120 using namespace boost::filesystem;
121
122 // empty path
123 except([&] {
124 Env env(
125 *this,
127 nullptr,
129 });
130
131 // file does not exist
132 except([&] {
133 Env env(
134 *this,
135 envconfig(ledgerConfig, sd.dbPath, "badfile.json", Config::LOAD_FILE, std::nullopt),
136 nullptr,
138 });
139
140 // make a corrupted version of the ledger file (last 10 bytes removed).
141 boost::system::error_code ec;
142 auto ledgerFileCorrupt = boost::filesystem::path{sd.dbPath} / "ledgerdata_bad.json";
143 copy_file(sd.ledgerFile, ledgerFileCorrupt, copy_options::overwrite_existing, ec);
144 if (!BEAST_EXPECTS(!ec, ec.message()))
145 return;
146 auto filesize = file_size(ledgerFileCorrupt, ec);
147 if (!BEAST_EXPECTS(!ec, ec.message()))
148 return;
149 resize_file(ledgerFileCorrupt, filesize - 10, ec);
150 if (!BEAST_EXPECTS(!ec, ec.message()))
151 return;
152
153 except([&] {
154 Env env(
155 *this,
156 envconfig(ledgerConfig, sd.dbPath, ledgerFileCorrupt.string(), Config::LOAD_FILE, std::nullopt),
157 nullptr,
159 });
160 }
161
162 void
164 {
165 testcase("Load by hash");
166 using namespace test::jtx;
167
168 // create a new env with the ledger hash specified for startup
169 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
170 boost::erase_all(ledgerHash, "\"");
171 Env env(
172 *this,
173 envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::LOAD, std::nullopt),
174 nullptr,
176 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
177 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 98);
178 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() <= sd.ledger[jss::ledger][jss::accountState].size());
179 }
180
181 void
183 {
184 testcase("Load and replay by hash");
185 using namespace test::jtx;
186
187 // create a new env with the ledger hash specified for startup
188 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
189 boost::erase_all(ledgerHash, "\"");
190 Env env(
191 *this,
192 envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, std::nullopt),
193 nullptr,
195 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
196 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
197 // in replace mode do not automatically accept the ledger being replayed
198
199 env.close();
200 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
201 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
202 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() <= sd.ledger[jss::ledger][jss::accountState].size());
203 }
204
205 void
207 {
208 testcase("Load and replay transaction by hash");
209 using namespace test::jtx;
210
211 // create a new env with the ledger hash specified for startup
212 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
213 boost::erase_all(ledgerHash, "\"");
214 Env env(
215 *this,
216 envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, sd.trapTxHash),
217 nullptr,
219 auto const jrb = env.rpc("ledger", "current", "full")[jss::result];
220 BEAST_EXPECT(jrb[jss::ledger][jss::accountState].size() == 97);
221 // in replace mode do not automatically accept the ledger being replayed
222
223 env.close();
224 auto const closed = env.rpc("ledger", "current", "full")[jss::result];
225 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() == 98);
226 BEAST_EXPECT(closed[jss::ledger][jss::accountState].size() <= sd.ledger[jss::ledger][jss::accountState].size());
227 }
228
229 void
231 {
232 testcase("Load and replay transaction by hash failure");
233 using namespace test::jtx;
234
235 // create a new env with the ledger hash specified for startup
236 auto ledgerHash = to_string(sd.hashes[sd.hashes.size() - 1]);
237 boost::erase_all(ledgerHash, "\"");
238 try
239 {
240 // will throw an exception, because we cannot load a ledger for
241 // replay when trapTxHash is set to an invalid transaction
242 Env env(
243 *this,
244 envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, ~sd.trapTxHash),
245 nullptr,
247 BEAST_EXPECT(false);
248 }
249 catch (std::runtime_error const&)
250 {
251 BEAST_EXPECT(true);
252 }
253 catch (...)
254 {
255 BEAST_EXPECT(false);
256 }
257 }
258
259 void
261 {
262 testcase("Load by keyword");
263 using namespace test::jtx;
264
265 // create a new env with the ledger "latest" specified for startup
266 Env env(
267 *this,
268 envconfig(ledgerConfig, sd.dbPath, "latest", Config::LOAD, std::nullopt),
269 nullptr,
271 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
272 BEAST_EXPECT(sd.ledger[jss::ledger][jss::accountState].size() == jrb[jss::ledger][jss::accountState].size());
273 }
274
275 void
277 {
278 testcase("Load by index");
279 using namespace test::jtx;
280
281 // create a new env with specific ledger index at startup
282 Env env(
283 *this,
284 envconfig(ledgerConfig, sd.dbPath, "43", Config::LOAD, std::nullopt),
285 nullptr,
287 auto jrb = env.rpc("ledger", "current", "full")[jss::result];
288 BEAST_EXPECT(sd.ledger[jss::ledger][jss::accountState].size() == jrb[jss::ledger][jss::accountState].size());
289 }
290
291public:
292 void
293 run() override
294 {
296 auto sd = setupLedger(td);
297
298 // test cases
299 testLoad(sd);
300 testBadFiles(sd);
301 testLoadByHash(sd);
302 testReplay(sd);
303 testReplayTx(sd);
305 testLoadLatest(sd);
306 testLoadIndex(sd);
307 }
308};
309
310BEAST_DEFINE_TESTSUITE(LedgerLoad, app, xrpl);
311
312} // 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:147
bool except(F &&f, String const &reason)
Definition suite.h:431
void testLoadLatest(SetupData const &sd)
void testReplay(SetupData const &sd)
void testReplayTxFail(SetupData const &sd)
void run() override
Runs the suite.
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)
static auto ledgerConfig(std::unique_ptr< Config > cfg, std::string const &dbPath, std::string const &ledger, Config::StartUpType type, std::optional< uint256 > trapTxHash)
void testLoad(SetupData const &sd)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:471
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:597
T to_string(T... args)