rippled
Loading...
Searching...
No Matches
XChain_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/attester.h>
4#include <test/jtx/multisign.h>
5#include <test/jtx/xchain_bridge.h>
6
7#include <xrpl/beast/unit_test/suite.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/Indexes.h>
10#include <xrpl/protocol/Issue.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STXChainBridge.h>
13#include <xrpl/protocol/Serializer.h>
14#include <xrpl/protocol/TER.h>
15#include <xrpl/protocol/XChainAttestations.h>
16
17#include <functional>
18#include <limits>
19#include <optional>
20#include <random>
21#include <string>
22#include <tuple>
23#include <variant>
24#include <vector>
25
26namespace xrpl::test {
27
28// SEnv class - encapsulate jtx::Env to make it more user-friendly,
29// for example having APIs that return a *this reference so that calls can be
30// chained (fluent interface) allowing to create an environment and use it
31// without encapsulating it in a curly brace block.
32// ---------------------------------------------------------------------------
33template <class T>
34struct SEnv
35{
37
39 T& s,
41 FeatureBitset features,
42 std::unique_ptr<Logs> logs = nullptr,
44 : env_(s, std::move(config), features, std::move(logs), thresh)
45 {
46 }
47
48 SEnv&
50 {
51 env_.close();
52 return *this;
53 }
54
55 SEnv&
56 enableFeature(uint256 const feature)
57 {
58 env_.enableFeature(feature);
59 return *this;
60 }
61
62 SEnv&
63 disableFeature(uint256 const feature)
64 {
65 env_.app().config().features.erase(feature);
66 return *this;
67 }
68
69 template <class Arg, class... Args>
70 SEnv&
71 fund(STAmount const& amount, Arg const& arg, Args const&... args)
72 {
73 env_.fund(amount, arg, args...);
74 return *this;
75 }
76
77 template <class JsonValue, class... FN>
78 SEnv&
79 tx(JsonValue&& jv, FN const&... fN)
80 {
81 env_(std::forward<JsonValue>(jv), fN...);
82 return *this;
83 }
84
85 template <class... FN>
86 SEnv&
87 multiTx(jtx::JValueVec const& jvv, FN const&... fN)
88 {
89 for (auto const& jv : jvv)
90 env_(jv, fN...);
91 return *this;
92 }
93
94 TER
95 ter() const
96 {
97 return env_.ter();
98 }
99
101 balance(jtx::Account const& account) const
102 {
103 return env_.balance(account).value();
104 }
105
107 balance(jtx::Account const& account, Issue const& issue) const
108 {
109 return env_.balance(account, issue).value();
110 }
111
114 {
115 return env_.current()->fees().accountReserve(count);
116 }
117
120 {
121 return env_.current()->fees().base;
122 }
123
125 account(jtx::Account const& account)
126 {
127 return env_.le(account);
128 }
129
131 bridge(Json::Value const& jvb)
132 {
133 STXChainBridge const b(jvb);
134
136 if (auto r = env_.le(keylet::bridge(b, ct)))
137 {
138 if ((*r)[sfXChainBridge] == b)
139 return r;
140 }
141 return nullptr;
142 };
143 if (auto r = tryGet(STXChainBridge::ChainType::locking))
144 return r;
146 }
147
150 {
151 return (*bridge(jvb))[sfXChainAccountClaimCount];
152 }
153
156 {
157 return (*bridge(jvb))[sfXChainClaimID];
158 }
159
162 {
164 }
165
171};
172
173// XEnv class used for XChain tests. The only difference with SEnv<T> is that it
174// funds some default accounts, and that it enables `testable_amendments() |
175// FeatureBitset{featureXChainBridge}` by default.
176// -----------------------------------------------------------------------------
177template <class T>
178struct XEnv : public jtx::XChainBridgeObjects, public SEnv<T>
179{
180 XEnv(T& s, bool side = false) : SEnv<T>(s, jtx::envconfig(), features)
181 {
182 using namespace jtx;
183 STAmount const xrp_funds{XRP(10000)};
184
185 if (!side)
186 {
187 this->fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
188
189 // Signer's list must match the attestation signers
190 // env_(jtx::signers(mcDoor, quorum, signers));
191 for (auto& s : signers)
192 this->fund(xrp_funds, s.account);
193 }
194 else
195 {
196 this->fund(xrp_funds, scDoor, scAlice, scBob, scCarol, scGw, scAttester, scReward);
197
198 for (auto& ra : payees)
199 this->fund(xrp_funds, ra);
200
201 for (auto& s : signers)
202 this->fund(xrp_funds, s.account);
203
204 // Signer's list must match the attestation signers
205 // env_(jtx::signers(Account::master, quorum, signers));
206 }
207 this->close();
208 }
209};
210
211// Tracks the xrp balance for one account
212template <class T>
214{
218
219 Balance(T& env, jtx::Account const& account) : account_(account), env_(env)
220 {
221 startAmount = env_.balance(account_);
222 }
223
225 diff() const
226 {
227 return env_.balance(account_) - startAmount;
228 }
229};
230
231// Tracks the xrp balance for multiple accounts involved in a crosss-chain
232// transfer
233template <class T>
235{
237
240 balance payer_; // pays the rewards
241 std::vector<balance> reward_accounts; // receives the reward
243
245 T& env,
246 jtx::Account const& from_acct,
247 jtx::Account const& to_acct,
248 jtx::Account const& payer,
249 jtx::Account const* payees,
250 size_t num_payees,
251 bool withClaim)
252 : from_(env, from_acct)
253 , to_(env, to_acct)
254 , payer_(env, payer)
255 , reward_accounts([&]() {
257 r.reserve(num_payees);
258 for (size_t i = 0; i < num_payees; ++i)
259 r.emplace_back(env, payees[i]);
260 return r;
261 }())
262 , txFees_(withClaim ? env.env_.current()->fees().base : XRPAmount(0))
263 {
264 }
265
267 T& env,
268 jtx::Account const& from_acct,
269 jtx::Account const& to_acct,
270 jtx::Account const& payer,
271 std::vector<jtx::Account> const& payees,
272 bool withClaim)
273 : BalanceTransfer(env, from_acct, to_acct, payer, &payees[0], payees.size(), withClaim)
274 {
275 }
276
277 bool
278 payees_received(STAmount const& reward) const
279 {
280 return std::all_of(reward_accounts.begin(), reward_accounts.end(), [&](balance const& b) {
281 return b.diff() == reward;
282 });
283 }
284
285 bool
286 check_most_balances(STAmount const& amt, STAmount const& reward)
287 {
288 return from_.diff() == -amt && to_.diff() == amt && payees_received(reward);
289 }
290
291 bool
292 has_happened(STAmount const& amt, STAmount const& reward, bool check_payer = true)
293 {
294 auto reward_cost = multiply(reward, STAmount(reward_accounts.size()), reward.issue());
295 return check_most_balances(amt, reward) &&
296 (!check_payer || payer_.diff() == -(reward_cost + txFees_));
297 }
298
299 bool
301 {
302 return check_most_balances(STAmount(0), STAmount(0)) &&
303 payer_.diff() <= txFees_; // could have paid fee for failed claim
304 }
305};
306
308{
315 uint32_t quorum;
318
319 template <class ENV>
320 void
321 initBridge(ENV& mcEnv, ENV& scEnv)
322 {
324
325 auto const optAccountCreate = [&]() -> std::optional<STAmount> {
326 if (issueA != xrpIssue() || issueB != xrpIssue())
327 return {};
328 return minAccountCreate;
329 }();
330 mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate))
332 .close();
333
334 scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate))
336 .close();
337 }
338};
339
341{
344 {
345 return XEnv(*this).env_.current()->fees().accountReserve(count);
346 }
347
350 {
351 return XEnv(*this).env_.current()->fees().base;
352 }
353
354 void
356 {
357 auto jBridge = create_bridge(mcDoor)[sfXChainBridge.jsonName];
358 bool exceptionPresent = false;
359 try
360 {
361 exceptionPresent = false;
362 [[maybe_unused]] STXChainBridge const testBridge1(jBridge);
363 }
364 catch (std::exception& ec)
365 {
366 exceptionPresent = true;
367 }
368
369 BEAST_EXPECT(!exceptionPresent);
370
371 try
372 {
373 exceptionPresent = false;
374 jBridge["Extra"] = 1;
375 [[maybe_unused]] STXChainBridge const testBridge2(jBridge);
376 }
377 catch ([[maybe_unused]] std::exception& ec)
378 {
379 exceptionPresent = true;
380 }
381
382 BEAST_EXPECT(exceptionPresent);
383 }
384
385 void
387 {
388 XRPAmount const res1 = reserve(1);
389
390 using namespace jtx;
391 testcase("Create Bridge");
392
393 // Normal create_bridge => should succeed
394 XEnv(*this).tx(create_bridge(mcDoor)).close();
395
396 // Bridge not owned by one of the door account.
398
399 // Create twice on the same account
401
402 // Create USD bridge Alice -> Bob ... should succeed
403 XEnv(*this).tx(
404 create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
405 ter(tesSUCCESS));
406
407 // Create USD bridge, Alice is both the locking door and locking issue,
408 // ... should fail.
409 XEnv(*this).tx(
412
413 // Bridge where the two door accounts are equal.
414 XEnv(*this).tx(
415 create_bridge(mcBob, bridge(mcBob, mcGw["USD"], mcBob, mcGw["USD"])),
417
418 // Both door accounts are on the same chain. This is not allowed.
419 // Although it doesn't violate any invariants, it's not a useful thing
420 // to do and it complicates the "add claim" transactions.
421 XEnv(*this)
422 .tx(create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])))
423 .close()
424 .tx(create_bridge(mcBob, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
426 .close();
427
428 // Create a bridge on an account with exactly enough balance to
429 // meet the new reserve should succeed
430 XEnv(*this)
431 .fund(res1, mcuDoor) // exact reserve for account + 1 object
432 .close()
434
435 // Create a bridge on an account with no enough balance to meet the
436 // new reserve
437 XEnv(*this)
438 .fund(res1 - 1, mcuDoor) // just short of required reserve
439 .close()
441
442 // Reward amount is non-xrp
443 XEnv(*this).tx(
445
446 // Reward amount is XRP and negative
447 XEnv(*this).tx(
449
450 // Reward amount is 1 xrp => should succeed
452
453 // Min create amount is 1 xrp, mincreate is 1 xrp => should succeed
454 XEnv(*this).tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)), ter(tesSUCCESS));
455
456 // Min create amount is non-xrp
457 XEnv(*this).tx(
458 create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)),
460
461 // Min create amount is zero (should fail, currently succeeds)
462 XEnv(*this).tx(
463 create_bridge(mcDoor, jvb, XRP(1), XRP(0)),
465
466 // Min create amount is negative
467 XEnv(*this).tx(
468 create_bridge(mcDoor, jvb, XRP(1), XRP(-1)),
470
471 // coverage test: BridgeCreate::preflight() - create bridge when feature
472 // disabled.
473 {
474 Env env(*this, testable_amendments() - featureXChainBridge);
476 }
477
478 // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER.
479 XEnv(*this).tx(
482
483 // coverage test: create_bridge transaction with incorrect flag
484 XEnv(*this).tx(create_bridge(mcAlice, jvb), txflags(tfFillOrKill), ter(temINVALID_FLAG));
485
486 // coverage test: create_bridge transaction with xchain feature disabled
487 XEnv(*this)
488 .disableFeature(featureXChainBridge)
490 }
491
492 void
494 {
540 using namespace jtx;
541 testcase("Bridge create constraints");
542 XEnv env(*this, true);
543 auto& A = scAlice;
544 auto& B = scBob;
545 auto& C = scCarol;
546 auto AUSD = A["USD"];
547 auto BUSD = B["USD"];
548 auto CUSD = C["USD"];
549 auto GUSD = scGw["USD"];
550 auto AEUR = A["EUR"];
551 auto BEUR = B["EUR"];
552 auto CEUR = C["EUR"];
553 auto GEUR = scGw["EUR"];
554
555 // Accounts to own single bridges
556 Account const a1("a1");
557 Account const a2("a2");
558 Account const a3("a3");
559 Account const a4("a4");
560 Account const a5("a5");
561 Account const a6("a6");
562
563 env.fund(XRP(10000), a1, a2, a3, a4, a5, a6);
564 env.close();
565
566 // Add a bridge on two different accounts with the same locking and
567 // issuing assets
568 env.tx(create_bridge(a1, bridge(a1, GUSD, B, BUSD))).close();
569 env.tx(create_bridge(a2, bridge(a2, GUSD, B, BUSD))).close();
570
571 // Add the exact same bridge to two different accounts (one locking
572 // account and one issuing)
573 env.tx(create_bridge(a3, bridge(a3, GUSD, a4, a4["USD"]))).close();
574 env.tx(create_bridge(a4, bridge(a3, GUSD, a4, a4["USD"])), ter(tecDUPLICATE)).close();
575
576 // Add the exact same bridge to two different accounts (one issuing
577 // account and one locking - opposite order from the test above)
578 env.tx(create_bridge(a5, bridge(a6, GUSD, a5, a5["USD"]))).close();
579 env.tx(create_bridge(a6, bridge(a6, GUSD, a5, a5["USD"])), ter(tecDUPLICATE)).close();
580
581 // Test case 1 ~ 5, create bridges
582 auto const goodBridge1 = bridge(A, GUSD, B, BUSD);
583 auto const goodBridge2 = bridge(A, BUSD, C, CUSD);
584 env.tx(create_bridge(B, goodBridge1)).close();
585 // Issuing asset is the same, this is a duplicate
586 env.tx(create_bridge(B, bridge(A, GEUR, B, BUSD)), ter(tecDUPLICATE)).close();
587 env.tx(create_bridge(A, goodBridge2), ter(tesSUCCESS)).close();
588 // Locking asset is the same - this is a duplicate
589 env.tx(create_bridge(A, bridge(A, BUSD, B, BEUR)), ter(tecDUPLICATE)).close();
590 // Locking asset is USD - this is a duplicate even tho it has a
591 // different issuer
592 env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE)).close();
593
594 // Test case 6 and 7, commits
595 env.tx(trust(C, BUSD(1000)))
596 .tx(trust(A, BUSD(1000)))
597 .close()
598 .tx(pay(B, C, BUSD(1000)))
599 .close();
600 auto const aBalanceStart = env.balance(A, BUSD);
601 auto const cBalanceStart = env.balance(C, BUSD);
602 env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close();
603 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(0));
604 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50));
605 env.tx(xchain_commit(C, goodBridge2, 1, BUSD(60))).close();
606 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(60));
607 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50 - 60));
608
609 // bridge modify test cases
610 env.tx(bridge_modify(B, goodBridge1, XRP(33), std::nullopt)).close();
611 BEAST_EXPECT((*env.bridge(goodBridge1))[sfSignatureReward] == XRP(33));
612 env.tx(bridge_modify(A, goodBridge2, XRP(44), std::nullopt)).close();
613 BEAST_EXPECT((*env.bridge(goodBridge2))[sfSignatureReward] == XRP(44));
614 }
615
616 void
618 {
619 using namespace jtx;
620 testcase("Create Bridge Matrix");
621
622 // Test all combinations of the following:`
623 // --------------------------------------
624 // - Locking chain is IOU with locking chain door account as issuer
625 // - Locking chain is IOU with issuing chain door account that
626 // exists on the locking chain as issuer
627 // - Locking chain is IOU with issuing chain door account that does
628 // not exists on the locking chain as issuer
629 // - Locking chain is IOU with non-door account (that exists on the
630 // locking chain ledger) as issuer
631 // - Locking chain is IOU with non-door account (that does not exist
632 // exists on the locking chain ledger) as issuer
633 // - Locking chain is XRP
634 // ---------------------------------------------------------------------
635 // - Issuing chain is IOU with issuing chain door account as the
636 // issuer
637 // - Issuing chain is IOU with locking chain door account (that
638 // exists on the issuing chain ledger) as the issuer
639 // - Issuing chain is IOU with locking chain door account (that does
640 // not exist on the issuing chain ledger) as the issuer
641 // - Issuing chain is IOU with non-door account (that exists on the
642 // issuing chain ledger) as the issuer
643 // - Issuing chain is IOU with non-door account (that does not
644 // exists on the issuing chain ledger) as the issuer
645 // - Issuing chain is XRP and issuing chain door account is not the
646 // root account
647 // - Issuing chain is XRP and issuing chain door account is the root
648 // account
649 // ---------------------------------------------------------------------
650 // That's 42 combinations. The only combinations that should succeed
651 // are:
652 // - Locking chain is any IOU,
653 // - Issuing chain is IOU with issuing chain door account as the
654 // issuer
655 // Locking chain is XRP,
656 // - Issuing chain is XRP with issuing chain is the root account.
657 // ---------------------------------------------------------------------
658 Account a("a"), b("b");
659 Issue ia, ib;
660
661 std::tuple lcs{
663 "Locking chain is IOU(locking chain door)",
664 [&](auto& env, bool) {
665 a = mcDoor;
666 ia = mcDoor["USD"];
667 }),
669 "Locking chain is IOU(issuing chain door funded on locking "
670 "chain)",
671 [&](auto& env, bool shouldFund) {
672 a = mcDoor;
673 ia = scDoor["USD"];
674 if (shouldFund)
675 env.fund(XRP(10000), scDoor);
676 }),
678 "Locking chain is IOU(issuing chain door account unfunded "
679 "on locking chain)",
680 [&](auto& env, bool) {
681 a = mcDoor;
682 ia = scDoor["USD"];
683 }),
685 "Locking chain is IOU(bob funded on locking chain)",
686 [&](auto& env, bool) {
687 a = mcDoor;
688 ia = mcGw["USD"];
689 }),
691 "Locking chain is IOU(bob unfunded on locking chain)",
692 [&](auto& env, bool) {
693 a = mcDoor;
694 ia = mcuGw["USD"];
695 }),
696 std::make_pair("Locking chain is XRP", [&](auto& env, bool) {
697 a = mcDoor;
698 ia = xrpIssue();
699 })};
700
701 std::tuple ics{
703 "Issuing chain is IOU(issuing chain door account)",
704 [&](auto& env, bool) {
705 b = scDoor;
706 ib = scDoor["USD"];
707 }),
709 "Issuing chain is IOU(locking chain door funded on issuing "
710 "chain)",
711 [&](auto& env, bool shouldFund) {
712 b = scDoor;
713 ib = mcDoor["USD"];
714 if (shouldFund)
715 env.fund(XRP(10000), mcDoor);
716 }),
718 "Issuing chain is IOU(locking chain door unfunded on "
719 "issuing chain)",
720 [&](auto& env, bool) {
721 b = scDoor;
722 ib = mcDoor["USD"];
723 }),
725 "Issuing chain is IOU(bob funded on issuing chain)",
726 [&](auto& env, bool) {
727 b = scDoor;
728 ib = mcGw["USD"];
729 }),
731 "Issuing chain is IOU(bob unfunded on issuing chain)",
732 [&](auto& env, bool) {
733 b = scDoor;
734 ib = mcuGw["USD"];
735 }),
737 "Issuing chain is XRP and issuing chain door account is "
738 "not the root account",
739 [&](auto& env, bool) {
740 b = scDoor;
741 ib = xrpIssue();
742 }),
744 "Issuing chain is XRP and issuing chain door account is "
745 "the root account ",
746 [&](auto& env, bool) {
747 b = Account::master;
748 ib = xrpIssue();
749 })};
750
751 std::vector<std::pair<int, int>> expected_result{
794
796
797 auto testcase = [&](auto const& lc, auto const& ic) {
798 XEnv mcEnv(*this);
799 XEnv scEnv(*this, true);
800
801 lc.second(mcEnv, true);
802 lc.second(scEnv, false);
803
804 ic.second(mcEnv, false);
805 ic.second(scEnv, true);
806
807 auto const& expected = expected_result[test_result.size()];
808
809 mcEnv.tx(create_bridge(a, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.first)));
810 TER const mcTER = mcEnv.env_.ter();
811
812 scEnv.tx(create_bridge(b, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.second)));
813 TER const scTER = scEnv.env_.ter();
814
815 bool const pass = isTesSuccess(mcTER) && isTesSuccess(scTER);
816
817 test_result.emplace_back(mcTER, scTER, pass);
818 };
819
820 auto apply_ics = [&](auto const& lc, auto const& ics) {
821 std::apply([&](auto const&... ic) { (testcase(lc, ic), ...); }, ics);
822 };
823
824 std::apply([&](auto const&... lc) { (apply_ics(lc, ics), ...); }, lcs);
825
826#if GENERATE_MTX_OUTPUT
827 // optional output of matrix results in markdown format
828 // ----------------------------------------------------
829 std::string fname{std::tmpnam(nullptr)};
830 fname += ".md";
831 std::cout << "Markdown output for matrix test: " << fname << "\n";
832
833 auto print_res = [](auto tup) -> std::string {
834 std::string status =
836
837 if (std::get<2>(tup))
838 return status;
839 else
840 {
841 // red
842 return std::string("`") + status + "`";
843 }
844 };
845
846 auto output_table = [&](auto print_res) {
847 size_t test_idx = 0;
848 std::string res;
849 res.reserve(10000); // should be enough :-)
850
851 // first two header lines
852 res += "| `issuing ->` | ";
853 std::apply([&](auto const&... ic) { ((res += ic.first, res += " | "), ...); }, ics);
854 res += "\n";
855
856 res += "| :--- | ";
858 [&](auto const&... ic) { (((void)ic.first, res += ":---: | "), ...); }, ics);
859 res += "\n";
860
861 auto output = [&](auto const& lc, auto const& ic) {
862 res += print_res(test_result[test_idx]);
863 res += " | ";
864 ++test_idx;
865 };
866
867 auto output_ics = [&](auto const& lc, auto const& ics) {
868 res += "| ";
869 res += lc.first;
870 res += " | ";
871 std::apply([&](auto const&... ic) { (output(lc, ic), ...); }, ics);
872 res += "\n";
873 };
874
875 std::apply([&](auto const&... lc) { (output_ics(lc, ics), ...); }, lcs);
876
877 return res;
878 };
879
880 std::ofstream(fname) << output_table(print_res);
881
882 std::string ter_fname{std::tmpnam(nullptr)};
883 std::cout << "ter output for matrix test: " << ter_fname << "\n";
884
885 std::ofstream ofs(ter_fname);
886 for (auto& t : test_result)
887 {
888 ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", "
889 << std::string(transToken(std::get<1>(t))) << "}\n,";
890 }
891#endif
892 }
893
894 void
896 {
897 using namespace jtx;
898 testcase("Modify Bridge");
899
900 // Changing a non-existent bridge should fail
901 XEnv(*this).tx(
903 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]), XRP(2), std::nullopt),
905
906 // must change something
907 // XEnv(*this)
908 // .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
909 // .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(1)),
910 // ter(temMALFORMED));
911
912 // must change something
913 XEnv(*this)
914 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
915 .close()
917
918 // Reward amount is non-xrp
919 XEnv(*this).tx(
921
922 // Reward amount is XRP and negative
923 XEnv(*this).tx(
925
926 // Min create amount is non-xrp
927 XEnv(*this).tx(
928 bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)),
930
931 // Min create amount is zero
932 XEnv(*this).tx(
933 bridge_modify(mcDoor, jvb, XRP(2), XRP(0)),
935
936 // Min create amount is negative
937 XEnv(*this).tx(
938 bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)),
940
941 // First check the regular claim process (without bridge_modify)
942 for (auto withClaim : {false, true})
943 {
944 XEnv mcEnv(*this);
945 XEnv scEnv(*this, true);
946
947 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
948
951 .close()
953 .close();
954
955 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
956 auto const amt = XRP(1000);
957 std::uint32_t const claimID = 1;
958 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
959
960 BalanceTransfer transfer(
961 scEnv,
963 scBob,
964 scAlice,
965 &payees[0],
967 withClaim);
968
969 scEnv
971 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
972 .close();
973
974 if (withClaim)
975 {
976 BEAST_EXPECT(transfer.has_not_happened());
977
978 // need to submit a claim transactions
979 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
980 }
981
982 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
983 }
984
985 // Check that the reward paid from a claim Id was the reward when
986 // the claim id was created, not the reward since the bridge was
987 // modified.
988 for (auto withClaim : {false, true})
989 {
990 XEnv mcEnv(*this);
991 XEnv scEnv(*this, true);
992
993 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
994
997 .close()
999 .close();
1000
1001 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1002 auto const amt = XRP(1000);
1003 std::uint32_t const claimID = 1;
1004 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1005
1006 // Now modify the reward on the bridge
1007 mcEnv.tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(10))).close();
1008 scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10))).close();
1009
1010 BalanceTransfer transfer(
1011 scEnv,
1013 scBob,
1014 scAlice,
1015 &payees[0],
1017 withClaim);
1018
1019 scEnv
1021 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
1022 .close();
1023
1024 if (withClaim)
1025 {
1026 BEAST_EXPECT(transfer.has_not_happened());
1027
1028 // need to submit a claim transactions
1029 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1030 }
1031
1032 // make sure the reward accounts indeed received the original
1033 // split reward (1 split 5 ways) instead of the updated 2 XRP.
1034 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1035 }
1036
1037 // Check that the signatures used to verify attestations and decide
1038 // if there is a quorum are the current signer's list on the door
1039 // account, not the signer's list that was in effect when the claim
1040 // id was created.
1041 for (auto withClaim : {false, true})
1042 {
1043 XEnv mcEnv(*this);
1044 XEnv scEnv(*this, true);
1045
1046 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1047
1050 .close()
1052 .close();
1053
1054 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1055 auto const amt = XRP(1000);
1056 std::uint32_t const claimID = 1;
1057 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1058
1059 // change signers - claim should not be processed is the batch
1060 // is signed by original signers
1062
1063 BalanceTransfer transfer(
1064 scEnv,
1066 scBob,
1067 scAlice,
1068 &payees[0],
1070 withClaim);
1071
1072 // submit claim using outdated signers - should fail
1073 scEnv
1074 .multiTx(
1076 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers),
1078 .close();
1079 if (withClaim)
1080 {
1081 // need to submit a claim transactions
1082 scEnv
1083 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1085 .close();
1086 }
1087
1088 // make sure transfer has not happened as we sent attestations
1089 // using outdated signers
1090 BEAST_EXPECT(transfer.has_not_happened());
1091
1092 // submit claim using current signers - should succeed
1093 scEnv
1095 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, alt_signers))
1096 .close();
1097 if (withClaim)
1098 {
1099 BEAST_EXPECT(transfer.has_not_happened());
1100
1101 // need to submit a claim transactions
1102 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1103 }
1104
1105 // make sure the transfer went through as we sent attestations
1106 // using new signers
1107 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum, false));
1108 }
1109
1110 // coverage test: bridge_modify transaction with incorrect flag
1111 XEnv(*this)
1113 .close()
1114 .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)),
1115 txflags(tfFillOrKill),
1117
1118 // coverage test: bridge_modify transaction with xchain feature
1119 // disabled
1120 XEnv(*this)
1122 .disableFeature(featureXChainBridge)
1123 .close()
1125
1126 // coverage test: bridge_modify return temSIDECHAIN_NONDOOR_OWNER;
1127 XEnv(*this)
1129 .close()
1131
1138 XEnv(*this)
1139 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20)))
1140 .close()
1142 .close()
1143 .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)),
1144 txflags(tfClearAccountCreateAmount),
1146 .close()
1147 .tx(bridge_modify(mcDoor, jvb, XRP(3), {}), txflags(tfClearAccountCreateAmount))
1148 .close()
1151 .close();
1152 }
1153
1154 void
1156 {
1157 using namespace jtx;
1158 XRPAmount const res1 = reserve(1);
1159 XRPAmount const tx_fee = txFee();
1160
1161 testcase("Create ClaimID");
1162
1163 // normal bridge create for sanity check with the exact necessary
1164 // account balance
1165 XEnv(*this, true)
1167 .fund(res1, scuAlice) // acct reserve + 1 object
1168 .close()
1170 .close();
1171
1172 // check reward not deducted when claim id is created
1173 {
1174 XEnv xenv(*this, true);
1175
1176 Balance const scAlice_bal(xenv, scAlice);
1177
1180 .close();
1181
1182 BEAST_EXPECT(scAlice_bal.diff() == -tx_fee);
1183 }
1184
1185 // Non-existent bridge
1186 XEnv(*this, true)
1188 scAlice, bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]), reward, mcAlice),
1190 .close();
1191
1192 // Creating the new object would put the account below the reserve
1193 XEnv(*this, true)
1195 .fund(res1 - xrp_dust, scuAlice) // barely not enough
1196 .close()
1199 .close();
1200
1201 // The specified reward doesn't match the reward on the bridge (test
1202 // by giving the reward amount for the other side, as well as a
1203 // completely non-matching reward)
1204 XEnv(*this, true)
1206 .close()
1209 .close();
1210
1211 // A reward amount that isn't XRP
1212 XEnv(*this, true)
1214 .close()
1217 .close();
1218
1219 // coverage test: xchain_create_claim_id transaction with incorrect
1220 // flag
1221 XEnv(*this, true)
1223 .close()
1225 txflags(tfFillOrKill),
1227 .close();
1228
1229 // coverage test: xchain_create_claim_id transaction with xchain
1230 // feature disabled
1231 XEnv(*this, true)
1233 .disableFeature(featureXChainBridge)
1234 .close()
1236 .close();
1237 }
1238
1239 void
1241 {
1242 using namespace jtx;
1243 XRPAmount const res0 = reserve(0);
1244 XRPAmount const tx_fee = txFee();
1245
1246 testcase("Commit");
1247
1248 // Commit to a non-existent bridge
1250
1251 // check that reward not deducted when doing the commit
1252 {
1253 XEnv xenv(*this);
1254
1255 Balance const alice_bal(xenv, mcAlice);
1256 auto const amt = XRP(1000);
1257
1258 xenv.tx(create_bridge(mcDoor, jvb))
1259 .close()
1260 .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob))
1261 .close();
1262
1263 STAmount const claim_cost = amt;
1264 BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee));
1265 }
1266
1267 // Commit a negative amount
1268 XEnv(*this)
1270 .close()
1272
1273 // Commit an amount whose issue that does not match the expected
1274 // issue on the bridge (either LockingChainIssue or
1275 // IssuingChainIssue, depending on the chain).
1276 XEnv(*this)
1278 .close()
1280
1281 // Commit an amount that would put the sender below the required
1282 // reserve (if XRP)
1283 XEnv(*this)
1285 .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough
1286 .close()
1288
1289 XEnv(*this)
1291 .fund(
1292 res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees
1293 mcuAlice) // exactly enough => should succeed
1294 .close()
1296
1297 // Commit an amount above the account's balance (for both XRP and
1298 // IOUs)
1299 XEnv(*this)
1301 .fund(res0, mcuAlice) // barely not enough
1302 .close()
1304
1305 auto jvb_USD = bridge(mcDoor, mcUSD, scGw, scUSD);
1306
1307 // commit sent from iou issuer (mcGw) succeeds - should it?
1308 XEnv(*this)
1309 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1310 .tx(create_bridge(mcDoor, jvb_USD))
1311 .close()
1312 .tx(xchain_commit(mcGw, jvb_USD, 1, mcUSD(1), scBob));
1313
1314 // commit to a door account from the door account. This should fail.
1315 XEnv(*this)
1316 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1317 .tx(create_bridge(mcDoor, jvb_USD))
1318 .close()
1320
1321 // commit sent from mcAlice which has no IOU balance => should fail
1322 XEnv(*this)
1323 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1324 .tx(create_bridge(mcDoor, jvb_USD))
1325 .close()
1326 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scBob), ter(terNO_LINE));
1327
1328 // commit sent from mcAlice which has no IOU balance => should fail
1329 // just changed the destination to scGw (which is the door account
1330 // and may not make much sense)
1331 XEnv(*this)
1332 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1333 .tx(create_bridge(mcDoor, jvb_USD))
1334 .close()
1335 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scGw), ter(terNO_LINE));
1336
1337 // commit sent from mcAlice which has a IOU balance => should
1338 // succeed
1339 XEnv(*this)
1340 .tx(trust(mcDoor, mcUSD(10000)))
1341 .tx(trust(mcAlice, mcUSD(10000)))
1342 .close()
1343 .tx(pay(mcGw, mcAlice, mcUSD(10)))
1344 .tx(create_bridge(mcDoor, jvb_USD))
1345 .close()
1346 //.tx(pay(mcAlice, mcDoor, mcUSD(10)));
1347 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(10), scAlice));
1348
1349 // coverage test: xchain_commit transaction with incorrect flag
1350 XEnv(*this)
1352 .close()
1354 txflags(tfFillOrKill),
1356
1357 // coverage test: xchain_commit transaction with xchain feature
1358 // disabled
1359 XEnv(*this)
1361 .disableFeature(featureXChainBridge)
1362 .close()
1364 }
1365
1366 void
1368 {
1369 using namespace jtx;
1370
1371 testcase("Add Attestation");
1372 XRPAmount const res0 = reserve(0);
1373 XRPAmount tx_fee = txFee();
1374
1375 auto multiTtxFee = [&](std::uint32_t m) -> STAmount {
1376 return multiply(tx_fee, STAmount(m), xrpIssue());
1377 };
1378
1379 // Add an attestation to a claim id that has already reached quorum.
1380 // This should succeed and share in the reward.
1381 // note: this is true only when either:
1382 // 1. dest account is not specified, so transfer requires a claim
1383 // 2. or the extra attestation is sent in the same batch as the
1384 // one reaching quorum
1385 for (auto withClaim : {true})
1386 {
1387 XEnv mcEnv(*this);
1388 XEnv scEnv(*this, true);
1389 std::uint32_t const claimID = 1;
1390
1391 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1392
1395 .close()
1397 .close();
1398
1399 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1400
1401 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1402 auto const amt = XRP(1000);
1403 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1404
1405 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
1406
1407 scEnv
1409 scAttester,
1410 jvb,
1411 mcAlice,
1412 amt,
1413 payees,
1414 true,
1415 claimID,
1416 dst,
1417 signers,
1419 .close();
1420 scEnv
1422 scAttester,
1423 jvb,
1424 mcAlice,
1425 amt,
1427 true,
1428 claimID,
1429 dst,
1431 .close();
1432
1433 if (withClaim)
1434 {
1435 BEAST_EXPECT(transfer.has_not_happened());
1436
1437 // need to submit a claim transactions
1438 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1439 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1440 BEAST_EXPECT(scEnv.claimID(jvb) == claimID);
1441 }
1442
1443 BEAST_EXPECT(transfer.has_happened(amt, split_reward_everyone));
1444 }
1445
1446 // Test that signature weights are correctly handled. Assign
1447 // signature weights of 1,2,4,4 and a quorum of 7. Check that the
1448 // 4,4 signatures reach a quorum, the 1,2,4, reach a quorum, but the
1449 // 4,2, 4,1 and 1,2 do not.
1450
1451 // 1,2,4 => should succeed
1452 for (auto withClaim : {false, true})
1453 {
1454 XEnv mcEnv(*this);
1455 XEnv scEnv(*this, true);
1456
1457 std::uint32_t const quorum_7 = 7;
1458 std::vector<signer> const signers_ = [] {
1459 constexpr int numSigners = 4;
1460 std::uint32_t const weights[] = {1, 2, 4, 4};
1461
1462 std::vector<signer> result;
1463 result.reserve(numSigners);
1464 for (int i = 0; i < numSigners; ++i)
1465 {
1466 using namespace std::literals;
1467 auto const a = Account("signer_"s + std::to_string(i));
1468 result.emplace_back(a, weights[i]);
1469 }
1470 return result;
1471 }();
1472
1473 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1474
1476 .tx(jtx::signers(Account::master, quorum_7, signers_))
1477 .close()
1479 .close();
1480 std::uint32_t const claimID = 1;
1481 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1482
1483 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1484 auto const amt = XRP(1000);
1485
1486 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1487
1488 BalanceTransfer transfer(
1489 scEnv, Account::master, scBob, scAlice, &payees[0], 3, withClaim);
1490
1491 scEnv
1493 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 3))
1494 .close();
1495
1496 if (withClaim)
1497 {
1498 BEAST_EXPECT(transfer.has_not_happened());
1499
1500 // need to submit a claim transactions
1501 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1502 }
1503
1504 BEAST_EXPECT(!scEnv.claimID(jvb, 1)); // claim id deleted
1505
1506 BEAST_EXPECT(transfer.has_happened(amt, divide(reward, STAmount(3), reward.issue())));
1507 }
1508
1509 // 4,4 => should succeed
1510 for (auto withClaim : {false, true})
1511 {
1512 XEnv mcEnv(*this);
1513 XEnv scEnv(*this, true);
1514
1515 std::uint32_t const quorum_7 = 7;
1516 std::vector<signer> const signers_ = [] {
1517 constexpr int numSigners = 4;
1518 std::uint32_t const weights[] = {1, 2, 4, 4};
1519
1520 std::vector<signer> result;
1521 result.reserve(numSigners);
1522 for (int i = 0; i < numSigners; ++i)
1523 {
1524 using namespace std::literals;
1525 auto const a = Account("signer_"s + std::to_string(i));
1526 result.emplace_back(a, weights[i]);
1527 }
1528 return result;
1529 }();
1530 STAmount const split_reward_ =
1531 divide(reward, STAmount(signers_.size()), reward.issue());
1532
1533 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1534
1536 .tx(jtx::signers(Account::master, quorum_7, signers_))
1537 .close()
1539 .close();
1540 std::uint32_t const claimID = 1;
1541 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1542
1543 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1544 auto const amt = XRP(1000);
1545
1546 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1547
1548 BalanceTransfer transfer(
1549 scEnv, Account::master, scBob, scAlice, &payees[2], 2, withClaim);
1550
1551 scEnv
1553 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 2))
1554 .close();
1555
1556 if (withClaim)
1557 {
1558 BEAST_EXPECT(transfer.has_not_happened());
1559
1560 // need to submit a claim transactions
1561 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1562 }
1563
1564 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1565
1566 BEAST_EXPECT(transfer.has_happened(amt, divide(reward, STAmount(2), reward.issue())));
1567 }
1568
1569 // 1,2 => should fail
1570 for (auto withClaim : {false, true})
1571 {
1572 XEnv mcEnv(*this);
1573 XEnv scEnv(*this, true);
1574
1575 std::uint32_t const quorum_7 = 7;
1576 std::vector<signer> const signers_ = [] {
1577 constexpr int numSigners = 4;
1578 std::uint32_t const weights[] = {1, 2, 4, 4};
1579
1580 std::vector<signer> result;
1581 result.reserve(numSigners);
1582 for (int i = 0; i < numSigners; ++i)
1583 {
1584 using namespace std::literals;
1585 auto const a = Account("signer_"s + std::to_string(i));
1586 result.emplace_back(a, weights[i]);
1587 }
1588 return result;
1589 }();
1590
1591 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1592
1594 .tx(jtx::signers(Account::master, quorum_7, signers_))
1595 .close()
1597 .close();
1598
1599 std::uint32_t const claimID = 1;
1600 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1601
1602 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1603 auto const amt = XRP(1000);
1604 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1605
1606 BalanceTransfer transfer(
1607 scEnv, Account::master, scBob, scAlice, &payees[0], 2, withClaim);
1608
1609 scEnv
1611 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2))
1612 .close();
1613 if (withClaim)
1614 {
1615 BEAST_EXPECT(transfer.has_not_happened());
1616
1617 // need to submit a claim transactions
1618 scEnv
1619 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1621 .close();
1622 }
1623
1624 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id still present
1625 BEAST_EXPECT(transfer.has_not_happened());
1626 }
1627
1628 // 2,4 => should fail
1629 for (auto withClaim : {false, true})
1630 {
1631 XEnv mcEnv(*this);
1632 XEnv scEnv(*this, true);
1633
1634 std::uint32_t const quorum_7 = 7;
1635 std::vector<signer> const signers_ = [] {
1636 constexpr int numSigners = 4;
1637 std::uint32_t const weights[] = {1, 2, 4, 4};
1638
1639 std::vector<signer> result;
1640 result.reserve(numSigners);
1641 for (int i = 0; i < numSigners; ++i)
1642 {
1643 using namespace std::literals;
1644 auto const a = Account("signer_"s + std::to_string(i));
1645 result.emplace_back(a, weights[i]);
1646 }
1647 return result;
1648 }();
1649
1650 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1651
1653 .tx(jtx::signers(Account::master, quorum_7, signers_))
1654 .close()
1656 .close();
1657
1658 std::uint32_t const claimID = 1;
1659 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1660
1661 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1662 auto const amt = XRP(1000);
1663
1664 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1665
1666 BalanceTransfer transfer(
1667 scEnv, Account::master, scBob, scAlice, &payees[1], 2, withClaim);
1668
1669 scEnv
1671 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 1))
1672 .close();
1673
1674 if (withClaim)
1675 {
1676 BEAST_EXPECT(transfer.has_not_happened());
1677
1678 // need to submit a claim transactions
1679 scEnv
1680 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1682 .close();
1683 }
1684
1685 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id still present
1686 BEAST_EXPECT(transfer.has_not_happened());
1687 }
1688
1689 // Confirm that account create transactions happen in the correct
1690 // order. If they reach quorum out of order they should not execute
1691 // until all the previous created transactions have occurred.
1692 // Re-adding an attestation should move funds.
1693 {
1694 XEnv mcEnv(*this);
1695 XEnv scEnv(*this, true);
1696 auto const amt = XRP(1000);
1697 auto const amt_plus_reward = amt + reward;
1698
1699 {
1700 Balance const door(mcEnv, mcDoor);
1701 Balance const carol(mcEnv, mcCarol);
1702
1703 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1704 .close()
1708 .close();
1709
1710 BEAST_EXPECT(
1711 door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee));
1712 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1713 }
1714
1717 .close();
1718
1719 {
1720 // send first batch of account create attest for all 3
1721 // account create
1722 Balance const attester(scEnv, scAttester);
1723 Balance const door(scEnv, Account::master);
1724
1725 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2))
1727 .multiTx(att_create_acct_vec(2, amt, scuBob, 2))
1728 .close();
1729
1730 BEAST_EXPECT(door.diff() == STAmount(0));
1731 // att_create_acct_vec return vectors of size 2, so 2*3 txns
1732 BEAST_EXPECT(attester.diff() == -multiTtxFee(6));
1733
1734 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // ca claim id present
1735 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1736 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1737 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count still 0
1738 }
1739
1740 {
1741 // complete attestations for 2nd account create => should
1742 // not complete
1743 Balance const attester(scEnv, scAttester);
1744 Balance const door(scEnv, Account::master);
1745
1746 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2)).close();
1747
1748 BEAST_EXPECT(door.diff() == STAmount(0));
1749 // att_create_acct_vec return vectors of size 3, so 3 txns
1750 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1751
1752 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1753 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count still 0
1754 }
1755
1756 {
1757 // complete attestations for 3rd account create => should
1758 // not complete
1759 Balance const attester(scEnv, scAttester);
1760 Balance const door(scEnv, Account::master);
1761
1762 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2)).close();
1763
1764 BEAST_EXPECT(door.diff() == STAmount(0));
1765 // att_create_acct_vec return vectors of size 3, so 3 txns
1766 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1767
1768 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1769 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count still 0
1770 }
1771
1772 {
1773 // complete attestations for 1st account create => account
1774 // should be created
1775 Balance const attester(scEnv, scAttester);
1776 Balance const door(scEnv, Account::master);
1777
1778 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1)).close();
1779
1780 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1781 // att_create_acct_vec return vectors of size 3, so 3 txns
1782 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1783 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
1784
1785 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id 1 deleted
1786 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
1787 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1788 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count now 1
1789 }
1790
1791 {
1792 // resend attestations for 3rd account create => still
1793 // should not complete
1794 Balance const attester(scEnv, scAttester);
1795 Balance const door(scEnv, Account::master);
1796
1797 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2)).close();
1798
1799 BEAST_EXPECT(door.diff() == STAmount(0));
1800 // att_create_acct_vec return vectors of size 3, so 3 txns
1801 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1802
1803 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
1804 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1805 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count still 1
1806 }
1807
1808 {
1809 // resend attestations for 2nd account create => account
1810 // should be created
1811 Balance const attester(scEnv, scAttester);
1812 Balance const door(scEnv, Account::master);
1813
1814 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close();
1815
1816 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1817 BEAST_EXPECT(attester.diff() == -tx_fee);
1818 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
1819
1820 BEAST_EXPECT(!scEnv.caClaimID(jvb, 2)); // claim id 2 deleted
1821 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1822 BEAST_EXPECT(scEnv.claimCount(jvb) == 2); // claim count now 2
1823 }
1824 {
1825 // resend attestations for 3rc account create => account
1826 // should be created
1827 Balance const attester(scEnv, scAttester);
1828 Balance const door(scEnv, Account::master);
1829
1830 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close();
1831
1832 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1833 BEAST_EXPECT(attester.diff() == -tx_fee);
1834 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
1835
1836 BEAST_EXPECT(!scEnv.caClaimID(jvb, 3)); // claim id 3 deleted
1837 BEAST_EXPECT(scEnv.claimCount(jvb) == 3); // claim count now 3
1838 }
1839 }
1840
1841 // Check that creating an account with less than the minimum reserve
1842 // fails.
1843 {
1844 XEnv mcEnv(*this);
1845 XEnv scEnv(*this, true);
1846
1847 auto const amt = res0 - XRP(1);
1848 auto const amt_plus_reward = amt + reward;
1849
1850 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
1851
1852 {
1853 Balance const door(mcEnv, mcDoor);
1854 Balance const carol(mcEnv, mcCarol);
1855
1857 .close();
1858
1859 BEAST_EXPECT(door.diff() == amt_plus_reward);
1860 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
1861 }
1862
1865 .close();
1866
1867 Balance const attester(scEnv, scAttester);
1868 Balance const door(scEnv, Account::master);
1869
1870 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close();
1871 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
1872 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
1873
1874 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2, 2)).close();
1875 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
1876 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
1877
1878 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
1879 BEAST_EXPECT(door.diff() == -reward);
1880 BEAST_EXPECT(!scEnv.account(scuAlice));
1881 }
1882
1883 // Check that sending funds with an account create txn to an
1884 // existing account works.
1885 {
1886 XEnv mcEnv(*this);
1887 XEnv scEnv(*this, true);
1888
1889 auto const amt = XRP(111);
1890 auto const amt_plus_reward = amt + reward;
1891
1892 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
1893
1894 {
1895 Balance const door(mcEnv, mcDoor);
1896 Balance const carol(mcEnv, mcCarol);
1897
1899 .close();
1900
1901 BEAST_EXPECT(door.diff() == amt_plus_reward);
1902 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
1903 }
1904
1907 .close();
1908
1909 Balance const attester(scEnv, scAttester);
1910 Balance const door(scEnv, Account::master);
1911 Balance const alice(scEnv, scAlice);
1912
1913 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
1914 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
1915 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
1916
1917 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
1918 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
1919 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
1920
1921 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1922 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
1923 BEAST_EXPECT(alice.diff() == amt);
1924 }
1925
1926 // Check that sending funds to an existing account with deposit auth
1927 // set fails for account create transactions.
1928 {
1929 XEnv mcEnv(*this);
1930 XEnv scEnv(*this, true);
1931
1932 auto const amt = XRP(1000);
1933 auto const amt_plus_reward = amt + reward;
1934
1935 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
1936
1937 {
1938 Balance const door(mcEnv, mcDoor);
1939 Balance const carol(mcEnv, mcCarol);
1940
1942 .close();
1943
1944 BEAST_EXPECT(door.diff() == amt_plus_reward);
1945 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
1946 }
1947
1950 .tx(fset("scAlice", asfDepositAuth)) // set deposit auth
1951 .close();
1952
1953 Balance const attester(scEnv, scAttester);
1954 Balance const door(scEnv, Account::master);
1955 Balance const alice(scEnv, scAlice);
1956
1957 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
1958 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
1959 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
1960
1961 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
1962 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
1963 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
1964
1965 BEAST_EXPECT(door.diff() == -reward);
1966 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
1967 BEAST_EXPECT(alice.diff() == STAmount(0));
1968 }
1969
1970 // If an account is unable to pay the reserve, check that it fails.
1971 // [greg todo] I don't know what this should test??
1972
1973 // If an attestation already exists for that server and claim id,
1974 // the new attestation should replace the old attestation
1975 {
1976 XEnv mcEnv(*this);
1977 XEnv scEnv(*this, true);
1978 auto const amt = XRP(1000);
1979 auto const amt_plus_reward = amt + reward;
1980
1981 {
1982 Balance const door(mcEnv, mcDoor);
1983 Balance const carol(mcEnv, mcCarol);
1984
1985 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1986 .close()
1988 .close() // make sure Alice gets claim #1
1990 .close() // make sure Bob gets claim #2
1992 .close(); // and Carol will get claim #3
1993
1994 BEAST_EXPECT(
1995 door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee));
1996 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1997 }
1998
1999 std::uint32_t const red_quorum = 2;
2001 .tx(jtx::signers(Account::master, red_quorum, signers))
2002 .close();
2003
2004 {
2005 Balance const attester(scEnv, scAttester);
2006 Balance const door(scEnv, Account::master);
2007 auto const bad_amt = XRP(10);
2008 std::uint32_t txCount = 0;
2009
2010 // send attestations with incorrect amounts to for all 3
2011 // AccountCreate. They will be replaced later
2012 scEnv.multiTx(att_create_acct_vec(1, bad_amt, scuAlice, 1))
2013 .multiTx(att_create_acct_vec(2, bad_amt, scuBob, 1, 2))
2014 .multiTx(att_create_acct_vec(3, bad_amt, scuCarol, 1, 1))
2015 .close();
2016 txCount += 3;
2017
2018 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 created");
2019 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 created");
2020 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 created");
2021
2022 // note: if we send inconsistent attestations in the same
2023 // batch, the transaction errors.
2024
2025 // from now on we send correct attestations
2026 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 0))
2027 .multiTx(att_create_acct_vec(2, amt, scuBob, 1, 2))
2028 .multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 4))
2029 .close();
2030 txCount += 3;
2031
2032 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 still there");
2033 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 still there");
2034 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2035 BEAST_EXPECTS(scEnv.claimCount(jvb) == 0, "No account created yet");
2036
2037 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 1)).close();
2038 txCount += 1;
2039
2040 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2041 BEAST_EXPECTS(scEnv.claimCount(jvb) == 0, "No account created yet");
2042
2043 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 2)).close();
2044 txCount += 1;
2045
2046 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 deleted");
2047 BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created");
2048
2049 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3))
2050 .multiTx(
2051 att_create_acct_vec(1, amt, scuAlice, 1, 3),
2053 .close();
2054 txCount += 2;
2055
2056 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 2), "claim id 2 deleted");
2057 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 not added");
2058 BEAST_EXPECTS(scEnv.claimCount(jvb) == 2, "scuAlice & scuBob created");
2059
2060 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 0)).close();
2061 txCount += 1;
2062
2063 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 3), "claim id 3 deleted");
2064 BEAST_EXPECTS(scEnv.claimCount(jvb) == 3, "All 3 accounts created");
2065
2066 // because of the division of the rewards among attesters,
2067 // sometimes a couple drops are left over unspent in the
2068 // door account (here 2 drops)
2069 BEAST_EXPECT(
2070 multiply(amt_plus_reward, STAmount(3), xrpIssue()) + door.diff() < drops(3));
2071 BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount));
2072 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2073 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2074 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2075 }
2076 }
2077
2078 // If attestation moves funds, confirm the claim ledger objects are
2079 // removed (for both account create and "regular" transactions)
2080 // [greg] we do this in all attestation tests
2081
2082 // coverage test: add_attestation transaction with incorrect flag
2083 {
2084 XEnv scEnv(*this, true);
2087 .close()
2089 scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]),
2090 txflags(tfFillOrKill),
2092 .close();
2093 }
2094
2095 // coverage test: add_attestation with xchain feature
2096 // disabled
2097 {
2098 XEnv scEnv(*this, true);
2101 .disableFeature(featureXChainBridge)
2102 .close()
2104 scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]),
2106 .close();
2107 }
2108 }
2109
2110 void
2112 {
2113 using namespace jtx;
2114
2115 testcase("Add Non Batch Claim Attestation");
2116
2117 {
2118 XEnv mcEnv(*this);
2119 XEnv scEnv(*this, true);
2120 std::uint32_t const claimID = 1;
2121
2122 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2123
2126 .close()
2128 .close();
2129
2130 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
2131
2132 Account const dst{scBob};
2133 auto const amt = XRP(1000);
2134 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2135
2136 auto const dstStartBalance = scEnv.env_.balance(dst);
2137
2138 for (int i = 0; i < signers.size(); ++i)
2139 {
2140 auto const att = claim_attestation(
2141 scAttester, jvb, mcAlice, amt, payees[i], true, claimID, dst, signers[i]);
2142
2143 TER const expectedTER = i < quorum ? tesSUCCESS : TER{tecXCHAIN_NO_CLAIM_ID};
2144 if (i + 1 == quorum)
2145 {
2146 scEnv.tx(att, ter(expectedTER)).close();
2147 }
2148 else
2149 {
2150 scEnv.tx(att, ter(expectedTER)).close();
2151 }
2152
2153 if (i + 1 < quorum)
2154 {
2155 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2156 }
2157 else
2158 {
2159 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2160 }
2161 }
2162 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2163 }
2164
2165 {
2182 XEnv mcEnv(*this);
2183 XEnv scEnv(*this, true);
2184 auto const amt = XRP(1000);
2185 std::uint32_t const claimID = 1;
2186
2187 for (auto i = 0; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2; ++i)
2188 scEnv.fund(amt, alt_signers[i].account);
2189
2190 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2191
2194 .close()
2196 .close();
2197
2198 Account const dst{scBob};
2199 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2200 auto const dstStartBalance = scEnv.env_.balance(dst);
2201
2202 {
2203 // G1: master key
2204 auto att = claim_attestation(
2205 scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, alt_signers[0]);
2206 scEnv.tx(att).close();
2207 }
2208 {
2209 // G2: regular key
2210 // alt_signers[0] is the regular key of alt_signers[1]
2211 // There should be 2 attestations after the transaction
2212 scEnv.tx(jtx::regkey(alt_signers[1].account, alt_signers[0].account)).close();
2213 auto att = claim_attestation(
2214 scAttester, jvb, mcAlice, amt, payees[1], true, claimID, dst, alt_signers[0]);
2215 att[sfAttestationSignerAccount.getJsonName()] = alt_signers[1].account.human();
2216 scEnv.tx(att).close();
2217 }
2218 {
2219 // B3: public key and non-exist (unfunded) account mismatch
2220 // G3: public key and non-exist (unfunded) account match
2221 auto const unfundedSigner1 = alt_signers[UT_XCHAIN_DEFAULT_NUM_SIGNERS - 1];
2222 auto const unfundedSigner2 = alt_signers[UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2];
2223 auto att = claim_attestation(
2224 scAttester,
2225 jvb,
2226 mcAlice,
2227 amt,
2229 true,
2230 claimID,
2231 dst,
2232 unfundedSigner1);
2233 att[sfAttestationSignerAccount.getJsonName()] = unfundedSigner2.account.human();
2235 att[sfAttestationSignerAccount.getJsonName()] = unfundedSigner1.account.human();
2236 scEnv.tx(att).close();
2237 }
2238 {
2239 // B2: single item signer list
2240 std::vector<signer> tempSignerList = {signers[0]};
2241 scEnv.tx(jtx::signers(alt_signers[2].account, 1, tempSignerList));
2242 auto att = claim_attestation(
2243 scAttester,
2244 jvb,
2245 mcAlice,
2246 amt,
2247 payees[2],
2248 true,
2249 claimID,
2250 dst,
2251 tempSignerList.front());
2252 att[sfAttestationSignerAccount.getJsonName()] = alt_signers[2].account.human();
2254 }
2255 {
2256 // B1: disabled master key
2257 scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0)).close();
2258 auto att = claim_attestation(
2259 scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, alt_signers[2]);
2261 }
2262 {
2263 // --B4: not on signer list
2264 auto att = claim_attestation(
2265 scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]);
2266 scEnv.tx(att, ter(tecNO_PERMISSION)).close();
2267 }
2268 {
2269 // --B5: missing sfAttestationSignerAccount field
2270 // Then submit the one with the field. Should reach quorum.
2271 auto att = claim_attestation(
2272 scAttester, jvb, mcAlice, amt, payees[3], true, claimID, dst, alt_signers[3]);
2273 att.removeMember(sfAttestationSignerAccount.getJsonName());
2274 scEnv.tx(att, ter(temMALFORMED)).close();
2275 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2276 att[sfAttestationSignerAccount.getJsonName()] = alt_signers[3].account.human();
2277 scEnv.tx(att).close();
2278 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2279 }
2280 }
2281 }
2282
2283 void
2285 {
2286 using namespace jtx;
2287
2288 testcase("Add Non Batch Account Create Attestation");
2289
2290 XEnv mcEnv(*this);
2291 XEnv scEnv(*this, true);
2292
2293 XRPAmount const tx_fee = mcEnv.txFee();
2294
2295 Account const a{"a"};
2296 Account const doorA{"doorA"};
2297
2298 STAmount const funds{XRP(10000)};
2299 mcEnv.fund(funds, a);
2300 mcEnv.fund(funds, doorA);
2301
2302 Account const ua{"ua"}; // unfunded account we want to create
2303
2304 BridgeDef xrp_b{
2305 doorA,
2306 xrpIssue(),
2308 xrpIssue(),
2309 XRP(1), // reward
2310 XRP(20), // minAccountCreate
2311 4, // quorum
2312 signers,
2314
2315 xrp_b.initBridge(mcEnv, scEnv);
2316
2317 auto const amt = XRP(777);
2318 auto const amt_plus_reward = amt + xrp_b.reward;
2319 {
2320 Balance const bal_doorA(mcEnv, doorA);
2321 Balance const bal_a(mcEnv, a);
2322
2323 mcEnv.tx(sidechain_xchain_account_create(a, xrp_b.jvb, ua, amt, xrp_b.reward)).close();
2324
2325 BEAST_EXPECT(bal_doorA.diff() == amt_plus_reward);
2326 BEAST_EXPECT(bal_a.diff() == -(amt_plus_reward + tx_fee));
2327 }
2328
2329 for (int i = 0; i < signers.size(); ++i)
2330 {
2331 auto const att = create_account_attestation(
2332 signers[0].account,
2333 xrp_b.jvb,
2334 a,
2335 amt,
2336 xrp_b.reward,
2337 signers[i].account,
2338 true,
2339 1,
2340 ua,
2341 signers[i]);
2342 TER const expectedTER =
2343 i < xrp_b.quorum ? tesSUCCESS : TER{tecXCHAIN_ACCOUNT_CREATE_PAST};
2344
2345 scEnv.tx(att, ter(expectedTER)).close();
2346 if (i + 1 < xrp_b.quorum)
2347 {
2348 BEAST_EXPECT(!scEnv.env_.le(ua));
2349 }
2350 else
2351 {
2352 BEAST_EXPECT(scEnv.env_.le(ua));
2353 }
2354 }
2355 BEAST_EXPECT(scEnv.env_.le(ua));
2356 }
2357
2358 void
2360 {
2361 using namespace jtx;
2362
2363 XRPAmount const res0 = reserve(0);
2364 XRPAmount const tx_fee = txFee();
2365
2366 testcase("Claim");
2367
2368 // Claim where the amount matches what is attested to, to an account
2369 // that exists, and there are enough attestations to reach a quorum
2370 // => should succeed
2371 // -----------------------------------------------------------------
2372 for (auto withClaim : {false, true})
2373 {
2374 XEnv mcEnv(*this);
2375 XEnv scEnv(*this, true);
2376
2377 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2378
2381 .close()
2383 .close();
2384
2385 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2386 auto const amt = XRP(1000);
2387 std::uint32_t const claimID = 1;
2388 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2389
2390 BalanceTransfer transfer(
2391 scEnv,
2393 scBob,
2394 scAlice,
2395 &payees[0],
2397 withClaim);
2398
2399 scEnv
2401 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2402 .close();
2403 if (withClaim)
2404 {
2405 BEAST_EXPECT(transfer.has_not_happened());
2406
2407 // need to submit a claim transactions
2408 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
2409 }
2410
2411 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2412 }
2413
2414 // Claim with just one attestation signed by the Master key
2415 // => should not succeed
2416 // -----------------------------------------------------------------
2417 for (auto withClaim : {false, true})
2418 {
2419 XEnv mcEnv(*this);
2420 XEnv scEnv(*this, true);
2421
2422 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2423
2424 scEnv
2426 //.tx(jtx::signers(Account::master, quorum, signers))
2427 .close()
2429 .close();
2430
2431 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2432 auto const amt = XRP(1000);
2433 std::uint32_t const claimID = 1;
2434 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2435
2436 BalanceTransfer transfer(
2437 scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim);
2438
2439 jtx::signer const master_signer(Account::master);
2440 scEnv
2442 scAttester,
2443 jvb,
2444 mcAlice,
2445 amt,
2446 payees[0],
2447 true,
2448 claimID,
2449 dst,
2450 master_signer),
2452 .close();
2453
2454 BEAST_EXPECT(transfer.has_not_happened());
2455 }
2456
2457 // Claim with just one attestation signed by a regular key
2458 // associated to the master account
2459 // => should not succeed
2460 // -----------------------------------------------------------------
2461 for (auto withClaim : {false, true})
2462 {
2463 XEnv mcEnv(*this);
2464 XEnv scEnv(*this, true);
2465
2466 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2467
2468 scEnv
2470 //.tx(jtx::signers(Account::master, quorum, signers))
2472 .close()
2474 .close();
2475
2476 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2477 auto const amt = XRP(1000);
2478 std::uint32_t const claimID = 1;
2479 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2480
2481 BalanceTransfer transfer(
2482 scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim);
2483
2484 jtx::signer const master_signer(payees[0]);
2485 scEnv
2487 scAttester,
2488 jvb,
2489 mcAlice,
2490 amt,
2491 payees[0],
2492 true,
2493 claimID,
2494 dst,
2495 master_signer),
2497 .close();
2498
2499 BEAST_EXPECT(transfer.has_not_happened());
2500 }
2501
2502 // Claim against non-existent bridge
2503 // ---------------------------------
2504 for (auto withClaim : {false, true})
2505 {
2506 XEnv mcEnv(*this);
2507 XEnv scEnv(*this, true);
2508
2509 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2510
2511 auto jvb_unknown = bridge(mcBob, xrpIssue(), Account::master, xrpIssue());
2512
2515 .close()
2517 .close();
2518
2519 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2520 auto const amt = XRP(1000);
2521 std::uint32_t const claimID = 1;
2522 mcEnv.tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst), ter(tecNO_ENTRY))
2523 .close();
2524
2525 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2526 scEnv
2528 scAttester,
2529 jvb_unknown,
2530 mcAlice,
2531 amt,
2532 payees[0],
2533 true,
2534 claimID,
2535 dst,
2536 signers[0]),
2538 .close();
2539
2540 if (withClaim)
2541 {
2542 BEAST_EXPECT(transfer.has_not_happened());
2543
2544 // need to submit a claim transactions
2545 scEnv.tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob), ter(tecNO_ENTRY))
2546 .close();
2547 }
2548
2549 BEAST_EXPECT(transfer.has_not_happened());
2550 }
2551
2552 // Claim against non-existent claim id
2553 // -----------------------------------
2554 for (auto withClaim : {false, true})
2555 {
2556 XEnv mcEnv(*this);
2557 XEnv scEnv(*this, true);
2558
2559 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2560
2563 .close()
2565 .close();
2566
2567 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2568 auto const amt = XRP(1000);
2569 std::uint32_t const claimID = 1;
2570 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2571
2572 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2573
2574 // attest using non-existent claim id
2575 scEnv
2577 scAttester, jvb, mcAlice, amt, payees[0], true, 999, dst, signers[0]),
2579 .close();
2580 if (withClaim)
2581 {
2582 BEAST_EXPECT(transfer.has_not_happened());
2583
2584 // claim using non-existent claim id
2586 .close();
2587 }
2588
2589 BEAST_EXPECT(transfer.has_not_happened());
2590 }
2591
2592 // Claim against a claim id owned by another account
2593 // -------------------------------------------------
2594 for (auto withClaim : {false, true})
2595 {
2596 XEnv mcEnv(*this);
2597 XEnv scEnv(*this, true);
2598
2599 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2600
2603 .close()
2605 .close();
2606
2607 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2608 auto const amt = XRP(1000);
2609 std::uint32_t const claimID = 1;
2610 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2611
2612 BalanceTransfer transfer(
2613 scEnv,
2615 scBob,
2616 scAlice,
2617 &payees[0],
2619 withClaim);
2620
2621 scEnv
2623 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2624 .close();
2625 if (withClaim)
2626 {
2627 BEAST_EXPECT(transfer.has_not_happened());
2628
2629 // submit a claim transaction with the wrong account (scGw
2630 // instead of scAlice)
2631 scEnv.tx(xchain_claim(scGw, jvb, claimID, amt, scBob), ter(tecXCHAIN_BAD_CLAIM_ID))
2632 .close();
2633 BEAST_EXPECT(transfer.has_not_happened());
2634 }
2635 else
2636 {
2637 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2638 }
2639 }
2640
2641 // Claim against a claim id with no attestations
2642 // ---------------------------------------------
2643 for (auto withClaim : {false, true})
2644 {
2645 XEnv mcEnv(*this);
2646 XEnv scEnv(*this, true);
2647
2648 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2649
2652 .close()
2654 .close();
2655
2656 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2657 auto const amt = XRP(1000);
2658 std::uint32_t const claimID = 1;
2659 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2660
2661 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2662
2663 // don't send any attestations
2664
2665 if (withClaim)
2666 {
2667 BEAST_EXPECT(transfer.has_not_happened());
2668
2669 // need to submit a claim transactions
2670 scEnv
2671 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
2673 .close();
2674 }
2675
2676 BEAST_EXPECT(transfer.has_not_happened());
2677 }
2678
2679 // Claim against a claim id with attestations, but not enough to
2680 // make a quorum
2681 // --------------------------------------------------------------------
2682 for (auto withClaim : {false, true})
2683 {
2684 XEnv mcEnv(*this);
2685 XEnv scEnv(*this, true);
2686
2687 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2688
2691 .close()
2693 .close();
2694
2695 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2696 auto const amt = XRP(1000);
2697 std::uint32_t const claimID = 1;
2698 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2699
2700 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2701
2702 auto tooFew = quorum - 1;
2703 scEnv
2705 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers, tooFew))
2706 .close();
2707 if (withClaim)
2708 {
2709 BEAST_EXPECT(transfer.has_not_happened());
2710
2711 // need to submit a claim transactions
2712 scEnv
2713 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
2715 .close();
2716 }
2717
2718 BEAST_EXPECT(transfer.has_not_happened());
2719 }
2720
2721 // Claim id of zero
2722 // ----------------
2723 for (auto withClaim : {false, true})
2724 {
2725 XEnv mcEnv(*this);
2726 XEnv scEnv(*this, true);
2727
2728 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2729
2732 .close()
2734 .close();
2735
2736 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2737 auto const amt = XRP(1000);
2738 std::uint32_t const claimID = 1;
2739 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2740
2741 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2742
2743 scEnv
2744 .multiTx(
2746 scAttester, jvb, mcAlice, amt, payees, true, 0, dst, signers),
2748 .close();
2749 if (withClaim)
2750 {
2751 BEAST_EXPECT(transfer.has_not_happened());
2752
2753 // need to submit a claim transactions
2755 .close();
2756 }
2757
2758 BEAST_EXPECT(transfer.has_not_happened());
2759 }
2760
2761 // Claim issue that does not match the expected issue on the bridge
2762 // (either LockingChainIssue or IssuingChainIssue, depending on the
2763 // chain). The claim id should already have enough attestations to
2764 // reach a quorum for this amount (for a different issuer).
2765 // ---------------------------------------------------------------------
2766 for (auto withClaim : {true})
2767 {
2768 XEnv mcEnv(*this);
2769 XEnv scEnv(*this, true);
2770
2771 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2772
2775 .close()
2777 .close();
2778
2779 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2780 auto const amt = XRP(1000);
2781 std::uint32_t const claimID = 1;
2782 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2783
2784 BalanceTransfer transfer(
2785 scEnv,
2787 scBob,
2788 scAlice,
2789 &payees[0],
2791 withClaim);
2792
2793 scEnv
2795 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2796 .close();
2797
2798 if (withClaim)
2799 {
2800 BEAST_EXPECT(transfer.has_not_happened());
2801
2802 // need to submit a claim transactions
2803 scEnv
2804 .tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob), ter(temBAD_AMOUNT))
2805 .close();
2806 }
2807
2808 BEAST_EXPECT(transfer.has_not_happened());
2809 }
2810
2811 // Claim to a destination that does not already exist on the chain
2812 // -----------------------------------------------------------------
2813 for (auto withClaim : {true})
2814 {
2815 XEnv mcEnv(*this);
2816 XEnv scEnv(*this, true);
2817
2818 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2819
2822 .close()
2824 .close();
2825
2826 auto dst(withClaim ? std::nullopt : std::optional<Account>{scuBob});
2827 auto const amt = XRP(1000);
2828 std::uint32_t const claimID = 1;
2829 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2830
2831 BalanceTransfer transfer(
2832 scEnv,
2834 scBob,
2835 scAlice,
2836 &payees[0],
2838 withClaim);
2839
2840 scEnv
2842 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2843 .close();
2844 if (withClaim)
2845 {
2846 BEAST_EXPECT(transfer.has_not_happened());
2847
2848 // need to submit a claim transactions
2849 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scuBob), ter(tecNO_DST)).close();
2850 }
2851
2852 BEAST_EXPECT(transfer.has_not_happened());
2853 }
2854
2855 // Claim where the claim id owner does not have enough XRP to pay
2856 // the reward
2857 // ------------------------------------------------------------------
2858 for (auto withClaim : {false, true})
2859 {
2860 XEnv mcEnv(*this);
2861 XEnv scEnv(*this, true);
2862
2863 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2864 STAmount const huge_reward{XRP(20000)};
2865 BEAST_EXPECT(huge_reward > scEnv.balance(scAlice));
2866
2867 scEnv.tx(create_bridge(Account::master, jvb, huge_reward))
2869 .close()
2870 .tx(xchain_create_claim_id(scAlice, jvb, huge_reward, mcAlice))
2871 .close();
2872
2873 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2874 auto const amt = XRP(1000);
2875 std::uint32_t const claimID = 1;
2876 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2877
2878 BalanceTransfer transfer(
2879 scEnv,
2881 scBob,
2882 scAlice,
2883 &payees[0],
2885 withClaim);
2886
2887 if (withClaim)
2888 {
2889 scEnv
2891 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2892 .close();
2893 BEAST_EXPECT(transfer.has_not_happened());
2894
2895 // need to submit a claim transactions
2896 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT))
2897 .close();
2898 }
2899 else
2900 {
2901 auto txns = claim_attestations(
2902 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers);
2903 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
2904 {
2905 scEnv.tx(txns[i]).close();
2906 }
2907 scEnv.tx(txns.back());
2908 scEnv.close();
2909 // The attestation should succeed, because it adds an
2910 // attestation, but the claim should fail with insufficient
2911 // funds
2912 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT))
2913 .close();
2914 }
2915
2916 BEAST_EXPECT(transfer.has_not_happened());
2917 }
2918
2919 // Claim where the claim id owner has enough XRP to pay the reward,
2920 // but it would put his balance below the reserve
2921 // --------------------------------------------------------------------
2922 for (auto withClaim : {false, true})
2923 {
2924 XEnv mcEnv(*this);
2925 XEnv scEnv(*this, true);
2926
2927 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2928
2931 .fund(
2932 res0 + reward,
2933 scuAlice) // just not enough because of fees
2934 .close()
2937 .close();
2938
2939 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2940 auto const amt = XRP(1000);
2941 std::uint32_t const claimID = 1;
2942 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2943
2944 BalanceTransfer transfer(scEnv, Account::master, scBob, scuAlice, payees, withClaim);
2945
2946 scEnv
2948 scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]),
2950 .close();
2951 if (withClaim)
2952 {
2953 BEAST_EXPECT(transfer.has_not_happened());
2954
2955 // need to submit a claim transactions
2956 scEnv
2957 .tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob),
2959 .close();
2960 }
2961
2962 BEAST_EXPECT(transfer.has_not_happened());
2963 }
2964
2965 // Pay to an account with deposit auth set
2966 // ---------------------------------------
2967 for (auto withClaim : {false, true})
2968 {
2969 XEnv mcEnv(*this);
2970 XEnv scEnv(*this, true);
2971
2972 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2973
2976 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
2977 .close()
2979 .close();
2980
2981 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2982 auto const amt = XRP(1000);
2983 std::uint32_t const claimID = 1;
2984 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2985
2986 BalanceTransfer transfer(
2987 scEnv,
2989 scBob,
2990 scAlice,
2991 &payees[0],
2993 withClaim);
2994 auto txns = claim_attestations(
2995 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers);
2996 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
2997 {
2998 scEnv.tx(txns[i]).close();
2999 }
3000 if (withClaim)
3001 {
3002 scEnv.tx(txns.back()).close();
3003
3004 BEAST_EXPECT(transfer.has_not_happened());
3005
3006 // need to submit a claim transactions
3007 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION))
3008 .close();
3009
3010 // the transfer failed, but check that we can still use the
3011 // claimID with a different account
3012 Balance const scCarol_bal(scEnv, scCarol);
3013
3014 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3015 BEAST_EXPECT(scCarol_bal.diff() == amt);
3016 }
3017 else
3018 {
3019 scEnv.tx(txns.back()).close();
3020 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION))
3021 .close();
3022 // A way would be to remove deposit auth and resubmit the
3023 // attestations (even though the witness servers won't do
3024 // it)
3025 scEnv
3026 .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth
3027 .close();
3028
3029 Balance const scBob_bal(scEnv, scBob);
3030 scEnv.tx(txns.back()).close();
3031 BEAST_EXPECT(scBob_bal.diff() == amt);
3032 }
3033 }
3034
3035 // Pay to an account with Destination Tag set
3036 // ------------------------------------------
3037 for (auto withClaim : {false, true})
3038 {
3039 XEnv mcEnv(*this);
3040 XEnv scEnv(*this, true);
3041
3042 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3043
3046 .tx(fset("scBob", asfRequireDest)) // set dest tag
3047 .close()
3049 .close();
3050
3051 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3052 auto const amt = XRP(1000);
3053 std::uint32_t const claimID = 1;
3054 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3055
3056 BalanceTransfer transfer(
3057 scEnv,
3059 scBob,
3060 scAlice,
3061 &payees[0],
3063 withClaim);
3064 auto txns = claim_attestations(
3065 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers);
3066 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3067 {
3068 scEnv.tx(txns[i]).close();
3069 }
3070 if (withClaim)
3071 {
3072 scEnv.tx(txns.back()).close();
3073 BEAST_EXPECT(transfer.has_not_happened());
3074
3075 // need to submit a claim transactions
3076 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED))
3077 .close();
3078
3079 // the transfer failed, but check that we can still use the
3080 // claimID with a different account
3081 Balance const scCarol_bal(scEnv, scCarol);
3082
3083 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3084 BEAST_EXPECT(scCarol_bal.diff() == amt);
3085 }
3086 else
3087 {
3088 scEnv.tx(txns.back()).close();
3089 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED))
3090 .close();
3091 // A way would be to remove the destination tag requirement
3092 // and resubmit the attestations (even though the witness
3093 // servers won't do it)
3094 scEnv
3095 .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag
3096 .close();
3097
3098 Balance const scBob_bal(scEnv, scBob);
3099
3100 scEnv.tx(txns.back()).close();
3101 BEAST_EXPECT(scBob_bal.diff() == amt);
3102 }
3103 }
3104
3105 // Pay to an account with deposit auth set. Check that the attestations
3106 // are still validated and that we can used the claimID to transfer the
3107 // funds to a different account (which doesn't have deposit auth set)
3108 // --------------------------------------------------------------------
3109 {
3110 XEnv mcEnv(*this);
3111 XEnv scEnv(*this, true);
3112
3113 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3114
3117 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3118 .close()
3120 .close();
3121
3122 auto dst(std::optional<Account>{scBob});
3123 auto const amt = XRP(1000);
3124 std::uint32_t const claimID = 1;
3125 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3126
3127 // we should be able to submit the attestations, but the transfer
3128 // should not occur because dest account has deposit auth set
3129 Balance const scBob_bal(scEnv, scBob);
3130
3132 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
3133 BEAST_EXPECT(scBob_bal.diff() == STAmount(0));
3134
3135 // Check that check that we still can use the claimID to transfer
3136 // the amount to a different account
3137 Balance const scCarol_bal(scEnv, scCarol);
3138
3139 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3140 BEAST_EXPECT(scCarol_bal.diff() == amt);
3141 }
3142
3143 // Claim where the amount different from what is attested to
3144 // ---------------------------------------------------------
3145 for (auto withClaim : {true})
3146 {
3147 XEnv mcEnv(*this);
3148 XEnv scEnv(*this, true);
3149
3150 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3151
3154 .close()
3156 .close();
3157
3158 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3159 auto const amt = XRP(1000);
3160 std::uint32_t const claimID = 1;
3161 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3162
3163 BalanceTransfer transfer(
3164 scEnv,
3166 scBob,
3167 scAlice,
3168 &payees[0],
3170 withClaim);
3172 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
3173 if (withClaim)
3174 {
3175 BEAST_EXPECT(transfer.has_not_happened());
3176
3177 // claim wrong amount
3178 scEnv
3179 .tx(xchain_claim(scAlice, jvb, claimID, one_xrp, scBob),
3181 .close();
3182 }
3183
3184 BEAST_EXPECT(transfer.has_not_happened());
3185 }
3186
3187 // Verify that rewards are paid from the account that owns the claim
3188 // id
3189 // --------------------------------------------------------------------
3190 for (auto withClaim : {false, true})
3191 {
3192 XEnv mcEnv(*this);
3193 XEnv scEnv(*this, true);
3194
3195 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3196
3199 .close()
3201 .close();
3202
3203 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3204 auto const amt = XRP(1000);
3205 std::uint32_t const claimID = 1;
3206 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3207
3208 BalanceTransfer transfer(
3209 scEnv,
3211 scBob,
3212 scAlice,
3213 &payees[0],
3215 withClaim);
3216 Balance const scAlice_bal(scEnv, scAlice);
3218 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
3219
3220 STAmount claim_cost = reward;
3221
3222 if (withClaim)
3223 {
3224 BEAST_EXPECT(transfer.has_not_happened());
3225
3226 // need to submit a claim transactions
3227 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
3228 claim_cost += tx_fee;
3229 }
3230
3231 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3232 BEAST_EXPECT(scAlice_bal.diff() == -claim_cost); // because reward % 4 == 0
3233 }
3234
3235 // Verify that if a reward is not evenly divisible among the reward
3236 // accounts, the remaining amount goes to the claim id owner.
3237 // ----------------------------------------------------------------
3238 for (auto withClaim : {false, true})
3239 {
3240 XEnv mcEnv(*this);
3241 XEnv scEnv(*this, true);
3242
3244
3247 .close()
3249 .close();
3250
3251 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3252 auto const amt = XRP(1000);
3253 std::uint32_t const claimID = 1;
3254 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3255
3256 BalanceTransfer transfer(
3257 scEnv,
3259 scBob,
3260 scAlice,
3261 &payees[0],
3263 withClaim);
3264 Balance const scAlice_bal(scEnv, scAlice);
3266 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
3267 STAmount claim_cost = tiny_reward;
3268
3269 if (withClaim)
3270 {
3271 BEAST_EXPECT(transfer.has_not_happened());
3272
3273 // need to submit a claim transactions
3274 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
3275 claim_cost += tx_fee;
3276 }
3277
3278 BEAST_EXPECT(transfer.has_happened(amt, tiny_reward_split));
3279 BEAST_EXPECT(scAlice_bal.diff() == -(claim_cost - tiny_reward_remainder));
3280 }
3281
3282 // If a reward distribution fails for one of the reward accounts
3283 // (the reward account doesn't exist or has deposit auth set), then
3284 // the txn should still succeed, but that portion should go to the
3285 // claim id owner.
3286 // -------------------------------------------------------------------
3287 for (auto withClaim : {false, true})
3288 {
3289 XEnv mcEnv(*this);
3290 XEnv scEnv(*this, true);
3291
3292 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3293
3294 std::vector<Account> alt_payees{payees.begin(), payees.end() - 1};
3295 alt_payees.back() = Account("inexistent");
3296
3299 .close()
3301 .close();
3302
3303 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3304 auto const amt = XRP(1000);
3305 std::uint32_t const claimID = 1;
3306 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3307
3308 BalanceTransfer transfer(
3309 scEnv,
3311 scBob,
3312 scAlice,
3313 &payees[0],
3315 withClaim);
3317 scAttester, jvb, mcAlice, amt, alt_payees, true, claimID, dst, signers));
3318
3319 if (withClaim)
3320 {
3321 BEAST_EXPECT(transfer.has_not_happened());
3322
3323 // need to submit a claim transactions
3324 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
3325 }
3326
3327 // this also checks that only 3 * split_reward was deducted from
3328 // scAlice (the payer account), since we passed alt_payees to
3329 // BalanceTransfer
3330 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3331 }
3332
3333 for (auto withClaim : {false, true})
3334 {
3335 XEnv mcEnv(*this);
3336 XEnv scEnv(*this, true);
3337
3338 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3339 auto& unpaid = payees[UT_XCHAIN_DEFAULT_QUORUM - 1];
3342 .tx(fset(unpaid, asfDepositAuth))
3343 .close()
3345 .close();
3346
3347 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3348 auto const amt = XRP(1000);
3349 std::uint32_t const claimID = 1;
3350 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3351
3352 // balance of last signer should not change (has deposit auth)
3353 Balance const last_signer(scEnv, unpaid);
3354
3355 // make sure all signers except the last one get the
3356 // split_reward
3357
3358 BalanceTransfer transfer(
3359 scEnv,
3361 scBob,
3362 scAlice,
3363 &payees[0],
3365 withClaim);
3367 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
3368
3369 if (withClaim)
3370 {
3371 BEAST_EXPECT(transfer.has_not_happened());
3372
3373 // need to submit a claim transactions
3374 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
3375 }
3376
3377 // this also checks that only 3 * split_reward was deducted from
3378 // scAlice (the payer account), since we passed payees.size() -
3379 // 1 to BalanceTransfer
3380 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3381
3382 // and make sure the account with deposit auth received nothing
3383 BEAST_EXPECT(last_signer.diff() == STAmount(0));
3384 }
3385
3386 // coverage test: xchain_claim transaction with incorrect flag
3387 XEnv(*this, true)
3389 .close()
3390 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3391 txflags(tfFillOrKill),
3393 .close();
3394
3395 // coverage test: xchain_claim transaction with xchain feature
3396 // disabled
3397 XEnv(*this, true)
3399 .disableFeature(featureXChainBridge)
3400 .close()
3402 .close();
3403
3404 // coverage test: XChainClaim::preclaim - isLockingChain = true;
3405 XEnv(*this)
3407 .close()
3409 }
3410
3411 void
3413 {
3414 using namespace jtx;
3415
3416 testcase("Bridge Create Account");
3417 XRPAmount const tx_fee = txFee();
3418
3419 // coverage test: transferHelper() - dst == src
3420 {
3421 XEnv scEnv(*this, true);
3422
3423 auto const amt = XRP(111);
3424 auto const amt_plus_reward = amt + reward;
3425
3428 .close();
3429
3430 Balance const door(scEnv, Account::master);
3431
3432 // scEnv.tx(att_create_acct_batch1(1, amt,
3433 // Account::master)).close();
3434 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2)).close();
3435 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
3436 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
3437
3438 // scEnv.tx(att_create_acct_batch2(1, amt,
3439 // Account::master)).close();
3440 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2, 2)).close();
3441 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
3442 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
3443
3444 BEAST_EXPECT(door.diff() == -reward);
3445 }
3446
3447 // Check that creating an account with less than the minimum create
3448 // amount fails.
3449 {
3450 XEnv mcEnv(*this);
3451
3452 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3453
3454 Balance const door(mcEnv, mcDoor);
3455 Balance const carol(mcEnv, mcCarol);
3456
3457 mcEnv
3460 .close();
3461
3462 BEAST_EXPECT(door.diff() == STAmount(0));
3463 BEAST_EXPECT(carol.diff() == -tx_fee);
3464 }
3465
3466 // Check that creating an account with invalid flags fails.
3467 {
3468 XEnv mcEnv(*this);
3469
3470 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3471
3472 Balance const door(mcEnv, mcDoor);
3473
3474 mcEnv
3476 txflags(tfFillOrKill),
3478 .close();
3479
3480 BEAST_EXPECT(door.diff() == STAmount(0));
3481 }
3482
3483 // Check that creating an account with the XChainBridge feature
3484 // disabled fails.
3485 {
3486 XEnv mcEnv(*this);
3487
3488 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3489
3490 Balance const door(mcEnv, mcDoor);
3491
3492 mcEnv.disableFeature(featureXChainBridge)
3495 .close();
3496
3497 BEAST_EXPECT(door.diff() == STAmount(0));
3498 }
3499
3500 // Check that creating an account with a negative amount fails
3501 {
3502 XEnv mcEnv(*this);
3503
3504 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3505
3506 Balance const door(mcEnv, mcDoor);
3507
3508 mcEnv
3511 .close();
3512
3513 BEAST_EXPECT(door.diff() == STAmount(0));
3514 }
3515
3516 // Check that creating an account with a negative reward fails
3517 {
3518 XEnv mcEnv(*this);
3519
3520 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3521
3522 Balance const door(mcEnv, mcDoor);
3523
3524 mcEnv
3527 .close();
3528
3529 BEAST_EXPECT(door.diff() == STAmount(0));
3530 }
3531
3532 // Check that door account can't lock funds onto itself
3533 {
3534 XEnv mcEnv(*this);
3535
3536 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3537
3538 Balance const door(mcEnv, mcDoor);
3539
3540 mcEnv
3543 .close();
3544
3545 BEAST_EXPECT(door.diff() == -tx_fee);
3546 }
3547
3548 // Check that reward matches the amount specified in bridge
3549 {
3550 XEnv mcEnv(*this);
3551
3552 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3553
3554 Balance const door(mcEnv, mcDoor);
3555
3556 mcEnv
3559 .close();
3560
3561 BEAST_EXPECT(door.diff() == STAmount(0));
3562 }
3563 }
3564
3565 void
3567 {
3568 using namespace jtx;
3569 XRPAmount const res0 = reserve(0);
3570 XRPAmount const tx_fee = txFee();
3571
3572 testcase("Fee dips into reserve");
3573
3574 // commit where the fee dips into the reserve, this should succeed
3575 XEnv(*this)
3577 .fund(res0 + one_xrp + tx_fee - drops(1), mcuAlice)
3578 .close()
3580
3581 // commit where the commit amount drips into the reserve, this should
3582 // fail
3583 XEnv(*this)
3585 .fund(res0 + one_xrp - drops(1), mcuAlice)
3586 .close()
3588
3589 auto const minAccountCreate = XRP(20);
3590
3591 // account create commit where the fee dips into the reserve,
3592 // this should succeed
3593 XEnv(*this)
3594 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
3595 .fund(res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice)
3596 .close()
3598 ter(tesSUCCESS));
3599
3600 // account create commit where the commit dips into the reserve,
3601 // this should fail
3602 XEnv(*this)
3603 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
3604 .fund(res0 + minAccountCreate + reward - drops(1), mcuAlice)
3605 .close()
3608 }
3609
3610 void
3612 {
3613 using namespace jtx;
3614
3615 testcase("Bridge Delete Door Account");
3616
3617 auto const acctDelFee{drops(XEnv(*this).env_.current()->fees().increment)};
3618
3619 // Deleting an account that owns bridge should fail
3620 {
3621 XEnv mcEnv(*this);
3622
3623 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1))).close();
3624
3625 // We don't allow an account to be deleted if its sequence
3626 // number is within 256 of the current ledger.
3627 for (size_t i = 0; i < 256; ++i)
3628 mcEnv.close();
3629
3630 // try to delete mcDoor, send funds to mcAlice
3631 mcEnv.tx(acctdelete(mcDoor, mcAlice), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
3632 }
3633
3634 // Deleting an account that owns a claim id should fail
3635 {
3636 XEnv scEnv(*this, true);
3637
3639 .close()
3641 .close();
3642
3643 // We don't allow an account to be deleted if its sequence
3644 // number is within 256 of the current ledger.
3645 for (size_t i = 0; i < 256; ++i)
3646 scEnv.close();
3647
3648 // try to delete scAlice, send funds to scBob
3649 scEnv.tx(acctdelete(scAlice, scBob), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
3650 }
3651 }
3652
3653 void
3655 {
3656 using namespace jtx;
3657
3658 testcase("Bad attestations");
3659 {
3660 // Create a bridge and add an attestation with a bad public key
3661 XEnv scEnv(*this, true);
3662 std::uint32_t const claimID = 1;
3663 std::optional<Account> const dst{scBob};
3664 auto const amt = XRP(1000);
3667 .close();
3669 auto jvAtt = claim_attestation(
3670 scAttester,
3671 jvb,
3672 mcAlice,
3673 amt,
3675 true,
3676 claimID,
3677 dst,
3679 {
3680 // Change to an invalid keytype
3681 auto k = jvAtt["PublicKey"].asString();
3682 k.at(1) = '9';
3683 jvAtt["PublicKey"] = k;
3684 }
3685 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
3686 }
3687 {
3688 // Create a bridge and add an create account attestation with a bad
3689 // public key
3690 XEnv scEnv(*this, true);
3691 std::uint32_t const createCount = 1;
3692 Account const dst{scBob};
3693 auto const amt = XRP(1000);
3694 auto const rewardAmt = XRP(1);
3697 .close();
3698 auto jvAtt = create_account_attestation(
3699 scAttester,
3700 jvb,
3701 mcAlice,
3702 amt,
3703 rewardAmt,
3705 true,
3706 createCount,
3707 dst,
3709 {
3710 // Change to an invalid keytype
3711 auto k = jvAtt["PublicKey"].asString();
3712 k.at(1) = '9';
3713 jvAtt["PublicKey"] = k;
3714 }
3715 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
3716 }
3717 }
3718
3719 void
3738};
3739
3740// -----------------------------------------------------------
3741// -----------------------------------------------------------
3743{
3744private:
3745 static constexpr size_t num_signers = 5;
3746
3747 // --------------------------------------------------
3748 enum class WithClaim { no, yes };
3760
3771
3773 using BridgeID = BridgeDef const*;
3774
3775 // tracking chain state
3776 // --------------------
3778 {
3781
3782 void
3783 init(ENV& env, jtx::Account const& acct)
3784 {
3785 startAmount = env.balance(acct);
3787 }
3788
3789 bool
3790 verify(ENV& env, jtx::Account const& acct) const
3791 {
3792 STAmount const diff{env.balance(acct) - startAmount};
3793 bool const check = diff == expectedDiff;
3794 return check;
3795 }
3796 };
3797
3798 // --------------------------------------------------
3800 {
3804
3805 ChainStateTrack(ENV& env) : env(env), tx_fee(env.env_.current()->fees().base)
3806 {
3807 }
3808
3809 void
3810 sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec& claims)
3811 {
3812 for (auto const& c : claims)
3813 {
3814 env.tx(c).close();
3815 spendFee(bridge->signers[signer_idx].account);
3816 }
3817 claims.clear();
3818 }
3819
3820 uint32_t
3822 {
3823 size_t num_successful = 0;
3824 for (auto const& c : claims)
3825 {
3826 env.tx(c).close();
3827 if (env.ter() == tesSUCCESS)
3828 {
3829 counters[bridge].signers.push_back(signer_idx);
3830 num_successful++;
3831 }
3832 spendFee(bridge->signers[signer_idx].account);
3833 }
3834 claims.clear();
3835 return num_successful;
3836 }
3837
3838 void
3840 {
3841 bool callback_called = false;
3842
3843 // we have this "do {} while" loop because we want to process
3844 // all the account create which can reach quorum at this time
3845 // stamp.
3846 do
3847 {
3848 callback_called = false;
3849 // cspell: ignore attns
3850 for (size_t i = 0; i < signers_attns.size(); ++i)
3851 {
3852 for (auto& [bridge, claims] : signers_attns[i])
3853 {
3854 sendAttestations(i, bridge, claims.xfer_claims);
3855
3856 auto& c = counters[bridge];
3857 auto& create_claims = claims.create_claims[c.claim_count];
3858 auto num_attns = create_claims.size();
3859 if (num_attns != 0u)
3860 {
3861 c.num_create_attn_sent +=
3862 sendCreateAttestations(i, bridge, create_claims);
3863 }
3864 assert(claims.create_claims[c.claim_count].empty());
3865 }
3866 }
3867 for (auto& [bridge, c] : counters)
3868 {
3869 if (c.num_create_attn_sent >= bridge->quorum)
3870 {
3871 callback_called = true;
3872 c.create_callbacks[c.claim_count](c.signers);
3873 ++c.claim_count;
3874 c.num_create_attn_sent = 0;
3875 c.signers.clear();
3876 }
3877 }
3878 } while (callback_called);
3879 }
3880
3881 void
3882 init(jtx::Account const& acct)
3883 {
3884 accounts[acct].init(env, acct);
3885 }
3886
3887 void
3888 receive(jtx::Account const& acct, STAmount amt, std::uint64_t divisor = 1)
3889 {
3890 if (amt.issue() != xrpIssue())
3891 return;
3892 auto it = accounts.find(acct);
3893 if (it == accounts.end())
3894 {
3895 accounts[acct].init(env, acct);
3896 // we just looked up the account, so expectedDiff == 0
3897 }
3898 else
3899 {
3900 it->second.expectedDiff +=
3901 (divisor == 1 ? amt : divide(amt, STAmount(amt.issue(), divisor), amt.issue()));
3902 }
3903 }
3904
3905 void
3906 spend(jtx::Account const& acct, STAmount amt, std::uint64_t times = 1)
3907 {
3908 if (amt.issue() != xrpIssue())
3909 return;
3910 receive(
3911 acct,
3912 times == 1 ? -amt : -multiply(amt, STAmount(amt.issue(), times), amt.issue()));
3913 }
3914
3915 void
3916 transfer(jtx::Account const& from, jtx::Account const& to, STAmount amt)
3917 {
3918 spend(from, amt);
3919 receive(to, amt);
3920 }
3921
3922 void
3923 spendFee(jtx::Account const& acct, size_t times = 1)
3924 {
3925 spend(acct, tx_fee, times);
3926 }
3927
3928 bool
3929 verify() const
3930 {
3931 for (auto const& [acct, state] : accounts)
3932 {
3933 if (!state.verify(env, acct))
3934 return false;
3935 }
3936 return true;
3937 }
3938
3940 {
3942
3944 uint32_t create_count{0}; // for account create. First should be 1
3945 uint32_t claim_count{0}; // for account create. Increments after quorum for
3946 // current create_count (starts at 1) is reached.
3947
3948 uint32_t num_create_attn_sent{0}; // for current claim_count
3951 };
3952
3958
3961
3967 };
3968
3970 {
3971 ChainStateTracker(ENV& a_env, ENV& b_env) : a_(a_env), b_(b_env)
3972 {
3973 }
3974
3975 bool
3976 verify() const
3977 {
3978 return a_.verify() && b_.verify();
3979 }
3980
3981 void
3983 {
3986 }
3987
3988 void
3989 init(jtx::Account const& acct)
3990 {
3991 a_.init(acct);
3992 b_.init(acct);
3993 }
3994
3997 };
3998
4007
4008 enum Act_Flags { af_a2b = 1 << 0 };
4009
4010 // --------------------------------------------------
4011 template <class T>
4013 {
4015 : bridge_(bridge), st_(chainstate)
4016 {
4017 }
4018
4019 public:
4022 {
4023 return static_cast<T&>(*this).a2b() ? st_->a_ : st_->b_;
4024 }
4025
4028 {
4029 return static_cast<T&>(*this).a2b() ? st_->b_ : st_->a_;
4030 }
4031
4032 jtx::Account const&
4034 {
4035 return static_cast<T&>(*this).a2b() ? bridge_.doorA : bridge_.doorB;
4036 }
4037
4038 jtx::Account const&
4040 {
4041 return static_cast<T&>(*this).a2b() ? bridge_.doorB : bridge_.doorA;
4042 }
4043
4044 protected:
4047
4048 friend T;
4049 };
4050
4051 // --------------------------------------------------
4052 class SmCreateAccount : public SmBase<SmCreateAccount>
4053 {
4054 public:
4056
4058 std::shared_ptr<ChainStateTracker> const& chainstate,
4059 BridgeDef const& bridge,
4061 : Base(chainstate, bridge), cr(std::move(create))
4062 {
4063 }
4064
4065 bool
4066 a2b() const
4067 {
4068 return cr.a2b;
4069 }
4070
4071 uint32_t
4073 {
4074 ChainStateTrack& st = srcState();
4075 jtx::Account const& srcdoor = srcDoor();
4076
4077 st.env
4079 .close(); // needed for claim_id sequence to be correct'
4080 st.spendFee(cr.from);
4081 st.transfer(cr.from, srcdoor, cr.amt);
4082 st.transfer(cr.from, srcdoor, cr.reward);
4083
4084 return ++st.counters[&bridge_].create_count;
4085 }
4086
4087 void
4089 {
4090 ChainStateTrack& st = destState();
4091
4092 // check all signers, but start at a random one
4093 size_t i = 0;
4094 for (i = 0; i < num_signers; ++i)
4095 {
4096 size_t const signer_idx = (rnd + i) % num_signers;
4097
4098 if (!(cr.attested[signer_idx]))
4099 {
4100 // enqueue one attestation for this signer
4101 cr.attested[signer_idx] = true;
4102
4103 st.signers_attns[signer_idx][&bridge_]
4104 .create_claims[cr.claim_id - 1]
4105 .emplace_back(create_account_attestation(
4106 bridge_.signers[signer_idx].account,
4107 bridge_.jvb,
4108 cr.from,
4109 cr.amt,
4110 cr.reward,
4111 bridge_.signers[signer_idx].account,
4112 cr.a2b,
4113 cr.claim_id,
4114 cr.to,
4115 bridge_.signers[signer_idx]));
4116 break;
4117 }
4118 }
4119
4120 if (i == num_signers)
4121 return; // did not attest
4122
4123 auto& counters = st.counters[&bridge_];
4124 if (counters.create_callbacks.size() < cr.claim_id)
4125 counters.create_callbacks.resize(cr.claim_id);
4126
4127 auto complete_cb = [&](std::vector<size_t> const& signers) {
4128 auto num_attestors = signers.size();
4129 st.env.close();
4130 assert(num_attestors <= std::count(cr.attested.begin(), cr.attested.end(), true));
4131 assert(num_attestors >= bridge_.quorum);
4132 assert(cr.claim_id - 1 == counters.claim_count);
4133
4134 auto r = cr.reward;
4135 auto reward = divide(r, STAmount(num_attestors), r.issue());
4136
4137 for (auto i : signers)
4138 st.receive(bridge_.signers[i].account, reward);
4139
4140 st.spend(dstDoor(), reward, num_attestors);
4141 st.transfer(dstDoor(), cr.to, cr.amt);
4142 st.env.env_.memoize(cr.to);
4144 };
4145
4146 counters.create_callbacks[cr.claim_id - 1] = std::move(complete_cb);
4147 }
4148
4149 SmState
4151 {
4152 switch (sm_state)
4153 {
4154 case st_initial:
4157 break;
4158
4159 case st_attesting:
4160 attest(time, rnd);
4161 break;
4162
4163 default:
4164 assert(0);
4165 break;
4166
4167 case st_completed:
4168 break; // will get this once
4169 }
4170 return sm_state;
4171 }
4172
4173 private:
4176 };
4177
4178 // --------------------------------------------------
4179 class SmTransfer : public SmBase<SmTransfer>
4180 {
4181 public:
4183
4185 std::shared_ptr<ChainStateTracker> const& chainstate,
4186 BridgeDef const& bridge,
4187 Transfer xfer)
4188 : Base(chainstate, bridge), xfer(std::move(xfer))
4189 {
4190 }
4191
4192 bool
4193 a2b() const
4194 {
4195 return xfer.a2b;
4196 }
4197
4198 uint32_t
4200 {
4201 ChainStateTrack& st = destState();
4202
4204 .close(); // needed for claim_id sequence to be
4205 // correct'
4206 st.spendFee(xfer.to);
4207 return ++st.counters[&bridge_].claim_id;
4208 }
4209
4210 void
4212 {
4213 ChainStateTrack& st = srcState();
4214 jtx::Account const& srcdoor = srcDoor();
4215
4216 if (xfer.amt.issue() != xrpIssue())
4217 {
4218 st.env.tx(pay(srcdoor, xfer.from, xfer.amt));
4219 st.spendFee(srcdoor);
4220 }
4221 st.env.tx(xchain_commit(
4222 xfer.from,
4223 bridge_.jvb,
4224 xfer.claim_id,
4225 xfer.amt,
4228 st.spendFee(xfer.from);
4229 st.transfer(xfer.from, srcdoor, xfer.amt);
4230 }
4231
4232 void
4234 {
4235 auto r = bridge_.reward;
4236 auto reward = divide(r, STAmount(bridge_.quorum), r.issue());
4237
4238 for (size_t i = 0; i < num_signers; ++i)
4239 {
4240 if (xfer.attested[i])
4241 st.receive(bridge_.signers[i].account, reward);
4242 }
4244 }
4245
4246 bool
4248 {
4249 ChainStateTrack& st = destState();
4250
4251 // check all signers, but start at a random one
4252 for (size_t i = 0; i < num_signers; ++i)
4253 {
4254 size_t const signer_idx = (rnd + i) % num_signers;
4255 if (!(xfer.attested[signer_idx]))
4256 {
4257 // enqueue one attestation for this signer
4258 xfer.attested[signer_idx] = true;
4259
4260 st.signers_attns[signer_idx][&bridge_].xfer_claims.emplace_back(
4262 bridge_.signers[signer_idx].account,
4263 bridge_.jvb,
4264 xfer.from,
4265 xfer.amt,
4266 bridge_.signers[signer_idx].account,
4267 xfer.a2b,
4268 xfer.claim_id,
4270 ? std::nullopt
4272 bridge_.signers[signer_idx]));
4273 break;
4274 }
4275 }
4276
4277 // return true if quorum was reached, false otherwise
4278 bool const quorum =
4281 {
4284 }
4285 return quorum;
4286 }
4287
4288 void
4297
4298 SmState
4300 {
4301 switch (sm_state)
4302 {
4303 case st_initial:
4306 break;
4307
4309 commit();
4311 break;
4312
4313 case st_attesting:
4314 if (attest(time, rnd))
4315 {
4317 }
4318 else
4319 {
4321 }
4322 break;
4323
4324 case st_attested:
4325 assert(xfer.with_claim == WithClaim::yes);
4326 claim();
4328 break;
4329
4330 default:
4331 case st_completed:
4332 assert(0); // should have been removed
4333 break;
4334 }
4335 return sm_state;
4336 }
4337
4338 private:
4341 };
4342
4343 // --------------------------------------------------
4346
4348
4349 void
4351 uint64_t time,
4352 std::shared_ptr<ChainStateTracker> const& chainstate,
4353 BridgeDef const& bridge,
4354 Transfer transfer)
4355 {
4356 sm_.emplace_back(time, SmTransfer(chainstate, bridge, std::move(transfer)));
4357 }
4358
4359 void
4361 std::shared_ptr<ChainStateTracker> const& chainstate,
4362 BridgeDef const& bridge,
4364 {
4365 sm_.emplace_back(time, SmCreateAccount(chainstate, bridge, std::move(ac)));
4366 }
4367
4368public:
4369 void
4370 runSimulation(std::shared_ptr<ChainStateTracker> const& st, bool verify_balances = true)
4371 {
4372 using namespace jtx;
4373 uint64_t time = 0;
4374 std::mt19937 gen(27); // Standard mersenne_twister_engine
4376
4377 while (!sm_.empty())
4378 {
4379 ++time;
4380 for (auto it = sm_.begin(); it != sm_.end();)
4381 {
4382 auto vis = [&](auto& sm) {
4383 uint32_t const rnd = distrib(gen);
4384 return sm.advance(time, rnd);
4385 };
4386 auto& [t, sm] = *it;
4387 if (t <= time && std::visit(vis, sm) == st_completed)
4388 {
4389 it = sm_.erase(it);
4390 }
4391 else
4392 {
4393 ++it;
4394 }
4395 }
4396
4397 // send attestations
4398 st->sendAttestations();
4399
4400 // make sure all transactions have been applied
4401 st->a_.env.close();
4402 st->b_.env.close();
4403
4404 if (verify_balances)
4405 {
4406 BEAST_EXPECT(st->verify());
4407 }
4408 }
4409 }
4410
4411 void
4413 {
4414 using namespace jtx;
4415
4416 testcase("Bridge usage simulation");
4417
4418 XEnv mcEnv(*this);
4419 XEnv scEnv(*this, true);
4420
4421 auto st = std::make_shared<ChainStateTracker>(mcEnv, scEnv);
4422
4423 // create 10 accounts + door funded on both chains, and store
4424 // in ChainStateTracker the initial amount of these accounts
4425 Account doorXRPLocking("doorXRPLocking"), doorUSDLocking("doorUSDLocking"),
4426 doorUSDIssuing("doorUSDIssuing");
4427
4428 constexpr size_t num_acct = 10;
4429 auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() {
4430 using namespace std::literals;
4431 std::vector<Account> result;
4432 result.reserve(num_acct);
4433 for (int i = 0; i < num_acct; ++i)
4434 {
4435 result.emplace_back(
4436 "a"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
4437 }
4438 result.emplace_back("doorXRPLocking");
4439 doorXRPLocking = result.back();
4440 result.emplace_back("doorUSDLocking");
4441 doorUSDLocking = result.back();
4442 result.emplace_back("doorUSDIssuing");
4443 doorUSDIssuing = result.back();
4444 return result;
4445 }();
4446
4447 for (auto& acct : a)
4448 {
4449 STAmount const amt{XRP(100000)};
4450
4451 mcEnv.fund(amt, acct);
4452 scEnv.fund(amt, acct);
4453 }
4454 Account const USDLocking{"USDLocking"};
4455 IOU const usdLocking{USDLocking["USD"]};
4456 IOU const usdIssuing{doorUSDIssuing["USD"]};
4457
4458 mcEnv.fund(XRP(100000), USDLocking);
4459 mcEnv.close();
4460 mcEnv.tx(trust(doorUSDLocking, usdLocking(100000)));
4461 mcEnv.close();
4462 mcEnv.tx(pay(USDLocking, doorUSDLocking, usdLocking(50000)));
4463
4464 for (int i = 0; i < a.size(); ++i)
4465 {
4466 auto& acct{a[i]};
4467 if (i < num_acct)
4468 {
4469 mcEnv.tx(trust(acct, usdLocking(100000)));
4470 scEnv.tx(trust(acct, usdIssuing(100000)));
4471 }
4472 st->init(acct);
4473 }
4474 for (auto& s : signers)
4475 st->init(s.account);
4476
4477 st->b_.init(Account::master);
4478
4479 // also create some unfunded accounts
4480 constexpr size_t num_ua = 20;
4481 auto ua = []() {
4482 using namespace std::literals;
4483 std::vector<Account> result;
4484 result.reserve(num_ua);
4485 for (int i = 0; i < num_ua; ++i)
4486 {
4487 result.emplace_back(
4488 "ua"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
4489 }
4490 return result;
4491 }();
4492
4493 // initialize a bridge from a BridgeDef
4494 auto initBridge = [&mcEnv, &scEnv, &st](BridgeDef& bd) {
4495 bd.initBridge(mcEnv, scEnv);
4496 st->a_.spendFee(bd.doorA, 2);
4497 st->b_.spendFee(bd.doorB, 2);
4498 };
4499
4500 // create XRP -> XRP bridge
4501 // ------------------------
4502 BridgeDef xrp_b{
4503 doorXRPLocking,
4504 xrpIssue(),
4506 xrpIssue(),
4507 XRP(1),
4508 XRP(20),
4509 quorum,
4510 signers,
4512
4513 initBridge(xrp_b);
4514
4515 // create USD -> USD bridge
4516 // ------------------------
4517 BridgeDef usd_b{
4518 doorUSDLocking,
4519 usdLocking,
4520 doorUSDIssuing,
4521 usdIssuing,
4522 XRP(1),
4523 XRP(20),
4524 quorum,
4525 signers,
4527
4528 initBridge(usd_b);
4529
4530 // try a single account create + transfer to validate the simulation
4531 // engine. Do the transfer 8 time steps after the account create, to
4532 // give time enough for ua[0] to be funded now so it can reserve
4533 // the claimID
4534 // -----------------------------------------------------------------
4535 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, true});
4536 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), true});
4537 runSimulation(st);
4538
4539 // try the same thing in the other direction
4540 // -----------------------------------------
4541 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, false});
4542 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), false});
4543 runSimulation(st);
4544
4545 // run multiple XRP transfers
4546 // --------------------------
4547 xfer(0, st, xrp_b, {a[0], a[0], a[1], XRP(6), true, WithClaim::no});
4548 xfer(1, st, xrp_b, {a[0], a[0], a[1], XRP(8), false, WithClaim::no});
4549 xfer(1, st, xrp_b, {a[1], a[1], a[1], XRP(1), true});
4550 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(3), false});
4551 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(5), false});
4552 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(7), false, WithClaim::no});
4553 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(9), true});
4554 runSimulation(st);
4555
4556 // run one USD transfer
4557 // --------------------
4558 xfer(0, st, usd_b, {a[0], a[1], a[2], usdLocking(3), true});
4559 runSimulation(st);
4560
4561 // run multiple USD transfers
4562 // --------------------------
4563 xfer(0, st, usd_b, {a[0], a[0], a[1], usdLocking(6), true});
4564 xfer(1, st, usd_b, {a[0], a[0], a[1], usdIssuing(8), false});
4565 xfer(1, st, usd_b, {a[1], a[1], a[1], usdLocking(1), true});
4566 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(3), false});
4567 xfer(2, st, usd_b, {a[1], a[1], a[1], usdIssuing(5), false});
4568 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(7), false});
4569 xfer(2, st, usd_b, {a[1], a[1], a[1], usdLocking(9), true});
4570 runSimulation(st);
4571
4572 // run mixed transfers
4573 // -------------------
4574 xfer(0, st, xrp_b, {a[0], a[0], a[0], XRP(1), true});
4575 xfer(0, st, usd_b, {a[1], a[3], a[3], usdIssuing(3), false});
4576 xfer(0, st, usd_b, {a[3], a[2], a[1], usdIssuing(5), false});
4577
4578 xfer(1, st, xrp_b, {a[0], a[0], a[0], XRP(4), false});
4579 xfer(1, st, xrp_b, {a[1], a[1], a[0], XRP(8), true});
4580 xfer(1, st, usd_b, {a[4], a[1], a[1], usdLocking(7), true});
4581
4582 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(7), true});
4583 xfer(3, st, xrp_b, {a[0], a[4], a[3], XRP(2), false});
4584 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(9), true});
4585 xfer(3, st, usd_b, {a[3], a[1], a[1], usdIssuing(11), false});
4586 runSimulation(st);
4587
4588 // run multiple account create to stress attestation batching
4589 // ----------------------------------------------------------
4590 ac(0, st, xrp_b, {a[0], ua[1], XRP(301), xrp_b.reward, true});
4591 ac(0, st, xrp_b, {a[1], ua[2], XRP(302), xrp_b.reward, true});
4592 ac(1, st, xrp_b, {a[0], ua[3], XRP(303), xrp_b.reward, true});
4593 ac(2, st, xrp_b, {a[1], ua[4], XRP(304), xrp_b.reward, true});
4594 ac(3, st, xrp_b, {a[0], ua[5], XRP(305), xrp_b.reward, true});
4595 ac(4, st, xrp_b, {a[1], ua[6], XRP(306), xrp_b.reward, true});
4596 ac(6, st, xrp_b, {a[0], ua[7], XRP(307), xrp_b.reward, true});
4597 ac(7, st, xrp_b, {a[2], ua[8], XRP(308), xrp_b.reward, true});
4598 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
4599 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
4600 ac(10, st, xrp_b, {a[0], ua[10], XRP(310), xrp_b.reward, true});
4601 ac(12, st, xrp_b, {a[0], ua[11], XRP(311), xrp_b.reward, true});
4602 ac(12, st, xrp_b, {a[3], ua[12], XRP(312), xrp_b.reward, true});
4603 ac(12, st, xrp_b, {a[4], ua[13], XRP(313), xrp_b.reward, true});
4604 ac(12, st, xrp_b, {a[3], ua[14], XRP(314), xrp_b.reward, true});
4605 ac(12, st, xrp_b, {a[6], ua[15], XRP(315), xrp_b.reward, true});
4606 ac(13, st, xrp_b, {a[7], ua[16], XRP(316), xrp_b.reward, true});
4607 ac(15, st, xrp_b, {a[3], ua[17], XRP(317), xrp_b.reward, true});
4608 runSimulation(st, true); // balances verification working now.
4609 }
4610
4611 void
4612 run() override
4613 {
4615 }
4616};
4617
4618BEAST_DEFINE_TESTSUITE(XChain, app, xrpl);
4619BEAST_DEFINE_TESTSUITE(XChainSim, app, xrpl);
4620
4621} // namespace xrpl::test
T all_of(T... args)
T apply(T... args)
T back(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
A testsuite class.
Definition suite.h:51
void pass()
Record a successful test condition.
Definition suite.h:497
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
virtual Config & config()=0
std::unordered_set< uint256, beast::uhash<> > features
Definition Config.h:261
A currency issued by an account.
Definition Issue.h:13
Issue const & issue() const
Definition STAmount.h:470
static constexpr TERSubset fromInt(int from)
Definition TER.h:413
std::shared_ptr< ChainStateTracker > st_
SmBase(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge)
SmCreateAccount(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, AccountCreate create)
void attest(uint64_t time, uint32_t rnd)
SmState advance(uint64_t time, uint32_t rnd)
void distribute_reward(ChainStateTrack &st)
SmState advance(uint64_t time, uint32_t rnd)
bool attest(uint64_t time, uint32_t rnd)
SmTransfer(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer xfer)
Immutable cryptographic account descriptor.
Definition Account.h:19
static Account const master
The master account.
Definition Account.h:28
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
TER ter() const
Return the TER for the last JTx.
Definition Env.h:658
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:258
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:270
void enableFeature(uint256 const feature)
Definition Env.cpp:654
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:168
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:141
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:329
Converts to IOU Issue or STAmount.
Set the fee on a JTx.
Definition fee.h:17
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Set the flags on a JTx.
Definition txflags.h:11
T clear(T... args)
T count(T... args)
T emplace_back(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T front(T... args)
T is_same_v
T make_pair(T... args)
@ nullValue
'null' value
Definition json_value.h:19
Severity
Severity level / threshold of a Journal message.
Definition Journal.h:12
STL namespace.
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:424
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:448
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:434
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount, NetClock::duration const &settleDelay, PublicKey const &pk, std::optional< NetClock::time_point > const &cancelAfter, std::optional< std::uint32_t > const &dstTag)
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
std::vector< Json::Value > JValueVec
Json::Value bridge_create(Account const &acc, Json::Value const &bridge, STAmount const &reward, std::optional< STAmount > const &minAccountCreate)
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
JValueVec claim_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:95
Json::Value bridge_modify(Account const &acc, Json::Value const &bridge, std::optional< STAmount > const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
FeatureBitset testable_amendments()
Definition Env.h:78
Json::Value xchain_commit(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, std::optional< Account > const &dst)
Json::Value bridge(Account const &lockingChainDoor, Issue const &lockingChainIssue, Account const &issuingChainDoor, Issue const &issuingChainIssue)
Json::Value create_account_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, jtx::signer const &signer)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM
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
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value claim_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, jtx::signer const &signer)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_LINE
Definition TER.h:199
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
std::string transToken(TER code)
Definition TER.cpp:243
@ current
This was a new validation and was added.
@ temBAD_ISSUER
Definition TER.h:73
@ temINVALID_FLAG
Definition TER.h:91
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
Definition TER.h:115
@ temMALFORMED
Definition TER.h:67
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition TER.h:114
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition TER.h:113
@ temDISABLED
Definition TER.h:94
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
Definition TER.h:116
@ temBAD_AMOUNT
Definition TER.h:69
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition TER.h:111
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition TER.h:327
@ tecUNFUNDED_PAYMENT
Definition TER.h:266
@ tecNO_ENTRY
Definition TER.h:287
@ tecXCHAIN_NO_SIGNERS_LIST
Definition TER.h:325
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition TER.h:328
@ tecXCHAIN_NO_CLAIM_ID
Definition TER.h:318
@ tecXCHAIN_BAD_CLAIM_ID
Definition TER.h:319
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
Definition TER.h:332
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
Definition TER.h:333
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
@ tecXCHAIN_SELF_COMMIT
Definition TER.h:331
@ tecXCHAIN_CLAIM_NO_QUORUM
Definition TER.h:320
@ tecXCHAIN_REWARD_MISMATCH
Definition TER.h:324
@ tecNO_PERMISSION
Definition TER.h:286
@ tecDST_TAG_NEEDED
Definition TER.h:290
@ tecNO_ISSUER
Definition TER.h:280
@ tecDUPLICATE
Definition TER.h:296
@ tecHAS_OBLIGATIONS
Definition TER.h:298
@ tecNO_DST
Definition TER.h:271
@ tesSUCCESS
Definition TER.h:225
T reserve(T... args)
T size(T... args)
bool has_happened(STAmount const &amt, STAmount const &reward, bool check_payer=true)
bool check_most_balances(STAmount const &amt, STAmount const &reward)
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payer, jtx::Account const *payees, size_t num_payees, bool withClaim)
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payer, std::vector< jtx::Account > const &payees, bool withClaim)
bool payees_received(STAmount const &reward) const
std::vector< balance > reward_accounts
STAmount diff() const
Balance(T &env, jtx::Account const &account)
jtx::Account const & account_
std::vector< jtx::signer > const & signers
void initBridge(ENV &mcEnv, ENV &scEnv)
SEnv & enableFeature(uint256 const feature)
std::shared_ptr< SLE const > account(jtx::Account const &account)
std::shared_ptr< SLE const > caClaimID(Json::Value const &jvb, std::uint64_t seq)
STAmount balance(jtx::Account const &account, Issue const &issue) const
SEnv(T &s, std::unique_ptr< Config > config, FeatureBitset features, std::unique_ptr< Logs > logs=nullptr, beast::severities::Severity thresh=beast::severities::kError)
SEnv & tx(JsonValue &&jv, FN const &... fN)
XRPAmount reserve(std::uint32_t count)
XRPAmount txFee()
SEnv & fund(STAmount const &amount, Arg const &arg, Args const &... args)
SEnv & disableFeature(uint256 const feature)
TER ter() const
STAmount balance(jtx::Account const &account) const
std::shared_ptr< SLE const > bridge(Json::Value const &jvb)
SEnv & multiTx(jtx::JValueVec const &jvv, FN const &... fN)
std::uint64_t claimCount(Json::Value const &jvb)
std::shared_ptr< SLE const > claimID(Json::Value const &jvb, std::uint64_t seq)
std::uint64_t claimID(Json::Value const &jvb)
std::array< bool, num_signers > attested
bool verify(ENV &env, jtx::Account const &acct) const
void init(ENV &env, jtx::Account const &acct)
std::map< BridgeID, BridgeCounters > counters
void spend(jtx::Account const &acct, STAmount amt, std::uint64_t times=1)
void spendFee(jtx::Account const &acct, size_t times=1)
void sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec &claims)
uint32_t sendCreateAttestations(size_t signer_idx, BridgeID bridge, CreateClaimVec &claims)
std::map< jtx::Account, AccountStateTrack > accounts
void transfer(jtx::Account const &from, jtx::Account const &to, STAmount amt)
void init(jtx::Account const &acct)
void receive(jtx::Account const &acct, STAmount amt, std::uint64_t divisor=1)
std::array< bool, num_signers > attested
void xfer(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer transfer)
void ac(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, AccountCreate ac)
void run() override
Runs the suite.
static constexpr size_t num_signers
void runSimulation(std::shared_ptr< ChainStateTracker > const &st, bool verify_balances=true)
void run() override
Runs the suite.
XRPAmount reserve(std::uint32_t count)
void testXChainAddClaimNonBatchAttestation()
void testXChainAddAccountCreateNonBatchAttestation()
XEnv(T &s, bool side=false)
STAmount const & value() const
std::vector< signer > const signers
Json::Value create_bridge(Account const &acc, Json::Value const &bridge=Json::nullValue, STAmount const &_reward=XRP(1), std::optional< STAmount > const &minAccountCreate=std::nullopt)
std::vector< signer > const alt_signers
std::vector< Account > const payees
JValueVec att_create_acct_vec(std::uint64_t createCount, jtx::AnyAmount const &amt, jtx::Account const &dst, std::size_t const numAtts, std::size_t const fromIdx=0)
Set the sequence number on a JTx.
Definition seq.h:14
A signer in a SignerList.
Definition multisign.h:19
T tmpnam(T... args)
T to_string(T... args)
T visit(T... args)