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