rippled
Loading...
Searching...
No Matches
View_test.cpp
1#include <test/jtx.h>
2
3#include <xrpld/app/ledger/Ledger.h>
4#include <xrpld/core/ConfigSections.h>
5
6#include <xrpl/ledger/ApplyViewImpl.h>
7#include <xrpl/ledger/OpenView.h>
8#include <xrpl/ledger/PaymentSandbox.h>
9#include <xrpl/ledger/Sandbox.h>
10#include <xrpl/protocol/Feature.h>
11
12#include <type_traits>
13
14namespace ripple {
15namespace test {
16
18{
19 // Convert a small integer to a key
20 static Keylet
22 {
23 return Keylet{ltACCOUNT_ROOT, uint256(id)};
24 }
25
26 // Create SLE with key and payload
29 {
30 auto const le = std::make_shared<SLE>(k(id));
31 le->setFieldU32(sfSequence, seq);
32 return le;
33 }
34
35 // Return payload for SLE
36 template <class T>
37 static std::uint32_t
39 {
40 return le->getFieldU32(sfSequence);
41 }
42
43 // Set payload on SLE
44 static void
46 {
47 le->setFieldU32(sfSequence, seq);
48 }
49
50 // Erase all state items
51 static void
52 wipe(OpenLedger& openLedger)
53 {
54 openLedger.modify([](OpenView& view, beast::Journal) {
55 // HACK!
57 next.emplace(0);
58 for (;;)
59 {
60 next = view.succ(*next);
61 if (!next)
62 break;
64 *view.read(keylet::unchecked(*next))));
65 }
66 return true;
67 });
68 }
69
70 static void
71 wipe(Ledger& ledger)
72 {
73 // HACK!
75 next.emplace(0);
76 for (;;)
77 {
78 next = ledger.succ(*next);
79 if (!next)
80 break;
81 ledger.rawErase(
83 }
84 }
85
86 // Test succ correctness
87 void
89 ReadView const& v,
92 {
93 auto const next = v.succ(k(id).key);
94 if (answer)
95 {
96 if (BEAST_EXPECT(next))
97 BEAST_EXPECT(*next == k(*answer).key);
98 }
99 else
100 {
101 BEAST_EXPECT(!next);
102 }
103 }
104
105 template <class T>
108 {
110 }
111
112 // Exercise Ledger implementation of ApplyView
113 void
115 {
116 testcase("Ledger");
117
118 using namespace jtx;
119 Env env(*this);
120 Config config;
123 config,
125 env.app().getNodeFamily());
126 auto const ledger = std::make_shared<Ledger>(
127 *genesis, env.app().timeKeeper().closeTime());
128 wipe(*ledger);
129 ReadView& v = *ledger;
130 succ(v, 0, std::nullopt);
131 ledger->rawInsert(sle(1, 1));
132 BEAST_EXPECT(v.exists(k(1)));
133 BEAST_EXPECT(seq(v.read(k(1))) == 1);
134 succ(v, 0, 1);
135 succ(v, 1, std::nullopt);
136 ledger->rawInsert(sle(2, 2));
137 BEAST_EXPECT(seq(v.read(k(2))) == 2);
138 ledger->rawInsert(sle(3, 3));
139 BEAST_EXPECT(seq(v.read(k(3))) == 3);
140 auto s = copy(v.read(k(2)));
141 seq(s, 4);
142 ledger->rawReplace(s);
143 BEAST_EXPECT(seq(v.read(k(2))) == 4);
144 ledger->rawErase(sle(2));
145 BEAST_EXPECT(!v.exists(k(2)));
146 BEAST_EXPECT(v.exists(k(1)));
147 BEAST_EXPECT(v.exists(k(3)));
148 }
149
150 void
152 {
153 testcase("Meta");
154
155 using namespace jtx;
156 Env env(*this);
157 wipe(env.app().openLedger());
158 auto const open = env.current();
160 succ(v, 0, std::nullopt);
161 v.insert(sle(1));
162 BEAST_EXPECT(v.exists(k(1)));
163 BEAST_EXPECT(seq(v.read(k(1))) == 1);
164 BEAST_EXPECT(seq(v.peek(k(1))) == 1);
165 succ(v, 0, 1);
166 succ(v, 1, std::nullopt);
167 v.insert(sle(2, 2));
168 BEAST_EXPECT(seq(v.read(k(2))) == 2);
169 v.insert(sle(3, 3));
170 auto s = v.peek(k(3));
171 BEAST_EXPECT(seq(s) == 3);
172 s = v.peek(k(2));
173 seq(s, 4);
174 v.update(s);
175 BEAST_EXPECT(seq(v.read(k(2))) == 4);
176 v.erase(s);
177 BEAST_EXPECT(!v.exists(k(2)));
178 BEAST_EXPECT(v.exists(k(1)));
179 BEAST_EXPECT(v.exists(k(3)));
180 }
181
182 // Exercise all succ paths
183 void
185 {
186 testcase("Meta succ");
187
188 using namespace jtx;
189 Env env(*this);
190 wipe(env.app().openLedger());
191 auto const open = env.current();
193 v0.insert(sle(1));
194 v0.insert(sle(2));
195 v0.insert(sle(4));
196 v0.insert(sle(7));
197 {
198 Sandbox v1(&v0);
199 v1.insert(sle(3));
200 v1.insert(sle(5));
201 v1.insert(sle(6));
202
203 // v0: 12-4--7
204 // v1: --3-56-
205
206 succ(v0, 0, 1);
207 succ(v0, 1, 2);
208 succ(v0, 2, 4);
209 succ(v0, 3, 4);
210 succ(v0, 4, 7);
211 succ(v0, 5, 7);
212 succ(v0, 6, 7);
213 succ(v0, 7, std::nullopt);
214
215 succ(v1, 0, 1);
216 succ(v1, 1, 2);
217 succ(v1, 2, 3);
218 succ(v1, 3, 4);
219 succ(v1, 4, 5);
220 succ(v1, 5, 6);
221 succ(v1, 6, 7);
222 succ(v1, 7, std::nullopt);
223
224 v1.erase(v1.peek(k(4)));
225 succ(v1, 3, 5);
226
227 v1.erase(v1.peek(k(6)));
228 succ(v1, 5, 7);
229 succ(v1, 6, 7);
230
231 // v0: 12----7
232 // v1: --3-5--
233
234 v1.apply(v0);
235 }
236
237 // v0: 123-5-7
238
239 succ(v0, 0, 1);
240 succ(v0, 1, 2);
241 succ(v0, 2, 3);
242 succ(v0, 3, 5);
243 succ(v0, 4, 5);
244 succ(v0, 5, 7);
245 succ(v0, 6, 7);
246 succ(v0, 7, std::nullopt);
247 }
248
249 void
251 {
252 testcase("Stacked");
253
254 using namespace jtx;
255 Env env(*this);
256 wipe(env.app().openLedger());
257 auto const open = env.current();
259 v0.rawInsert(sle(1, 1));
260 v0.rawInsert(sle(2, 2));
261 v0.rawInsert(sle(4, 4));
262
263 {
264 Sandbox v1(&v0);
265 v1.erase(v1.peek(k(2)));
266 v1.insert(sle(3, 3));
267 auto s = v1.peek(k(4));
268 seq(s, 5);
269 v1.update(s);
270 BEAST_EXPECT(seq(v1.read(k(1))) == 1);
271 BEAST_EXPECT(!v1.exists(k(2)));
272 BEAST_EXPECT(seq(v1.read(k(3))) == 3);
273 BEAST_EXPECT(seq(v1.read(k(4))) == 5);
274 {
275 Sandbox v2(&v1);
276 auto s2 = v2.peek(k(3));
277 seq(s2, 6);
278 v2.update(s2);
279 v2.erase(v2.peek(k(4)));
280 BEAST_EXPECT(seq(v2.read(k(1))) == 1);
281 BEAST_EXPECT(!v2.exists(k(2)));
282 BEAST_EXPECT(seq(v2.read(k(3))) == 6);
283 BEAST_EXPECT(!v2.exists(k(4)));
284 // discard v2
285 }
286 BEAST_EXPECT(seq(v1.read(k(1))) == 1);
287 BEAST_EXPECT(!v1.exists(k(2)));
288 BEAST_EXPECT(seq(v1.read(k(3))) == 3);
289 BEAST_EXPECT(seq(v1.read(k(4))) == 5);
290
291 {
292 Sandbox v2(&v1);
293 auto s2 = v2.peek(k(3));
294 seq(s2, 6);
295 v2.update(s2);
296 v2.erase(v2.peek(k(4)));
297 BEAST_EXPECT(seq(v2.read(k(1))) == 1);
298 BEAST_EXPECT(!v2.exists(k(2)));
299 BEAST_EXPECT(seq(v2.read(k(3))) == 6);
300 BEAST_EXPECT(!v2.exists(k(4)));
301 v2.apply(v1);
302 }
303 BEAST_EXPECT(seq(v1.read(k(1))) == 1);
304 BEAST_EXPECT(!v1.exists(k(2)));
305 BEAST_EXPECT(seq(v1.read(k(3))) == 6);
306 BEAST_EXPECT(!v1.exists(k(4)));
307 v1.apply(v0);
308 }
309 BEAST_EXPECT(seq(v0.read(k(1))) == 1);
310 BEAST_EXPECT(!v0.exists(k(2)));
311 BEAST_EXPECT(seq(v0.read(k(3))) == 6);
312 BEAST_EXPECT(!v0.exists(k(4)));
313 }
314
315 // Verify contextual information
316 void
318 {
319 testcase("Context");
320
321 using namespace jtx;
322 using namespace std::chrono;
323 {
324 Env env(*this);
325 wipe(env.app().openLedger());
326 auto const open = env.current();
327 OpenView v0(open.get());
328 BEAST_EXPECT(v0.seq() != 98);
329 BEAST_EXPECT(v0.seq() == open->seq());
330 BEAST_EXPECT(v0.parentCloseTime() != NetClock::time_point{99s});
331 BEAST_EXPECT(v0.parentCloseTime() == open->parentCloseTime());
332 {
333 // shallow copy
334 OpenView v1(v0);
335 BEAST_EXPECT(v1.seq() == v0.seq());
336 BEAST_EXPECT(v1.parentCloseTime() == v1.parentCloseTime());
337
338 ApplyViewImpl v2(&v1, tapRETRY);
339 BEAST_EXPECT(v2.parentCloseTime() == v1.parentCloseTime());
340 BEAST_EXPECT(v2.seq() == v1.seq());
341 BEAST_EXPECT(v2.flags() == tapRETRY);
342
343 Sandbox v3(&v2);
344 BEAST_EXPECT(v3.seq() == v2.seq());
345 BEAST_EXPECT(v3.parentCloseTime() == v2.parentCloseTime());
346 BEAST_EXPECT(v3.flags() == tapRETRY);
347 }
348 {
349 ApplyViewImpl v1(&v0, tapRETRY);
350 PaymentSandbox v2(&v1);
351 BEAST_EXPECT(v2.seq() == v0.seq());
352 BEAST_EXPECT(v2.parentCloseTime() == v0.parentCloseTime());
353 BEAST_EXPECT(v2.flags() == tapRETRY);
354 PaymentSandbox v3(&v2);
355 BEAST_EXPECT(v3.seq() == v2.seq());
356 BEAST_EXPECT(v3.parentCloseTime() == v2.parentCloseTime());
357 BEAST_EXPECT(v3.flags() == v2.flags());
358 }
359 }
360 }
361
362 // Return a list of keys found via sles
364 sles(ReadView const& ledger)
365 {
367 v.reserve(32);
368 for (auto const& sle : ledger.sles)
369 v.push_back(sle->key());
370 return v;
371 }
372
373 template <class... Args>
375 list(Args... args)
376 {
377 return std::vector<uint256>({uint256(args)...});
378 }
379
380 void
382 {
383 testcase("Upper and lower bound");
384
385 using namespace jtx;
386 Env env(*this);
387 Config config;
390 config,
392 env.app().getNodeFamily());
393 auto const ledger = std::make_shared<Ledger>(
394 *genesis, env.app().timeKeeper().closeTime());
395
396 auto setup = [&ledger](std::vector<int> const& vec) {
397 wipe(*ledger);
398 for (auto x : vec)
399 {
400 ledger->rawInsert(sle(x));
401 }
402 };
403 {
404 setup({1, 2, 3});
405 BEAST_EXPECT(sles(*ledger) == list(1, 2, 3));
406 auto e = ledger->stateMap().end();
407 auto b1 = ledger->stateMap().begin();
408 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == e);
409 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(2)) == b1);
410 ++b1;
411 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(3)) == b1);
412 ++b1;
413 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(4)) == b1);
414 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(5)) == b1);
415 b1 = ledger->stateMap().begin();
416 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(0)) == b1);
417 ++b1;
418 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(1)) == b1);
419 ++b1;
420 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(2)) == b1);
421 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(3)) == e);
422 }
423
424 {
425 setup({2, 4, 6});
426 BEAST_EXPECT(sles(*ledger) == list(2, 4, 6));
427 auto e = ledger->stateMap().end();
428 auto b1 = ledger->stateMap().begin();
429 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == e);
430 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(2)) == e);
431 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(3)) == b1);
432 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(4)) == b1);
433 ++b1;
434 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(5)) == b1);
435 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(6)) == b1);
436 ++b1;
437 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(7)) == b1);
438 b1 = ledger->stateMap().begin();
439 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(1)) == b1);
440 ++b1;
441 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(2)) == b1);
442 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(3)) == b1);
443 ++b1;
444
445 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(4)) == b1);
446 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(5)) == b1);
447 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(6)) == e);
448 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(7)) == e);
449 }
450 {
451 setup({2, 3, 5, 6, 10, 15});
452 BEAST_EXPECT(sles(*ledger) == list(2, 3, 5, 6, 10, 15));
453 auto e = ledger->stateMap().end();
454 auto b = ledger->stateMap().begin();
455 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == e);
456 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(2)) == e);
457 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(3)) == b);
458 ++b;
459 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(4)) == b);
460 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(5)) == b);
461 ++b;
462 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(6)) == b);
463 ++b;
464 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(7)) == b);
465 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(8)) == b);
466 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(9)) == b);
467 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(10)) == b);
468 ++b;
469 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(11)) == b);
470 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(12)) == b);
471 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(13)) == b);
472 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(14)) == b);
473 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(15)) == b);
474 ++b;
475 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(16)) == b);
476 b = ledger->stateMap().begin();
477 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(0)) == b);
478 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(1)) == b);
479 ++b;
480 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(2)) == b);
481 ++b;
482 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(3)) == b);
483 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(4)) == b);
484 ++b;
485 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(5)) == b);
486 ++b;
487 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(6)) == b);
488 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(7)) == b);
489 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(8)) == b);
490 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(9)) == b);
491 ++b;
492 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(10)) == b);
493 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(11)) == b);
494 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(12)) == b);
495 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(13)) == b);
496 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(14)) == b);
497 ++b;
498 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(15)) == e);
499 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(16)) == e);
500 }
501 {
502 // some full trees, some empty trees, etc
503 setup({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
504 13, 14, 15, 16, 20, 25, 30, 32, 33, 34, 35, 36, 37,
505 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 66, 100});
506 BEAST_EXPECT(
507 sles(*ledger) ==
508 list(
509 0,
510 1,
511 2,
512 3,
513 4,
514 5,
515 6,
516 7,
517 8,
518 9,
519 10,
520 11,
521 12,
522 13,
523 14,
524 15,
525 16,
526 20,
527 25,
528 30,
529 32,
530 33,
531 34,
532 35,
533 36,
534 37,
535 38,
536 39,
537 40,
538 41,
539 42,
540 43,
541 44,
542 45,
543 46,
544 47,
545 48,
546 66,
547 100));
548 auto b = ledger->stateMap().begin();
549 auto e = ledger->stateMap().end();
550 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(0)) == e);
551 BEAST_EXPECT(ledger->stateMap().lower_bound(uint256(1)) == b);
552 BEAST_EXPECT(
553 ledger->stateMap().lower_bound(uint256(5))->key() ==
554 uint256(4));
555 BEAST_EXPECT(
556 ledger->stateMap().lower_bound(uint256(15))->key() ==
557 uint256(14));
558 BEAST_EXPECT(
559 ledger->stateMap().lower_bound(uint256(16))->key() ==
560 uint256(15));
561 BEAST_EXPECT(
562 ledger->stateMap().lower_bound(uint256(19))->key() ==
563 uint256(16));
564 BEAST_EXPECT(
565 ledger->stateMap().lower_bound(uint256(20))->key() ==
566 uint256(16));
567 BEAST_EXPECT(
568 ledger->stateMap().lower_bound(uint256(24))->key() ==
569 uint256(20));
570 BEAST_EXPECT(
571 ledger->stateMap().lower_bound(uint256(31))->key() ==
572 uint256(30));
573 BEAST_EXPECT(
574 ledger->stateMap().lower_bound(uint256(32))->key() ==
575 uint256(30));
576 BEAST_EXPECT(
577 ledger->stateMap().lower_bound(uint256(40))->key() ==
578 uint256(39));
579 BEAST_EXPECT(
580 ledger->stateMap().lower_bound(uint256(47))->key() ==
581 uint256(46));
582 BEAST_EXPECT(
583 ledger->stateMap().lower_bound(uint256(48))->key() ==
584 uint256(47));
585 BEAST_EXPECT(
586 ledger->stateMap().lower_bound(uint256(64))->key() ==
587 uint256(48));
588
589 BEAST_EXPECT(
590 ledger->stateMap().lower_bound(uint256(90))->key() ==
591 uint256(66));
592 BEAST_EXPECT(
593 ledger->stateMap().lower_bound(uint256(96))->key() ==
594 uint256(66));
595 BEAST_EXPECT(
596 ledger->stateMap().lower_bound(uint256(100))->key() ==
597 uint256(66));
598
599 BEAST_EXPECT(
600 ledger->stateMap().upper_bound(uint256(0))->key() ==
601 uint256(1));
602 BEAST_EXPECT(
603 ledger->stateMap().upper_bound(uint256(5))->key() ==
604 uint256(6));
605 BEAST_EXPECT(
606 ledger->stateMap().upper_bound(uint256(15))->key() ==
607 uint256(16));
608 BEAST_EXPECT(
609 ledger->stateMap().upper_bound(uint256(16))->key() ==
610 uint256(20));
611 BEAST_EXPECT(
612 ledger->stateMap().upper_bound(uint256(18))->key() ==
613 uint256(20));
614 BEAST_EXPECT(
615 ledger->stateMap().upper_bound(uint256(20))->key() ==
616 uint256(25));
617 BEAST_EXPECT(
618 ledger->stateMap().upper_bound(uint256(31))->key() ==
619 uint256(32));
620 BEAST_EXPECT(
621 ledger->stateMap().upper_bound(uint256(32))->key() ==
622 uint256(33));
623 BEAST_EXPECT(
624 ledger->stateMap().upper_bound(uint256(47))->key() ==
625 uint256(48));
626 BEAST_EXPECT(
627 ledger->stateMap().upper_bound(uint256(48))->key() ==
628 uint256(66));
629 BEAST_EXPECT(
630 ledger->stateMap().upper_bound(uint256(53))->key() ==
631 uint256(66));
632 BEAST_EXPECT(
633 ledger->stateMap().upper_bound(uint256(66))->key() ==
634 uint256(100));
635 BEAST_EXPECT(
636 ledger->stateMap().upper_bound(uint256(70))->key() ==
637 uint256(100));
638 BEAST_EXPECT(
639 ledger->stateMap().upper_bound(uint256(85))->key() ==
640 uint256(100));
641 BEAST_EXPECT(
642 ledger->stateMap().upper_bound(uint256(98))->key() ==
643 uint256(100));
644 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(100)) == e);
645 BEAST_EXPECT(ledger->stateMap().upper_bound(uint256(155)) == e);
646 }
647 }
648
649 void
651 {
652 testcase("Sles");
653
654 using namespace jtx;
655 Env env(*this);
656 Config config;
659 config,
661 env.app().getNodeFamily());
662 auto const ledger = std::make_shared<Ledger>(
663 *genesis, env.app().timeKeeper().closeTime());
664 auto setup123 = [&ledger, this]() {
665 // erase middle element
666 wipe(*ledger);
667 ledger->rawInsert(sle(1));
668 ledger->rawInsert(sle(2));
669 ledger->rawInsert(sle(3));
670 BEAST_EXPECT(sles(*ledger) == list(1, 2, 3));
671 };
672 {
673 setup123();
674 OpenView view(ledger.get());
675 view.rawErase(sle(1));
676 view.rawInsert(sle(4));
677 view.rawInsert(sle(5));
678 BEAST_EXPECT(sles(view) == list(2, 3, 4, 5));
679 auto b = view.sles.begin();
680 BEAST_EXPECT(view.sles.upper_bound(uint256(1)) == b);
681 ++b;
682 BEAST_EXPECT(view.sles.upper_bound(uint256(2)) == b);
683 ++b;
684 BEAST_EXPECT(view.sles.upper_bound(uint256(3)) == b);
685 ++b;
686 BEAST_EXPECT(view.sles.upper_bound(uint256(4)) == b);
687 ++b;
688 BEAST_EXPECT(view.sles.upper_bound(uint256(5)) == b);
689 }
690 {
691 setup123();
692 OpenView view(ledger.get());
693 view.rawErase(sle(1));
694 view.rawErase(sle(2));
695 view.rawInsert(sle(4));
696 view.rawInsert(sle(5));
697 BEAST_EXPECT(sles(view) == list(3, 4, 5));
698 auto b = view.sles.begin();
699 BEAST_EXPECT(view.sles.upper_bound(uint256(1)) == b);
700 BEAST_EXPECT(view.sles.upper_bound(uint256(2)) == b);
701 ++b;
702 BEAST_EXPECT(view.sles.upper_bound(uint256(3)) == b);
703 ++b;
704 BEAST_EXPECT(view.sles.upper_bound(uint256(4)) == b);
705 ++b;
706 BEAST_EXPECT(view.sles.upper_bound(uint256(5)) == b);
707 }
708 {
709 setup123();
710 OpenView view(ledger.get());
711 view.rawErase(sle(1));
712 view.rawErase(sle(2));
713 view.rawErase(sle(3));
714 view.rawInsert(sle(4));
715 view.rawInsert(sle(5));
716 BEAST_EXPECT(sles(view) == list(4, 5));
717 auto b = view.sles.begin();
718 BEAST_EXPECT(view.sles.upper_bound(uint256(1)) == b);
719 BEAST_EXPECT(view.sles.upper_bound(uint256(2)) == b);
720 BEAST_EXPECT(view.sles.upper_bound(uint256(3)) == b);
721 ++b;
722 BEAST_EXPECT(view.sles.upper_bound(uint256(4)) == b);
723 ++b;
724 BEAST_EXPECT(view.sles.upper_bound(uint256(5)) == b);
725 }
726 {
727 setup123();
728 OpenView view(ledger.get());
729 view.rawErase(sle(3));
730 view.rawInsert(sle(4));
731 view.rawInsert(sle(5));
732 BEAST_EXPECT(sles(view) == list(1, 2, 4, 5));
733 auto b = view.sles.begin();
734 ++b;
735 BEAST_EXPECT(view.sles.upper_bound(uint256(1)) == b);
736 ++b;
737 BEAST_EXPECT(view.sles.upper_bound(uint256(2)) == b);
738 BEAST_EXPECT(view.sles.upper_bound(uint256(3)) == b);
739 ++b;
740 BEAST_EXPECT(view.sles.upper_bound(uint256(4)) == b);
741 ++b;
742 BEAST_EXPECT(view.sles.upper_bound(uint256(5)) == b);
743 }
744 {
745 setup123();
746 OpenView view(ledger.get());
747 view.rawReplace(sle(1, 10));
748 view.rawReplace(sle(3, 30));
749 BEAST_EXPECT(sles(view) == list(1, 2, 3));
750 BEAST_EXPECT(seq(view.read(k(1))) == 10);
751 BEAST_EXPECT(seq(view.read(k(2))) == 1);
752 BEAST_EXPECT(seq(view.read(k(3))) == 30);
753
754 view.rawErase(sle(3));
755 BEAST_EXPECT(sles(view) == list(1, 2));
756 auto b = view.sles.begin();
757 ++b;
758 BEAST_EXPECT(view.sles.upper_bound(uint256(1)) == b);
759 ++b;
760 BEAST_EXPECT(view.sles.upper_bound(uint256(2)) == b);
761 BEAST_EXPECT(view.sles.upper_bound(uint256(3)) == b);
762 BEAST_EXPECT(view.sles.upper_bound(uint256(4)) == b);
763 BEAST_EXPECT(view.sles.upper_bound(uint256(5)) == b);
764
765 view.rawInsert(sle(5));
766 view.rawInsert(sle(4));
767 view.rawInsert(sle(3));
768 BEAST_EXPECT(sles(view) == list(1, 2, 3, 4, 5));
769 b = view.sles.begin();
770 ++b;
771 BEAST_EXPECT(view.sles.upper_bound(uint256(1)) == b);
772 ++b;
773 BEAST_EXPECT(view.sles.upper_bound(uint256(2)) == b);
774 ++b;
775 BEAST_EXPECT(view.sles.upper_bound(uint256(3)) == b);
776 ++b;
777 BEAST_EXPECT(view.sles.upper_bound(uint256(4)) == b);
778 ++b;
779 BEAST_EXPECT(view.sles.upper_bound(uint256(5)) == b);
780 }
781 }
782
783 void
785 {
786 testcase("Flags");
787
788 using namespace jtx;
789 Env env(*this);
790
791 auto const alice = Account("alice");
792 auto const bob = Account("bob");
793 auto const carol = Account("carol");
794 auto const gw = Account("gateway");
795 auto const USD = gw["USD"];
796 auto const EUR = gw["EUR"];
797
798 env.fund(XRP(10000), alice, bob, carol, gw);
799 env.close();
800 env.trust(USD(100), alice, bob, carol);
801 {
802 // Global freezing.
803 env(pay(gw, alice, USD(50)));
804 env(offer(alice, XRP(5), USD(5)));
805
806 // Now freeze gw.
807 env(fset(gw, asfGlobalFreeze));
808 env.close();
809 env(offer(alice, XRP(4), USD(5)), ter(tecFROZEN));
810 env.close();
811
812 // Alice's USD balance should be zero if frozen.
813 BEAST_EXPECT(
814 USD(0) ==
816 *env.closed(),
817 alice,
818 USD.currency,
819 gw,
821 env.journal));
822
823 // Thaw gw and try again.
824 env(fclear(gw, asfGlobalFreeze));
825 env.close();
826 env(offer("alice", XRP(4), USD(5)));
827 }
828 {
829 // Local freezing.
830 env(pay(gw, bob, USD(50)));
831 env.close();
832
833 // Now gw freezes bob's USD trust line.
834 env(trust(gw, USD(100), bob, tfSetFreeze));
835 env.close();
836
837 // Bob's balance should be zero if frozen.
838 BEAST_EXPECT(
839 USD(0) ==
841 *env.closed(),
842 bob,
843 USD.currency,
844 gw,
846 env.journal));
847
848 // gw thaws bob's trust line. bob gets his money back.
849 env(trust(gw, USD(100), bob, tfClearFreeze));
850 env.close();
851 BEAST_EXPECT(
852 USD(50) ==
854 *env.closed(),
855 bob,
856 USD.currency,
857 gw,
859 env.journal));
860 }
861 {
862 // accountHolds().
863 env(pay(gw, carol, USD(50)));
864 env.close();
865
866 // carol has no EUR.
867 BEAST_EXPECT(
868 EUR(0) ==
870 *env.closed(),
871 carol,
872 EUR.currency,
873 gw,
875 env.journal));
876
877 // But carol does have USD.
878 BEAST_EXPECT(
879 USD(50) ==
881 *env.closed(),
882 carol,
883 USD.currency,
884 gw,
886 env.journal));
887
888 // carol's XRP balance should be her holdings minus her reserve.
889 auto const carolsXRP = accountHolds(
890 *env.closed(),
891 carol,
892 xrpCurrency(),
893 xrpAccount(),
895 env.journal);
896 // carol's XRP balance: 10000
897 // base reserve: -200
898 // 1 trust line times its reserve: 1 * -50
899 // -------
900 // carol's available balance: 9750
901 BEAST_EXPECT(carolsXRP == XRP(9750));
902
903 // carol should be able to spend *more* than her XRP balance on
904 // a fee by eating into her reserve.
905 env(noop(carol), fee(carolsXRP + XRP(10)));
906 env.close();
907
908 // carol's XRP balance should now show as zero.
909 BEAST_EXPECT(
910 XRP(0) ==
912 *env.closed(),
913 carol,
914 xrpCurrency(),
915 gw,
917 env.journal));
918 }
919 {
920 // accountFunds().
921 // Gateways have whatever funds they claim to have.
922 auto const gwUSD = accountFunds(
923 *env.closed(), gw, USD(314159), fhZERO_IF_FROZEN, env.journal);
924 BEAST_EXPECT(gwUSD == USD(314159));
925
926 // carol has funds from the gateway.
927 auto carolsUSD = accountFunds(
928 *env.closed(), carol, USD(0), fhZERO_IF_FROZEN, env.journal);
929 BEAST_EXPECT(carolsUSD == USD(50));
930
931 // If carol's funds are frozen she has no funds...
932 env(fset(gw, asfGlobalFreeze));
933 env.close();
934 carolsUSD = accountFunds(
935 *env.closed(), carol, USD(0), fhZERO_IF_FROZEN, env.journal);
936 BEAST_EXPECT(carolsUSD == USD(0));
937
938 // ... unless the query ignores the FROZEN state.
939 carolsUSD = accountFunds(
940 *env.closed(), carol, USD(0), fhIGNORE_FREEZE, env.journal);
941 BEAST_EXPECT(carolsUSD == USD(50));
942
943 // Just to be tidy, thaw gw.
944 env(fclear(gw, asfGlobalFreeze));
945 env.close();
946 }
947 }
948
949 void
951 {
952 testcase("Transfer rate");
953
954 using namespace jtx;
955 Env env(*this);
956
957 auto const gw1 = Account("gw1");
958
959 env.fund(XRP(10000), gw1);
960 env.close();
961
962 auto rdView = env.closed();
963 // Test with no rate set on gw1.
964 BEAST_EXPECT(transferRate(*rdView, gw1) == parityRate);
965
966 env(rate(gw1, 1.02));
967 env.close();
968
969 rdView = env.closed();
970 BEAST_EXPECT(transferRate(*rdView, gw1) == Rate{1020000000});
971 }
972
973 void
975 {
976 // This test requires incompatible ledgers. The good news we can
977 // construct and manage two different Env instances at the same
978 // time. So we can use two Env instances to produce mutually
979 // incompatible ledgers.
980 testcase("Are compatible");
981
982 using namespace jtx;
983 auto const alice = Account("alice");
984 auto const bob = Account("bob");
985
986 // The first Env.
987 Env eA(*this, envconfig(), nullptr, beast::severities::kDisabled);
988
989 eA.fund(XRP(10000), alice);
990 eA.close();
991 auto const rdViewA3 = eA.closed();
992
993 eA.fund(XRP(10000), bob);
994 eA.close();
995 auto const rdViewA4 = eA.closed();
996
997 // The two Env's can't share the same ports, so modify the config
998 // of the second Env to use higher port numbers
999 Env eB{*this, envconfig(), nullptr, beast::severities::kDisabled};
1000
1001 // Make ledgers that are incompatible with the first ledgers. Note
1002 // that bob is funded before alice.
1003 eB.fund(XRP(10000), bob);
1004 eB.close();
1005 auto const rdViewB3 = eB.closed();
1006
1007 eB.fund(XRP(10000), alice);
1008 eB.close();
1009 auto const rdViewB4 = eB.closed();
1010
1011 // Check for compatibility.
1012 auto jStream = eA.journal.error();
1013 BEAST_EXPECT(areCompatible(*rdViewA3, *rdViewA4, jStream, ""));
1014 BEAST_EXPECT(areCompatible(*rdViewA4, *rdViewA3, jStream, ""));
1015 BEAST_EXPECT(areCompatible(*rdViewA4, *rdViewA4, jStream, ""));
1016 BEAST_EXPECT(!areCompatible(*rdViewA3, *rdViewB4, jStream, ""));
1017 BEAST_EXPECT(!areCompatible(*rdViewA4, *rdViewB3, jStream, ""));
1018 BEAST_EXPECT(!areCompatible(*rdViewA4, *rdViewB4, jStream, ""));
1019
1020 // Try the other interface.
1021 // Note that the different interface has different outcomes.
1022 auto const& iA3 = rdViewA3->info();
1023 auto const& iA4 = rdViewA4->info();
1024
1025 BEAST_EXPECT(areCompatible(iA3.hash, iA3.seq, *rdViewA4, jStream, ""));
1026 BEAST_EXPECT(areCompatible(iA4.hash, iA4.seq, *rdViewA3, jStream, ""));
1027 BEAST_EXPECT(areCompatible(iA4.hash, iA4.seq, *rdViewA4, jStream, ""));
1028 BEAST_EXPECT(!areCompatible(iA3.hash, iA3.seq, *rdViewB4, jStream, ""));
1029 BEAST_EXPECT(areCompatible(iA4.hash, iA4.seq, *rdViewB3, jStream, ""));
1030 BEAST_EXPECT(!areCompatible(iA4.hash, iA4.seq, *rdViewB4, jStream, ""));
1031 }
1032
1033 void
1035 {
1036 testcase("Regressions");
1037
1038 using namespace jtx;
1039
1040 // Create a ledger with 1 item, put a
1041 // ApplyView on that, then another ApplyView,
1042 // erase the item, apply.
1043 {
1044 Env env(*this);
1045 Config config;
1046 std::shared_ptr<Ledger const> const genesis =
1049 config,
1051 env.app().getNodeFamily());
1052 auto const ledger = std::make_shared<Ledger>(
1053 *genesis, env.app().timeKeeper().closeTime());
1054 wipe(*ledger);
1055 ledger->rawInsert(sle(1));
1056 ReadView& v0 = *ledger;
1057 ApplyViewImpl v1(&v0, tapNONE);
1058 {
1059 Sandbox v2(&v1);
1060 v2.erase(v2.peek(k(1)));
1061 v2.apply(v1);
1062 }
1063 BEAST_EXPECT(!v1.exists(k(1)));
1064 }
1065
1066 // Make sure OpenLedger::empty works
1067 {
1068 Env env(*this);
1069 BEAST_EXPECT(env.app().openLedger().empty());
1070 env.fund(XRP(10000), Account("test"));
1071 BEAST_EXPECT(!env.app().openLedger().empty());
1072 }
1073 }
1074
1075 void
1076 run() override
1077 {
1078 // This had better work, or else
1079 BEAST_EXPECT(k(0).key < k(1).key);
1080
1081 testLedger();
1082 testMeta();
1083 testMetaSucc();
1084 testStacked();
1085 testContext();
1086 testSles();
1088 testFlags();
1092 }
1093};
1094
1096{
1097 void
1099 {
1100 using namespace jtx;
1101 Env env{*this, envconfig(validator, "")};
1102
1103 // Start out with no amendments.
1104 auto majorities = getMajorityAmendments(*env.closed());
1105 BEAST_EXPECT(majorities.empty());
1106
1107 // Now close ledgers until the amendments show up.
1108 int i = 0;
1109 for (i = 0; i <= 256; ++i)
1110 {
1111 env.close();
1112 majorities = getMajorityAmendments(*env.closed());
1113 if (!majorities.empty())
1114 break;
1115 }
1116
1117 // There should be at least 5 amendments. Don't do exact comparison
1118 // to avoid maintenance as more amendments are added in the future.
1119 BEAST_EXPECT(i == 254);
1120 BEAST_EXPECT(majorities.size() >= 5);
1121
1122 // None of the amendments should be enabled yet.
1123 auto enableds = getEnabledAmendments(*env.closed());
1124 BEAST_EXPECT(enableds.empty());
1125
1126 // Now wait 2 weeks modulo 256 ledgers for the amendments to be
1127 // enabled. Speed the process by closing ledgers every 80 minutes,
1128 // which should get us to just past 2 weeks after 256 ledgers.
1129 for (i = 0; i <= 256; ++i)
1130 {
1131 using namespace std::chrono_literals;
1132 env.close(80min);
1133 enableds = getEnabledAmendments(*env.closed());
1134 if (!enableds.empty())
1135 break;
1136 }
1137 BEAST_EXPECT(i == 255);
1138 BEAST_EXPECT(enableds.size() >= 5);
1139 }
1140
1141 void
1142 run() override
1143 {
1145 }
1146};
1147
1148BEAST_DEFINE_TESTSUITE(View, ledger, ripple);
1149BEAST_DEFINE_TESTSUITE(GetAmendments, ledger, ripple);
1150
1151} // namespace test
1152} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:41
Stream error() const
Definition Journal.h:327
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
virtual OpenLedger & openLedger()=0
virtual TimeKeeper & timeKeeper()=0
Editable, discardable view that can build metadata for one tx.
Holds a ledger.
Definition Ledger.h:61
void rawErase(std::shared_ptr< SLE > const &sle) override
Delete an existing state item.
Definition Ledger.cpp:498
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition Ledger.cpp:413
std::optional< uint256 > succ(uint256 const &key, std::optional< uint256 > const &last=std::nullopt) const override
Definition Ledger.cpp:402
Represents the open ledger.
Definition OpenLedger.h:33
bool modify(modify_type const &f)
Modify the open ledger.
bool empty() const
Returns true if there are no transactions.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:46
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.
Definition OpenView.cpp:143
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition OpenView.cpp:150
void rawErase(std::shared_ptr< SLE > const &sle) override
Delete an existing state item.
Definition OpenView.cpp:212
void rawReplace(std::shared_ptr< SLE > const &sle) override
Unconditionally replace a state item.
Definition OpenView.cpp:224
void rawInsert(std::shared_ptr< SLE > const &sle) override
Unconditionally insert a state item.
Definition OpenView.cpp:218
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition ReadView.h:32
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition ReadView.h:92
virtual std::optional< key_type > succ(key_type const &key, std::optional< key_type > const &last=std::nullopt) const =0
Return the key of the next state item.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:99
sles_type sles
Iterable range of ledger state items.
Definition ReadView.h:226
Discardable, editable view to a ledger.
Definition Sandbox.h:16
void apply(RawView &to)
Definition Sandbox.h:36
time_point closeTime() const
Returns the predicted close time, in network time.
Definition TimeKeeper.h:57
void erase(std::shared_ptr< SLE > const &sle) override
Remove a peeked SLE.
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.
void rawInsert(std::shared_ptr< SLE > const &sle) override
Unconditionally insert a state item.
bool exists(Keylet const &k) const override
Determine if a state item exists.
ApplyFlags flags() const override
Returns the tx apply flags.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void run() override
Runs the suite.
static std::shared_ptr< SLE > sle(std::uint64_t id, std::uint32_t seq=1)
Definition View_test.cpp:28
static void wipe(Ledger &ledger)
Definition View_test.cpp:71
static void seq(std::shared_ptr< SLE > const &le, std::uint32_t seq)
Definition View_test.cpp:45
void succ(ReadView const &v, std::uint32_t id, std::optional< std::uint32_t > answer)
Definition View_test.cpp:88
static Keylet k(std::uint64_t id)
Definition View_test.cpp:21
static void wipe(OpenLedger &openLedger)
Definition View_test.cpp:52
static std::shared_ptr< std::remove_const_t< T > > copy(std::shared_ptr< T > const &sp)
static std::vector< uint256 > sles(ReadView const &ledger)
void run() override
Runs the suite.
static std::uint32_t seq(std::shared_ptr< T > const &le)
Definition View_test.cpp:38
static std::vector< uint256 > list(Args... args)
Immutable cryptographic account descriptor.
Definition Account.h:20
A transaction testing environment.
Definition Env.h:102
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:97
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:302
Application & app()
Definition Env.h:242
beast::Journal const journal
Definition Env.h:143
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
Set the fee on a JTx.
Definition fee.h:18
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
T is_same_v
Keylet unchecked(uint256 const &key) noexcept
Any ledger entry.
Definition Indexes.cpp:349
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
Definition envconfig.cpp:94
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t asfGlobalFreeze
Definition TxFlags.h:64
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:535
@ fhZERO_IF_FROZEN
Definition View.h:58
@ fhIGNORE_FREEZE
Definition View.h:58
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition View.cpp:780
AccountID const & xrpAccount()
Compute AccountID from public key.
base_uint< 256 > uint256
Definition base_uint.h:539
std::set< uint256 > getEnabledAmendments(ReadView const &view)
Definition View.cpp:902
@ open
We haven't closed our ledger yet, but others might have.
Currency const & xrpCurrency()
XRP currency.
constexpr std::uint32_t tfClearFreeze
Definition TxFlags.h:100
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:743
@ tecFROZEN
Definition TER.h:285
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:368
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition View.cpp:919
create_genesis_t const create_genesis
Definition Ledger.cpp:32
@ tapRETRY
Definition ApplyView.h:20
@ tapNONE
Definition ApplyView.h:12
constexpr std::uint32_t tfSetFreeze
Definition TxFlags.h:99
Rate const parityRate
A transfer rate signifying a 1:1 exchange.
T push_back(T... args)
T reserve(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
Represents a transfer rate.
Definition Rate.h:21
iterator upper_bound(key_type const &key) const
Definition ReadView.cpp:22
iterator begin() const
Definition ReadView.cpp:10
Set the sequence number on a JTx.
Definition seq.h:15