rippled
Loading...
Searching...
No Matches
SHAMapStore_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2015 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/envconfig.h>
22
23#include <xrpld/app/main/Application.h>
24#include <xrpld/app/main/NodeStoreScheduler.h>
25#include <xrpld/app/misc/SHAMapStore.h>
26#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
27#include <xrpld/core/ConfigSections.h>
28#include <xrpld/nodestore/detail/DatabaseRotatingImp.h>
29
30#include <xrpl/protocol/jss.h>
31
32namespace ripple {
33namespace test {
34
36{
37 static auto const deleteInterval = 8;
38
39 static auto
41 {
42 cfg->LEDGER_HISTORY = deleteInterval;
43 auto& section = cfg->section(ConfigSection::nodeDatabase());
44 section.set("online_delete", std::to_string(deleteInterval));
45 return cfg;
46 }
47
48 static auto
50 {
51 cfg = onlineDelete(std::move(cfg));
52 cfg->section(ConfigSection::nodeDatabase()).set("advisory_delete", "1");
53 return cfg;
54 }
55
56 bool
58 jtx::Env& env,
59 Json::Value const& json,
60 std::string ledgerID,
61 bool checkDB = false)
62 {
63 auto good = json.isMember(jss::result) &&
64 !RPC::contains_error(json[jss::result]) &&
65 json[jss::result][jss::ledger][jss::ledger_index] == ledgerID;
66 if (!good || !checkDB)
67 return good;
68
69 auto const seq = json[jss::result][jss::ledger_index].asUInt();
70
73 if (!oinfo)
74 return false;
75 LedgerInfo const& info = oinfo.value();
76
77 std::string const outHash = to_string(info.hash);
78 LedgerIndex const outSeq = info.seq;
79 std::string const outParentHash = to_string(info.parentHash);
80 std::string const outDrops = to_string(info.drops);
81 std::uint64_t const outCloseTime =
82 info.closeTime.time_since_epoch().count();
83 std::uint64_t const outParentCloseTime =
84 info.parentCloseTime.time_since_epoch().count();
85 std::uint64_t const outCloseTimeResolution =
87 std::uint64_t const outCloseFlags = info.closeFlags;
88 std::string const outAccountHash = to_string(info.accountHash);
89 std::string const outTxHash = to_string(info.txHash);
90
91 auto const& ledger = json[jss::result][jss::ledger];
92 return outHash == ledger[jss::ledger_hash].asString() &&
93 outSeq == seq &&
94 outParentHash == ledger[jss::parent_hash].asString() &&
95 outDrops == ledger[jss::total_coins].asString() &&
96 outCloseTime == ledger[jss::close_time].asUInt() &&
97 outParentCloseTime == ledger[jss::parent_close_time].asUInt() &&
98 outCloseTimeResolution ==
99 ledger[jss::close_time_resolution].asUInt() &&
100 outCloseFlags == ledger[jss::close_flags].asUInt() &&
101 outAccountHash == ledger[jss::account_hash].asString() &&
102 outTxHash == ledger[jss::transaction_hash].asString();
103 }
104
105 bool
107 {
108 return json.isMember(jss::result) &&
109 RPC::contains_error(json[jss::result]) &&
110 json[jss::result][jss::error_code] == error;
111 }
112
115 {
116 BEAST_EXPECT(
117 json.isMember(jss::result) &&
118 json[jss::result].isMember(jss::ledger) &&
119 json[jss::result][jss::ledger].isMember(jss::ledger_hash) &&
120 json[jss::result][jss::ledger][jss::ledger_hash].isString());
121 return json[jss::result][jss::ledger][jss::ledger_hash].asString();
122 }
123
124 void
125 ledgerCheck(jtx::Env& env, int const rows, int const first)
126 {
127 auto const [actualRows, actualFirst, actualLast] =
128 dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase())
129 ->getLedgerCountMinMax();
130
131 BEAST_EXPECT(actualRows == rows);
132 BEAST_EXPECT(actualFirst == first);
133 BEAST_EXPECT(actualLast == first + rows - 1);
134 }
135
136 void
137 transactionCheck(jtx::Env& env, int const rows)
138 {
139 BEAST_EXPECT(
140 dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase())
141 ->getTransactionCount() == rows);
142 }
143
144 void
145 accountTransactionCheck(jtx::Env& env, int const rows)
146 {
147 BEAST_EXPECT(
148 dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase())
149 ->getAccountTransactionCount() == rows);
150 }
151
152 int
154 {
155 using namespace std::chrono_literals;
156
157 auto& store = env.app().getSHAMapStore();
158
159 int ledgerSeq = 3;
160 store.rendezvous();
161 BEAST_EXPECT(!store.getLastRotated());
162
163 env.close();
164 store.rendezvous();
165
166 auto ledger = env.rpc("ledger", "validated");
167 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++)));
168
169 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
170 return ledgerSeq;
171 }
172
173public:
174 void
176 {
177 using namespace std::chrono_literals;
178
179 testcase("clearPrior");
180 using namespace jtx;
181
182 Env env(*this, envconfig(onlineDelete));
183
184 auto& store = env.app().getSHAMapStore();
185 env.fund(XRP(10000), noripple("alice"));
186
187 ledgerCheck(env, 1, 2);
188 transactionCheck(env, 0);
190
192
193 auto ledgerTmp = env.rpc("ledger", "0");
194 BEAST_EXPECT(bad(ledgerTmp));
195
196 ledgers.emplace(std::make_pair(1, env.rpc("ledger", "1")));
197 BEAST_EXPECT(goodLedger(env, ledgers[1], "1"));
198
199 ledgers.emplace(std::make_pair(2, env.rpc("ledger", "2")));
200 BEAST_EXPECT(goodLedger(env, ledgers[2], "2"));
201
202 ledgerTmp = env.rpc("ledger", "current");
203 BEAST_EXPECT(goodLedger(env, ledgerTmp, "3"));
204
205 ledgerTmp = env.rpc("ledger", "4");
206 BEAST_EXPECT(bad(ledgerTmp));
207
208 ledgerTmp = env.rpc("ledger", "100");
209 BEAST_EXPECT(bad(ledgerTmp));
210
211 auto const firstSeq = waitForReady(env);
212 auto lastRotated = firstSeq - 1;
213
214 for (auto i = firstSeq + 1; i < deleteInterval + firstSeq; ++i)
215 {
216 env.fund(XRP(10000), noripple("test" + std::to_string(i)));
217 env.close();
218
219 ledgerTmp = env.rpc("ledger", "current");
220 BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i)));
221 }
222 BEAST_EXPECT(store.getLastRotated() == lastRotated);
223
224 for (auto i = 3; i < deleteInterval + lastRotated; ++i)
225 {
226 ledgers.emplace(
227 std::make_pair(i, env.rpc("ledger", std::to_string(i))));
228 BEAST_EXPECT(
229 goodLedger(env, ledgers[i], std::to_string(i), true) &&
230 getHash(ledgers[i]).length());
231 }
232
233 ledgerCheck(env, deleteInterval + 1, 2);
236
237 {
238 // Closing one more ledger triggers a rotate
239 env.close();
240
241 auto ledger = env.rpc("ledger", "current");
242 BEAST_EXPECT(
243 goodLedger(env, ledger, std::to_string(deleteInterval + 4)));
244 }
245
246 store.rendezvous();
247
248 BEAST_EXPECT(store.getLastRotated() == deleteInterval + 3);
249 lastRotated = store.getLastRotated();
250 BEAST_EXPECT(lastRotated == 11);
251
252 // That took care of the fake hashes
253 ledgerCheck(env, deleteInterval + 1, 3);
256
257 // The last iteration of this loop should trigger a rotate
258 for (auto i = lastRotated - 1; i < lastRotated + deleteInterval - 1;
259 ++i)
260 {
261 env.close();
262
263 ledgerTmp = env.rpc("ledger", "current");
264 BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i + 3)));
265
266 ledgers.emplace(
267 std::make_pair(i, env.rpc("ledger", std::to_string(i))));
268 BEAST_EXPECT(
269 store.getLastRotated() == lastRotated ||
270 i == lastRotated + deleteInterval - 2);
271 BEAST_EXPECT(
272 goodLedger(env, ledgers[i], std::to_string(i), true) &&
273 getHash(ledgers[i]).length());
274 }
275
276 store.rendezvous();
277
278 BEAST_EXPECT(store.getLastRotated() == deleteInterval + lastRotated);
279
280 ledgerCheck(env, deleteInterval + 1, lastRotated);
281 transactionCheck(env, 0);
283 }
284
285 void
287 {
288 testcase("automatic online_delete");
289 using namespace jtx;
290 using namespace std::chrono_literals;
291
292 Env env(*this, envconfig(onlineDelete));
293 auto& store = env.app().getSHAMapStore();
294
295 auto ledgerSeq = waitForReady(env);
296 auto lastRotated = ledgerSeq - 1;
297 BEAST_EXPECT(store.getLastRotated() == lastRotated);
298 BEAST_EXPECT(lastRotated != 2);
299
300 // Because advisory_delete is unset,
301 // "can_delete" is disabled.
302 auto const canDelete = env.rpc("can_delete");
303 BEAST_EXPECT(bad(canDelete, rpcNOT_ENABLED));
304
305 // Close ledgers without triggering a rotate
306 for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
307 {
308 env.close();
309
310 auto ledger = env.rpc("ledger", "validated");
311 BEAST_EXPECT(
312 goodLedger(env, ledger, std::to_string(ledgerSeq), true));
313 }
314
315 store.rendezvous();
316
317 // The database will always have back to ledger 2,
318 // regardless of lastRotated.
319 ledgerCheck(env, ledgerSeq - 2, 2);
320 BEAST_EXPECT(lastRotated == store.getLastRotated());
321
322 {
323 // Closing one more ledger triggers a rotate
324 env.close();
325
326 auto ledger = env.rpc("ledger", "validated");
327 BEAST_EXPECT(
328 goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
329 }
330
331 store.rendezvous();
332
333 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
334 BEAST_EXPECT(lastRotated != store.getLastRotated());
335
336 lastRotated = store.getLastRotated();
337
338 // Close enough ledgers to trigger another rotate
339 for (; ledgerSeq < lastRotated + deleteInterval + 1; ++ledgerSeq)
340 {
341 env.close();
342
343 auto ledger = env.rpc("ledger", "validated");
344 BEAST_EXPECT(
345 goodLedger(env, ledger, std::to_string(ledgerSeq), true));
346 }
347
348 store.rendezvous();
349
350 ledgerCheck(env, deleteInterval + 1, lastRotated);
351 BEAST_EXPECT(lastRotated != store.getLastRotated());
352 }
353
354 void
356 {
357 testcase("online_delete with advisory_delete");
358 using namespace jtx;
359 using namespace std::chrono_literals;
360
361 // Same config with advisory_delete enabled
362 Env env(*this, envconfig(advisoryDelete));
363 auto& store = env.app().getSHAMapStore();
364
365 auto ledgerSeq = waitForReady(env);
366 auto lastRotated = ledgerSeq - 1;
367 BEAST_EXPECT(store.getLastRotated() == lastRotated);
368 BEAST_EXPECT(lastRotated != 2);
369
370 auto canDelete = env.rpc("can_delete");
371 BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
372 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == 0);
373
374 canDelete = env.rpc("can_delete", "never");
375 BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
376 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == 0);
377
378 auto const firstBatch = deleteInterval + ledgerSeq;
379 for (; ledgerSeq < firstBatch; ++ledgerSeq)
380 {
381 env.close();
382
383 auto ledger = env.rpc("ledger", "validated");
384 BEAST_EXPECT(
385 goodLedger(env, ledger, std::to_string(ledgerSeq), true));
386 }
387
388 store.rendezvous();
389
390 ledgerCheck(env, ledgerSeq - 2, 2);
391 BEAST_EXPECT(lastRotated == store.getLastRotated());
392
393 // This does not kick off a cleanup
394 canDelete = env.rpc(
395 "can_delete", std::to_string(ledgerSeq + deleteInterval / 2));
396 BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
397 BEAST_EXPECT(
398 canDelete[jss::result][jss::can_delete] ==
399 ledgerSeq + deleteInterval / 2);
400
401 store.rendezvous();
402
403 ledgerCheck(env, ledgerSeq - 2, 2);
404 BEAST_EXPECT(store.getLastRotated() == lastRotated);
405
406 {
407 // This kicks off a cleanup, but it stays small.
408 env.close();
409
410 auto ledger = env.rpc("ledger", "validated");
411 BEAST_EXPECT(
412 goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
413 }
414
415 store.rendezvous();
416
417 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
418
419 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
420 lastRotated = ledgerSeq - 1;
421
422 for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
423 {
424 // No cleanups in this loop.
425 env.close();
426
427 auto ledger = env.rpc("ledger", "validated");
428 BEAST_EXPECT(
429 goodLedger(env, ledger, std::to_string(ledgerSeq), true));
430 }
431
432 store.rendezvous();
433
434 BEAST_EXPECT(store.getLastRotated() == lastRotated);
435
436 {
437 // This kicks off another cleanup.
438 env.close();
439
440 auto ledger = env.rpc("ledger", "validated");
441 BEAST_EXPECT(
442 goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
443 }
444
445 store.rendezvous();
446
447 ledgerCheck(env, ledgerSeq - firstBatch, firstBatch);
448
449 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
450 lastRotated = ledgerSeq - 1;
451
452 // This does not kick off a cleanup
453 canDelete = env.rpc("can_delete", "always");
454 BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
455 BEAST_EXPECT(
456 canDelete[jss::result][jss::can_delete] ==
458
459 for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
460 {
461 // No cleanups in this loop.
462 env.close();
463
464 auto ledger = env.rpc("ledger", "validated");
465 BEAST_EXPECT(
466 goodLedger(env, ledger, std::to_string(ledgerSeq), true));
467 }
468
469 store.rendezvous();
470
471 BEAST_EXPECT(store.getLastRotated() == lastRotated);
472
473 {
474 // This kicks off another cleanup.
475 env.close();
476
477 auto ledger = env.rpc("ledger", "validated");
478 BEAST_EXPECT(
479 goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
480 }
481
482 store.rendezvous();
483
484 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
485
486 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
487 lastRotated = ledgerSeq - 1;
488
489 // This does not kick off a cleanup
490 canDelete = env.rpc("can_delete", "now");
491 BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
492 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq - 1);
493
494 for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
495 {
496 // No cleanups in this loop.
497 env.close();
498
499 auto ledger = env.rpc("ledger", "validated");
500 BEAST_EXPECT(
501 goodLedger(env, ledger, std::to_string(ledgerSeq), true));
502 }
503
504 store.rendezvous();
505
506 BEAST_EXPECT(store.getLastRotated() == lastRotated);
507
508 {
509 // This kicks off another cleanup.
510 env.close();
511
512 auto ledger = env.rpc("ledger", "validated");
513 BEAST_EXPECT(
514 goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
515 }
516
517 store.rendezvous();
518
519 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
520
521 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
522 lastRotated = ledgerSeq - 1;
523 }
524
527 jtx::Env& env,
528 NodeStoreScheduler& scheduler,
530 {
531 Section section{
533 boost::filesystem::path newPath;
534
535 if (!BEAST_EXPECT(path.size()))
536 return {};
537 newPath = path;
538 section.set("path", newPath.string());
539
541 section,
544 scheduler,
545 env.app().logs().journal("NodeStoreTest"))};
546 backend->open();
547 return backend;
548 }
549
550 void
552 {
553 // The only purpose of this test is to ensure that if something that
554 // should never happen happens, we don't get a deadlock.
555 testcase("rotate with lock contention");
556
557 using namespace jtx;
558 Env env(*this, envconfig(onlineDelete));
559
561 // Create the backend. Normally, SHAMapStoreImp handles all these
562 // details
563 auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
564
565 // Provide default values:
566 if (!nscfg.exists("cache_size"))
567 nscfg.set(
568 "cache_size",
571
572 if (!nscfg.exists("cache_age"))
573 nscfg.set(
574 "cache_age",
577
578 NodeStoreScheduler scheduler(env.app().getJobQueue());
579
580 std::string const writableDb = "write";
581 std::string const archiveDb = "archive";
582 auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
583 auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
584
585 // Create NodeStore with two backends to allow online deletion of
586 // data
587 constexpr int readThreads = 4;
589 scheduler,
590 readThreads,
591 std::move(writableBackend),
592 std::move(archiveBackend),
593 nscfg,
594 env.app().logs().journal("NodeStoreTest"));
595
597 // Check basic functionality
598 using namespace std::chrono_literals;
599 std::atomic<int> threadNum = 0;
600
601 {
602 auto newBackend = makeBackendRotating(
603 env, scheduler, std::to_string(++threadNum));
604
605 auto const cb = [&](std::string const& writableName,
606 std::string const& archiveName) {
607 BEAST_EXPECT(writableName == "1");
608 BEAST_EXPECT(archiveName == "write");
609 // Ensure that dbr functions can be called from within the
610 // callback
611 BEAST_EXPECT(dbr->getName() == "1");
612 };
613
614 dbr->rotate(std::move(newBackend), cb);
615 }
616 BEAST_EXPECT(threadNum == 1);
617 BEAST_EXPECT(dbr->getName() == "1");
618
620 // Do something stupid. Try to re-enter rotate from inside the callback.
621 {
622 auto const cb = [&](std::string const& writableName,
623 std::string const& archiveName) {
624 BEAST_EXPECT(writableName == "3");
625 BEAST_EXPECT(archiveName == "2");
626 // Ensure that dbr functions can be called from within the
627 // callback
628 BEAST_EXPECT(dbr->getName() == "3");
629 };
630 auto const cbReentrant = [&](std::string const& writableName,
631 std::string const& archiveName) {
632 BEAST_EXPECT(writableName == "2");
633 BEAST_EXPECT(archiveName == "1");
634 auto newBackend = makeBackendRotating(
635 env, scheduler, std::to_string(++threadNum));
636 // Reminder: doing this is stupid and should never happen
637 dbr->rotate(std::move(newBackend), cb);
638 };
639 auto newBackend = makeBackendRotating(
640 env, scheduler, std::to_string(++threadNum));
641 dbr->rotate(std::move(newBackend), cbReentrant);
642 }
643
644 BEAST_EXPECT(threadNum == 3);
645 BEAST_EXPECT(dbr->getName() == "3");
646 }
647
648 void
649 run() override
650 {
651 testClear();
654 testRotate();
655 }
656};
657
658// VFALCO This test fails because of thread asynchronous issues
659BEAST_DEFINE_TESTSUITE(SHAMapStore, app, ripple);
660
661} // namespace test
662} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
A testsuite class.
Definition suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
virtual Config & config()=0
virtual SHAMapStore & getSHAMapStore()=0
virtual JobQueue & getJobQueue()=0
virtual RelationalDatabase & getRelationalDatabase()=0
virtual Logs & logs()=0
Section & section(std::string const &name)
Returns the section with the given name.
int getValueFor(SizedItem item, std::optional< std::size_t > node=std::nullopt) const
Retrieve the default value for the item at the specified node size.
Definition Config.cpp:1116
beast::Journal journal(std::string const &name)
Definition Log.cpp:160
A NodeStore::Scheduler which uses the JobQueue.
virtual std::unique_ptr< Backend > make_Backend(Section const &parameters, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal)=0
Create a backend.
static Manager & instance()
Returns the instance of the manager singleton.
virtual std::optional< LedgerInfo > getLedgerInfoByIndex(LedgerIndex ledgerSeq)=0
getLedgerInfoByIndex Returns a ledger by its sequence.
class to create database, launch online delete thread, and related SQLite database
Definition SHAMapStore.h:37
virtual void rendezvous() const =0
virtual std::size_t getAccountTransactionCount()=0
getAccountTransactionCount Returns the number of account transactions.
virtual std::size_t getTransactionCount()=0
getTransactionCount Returns the number of transactions.
Holds a collection of configuration values.
Definition BasicConfig.h:45
void set(std::string const &key, std::string const &value)
Set a key/value pair.
void ledgerCheck(jtx::Env &env, int const rows, int const first)
std::string getHash(Json::Value const &json)
static auto advisoryDelete(std::unique_ptr< Config > cfg)
bool bad(Json::Value const &json, error_code_i error=rpcLGR_NOT_FOUND)
void run() override
Runs the suite.
void accountTransactionCheck(jtx::Env &env, int const rows)
static auto onlineDelete(std::unique_ptr< Config > cfg)
std::unique_ptr< NodeStore::Backend > makeBackendRotating(jtx::Env &env, NodeStoreScheduler &scheduler, std::string path)
void transactionCheck(jtx::Env &env, int const rows)
bool goodLedger(jtx::Env &env, Json::Value const &json, std::string ledgerID, bool checkDB=false)
A transaction testing environment.
Definition Env.h:121
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:121
Application & app()
Definition Env.h:261
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:791
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:289
Inject raw JSON.
Definition jtx_json.h:33
Add a path.
Definition paths.h:58
T emplace(T... args)
T is_same_v
T make_pair(T... args)
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:54
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:111
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr auto megabytes(T value) noexcept
@ rpcLGR_NOT_FOUND
Definition ErrorCodes.h:72
@ rpcNOT_ENABLED
Definition ErrorCodes.h:59
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
static std::string nodeDatabase()
Information about the notional ledger backing the view.
NetClock::time_point closeTime
NetClock::duration closeTimeResolution
NetClock::time_point parentCloseTime
Set the sequence number on a JTx.
Definition seq.h:34
T time_since_epoch(T... args)
T to_string(T... args)
T value(T... args)