xrpld
Loading...
Searching...
No Matches
Directory_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/credentials.h>
7#include <test/jtx/directory.h>
8#include <test/jtx/multisign.h>
9#include <test/jtx/noop.h>
10#include <test/jtx/offer.h>
11#include <test/jtx/owners.h> // IWYU pragma: keep
12#include <test/jtx/pay.h>
13#include <test/jtx/tags.h>
14#include <test/jtx/ter.h>
15#include <test/jtx/trust.h>
16
17#include <xrpl/basics/base_uint.h>
18#include <xrpl/basics/random.h>
19#include <xrpl/beast/unit_test/suite.h>
20#include <xrpl/core/ServiceRegistry.h>
21#include <xrpl/json/json_value.h>
22#include <xrpl/json/to_string.h>
23#include <xrpl/ledger/ApplyView.h>
24#include <xrpl/ledger/BookDirs.h>
25#include <xrpl/ledger/Sandbox.h>
26#include <xrpl/ledger/helpers/DirectoryHelpers.h>
27#include <xrpl/protocol/Book.h>
28#include <xrpl/protocol/Feature.h>
29#include <xrpl/protocol/Indexes.h>
30#include <xrpl/protocol/Issue.h>
31#include <xrpl/protocol/LedgerFormats.h>
32#include <xrpl/protocol/Protocol.h>
33#include <xrpl/protocol/SField.h>
34#include <xrpl/protocol/TER.h>
35#include <xrpl/protocol/jss.h>
36
37#include <algorithm>
38#include <cstddef>
39#include <cstdint>
40#include <limits>
41#include <memory>
42#include <optional>
43#include <string>
44#include <tuple>
45#include <vector>
46
47namespace xrpl::test {
48
50{
51 // Map [0-15576] into a unique 3 letter currency code
54 {
55 // There are only 17576 possible combinations
56 BEAST_EXPECT(i < 17577);
57
58 std::string code;
59
60 for (int j = 0; j != 3; ++j)
61 {
62 code.push_back('A' + (i % 26));
63 i /= 26;
64 }
65
66 return code;
67 }
68
69 // Insert n empty pages, numbered [0, ... n - 1], in the
70 // specified directory:
71 static void
73 {
74 for (std::uint64_t i = 0; i < n; ++i)
75 {
76 auto p = std::make_shared<SLE>(keylet::page(base, i));
77
78 p->setFieldV256(sfIndexes, STVector256{});
79
80 if (i + 1 == n)
81 {
82 p->setFieldU64(sfIndexNext, 0);
83 }
84 else
85 {
86 p->setFieldU64(sfIndexNext, i + 1);
87 }
88
89 if (i == 0)
90 {
91 p->setFieldU64(sfIndexPrevious, n - 1);
92 }
93 else
94 {
95 p->setFieldU64(sfIndexPrevious, i - 1);
96 }
97
98 sb.insert(p);
99 }
100 }
101
102 void
104 {
105 using namespace jtx;
106
107 auto gw = Account("gw");
108 auto usd = gw["USD"];
109 auto alice = Account("alice");
110 auto bob = Account("bob");
111
112 testcase("Directory Ordering (with 'SortedDirectories' amendment)");
113
114 Env env(*this);
115 env.fund(XRP(10000000), alice, gw);
116
117 std::uint32_t const firstOfferSeq{env.seq(alice)};
118 for (std::size_t i = 1; i <= 400; ++i)
119 env(offer(alice, usd(i), XRP(i)));
120 env.close();
121
122 // Check Alice's directory: it should contain one
123 // entry for each offer she added, and, within each
124 // page the entries should be in sorted order.
125 {
126 auto const view = env.closed();
127
128 std::uint64_t page = 0;
129
130 do
131 {
132 auto p = view->read(keylet::page(keylet::ownerDir(alice), page));
133
134 // Ensure that the entries in the page are sorted
135 auto const& v = p->getFieldV256(sfIndexes);
136 BEAST_EXPECT(std::ranges::is_sorted(v));
137
138 // Ensure that the page contains the correct orders by
139 // calculating which sequence numbers belong here.
140 std::uint32_t const minSeq = firstOfferSeq + (page * kDirNodeMaxEntries);
141 std::uint32_t const maxSeq = minSeq + kDirNodeMaxEntries;
142
143 for (auto const& e : v)
144 {
145 auto c = view->read(keylet::child(e));
146 BEAST_EXPECT(c);
147 BEAST_EXPECT(c->getFieldU32(sfSequence) >= minSeq);
148 BEAST_EXPECT(c->getFieldU32(sfSequence) < maxSeq);
149 }
150
151 page = p->getFieldU64(sfIndexNext);
152 } while (page != 0);
153 }
154
155 // Now check the orderbook: it should be in the order we placed
156 // the offers.
157 auto book = BookDirs(*env.current(), Book({xrpIssue(), usd, std::nullopt}));
158 int count = 1;
159
160 for (auto const& offer : book)
161 {
162 count++;
163 BEAST_EXPECT(offer->getFieldAmount(sfTakerPays) == usd(count));
164 BEAST_EXPECT(offer->getFieldAmount(sfTakerGets) == XRP(count));
165 }
166 }
167
168 void
170 {
171 testcase("dirIsEmpty");
172
173 using namespace jtx;
174 auto const alice = Account("alice");
175 auto const bob = Account("bob");
176 auto const charlie = Account("charlie");
177 auto const gw = Account("gw");
178
179 Env env(*this);
180
181 env.fund(XRP(1000000), alice, charlie, gw);
182 env.close();
183
184 // alice should have an empty directory.
185 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
186
187 // Give alice a signer list, then there will be stuff in the directory.
188 env(signers(alice, 1, {{bob, 1}}));
189 env.close();
190 BEAST_EXPECT(!dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
191
192 env(signers(alice, jtx::kNone));
193 env.close();
194 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
195
196 std::vector<IOU> const currencies = [this, &gw]() {
198
199 c.reserve((2 * kDirNodeMaxEntries) + 3);
200
201 while (c.size() != c.capacity())
202 c.push_back(gw[currcode(c.size())]);
203
204 return c;
205 }();
206
207 // First, Alice creates a lot of trustlines, and then
208 // deletes them in a different order:
209 {
210 auto cl = currencies;
211
212 for (auto const& c : cl)
213 {
214 env(trust(alice, c(50)));
215 env.close();
216 }
217
218 BEAST_EXPECT(!dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
219
220 std::shuffle(cl.begin(), cl.end(), defaultPrng());
221
222 for (auto const& c : cl)
223 {
224 env(trust(alice, c(0)));
225 env.close();
226 }
227
228 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
229 }
230
231 // Now, Alice creates offers to buy currency, creating
232 // implicit trust lines.
233 {
234 auto cl = currencies;
235
236 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
237
238 for (auto const& c : currencies)
239 {
240 env(trust(charlie, c(50)));
241 env.close();
242 env(pay(gw, charlie, c(50)));
243 env.close();
244 env(offer(alice, c(50), XRP(50)));
245 env.close();
246 }
247
248 BEAST_EXPECT(!dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
249
250 // Now fill the offers in a random order. Offer
251 // entries will drop, and be replaced by trust
252 // lines that are implicitly created.
253 std::shuffle(cl.begin(), cl.end(), defaultPrng());
254
255 for (auto const& c : cl)
256 {
257 env(offer(charlie, XRP(50), c(50)));
258 env.close();
259 }
260 BEAST_EXPECT(!dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
261 // Finally, Alice now sends the funds back to
262 // Charlie. The implicitly created trust lines
263 // should drop away:
264 std::shuffle(cl.begin(), cl.end(), defaultPrng());
265
266 for (auto const& c : cl)
267 {
268 env(pay(alice, charlie, c(50)));
269 env.close();
270 }
271
272 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
273 }
274 }
275
276 void
278 {
279 testcase("RIPD-1353 Empty Offer Directories");
280
281 using namespace jtx;
282 Env env(*this);
283
284 auto const gw = Account{"gateway"};
285 auto const alice = Account{"alice"};
286 auto const usd = gw["USD"];
287
288 env.fund(XRP(10000), alice, gw);
289 env.close();
290 env.trust(usd(1000), alice);
291 env(pay(gw, alice, usd(1000)));
292
293 auto const firstOfferSeq = env.seq(alice);
294
295 // Fill up three pages of offers
296 for (int i = 0; i < 3; ++i)
297 {
298 for (int j = 0; j < kDirNodeMaxEntries; ++j)
299 env(offer(alice, XRP(1), usd(1)));
300 }
301 env.close();
302
303 // remove all the offers. Remove the middle page last
304 for (auto page : {0, 2, 1})
305 {
306 for (int i = 0; i < kDirNodeMaxEntries; ++i)
307 {
308 env(offerCancel(alice, firstOfferSeq + (page * kDirNodeMaxEntries) + i));
309 env.close();
310 }
311 }
312
313 // All the offers have been cancelled, so the book
314 // should have no entries and be empty:
315 {
316 Sandbox const sb(env.closed().get(), TapNone);
317 uint256 const bookBase = getBookBase({xrpIssue(), usd, std::nullopt});
318
319 BEAST_EXPECT(dirIsEmpty(sb, keylet::page(bookBase)));
320 BEAST_EXPECT(!sb.succ(bookBase, getQualityNext(bookBase)));
321 }
322
323 // Alice returns the USD she has to the gateway
324 // and removes her trust line. Her owner directory
325 // should now be empty:
326 {
327 env.trust(usd(0), alice);
328 env(pay(alice, gw, alice["USD"](1000)));
329 env.close();
330 BEAST_EXPECT(dirIsEmpty(*env.closed(), keylet::ownerDir(alice)));
331 }
332 }
333
334 void
336 {
337 testcase("Empty Chain on Delete");
338
339 using namespace jtx;
340 Env env(*this);
341
342 auto const gw = Account{"gateway"};
343 auto const alice = Account{"alice"};
344 auto const usd = gw["USD"];
345
346 env.fund(XRP(10000), alice);
347 env.close();
348
349 constexpr uint256 kBase("fb71c9aa3310141da4b01d6c744a98286af2d72ab5448d5adc0910ca0c910880");
350
351 constexpr uint256 kItem("bad0f021aa3b2f6754a8fe82a5779730aa0bbbab82f17201ef24900efc2c7312");
352
353 {
354 // Create a chain of three pages:
355 Sandbox sb(env.closed().get(), TapNone);
356 makePages(sb, kBase, 3);
357
358 // Insert an item in the middle page:
359 {
360 auto p = sb.peek(keylet::page(kBase, 1));
361 BEAST_EXPECT(p);
362
363 STVector256 v;
364 v.pushBack(kItem);
365 p->setFieldV256(sfIndexes, v);
366 sb.update(p);
367 }
368
369 // Now, try to delete the item from the middle
370 // page. This should cause all pages to be deleted:
371 BEAST_EXPECT(sb.dirRemove(keylet::page(kBase, 0), 1, keylet::unchecked(kItem), false));
372 BEAST_EXPECT(!sb.peek(keylet::page(kBase, 2)));
373 BEAST_EXPECT(!sb.peek(keylet::page(kBase, 1)));
374 BEAST_EXPECT(!sb.peek(keylet::page(kBase, 0)));
375 }
376
377 {
378 // Create a chain of four pages:
379 Sandbox sb(env.closed().get(), TapNone);
380 makePages(sb, kBase, 4);
381
382 // Now add items on pages 1 and 2:
383 {
384 auto p1 = sb.peek(keylet::page(kBase, 1));
385 BEAST_EXPECT(p1);
386
387 STVector256 v1;
388 v1.pushBack(~kItem);
389 p1->setFieldV256(sfIndexes, v1);
390 sb.update(p1);
391
392 auto p2 = sb.peek(keylet::page(kBase, 2));
393 BEAST_EXPECT(p2);
394
395 STVector256 v2;
396 v2.pushBack(kItem);
397 p2->setFieldV256(sfIndexes, v2);
398 sb.update(p2);
399 }
400
401 // Now, try to delete the item from page 2.
402 // This should cause pages 2 and 3 to be
403 // deleted:
404 BEAST_EXPECT(sb.dirRemove(keylet::page(kBase, 0), 2, keylet::unchecked(kItem), false));
405 BEAST_EXPECT(!sb.peek(keylet::page(kBase, 3)));
406 BEAST_EXPECT(!sb.peek(keylet::page(kBase, 2)));
407
408 auto p1 = sb.peek(keylet::page(kBase, 1));
409 BEAST_EXPECT(p1);
410 BEAST_EXPECT(p1->getFieldU64(sfIndexNext) == 0);
411 BEAST_EXPECT(p1->getFieldU64(sfIndexPrevious) == 0);
412
413 auto p0 = sb.peek(keylet::page(kBase, 0));
414 BEAST_EXPECT(p0);
415 BEAST_EXPECT(p0->getFieldU64(sfIndexNext) == 1);
416 BEAST_EXPECT(p0->getFieldU64(sfIndexPrevious) == 1);
417 }
418 }
419
420 void
422 {
423 testcase("fixPreviousTxnID");
424 using namespace jtx;
425
426 auto const gw = Account{"gateway"};
427 auto const alice = Account{"alice"};
428 auto const usd = gw["USD"];
429
430 auto ledgerData = [this](Env& env) {
431 json::Value params;
432 params[jss::type] = jss::directory;
433 params[jss::ledger_index] = "validated";
434 auto const result = env.rpc("json", "ledger_data", to_string(params))[jss::result];
435 BEAST_EXPECT(!result.isMember(jss::marker));
436 return result;
437 };
438
439 // fixPreviousTxnID is disabled.
440 Env env(*this, testableAmendments() - fixPreviousTxnID);
441 env.fund(XRP(10000), alice, gw);
442 env.close();
443 env.trust(usd(1000), alice);
444 env(pay(gw, alice, usd(1000)));
445 env.close();
446
447 {
448 auto const jrr = ledgerData(env);
449 auto const& jstate = jrr[jss::state];
450 BEAST_EXPECTS(checkArraySize(jstate, 2), jrr.toStyledString());
451 for (auto const& directory : jstate)
452 {
453 BEAST_EXPECT(directory["LedgerEntryType"] == jss::DirectoryNode); // sanity check
454 // The PreviousTxnID and PreviousTxnLgrSeq fields should not be
455 // on the DirectoryNode object when the amendment is disabled
456 BEAST_EXPECT(!directory.isMember("PreviousTxnID"));
457 BEAST_EXPECT(!directory.isMember("PreviousTxnLgrSeq"));
458 }
459 }
460
461 // Now enable the amendment so the directory node is updated.
462 env.enableFeature(fixPreviousTxnID);
463 env.close();
464
465 // Make sure the `PreviousTxnID` and `PreviousTxnLgrSeq` fields now
466 // exist
467 env(offer(alice, XRP(1), usd(1)));
468 auto const txID = to_string(env.tx()->getTransactionID());
469 auto const ledgerSeq = env.current()->header().seq;
470 env.close();
471 // Make sure the fields only exist if the object is touched
472 env(noop(gw));
473 env.close();
474
475 {
476 auto const jrr = ledgerData(env);
477 auto const& jstate = jrr[jss::state];
478 BEAST_EXPECTS(checkArraySize(jstate, 3), jrr.toStyledString());
479 for (auto const& directory : jstate)
480 {
481 BEAST_EXPECT(directory["LedgerEntryType"] == jss::DirectoryNode); // sanity check
482 if (directory[jss::Owner] == gw.human())
483 {
484 // gw's directory did not get touched, so it
485 // should not have those fields populated
486 BEAST_EXPECT(!directory.isMember("PreviousTxnID"));
487 BEAST_EXPECT(!directory.isMember("PreviousTxnLgrSeq"));
488 }
489 else
490 {
491 // All of the other directories, including the order
492 // book, did get touched, so they should have those
493 // fields
494 BEAST_EXPECT(
495 directory.isMember("PreviousTxnID") &&
496 directory["PreviousTxnID"].asString() == txID);
497 BEAST_EXPECT(
498 directory.isMember("PreviousTxnLgrSeq") &&
499 directory["PreviousTxnLgrSeq"].asUInt() == ledgerSeq);
500 }
501 }
502 }
503 }
504
505 void
507 {
508 using namespace test::jtx;
509 Account const alice("alice");
510
511 auto const testCase = [&, this](FeatureBitset features, auto setup) {
512 using namespace test::jtx;
513
514 Env env(*this, features);
515 env.fund(XRP(20000), alice);
516 env.close();
517
518 auto const [lastPage, full] = setup(env);
519
520 // Populate root page and last page
521 for (int i = 0; i < 63; ++i)
522 env(credentials::create(alice, alice, std::to_string(i)));
523 env.close();
524
525 // NOTE, everything below can only be tested on open ledger because
526 // there is no transaction type to express what bumpLastPage does.
527
528 // Bump position of last page from 1 to highest possible
529 auto const res = directory::bumpLastPage(
530 env,
531 lastPage,
532 keylet::ownerDir(alice.id()),
533 [lastPage, this](ApplyView& view, uint256 key, std::uint64_t page) {
534 auto sle = view.peek({ltCREDENTIAL, key});
535 if (!BEAST_EXPECT(sle))
536 return false;
537
538 BEAST_EXPECT(page == lastPage);
539 sle->setFieldU64(sfIssuerNode, page);
540 // sfSubjectNode is not set in self-issued credentials
541 view.update(sle);
542 return true;
543 });
544 BEAST_EXPECT(res);
545
546 // Create one more credential
547 env(credentials::create(alice, alice, std::to_string(63)));
548
549 // Not enough space for another object if full
550 auto const expected = full ? Ter{tecDIR_FULL} : Ter{tesSUCCESS};
551 env(credentials::create(alice, alice, "foo"), expected);
552
553 // Destroy all objects in directory
554 for (int i = 0; i < 64; ++i)
555 env(credentials::deleteCred(alice, alice, alice, std::to_string(i)));
556
557 if (!full)
558 env(credentials::deleteCred(alice, alice, alice, "foo"));
559
560 // Verify directory is empty.
561 auto const sle = env.le(keylet::ownerDir(alice.id()));
562 BEAST_EXPECT(sle == nullptr);
563
564 // Test completed
565 env.close();
566 };
567
568 testCase(
569 testableAmendments() - fixDirectoryLimit,
571 testcase("directory full without fixDirectoryLimit");
572 return {kDirNodeMaxPages - 1, true};
573 });
574 testCase(
576 [this](Env&) -> std::tuple<std::uint64_t, bool> {
577 testcase("directory not full with fixDirectoryLimit");
578 return {kDirNodeMaxPages - 1, false};
579 });
580 testCase(
582 [this](Env&) -> std::tuple<std::uint64_t, bool> {
583 testcase("directory full with fixDirectoryLimit");
585 });
586 }
587
588 void
589 run() override
590 {
593 testRipd1353();
597 }
598};
599
601
602} // namespace xrpl::test
T capacity(T... args)
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
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:118
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void update(SLE::ref sle)=0
Indicate changes to a peeked SLE.
Specifies an order book.
Definition Book.h:16
void pushBack(uint256 const &v)
Discardable, editable view to a ledger.
Definition Sandbox.h:15
void insert(SLE::ref sle) override
Insert a new state SLE.
std::optional< key_type > succ(key_type const &key, std::optional< key_type > const &last=std::nullopt) const override
Return the key of the next state item.
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void update(SLE::ref sle) override
Indicate changes to a peeked SLE.
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
AccountID id() const
Returns the Account ID.
Definition jtx/Account.h:85
A transaction testing environment.
Definition Env.h:143
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:127
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
void enableFeature(uint256 const feature)
Definition Env.cpp:682
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:327
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition Env.cpp:533
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
T is_sorted(T... args)
T make_shared(T... args)
T max(T... args)
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:351
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:357
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:192
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:363
json::Value deleteCred(jtx::Account const &acc, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:40
json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:16
Directory operations.
Definition directory.h:13
auto bumpLastPage(Env &env, std::uint64_t newLastPage, Keylet directory, std::function< bool(ApplyView &, uint256, std::uint64_t)> adjust) -> std::expected< void, Error >
Move the position of the last page in the user's directory on open ledger to newLastPage.
Definition directory.cpp:25
static NoneT const kNone
Definition tags.h:9
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
json::Value offerCancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:31
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
json::Value noop(Account const &account)
The null transaction.
Definition noop.h:9
FeatureBitset testableAmendments()
Definition Env.h:76
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:18
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
Definition multisign.cpp:31
bool checkArraySize(json::Value const &val, unsigned int size)
BEAST_DEFINE_TESTSUITE_PRIO(AccountDelete, app, xrpl, 2)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
bool dirIsEmpty(ReadView const &view, Keylet const &k)
Returns true if the directory is empty.
constexpr std::size_t kDirNodeMaxEntries
The maximum number of entries per directory page.
Definition Protocol.h:41
BaseUInt< 256, detail::DirectoryTag > Directory
Directory is an index into the directory of offer books.
Definition UintTypes.h:33
constexpr std::uint64_t kDirNodeMaxPages
The maximum number of pages allowed in a directory.
Definition Protocol.h:47
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
uint256 getQualityNext(uint256 const &uBase)
Definition Indexes.cpp:144
uint256 getBookBase(Book const &book)
Definition Indexes.cpp:102
@ TapNone
Definition ApplyView.h:13
beast::xor_shift_engine & defaultPrng()
Return the default random engine.
@ tecDIR_FULL
Definition TER.h:285
BaseUInt< 256 > uint256
Definition base_uint.h:562
@ tesSUCCESS
Definition TER.h:240
T push_back(T... args)
T shuffle(T... args)
T reserve(T... args)
T size(T... args)
void run() override
Runs the suite.
std::string currcode(std::size_t i)
static void makePages(Sandbox &sb, uint256 const &base, std::uint64_t n)
T to_string(T... args)