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 ripple::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&& 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 b(jvb);
134
135 auto tryGet =
137 if (auto r = env_.le(keylet::bridge(b, ct)))
138 {
139 if ((*r)[sfXChainBridge] == b)
140 return r;
141 }
142 return nullptr;
143 };
144 if (auto r = tryGet(STXChainBridge::ChainType::locking))
145 return r;
147 }
148
151 {
152 return (*bridge(jvb))[sfXChainAccountClaimCount];
153 }
154
157 {
158 return (*bridge(jvb))[sfXChainClaimID];
159 }
160
163 {
165 }
166
173};
174
175// XEnv class used for XChain tests. The only difference with SEnv<T> is that it
176// funds some default accounts, and that it enables `testable_amendments() |
177// FeatureBitset{featureXChainBridge}` by default.
178// -----------------------------------------------------------------------------
179template <class T>
180struct XEnv : public jtx::XChainBridgeObjects, public SEnv<T>
181{
182 XEnv(T& s, bool side = false) : SEnv<T>(s, jtx::envconfig(), features)
183 {
184 using namespace jtx;
185 STAmount xrp_funds{XRP(10000)};
186
187 if (!side)
188 {
189 this->fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
190
191 // Signer's list must match the attestation signers
192 // env_(jtx::signers(mcDoor, quorum, signers));
193 for (auto& s : signers)
194 this->fund(xrp_funds, s.account);
195 }
196 else
197 {
198 this->fund(
199 xrp_funds,
200 scDoor,
201 scAlice,
202 scBob,
203 scCarol,
204 scGw,
206 scReward);
207
208 for (auto& ra : payees)
209 this->fund(xrp_funds, ra);
210
211 for (auto& s : signers)
212 this->fund(xrp_funds, s.account);
213
214 // Signer's list must match the attestation signers
215 // env_(jtx::signers(Account::master, quorum, signers));
216 }
217 this->close();
218 }
219};
220
221// Tracks the xrp balance for one account
222template <class T>
224{
228
229 Balance(T& env, jtx::Account const& account) : account_(account), env_(env)
230 {
231 startAmount = env_.balance(account_);
232 }
233
235 diff() const
236 {
237 return env_.balance(account_) - startAmount;
238 }
239};
240
241// Tracks the xrp balance for multiple accounts involved in a crosss-chain
242// transfer
243template <class T>
245{
247
250 balance payor_; // pays the rewards
251 std::vector<balance> reward_accounts; // receives the reward
253
255 T& env,
256 jtx::Account const& from_acct,
257 jtx::Account const& to_acct,
258 jtx::Account const& payor,
259 jtx::Account const* payees,
260 size_t num_payees,
261 bool withClaim)
262 : from_(env, from_acct)
263 , to_(env, to_acct)
264 , payor_(env, payor)
265 , reward_accounts([&]() {
267 r.reserve(num_payees);
268 for (size_t i = 0; i < num_payees; ++i)
269 r.emplace_back(env, payees[i]);
270 return r;
271 }())
272 , txFees_(withClaim ? env.env_.current()->fees().base : XRPAmount(0))
273 {
274 }
275
277 T& env,
278 jtx::Account const& from_acct,
279 jtx::Account const& to_acct,
280 jtx::Account const& payor,
281 std::vector<jtx::Account> const& payees,
282 bool withClaim)
284 env,
285 from_acct,
286 to_acct,
287 payor,
288 &payees[0],
289 payees.size(),
290 withClaim)
291 {
292 }
293
294 bool
295 payees_received(STAmount const& reward) const
296 {
297 return std::all_of(
298 reward_accounts.begin(),
299 reward_accounts.end(),
300 [&](balance const& b) { return b.diff() == reward; });
301 }
302
303 bool
304 check_most_balances(STAmount const& amt, STAmount const& reward)
305 {
306 return from_.diff() == -amt && to_.diff() == amt &&
307 payees_received(reward);
308 }
309
310 bool
312 STAmount const& amt,
313 STAmount const& reward,
314 bool check_payer = true)
315 {
316 auto reward_cost =
317 multiply(reward, STAmount(reward_accounts.size()), reward.issue());
318 return check_most_balances(amt, reward) &&
319 (!check_payer || payor_.diff() == -(reward_cost + txFees_));
320 }
321
322 bool
324 {
325 return check_most_balances(STAmount(0), STAmount(0)) &&
326 payor_.diff() <= txFees_; // could have paid fee for failed claim
327 }
328};
329
331{
338 uint32_t quorum;
341
342 template <class ENV>
343 void
344 initBridge(ENV& mcEnv, ENV& scEnv)
345 {
347
348 auto const optAccountCreate = [&]() -> std::optional<STAmount> {
349 if (issueA != xrpIssue() || issueB != xrpIssue())
350 return {};
351 return minAccountCreate;
352 }();
353 mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate))
355 .close();
356
357 scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate))
359 .close();
360 }
361};
362
365{
368 {
369 return XEnv(*this).env_.current()->fees().accountReserve(count);
370 }
371
374 {
375 return XEnv(*this).env_.current()->fees().base;
376 }
377
378 void
380 {
381 auto jBridge = create_bridge(mcDoor)[sfXChainBridge.jsonName];
382 bool exceptionPresent = false;
383 try
384 {
385 exceptionPresent = false;
386 [[maybe_unused]] STXChainBridge testBridge1(jBridge);
387 }
388 catch (std::exception& ec)
389 {
390 exceptionPresent = true;
391 }
392
393 BEAST_EXPECT(!exceptionPresent);
394
395 try
396 {
397 exceptionPresent = false;
398 jBridge["Extra"] = 1;
399 [[maybe_unused]] STXChainBridge testBridge2(jBridge);
400 }
401 catch ([[maybe_unused]] std::exception& ec)
402 {
403 exceptionPresent = true;
404 }
405
406 BEAST_EXPECT(exceptionPresent);
407 }
408
409 void
411 {
412 XRPAmount res1 = reserve(1);
413
414 using namespace jtx;
415 testcase("Create Bridge");
416
417 // Normal create_bridge => should succeed
418 XEnv(*this).tx(create_bridge(mcDoor)).close();
419
420 // Bridge not owned by one of the door account.
421 XEnv(*this).tx(
423
424 // Create twice on the same account
425 XEnv(*this)
427 .close()
429
430 // Create USD bridge Alice -> Bob ... should succeed
431 XEnv(*this).tx(
433 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
434 ter(tesSUCCESS));
435
436 // Create USD bridge, Alice is both the locking door and locking issue,
437 // ... should fail.
438 XEnv(*this).tx(
440 mcAlice, bridge(mcAlice, mcAlice["USD"], mcBob, mcBob["USD"])),
442
443 // Bridge where the two door accounts are equal.
444 XEnv(*this).tx(
446 mcBob, bridge(mcBob, mcGw["USD"], mcBob, mcGw["USD"])),
448
449 // Both door accounts are on the same chain. This is not allowed.
450 // Although it doesn't violate any invariants, it's not a useful thing
451 // to do and it complicates the "add claim" transactions.
452 XEnv(*this)
454 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])))
455 .close()
457 mcBob, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
459 .close();
460
461 // Create a bridge on an account with exactly enough balance to
462 // meet the new reserve should succeed
463 XEnv(*this)
464 .fund(res1, mcuDoor) // exact reserve for account + 1 object
465 .close()
467
468 // Create a bridge on an account with no enough balance to meet the
469 // new reserve
470 XEnv(*this)
471 .fund(res1 - 1, mcuDoor) // just short of required reserve
472 .close()
474
475 // Reward amount is non-xrp
476 XEnv(*this).tx(
479
480 // Reward amount is XRP and negative
481 XEnv(*this).tx(
484
485 // Reward amount is 1 xrp => should succeed
487
488 // Min create amount is 1 xrp, mincreate is 1 xrp => should succeed
489 XEnv(*this).tx(
491
492 // Min create amount is non-xrp
493 XEnv(*this).tx(
494 create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)),
496
497 // Min create amount is zero (should fail, currently succeeds)
498 XEnv(*this).tx(
499 create_bridge(mcDoor, jvb, XRP(1), XRP(0)),
501
502 // Min create amount is negative
503 XEnv(*this).tx(
504 create_bridge(mcDoor, jvb, XRP(1), XRP(-1)),
506
507 // coverage test: BridgeCreate::preflight() - create bridge when feature
508 // disabled.
509 {
510 Env env(*this, testable_amendments() - featureXChainBridge);
512 }
513
514 // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER.
515 XEnv(*this).tx(
517 mcAlice, bridge(mcAlice, mcuAlice["USD"], mcBob, mcBob["USD"])),
519
520 // coverage test: create_bridge transaction with incorrect flag
521 XEnv(*this).tx(
525
526 // coverage test: create_bridge transaction with xchain feature disabled
527 XEnv(*this)
528 .disableFeature(featureXChainBridge)
530 }
531
532 void
534 {
580 using namespace jtx;
581 testcase("Bridge create constraints");
582 XEnv env(*this, true);
583 auto& A = scAlice;
584 auto& B = scBob;
585 auto& C = scCarol;
586 auto AUSD = A["USD"];
587 auto BUSD = B["USD"];
588 auto CUSD = C["USD"];
589 auto GUSD = scGw["USD"];
590 auto AEUR = A["EUR"];
591 auto BEUR = B["EUR"];
592 auto CEUR = C["EUR"];
593 auto GEUR = scGw["EUR"];
594
595 // Accounts to own single brdiges
596 Account const a1("a1");
597 Account const a2("a2");
598 Account const a3("a3");
599 Account const a4("a4");
600 Account const a5("a5");
601 Account const a6("a6");
602
603 env.fund(XRP(10000), a1, a2, a3, a4, a5, a6);
604 env.close();
605
606 // Add a bridge on two different accounts with the same locking and
607 // issuing assets
608 env.tx(create_bridge(a1, bridge(a1, GUSD, B, BUSD))).close();
609 env.tx(create_bridge(a2, bridge(a2, GUSD, B, BUSD))).close();
610
611 // Add the exact same bridge to two different accounts (one locking
612 // account and one issuing)
613 env.tx(create_bridge(a3, bridge(a3, GUSD, a4, a4["USD"]))).close();
614 env.tx(create_bridge(a4, bridge(a3, GUSD, a4, a4["USD"])),
616 .close();
617
618 // Add the exact same bridge to two different accounts (one issuing
619 // account and one locking - opposite order from the test above)
620 env.tx(create_bridge(a5, bridge(a6, GUSD, a5, a5["USD"]))).close();
621 env.tx(create_bridge(a6, bridge(a6, GUSD, a5, a5["USD"])),
623 .close();
624
625 // Test case 1 ~ 5, create bridges
626 auto const goodBridge1 = bridge(A, GUSD, B, BUSD);
627 auto const goodBridge2 = bridge(A, BUSD, C, CUSD);
628 env.tx(create_bridge(B, goodBridge1)).close();
629 // Issuing asset is the same, this is a duplicate
630 env.tx(create_bridge(B, bridge(A, GEUR, B, BUSD)), ter(tecDUPLICATE))
631 .close();
632 env.tx(create_bridge(A, goodBridge2), ter(tesSUCCESS)).close();
633 // Locking asset is the same - this is a duplicate
634 env.tx(create_bridge(A, bridge(A, BUSD, B, BEUR)), ter(tecDUPLICATE))
635 .close();
636 // Locking asset is USD - this is a duplicate even tho it has a
637 // different issuer
638 env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE))
639 .close();
640
641 // Test case 6 and 7, commits
642 env.tx(trust(C, BUSD(1000)))
643 .tx(trust(A, BUSD(1000)))
644 .close()
645 .tx(pay(B, C, BUSD(1000)))
646 .close();
647 auto const aBalanceStart = env.balance(A, BUSD);
648 auto const cBalanceStart = env.balance(C, BUSD);
649 env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close();
650 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(0));
651 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50));
652 env.tx(xchain_commit(C, goodBridge2, 1, BUSD(60))).close();
653 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(60));
654 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50 - 60));
655
656 // bridge modify test cases
657 env.tx(bridge_modify(B, goodBridge1, XRP(33), std::nullopt)).close();
658 BEAST_EXPECT((*env.bridge(goodBridge1))[sfSignatureReward] == XRP(33));
659 env.tx(bridge_modify(A, goodBridge2, XRP(44), std::nullopt)).close();
660 BEAST_EXPECT((*env.bridge(goodBridge2))[sfSignatureReward] == XRP(44));
661 }
662
663 void
665 {
666 using namespace jtx;
667 testcase("Create Bridge Matrix");
668
669 // Test all combinations of the following:`
670 // --------------------------------------
671 // - Locking chain is IOU with locking chain door account as issuer
672 // - Locking chain is IOU with issuing chain door account that
673 // exists on the locking chain as issuer
674 // - Locking chain is IOU with issuing chain door account that does
675 // not exists on the locking chain as issuer
676 // - Locking chain is IOU with non-door account (that exists on the
677 // locking chain ledger) as issuer
678 // - Locking chain is IOU with non-door account (that does not exist
679 // exists on the locking chain ledger) as issuer
680 // - Locking chain is XRP
681 // ---------------------------------------------------------------------
682 // - Issuing chain is IOU with issuing chain door account as the
683 // issuer
684 // - Issuing chain is IOU with locking chain door account (that
685 // exists on the issuing chain ledger) as the issuer
686 // - Issuing chain is IOU with locking chain door account (that does
687 // not exist on the issuing chain ledger) as the issuer
688 // - Issuing chain is IOU with non-door account (that exists on the
689 // issuing chain ledger) as the issuer
690 // - Issuing chain is IOU with non-door account (that does not
691 // exists on the issuing chain ledger) as the issuer
692 // - Issuing chain is XRP and issuing chain door account is not the
693 // root account
694 // - Issuing chain is XRP and issuing chain door account is the root
695 // account
696 // ---------------------------------------------------------------------
697 // That's 42 combinations. The only combinations that should succeed
698 // are:
699 // - Locking chain is any IOU,
700 // - Issuing chain is IOU with issuing chain door account as the
701 // issuer
702 // Locking chain is XRP,
703 // - Issuing chain is XRP with issuing chain is the root account.
704 // ---------------------------------------------------------------------
705 Account a("a"), b("b");
706 Issue ia, ib;
707
708 std::tuple lcs{
710 "Locking chain is IOU(locking chain door)",
711 [&](auto& env, bool) {
712 a = mcDoor;
713 ia = mcDoor["USD"];
714 }),
716 "Locking chain is IOU(issuing chain door funded on locking "
717 "chain)",
718 [&](auto& env, bool shouldFund) {
719 a = mcDoor;
720 ia = scDoor["USD"];
721 if (shouldFund)
722 env.fund(XRP(10000), scDoor);
723 }),
725 "Locking chain is IOU(issuing chain door account unfunded "
726 "on locking chain)",
727 [&](auto& env, bool) {
728 a = mcDoor;
729 ia = scDoor["USD"];
730 }),
732 "Locking chain is IOU(bob funded on locking chain)",
733 [&](auto& env, bool) {
734 a = mcDoor;
735 ia = mcGw["USD"];
736 }),
738 "Locking chain is IOU(bob unfunded on locking chain)",
739 [&](auto& env, bool) {
740 a = mcDoor;
741 ia = mcuGw["USD"];
742 }),
743 std::make_pair("Locking chain is XRP", [&](auto& env, bool) {
744 a = mcDoor;
745 ia = xrpIssue();
746 })};
747
748 std::tuple ics{
750 "Issuing chain is IOU(issuing chain door account)",
751 [&](auto& env, bool) {
752 b = scDoor;
753 ib = scDoor["USD"];
754 }),
756 "Issuing chain is IOU(locking chain door funded on issuing "
757 "chain)",
758 [&](auto& env, bool shouldFund) {
759 b = scDoor;
760 ib = mcDoor["USD"];
761 if (shouldFund)
762 env.fund(XRP(10000), mcDoor);
763 }),
765 "Issuing chain is IOU(locking chain door unfunded on "
766 "issuing chain)",
767 [&](auto& env, bool) {
768 b = scDoor;
769 ib = mcDoor["USD"];
770 }),
772 "Issuing chain is IOU(bob funded on issuing chain)",
773 [&](auto& env, bool) {
774 b = scDoor;
775 ib = mcGw["USD"];
776 }),
778 "Issuing chain is IOU(bob unfunded on issuing chain)",
779 [&](auto& env, bool) {
780 b = scDoor;
781 ib = mcuGw["USD"];
782 }),
784 "Issuing chain is XRP and issuing chain door account is "
785 "not the root account",
786 [&](auto& env, bool) {
787 b = scDoor;
788 ib = xrpIssue();
789 }),
791 "Issuing chain is XRP and issuing chain door account is "
792 "the root account ",
793 [&](auto& env, bool) {
794 b = Account::master;
795 ib = xrpIssue();
796 })};
797
798 std::vector<std::pair<int, int>> expected_result{
841
843
844 auto testcase = [&](auto const& lc, auto const& ic) {
845 XEnv mcEnv(*this);
846 XEnv scEnv(*this, true);
847
848 lc.second(mcEnv, true);
849 lc.second(scEnv, false);
850
851 ic.second(mcEnv, false);
852 ic.second(scEnv, true);
853
854 auto const& expected = expected_result[test_result.size()];
855
856 mcEnv.tx(
857 create_bridge(a, bridge(a, ia, b, ib)),
858 ter(TER::fromInt(expected.first)));
859 TER mcTER = mcEnv.env_.ter();
860
861 scEnv.tx(
862 create_bridge(b, bridge(a, ia, b, ib)),
863 ter(TER::fromInt(expected.second)));
864 TER scTER = scEnv.env_.ter();
865
866 bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS;
867
868 test_result.emplace_back(mcTER, scTER, pass);
869 };
870
871 auto apply_ics = [&](auto const& lc, auto const& ics) {
873 [&](auto const&... ic) { (testcase(lc, ic), ...); }, ics);
874 };
875
876 std::apply([&](auto const&... lc) { (apply_ics(lc, ics), ...); }, lcs);
877
878#if GENERATE_MTX_OUTPUT
879 // optional output of matrix results in markdown format
880 // ----------------------------------------------------
881 std::string fname{std::tmpnam(nullptr)};
882 fname += ".md";
883 std::cout << "Markdown output for matrix test: " << fname << "\n";
884
885 auto print_res = [](auto tup) -> std::string {
887 " / " + transToken(std::get<1>(tup));
888
889 if (std::get<2>(tup))
890 return status;
891 else
892 {
893 // red
894 return std::string("`") + status + "`";
895 }
896 };
897
898 auto output_table = [&](auto print_res) {
899 size_t test_idx = 0;
900 std::string res;
901 res.reserve(10000); // should be enough :-)
902
903 // first two header lines
904 res += "| `issuing ->` | ";
906 [&](auto const&... ic) {
907 ((res += ic.first, res += " | "), ...);
908 },
909 ics);
910 res += "\n";
911
912 res += "| :--- | ";
914 [&](auto const&... ic) {
915 (((void)ic.first, res += ":---: | "), ...);
916 },
917 ics);
918 res += "\n";
919
920 auto output = [&](auto const& lc, auto const& ic) {
921 res += print_res(test_result[test_idx]);
922 res += " | ";
923 ++test_idx;
924 };
925
926 auto output_ics = [&](auto const& lc, auto const& ics) {
927 res += "| ";
928 res += lc.first;
929 res += " | ";
931 [&](auto const&... ic) { (output(lc, ic), ...); }, ics);
932 res += "\n";
933 };
934
936 [&](auto const&... lc) { (output_ics(lc, ics), ...); }, lcs);
937
938 return res;
939 };
940
941 std::ofstream(fname) << output_table(print_res);
942
943 std::string ter_fname{std::tmpnam(nullptr)};
944 std::cout << "ter output for matrix test: " << ter_fname << "\n";
945
946 std::ofstream ofs(ter_fname);
947 for (auto& t : test_result)
948 {
949 ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", "
950 << std::string(transToken(std::get<1>(t))) << "}\n,";
951 }
952#endif
953 }
954
955 void
957 {
958 using namespace jtx;
959 testcase("Modify Bridge");
960
961 // Changing a non-existent bridge should fail
962 XEnv(*this).tx(
964 mcAlice,
965 bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]),
966 XRP(2),
969
970 // must change something
971 // XEnv(*this)
972 // .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
973 // .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(1)),
974 // ter(temMALFORMED));
975
976 // must change something
977 XEnv(*this)
978 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
979 .close()
981
982 // Reward amount is non-xrp
983 XEnv(*this).tx(
984 bridge_modify(mcDoor, jvb, mcUSD(2), XRP(10)),
986
987 // Reward amount is XRP and negative
988 XEnv(*this).tx(
989 bridge_modify(mcDoor, jvb, XRP(-2), XRP(10)),
991
992 // Min create amount is non-xrp
993 XEnv(*this).tx(
994 bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)),
996
997 // Min create amount is zero
998 XEnv(*this).tx(
999 bridge_modify(mcDoor, jvb, XRP(2), XRP(0)),
1001
1002 // Min create amount is negative
1003 XEnv(*this).tx(
1004 bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)),
1006
1007 // First check the regular claim process (without bridge_modify)
1008 for (auto withClaim : {false, true})
1009 {
1010 XEnv mcEnv(*this);
1011 XEnv scEnv(*this, true);
1012
1013 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1014
1017 .close()
1019 .close();
1020
1021 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1022 auto const amt = XRP(1000);
1023 std::uint32_t const claimID = 1;
1024 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1025
1026 BalanceTransfer transfer(
1027 scEnv,
1029 scBob,
1030 scAlice,
1031 &payees[0],
1033 withClaim);
1034
1035 scEnv
1037 scAttester,
1038 jvb,
1039 mcAlice,
1040 amt,
1041 payees,
1042 true,
1043 claimID,
1044 dst,
1045 signers))
1046 .close();
1047
1048 if (withClaim)
1049 {
1050 BEAST_EXPECT(transfer.has_not_happened());
1051
1052 // need to submit a claim transactions
1053 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1054 .close();
1055 }
1056
1057 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1058 }
1059
1060 // Check that the reward paid from a claim Id was the reward when
1061 // the claim id was created, not the reward since the bridge was
1062 // modified.
1063 for (auto withClaim : {false, true})
1064 {
1065 XEnv mcEnv(*this);
1066 XEnv scEnv(*this, true);
1067
1068 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1069
1072 .close()
1074 .close();
1075
1076 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1077 auto const amt = XRP(1000);
1078 std::uint32_t const claimID = 1;
1079 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1080
1081 // Now modify the reward on the bridge
1082 mcEnv.tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(10))).close();
1083 scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10)))
1084 .close();
1085
1086 BalanceTransfer transfer(
1087 scEnv,
1089 scBob,
1090 scAlice,
1091 &payees[0],
1093 withClaim);
1094
1095 scEnv
1097 scAttester,
1098 jvb,
1099 mcAlice,
1100 amt,
1101 payees,
1102 true,
1103 claimID,
1104 dst,
1105 signers))
1106 .close();
1107
1108 if (withClaim)
1109 {
1110 BEAST_EXPECT(transfer.has_not_happened());
1111
1112 // need to submit a claim transactions
1113 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1114 .close();
1115 }
1116
1117 // make sure the reward accounts indeed received the original
1118 // split reward (1 split 5 ways) instead of the updated 2 XRP.
1119 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1120 }
1121
1122 // Check that the signatures used to verify attestations and decide
1123 // if there is a quorum are the current signer's list on the door
1124 // account, not the signer's list that was in effect when the claim
1125 // id was created.
1126 for (auto withClaim : {false, true})
1127 {
1128 XEnv mcEnv(*this);
1129 XEnv scEnv(*this, true);
1130
1131 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1132
1135 .close()
1137 .close();
1138
1139 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1140 auto const amt = XRP(1000);
1141 std::uint32_t const claimID = 1;
1142 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1143
1144 // change signers - claim should not be processed is the batch
1145 // is signed by original signers
1147 .close();
1148
1149 BalanceTransfer transfer(
1150 scEnv,
1152 scBob,
1153 scAlice,
1154 &payees[0],
1156 withClaim);
1157
1158 // submit claim using outdated signers - should fail
1159 scEnv
1160 .multiTx(
1162 scAttester,
1163 jvb,
1164 mcAlice,
1165 amt,
1166 payees,
1167 true,
1168 claimID,
1169 dst,
1170 signers),
1172 .close();
1173 if (withClaim)
1174 {
1175 // need to submit a claim transactions
1176 scEnv
1177 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1179 .close();
1180 }
1181
1182 // make sure transfer has not happened as we sent attestations
1183 // using outdated signers
1184 BEAST_EXPECT(transfer.has_not_happened());
1185
1186 // submit claim using current signers - should succeed
1187 scEnv
1189 scAttester,
1190 jvb,
1191 mcAlice,
1192 amt,
1193 payees,
1194 true,
1195 claimID,
1196 dst,
1197 alt_signers))
1198 .close();
1199 if (withClaim)
1200 {
1201 BEAST_EXPECT(transfer.has_not_happened());
1202
1203 // need to submit a claim transactions
1204 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1205 .close();
1206 }
1207
1208 // make sure the transfer went through as we sent attestations
1209 // using new signers
1210 BEAST_EXPECT(
1211 transfer.has_happened(amt, split_reward_quorum, false));
1212 }
1213
1214 // coverage test: bridge_modify transaction with incorrect flag
1215 XEnv(*this)
1217 .close()
1218 .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)),
1221
1222 // coverage test: bridge_modify transaction with xchain feature
1223 // disabled
1224 XEnv(*this)
1226 .disableFeature(featureXChainBridge)
1227 .close()
1229
1230 // coverage test: bridge_modify return temSIDECHAIN_NONDOOR_OWNER;
1231 XEnv(*this)
1233 .close()
1234 .tx(bridge_modify(mcAlice, jvb, XRP(1), XRP(2)),
1236
1243 XEnv(*this)
1244 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20)))
1245 .close()
1247 mcAlice, jvb, scuAlice, XRP(100), reward))
1248 .close()
1249 .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)),
1252 .close()
1253 .tx(bridge_modify(mcDoor, jvb, XRP(3), {}),
1255 .close()
1257 mcAlice, jvb, scuBob, XRP(100), XRP(3)),
1259 .close();
1260 }
1261
1262 void
1264 {
1265 using namespace jtx;
1266 XRPAmount res1 = reserve(1);
1267 XRPAmount tx_fee = txFee();
1268
1269 testcase("Create ClaimID");
1270
1271 // normal bridge create for sanity check with the exact necessary
1272 // account balance
1273 XEnv(*this, true)
1275 .fund(res1, scuAlice) // acct reserve + 1 object
1276 .close()
1278 .close();
1279
1280 // check reward not deducted when claim id is created
1281 {
1282 XEnv xenv(*this, true);
1283
1284 Balance scAlice_bal(xenv, scAlice);
1285
1288 .close();
1289
1290 BEAST_EXPECT(scAlice_bal.diff() == -tx_fee);
1291 }
1292
1293 // Non-existent bridge
1294 XEnv(*this, true)
1296 scAlice,
1297 bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]),
1298 reward,
1299 mcAlice),
1301 .close();
1302
1303 // Creating the new object would put the account below the reserve
1304 XEnv(*this, true)
1306 .fund(res1 - xrp_dust, scuAlice) // barely not enough
1307 .close()
1310 .close();
1311
1312 // The specified reward doesn't match the reward on the bridge (test
1313 // by giving the reward amount for the other side, as well as a
1314 // completely non-matching reward)
1315 XEnv(*this, true)
1317 .close()
1321 .close();
1322
1323 // A reward amount that isn't XRP
1324 XEnv(*this, true)
1326 .close()
1329 .close();
1330
1331 // coverage test: xchain_create_claim_id transaction with incorrect
1332 // flag
1333 XEnv(*this, true)
1335 .close()
1339 .close();
1340
1341 // coverage test: xchain_create_claim_id transaction with xchain
1342 // feature disabled
1343 XEnv(*this, true)
1345 .disableFeature(featureXChainBridge)
1346 .close()
1349 .close();
1350 }
1351
1352 void
1354 {
1355 using namespace jtx;
1356 XRPAmount res0 = reserve(0);
1357 XRPAmount tx_fee = txFee();
1358
1359 testcase("Commit");
1360
1361 // Commit to a non-existent bridge
1362 XEnv(*this).tx(
1364
1365 // check that reward not deducted when doing the commit
1366 {
1367 XEnv xenv(*this);
1368
1369 Balance alice_bal(xenv, mcAlice);
1370 auto const amt = XRP(1000);
1371
1372 xenv.tx(create_bridge(mcDoor, jvb))
1373 .close()
1374 .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob))
1375 .close();
1376
1377 STAmount claim_cost = amt;
1378 BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee));
1379 }
1380
1381 // Commit a negative amount
1382 XEnv(*this)
1384 .close()
1385 .tx(xchain_commit(mcAlice, jvb, 1, XRP(-1), scBob),
1387
1388 // Commit an amount whose issue that does not match the expected
1389 // issue on the bridge (either LockingChainIssue or
1390 // IssuingChainIssue, depending on the chain).
1391 XEnv(*this)
1393 .close()
1394 .tx(xchain_commit(mcAlice, jvb, 1, mcUSD(100), scBob),
1396
1397 // Commit an amount that would put the sender below the required
1398 // reserve (if XRP)
1399 XEnv(*this)
1401 .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough
1402 .close()
1405
1406 XEnv(*this)
1408 .fund(
1409 res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees
1410 mcuAlice) // exactly enough => should succeed
1411 .close()
1413
1414 // Commit an amount above the account's balance (for both XRP and
1415 // IOUs)
1416 XEnv(*this)
1418 .fund(res0, mcuAlice) // barely not enough
1419 .close()
1420 .tx(xchain_commit(mcuAlice, jvb, 1, res0 + one_xrp, scBob),
1422
1423 auto jvb_USD = bridge(mcDoor, mcUSD, scGw, scUSD);
1424
1425 // commit sent from iou issuer (mcGw) succeeds - should it?
1426 XEnv(*this)
1427 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1428 .tx(create_bridge(mcDoor, jvb_USD))
1429 .close()
1430 .tx(xchain_commit(mcGw, jvb_USD, 1, mcUSD(1), scBob));
1431
1432 // commit to a door account from the door account. This should fail.
1433 XEnv(*this)
1434 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1435 .tx(create_bridge(mcDoor, jvb_USD))
1436 .close()
1437 .tx(xchain_commit(mcDoor, jvb_USD, 1, mcUSD(1), scBob),
1439
1440 // commit sent from mcAlice which has no IOU balance => should fail
1441 XEnv(*this)
1442 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1443 .tx(create_bridge(mcDoor, jvb_USD))
1444 .close()
1445 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scBob),
1446 ter(terNO_LINE));
1447
1448 // commit sent from mcAlice which has no IOU balance => should fail
1449 // just changed the destination to scGw (which is the door account
1450 // and may not make much sense)
1451 XEnv(*this)
1452 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1453 .tx(create_bridge(mcDoor, jvb_USD))
1454 .close()
1455 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scGw),
1456 ter(terNO_LINE));
1457
1458 // commit sent from mcAlice which has a IOU balance => should
1459 // succeed
1460 XEnv(*this)
1461 .tx(trust(mcDoor, mcUSD(10000)))
1462 .tx(trust(mcAlice, mcUSD(10000)))
1463 .close()
1464 .tx(pay(mcGw, mcAlice, mcUSD(10)))
1465 .tx(create_bridge(mcDoor, jvb_USD))
1466 .close()
1467 //.tx(pay(mcAlice, mcDoor, mcUSD(10)));
1468 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(10), scAlice));
1469
1470 // coverage test: xchain_commit transaction with incorrect flag
1471 XEnv(*this)
1473 .close()
1477
1478 // coverage test: xchain_commit transaction with xchain feature
1479 // disabled
1480 XEnv(*this)
1482 .disableFeature(featureXChainBridge)
1483 .close()
1485 ter(temDISABLED));
1486 }
1487
1488 void
1490 {
1491 using namespace jtx;
1492
1493 testcase("Add Attestation");
1494 XRPAmount res0 = reserve(0);
1495 XRPAmount tx_fee = txFee();
1496
1497 auto multiTtxFee = [&](std::uint32_t m) -> STAmount {
1498 return multiply(tx_fee, STAmount(m), xrpIssue());
1499 };
1500
1501 // Add an attestation to a claim id that has already reached quorum.
1502 // This should succeed and share in the reward.
1503 // note: this is true only when either:
1504 // 1. dest account is not specified, so transfer requires a claim
1505 // 2. or the extra attestation is sent in the same batch as the
1506 // one reaching quorum
1507 for (auto withClaim : {true})
1508 {
1509 XEnv mcEnv(*this);
1510 XEnv scEnv(*this, true);
1511 std::uint32_t const claimID = 1;
1512
1513 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1514
1517 .close()
1519 .close();
1520
1521 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1522
1523 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1524 auto const amt = XRP(1000);
1525 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1526
1527 BalanceTransfer transfer(
1528 scEnv, Account::master, scBob, scAlice, payees, withClaim);
1529
1530 scEnv
1532 scAttester,
1533 jvb,
1534 mcAlice,
1535 amt,
1536 payees,
1537 true,
1538 claimID,
1539 dst,
1540 signers,
1542 .close();
1543 scEnv
1545 scAttester,
1546 jvb,
1547 mcAlice,
1548 amt,
1550 true,
1551 claimID,
1552 dst,
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))
1562 .close();
1563 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1564 BEAST_EXPECT(scEnv.claimID(jvb) == claimID);
1565 }
1566
1567 BEAST_EXPECT(transfer.has_happened(amt, split_reward_everyone));
1568 }
1569
1570 // Test that signature weights are correctly handled. Assign
1571 // signature weights of 1,2,4,4 and a quorum of 7. Check that the
1572 // 4,4 signatures reach a quorum, the 1,2,4, reach a quorum, but the
1573 // 4,2, 4,1 and 1,2 do not.
1574
1575 // 1,2,4 => should succeed
1576 for (auto withClaim : {false, true})
1577 {
1578 XEnv mcEnv(*this);
1579 XEnv scEnv(*this, true);
1580
1581 std::uint32_t const quorum_7 = 7;
1582 std::vector<signer> const signers_ = [] {
1583 constexpr int numSigners = 4;
1584 std::uint32_t weights[] = {1, 2, 4, 4};
1585
1586 std::vector<signer> result;
1587 result.reserve(numSigners);
1588 for (int i = 0; i < numSigners; ++i)
1589 {
1590 using namespace std::literals;
1591 auto const a = Account("signer_"s + std::to_string(i));
1592 result.emplace_back(a, weights[i]);
1593 }
1594 return result;
1595 }();
1596
1597 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1598
1600 .tx(jtx::signers(Account::master, quorum_7, signers_))
1601 .close()
1603 .close();
1604 std::uint32_t const claimID = 1;
1605 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1606
1607 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1608 auto const amt = XRP(1000);
1609
1610 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1611
1612 BalanceTransfer transfer(
1613 scEnv,
1615 scBob,
1616 scAlice,
1617 &payees[0],
1618 3,
1619 withClaim);
1620
1621 scEnv
1623 scAttester,
1624 jvb,
1625 mcAlice,
1626 amt,
1627 payees,
1628 true,
1629 claimID,
1630 dst,
1631 signers_,
1632 3))
1633 .close();
1634
1635 if (withClaim)
1636 {
1637 BEAST_EXPECT(transfer.has_not_happened());
1638
1639 // need to submit a claim transactions
1640 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1641 .close();
1642 }
1643
1644 BEAST_EXPECT(!scEnv.claimID(jvb, 1)); // claim id deleted
1645
1646 BEAST_EXPECT(transfer.has_happened(
1647 amt, divide(reward, STAmount(3), reward.issue())));
1648 }
1649
1650 // 4,4 => should succeed
1651 for (auto withClaim : {false, true})
1652 {
1653 XEnv mcEnv(*this);
1654 XEnv scEnv(*this, true);
1655
1656 std::uint32_t const quorum_7 = 7;
1657 std::vector<signer> const signers_ = [] {
1658 constexpr int numSigners = 4;
1659 std::uint32_t weights[] = {1, 2, 4, 4};
1660
1661 std::vector<signer> result;
1662 result.reserve(numSigners);
1663 for (int i = 0; i < numSigners; ++i)
1664 {
1665 using namespace std::literals;
1666 auto const a = Account("signer_"s + std::to_string(i));
1667 result.emplace_back(a, weights[i]);
1668 }
1669 return result;
1670 }();
1671 STAmount const split_reward_ =
1672 divide(reward, STAmount(signers_.size()), reward.issue());
1673
1674 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1675
1677 .tx(jtx::signers(Account::master, quorum_7, signers_))
1678 .close()
1680 .close();
1681 std::uint32_t const claimID = 1;
1682 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1683
1684 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1685 auto const amt = XRP(1000);
1686
1687 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1688
1689 BalanceTransfer transfer(
1690 scEnv,
1692 scBob,
1693 scAlice,
1694 &payees[2],
1695 2,
1696 withClaim);
1697
1698 scEnv
1700 scAttester,
1701 jvb,
1702 mcAlice,
1703 amt,
1704 payees,
1705 true,
1706 claimID,
1707 dst,
1708 signers_,
1709 2,
1710 2))
1711 .close();
1712
1713 if (withClaim)
1714 {
1715 BEAST_EXPECT(transfer.has_not_happened());
1716
1717 // need to submit a claim transactions
1718 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1719 .close();
1720 }
1721
1722 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1723
1724 BEAST_EXPECT(transfer.has_happened(
1725 amt, divide(reward, STAmount(2), reward.issue())));
1726 }
1727
1728 // 1,2 => should fail
1729 for (auto withClaim : {false, true})
1730 {
1731 XEnv mcEnv(*this);
1732 XEnv scEnv(*this, true);
1733
1734 std::uint32_t const quorum_7 = 7;
1735 std::vector<signer> const signers_ = [] {
1736 constexpr int numSigners = 4;
1737 std::uint32_t weights[] = {1, 2, 4, 4};
1738
1739 std::vector<signer> result;
1740 result.reserve(numSigners);
1741 for (int i = 0; i < numSigners; ++i)
1742 {
1743 using namespace std::literals;
1744 auto const a = Account("signer_"s + std::to_string(i));
1745 result.emplace_back(a, weights[i]);
1746 }
1747 return result;
1748 }();
1749
1750 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1751
1753 .tx(jtx::signers(Account::master, quorum_7, signers_))
1754 .close()
1756 .close();
1757
1758 std::uint32_t const claimID = 1;
1759 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1760
1761 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1762 auto const amt = XRP(1000);
1763 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1764
1765 BalanceTransfer transfer(
1766 scEnv,
1768 scBob,
1769 scAlice,
1770 &payees[0],
1771 2,
1772 withClaim);
1773
1774 scEnv
1776 scAttester,
1777 jvb,
1778 mcAlice,
1779 amt,
1780 payees,
1781 true,
1782 claimID,
1783 dst,
1784 signers_,
1785 2))
1786 .close();
1787 if (withClaim)
1788 {
1789 BEAST_EXPECT(transfer.has_not_happened());
1790
1791 // need to submit a claim transactions
1792 scEnv
1793 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1795 .close();
1796 }
1797
1798 BEAST_EXPECT(
1799 !!scEnv.claimID(jvb, claimID)); // claim id still present
1800 BEAST_EXPECT(transfer.has_not_happened());
1801 }
1802
1803 // 2,4 => should fail
1804 for (auto withClaim : {false, true})
1805 {
1806 XEnv mcEnv(*this);
1807 XEnv scEnv(*this, true);
1808
1809 std::uint32_t const quorum_7 = 7;
1810 std::vector<signer> const signers_ = [] {
1811 constexpr int numSigners = 4;
1812 std::uint32_t weights[] = {1, 2, 4, 4};
1813
1814 std::vector<signer> result;
1815 result.reserve(numSigners);
1816 for (int i = 0; i < numSigners; ++i)
1817 {
1818 using namespace std::literals;
1819 auto const a = Account("signer_"s + std::to_string(i));
1820 result.emplace_back(a, weights[i]);
1821 }
1822 return result;
1823 }();
1824
1825 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1826
1828 .tx(jtx::signers(Account::master, quorum_7, signers_))
1829 .close()
1831 .close();
1832
1833 std::uint32_t const claimID = 1;
1834 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1835
1836 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1837 auto const amt = XRP(1000);
1838
1839 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1840
1841 BalanceTransfer transfer(
1842 scEnv,
1844 scBob,
1845 scAlice,
1846 &payees[1],
1847 2,
1848 withClaim);
1849
1850 scEnv
1852 scAttester,
1853 jvb,
1854 mcAlice,
1855 amt,
1856 payees,
1857 true,
1858 claimID,
1859 dst,
1860 signers_,
1861 2,
1862 1))
1863 .close();
1864
1865 if (withClaim)
1866 {
1867 BEAST_EXPECT(transfer.has_not_happened());
1868
1869 // need to submit a claim transactions
1870 scEnv
1871 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1873 .close();
1874 }
1875
1876 BEAST_EXPECT(
1877 !!scEnv.claimID(jvb, claimID)); // claim id still present
1878 BEAST_EXPECT(transfer.has_not_happened());
1879 }
1880
1881 // Confirm that account create transactions happen in the correct
1882 // order. If they reach quorum out of order they should not execute
1883 // until all the previous created transactions have occurred.
1884 // Re-adding an attestation should move funds.
1885 {
1886 XEnv mcEnv(*this);
1887 XEnv scEnv(*this, true);
1888 auto const amt = XRP(1000);
1889 auto const amt_plus_reward = amt + reward;
1890
1891 {
1892 Balance door(mcEnv, mcDoor);
1893 Balance carol(mcEnv, mcCarol);
1894
1895 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1896 .close()
1898 mcAlice, jvb, scuAlice, amt, reward))
1900 mcBob, jvb, scuBob, amt, reward))
1902 mcCarol, jvb, scuCarol, amt, reward))
1903 .close();
1904
1905 BEAST_EXPECT(
1906 door.diff() ==
1907 (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
1908 tx_fee));
1909 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1910 }
1911
1914 .close();
1915
1916 {
1917 // send first batch of account create attest for all 3
1918 // account create
1919 Balance attester(scEnv, scAttester);
1920 Balance door(scEnv, Account::master);
1921
1922 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2))
1924 .multiTx(att_create_acct_vec(2, amt, scuBob, 2))
1925 .close();
1926
1927 BEAST_EXPECT(door.diff() == STAmount(0));
1928 // att_create_acct_vec return vectors of size 2, so 2*3 txns
1929 BEAST_EXPECT(attester.diff() == -multiTtxFee(6));
1930
1931 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // ca claim id present
1932 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1933 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1934 BEAST_EXPECT(
1935 scEnv.claimCount(jvb) == 0); // claim count still 0
1936 }
1937
1938 {
1939 // complete attestations for 2nd account create => should
1940 // not complete
1941 Balance attester(scEnv, scAttester);
1942 Balance door(scEnv, Account::master);
1943
1944 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2))
1945 .close();
1946
1947 BEAST_EXPECT(door.diff() == STAmount(0));
1948 // att_create_acct_vec return vectors of size 3, so 3 txns
1949 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1950
1951 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1952 BEAST_EXPECT(
1953 scEnv.claimCount(jvb) == 0); // claim count still 0
1954 }
1955
1956 {
1957 // complete attestations for 3rd account create => should
1958 // not complete
1959 Balance attester(scEnv, scAttester);
1960 Balance door(scEnv, Account::master);
1961
1962 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
1963 .close();
1964
1965 BEAST_EXPECT(door.diff() == STAmount(0));
1966 // att_create_acct_vec return vectors of size 3, so 3 txns
1967 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1968
1969 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1970 BEAST_EXPECT(
1971 scEnv.claimCount(jvb) == 0); // claim count still 0
1972 }
1973
1974 {
1975 // complete attestations for 1st account create => account
1976 // should be created
1977 Balance attester(scEnv, scAttester);
1978 Balance door(scEnv, Account::master);
1979
1980 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1))
1981 .close();
1982
1983 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1984 // att_create_acct_vec return vectors of size 3, so 3 txns
1985 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1986 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
1987
1988 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id 1 deleted
1989 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
1990 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1991 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count now 1
1992 }
1993
1994 {
1995 // resend attestations for 3rd account create => still
1996 // should not complete
1997 Balance attester(scEnv, scAttester);
1998 Balance door(scEnv, Account::master);
1999
2000 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
2001 .close();
2002
2003 BEAST_EXPECT(door.diff() == STAmount(0));
2004 // att_create_acct_vec return vectors of size 3, so 3 txns
2005 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2006
2007 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2008 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2009 BEAST_EXPECT(
2010 scEnv.claimCount(jvb) == 1); // claim count still 1
2011 }
2012
2013 {
2014 // resend attestations for 2nd account create => account
2015 // should be created
2016 Balance attester(scEnv, scAttester);
2017 Balance door(scEnv, Account::master);
2018
2019 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close();
2020
2021 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2022 BEAST_EXPECT(attester.diff() == -tx_fee);
2023 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2024
2025 BEAST_EXPECT(!scEnv.caClaimID(jvb, 2)); // claim id 2 deleted
2026 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2027 BEAST_EXPECT(scEnv.claimCount(jvb) == 2); // claim count now 2
2028 }
2029 {
2030 // resend attestations for 3rc account create => account
2031 // should be created
2032 Balance attester(scEnv, scAttester);
2033 Balance door(scEnv, Account::master);
2034
2035 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close();
2036
2037 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2038 BEAST_EXPECT(attester.diff() == -tx_fee);
2039 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2040
2041 BEAST_EXPECT(!scEnv.caClaimID(jvb, 3)); // claim id 3 deleted
2042 BEAST_EXPECT(scEnv.claimCount(jvb) == 3); // claim count now 3
2043 }
2044 }
2045
2046 // Check that creating an account with less than the minimum reserve
2047 // fails.
2048 {
2049 XEnv mcEnv(*this);
2050 XEnv scEnv(*this, true);
2051
2052 auto const amt = res0 - XRP(1);
2053 auto const amt_plus_reward = amt + reward;
2054
2055 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2056
2057 {
2058 Balance door(mcEnv, mcDoor);
2059 Balance carol(mcEnv, mcCarol);
2060
2061 mcEnv
2063 mcCarol, jvb, scuAlice, amt, reward))
2064 .close();
2065
2066 BEAST_EXPECT(door.diff() == amt_plus_reward);
2067 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2068 }
2069
2072 .close();
2073
2074 Balance attester(scEnv, scAttester);
2075 Balance door(scEnv, Account::master);
2076
2077 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close();
2078 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2079 BEAST_EXPECT(
2080 scEnv.claimCount(jvb) == 0); // claim count is one less
2081
2082 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2, 2)).close();
2083 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2084 BEAST_EXPECT(
2085 scEnv.claimCount(jvb) == 1); // claim count was incremented
2086
2087 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2088 BEAST_EXPECT(door.diff() == -reward);
2089 BEAST_EXPECT(!scEnv.account(scuAlice));
2090 }
2091
2092 // Check that sending funds with an account create txn to an
2093 // existing account works.
2094 {
2095 XEnv mcEnv(*this);
2096 XEnv scEnv(*this, true);
2097
2098 auto const amt = XRP(111);
2099 auto const amt_plus_reward = amt + reward;
2100
2101 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2102
2103 {
2104 Balance door(mcEnv, mcDoor);
2105 Balance carol(mcEnv, mcCarol);
2106
2107 mcEnv
2109 mcCarol, jvb, scAlice, amt, reward))
2110 .close();
2111
2112 BEAST_EXPECT(door.diff() == amt_plus_reward);
2113 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2114 }
2115
2118 .close();
2119
2120 Balance attester(scEnv, scAttester);
2121 Balance door(scEnv, Account::master);
2122 Balance alice(scEnv, scAlice);
2123
2124 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2125 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2126 BEAST_EXPECT(
2127 scEnv.claimCount(jvb) == 0); // claim count is one less
2128
2129 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2130 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2131 BEAST_EXPECT(
2132 scEnv.claimCount(jvb) == 1); // claim count was incremented
2133
2134 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2135 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2136 BEAST_EXPECT(alice.diff() == amt);
2137 }
2138
2139 // Check that sending funds to an existing account with deposit auth
2140 // set fails for account create transactions.
2141 {
2142 XEnv mcEnv(*this);
2143 XEnv scEnv(*this, true);
2144
2145 auto const amt = XRP(1000);
2146 auto const amt_plus_reward = amt + reward;
2147
2148 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2149
2150 {
2151 Balance door(mcEnv, mcDoor);
2152 Balance carol(mcEnv, mcCarol);
2153
2154 mcEnv
2156 mcCarol, jvb, scAlice, amt, reward))
2157 .close();
2158
2159 BEAST_EXPECT(door.diff() == amt_plus_reward);
2160 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2161 }
2162
2165 .tx(fset("scAlice", asfDepositAuth)) // set deposit auth
2166 .close();
2167
2168 Balance attester(scEnv, scAttester);
2169 Balance door(scEnv, Account::master);
2170 Balance alice(scEnv, scAlice);
2171
2172 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2173 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2174 BEAST_EXPECT(
2175 scEnv.claimCount(jvb) == 0); // claim count is one less
2176
2177 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2178 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2179 BEAST_EXPECT(
2180 scEnv.claimCount(jvb) == 1); // claim count was incremented
2181
2182 BEAST_EXPECT(door.diff() == -reward);
2183 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2184 BEAST_EXPECT(alice.diff() == STAmount(0));
2185 }
2186
2187 // If an account is unable to pay the reserve, check that it fails.
2188 // [greg todo] I don't know what this should test??
2189
2190 // If an attestation already exists for that server and claim id,
2191 // the new attestation should replace the old attestation
2192 {
2193 XEnv mcEnv(*this);
2194 XEnv scEnv(*this, true);
2195 auto const amt = XRP(1000);
2196 auto const amt_plus_reward = amt + reward;
2197
2198 {
2199 Balance door(mcEnv, mcDoor);
2200 Balance carol(mcEnv, mcCarol);
2201
2202 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
2203 .close()
2205 mcAlice, jvb, scuAlice, amt, reward))
2206 .close() // make sure Alice gets claim #1
2208 mcBob, jvb, scuBob, amt, reward))
2209 .close() // make sure Bob gets claim #2
2211 mcCarol, jvb, scuCarol, amt, reward))
2212 .close(); // and Carol will get claim #3
2213
2214 BEAST_EXPECT(
2215 door.diff() ==
2216 (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
2217 tx_fee));
2218 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
2219 }
2220
2221 std::uint32_t const red_quorum = 2;
2223 .tx(jtx::signers(Account::master, red_quorum, signers))
2224 .close();
2225
2226 {
2227 Balance attester(scEnv, scAttester);
2228 Balance door(scEnv, Account::master);
2229 auto const bad_amt = XRP(10);
2230 std::uint32_t txCount = 0;
2231
2232 // send attestations with incorrect amounts to for all 3
2233 // AccountCreate. They will be replaced later
2234 scEnv.multiTx(att_create_acct_vec(1, bad_amt, scuAlice, 1))
2235 .multiTx(att_create_acct_vec(2, bad_amt, scuBob, 1, 2))
2236 .multiTx(att_create_acct_vec(3, bad_amt, scuCarol, 1, 1))
2237 .close();
2238 txCount += 3;
2239
2240 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 created");
2241 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 created");
2242 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 created");
2243
2244 // note: if we send inconsistent attestations in the same
2245 // batch, the transaction errors.
2246
2247 // from now on we send correct attestations
2248 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 0))
2249 .multiTx(att_create_acct_vec(2, amt, scuBob, 1, 2))
2250 .multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 4))
2251 .close();
2252 txCount += 3;
2253
2254 BEAST_EXPECTS(
2255 !!scEnv.caClaimID(jvb, 1), "claim id 1 still there");
2256 BEAST_EXPECTS(
2257 !!scEnv.caClaimID(jvb, 2), "claim id 2 still there");
2258 BEAST_EXPECTS(
2259 !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2260 BEAST_EXPECTS(
2261 scEnv.claimCount(jvb) == 0, "No account created yet");
2262
2263 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 1))
2264 .close();
2265 txCount += 1;
2266
2267 BEAST_EXPECTS(
2268 !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2269 BEAST_EXPECTS(
2270 scEnv.claimCount(jvb) == 0, "No account created yet");
2271
2272 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 2))
2273 .close();
2274 txCount += 1;
2275
2276 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 deleted");
2277 BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created");
2278
2279 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3))
2280 .multiTx(
2281 att_create_acct_vec(1, amt, scuAlice, 1, 3),
2283 .close();
2284 txCount += 2;
2285
2286 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 2), "claim id 2 deleted");
2287 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 not added");
2288 BEAST_EXPECTS(
2289 scEnv.claimCount(jvb) == 2, "scuAlice & scuBob created");
2290
2291 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 0))
2292 .close();
2293 txCount += 1;
2294
2295 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 3), "claim id 3 deleted");
2296 BEAST_EXPECTS(
2297 scEnv.claimCount(jvb) == 3, "All 3 accounts created");
2298
2299 // because of the division of the rewards among attesters,
2300 // sometimes a couple drops are left over unspent in the
2301 // door account (here 2 drops)
2302 BEAST_EXPECT(
2303 multiply(amt_plus_reward, STAmount(3), xrpIssue()) +
2304 door.diff() <
2305 drops(3));
2306 BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount));
2307 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2308 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2309 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2310 }
2311 }
2312
2313 // If attestation moves funds, confirm the claim ledger objects are
2314 // removed (for both account create and "regular" transactions)
2315 // [greg] we do this in all attestation tests
2316
2317 // coverage test: add_attestation transaction with incorrect flag
2318 {
2319 XEnv scEnv(*this, true);
2322 .close()
2324 scAttester,
2325 jvb,
2326 mcAlice,
2327 XRP(1000),
2328 payees[0],
2329 true,
2330 1,
2331 {},
2332 signers[0]),
2335 .close();
2336 }
2337
2338 // coverage test: add_attestation with xchain feature
2339 // disabled
2340 {
2341 XEnv scEnv(*this, true);
2344 .disableFeature(featureXChainBridge)
2345 .close()
2347 scAttester,
2348 jvb,
2349 mcAlice,
2350 XRP(1000),
2351 payees[0],
2352 true,
2353 1,
2354 {},
2355 signers[0]),
2357 .close();
2358 }
2359 }
2360
2361 void
2363 {
2364 using namespace jtx;
2365
2366 testcase("Add Non Batch Claim Attestation");
2367
2368 {
2369 XEnv mcEnv(*this);
2370 XEnv scEnv(*this, true);
2371 std::uint32_t const claimID = 1;
2372
2373 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2374
2377 .close()
2379 .close();
2380
2381 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
2382
2383 Account const dst{scBob};
2384 auto const amt = XRP(1000);
2385 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2386
2387 auto const dstStartBalance = scEnv.env_.balance(dst);
2388
2389 for (int i = 0; i < signers.size(); ++i)
2390 {
2391 auto const att = claim_attestation(
2392 scAttester,
2393 jvb,
2394 mcAlice,
2395 amt,
2396 payees[i],
2397 true,
2398 claimID,
2399 dst,
2400 signers[i]);
2401
2402 TER const expectedTER =
2404 if (i + 1 == quorum)
2405 scEnv.tx(att, ter(expectedTER)).close();
2406 else
2407 scEnv.tx(att, ter(expectedTER)).close();
2408
2409 if (i + 1 < quorum)
2410 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2411 else
2412 BEAST_EXPECT(
2413 dstStartBalance + amt == scEnv.env_.balance(dst));
2414 }
2415 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2416 }
2417
2418 {
2435 XEnv mcEnv(*this);
2436 XEnv scEnv(*this, true);
2437 auto const amt = XRP(1000);
2438 std::uint32_t const claimID = 1;
2439
2440 for (auto i = 0; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2; ++i)
2441 scEnv.fund(amt, alt_signers[i].account);
2442
2443 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2444
2447 .close()
2449 .close();
2450
2451 Account const dst{scBob};
2452 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2453 auto const dstStartBalance = scEnv.env_.balance(dst);
2454
2455 {
2456 // G1: master key
2457 auto att = claim_attestation(
2458 scAttester,
2459 jvb,
2460 mcAlice,
2461 amt,
2462 payees[0],
2463 true,
2464 claimID,
2465 dst,
2466 alt_signers[0]);
2467 scEnv.tx(att).close();
2468 }
2469 {
2470 // G2: regular key
2471 // alt_signers[0] is the regular key of alt_signers[1]
2472 // There should be 2 attestations after the transaction
2473 scEnv
2474 .tx(jtx::regkey(
2475 alt_signers[1].account, alt_signers[0].account))
2476 .close();
2477 auto att = claim_attestation(
2478 scAttester,
2479 jvb,
2480 mcAlice,
2481 amt,
2482 payees[1],
2483 true,
2484 claimID,
2485 dst,
2486 alt_signers[0]);
2487 att[sfAttestationSignerAccount.getJsonName()] =
2488 alt_signers[1].account.human();
2489 scEnv.tx(att).close();
2490 }
2491 {
2492 // B3: public key and non-exist (unfunded) account mismatch
2493 // G3: public key and non-exist (unfunded) account match
2494 auto const unfundedSigner1 =
2496 auto const unfundedSigner2 =
2498 auto att = claim_attestation(
2499 scAttester,
2500 jvb,
2501 mcAlice,
2502 amt,
2504 true,
2505 claimID,
2506 dst,
2507 unfundedSigner1);
2508 att[sfAttestationSignerAccount.getJsonName()] =
2509 unfundedSigner2.account.human();
2511 .close();
2512 att[sfAttestationSignerAccount.getJsonName()] =
2513 unfundedSigner1.account.human();
2514 scEnv.tx(att).close();
2515 }
2516 {
2517 // B2: single item signer list
2518 std::vector<signer> tempSignerList = {signers[0]};
2519 scEnv.tx(
2520 jtx::signers(alt_signers[2].account, 1, tempSignerList));
2521 auto att = claim_attestation(
2522 scAttester,
2523 jvb,
2524 mcAlice,
2525 amt,
2526 payees[2],
2527 true,
2528 claimID,
2529 dst,
2530 tempSignerList.front());
2531 att[sfAttestationSignerAccount.getJsonName()] =
2532 alt_signers[2].account.human();
2534 .close();
2535 }
2536 {
2537 // B1: disabled master key
2538 scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0))
2539 .close();
2540 auto att = claim_attestation(
2541 scAttester,
2542 jvb,
2543 mcAlice,
2544 amt,
2545 payees[2],
2546 true,
2547 claimID,
2548 dst,
2549 alt_signers[2]);
2551 .close();
2552 }
2553 {
2554 // --B4: not on signer list
2555 auto att = claim_attestation(
2556 scAttester,
2557 jvb,
2558 mcAlice,
2559 amt,
2560 payees[0],
2561 true,
2562 claimID,
2563 dst,
2564 signers[0]);
2565 scEnv.tx(att, ter(tecNO_PERMISSION)).close();
2566 }
2567 {
2568 // --B5: missing sfAttestationSignerAccount field
2569 // Then submit the one with the field. Should rearch quorum.
2570 auto att = claim_attestation(
2571 scAttester,
2572 jvb,
2573 mcAlice,
2574 amt,
2575 payees[3],
2576 true,
2577 claimID,
2578 dst,
2579 alt_signers[3]);
2580 att.removeMember(sfAttestationSignerAccount.getJsonName());
2581 scEnv.tx(att, ter(temMALFORMED)).close();
2582 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2583 att[sfAttestationSignerAccount.getJsonName()] =
2584 alt_signers[3].account.human();
2585 scEnv.tx(att).close();
2586 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2587 }
2588 }
2589 }
2590
2591 void
2593 {
2594 using namespace jtx;
2595
2596 testcase("Add Non Batch Account Create Attestation");
2597
2598 XEnv mcEnv(*this);
2599 XEnv scEnv(*this, true);
2600
2601 XRPAmount tx_fee = mcEnv.txFee();
2602
2603 Account a{"a"};
2604 Account doorA{"doorA"};
2605
2606 STAmount funds{XRP(10000)};
2607 mcEnv.fund(funds, a);
2608 mcEnv.fund(funds, doorA);
2609
2610 Account ua{"ua"}; // unfunded account we want to create
2611
2612 BridgeDef xrp_b{
2613 doorA,
2614 xrpIssue(),
2616 xrpIssue(),
2617 XRP(1), // reward
2618 XRP(20), // minAccountCreate
2619 4, // quorum
2620 signers,
2622
2623 xrp_b.initBridge(mcEnv, scEnv);
2624
2625 auto const amt = XRP(777);
2626 auto const amt_plus_reward = amt + xrp_b.reward;
2627 {
2628 Balance bal_doorA(mcEnv, doorA);
2629 Balance bal_a(mcEnv, a);
2630
2631 mcEnv
2633 a, xrp_b.jvb, ua, amt, xrp_b.reward))
2634 .close();
2635
2636 BEAST_EXPECT(bal_doorA.diff() == amt_plus_reward);
2637 BEAST_EXPECT(bal_a.diff() == -(amt_plus_reward + tx_fee));
2638 }
2639
2640 for (int i = 0; i < signers.size(); ++i)
2641 {
2642 auto const att = create_account_attestation(
2643 signers[0].account,
2644 xrp_b.jvb,
2645 a,
2646 amt,
2647 xrp_b.reward,
2648 signers[i].account,
2649 true,
2650 1,
2651 ua,
2652 signers[i]);
2653 TER const expectedTER = i < xrp_b.quorum
2654 ? tesSUCCESS
2656
2657 scEnv.tx(att, ter(expectedTER)).close();
2658 if (i + 1 < xrp_b.quorum)
2659 BEAST_EXPECT(!scEnv.env_.le(ua));
2660 else
2661 BEAST_EXPECT(scEnv.env_.le(ua));
2662 }
2663 BEAST_EXPECT(scEnv.env_.le(ua));
2664 }
2665
2666 void
2668 {
2669 using namespace jtx;
2670
2671 XRPAmount res0 = reserve(0);
2672 XRPAmount tx_fee = txFee();
2673
2674 testcase("Claim");
2675
2676 // Claim where the amount matches what is attested to, to an account
2677 // that exists, and there are enough attestations to reach a quorum
2678 // => should succeed
2679 // -----------------------------------------------------------------
2680 for (auto withClaim : {false, true})
2681 {
2682 XEnv mcEnv(*this);
2683 XEnv scEnv(*this, true);
2684
2685 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2686
2689 .close()
2691 .close();
2692
2693 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2694 auto const amt = XRP(1000);
2695 std::uint32_t const claimID = 1;
2696 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2697
2698 BalanceTransfer transfer(
2699 scEnv,
2701 scBob,
2702 scAlice,
2703 &payees[0],
2705 withClaim);
2706
2707 scEnv
2709 scAttester,
2710 jvb,
2711 mcAlice,
2712 amt,
2713 payees,
2714 true,
2715 claimID,
2716 dst,
2717 signers))
2718 .close();
2719 if (withClaim)
2720 {
2721 BEAST_EXPECT(transfer.has_not_happened());
2722
2723 // need to submit a claim transactions
2724 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
2725 .close();
2726 }
2727
2728 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2729 }
2730
2731 // Claim with just one attestation signed by the Master key
2732 // => should not succeed
2733 // -----------------------------------------------------------------
2734 for (auto withClaim : {false, true})
2735 {
2736 XEnv mcEnv(*this);
2737 XEnv scEnv(*this, true);
2738
2739 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2740
2741 scEnv
2743 //.tx(jtx::signers(Account::master, quorum, signers))
2744 .close()
2746 .close();
2747
2748 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2749 auto const amt = XRP(1000);
2750 std::uint32_t const claimID = 1;
2751 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2752
2753 BalanceTransfer transfer(
2754 scEnv,
2756 scBob,
2757 scAlice,
2758 &payees[0],
2759 1,
2760 withClaim);
2761
2762 jtx::signer master_signer(Account::master);
2763 scEnv
2765 scAttester,
2766 jvb,
2767 mcAlice,
2768 amt,
2769 payees[0],
2770 true,
2771 claimID,
2772 dst,
2773 master_signer),
2775 .close();
2776
2777 BEAST_EXPECT(transfer.has_not_happened());
2778 }
2779
2780 // Claim with just one attestation signed by a regular key
2781 // associated to the master account
2782 // => should not succeed
2783 // -----------------------------------------------------------------
2784 for (auto withClaim : {false, true})
2785 {
2786 XEnv mcEnv(*this);
2787 XEnv scEnv(*this, true);
2788
2789 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2790
2791 scEnv
2793 //.tx(jtx::signers(Account::master, quorum, signers))
2795 .close()
2797 .close();
2798
2799 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2800 auto const amt = XRP(1000);
2801 std::uint32_t const claimID = 1;
2802 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2803
2804 BalanceTransfer transfer(
2805 scEnv,
2807 scBob,
2808 scAlice,
2809 &payees[0],
2810 1,
2811 withClaim);
2812
2813 jtx::signer master_signer(payees[0]);
2814 scEnv
2816 scAttester,
2817 jvb,
2818 mcAlice,
2819 amt,
2820 payees[0],
2821 true,
2822 claimID,
2823 dst,
2824 master_signer),
2826 .close();
2827
2828 BEAST_EXPECT(transfer.has_not_happened());
2829 }
2830
2831 // Claim against non-existent bridge
2832 // ---------------------------------
2833 for (auto withClaim : {false, true})
2834 {
2835 XEnv mcEnv(*this);
2836 XEnv scEnv(*this, true);
2837
2838 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2839
2840 auto jvb_unknown =
2842
2845 .close()
2847 scAlice, jvb_unknown, reward, mcAlice),
2849 .close();
2850
2851 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2852 auto const amt = XRP(1000);
2853 std::uint32_t const claimID = 1;
2854 mcEnv
2855 .tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst),
2857 .close();
2858
2859 BalanceTransfer transfer(
2860 scEnv, Account::master, scBob, scAlice, payees, withClaim);
2861 scEnv
2863 scAttester,
2864 jvb_unknown,
2865 mcAlice,
2866 amt,
2867 payees[0],
2868 true,
2869 claimID,
2870 dst,
2871 signers[0]),
2873 .close();
2874
2875 if (withClaim)
2876 {
2877 BEAST_EXPECT(transfer.has_not_happened());
2878
2879 // need to submit a claim transactions
2880 scEnv
2881 .tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob),
2883 .close();
2884 }
2885
2886 BEAST_EXPECT(transfer.has_not_happened());
2887 }
2888
2889 // Claim against non-existent claim id
2890 // -----------------------------------
2891 for (auto withClaim : {false, true})
2892 {
2893 XEnv mcEnv(*this);
2894 XEnv scEnv(*this, true);
2895
2896 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2897
2900 .close()
2902 .close();
2903
2904 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2905 auto const amt = XRP(1000);
2906 std::uint32_t const claimID = 1;
2907 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2908
2909 BalanceTransfer transfer(
2910 scEnv, Account::master, scBob, scAlice, payees, withClaim);
2911
2912 // attest using non-existent claim id
2913 scEnv
2915 scAttester,
2916 jvb,
2917 mcAlice,
2918 amt,
2919 payees[0],
2920 true,
2921 999,
2922 dst,
2923 signers[0]),
2925 .close();
2926 if (withClaim)
2927 {
2928 BEAST_EXPECT(transfer.has_not_happened());
2929
2930 // claim using non-existent claim id
2931 scEnv
2932 .tx(xchain_claim(scAlice, jvb, 999, amt, scBob),
2934 .close();
2935 }
2936
2937 BEAST_EXPECT(transfer.has_not_happened());
2938 }
2939
2940 // Claim against a claim id owned by another account
2941 // -------------------------------------------------
2942 for (auto withClaim : {false, true})
2943 {
2944 XEnv mcEnv(*this);
2945 XEnv scEnv(*this, true);
2946
2947 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2948
2951 .close()
2953 .close();
2954
2955 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2956 auto const amt = XRP(1000);
2957 std::uint32_t const claimID = 1;
2958 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2959
2960 BalanceTransfer transfer(
2961 scEnv,
2963 scBob,
2964 scAlice,
2965 &payees[0],
2967 withClaim);
2968
2969 scEnv
2971 scAttester,
2972 jvb,
2973 mcAlice,
2974 amt,
2975 payees,
2976 true,
2977 claimID,
2978 dst,
2979 signers))
2980 .close();
2981 if (withClaim)
2982 {
2983 BEAST_EXPECT(transfer.has_not_happened());
2984
2985 // submit a claim transaction with the wrong account (scGw
2986 // instead of scAlice)
2987 scEnv
2988 .tx(xchain_claim(scGw, jvb, claimID, amt, scBob),
2990 .close();
2991 BEAST_EXPECT(transfer.has_not_happened());
2992 }
2993 else
2994 {
2995 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2996 }
2997 }
2998
2999 // Claim against a claim id with no attestations
3000 // ---------------------------------------------
3001 for (auto withClaim : {false, true})
3002 {
3003 XEnv mcEnv(*this);
3004 XEnv scEnv(*this, true);
3005
3006 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3007
3010 .close()
3012 .close();
3013
3014 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3015 auto const amt = XRP(1000);
3016 std::uint32_t const claimID = 1;
3017 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3018
3019 BalanceTransfer transfer(
3020 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3021
3022 // don't send any attestations
3023
3024 if (withClaim)
3025 {
3026 BEAST_EXPECT(transfer.has_not_happened());
3027
3028 // need to submit a claim transactions
3029 scEnv
3030 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3032 .close();
3033 }
3034
3035 BEAST_EXPECT(transfer.has_not_happened());
3036 }
3037
3038 // Claim against a claim id with attestations, but not enough to
3039 // make a quorum
3040 // --------------------------------------------------------------------
3041 for (auto withClaim : {false, true})
3042 {
3043 XEnv mcEnv(*this);
3044 XEnv scEnv(*this, true);
3045
3046 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3047
3050 .close()
3052 .close();
3053
3054 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3055 auto const amt = XRP(1000);
3056 std::uint32_t const claimID = 1;
3057 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3058
3059 BalanceTransfer transfer(
3060 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3061
3062 auto tooFew = quorum - 1;
3063 scEnv
3065 scAttester,
3066 jvb,
3067 mcAlice,
3068 amt,
3069 payees,
3070 true,
3071 claimID,
3072 dst,
3073 signers,
3074 tooFew))
3075 .close();
3076 if (withClaim)
3077 {
3078 BEAST_EXPECT(transfer.has_not_happened());
3079
3080 // need to submit a claim transactions
3081 scEnv
3082 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3084 .close();
3085 }
3086
3087 BEAST_EXPECT(transfer.has_not_happened());
3088 }
3089
3090 // Claim id of zero
3091 // ----------------
3092 for (auto withClaim : {false, true})
3093 {
3094 XEnv mcEnv(*this);
3095 XEnv scEnv(*this, true);
3096
3097 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3098
3101 .close()
3103 .close();
3104
3105 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3106 auto const amt = XRP(1000);
3107 std::uint32_t const claimID = 1;
3108 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3109
3110 BalanceTransfer transfer(
3111 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3112
3113 scEnv
3114 .multiTx(
3116 scAttester,
3117 jvb,
3118 mcAlice,
3119 amt,
3120 payees,
3121 true,
3122 0,
3123 dst,
3124 signers),
3126 .close();
3127 if (withClaim)
3128 {
3129 BEAST_EXPECT(transfer.has_not_happened());
3130
3131 // need to submit a claim transactions
3132 scEnv
3133 .tx(xchain_claim(scAlice, jvb, 0, amt, scBob),
3135 .close();
3136 }
3137
3138 BEAST_EXPECT(transfer.has_not_happened());
3139 }
3140
3141 // Claim issue that does not match the expected issue on the bridge
3142 // (either LockingChainIssue or IssuingChainIssue, depending on the
3143 // chain). The claim id should already have enough attestations to
3144 // reach a quorum for this amount (for a different issuer).
3145 // ---------------------------------------------------------------------
3146 for (auto withClaim : {true})
3147 {
3148 XEnv mcEnv(*this);
3149 XEnv scEnv(*this, true);
3150
3151 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3152
3155 .close()
3157 .close();
3158
3159 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3160 auto const amt = XRP(1000);
3161 std::uint32_t const claimID = 1;
3162 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3163
3164 BalanceTransfer transfer(
3165 scEnv,
3167 scBob,
3168 scAlice,
3169 &payees[0],
3171 withClaim);
3172
3173 scEnv
3175 scAttester,
3176 jvb,
3177 mcAlice,
3178 amt,
3179 payees,
3180 true,
3181 claimID,
3182 dst,
3183 signers))
3184 .close();
3185
3186 if (withClaim)
3187 {
3188 BEAST_EXPECT(transfer.has_not_happened());
3189
3190 // need to submit a claim transactions
3191 scEnv
3192 .tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob),
3194 .close();
3195 }
3196
3197 BEAST_EXPECT(transfer.has_not_happened());
3198 }
3199
3200 // Claim to a destination that does not already exist on the chain
3201 // -----------------------------------------------------------------
3202 for (auto withClaim : {true})
3203 {
3204 XEnv mcEnv(*this);
3205 XEnv scEnv(*this, true);
3206
3207 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3208
3211 .close()
3213 .close();
3214
3215 auto dst(withClaim ? std::nullopt : std::optional<Account>{scuBob});
3216 auto const amt = XRP(1000);
3217 std::uint32_t const claimID = 1;
3218 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3219
3220 BalanceTransfer transfer(
3221 scEnv,
3223 scBob,
3224 scAlice,
3225 &payees[0],
3227 withClaim);
3228
3229 scEnv
3231 scAttester,
3232 jvb,
3233 mcAlice,
3234 amt,
3235 payees,
3236 true,
3237 claimID,
3238 dst,
3239 signers))
3240 .close();
3241 if (withClaim)
3242 {
3243 BEAST_EXPECT(transfer.has_not_happened());
3244
3245 // need to submit a claim transactions
3246 scEnv
3247 .tx(xchain_claim(scAlice, jvb, claimID, amt, scuBob),
3248 ter(tecNO_DST))
3249 .close();
3250 }
3251
3252 BEAST_EXPECT(transfer.has_not_happened());
3253 }
3254
3255 // Claim where the claim id owner does not have enough XRP to pay
3256 // the reward
3257 // ------------------------------------------------------------------
3258 for (auto withClaim : {false, true})
3259 {
3260 XEnv mcEnv(*this);
3261 XEnv scEnv(*this, true);
3262
3263 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3264 STAmount huge_reward{XRP(20000)};
3265 BEAST_EXPECT(huge_reward > scEnv.balance(scAlice));
3266
3267 scEnv.tx(create_bridge(Account::master, jvb, huge_reward))
3269 .close()
3270 .tx(xchain_create_claim_id(scAlice, jvb, huge_reward, mcAlice))
3271 .close();
3272
3273 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3274 auto const amt = XRP(1000);
3275 std::uint32_t const claimID = 1;
3276 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3277
3278 BalanceTransfer transfer(
3279 scEnv,
3281 scBob,
3282 scAlice,
3283 &payees[0],
3285 withClaim);
3286
3287 if (withClaim)
3288 {
3289 scEnv
3291 scAttester,
3292 jvb,
3293 mcAlice,
3294 amt,
3295 payees,
3296 true,
3297 claimID,
3298 dst,
3299 signers))
3300 .close();
3301 BEAST_EXPECT(transfer.has_not_happened());
3302
3303 // need to submit a claim transactions
3304 scEnv
3305 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3307 .close();
3308 }
3309 else
3310 {
3311 auto txns = claim_attestations(
3312 scAttester,
3313 jvb,
3314 mcAlice,
3315 amt,
3316 payees,
3317 true,
3318 claimID,
3319 dst,
3320 signers);
3321 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3322 {
3323 scEnv.tx(txns[i]).close();
3324 }
3325 scEnv.tx(txns.back());
3326 scEnv.close();
3327 // The attestation should succeed, because it adds an
3328 // attestation, but the claim should fail with insufficient
3329 // funds
3330 scEnv
3331 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3333 .close();
3334 }
3335
3336 BEAST_EXPECT(transfer.has_not_happened());
3337 }
3338
3339 // Claim where the claim id owner has enough XRP to pay the reward,
3340 // but it would put his balance below the reserve
3341 // --------------------------------------------------------------------
3342 for (auto withClaim : {false, true})
3343 {
3344 XEnv mcEnv(*this);
3345 XEnv scEnv(*this, true);
3346
3347 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3348
3351 .fund(
3352 res0 + reward,
3353 scuAlice) // just not enough because of fees
3354 .close()
3357 .close();
3358
3359 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3360 auto const amt = XRP(1000);
3361 std::uint32_t const claimID = 1;
3362 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3363
3364 BalanceTransfer transfer(
3365 scEnv, Account::master, scBob, scuAlice, payees, withClaim);
3366
3367 scEnv
3369 scAttester,
3370 jvb,
3371 mcAlice,
3372 amt,
3373 payees[0],
3374 true,
3375 claimID,
3376 dst,
3377 signers[0]),
3379 .close();
3380 if (withClaim)
3381 {
3382 BEAST_EXPECT(transfer.has_not_happened());
3383
3384 // need to submit a claim transactions
3385 scEnv
3386 .tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob),
3388 .close();
3389 }
3390
3391 BEAST_EXPECT(transfer.has_not_happened());
3392 }
3393
3394 // Pay to an account with deposit auth set
3395 // ---------------------------------------
3396 for (auto withClaim : {false, true})
3397 {
3398 XEnv mcEnv(*this);
3399 XEnv scEnv(*this, true);
3400
3401 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3402
3405 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3406 .close()
3408 .close();
3409
3410 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3411 auto const amt = XRP(1000);
3412 std::uint32_t const claimID = 1;
3413 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3414
3415 BalanceTransfer transfer(
3416 scEnv,
3418 scBob,
3419 scAlice,
3420 &payees[0],
3422 withClaim);
3423 auto txns = claim_attestations(
3424 scAttester,
3425 jvb,
3426 mcAlice,
3427 amt,
3428 payees,
3429 true,
3430 claimID,
3431 dst,
3432 signers);
3433 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3434 {
3435 scEnv.tx(txns[i]).close();
3436 }
3437 if (withClaim)
3438 {
3439 scEnv.tx(txns.back()).close();
3440
3441 BEAST_EXPECT(transfer.has_not_happened());
3442
3443 // need to submit a claim transactions
3444 scEnv
3445 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3447 .close();
3448
3449 // the transfer failed, but check that we can still use the
3450 // claimID with a different account
3451 Balance scCarol_bal(scEnv, scCarol);
3452
3453 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3454 .close();
3455 BEAST_EXPECT(scCarol_bal.diff() == amt);
3456 }
3457 else
3458 {
3459 scEnv.tx(txns.back()).close();
3460 scEnv
3461 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3463 .close();
3464 // A way would be to remove deposit auth and resubmit the
3465 // attestations (even though the witness servers won't do
3466 // it)
3467 scEnv
3468 .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth
3469 .close();
3470
3471 Balance scBob_bal(scEnv, scBob);
3472 scEnv.tx(txns.back()).close();
3473 BEAST_EXPECT(scBob_bal.diff() == amt);
3474 }
3475 }
3476
3477 // Pay to an account with Destination Tag set
3478 // ------------------------------------------
3479 for (auto withClaim : {false, true})
3480 {
3481 XEnv mcEnv(*this);
3482 XEnv scEnv(*this, true);
3483
3484 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3485
3488 .tx(fset("scBob", asfRequireDest)) // set dest tag
3489 .close()
3491 .close();
3492
3493 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3494 auto const amt = XRP(1000);
3495 std::uint32_t const claimID = 1;
3496 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3497
3498 BalanceTransfer transfer(
3499 scEnv,
3501 scBob,
3502 scAlice,
3503 &payees[0],
3505 withClaim);
3506 auto txns = claim_attestations(
3507 scAttester,
3508 jvb,
3509 mcAlice,
3510 amt,
3511 payees,
3512 true,
3513 claimID,
3514 dst,
3515 signers);
3516 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3517 {
3518 scEnv.tx(txns[i]).close();
3519 }
3520 if (withClaim)
3521 {
3522 scEnv.tx(txns.back()).close();
3523 BEAST_EXPECT(transfer.has_not_happened());
3524
3525 // need to submit a claim transactions
3526 scEnv
3527 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3529 .close();
3530
3531 // the transfer failed, but check that we can still use the
3532 // claimID with a different account
3533 Balance scCarol_bal(scEnv, scCarol);
3534
3535 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3536 .close();
3537 BEAST_EXPECT(scCarol_bal.diff() == amt);
3538 }
3539 else
3540 {
3541 scEnv.tx(txns.back()).close();
3542 scEnv
3543 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3545 .close();
3546 // A way would be to remove the destination tag requirement
3547 // and resubmit the attestations (even though the witness
3548 // servers won't do it)
3549 scEnv
3550 .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag
3551 .close();
3552
3553 Balance scBob_bal(scEnv, scBob);
3554
3555 scEnv.tx(txns.back()).close();
3556 BEAST_EXPECT(scBob_bal.diff() == amt);
3557 }
3558 }
3559
3560 // Pay to an account with deposit auth set. Check that the attestations
3561 // are still validated and that we can used the claimID to transfer the
3562 // funds to a different account (which doesn't have deposit auth set)
3563 // --------------------------------------------------------------------
3564 {
3565 XEnv mcEnv(*this);
3566 XEnv scEnv(*this, true);
3567
3568 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3569
3572 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3573 .close()
3575 .close();
3576
3577 auto dst(std::optional<Account>{scBob});
3578 auto const amt = XRP(1000);
3579 std::uint32_t const claimID = 1;
3580 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3581
3582 // we should be able to submit the attestations, but the transfer
3583 // should not occur because dest account has deposit auth set
3584 Balance scBob_bal(scEnv, scBob);
3585
3587 scAttester,
3588 jvb,
3589 mcAlice,
3590 amt,
3591 payees,
3592 true,
3593 claimID,
3594 dst,
3595 signers));
3596 BEAST_EXPECT(scBob_bal.diff() == STAmount(0));
3597
3598 // Check that check that we still can use the claimID to transfer
3599 // the amount to a different account
3600 Balance scCarol_bal(scEnv, scCarol);
3601
3602 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3603 BEAST_EXPECT(scCarol_bal.diff() == amt);
3604 }
3605
3606 // Claim where the amount different from what is attested to
3607 // ---------------------------------------------------------
3608 for (auto withClaim : {true})
3609 {
3610 XEnv mcEnv(*this);
3611 XEnv scEnv(*this, true);
3612
3613 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3614
3617 .close()
3619 .close();
3620
3621 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3622 auto const amt = XRP(1000);
3623 std::uint32_t const claimID = 1;
3624 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3625
3626 BalanceTransfer transfer(
3627 scEnv,
3629 scBob,
3630 scAlice,
3631 &payees[0],
3633 withClaim);
3635 scAttester,
3636 jvb,
3637 mcAlice,
3638 amt,
3639 payees,
3640 true,
3641 claimID,
3642 dst,
3643 signers));
3644 if (withClaim)
3645 {
3646 BEAST_EXPECT(transfer.has_not_happened());
3647
3648 // claim wrong amount
3649 scEnv
3650 .tx(xchain_claim(scAlice, jvb, claimID, one_xrp, scBob),
3652 .close();
3653 }
3654
3655 BEAST_EXPECT(transfer.has_not_happened());
3656 }
3657
3658 // Verify that rewards are paid from the account that owns the claim
3659 // id
3660 // --------------------------------------------------------------------
3661 for (auto withClaim : {false, true})
3662 {
3663 XEnv mcEnv(*this);
3664 XEnv scEnv(*this, true);
3665
3666 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3667
3670 .close()
3672 .close();
3673
3674 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3675 auto const amt = XRP(1000);
3676 std::uint32_t const claimID = 1;
3677 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3678
3679 BalanceTransfer transfer(
3680 scEnv,
3682 scBob,
3683 scAlice,
3684 &payees[0],
3686 withClaim);
3687 Balance scAlice_bal(scEnv, scAlice);
3689 scAttester,
3690 jvb,
3691 mcAlice,
3692 amt,
3693 payees,
3694 true,
3695 claimID,
3696 dst,
3697 signers));
3698
3699 STAmount claim_cost = reward;
3700
3701 if (withClaim)
3702 {
3703 BEAST_EXPECT(transfer.has_not_happened());
3704
3705 // need to submit a claim transactions
3706 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3707 .close();
3708 claim_cost += tx_fee;
3709 }
3710
3711 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3712 BEAST_EXPECT(
3713 scAlice_bal.diff() == -claim_cost); // because reward % 4 == 0
3714 }
3715
3716 // Verify that if a reward is not evenly divisible among the reward
3717 // accounts, the remaining amount goes to the claim id owner.
3718 // ----------------------------------------------------------------
3719 for (auto withClaim : {false, true})
3720 {
3721 XEnv mcEnv(*this);
3722 XEnv scEnv(*this, true);
3723
3725
3728 .close()
3730 .close();
3731
3732 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3733 auto const amt = XRP(1000);
3734 std::uint32_t const claimID = 1;
3735 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3736
3737 BalanceTransfer transfer(
3738 scEnv,
3740 scBob,
3741 scAlice,
3742 &payees[0],
3744 withClaim);
3745 Balance scAlice_bal(scEnv, scAlice);
3747 scAttester,
3748 jvb,
3749 mcAlice,
3750 amt,
3751 payees,
3752 true,
3753 claimID,
3754 dst,
3755 signers));
3756 STAmount claim_cost = tiny_reward;
3757
3758 if (withClaim)
3759 {
3760 BEAST_EXPECT(transfer.has_not_happened());
3761
3762 // need to submit a claim transactions
3763 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3764 .close();
3765 claim_cost += tx_fee;
3766 }
3767
3768 BEAST_EXPECT(transfer.has_happened(amt, tiny_reward_split));
3769 BEAST_EXPECT(
3770 scAlice_bal.diff() == -(claim_cost - tiny_reward_remainder));
3771 }
3772
3773 // If a reward distribution fails for one of the reward accounts
3774 // (the reward account doesn't exist or has deposit auth set), then
3775 // the txn should still succeed, but that portion should go to the
3776 // claim id owner.
3777 // -------------------------------------------------------------------
3778 for (auto withClaim : {false, true})
3779 {
3780 XEnv mcEnv(*this);
3781 XEnv scEnv(*this, true);
3782
3783 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3784
3785 std::vector<Account> alt_payees{payees.begin(), payees.end() - 1};
3786 alt_payees.back() = Account("inexistent");
3787
3790 .close()
3792 .close();
3793
3794 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3795 auto const amt = XRP(1000);
3796 std::uint32_t const claimID = 1;
3797 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3798
3799 BalanceTransfer transfer(
3800 scEnv,
3802 scBob,
3803 scAlice,
3804 &payees[0],
3806 withClaim);
3808 scAttester,
3809 jvb,
3810 mcAlice,
3811 amt,
3812 alt_payees,
3813 true,
3814 claimID,
3815 dst,
3816 signers));
3817
3818 if (withClaim)
3819 {
3820 BEAST_EXPECT(transfer.has_not_happened());
3821
3822 // need to submit a claim transactions
3823 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3824 .close();
3825 }
3826
3827 // this also checks that only 3 * split_reward was deducted from
3828 // scAlice (the payor account), since we passed alt_payees to
3829 // BalanceTransfer
3830 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3831 }
3832
3833 for (auto withClaim : {false, true})
3834 {
3835 XEnv mcEnv(*this);
3836 XEnv scEnv(*this, true);
3837
3838 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3839 auto& unpaid = payees[UT_XCHAIN_DEFAULT_QUORUM - 1];
3842 .tx(fset(unpaid, asfDepositAuth))
3843 .close()
3845 .close();
3846
3847 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3848 auto const amt = XRP(1000);
3849 std::uint32_t const claimID = 1;
3850 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3851
3852 // balance of last signer should not change (has deposit auth)
3853 Balance last_signer(scEnv, unpaid);
3854
3855 // make sure all signers except the last one get the
3856 // split_reward
3857
3858 BalanceTransfer transfer(
3859 scEnv,
3861 scBob,
3862 scAlice,
3863 &payees[0],
3865 withClaim);
3867 scAttester,
3868 jvb,
3869 mcAlice,
3870 amt,
3871 payees,
3872 true,
3873 claimID,
3874 dst,
3875 signers));
3876
3877 if (withClaim)
3878 {
3879 BEAST_EXPECT(transfer.has_not_happened());
3880
3881 // need to submit a claim transactions
3882 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3883 .close();
3884 }
3885
3886 // this also checks that only 3 * split_reward was deducted from
3887 // scAlice (the payor account), since we passed payees.size() -
3888 // 1 to BalanceTransfer
3889 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3890
3891 // and make sure the account with deposit auth received nothing
3892 BEAST_EXPECT(last_signer.diff() == STAmount(0));
3893 }
3894
3895 // coverage test: xchain_claim transaction with incorrect flag
3896 XEnv(*this, true)
3898 .close()
3899 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3902 .close();
3903
3904 // coverage test: xchain_claim transaction with xchain feature
3905 // disabled
3906 XEnv(*this, true)
3908 .disableFeature(featureXChainBridge)
3909 .close()
3910 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3912 .close();
3913
3914 // coverage test: XChainClaim::preclaim - isLockingChain = true;
3915 XEnv(*this)
3917 .close()
3918 .tx(xchain_claim(mcAlice, jvb, 1, XRP(1000), mcBob),
3920 }
3921
3922 void
3924 {
3925 using namespace jtx;
3926
3927 testcase("Bridge Create Account");
3928 XRPAmount tx_fee = txFee();
3929
3930 // coverage test: transferHelper() - dst == src
3931 {
3932 XEnv scEnv(*this, true);
3933
3934 auto const amt = XRP(111);
3935 auto const amt_plus_reward = amt + reward;
3936
3939 .close();
3940
3941 Balance door(scEnv, Account::master);
3942
3943 // scEnv.tx(att_create_acct_batch1(1, amt,
3944 // Account::master)).close();
3946 .close();
3947 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
3948 BEAST_EXPECT(
3949 scEnv.claimCount(jvb) == 0); // claim count is one less
3950
3951 // scEnv.tx(att_create_acct_batch2(1, amt,
3952 // Account::master)).close();
3953 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2, 2))
3954 .close();
3955 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
3956 BEAST_EXPECT(
3957 scEnv.claimCount(jvb) == 1); // claim count was incremented
3958
3959 BEAST_EXPECT(door.diff() == -reward);
3960 }
3961
3962 // Check that creating an account with less than the minimum create
3963 // amount fails.
3964 {
3965 XEnv mcEnv(*this);
3966
3967 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3968
3969 Balance door(mcEnv, mcDoor);
3970 Balance carol(mcEnv, mcCarol);
3971
3972 mcEnv
3974 mcCarol, jvb, scuAlice, XRP(19), reward),
3976 .close();
3977
3978 BEAST_EXPECT(door.diff() == STAmount(0));
3979 BEAST_EXPECT(carol.diff() == -tx_fee);
3980 }
3981
3982 // Check that creating an account with invalid flags fails.
3983 {
3984 XEnv mcEnv(*this);
3985
3986 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3987
3988 Balance door(mcEnv, mcDoor);
3989
3990 mcEnv
3992 mcCarol, jvb, scuAlice, XRP(20), reward),
3995 .close();
3996
3997 BEAST_EXPECT(door.diff() == STAmount(0));
3998 }
3999
4000 // Check that creating an account with the XChainBridge feature
4001 // disabled fails.
4002 {
4003 XEnv mcEnv(*this);
4004
4005 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4006
4007 Balance door(mcEnv, mcDoor);
4008
4009 mcEnv.disableFeature(featureXChainBridge)
4011 mcCarol, jvb, scuAlice, XRP(20), reward),
4013 .close();
4014
4015 BEAST_EXPECT(door.diff() == STAmount(0));
4016 }
4017
4018 // Check that creating an account with a negative amount fails
4019 {
4020 XEnv mcEnv(*this);
4021
4022 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4023
4024 Balance door(mcEnv, mcDoor);
4025
4026 mcEnv
4028 mcCarol, jvb, scuAlice, XRP(-20), reward),
4030 .close();
4031
4032 BEAST_EXPECT(door.diff() == STAmount(0));
4033 }
4034
4035 // Check that creating an account with a negative reward fails
4036 {
4037 XEnv mcEnv(*this);
4038
4039 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4040
4041 Balance door(mcEnv, mcDoor);
4042
4043 mcEnv
4045 mcCarol, jvb, scuAlice, XRP(20), XRP(-1)),
4047 .close();
4048
4049 BEAST_EXPECT(door.diff() == STAmount(0));
4050 }
4051
4052 // Check that door account can't lock funds onto itself
4053 {
4054 XEnv mcEnv(*this);
4055
4056 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4057
4058 Balance door(mcEnv, mcDoor);
4059
4060 mcEnv
4062 mcDoor, jvb, scuAlice, XRP(20), XRP(1)),
4064 .close();
4065
4066 BEAST_EXPECT(door.diff() == -tx_fee);
4067 }
4068
4069 // Check that reward matches the amount specified in bridge
4070 {
4071 XEnv mcEnv(*this);
4072
4073 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4074
4075 Balance door(mcEnv, mcDoor);
4076
4077 mcEnv
4079 mcCarol, jvb, scuAlice, XRP(20), XRP(2)),
4081 .close();
4082
4083 BEAST_EXPECT(door.diff() == STAmount(0));
4084 }
4085 }
4086
4087 void
4089 {
4090 using namespace jtx;
4091 XRPAmount res0 = reserve(0);
4092 XRPAmount tx_fee = txFee();
4093
4094 testcase("Fee dips into reserve");
4095
4096 // commit where the fee dips into the reserve, this should succeed
4097 XEnv(*this)
4099 .fund(res0 + one_xrp + tx_fee - drops(1), mcuAlice)
4100 .close()
4102 ter(tesSUCCESS));
4103
4104 // commit where the commit amount drips into the reserve, this should
4105 // fail
4106 XEnv(*this)
4108 .fund(res0 + one_xrp - drops(1), mcuAlice)
4109 .close()
4112
4113 auto const minAccountCreate = XRP(20);
4114
4115 // account create commit where the fee dips into the reserve,
4116 // this should succeed
4117 XEnv(*this)
4118 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4119 .fund(
4120 res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice)
4121 .close()
4123 mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4124 ter(tesSUCCESS));
4125
4126 // account create commit where the commit dips into the reserve,
4127 // this should fail
4128 XEnv(*this)
4129 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4130 .fund(res0 + minAccountCreate + reward - drops(1), mcuAlice)
4131 .close()
4133 mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4135 }
4136
4137 void
4139 {
4140 using namespace jtx;
4141
4142 testcase("Bridge Delete Door Account");
4143
4144 auto const acctDelFee{
4145 drops(XEnv(*this).env_.current()->fees().increment)};
4146
4147 // Deleting an account that owns bridge should fail
4148 {
4149 XEnv mcEnv(*this);
4150
4151 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1))).close();
4152
4153 // We don't allow an account to be deleted if its sequence
4154 // number is within 256 of the current ledger.
4155 for (size_t i = 0; i < 256; ++i)
4156 mcEnv.close();
4157
4158 // try to delete mcDoor, send funds to mcAlice
4159 mcEnv.tx(
4161 fee(acctDelFee),
4163 }
4164
4165 // Deleting an account that owns a claim id should fail
4166 {
4167 XEnv scEnv(*this, true);
4168
4170 .close()
4172 .close();
4173
4174 // We don't allow an account to be deleted if its sequence
4175 // number is within 256 of the current ledger.
4176 for (size_t i = 0; i < 256; ++i)
4177 scEnv.close();
4178
4179 // try to delete scAlice, send funds to scBob
4180 scEnv.tx(
4182 fee(acctDelFee),
4184 }
4185 }
4186
4187 void
4189 {
4190 using namespace jtx;
4191
4192 testcase("Bad attestations");
4193 {
4194 // Create a bridge and add an attestation with a bad public key
4195 XEnv scEnv(*this, true);
4196 std::uint32_t const claimID = 1;
4198 auto const amt = XRP(1000);
4201 .close();
4203 .close();
4204 auto jvAtt = claim_attestation(
4205 scAttester,
4206 jvb,
4207 mcAlice,
4208 amt,
4210 true,
4211 claimID,
4212 dst,
4214 {
4215 // Change to an invalid keytype
4216 auto k = jvAtt["PublicKey"].asString();
4217 k.at(1) = '9';
4218 jvAtt["PublicKey"] = k;
4219 }
4220 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
4221 }
4222 {
4223 // Create a bridge and add an create account attestation with a bad
4224 // public key
4225 XEnv scEnv(*this, true);
4226 std::uint32_t const createCount = 1;
4227 Account dst{scBob};
4228 auto const amt = XRP(1000);
4229 auto const rewardAmt = XRP(1);
4232 .close();
4233 auto jvAtt = create_account_attestation(
4234 scAttester,
4235 jvb,
4236 mcAlice,
4237 amt,
4238 rewardAmt,
4240 true,
4241 createCount,
4242 dst,
4244 {
4245 // Change to an invalid keytype
4246 auto k = jvAtt["PublicKey"].asString();
4247 k.at(1) = '9';
4248 jvAtt["PublicKey"] = k;
4249 }
4250 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
4251 }
4252 }
4253
4254 void
4273};
4274
4275// -----------------------------------------------------------
4276// -----------------------------------------------------------
4279{
4280private:
4281 static constexpr size_t num_signers = 5;
4282
4283 // --------------------------------------------------
4284 enum class WithClaim { no, yes };
4296
4307
4309 using BridgeID = BridgeDef const*;
4310
4311 // tracking chain state
4312 // --------------------
4314 {
4317
4318 void
4319 init(ENV& env, jtx::Account const& acct)
4320 {
4321 startAmount = env.balance(acct);
4323 }
4324
4325 bool
4326 verify(ENV& env, jtx::Account const& acct) const
4327 {
4328 STAmount diff{env.balance(acct) - startAmount};
4329 bool check = diff == expectedDiff;
4330 return check;
4331 }
4332 };
4333
4334 // --------------------------------------------------
4336 {
4340
4342 : env(env), tx_fee(env.env_.current()->fees().base)
4343 {
4344 }
4345
4346 void
4347 sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec& claims)
4348 {
4349 for (auto const& c : claims)
4350 {
4351 env.tx(c).close();
4352 spendFee(bridge->signers[signer_idx].account);
4353 }
4354 claims.clear();
4355 }
4356
4357 uint32_t
4359 size_t signer_idx,
4361 CreateClaimVec& claims)
4362 {
4363 size_t num_successful = 0;
4364 for (auto const& c : claims)
4365 {
4366 env.tx(c).close();
4367 if (env.ter() == tesSUCCESS)
4368 {
4369 counters[bridge].signers.push_back(signer_idx);
4370 num_successful++;
4371 }
4372 spendFee(bridge->signers[signer_idx].account);
4373 }
4374 claims.clear();
4375 return num_successful;
4376 }
4377
4378 void
4380 {
4381 bool callback_called;
4382
4383 // we have this "do {} while" loop because we want to process
4384 // all the account create which can reach quorum at this time
4385 // stamp.
4386 do
4387 {
4388 callback_called = false;
4389 for (size_t i = 0; i < signers_attns.size(); ++i)
4390 {
4391 for (auto& [bridge, claims] : signers_attns[i])
4392 {
4393 sendAttestations(i, bridge, claims.xfer_claims);
4394
4395 auto& c = counters[bridge];
4396 auto& create_claims =
4397 claims.create_claims[c.claim_count];
4398 auto num_attns = create_claims.size();
4399 if (num_attns)
4400 {
4401 c.num_create_attn_sent += sendCreateAttestations(
4402 i, bridge, create_claims);
4403 }
4404 assert(claims.create_claims[c.claim_count].empty());
4405 }
4406 }
4407 for (auto& [bridge, c] : counters)
4408 {
4409 if (c.num_create_attn_sent >= bridge->quorum)
4410 {
4411 callback_called = true;
4412 c.create_callbacks[c.claim_count](c.signers);
4413 ++c.claim_count;
4414 c.num_create_attn_sent = 0;
4415 c.signers.clear();
4416 }
4417 }
4418 } while (callback_called);
4419 }
4420
4421 void
4422 init(jtx::Account const& acct)
4423 {
4424 accounts[acct].init(env, acct);
4425 }
4426
4427 void
4429 jtx::Account const& acct,
4430 STAmount amt,
4431 std::uint64_t divisor = 1)
4432 {
4433 if (amt.issue() != xrpIssue())
4434 return;
4435 auto it = accounts.find(acct);
4436 if (it == accounts.end())
4437 {
4438 accounts[acct].init(env, acct);
4439 // we just looked up the account, so expectedDiff == 0
4440 }
4441 else
4442 {
4443 it->second.expectedDiff +=
4444 (divisor == 1 ? amt
4445 : divide(
4446 amt,
4447 STAmount(amt.issue(), divisor),
4448 amt.issue()));
4449 }
4450 }
4451
4452 void
4453 spend(jtx::Account const& acct, STAmount amt, std::uint64_t times = 1)
4454 {
4455 if (amt.issue() != xrpIssue())
4456 return;
4457 receive(
4458 acct,
4459 times == 1
4460 ? -amt
4461 : -multiply(
4462 amt, STAmount(amt.issue(), times), amt.issue()));
4463 }
4464
4465 void
4466 transfer(jtx::Account const& from, jtx::Account const& to, STAmount amt)
4467 {
4468 spend(from, amt);
4469 receive(to, amt);
4470 }
4471
4472 void
4473 spendFee(jtx::Account const& acct, size_t times = 1)
4474 {
4475 spend(acct, tx_fee, times);
4476 }
4477
4478 bool
4479 verify() const
4480 {
4481 for (auto const& [acct, state] : accounts)
4482 if (!state.verify(env, acct))
4483 return false;
4484 return true;
4485 }
4486
4488 {
4491
4493 uint32_t create_count{0}; // for account create. First should be 1
4495 0}; // for account create. Increments after quorum for
4496 // current create_count (starts at 1) is reached.
4497
4498 uint32_t num_create_attn_sent{0}; // for current claim_count
4501 };
4502
4508
4511
4517 };
4518
4520 {
4521 ChainStateTracker(ENV& a_env, ENV& b_env) : a_(a_env), b_(b_env)
4522 {
4523 }
4524
4525 bool
4526 verify() const
4527 {
4528 return a_.verify() && b_.verify();
4529 }
4530
4531 void
4533 {
4536 }
4537
4538 void
4539 init(jtx::Account const& acct)
4540 {
4541 a_.init(acct);
4542 b_.init(acct);
4543 }
4544
4547 };
4548
4557
4558 enum Act_Flags { af_a2b = 1 << 0 };
4559
4560 // --------------------------------------------------
4561 template <class T>
4563 {
4564 public:
4566 std::shared_ptr<ChainStateTracker> const& chainstate,
4567 BridgeDef const& bridge)
4568 : bridge_(bridge), st_(chainstate)
4569 {
4570 }
4571
4574 {
4575 return static_cast<T&>(*this).a2b() ? st_->a_ : st_->b_;
4576 }
4577
4580 {
4581 return static_cast<T&>(*this).a2b() ? st_->b_ : st_->a_;
4582 }
4583
4584 jtx::Account const&
4586 {
4587 return static_cast<T&>(*this).a2b() ? bridge_.doorA : bridge_.doorB;
4588 }
4589
4590 jtx::Account const&
4592 {
4593 return static_cast<T&>(*this).a2b() ? bridge_.doorB : bridge_.doorA;
4594 }
4595
4596 protected:
4599 };
4600
4601 // --------------------------------------------------
4602 class SmCreateAccount : public SmBase<SmCreateAccount>
4603 {
4604 public:
4606
4608 std::shared_ptr<ChainStateTracker> const& chainstate,
4609 BridgeDef const& bridge,
4611 : Base(chainstate, bridge)
4613 , cr(std::move(create))
4614 {
4615 }
4616
4617 bool
4618 a2b() const
4619 {
4620 return cr.a2b;
4621 }
4622
4623 uint32_t
4625 {
4626 ChainStateTrack& st = srcState();
4627 jtx::Account const& srcdoor = srcDoor();
4628
4629 st.env
4632 .close(); // needed for claim_id sequence to be correct'
4633 st.spendFee(cr.from);
4634 st.transfer(cr.from, srcdoor, cr.amt);
4635 st.transfer(cr.from, srcdoor, cr.reward);
4636
4637 return ++st.counters[&bridge_].create_count;
4638 }
4639
4640 void
4642 {
4643 ChainStateTrack& st = destState();
4644
4645 // check all signers, but start at a random one
4646 size_t i;
4647 for (i = 0; i < num_signers; ++i)
4648 {
4649 size_t signer_idx = (rnd + i) % num_signers;
4650
4651 if (!(cr.attested[signer_idx]))
4652 {
4653 // enqueue one attestation for this signer
4654 cr.attested[signer_idx] = true;
4655
4656 st.signers_attns[signer_idx][&bridge_]
4657 .create_claims[cr.claim_id - 1]
4658 .emplace_back(create_account_attestation(
4659 bridge_.signers[signer_idx].account,
4660 bridge_.jvb,
4661 cr.from,
4662 cr.amt,
4663 cr.reward,
4664 bridge_.signers[signer_idx].account,
4665 cr.a2b,
4666 cr.claim_id,
4667 cr.to,
4668 bridge_.signers[signer_idx]));
4669 break;
4670 }
4671 }
4672
4673 if (i == num_signers)
4674 return; // did not attest
4675
4676 auto& counters = st.counters[&bridge_];
4677 if (counters.create_callbacks.size() < cr.claim_id)
4678 counters.create_callbacks.resize(cr.claim_id);
4679
4680 auto complete_cb = [&](std::vector<size_t> const& signers) {
4681 auto num_attestors = signers.size();
4682 st.env.close();
4683 assert(
4684 num_attestors <=
4685 std::count(cr.attested.begin(), cr.attested.end(), true));
4686 assert(num_attestors >= bridge_.quorum);
4687 assert(cr.claim_id - 1 == counters.claim_count);
4688
4689 auto r = cr.reward;
4690 auto reward = divide(r, STAmount(num_attestors), r.issue());
4691
4692 for (auto i : signers)
4693 st.receive(bridge_.signers[i].account, reward);
4694
4695 st.spend(dstDoor(), reward, num_attestors);
4696 st.transfer(dstDoor(), cr.to, cr.amt);
4697 st.env.env_.memoize(cr.to);
4699 };
4700
4701 counters.create_callbacks[cr.claim_id - 1] = std::move(complete_cb);
4702 }
4703
4704 SmState
4706 {
4707 switch (sm_state)
4708 {
4709 case st_initial:
4712 break;
4713
4714 case st_attesting:
4715 attest(time, rnd);
4716 break;
4717
4718 default:
4719 assert(0);
4720 break;
4721
4722 case st_completed:
4723 break; // will get this once
4724 }
4725 return sm_state;
4726 }
4727
4728 private:
4731 };
4732
4733 // --------------------------------------------------
4734 class SmTransfer : public SmBase<SmTransfer>
4735 {
4736 public:
4738
4740 std::shared_ptr<ChainStateTracker> const& chainstate,
4741 BridgeDef const& bridge,
4742 Transfer xfer)
4743 : Base(chainstate, bridge)
4744 , xfer(std::move(xfer))
4746 {
4747 }
4748
4749 bool
4750 a2b() const
4751 {
4752 return xfer.a2b;
4753 }
4754
4755 uint32_t
4757 {
4758 ChainStateTrack& st = destState();
4759
4760 st.env
4763 .close(); // needed for claim_id sequence to be
4764 // correct'
4765 st.spendFee(xfer.to);
4766 return ++st.counters[&bridge_].claim_id;
4767 }
4768
4769 void
4771 {
4772 ChainStateTrack& st = srcState();
4773 jtx::Account const& srcdoor = srcDoor();
4774
4775 if (xfer.amt.issue() != xrpIssue())
4776 {
4777 st.env.tx(pay(srcdoor, xfer.from, xfer.amt));
4778 st.spendFee(srcdoor);
4779 }
4780 st.env.tx(xchain_commit(
4781 xfer.from,
4782 bridge_.jvb,
4783 xfer.claim_id,
4784 xfer.amt,
4786 ? std::nullopt
4788 st.spendFee(xfer.from);
4789 st.transfer(xfer.from, srcdoor, xfer.amt);
4790 }
4791
4792 void
4794 {
4795 auto r = bridge_.reward;
4796 auto reward = divide(r, STAmount(bridge_.quorum), r.issue());
4797
4798 for (size_t i = 0; i < num_signers; ++i)
4799 {
4800 if (xfer.attested[i])
4801 st.receive(bridge_.signers[i].account, reward);
4802 }
4804 }
4805
4806 bool
4808 {
4809 ChainStateTrack& st = destState();
4810
4811 // check all signers, but start at a random one
4812 for (size_t i = 0; i < num_signers; ++i)
4813 {
4814 size_t signer_idx = (rnd + i) % num_signers;
4815 if (!(xfer.attested[signer_idx]))
4816 {
4817 // enqueue one attestation for this signer
4818 xfer.attested[signer_idx] = true;
4819
4820 st.signers_attns[signer_idx][&bridge_]
4821 .xfer_claims.emplace_back(claim_attestation(
4822 bridge_.signers[signer_idx].account,
4823 bridge_.jvb,
4824 xfer.from,
4825 xfer.amt,
4826 bridge_.signers[signer_idx].account,
4827 xfer.a2b,
4828 xfer.claim_id,
4830 ? std::nullopt
4832 bridge_.signers[signer_idx]));
4833 break;
4834 }
4835 }
4836
4837 // return true if quorum was reached, false otherwise
4838 bool quorum =
4842 {
4845 }
4846 return quorum;
4847 }
4848
4849 void
4851 {
4852 ChainStateTrack& st = destState();
4853 st.env.tx(xchain_claim(
4857 st.spendFee(xfer.to);
4858 }
4859
4860 SmState
4862 {
4863 switch (sm_state)
4864 {
4865 case st_initial:
4868 break;
4869
4870 case st_claimid_created:
4871 commit();
4873 break;
4874
4875 case st_attesting:
4876 sm_state = attest(time, rnd)
4878 : st_completed)
4879 : st_attesting;
4880 break;
4881
4882 case st_attested:
4883 assert(xfer.with_claim == WithClaim::yes);
4884 claim();
4886 break;
4887
4888 default:
4889 case st_completed:
4890 assert(0); // should have been removed
4891 break;
4892 }
4893 return sm_state;
4894 }
4895
4896 private:
4899 };
4900
4901 // --------------------------------------------------
4904
4906
4907 void
4909 uint64_t time,
4910 std::shared_ptr<ChainStateTracker> const& chainstate,
4911 BridgeDef const& bridge,
4912 Transfer transfer)
4913 {
4915 time, SmTransfer(chainstate, bridge, std::move(transfer)));
4916 }
4917
4918 void
4920 std::shared_ptr<ChainStateTracker> const& chainstate,
4921 BridgeDef const& bridge,
4923 {
4925 time, SmCreateAccount(chainstate, bridge, std::move(ac)));
4926 }
4927
4928public:
4929 void
4932 bool verify_balances = true)
4933 {
4934 using namespace jtx;
4935 uint64_t time = 0;
4936 std::mt19937 gen(27); // Standard mersenne_twister_engine
4938
4939 while (!sm_.empty())
4940 {
4941 ++time;
4942 for (auto it = sm_.begin(); it != sm_.end();)
4943 {
4944 auto vis = [&](auto& sm) {
4945 uint32_t rnd = distrib(gen);
4946 return sm.advance(time, rnd);
4947 };
4948 auto& [t, sm] = *it;
4949 if (t <= time && std::visit(vis, sm) == st_completed)
4950 it = sm_.erase(it);
4951 else
4952 ++it;
4953 }
4954
4955 // send attestations
4956 st->sendAttestations();
4957
4958 // make sure all transactions have been applied
4959 st->a_.env.close();
4960 st->b_.env.close();
4961
4962 if (verify_balances)
4963 {
4964 BEAST_EXPECT(st->verify());
4965 }
4966 }
4967 }
4968
4969 void
4971 {
4972 using namespace jtx;
4973
4974 testcase("Bridge usage simulation");
4975
4976 XEnv mcEnv(*this);
4977 XEnv scEnv(*this, true);
4978
4979 auto st = std::make_shared<ChainStateTracker>(mcEnv, scEnv);
4980
4981 // create 10 accounts + door funded on both chains, and store
4982 // in ChainStateTracker the initial amount of these accounts
4983 Account doorXRPLocking("doorXRPLocking"),
4984 doorUSDLocking("doorUSDLocking"), doorUSDIssuing("doorUSDIssuing");
4985
4986 constexpr size_t num_acct = 10;
4987 auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() {
4988 using namespace std::literals;
4989 std::vector<Account> result;
4990 result.reserve(num_acct);
4991 for (int i = 0; i < num_acct; ++i)
4992 result.emplace_back(
4993 "a"s + std::to_string(i),
4995 result.emplace_back("doorXRPLocking");
4996 doorXRPLocking = result.back();
4997 result.emplace_back("doorUSDLocking");
4998 doorUSDLocking = result.back();
4999 result.emplace_back("doorUSDIssuing");
5000 doorUSDIssuing = result.back();
5001 return result;
5002 }();
5003
5004 for (auto& acct : a)
5005 {
5006 STAmount amt{XRP(100000)};
5007
5008 mcEnv.fund(amt, acct);
5009 scEnv.fund(amt, acct);
5010 }
5011 Account USDLocking{"USDLocking"};
5012 IOU usdLocking{USDLocking["USD"]};
5013 IOU usdIssuing{doorUSDIssuing["USD"]};
5014
5015 mcEnv.fund(XRP(100000), USDLocking);
5016 mcEnv.close();
5017 mcEnv.tx(trust(doorUSDLocking, usdLocking(100000)));
5018 mcEnv.close();
5019 mcEnv.tx(pay(USDLocking, doorUSDLocking, usdLocking(50000)));
5020
5021 for (int i = 0; i < a.size(); ++i)
5022 {
5023 auto& acct{a[i]};
5024 if (i < num_acct)
5025 {
5026 mcEnv.tx(trust(acct, usdLocking(100000)));
5027 scEnv.tx(trust(acct, usdIssuing(100000)));
5028 }
5029 st->init(acct);
5030 }
5031 for (auto& s : signers)
5032 st->init(s.account);
5033
5034 st->b_.init(Account::master);
5035
5036 // also create some unfunded accounts
5037 constexpr size_t num_ua = 20;
5038 auto ua = []() {
5039 using namespace std::literals;
5040 std::vector<Account> result;
5041 result.reserve(num_ua);
5042 for (int i = 0; i < num_ua; ++i)
5043 result.emplace_back(
5044 "ua"s + std::to_string(i),
5046 return result;
5047 }();
5048
5049 // initialize a bridge from a BridgeDef
5050 auto initBridge = [&mcEnv, &scEnv, &st](BridgeDef& bd) {
5051 bd.initBridge(mcEnv, scEnv);
5052 st->a_.spendFee(bd.doorA, 2);
5053 st->b_.spendFee(bd.doorB, 2);
5054 };
5055
5056 // create XRP -> XRP bridge
5057 // ------------------------
5058 BridgeDef xrp_b{
5059 doorXRPLocking,
5060 xrpIssue(),
5062 xrpIssue(),
5063 XRP(1),
5064 XRP(20),
5065 quorum,
5066 signers,
5068
5069 initBridge(xrp_b);
5070
5071 // create USD -> USD bridge
5072 // ------------------------
5073 BridgeDef usd_b{
5074 doorUSDLocking,
5075 usdLocking,
5076 doorUSDIssuing,
5077 usdIssuing,
5078 XRP(1),
5079 XRP(20),
5080 quorum,
5081 signers,
5083
5084 initBridge(usd_b);
5085
5086 // try a single account create + transfer to validate the simulation
5087 // engine. Do the transfer 8 time steps after the account create, to
5088 // give time enough for ua[0] to be funded now so it can reserve
5089 // the claimID
5090 // -----------------------------------------------------------------
5091 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, true});
5092 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), true});
5093 runSimulation(st);
5094
5095 // try the same thing in the other direction
5096 // -----------------------------------------
5097 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, false});
5098 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), false});
5099 runSimulation(st);
5100
5101 // run multiple XRP transfers
5102 // --------------------------
5103 xfer(0, st, xrp_b, {a[0], a[0], a[1], XRP(6), true, WithClaim::no});
5104 xfer(1, st, xrp_b, {a[0], a[0], a[1], XRP(8), false, WithClaim::no});
5105 xfer(1, st, xrp_b, {a[1], a[1], a[1], XRP(1), true});
5106 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(3), false});
5107 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(5), false});
5108 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(7), false, WithClaim::no});
5109 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(9), true});
5110 runSimulation(st);
5111
5112 // run one USD transfer
5113 // --------------------
5114 xfer(0, st, usd_b, {a[0], a[1], a[2], usdLocking(3), true});
5115 runSimulation(st);
5116
5117 // run multiple USD transfers
5118 // --------------------------
5119 xfer(0, st, usd_b, {a[0], a[0], a[1], usdLocking(6), true});
5120 xfer(1, st, usd_b, {a[0], a[0], a[1], usdIssuing(8), false});
5121 xfer(1, st, usd_b, {a[1], a[1], a[1], usdLocking(1), true});
5122 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(3), false});
5123 xfer(2, st, usd_b, {a[1], a[1], a[1], usdIssuing(5), false});
5124 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(7), false});
5125 xfer(2, st, usd_b, {a[1], a[1], a[1], usdLocking(9), true});
5126 runSimulation(st);
5127
5128 // run mixed transfers
5129 // -------------------
5130 xfer(0, st, xrp_b, {a[0], a[0], a[0], XRP(1), true});
5131 xfer(0, st, usd_b, {a[1], a[3], a[3], usdIssuing(3), false});
5132 xfer(0, st, usd_b, {a[3], a[2], a[1], usdIssuing(5), false});
5133
5134 xfer(1, st, xrp_b, {a[0], a[0], a[0], XRP(4), false});
5135 xfer(1, st, xrp_b, {a[1], a[1], a[0], XRP(8), true});
5136 xfer(1, st, usd_b, {a[4], a[1], a[1], usdLocking(7), true});
5137
5138 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(7), true});
5139 xfer(3, st, xrp_b, {a[0], a[4], a[3], XRP(2), false});
5140 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(9), true});
5141 xfer(3, st, usd_b, {a[3], a[1], a[1], usdIssuing(11), false});
5142 runSimulation(st);
5143
5144 // run multiple account create to stress attestation batching
5145 // ----------------------------------------------------------
5146 ac(0, st, xrp_b, {a[0], ua[1], XRP(301), xrp_b.reward, true});
5147 ac(0, st, xrp_b, {a[1], ua[2], XRP(302), xrp_b.reward, true});
5148 ac(1, st, xrp_b, {a[0], ua[3], XRP(303), xrp_b.reward, true});
5149 ac(2, st, xrp_b, {a[1], ua[4], XRP(304), xrp_b.reward, true});
5150 ac(3, st, xrp_b, {a[0], ua[5], XRP(305), xrp_b.reward, true});
5151 ac(4, st, xrp_b, {a[1], ua[6], XRP(306), xrp_b.reward, true});
5152 ac(6, st, xrp_b, {a[0], ua[7], XRP(307), xrp_b.reward, true});
5153 ac(7, st, xrp_b, {a[2], ua[8], XRP(308), xrp_b.reward, true});
5154 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5155 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5156 ac(10, st, xrp_b, {a[0], ua[10], XRP(310), xrp_b.reward, true});
5157 ac(12, st, xrp_b, {a[0], ua[11], XRP(311), xrp_b.reward, true});
5158 ac(12, st, xrp_b, {a[3], ua[12], XRP(312), xrp_b.reward, true});
5159 ac(12, st, xrp_b, {a[4], ua[13], XRP(313), xrp_b.reward, true});
5160 ac(12, st, xrp_b, {a[3], ua[14], XRP(314), xrp_b.reward, true});
5161 ac(12, st, xrp_b, {a[6], ua[15], XRP(315), xrp_b.reward, true});
5162 ac(13, st, xrp_b, {a[7], ua[16], XRP(316), xrp_b.reward, true});
5163 ac(15, st, xrp_b, {a[3], ua[17], XRP(317), xrp_b.reward, true});
5164 runSimulation(st, true); // balances verification working now.
5165 }
5166
5167 void
5168 run() override
5169 {
5171 }
5172};
5173
5174BEAST_DEFINE_TESTSUITE(XChain, app, ripple);
5175BEAST_DEFINE_TESTSUITE(XChainSim, app, ripple);
5176
5177} // namespace ripple::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:52
void pass()
Record a successful test condition.
Definition suite.h:508
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
virtual Config & config()=0
std::unordered_set< uint256, beast::uhash<> > features
Definition Config.h:257
A currency issued by an account.
Definition Issue.h:14
Issue const & issue() const
Definition STAmount.h:477
static constexpr TERSubset fromInt(int from)
Definition TER.h:414
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)
SmState advance(uint64_t time, uint32_t rnd)
void attest(uint64_t time, uint32_t rnd)
void distribute_reward(ChainStateTrack &st)
SmTransfer(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer xfer)
SmState advance(uint64_t time, uint32_t rnd)
bool attest(uint64_t time, uint32_t rnd)
Immutable cryptographic account descriptor.
Definition Account.h:20
static Account const master
The master account.
Definition Account.h:29
A transaction testing environment.
Definition Env.h:102
TER ter() const
Return the TER for the last JTx.
Definition Env.h:579
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:312
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:103
void enableFeature(uint256 const feature)
Definition Env.cpp:656
Application & app()
Definition Env.h:242
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:271
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:165
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:138
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:259
Converts to IOU Issue or STAmount.
Set the fee on a JTx.
Definition fee.h:18
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:16
Set the flags on a JTx.
Definition txflags.h:12
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:13
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:467
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:454
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:481
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)
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM
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)
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 bridge_create(Account const &acc, Json::Value const &bridge, STAmount const &reward, std::optional< STAmount > const &minAccountCreate)
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)
Json::Value bridge(Account const &lockingChainDoor, Issue const &lockingChainIssue, Account const &issuingChainDoor, Issue const &issuingChainIssue)
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
std::vector< Json::Value > JValueVec
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 sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
Json::Value bridge_modify(Account const &acc, Json::Value const &bridge, std::optional< STAmount > const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Json::Value xchain_commit(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, std::optional< Account > const &dst)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:96
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:74
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:58
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
constexpr std::uint32_t tfFillOrKill
Definition TxFlags.h:81
@ current
This was a new validation and was added.
constexpr std::uint32_t asfDisableMaster
Definition TxFlags.h:61
std::string transToken(TER code)
Definition TER.cpp:245
constexpr std::uint32_t tfClearAccountCreateAmount
Definition TxFlags.h:247
@ tecNO_ENTRY
Definition TER.h:288
@ tecNO_DST
Definition TER.h:272
@ tecXCHAIN_SELF_COMMIT
Definition TER.h:332
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition TER.h:328
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
Definition TER.h:334
@ tecNO_ISSUER
Definition TER.h:281
@ tecXCHAIN_NO_CLAIM_ID
Definition TER.h:319
@ tecXCHAIN_CLAIM_NO_QUORUM
Definition TER.h:321
@ tecXCHAIN_BAD_CLAIM_ID
Definition TER.h:320
@ tecDUPLICATE
Definition TER.h:297
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition TER.h:329
@ tecNO_PERMISSION
Definition TER.h:287
@ tecDST_TAG_NEEDED
Definition TER.h:291
@ tecHAS_OBLIGATIONS
Definition TER.h:299
@ tecXCHAIN_REWARD_MISMATCH
Definition TER.h:325
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
Definition TER.h:333
@ tecUNFUNDED_PAYMENT
Definition TER.h:267
@ tecXCHAIN_NO_SIGNERS_LIST
Definition TER.h:326
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tesSUCCESS
Definition TER.h:226
@ terNO_LINE
Definition TER.h:200
@ temBAD_ISSUER
Definition TER.h:74
@ temBAD_AMOUNT
Definition TER.h:70
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
Definition TER.h:116
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
Definition TER.h:117
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition TER.h:112
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition TER.h:115
@ temMALFORMED
Definition TER.h:68
@ temINVALID_FLAG
Definition TER.h:92
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition TER.h:114
@ temDISABLED
Definition TER.h:95
STL namespace.
T reserve(T... args)
T size(T... args)
bool payees_received(STAmount const &reward) const
std::vector< balance > reward_accounts
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payor, jtx::Account const *payees, size_t num_payees, bool withClaim)
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 &payor, std::vector< jtx::Account > const &payees, bool withClaim)
bool has_happened(STAmount const &amt, STAmount const &reward, bool check_payer=true)
Balance(T &env, jtx::Account const &account)
jtx::Account const & account_
STAmount diff() const
void initBridge(ENV &mcEnv, ENV &scEnv)
std::vector< jtx::signer > const & signers
std::shared_ptr< SLE const > bridge(Json::Value const &jvb)
SEnv(T &s, std::unique_ptr< Config > config, FeatureBitset features, std::unique_ptr< Logs > logs=nullptr, beast::severities::Severity thresh=beast::severities::kError)
SEnv & enableFeature(uint256 const feature)
std::uint64_t claimCount(Json::Value const &jvb)
std::shared_ptr< SLE const > account(jtx::Account const &account)
SEnv & multiTx(jtx::JValueVec &&jvv, FN const &... fN)
std::shared_ptr< SLE const > caClaimID(Json::Value const &jvb, std::uint64_t seq)
std::uint64_t claimID(Json::Value const &jvb)
std::shared_ptr< SLE const > claimID(Json::Value const &jvb, std::uint64_t seq)
SEnv & tx(JsonValue &&jv, FN const &... fN)
XRPAmount reserve(std::uint32_t count)
STAmount balance(jtx::Account const &account) const
SEnv & disableFeature(uint256 const feature)
STAmount balance(jtx::Account const &account, Issue const &issue) const
SEnv & fund(STAmount const &amount, Arg const &arg, Args const &... args)
std::array< bool, num_signers > attested
void init(ENV &env, jtx::Account const &acct)
bool verify(ENV &env, jtx::Account const &acct) const
void spend(jtx::Account const &acct, STAmount amt, std::uint64_t times=1)
void transfer(jtx::Account const &from, jtx::Account const &to, STAmount amt)
uint32_t sendCreateAttestations(size_t signer_idx, BridgeID bridge, CreateClaimVec &claims)
void spendFee(jtx::Account const &acct, size_t times=1)
std::map< BridgeID, BridgeCounters > counters
void sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec &claims)
void receive(jtx::Account const &acct, STAmount amt, std::uint64_t divisor=1)
std::map< jtx::Account, AccountStateTrack > accounts
std::array< bool, num_signers > attested
void ac(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, AccountCreate ac)
void xfer(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer transfer)
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.
void testXChainAddAccountCreateNonBatchAttestation()
XRPAmount reserve(std::uint32_t count)
XEnv(T &s, bool side=false)
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)
std::vector< signer > const alt_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< Account > const payees
std::vector< signer > const signers
Set the sequence number on a JTx.
Definition seq.h:15
A signer in a SignerList.
Definition multisign.h:20
T tmpnam(T... args)
T to_string(T... args)
T visit(T... args)