rippled
Loading...
Searching...
No Matches
Env_test.cpp
1#include <test/jtx.h>
2
3#include <xrpld/app/misc/TxQ.h>
4
5#include <xrpl/beast/hash/uhash.h>
6#include <xrpl/beast/unit_test.h>
7#include <xrpl/protocol/Feature.h>
8#include <xrpl/protocol/TxFlags.h>
9#include <xrpl/protocol/jss.h>
10#include <xrpl/server/NetworkOPs.h>
11
12#include <boost/lexical_cast.hpp>
13
14#include <optional>
15#include <utility>
16
17namespace xrpl {
18namespace test {
19
21{
22public:
23 template <class T>
24 static std::string
25 to_string(T const& t)
26 {
27 return boost::lexical_cast<std::string>(t);
28 }
29
30 // Declarations in Account.h
31 static void
33 {
34 using namespace jtx;
35 {
36 Account a("chad");
37 Account b(a);
38 a = b;
39 a = std::move(b);
40 Account const c(std::move(a));
41 }
42 Account("alice"); // NOLINT(bugprone-unused-raii)
43 Account("alice", KeyType::secp256k1); // NOLINT(bugprone-unused-raii)
44 Account("alice", KeyType::ed25519); // NOLINT(bugprone-unused-raii)
45 auto const gw = Account("gw");
46 [](AccountID) {}(gw);
47 auto const USD = gw["USD"];
48 void(Account("alice") < gw);
49 std::set<Account>().emplace(gw);
51 }
52
53 // Declarations in amount.h
54 void
56 {
57 using namespace jtx;
58
59 PrettyAmount(0); // NOLINT(bugprone-unused-raii)
60 PrettyAmount(1); // NOLINT(bugprone-unused-raii)
61 PrettyAmount(0u); // NOLINT(bugprone-unused-raii)
62 PrettyAmount(1u); // NOLINT(bugprone-unused-raii)
63 PrettyAmount(-1); // NOLINT(bugprone-unused-raii)
68
69 try
70 {
71 XRP(0.0000001);
72 fail("missing exception");
73 }
74 catch (std::domain_error const&)
75 {
76 pass();
77 }
78 XRP(-0.000001);
79 try
80 {
81 XRP(-0.0000009);
82 fail("missing exception");
83 }
84 catch (std::domain_error const&)
85 {
86 pass();
87 }
88
89 BEAST_EXPECT(to_string(XRP(5)) == "5 XRP");
90 BEAST_EXPECT(to_string(XRP(.80)) == "0.8 XRP");
91 BEAST_EXPECT(to_string(XRP(.005)) == "5000 drops");
92 BEAST_EXPECT(to_string(XRP(0.1)) == "0.1 XRP");
93 BEAST_EXPECT(to_string(XRP(10000)) == "10000 XRP");
94 BEAST_EXPECT(to_string(drops(10)) == "10 drops");
95 BEAST_EXPECT(to_string(drops(123400000)) == "123.4 XRP");
96 BEAST_EXPECT(to_string(XRP(-5)) == "-5 XRP");
97 BEAST_EXPECT(to_string(XRP(-.99)) == "-0.99 XRP");
98 BEAST_EXPECT(to_string(XRP(-.005)) == "-5000 drops");
99 BEAST_EXPECT(to_string(XRP(-0.1)) == "-0.1 XRP");
100 BEAST_EXPECT(to_string(drops(-10)) == "-10 drops");
101 BEAST_EXPECT(to_string(drops(-123400000)) == "-123.4 XRP");
102
103 BEAST_EXPECT(XRP(1) == drops(1000000));
104 BEAST_EXPECT(XRP(1) == STAmount(1000000));
105 BEAST_EXPECT(STAmount(1000000) == XRP(1));
106
107 auto const gw = Account("gw");
108 auto const USD = gw["USD"];
109 BEAST_EXPECT(to_string(USD(0)) == "0/USD(gw)");
110 BEAST_EXPECT(to_string(USD(10)) == "10/USD(gw)");
111 BEAST_EXPECT(to_string(USD(-10)) == "-10/USD(gw)");
112 BEAST_EXPECT(USD(0) == STAmount(USD, 0));
113 BEAST_EXPECT(USD(1) == STAmount(USD, 1));
114 BEAST_EXPECT(USD(-1) == STAmount(USD, -1));
115
116 auto const get = [](AnyAmount a) { return a; };
117 BEAST_EXPECT(!get(USD(10)).is_any);
118 BEAST_EXPECT(get(any(USD(10))).is_any);
119 }
120
121 // Test Env
122 void
124 {
125 using namespace jtx;
126 auto const n = XRP(10000);
127 auto const gw = Account("gw");
128 auto const USD = gw["USD"];
129 auto const alice = Account("alice");
130
131 // unfunded
132 {
133 Env env(*this);
134 env(pay("alice", "bob", XRP(1000)), seq(1), fee(10), sig("alice"), ter(terNO_ACCOUNT));
135 }
136
137 // fund
138 {
139 Env env(*this);
140
141 // variadics
142 env.fund(n, "alice");
143 env.fund(n, "bob", "carol");
144 env.fund(n, "dave", noripple("eric"));
145 env.fund(n, "fred", noripple("gary", "hank"));
146 env.fund(n, noripple("irene"));
147 env.fund(n, noripple("jim"), "karen");
148 env.fund(n, noripple("lisa", "mary"));
149
150 // flags
151 env.fund(n, noripple("xavier"));
152 env.require(nflags("xavier", asfDefaultRipple));
153 env.fund(n, "zachary");
154 env.require(flags("zachary", asfDefaultRipple));
155 }
156
157 // trust
158 {
159 Env env(*this);
160 env.fund(n, "alice", "bob", gw);
161 env.close();
162 env(trust("alice", USD(100)), require(lines("alice", 1)));
163 }
164
165 // balance
166 {
167 Env env(*this);
168 BEAST_EXPECT(env.balance(alice) == 0);
169 BEAST_EXPECT(env.balance(alice, USD) != 0);
170 BEAST_EXPECT(env.balance(alice, USD) == USD(0));
171 env.fund(n, alice, gw);
172 env.close();
173 BEAST_EXPECT(env.balance(alice) == n);
174 BEAST_EXPECT(env.balance(gw) == n);
175 env.trust(USD(1000), alice);
176 env(pay(gw, alice, USD(10)));
177 BEAST_EXPECT(to_string(env.balance("alice", USD)) == "10/USD(gw)");
178 BEAST_EXPECT(to_string(env.balance(gw, alice["USD"])) == "-10/USD(alice)");
179 }
180
181 // seq
182 {
183 Env env(*this);
184 env.fund(n, noripple("alice", gw));
185 BEAST_EXPECT(env.seq("alice") == 3);
186 BEAST_EXPECT(env.seq(gw) == 3);
187 }
188
189 // autofill
190 {
191 Env env(*this);
192 env.fund(n, "alice");
193 env.require(balance("alice", n));
194 env(noop("alice"), fee(1), ter(telINSUF_FEE_P));
195 env(noop("alice"), seq(none), ter(temMALFORMED));
196 env(noop("alice"), seq(none), fee(10), ter(temMALFORMED));
197 env(noop("alice"), fee(none), ter(temMALFORMED));
198 env(noop("alice"), sig(none), ter(temMALFORMED));
199 env(noop("alice"), fee(autofill));
200 env(noop("alice"), fee(autofill), seq(autofill));
201 env(noop("alice"), fee(autofill), seq(autofill), sig(autofill));
202 }
203 }
204
205 // Env::require
206 void
208 {
209 using namespace jtx;
210 Env env(*this);
211 auto const gw = Account("gw");
212 auto const USD = gw["USD"];
213 env.require(balance("alice", none));
214 env.require(balance("alice", XRP(none)));
215 env.fund(XRP(10000), "alice", gw);
216 env.close();
217 env.require(balance("alice", USD(none)));
218 env.trust(USD(100), "alice");
219 env.require(balance("alice", XRP(10000))); // fee refunded
220 env.require(balance("alice", USD(0)));
221 env(pay(gw, "alice", USD(10)), require(balance("alice", USD(10))));
222
223 env.require(nflags("alice", asfRequireDest));
224 env(fset("alice", asfRequireDest), require(flags("alice", asfRequireDest)));
225 env(fclear("alice", asfRequireDest), require(nflags("alice", asfRequireDest)));
226 }
227
228 // Signing with secp256k1 and ed25519 keys
229 void
231 {
232 using namespace jtx;
233
234 Env env{*this, testable_amendments()};
235 Account const alice("alice", KeyType::ed25519);
236 Account const bob("bob", KeyType::secp256k1);
237 Account const carol("carol");
238 env.fund(XRP(10000), alice, bob);
239
240 // Master key only
241 env(noop(alice));
242 env(noop(bob));
243 env(noop(alice), sig("alice"), ter(tefBAD_AUTH));
244 env(noop(alice), sig(Account("alice", KeyType::secp256k1)), ter(tefBAD_AUTH));
245 env(noop(bob), sig(Account("bob", KeyType::ed25519)), ter(tefBAD_AUTH));
246 env(noop(alice), sig(carol), ter(tefBAD_AUTH));
247
248 // Master and Regular key
249 env(regkey(alice, bob));
250 env(noop(alice));
251 env(noop(alice), sig(bob));
252 env(noop(alice), sig(alice));
253
254 // Regular key only
255 env(fset(alice, asfDisableMaster), sig(alice));
256 env(noop(alice));
257 env(noop(alice), sig(bob));
258 env(noop(alice), sig(alice), ter(tefMASTER_DISABLED));
259 env(fclear(alice, asfDisableMaster), sig(alice), ter(tefMASTER_DISABLED));
260 env(fclear(alice, asfDisableMaster), sig(bob));
261 env(noop(alice), sig(alice));
262 }
263
264 // Payment basics
265 void
267 {
268 using namespace jtx;
269 Env env(*this);
270 auto const gw = Account("gateway");
271 auto const USD = gw["USD"];
272
273 env.fund(XRP(10000), "alice", "bob", "carol", gw);
274 env.require(balance("alice", XRP(10000)));
275 env.require(balance("bob", XRP(10000)));
276 env.require(balance("carol", XRP(10000)));
277 env.require(balance(gw, XRP(10000)));
278
279 env(pay(env.master, "alice", XRP(1000)), fee(none), ter(temMALFORMED));
280 env(pay(env.master, "alice", XRP(1000)), fee(1), ter(telINSUF_FEE_P));
281 env(pay(env.master, "alice", XRP(1000)), seq(none), ter(temMALFORMED));
282 env(pay(env.master, "alice", XRP(1000)), seq(20), ter(terPRE_SEQ));
283 env(pay(env.master, "alice", XRP(1000)), sig(none), ter(temMALFORMED));
284 env(pay(env.master, "alice", XRP(1000)), sig("bob"), ter(tefBAD_AUTH));
285
286 env(pay(env.master, "dilbert", XRP(1000)), sig(env.master));
287
288 env.trust(USD(100), "alice", "bob", "carol");
289 env.require(owners("alice", 1), lines("alice", 1));
290 env(rate(gw, 1.05));
291
292 env(pay(gw, "carol", USD(50)));
293 env.require(balance("carol", USD(50)));
294 env.require(balance(gw, Account("carol")["USD"](-50)));
295
296 env(offer("carol", XRP(50), USD(50)), require(owners("carol", 2)));
297 env(pay("alice", "bob", any(USD(10))), ter(tecPATH_DRY));
298 env(pay("alice", "bob", any(USD(10))), paths(XRP), sendmax(XRP(10)), ter(tecPATH_PARTIAL));
299 env(pay("alice", "bob", any(USD(10))), paths(XRP), sendmax(XRP(20)));
300 env.require(balance("bob", USD(10)));
301 env.require(balance("carol", USD(39.5)));
302
303 env.memoize("eric");
304 env(regkey("alice", "eric"));
305 env(noop("alice"));
306 env(noop("alice"), sig("alice"));
307 env(noop("alice"), sig("eric"));
308 env(noop("alice"), sig("bob"), ter(tefBAD_AUTH));
309 env(fset("alice", asfDisableMaster), ter(tecNEED_MASTER_KEY));
310 env(fset("alice", asfDisableMaster), sig("eric"), ter(tecNEED_MASTER_KEY));
311 env.require(nflags("alice", asfDisableMaster));
312 env(fset("alice", asfDisableMaster), sig("alice"));
313 env.require(flags("alice", asfDisableMaster));
314 env(regkey("alice", disabled), ter(tecNO_ALTERNATIVE_KEY));
315 env(noop("alice"));
316 env(noop("alice"), sig("alice"), ter(tefMASTER_DISABLED));
317 env(noop("alice"), sig("eric"));
318 env(noop("alice"), sig("bob"), ter(tefBAD_AUTH));
319 env(fclear("alice", asfDisableMaster), sig("bob"), ter(tefBAD_AUTH));
320 env(fclear("alice", asfDisableMaster), sig("alice"), ter(tefMASTER_DISABLED));
321 env(fclear("alice", asfDisableMaster));
322 env.require(nflags("alice", asfDisableMaster));
323 env(regkey("alice", disabled));
324 env(noop("alice"), sig("eric"), ter(tefBAD_AUTH));
325 env(noop("alice"));
326 }
327
328 // Rudimentary test to ensure fail_hard
329 // transactions are neither queued nor
330 // held.
331 void
333 {
334 using namespace jtx;
335 Env env(*this);
336 auto const gw = Account("gateway");
337 auto const USD = gw["USD"];
338
339 auto const alice = Account{"alice"};
340 env.fund(XRP(10000), alice);
341
342 auto const localTxCnt = env.app().getOPs().getLocalTxCount();
343 auto const queueTxCount = env.app().getTxQ().getMetrics(*env.current()).txCount;
344 auto const openTxCount = env.current()->txCount();
345 BEAST_EXPECT(localTxCnt == 2 && queueTxCount == 0 && openTxCount == 2);
346
347 auto applyTxn = [&env](auto&&... txnArgs) {
348 auto jt = env.jt(txnArgs...);
349 Serializer s;
350 jt.stx->add(s);
351
353
354 args[jss::tx_blob] = strHex(s.slice());
355 args[jss::fail_hard] = true;
356
357 return env.rpc("json", "submit", args.toStyledString());
358 };
359
360 auto jr = applyTxn(noop(alice), fee(1));
361
362 BEAST_EXPECT(jr[jss::result][jss::engine_result] == "telINSUF_FEE_P");
363 BEAST_EXPECT(env.app().getTxQ().getMetrics(*env.current()).txCount == queueTxCount);
364 BEAST_EXPECT(env.app().getOPs().getLocalTxCount() == localTxCnt);
365 BEAST_EXPECT(env.current()->txCount() == openTxCount);
366
367 jr = applyTxn(noop(alice), sig("bob"));
368
369 BEAST_EXPECT(jr[jss::result][jss::engine_result] == "tefBAD_AUTH");
370 BEAST_EXPECT(env.app().getTxQ().getMetrics(*env.current()).txCount == queueTxCount);
371 BEAST_EXPECT(env.app().getOPs().getLocalTxCount() == localTxCnt);
372 BEAST_EXPECT(env.current()->txCount() == openTxCount);
373
374 jr = applyTxn(noop(alice), seq(20));
375
376 BEAST_EXPECT(jr[jss::result][jss::engine_result] == "terPRE_SEQ");
377 BEAST_EXPECT(env.app().getTxQ().getMetrics(*env.current()).txCount == queueTxCount);
378 BEAST_EXPECT(env.app().getOPs().getLocalTxCount() == localTxCnt);
379 BEAST_EXPECT(env.current()->txCount() == openTxCount);
380
381 jr = applyTxn(offer(alice, XRP(1000), USD(1000)));
382
383 BEAST_EXPECT(jr[jss::result][jss::engine_result] == "tecUNFUNDED_OFFER");
384 BEAST_EXPECT(env.app().getTxQ().getMetrics(*env.current()).txCount == queueTxCount);
385 BEAST_EXPECT(env.app().getOPs().getLocalTxCount() == localTxCnt);
386 BEAST_EXPECT(env.current()->txCount() == openTxCount);
387
388 jr = applyTxn(noop(alice), fee(drops(-10)));
389
390 BEAST_EXPECT(jr[jss::result][jss::engine_result] == "temBAD_FEE");
391 BEAST_EXPECT(env.app().getTxQ().getMetrics(*env.current()).txCount == queueTxCount);
392 BEAST_EXPECT(env.app().getOPs().getLocalTxCount() == localTxCnt);
393 BEAST_EXPECT(env.current()->txCount() == openTxCount);
394
395 jr = applyTxn(noop(alice));
396
397 BEAST_EXPECT(jr[jss::result][jss::engine_result] == "tesSUCCESS");
398 BEAST_EXPECT(env.app().getOPs().getLocalTxCount() == localTxCnt + 1);
399 BEAST_EXPECT(env.current()->txCount() == openTxCount + 1);
400 }
401
402 // Multi-sign basics
403 void
405 {
406 using namespace jtx;
407
408 Env env(*this);
409 env.fund(XRP(10000), "alice");
410 env(signers("alice", 1, {{"alice", 1}, {"bob", 2}}), ter(temBAD_SIGNER));
411 env(signers("alice", 1, {{"bob", 1}, {"carol", 2}}));
412 env(noop("alice"));
413
414 auto const baseFee = env.current()->fees().base;
415 env(noop("alice"), msig("bob"), fee(2 * baseFee));
416 env(noop("alice"), msig("carol"), fee(2 * baseFee));
417 env(noop("alice"), msig("bob", "carol"), fee(3 * baseFee));
418 env(noop("alice"),
419 msig("bob", "carol", "dilbert"),
420 fee(4 * baseFee),
422
423 env(signers("alice", none));
424 }
425
426 void
428 {
429 using namespace jtx;
430 // create syntax
431 ticket::create("alice", 1);
432
433 {
434 Env env(*this);
435 env.fund(XRP(10000), "alice");
436 env(noop("alice"), require(owners("alice", 0), tickets("alice", 0)));
437 env(ticket::create("alice", 1), require(owners("alice", 1), tickets("alice", 1)));
438 }
439 }
440
441 struct UDT
442 {
443 };
444
445 void
447 {
448 struct T
449 {
450 };
451 using namespace jtx;
452 JTx jt1;
453 // Test a straightforward
454 // property
455 BEAST_EXPECT(!jt1.get<int>());
456 jt1.set<int>(7);
457 BEAST_EXPECT(jt1.get<int>());
458 BEAST_EXPECT(*jt1.get<int>() == 7);
459 BEAST_EXPECT(!jt1.get<UDT>());
460
461 // Test that the property is
462 // replaced if it exists.
463 jt1.set<int>(17);
464 BEAST_EXPECT(jt1.get<int>());
465 BEAST_EXPECT(*jt1.get<int>() == 17);
466 BEAST_EXPECT(!jt1.get<UDT>());
467
468 // Test that modifying the
469 // returned prop is saved
470 *jt1.get<int>() = 42;
471 BEAST_EXPECT(jt1.get<int>());
472 BEAST_EXPECT(*jt1.get<int>() == 42);
473 BEAST_EXPECT(!jt1.get<UDT>());
474
475 // Test get() const
476 auto const& jt2 = jt1;
477 BEAST_EXPECT(jt2.get<int>());
478 BEAST_EXPECT(*jt2.get<int>() == 42);
479 BEAST_EXPECT(!jt2.get<UDT>());
480 }
481
482 void
484 {
485 using namespace jtx;
486 Env env(*this);
487 env.fund(XRP(100000), "alice");
488 auto jt1 = env.jt(noop("alice"));
489 BEAST_EXPECT(!jt1.get<std::uint16_t>());
490 auto jt2 = env.jt(noop("alice"), prop<std::uint16_t>(-1));
491 BEAST_EXPECT(jt2.get<std::uint16_t>());
492 BEAST_EXPECT(*jt2.get<std::uint16_t>() == 65535);
493 auto jt3 = env.jt(noop("alice"), prop<std::string>("Hello, world!"), prop<bool>(false));
494 BEAST_EXPECT(jt3.get<std::string>());
495 BEAST_EXPECT(*jt3.get<std::string>() == "Hello, world!");
496 BEAST_EXPECT(jt3.get<bool>());
497 BEAST_EXPECT(!*jt3.get<bool>());
498 }
499
500 void
502 {
503 struct T
504 {
505 };
506 using namespace jtx;
507 JTx jt1;
508 jt1.set<int>(7);
509 BEAST_EXPECT(jt1.get<int>());
510 BEAST_EXPECT(*jt1.get<int>() == 7);
511 BEAST_EXPECT(!jt1.get<UDT>());
512 JTx jt2(jt1);
513 BEAST_EXPECT(jt2.get<int>());
514 BEAST_EXPECT(*jt2.get<int>() == 7);
515 BEAST_EXPECT(!jt2.get<UDT>());
516 JTx jt3;
517 jt3 = jt1;
518 BEAST_EXPECT(jt3.get<int>());
519 BEAST_EXPECT(*jt3.get<int>() == 7);
520 BEAST_EXPECT(!jt3.get<UDT>());
521 }
522
523 void
525 {
526 struct T
527 {
528 };
529 using namespace jtx;
530 JTx jt1;
531 jt1.set<int>(7);
532 BEAST_EXPECT(jt1.get<int>());
533 BEAST_EXPECT(*jt1.get<int>() == 7);
534 BEAST_EXPECT(!jt1.get<UDT>());
535 JTx jt2(std::move(jt1));
536 BEAST_EXPECT(!jt1.get<int>()); // NOLINT(bugprone-use-after-move)
537 BEAST_EXPECT(!jt1.get<UDT>()); // NOLINT(bugprone-use-after-move)
538 BEAST_EXPECT(jt2.get<int>());
539 BEAST_EXPECT(*jt2.get<int>() == 7);
540 BEAST_EXPECT(!jt2.get<UDT>());
541 jt1 = std::move(jt2);
542 BEAST_EXPECT(!jt2.get<int>()); // NOLINT(bugprone-use-after-move)
543 BEAST_EXPECT(!jt2.get<UDT>()); // NOLINT(bugprone-use-after-move)
544 BEAST_EXPECT(jt1.get<int>());
545 BEAST_EXPECT(*jt1.get<int>() == 7);
546 BEAST_EXPECT(!jt1.get<UDT>());
547 }
548
549 void
551 {
552 using namespace jtx;
553 Env env(*this);
554 env.fund(XRP(10000), "alice");
555 env(noop("alice"), memo_data("data"));
556 env(noop("alice"), memo_format("format"));
557 env(noop("alice"), memo_type("type"));
558 env(noop("alice"), memo("data", "format", "type"));
559 env(noop("alice"), memo("data1", "format1", "type1"), memo("data2", "format2", "type2"));
560 }
561
562 void
564 {
565 using namespace jtx;
566 Env env(*this);
567 JTx jt(noop("alice"));
568 memo("data", "format", "type")(env, jt);
569
570 auto const& memo = jt.jv["Memos"][0u]["Memo"];
571 BEAST_EXPECT(memo["MemoData"].asString() == strHex(std::string("data")));
572 BEAST_EXPECT(memo["MemoFormat"].asString() == strHex(std::string("format")));
573 BEAST_EXPECT(memo["MemoType"].asString() == strHex(std::string("type")));
574 }
575
576 void
578 {
579 using namespace jtx;
580 Env env(*this);
581 auto seq = env.current()->seq();
582 BEAST_EXPECT(seq == env.closed()->seq() + 1);
583 env.close();
584 BEAST_EXPECT(env.closed()->seq() == seq);
585 BEAST_EXPECT(env.current()->seq() == seq + 1);
586 env.close();
587 BEAST_EXPECT(env.closed()->seq() == seq + 1);
588 BEAST_EXPECT(env.current()->seq() == seq + 2);
589 }
590
591 void
593 {
594 using namespace jtx;
595 Env env(*this);
596 env.close();
597 env.close();
598 env.fund(XRP(100000), "alice", "bob");
599 env.close();
600 env(pay("alice", "bob", XRP(100)));
601 env.close();
602 env(noop("alice"));
603 env.close();
604 env(noop("bob"));
605 }
606
607 void
609 {
610 using namespace jtx;
611 Env env(*this);
612 auto const gw = Account("gw");
613 auto const USD = gw["USD"];
614 env.fund(XRP(10000), "alice", "bob");
615 env.close();
616 env.json(
617 pay("alice", "bob", USD(10)),
618 path(Account("alice")),
619 path("bob"),
620 path(USD),
621 path(~XRP),
622 path(~USD),
623 path("bob", USD, ~XRP, ~USD));
624 }
625
626 // Test that jtx can re-sign a transaction that's already been signed.
627 void
629 {
630 using namespace jtx;
631 Env env(*this);
632
633 env.fund(XRP(10000), "alice");
634 auto const baseFee = env.current()->fees().base;
635 std::uint32_t const aliceSeq = env.seq("alice");
636
637 // Sign jsonNoop.
638 Json::Value const jsonNoop =
639 env.json(noop("alice"), fee(baseFee), seq(aliceSeq), sig("alice"));
640 // Re-sign jsonNoop.
641 JTx const jt = env.jt(jsonNoop);
642 env(jt);
643 }
644
645 void
647 {
648 using namespace jtx;
649 Env env(*this);
650 Env_ss envs(env);
651
652 auto const baseFee = env.current()->fees().base;
653
654 auto const alice = Account("alice");
655 env.fund(XRP(10000), alice);
656
657 {
658 envs(noop(alice), fee(none), seq(none))();
659
660 // Make sure we get the right account back.
661 auto tx = env.tx();
662 if (BEAST_EXPECT(tx))
663 {
664 BEAST_EXPECT(tx->getAccountID(sfAccount) == alice.id());
665 BEAST_EXPECT(tx->getTxnType() == ttACCOUNT_SET);
666 }
667 }
668
669 {
670 auto params = Json::Value(Json::nullValue);
671 envs(noop(alice), fee(none), seq(none))(params);
672
673 // Make sure we get the right account back.
674 auto tx = env.tx();
675 if (BEAST_EXPECT(tx))
676 {
677 BEAST_EXPECT(tx->getAccountID(sfAccount) == alice.id());
678 BEAST_EXPECT(tx->getTxnType() == ttACCOUNT_SET);
679 }
680 }
681
682 {
683 auto params = Json::Value(Json::objectValue);
684 // Force the factor low enough to fail
685 params[jss::fee_mult_max] = 1;
686 params[jss::fee_div_max] = 2;
687
688 auto const expectedErrorString = "Fee of " + std::to_string(baseFee.drops()) +
689 " exceeds the requested tx limit of " + std::to_string(baseFee.drops() / 2);
690 envs(noop(alice), fee(none), seq(none), rpc(rpcHIGH_FEE, expectedErrorString))(params);
691
692 auto tx = env.tx();
693 BEAST_EXPECT(!tx);
694 }
695 }
696
697 void
699 {
700 testcase("Env features");
701 using namespace jtx;
702 auto const supported = testable_amendments();
703
704 // this finds a feature that is not in
705 // the supported amendments list and tests that it can be
706 // enabled explicitly
707
708 auto const neverSupportedFeat = [&]() -> std::optional<uint256> {
709 auto const n = supported.size();
710 for (size_t i = 0; i < n; ++i)
711 {
712 if (!supported[i])
713 return bitsetIndexToFeature(i);
714 }
715
716 return std::nullopt;
717 }();
718
719 if (!neverSupportedFeat)
720 {
721 log << "No unsupported features found - skipping test." << std::endl;
722 pass();
723 return;
724 }
725
726 auto hasFeature = [](Env& env, uint256 const& f) {
727 return (env.app().config().features.contains(f));
728 };
729
730 {
731 // default Env has all supported features
732 Env env{*this};
733 BEAST_EXPECT(supported.count() == env.app().config().features.size());
735 supported, [&](uint256 const& f) { this->BEAST_EXPECT(hasFeature(env, f)); });
736 }
737
738 {
739 // a Env FeatureBitset has *only* those features
740 Env env{*this, FeatureBitset{featureDynamicMPT | featureTokenEscrow}};
741 BEAST_EXPECT(env.app().config().features.size() == 2);
742 foreachFeature(supported, [&](uint256 const& f) {
743 bool const has = (f == featureDynamicMPT || f == featureTokenEscrow);
744 this->BEAST_EXPECT(has == hasFeature(env, f));
745 });
746 }
747
748 auto const missingSomeFeatures =
749 testable_amendments() - featureDynamicMPT - featureTokenEscrow;
750 BEAST_EXPECT(missingSomeFeatures.count() == (supported.count() - 2));
751 {
752 // a Env supported_features_except is missing *only* those features
753 Env env{*this, missingSomeFeatures};
754 BEAST_EXPECT(env.app().config().features.size() == (supported.count() - 2));
755 foreachFeature(supported, [&](uint256 const& f) {
756 bool const hasnot = (f == featureDynamicMPT || f == featureTokenEscrow);
757 this->BEAST_EXPECT(hasnot != hasFeature(env, f));
758 });
759 }
760
761 {
762 // add a feature that is NOT in the supported amendments list
763 // along with a list of explicit amendments
764 // the unsupported feature should be enabled along with
765 // the two supported ones
766 Env env{
767 *this, FeatureBitset{featureDynamicMPT, featureTokenEscrow, *neverSupportedFeat}};
768
769 // this app will have just 2 supported amendments and
770 // one additional never supported feature flag
771 BEAST_EXPECT(env.app().config().features.size() == (2 + 1));
772 BEAST_EXPECT(hasFeature(env, *neverSupportedFeat));
773
774 foreachFeature(supported, [&](uint256 const& f) {
775 bool const has = (f == featureDynamicMPT || f == featureTokenEscrow);
776 this->BEAST_EXPECT(has == hasFeature(env, f));
777 });
778 }
779
780 {
781 // add a feature that is NOT in the supported amendments list
782 // and omit a few standard amendments
783 // the unsupported features should be enabled
784 Env env{*this, missingSomeFeatures | FeatureBitset{*neverSupportedFeat}};
785
786 // this app will have all supported amendments minus 2 and then the
787 // one additional never supported feature flag
788 BEAST_EXPECT(env.app().config().features.size() == (supported.count() - 2 + 1));
789 BEAST_EXPECT(hasFeature(env, *neverSupportedFeat));
790 foreachFeature(supported, [&](uint256 const& f) {
791 bool const hasnot = (f == featureDynamicMPT || f == featureTokenEscrow);
792 this->BEAST_EXPECT(hasnot != hasFeature(env, f));
793 });
794 }
795
796 {
797 // add a feature that is NOT in the supported amendments list
798 // along with all supported amendments
799 // the unsupported features should be enabled
800 Env env{*this, testable_amendments().set(*neverSupportedFeat)};
801
802 // this app will have all supported amendments and then the
803 // one additional never supported feature flag
804 BEAST_EXPECT(env.app().config().features.size() == (supported.count() + 1));
805 BEAST_EXPECT(hasFeature(env, *neverSupportedFeat));
807 supported, [&](uint256 const& f) { this->BEAST_EXPECT(hasFeature(env, f)); });
808 }
809 }
810
811 void
813 {
814 except([this] {
815 jtx::Env const env{
816 *this,
818 (*cfg).deprecatedClearSection("port_rpc");
819 return cfg;
820 }),
821 nullptr,
823 });
824 pass();
825 }
826
827 void
828 run() override
829 {
830 testAccount();
831 testAmount();
832 testEnv();
833 testRequire();
834 testKeyType();
835 testPayments();
836 testFailHard();
838 testTicket();
840 testProp();
841 testJTxCopy();
842 testJTxMove();
843 testMemo();
845 testAdvance();
846 testClose();
847 testPath();
850 testFeatures();
852 }
853};
854
855BEAST_DEFINE_TESTSUITE(Env, jtx, xrpl);
856
857} // namespace test
858} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
A testsuite class.
Definition suite.h:51
log_os< char > log
Logging output stream.
Definition suite.h:147
void pass()
Record a successful test condition.
Definition suite.h:497
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
bool except(F &&f, String const &reason)
Definition suite.h:434
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:519
virtual Config & config()=0
std::unordered_set< uint256, beast::uhash<> > features
Definition Config.h:261
FeatureBitset & set(uint256 const &f, bool value=true)
Definition Feature.h:257
virtual std::size_t getLocalTxCount()=0
Slice slice() const noexcept
Definition Serializer.h:44
virtual TxQ & getTxQ()=0
virtual NetworkOPs & getOPs()=0
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition TxQ.cpp:1692
static std::string to_string(T const &t)
Definition Env_test.cpp:25
void run() override
Runs the suite.
Definition Env_test.cpp:828
static void testAccount()
Definition Env_test.cpp:32
void testExceptionalShutdown()
Definition Env_test.cpp:812
Immutable cryptographic account descriptor.
Definition Account.h:19
A transaction testing environment wrapper.
Definition Env_ss.h:14
A transaction testing environment.
Definition Env.h:122
Application & app()
Definition Env.h:259
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:100
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:94
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:270
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:249
Account const & master
Definition Env.h:126
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition Env.h:549
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:168
Json::Value json(JsonValue &&jv, FN const &... fN)
Create JSON from parameters.
Definition Env.h:575
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:301
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:847
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
Definition Env.cpp:505
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:141
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:588
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:329
A balance matches.
Definition balance.h:19
Set the fee on a JTx.
Definition fee.h:17
Match set account flags.
Definition flags.h:108
Add a memo to a JTx.
Definition memo.h:15
Set a multisignature on a JTx.
Definition multisign.h:41
Match clear account flags.
Definition flags.h:124
Match the number of items in the account's owner directory.
Definition owners.h:52
Add a path.
Definition paths.h:38
Set Paths, SendMax on a JTx.
Definition paths.h:15
Check a set of conditions.
Definition require.h:46
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
Sets the SendMax on a JTx.
Definition sendmax.h:13
Set the regular signature on a JTx.
Definition sig.h:15
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
T endl(T... args)
T is_same_v
@ nullValue
'null' value
Definition json_value.h:19
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
any_t const any
Returns an amount representing "any issuer".
Definition amount.cpp:116
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Definition rate.cpp:13
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:95
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
static autofill_t const autofill
Definition tags.h:22
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
static none_t const none
Definition tags.h:14
FeatureBitset testable_amendments()
Definition Env.h:78
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:44
owner_count< ltRIPPLE_STATE > lines
Match the number of trust lines in the account's owner directory.
Definition owners.h:67
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
static disabled_t const disabled
Definition tags.h:30
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telINSUF_FEE_P
Definition TER.h:37
@ terPRE_SEQ
Definition TER.h:201
@ terNO_ACCOUNT
Definition TER.h:197
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
@ tefMASTER_DISABLED
Definition TER.h:157
@ tefBAD_SIGNATURE
Definition TER.h:159
@ tefBAD_AUTH
Definition TER.h:149
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
void foreachFeature(FeatureBitset bs, F &&f)
Definition Feature.h:373
@ temMALFORMED
Definition TER.h:67
@ temBAD_SIGNER
Definition TER.h:95
@ tecPATH_PARTIAL
Definition TER.h:263
@ tecNEED_MASTER_KEY
Definition TER.h:289
@ tecPATH_DRY
Definition TER.h:275
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:277
uint256 bitsetIndexToFeature(size_t i)
@ rpcHIGH_FEE
Definition ErrorCodes.h:38
std::size_t txCount
Number of transactions in the queue.
Definition TxQ.h:149
Amount specifier with an option for any issuer.
Execution context for applying a JSON transaction.
Definition JTx.h:25
Prop * get()
Return a property if it exists.
Definition JTx.h:72
void set(std::unique_ptr< basic_prop > p)
Set a property If the property already exists, it is replaced.
Definition JTx.h:101
Json::Value jv
Definition JTx.h:26
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
Set a property on a JTx.
Definition prop.h:14
Set the sequence number on a JTx.
Definition seq.h:14
T to_string(T... args)