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