xrpld
Loading...
Searching...
No Matches
SHAMapStore_test.cpp
1#include <test/jtx/Env.h>
2#include <test/jtx/amount.h>
3#include <test/jtx/envconfig.h>
4
5#include <xrpld/app/main/Application.h>
6#include <xrpld/app/main/NodeStoreScheduler.h>
7#include <xrpld/app/misc/SHAMapStore.h>
8#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
9#include <xrpld/core/Config.h>
10
11#include <xrpl/basics/ByteUtilities.h>
12#include <xrpl/basics/base_uint.h>
13#include <xrpl/beast/unit_test/suite.h>
14#include <xrpl/config/BasicConfig.h>
15#include <xrpl/config/Constants.h>
16#include <xrpl/json/json_value.h>
17#include <xrpl/nodestore/Backend.h>
18#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
19#include <xrpl/protocol/ErrorCodes.h>
20#include <xrpl/protocol/LedgerHeader.h>
21#include <xrpl/protocol/Protocol.h>
22#include <xrpl/protocol/XRPAmount.h>
23#include <xrpl/protocol/jss.h>
24
25#include <boost/filesystem/path.hpp>
26
27#include <atomic>
28#include <cstdint>
29#include <limits>
30#include <map>
31#include <memory>
32#include <optional>
33#include <string>
34#include <utility>
35
36namespace xrpl::test {
37
39{
40 static auto const kDeleteInterval = 8;
41
42 static auto
44 {
45 cfg->ledgerHistory = kDeleteInterval;
46 auto& section = cfg->section(Sections::kNodeDatabase);
48 return cfg;
49 }
50
51 static auto
53 {
54 cfg = onlineDelete(std::move(cfg));
55 cfg->section(Sections::kNodeDatabase).set(Keys::kAdvisoryDelete, "1");
56 return cfg;
57 }
58
59 static bool
60 goodLedger(jtx::Env& env, json::Value const& json, std::string ledgerID, bool checkDB = false)
61 {
62 auto good = json.isMember(jss::result) && !RPC::containsError(json[jss::result]) &&
63 json[jss::result][jss::ledger][jss::ledger_index] == ledgerID;
64 if (!good || !checkDB)
65 return good;
66
67 auto const seq = json[jss::result][jss::ledger_index].asUInt();
68
71 if (!outInfo)
72 return false;
73 LedgerHeader const& info = outInfo.value();
74
75 std::string const outHash = to_string(info.hash);
76 LedgerIndex const outSeq = info.seq;
77 std::string const outParentHash = to_string(info.parentHash);
78 std::string const outDrops = to_string(info.drops);
79 std::uint64_t const outCloseTime = info.closeTime.time_since_epoch().count();
80 std::uint64_t const outParentCloseTime = info.parentCloseTime.time_since_epoch().count();
81 std::uint64_t const outCloseTimeResolution = info.closeTimeResolution.count();
82 std::uint64_t const outCloseFlags = info.closeFlags;
83 std::string const outAccountHash = to_string(info.accountHash);
84 std::string const outTxHash = to_string(info.txHash);
85
86 auto const& ledger = json[jss::result][jss::ledger];
87 return outHash == ledger[jss::ledger_hash].asString() && outSeq == seq &&
88 outParentHash == ledger[jss::parent_hash].asString() &&
89 outDrops == ledger[jss::total_coins].asString() &&
90 outCloseTime == ledger[jss::close_time].asUInt() &&
91 outParentCloseTime == ledger[jss::parent_close_time].asUInt() &&
92 outCloseTimeResolution == ledger[jss::close_time_resolution].asUInt() &&
93 outCloseFlags == ledger[jss::close_flags].asUInt() &&
94 outAccountHash == ledger[jss::account_hash].asString() &&
95 outTxHash == ledger[jss::transaction_hash].asString();
96 }
97
98 static bool
100 {
101 return json.isMember(jss::result) && RPC::containsError(json[jss::result]) &&
102 json[jss::result][jss::error_code] == error;
103 }
104
107 {
108 BEAST_EXPECT(
109 json.isMember(jss::result) && json[jss::result].isMember(jss::ledger) &&
110 json[jss::result][jss::ledger].isMember(jss::ledger_hash) &&
111 json[jss::result][jss::ledger][jss::ledger_hash].isString());
112 return json[jss::result][jss::ledger][jss::ledger_hash].asString();
113 }
114
115 void
116 ledgerCheck(jtx::Env& env, int const rows, int const first)
117 {
118 auto const [actualRows, actualFirst, actualLast] =
120
121 BEAST_EXPECT(actualRows == rows);
122 BEAST_EXPECT(actualFirst == first);
123 BEAST_EXPECT(actualLast == first + rows - 1);
124 }
125
126 void
127 transactionCheck(jtx::Env& env, int const rows)
128 {
129 BEAST_EXPECT(env.app().getRelationalDatabase().getTransactionCount() == rows);
130 }
131
132 void
133 accountTransactionCheck(jtx::Env& env, int const rows)
134 {
135 BEAST_EXPECT(env.app().getRelationalDatabase().getAccountTransactionCount() == rows);
136 }
137
138 int
140 {
141 using namespace std::chrono_literals;
142
143 auto& store = env.app().getSHAMapStore();
144
145 int ledgerSeq = 3;
146 store.rendezvous();
147 BEAST_EXPECT(!store.getLastRotated());
148
149 env.close();
150 store.rendezvous();
151
152 auto ledger = env.rpc("ledger", "validated");
153 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++)));
154
155 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
156 return ledgerSeq;
157 }
158
159public:
160 void
162 {
163 using namespace std::chrono_literals;
164
165 testcase("clearPrior");
166 using namespace jtx;
167
168 Env env(*this, envconfig(onlineDelete));
169
170 auto& store = env.app().getSHAMapStore();
171 env.fund(XRP(10000), noripple("alice"));
172
173 ledgerCheck(env, 1, 2);
174 transactionCheck(env, 0);
176
178
179 auto ledgerTmp = env.rpc("ledger", "0");
180 BEAST_EXPECT(bad(ledgerTmp));
181
182 ledgers.emplace(1, env.rpc("ledger", "1"));
183 BEAST_EXPECT(goodLedger(env, ledgers[1], "1"));
184
185 ledgers.emplace(2, env.rpc("ledger", "2"));
186 BEAST_EXPECT(goodLedger(env, ledgers[2], "2"));
187
188 ledgerTmp = env.rpc("ledger", "current");
189 BEAST_EXPECT(goodLedger(env, ledgerTmp, "3"));
190
191 ledgerTmp = env.rpc("ledger", "4");
192 BEAST_EXPECT(bad(ledgerTmp));
193
194 ledgerTmp = env.rpc("ledger", "100");
195 BEAST_EXPECT(bad(ledgerTmp));
196
197 auto const firstSeq = waitForReady(env);
198 auto lastRotated = firstSeq - 1;
199
200 for (auto i = firstSeq + 1; i < kDeleteInterval + firstSeq; ++i)
201 {
202 env.fund(XRP(10000), noripple("test" + std::to_string(i)));
203 env.close();
204
205 ledgerTmp = env.rpc("ledger", "current");
206 BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i)));
207 }
208 BEAST_EXPECT(store.getLastRotated() == lastRotated);
209
210 for (auto i = 3; i < kDeleteInterval + lastRotated; ++i)
211 {
212 ledgers.emplace(i, env.rpc("ledger", std::to_string(i)));
213 BEAST_EXPECT(
214 goodLedger(env, ledgers[i], std::to_string(i), true) &&
215 !getHash(ledgers[i]).empty());
216 }
217
218 ledgerCheck(env, kDeleteInterval + 1, 2);
221
222 {
223 // Closing one more ledger triggers a rotate
224 env.close();
225
226 auto ledger = env.rpc("ledger", "current");
227 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(kDeleteInterval + 4)));
228 }
229
230 store.rendezvous();
231
232 BEAST_EXPECT(store.getLastRotated() == kDeleteInterval + 3);
233 lastRotated = store.getLastRotated();
234 BEAST_EXPECT(lastRotated == 11);
235
236 // That took care of the fake hashes
237 ledgerCheck(env, kDeleteInterval + 1, 3);
240
241 // The last iteration of this loop should trigger a rotate
242 for (auto i = lastRotated - 1; i < lastRotated + kDeleteInterval - 1; ++i)
243 {
244 env.close();
245
246 ledgerTmp = env.rpc("ledger", "current");
247 BEAST_EXPECT(goodLedger(env, ledgerTmp, std::to_string(i + 3)));
248
249 ledgers.emplace(i, env.rpc("ledger", std::to_string(i)));
250 BEAST_EXPECT(
251 store.getLastRotated() == lastRotated || i == lastRotated + kDeleteInterval - 2);
252 BEAST_EXPECT(
253 goodLedger(env, ledgers[i], std::to_string(i), true) &&
254 !getHash(ledgers[i]).empty());
255 }
256
257 store.rendezvous();
258
259 BEAST_EXPECT(store.getLastRotated() == kDeleteInterval + lastRotated);
260
261 ledgerCheck(env, kDeleteInterval + 1, lastRotated);
262 transactionCheck(env, 0);
264 }
265
266 void
268 {
269 testcase("automatic online_delete");
270 using namespace jtx;
271 using namespace std::chrono_literals;
272
273 Env env(*this, envconfig(onlineDelete));
274 auto& store = env.app().getSHAMapStore();
275
276 auto ledgerSeq = waitForReady(env);
277 auto lastRotated = ledgerSeq - 1;
278 BEAST_EXPECT(store.getLastRotated() == lastRotated);
279 BEAST_EXPECT(lastRotated != 2);
280
281 // Because advisory_delete is unset,
282 // "can_delete" is disabled.
283 auto const canDelete = env.rpc("can_delete");
284 BEAST_EXPECT(bad(canDelete, RpcNotEnabled));
285
286 // Close ledgers without triggering a rotate
287 for (; ledgerSeq < lastRotated + kDeleteInterval; ++ledgerSeq)
288 {
289 env.close();
290
291 auto ledger = env.rpc("ledger", "validated");
292 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
293 }
294
295 store.rendezvous();
296
297 // The database will always have back to ledger 2,
298 // regardless of lastRotated.
299 ledgerCheck(env, ledgerSeq - 2, 2);
300 BEAST_EXPECT(lastRotated == store.getLastRotated());
301
302 {
303 // Closing one more ledger triggers a rotate
304 env.close();
305
306 auto ledger = env.rpc("ledger", "validated");
307 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
308 }
309
310 store.rendezvous();
311
312 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
313 BEAST_EXPECT(lastRotated != store.getLastRotated());
314
315 lastRotated = store.getLastRotated();
316
317 // Close enough ledgers to trigger another rotate
318 for (; ledgerSeq < lastRotated + kDeleteInterval + 1; ++ledgerSeq)
319 {
320 env.close();
321
322 auto ledger = env.rpc("ledger", "validated");
323 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
324 }
325
326 store.rendezvous();
327
328 ledgerCheck(env, kDeleteInterval + 1, lastRotated);
329 BEAST_EXPECT(lastRotated != store.getLastRotated());
330 }
331
332 void
334 {
335 testcase("online_delete with advisory_delete");
336 using namespace jtx;
337 using namespace std::chrono_literals;
338
339 // Same config with advisory_delete enabled
340 Env env(*this, envconfig(advisoryDelete));
341 auto& store = env.app().getSHAMapStore();
342
343 auto ledgerSeq = waitForReady(env);
344 auto lastRotated = ledgerSeq - 1;
345 BEAST_EXPECT(store.getLastRotated() == lastRotated);
346 BEAST_EXPECT(lastRotated != 2);
347
348 auto canDelete = env.rpc("can_delete");
349 BEAST_EXPECT(!RPC::containsError(canDelete[jss::result]));
350 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == 0);
351
352 canDelete = env.rpc("can_delete", "never");
353 BEAST_EXPECT(!RPC::containsError(canDelete[jss::result]));
354 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == 0);
355
356 auto const firstBatch = kDeleteInterval + ledgerSeq;
357 for (; ledgerSeq < firstBatch; ++ledgerSeq)
358 {
359 env.close();
360
361 auto ledger = env.rpc("ledger", "validated");
362 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
363 }
364
365 store.rendezvous();
366
367 ledgerCheck(env, ledgerSeq - 2, 2);
368 BEAST_EXPECT(lastRotated == store.getLastRotated());
369
370 // This does not kick off a cleanup
371 canDelete = env.rpc("can_delete", std::to_string(ledgerSeq + (kDeleteInterval / 2)));
372 BEAST_EXPECT(!RPC::containsError(canDelete[jss::result]));
373 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq + (kDeleteInterval / 2));
374
375 store.rendezvous();
376
377 ledgerCheck(env, ledgerSeq - 2, 2);
378 BEAST_EXPECT(store.getLastRotated() == lastRotated);
379
380 {
381 // This kicks off a cleanup, but it stays small.
382 env.close();
383
384 auto ledger = env.rpc("ledger", "validated");
385 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
386 }
387
388 store.rendezvous();
389
390 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
391
392 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
393 lastRotated = ledgerSeq - 1;
394
395 for (; ledgerSeq < lastRotated + kDeleteInterval; ++ledgerSeq)
396 {
397 // No cleanups in this loop.
398 env.close();
399
400 auto ledger = env.rpc("ledger", "validated");
401 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
402 }
403
404 store.rendezvous();
405
406 BEAST_EXPECT(store.getLastRotated() == lastRotated);
407
408 {
409 // This kicks off another cleanup.
410 env.close();
411
412 auto ledger = env.rpc("ledger", "validated");
413 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
414 }
415
416 store.rendezvous();
417
418 ledgerCheck(env, ledgerSeq - firstBatch, firstBatch);
419
420 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
421 lastRotated = ledgerSeq - 1;
422
423 // This does not kick off a cleanup
424 canDelete = env.rpc("can_delete", "always");
425 BEAST_EXPECT(!RPC::containsError(canDelete[jss::result]));
426 BEAST_EXPECT(
427 canDelete[jss::result][jss::can_delete] == std::numeric_limits<unsigned int>::max());
428
429 for (; ledgerSeq < lastRotated + kDeleteInterval; ++ledgerSeq)
430 {
431 // No cleanups in this loop.
432 env.close();
433
434 auto ledger = env.rpc("ledger", "validated");
435 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
436 }
437
438 store.rendezvous();
439
440 BEAST_EXPECT(store.getLastRotated() == lastRotated);
441
442 {
443 // This kicks off another cleanup.
444 env.close();
445
446 auto ledger = env.rpc("ledger", "validated");
447 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
448 }
449
450 store.rendezvous();
451
452 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
453
454 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
455 lastRotated = ledgerSeq - 1;
456
457 // This does not kick off a cleanup
458 canDelete = env.rpc("can_delete", "now");
459 BEAST_EXPECT(!RPC::containsError(canDelete[jss::result]));
460 BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq - 1);
461
462 for (; ledgerSeq < lastRotated + kDeleteInterval; ++ledgerSeq)
463 {
464 // No cleanups in this loop.
465 env.close();
466
467 auto ledger = env.rpc("ledger", "validated");
468 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq), true));
469 }
470
471 store.rendezvous();
472
473 BEAST_EXPECT(store.getLastRotated() == lastRotated);
474
475 {
476 // This kicks off another cleanup.
477 env.close();
478
479 auto ledger = env.rpc("ledger", "validated");
480 BEAST_EXPECT(goodLedger(env, ledger, std::to_string(ledgerSeq++), true));
481 }
482
483 store.rendezvous();
484
485 ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
486
487 BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
488 lastRotated = ledgerSeq - 1;
489 }
490
493 {
495 boost::filesystem::path newPath;
496
497 if (!BEAST_EXPECT(path.size()))
498 return {};
499 newPath = path;
500 section.set(Keys::kPath, newPath.string());
501
503 section,
504 megabytes(env.app().config().getValueFor(SizedItem::BurstSize, std::nullopt)),
505 scheduler,
506 env.app().getJournal("NodeStoreTest"))};
507 backend->open();
508 return backend;
509 }
510
511 void
513 {
514 // The only purpose of this test is to ensure that if something that
515 // should never happen happens, we don't get a deadlock.
516 testcase("rotate with lock contention");
517
518 using namespace jtx;
519 Env env(*this, envconfig(onlineDelete));
520
522 // Create NodeStore with two backends to allow online deletion of data.
523 // Normally, SHAMapStoreImp handles all these details.
524 auto nscfg = env.app().config().section(Sections::kNodeDatabase);
525
526 // Provide default values.
527 if (!nscfg.exists(Keys::kCacheSize))
528 {
529 nscfg.set(
532 env.app().config().getValueFor(SizedItem::TreeCacheSize, std::nullopt)));
533 }
534
535 if (!nscfg.exists(Keys::kCacheAge))
536 {
537 nscfg.set(
540 env.app().config().getValueFor(SizedItem::TreeCacheAge, std::nullopt)));
541 }
542
543 NodeStoreScheduler scheduler(env.app().getJobQueue());
544
545 std::string const writableDb = "write";
546 std::string const archiveDb = "archive";
547 auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
548 auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
549
550 static constexpr int kReadThreads = 4;
552 scheduler,
553 kReadThreads,
554 std::move(writableBackend),
555 std::move(archiveBackend),
556 nscfg,
557 env.app().getJournal("NodeStoreTest"));
558
560 // Check basic functionality
561 using namespace std::chrono_literals;
562 std::atomic<int> threadNum = 0;
563
564 {
565 auto newBackend = makeBackendRotating(env, scheduler, std::to_string(++threadNum));
566
567 auto const cb = [&](std::string const& writableName, std::string const& archiveName) {
568 BEAST_EXPECT(writableName == "1");
569 BEAST_EXPECT(archiveName == "write");
570 // Ensure that dbr functions can be called from within the
571 // callback
572 BEAST_EXPECT(dbr->getName() == "1");
573 };
574
575 dbr->rotate(std::move(newBackend), cb);
576 }
577 BEAST_EXPECT(threadNum == 1);
578 BEAST_EXPECT(dbr->getName() == "1");
579
581 // Do something stupid. Try to re-enter rotate from inside the callback.
582 {
583 auto const cb = [&](std::string const& writableName, std::string const& archiveName) {
584 BEAST_EXPECT(writableName == "3");
585 BEAST_EXPECT(archiveName == "2");
586 // Ensure that dbr functions can be called from within the
587 // callback
588 BEAST_EXPECT(dbr->getName() == "3");
589 };
590 auto const cbReentrant = [&](std::string const& writableName,
591 std::string const& archiveName) {
592 BEAST_EXPECT(writableName == "2");
593 BEAST_EXPECT(archiveName == "1");
594 auto newBackend = makeBackendRotating(env, scheduler, std::to_string(++threadNum));
595 // Reminder: doing this is stupid and should never happen
596 dbr->rotate(std::move(newBackend), cb);
597 };
598 auto newBackend = makeBackendRotating(env, scheduler, std::to_string(++threadNum));
599 dbr->rotate(std::move(newBackend), cbReentrant);
600 }
601
602 BEAST_EXPECT(threadNum == 3);
603 BEAST_EXPECT(dbr->getName() == "3");
604 }
605
606 void
607 run() override
608 {
609 testClear();
612 testRotate();
613 }
614};
615
616// VFALCO This test fails because of thread asynchronous issues
618
619} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
virtual Config & config()=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:1190
A NodeStore::Scheduler which uses the JobQueue.
virtual std::unique_ptr< Backend > makeBackend(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::size_t getAccountTransactionCount()=0
getAccountTransactionCount Returns the number of account transactions.
virtual std::size_t getTransactionCount()=0
getTransactionCount Returns the number of transactions.
virtual CountMinMax getLedgerCountMinMax()=0
getLedgerCountMinMax Returns the minimum ledger sequence, maximum ledger sequence and total number of...
virtual std::optional< LedgerHeader > 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:19
virtual void rendezvous() const =0
Holds a collection of configuration values.
Definition BasicConfig.h:24
void set(std::string const &key, std::string const &value)
Set a key/value pair.
virtual JobQueue & getJobQueue()=0
virtual RelationalDatabase & getRelationalDatabase()=0
virtual beast::Journal getJournal(std::string const &name)=0
virtual SHAMapStore & getSHAMapStore()=0
static auto onlineDelete(std::unique_ptr< Config > cfg)
static auto advisoryDelete(std::unique_ptr< Config > cfg)
static bool bad(json::Value const &json, ErrorCodeI error=RpcLgrNotFound)
std::unique_ptr< NodeStore::Backend > makeBackendRotating(jtx::Env &env, NodeStoreScheduler &scheduler, std::string path)
void ledgerCheck(jtx::Env &env, int const rows, int const first)
void accountTransactionCheck(jtx::Env &env, int const rows)
void run() override
Runs the suite.
void transactionCheck(jtx::Env &env, int const rows)
std::string getHash(json::Value const &json)
static bool goodLedger(jtx::Env &env, json::Value const &json, std::string ledgerID, bool checkDB=false)
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
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:864
T emplace(T... args)
T make_unique(T... args)
T max(T... args)
JSON (JavaScript Object Notation).
Definition json_errors.h:5
bool containsError(json::Value const &json)
Returns true if the json contains an rpc error specification.
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
Definition Env.h:70
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:28
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
ErrorCodeI
Definition ErrorCodes.h:22
@ RpcLgrNotFound
Definition ErrorCodes.h:54
@ RpcNotEnabled
Definition ErrorCodes.h:41
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:259
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
constexpr auto megabytes(T value) noexcept
static constexpr auto kAdvisoryDelete
Definition Constants.h:88
static constexpr auto kCacheSize
Definition Constants.h:98
static constexpr auto kPath
Definition Constants.h:140
static constexpr auto kCacheAge
Definition Constants.h:96
static constexpr auto kOnlineDelete
Definition Constants.h:133
Information about the notional ledger backing the view.
NetClock::time_point parentCloseTime
NetClock::duration closeTimeResolution
NetClock::time_point closeTime
static constexpr auto kNodeDatabase
Definition Constants.h:31
T time_since_epoch(T... args)
T to_string(T... args)
T value(T... args)