rippled
Loading...
Searching...
No Matches
XChain_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/Env.h>
3#include <test/jtx/attester.h>
4#include <test/jtx/multisign.h>
5#include <test/jtx/xchain_bridge.h>
6
7#include <xrpl/beast/unit_test/suite.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/Indexes.h>
10#include <xrpl/protocol/Issue.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STXChainBridge.h>
13#include <xrpl/protocol/Serializer.h>
14#include <xrpl/protocol/TER.h>
15#include <xrpl/protocol/XChainAttestations.h>
16
17#include <functional>
18#include <limits>
19#include <optional>
20#include <random>
21#include <string>
22#include <tuple>
23#include <variant>
24#include <vector>
25
26namespace xrpl::test {
27
28// SEnv class - encapsulate jtx::Env to make it more user-friendly,
29// for example having APIs that return a *this reference so that calls can be
30// chained (fluent interface) allowing to create an environment and use it
31// without encapsulating it in a curly brace block.
32// ---------------------------------------------------------------------------
33template <class T>
34struct SEnv
35{
37
39 T& s,
41 FeatureBitset features,
42 std::unique_ptr<Logs> logs = nullptr,
44 : env_(s, std::move(config), features, std::move(logs), thresh)
45 {
46 }
47
48 SEnv&
50 {
51 env_.close();
52 return *this;
53 }
54
55 SEnv&
56 enableFeature(uint256 const feature)
57 {
58 env_.enableFeature(feature);
59 return *this;
60 }
61
62 SEnv&
63 disableFeature(uint256 const feature)
64 {
65 env_.app().config().features.erase(feature);
66 return *this;
67 }
68
69 template <class Arg, class... Args>
70 SEnv&
71 fund(STAmount const& amount, Arg const& arg, Args const&... args)
72 {
73 env_.fund(amount, arg, args...);
74 return *this;
75 }
76
77 template <class JsonValue, class... FN>
78 SEnv&
79 tx(JsonValue&& jv, FN const&... fN)
80 {
81 env_(std::forward<JsonValue>(jv), fN...);
82 return *this;
83 }
84
85 template <class... FN>
86 SEnv&
87 multiTx(jtx::JValueVec&& 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
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
126 {
127 return env_.le(account);
128 }
129
131 bridge(Json::Value const& jvb)
132 {
133 STXChainBridge b(jvb);
134
136 if (auto r = env_.le(keylet::bridge(b, ct)))
137 {
138 if ((*r)[sfXChainBridge] == b)
139 return r;
140 }
141 return nullptr;
142 };
143 if (auto r = tryGet(STXChainBridge::ChainType::locking))
144 return r;
146 }
147
150 {
151 return (*bridge(jvb))[sfXChainAccountClaimCount];
152 }
153
156 {
157 return (*bridge(jvb))[sfXChainClaimID];
158 }
159
162 {
164 }
165
171};
172
173// XEnv class used for XChain tests. The only difference with SEnv<T> is that it
174// funds some default accounts, and that it enables `testable_amendments() |
175// FeatureBitset{featureXChainBridge}` by default.
176// -----------------------------------------------------------------------------
177template <class T>
178struct XEnv : public jtx::XChainBridgeObjects, public SEnv<T>
179{
180 XEnv(T& s, bool side = false) : SEnv<T>(s, jtx::envconfig(), features)
181 {
182 using namespace jtx;
183 STAmount xrp_funds{XRP(10000)};
184
185 if (!side)
186 {
187 this->fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
188
189 // Signer's list must match the attestation signers
190 // env_(jtx::signers(mcDoor, quorum, signers));
191 for (auto& s : signers)
192 this->fund(xrp_funds, s.account);
193 }
194 else
195 {
196 this->fund(xrp_funds, scDoor, scAlice, scBob, scCarol, scGw, scAttester, scReward);
197
198 for (auto& ra : payees)
199 this->fund(xrp_funds, ra);
200
201 for (auto& s : signers)
202 this->fund(xrp_funds, s.account);
203
204 // Signer's list must match the attestation signers
205 // env_(jtx::signers(Account::master, quorum, signers));
206 }
207 this->close();
208 }
209};
210
211// Tracks the xrp balance for one account
212template <class T>
214{
218
219 Balance(T& env, jtx::Account const& account) : account_(account), env_(env)
220 {
221 startAmount = env_.balance(account_);
222 }
223
225 diff() const
226 {
227 return env_.balance(account_) - startAmount;
228 }
229};
230
231// Tracks the xrp balance for multiple accounts involved in a crosss-chain
232// transfer
233template <class T>
235{
237
240 balance payer_; // pays the rewards
241 std::vector<balance> reward_accounts; // receives the reward
243
245 T& env,
246 jtx::Account const& from_acct,
247 jtx::Account const& to_acct,
248 jtx::Account const& payer,
249 jtx::Account const* payees,
250 size_t num_payees,
251 bool withClaim)
252 : from_(env, from_acct)
253 , to_(env, to_acct)
254 , payer_(env, payer)
255 , reward_accounts([&]() {
257 r.reserve(num_payees);
258 for (size_t i = 0; i < num_payees; ++i)
259 r.emplace_back(env, payees[i]);
260 return r;
261 }())
262 , txFees_(withClaim ? env.env_.current()->fees().base : XRPAmount(0))
263 {
264 }
265
267 T& env,
268 jtx::Account const& from_acct,
269 jtx::Account const& to_acct,
270 jtx::Account const& payer,
271 std::vector<jtx::Account> const& payees,
272 bool withClaim)
273 : BalanceTransfer(env, from_acct, to_acct, payer, &payees[0], payees.size(), withClaim)
274 {
275 }
276
277 bool
278 payees_received(STAmount const& reward) const
279 {
280 return std::all_of(
281 reward_accounts.begin(), reward_accounts.end(), [&](balance const& b) { return b.diff() == reward; });
282 }
283
284 bool
285 check_most_balances(STAmount const& amt, STAmount const& reward)
286 {
287 return from_.diff() == -amt && to_.diff() == amt && payees_received(reward);
288 }
289
290 bool
291 has_happened(STAmount const& amt, STAmount const& reward, bool check_payer = true)
292 {
293 auto reward_cost = multiply(reward, STAmount(reward_accounts.size()), reward.issue());
294 return check_most_balances(amt, reward) && (!check_payer || payer_.diff() == -(reward_cost + txFees_));
295 }
296
297 bool
299 {
300 return check_most_balances(STAmount(0), STAmount(0)) &&
301 payer_.diff() <= txFees_; // could have paid fee for failed claim
302 }
303};
304
306{
313 uint32_t quorum;
316
317 template <class ENV>
318 void
319 initBridge(ENV& mcEnv, ENV& scEnv)
320 {
322
323 auto const optAccountCreate = [&]() -> std::optional<STAmount> {
324 if (issueA != xrpIssue() || issueB != xrpIssue())
325 return {};
326 return minAccountCreate;
327 }();
328 mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate)).tx(jtx::signers(doorA, quorum, signers)).close();
329
330 scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate)).tx(jtx::signers(doorB, quorum, signers)).close();
331 }
332};
333
335{
338 {
339 return XEnv(*this).env_.current()->fees().accountReserve(count);
340 }
341
344 {
345 return XEnv(*this).env_.current()->fees().base;
346 }
347
348 void
350 {
351 auto jBridge = create_bridge(mcDoor)[sfXChainBridge.jsonName];
352 bool exceptionPresent = false;
353 try
354 {
355 exceptionPresent = false;
356 [[maybe_unused]] STXChainBridge testBridge1(jBridge);
357 }
358 catch (std::exception& ec)
359 {
360 exceptionPresent = true;
361 }
362
363 BEAST_EXPECT(!exceptionPresent);
364
365 try
366 {
367 exceptionPresent = false;
368 jBridge["Extra"] = 1;
369 [[maybe_unused]] STXChainBridge testBridge2(jBridge);
370 }
371 catch ([[maybe_unused]] std::exception& ec)
372 {
373 exceptionPresent = true;
374 }
375
376 BEAST_EXPECT(exceptionPresent);
377 }
378
379 void
381 {
382 XRPAmount res1 = reserve(1);
383
384 using namespace jtx;
385 testcase("Create Bridge");
386
387 // Normal create_bridge => should succeed
388 XEnv(*this).tx(create_bridge(mcDoor)).close();
389
390 // Bridge not owned by one of the door account.
392
393 // Create twice on the same account
395
396 // Create USD bridge Alice -> Bob ... should succeed
397 XEnv(*this).tx(create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])), ter(tesSUCCESS));
398
399 // Create USD bridge, Alice is both the locking door and locking issue,
400 // ... should fail.
401 XEnv(*this).tx(
404
405 // Bridge where the two door accounts are equal.
406 XEnv(*this).tx(
408
409 // Both door accounts are on the same chain. This is not allowed.
410 // Although it doesn't violate any invariants, it's not a useful thing
411 // to do and it complicates the "add claim" transactions.
412 XEnv(*this)
413 .tx(create_bridge(mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])))
414 .close()
416 .close();
417
418 // Create a bridge on an account with exactly enough balance to
419 // meet the new reserve should succeed
420 XEnv(*this)
421 .fund(res1, mcuDoor) // exact reserve for account + 1 object
422 .close()
424
425 // Create a bridge on an account with no enough balance to meet the
426 // new reserve
427 XEnv(*this)
428 .fund(res1 - 1, mcuDoor) // just short of required reserve
429 .close()
431
432 // Reward amount is non-xrp
434
435 // Reward amount is XRP and negative
437
438 // Reward amount is 1 xrp => should succeed
440
441 // Min create amount is 1 xrp, mincreate is 1 xrp => should succeed
442 XEnv(*this).tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)), ter(tesSUCCESS));
443
444 // Min create amount is non-xrp
445 XEnv(*this).tx(
447
448 // Min create amount is zero (should fail, currently succeeds)
450
451 // Min create amount is negative
452 XEnv(*this).tx(
454
455 // coverage test: BridgeCreate::preflight() - create bridge when feature
456 // disabled.
457 {
458 Env env(*this, testable_amendments() - featureXChainBridge);
460 }
461
462 // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER.
463 XEnv(*this).tx(
465
466 // coverage test: create_bridge transaction with incorrect flag
468
469 // coverage test: create_bridge transaction with xchain feature disabled
470 XEnv(*this).disableFeature(featureXChainBridge).tx(create_bridge(mcAlice, jvb), ter(temDISABLED));
471 }
472
473 void
475 {
521 using namespace jtx;
522 testcase("Bridge create constraints");
523 XEnv env(*this, true);
524 auto& A = scAlice;
525 auto& B = scBob;
526 auto& C = scCarol;
527 auto AUSD = A["USD"];
528 auto BUSD = B["USD"];
529 auto CUSD = C["USD"];
530 auto GUSD = scGw["USD"];
531 auto AEUR = A["EUR"];
532 auto BEUR = B["EUR"];
533 auto CEUR = C["EUR"];
534 auto GEUR = scGw["EUR"];
535
536 // Accounts to own single bridges
537 Account const a1("a1");
538 Account const a2("a2");
539 Account const a3("a3");
540 Account const a4("a4");
541 Account const a5("a5");
542 Account const a6("a6");
543
544 env.fund(XRP(10000), a1, a2, a3, a4, a5, a6);
545 env.close();
546
547 // Add a bridge on two different accounts with the same locking and
548 // issuing assets
549 env.tx(create_bridge(a1, bridge(a1, GUSD, B, BUSD))).close();
550 env.tx(create_bridge(a2, bridge(a2, GUSD, B, BUSD))).close();
551
552 // Add the exact same bridge to two different accounts (one locking
553 // account and one issuing)
554 env.tx(create_bridge(a3, bridge(a3, GUSD, a4, a4["USD"]))).close();
555 env.tx(create_bridge(a4, bridge(a3, GUSD, a4, a4["USD"])), ter(tecDUPLICATE)).close();
556
557 // Add the exact same bridge to two different accounts (one issuing
558 // account and one locking - opposite order from the test above)
559 env.tx(create_bridge(a5, bridge(a6, GUSD, a5, a5["USD"]))).close();
560 env.tx(create_bridge(a6, bridge(a6, GUSD, a5, a5["USD"])), ter(tecDUPLICATE)).close();
561
562 // Test case 1 ~ 5, create bridges
563 auto const goodBridge1 = bridge(A, GUSD, B, BUSD);
564 auto const goodBridge2 = bridge(A, BUSD, C, CUSD);
565 env.tx(create_bridge(B, goodBridge1)).close();
566 // Issuing asset is the same, this is a duplicate
567 env.tx(create_bridge(B, bridge(A, GEUR, B, BUSD)), ter(tecDUPLICATE)).close();
568 env.tx(create_bridge(A, goodBridge2), ter(tesSUCCESS)).close();
569 // Locking asset is the same - this is a duplicate
570 env.tx(create_bridge(A, bridge(A, BUSD, B, BEUR)), ter(tecDUPLICATE)).close();
571 // Locking asset is USD - this is a duplicate even tho it has a
572 // different issuer
573 env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE)).close();
574
575 // Test case 6 and 7, commits
576 env.tx(trust(C, BUSD(1000))).tx(trust(A, BUSD(1000))).close().tx(pay(B, C, BUSD(1000))).close();
577 auto const aBalanceStart = env.balance(A, BUSD);
578 auto const cBalanceStart = env.balance(C, BUSD);
579 env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close();
580 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(0));
581 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50));
582 env.tx(xchain_commit(C, goodBridge2, 1, BUSD(60))).close();
583 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(60));
584 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50 - 60));
585
586 // bridge modify test cases
587 env.tx(bridge_modify(B, goodBridge1, XRP(33), std::nullopt)).close();
588 BEAST_EXPECT((*env.bridge(goodBridge1))[sfSignatureReward] == XRP(33));
589 env.tx(bridge_modify(A, goodBridge2, XRP(44), std::nullopt)).close();
590 BEAST_EXPECT((*env.bridge(goodBridge2))[sfSignatureReward] == XRP(44));
591 }
592
593 void
595 {
596 using namespace jtx;
597 testcase("Create Bridge Matrix");
598
599 // Test all combinations of the following:`
600 // --------------------------------------
601 // - Locking chain is IOU with locking chain door account as issuer
602 // - Locking chain is IOU with issuing chain door account that
603 // exists on the locking chain as issuer
604 // - Locking chain is IOU with issuing chain door account that does
605 // not exists on the locking chain as issuer
606 // - Locking chain is IOU with non-door account (that exists on the
607 // locking chain ledger) as issuer
608 // - Locking chain is IOU with non-door account (that does not exist
609 // exists on the locking chain ledger) as issuer
610 // - Locking chain is XRP
611 // ---------------------------------------------------------------------
612 // - Issuing chain is IOU with issuing chain door account as the
613 // issuer
614 // - Issuing chain is IOU with locking chain door account (that
615 // exists on the issuing chain ledger) as the issuer
616 // - Issuing chain is IOU with locking chain door account (that does
617 // not exist on the issuing chain ledger) as the issuer
618 // - Issuing chain is IOU with non-door account (that exists on the
619 // issuing chain ledger) as the issuer
620 // - Issuing chain is IOU with non-door account (that does not
621 // exists on the issuing chain ledger) as the issuer
622 // - Issuing chain is XRP and issuing chain door account is not the
623 // root account
624 // - Issuing chain is XRP and issuing chain door account is the root
625 // account
626 // ---------------------------------------------------------------------
627 // That's 42 combinations. The only combinations that should succeed
628 // are:
629 // - Locking chain is any IOU,
630 // - Issuing chain is IOU with issuing chain door account as the
631 // issuer
632 // Locking chain is XRP,
633 // - Issuing chain is XRP with issuing chain is the root account.
634 // ---------------------------------------------------------------------
635 Account a("a"), b("b");
636 Issue ia, ib;
637
638 std::tuple lcs{
640 "Locking chain is IOU(locking chain door)",
641 [&](auto& env, bool) {
642 a = mcDoor;
643 ia = mcDoor["USD"];
644 }),
646 "Locking chain is IOU(issuing chain door funded on locking "
647 "chain)",
648 [&](auto& env, bool shouldFund) {
649 a = mcDoor;
650 ia = scDoor["USD"];
651 if (shouldFund)
652 env.fund(XRP(10000), scDoor);
653 }),
655 "Locking chain is IOU(issuing chain door account unfunded "
656 "on locking chain)",
657 [&](auto& env, bool) {
658 a = mcDoor;
659 ia = scDoor["USD"];
660 }),
662 "Locking chain is IOU(bob funded on locking chain)",
663 [&](auto& env, bool) {
664 a = mcDoor;
665 ia = mcGw["USD"];
666 }),
668 "Locking chain is IOU(bob unfunded on locking chain)",
669 [&](auto& env, bool) {
670 a = mcDoor;
671 ia = mcuGw["USD"];
672 }),
673 std::make_pair("Locking chain is XRP", [&](auto& env, bool) {
674 a = mcDoor;
675 ia = xrpIssue();
676 })};
677
678 std::tuple ics{
680 "Issuing chain is IOU(issuing chain door account)",
681 [&](auto& env, bool) {
682 b = scDoor;
683 ib = scDoor["USD"];
684 }),
686 "Issuing chain is IOU(locking chain door funded on issuing "
687 "chain)",
688 [&](auto& env, bool shouldFund) {
689 b = scDoor;
690 ib = mcDoor["USD"];
691 if (shouldFund)
692 env.fund(XRP(10000), mcDoor);
693 }),
695 "Issuing chain is IOU(locking chain door unfunded on "
696 "issuing chain)",
697 [&](auto& env, bool) {
698 b = scDoor;
699 ib = mcDoor["USD"];
700 }),
702 "Issuing chain is IOU(bob funded on issuing chain)",
703 [&](auto& env, bool) {
704 b = scDoor;
705 ib = mcGw["USD"];
706 }),
708 "Issuing chain is IOU(bob unfunded on issuing chain)",
709 [&](auto& env, bool) {
710 b = scDoor;
711 ib = mcuGw["USD"];
712 }),
714 "Issuing chain is XRP and issuing chain door account is "
715 "not the root account",
716 [&](auto& env, bool) {
717 b = scDoor;
718 ib = xrpIssue();
719 }),
721 "Issuing chain is XRP and issuing chain door account is "
722 "the root account ",
723 [&](auto& env, bool) {
724 b = Account::master;
725 ib = xrpIssue();
726 })};
727
728 std::vector<std::pair<int, int>> expected_result{
771
773
774 auto testcase = [&](auto const& lc, auto const& ic) {
775 XEnv mcEnv(*this);
776 XEnv scEnv(*this, true);
777
778 lc.second(mcEnv, true);
779 lc.second(scEnv, false);
780
781 ic.second(mcEnv, false);
782 ic.second(scEnv, true);
783
784 auto const& expected = expected_result[test_result.size()];
785
786 mcEnv.tx(create_bridge(a, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.first)));
787 TER mcTER = mcEnv.env_.ter();
788
789 scEnv.tx(create_bridge(b, bridge(a, ia, b, ib)), ter(TER::fromInt(expected.second)));
790 TER scTER = scEnv.env_.ter();
791
792 bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS;
793
794 test_result.emplace_back(mcTER, scTER, pass);
795 };
796
797 auto apply_ics = [&](auto const& lc, auto const& ics) {
798 std::apply([&](auto const&... ic) { (testcase(lc, ic), ...); }, ics);
799 };
800
801 std::apply([&](auto const&... lc) { (apply_ics(lc, ics), ...); }, lcs);
802
803#if GENERATE_MTX_OUTPUT
804 // optional output of matrix results in markdown format
805 // ----------------------------------------------------
806 std::string fname{std::tmpnam(nullptr)};
807 fname += ".md";
808 std::cout << "Markdown output for matrix test: " << fname << "\n";
809
810 auto print_res = [](auto tup) -> std::string {
811 std::string status = std::string(transToken(std::get<0>(tup))) + " / " + transToken(std::get<1>(tup));
812
813 if (std::get<2>(tup))
814 return status;
815 else
816 {
817 // red
818 return std::string("`") + status + "`";
819 }
820 };
821
822 auto output_table = [&](auto print_res) {
823 size_t test_idx = 0;
824 std::string res;
825 res.reserve(10000); // should be enough :-)
826
827 // first two header lines
828 res += "| `issuing ->` | ";
829 std::apply([&](auto const&... ic) { ((res += ic.first, res += " | "), ...); }, ics);
830 res += "\n";
831
832 res += "| :--- | ";
833 std::apply([&](auto const&... ic) { (((void)ic.first, res += ":---: | "), ...); }, ics);
834 res += "\n";
835
836 auto output = [&](auto const& lc, auto const& ic) {
837 res += print_res(test_result[test_idx]);
838 res += " | ";
839 ++test_idx;
840 };
841
842 auto output_ics = [&](auto const& lc, auto const& ics) {
843 res += "| ";
844 res += lc.first;
845 res += " | ";
846 std::apply([&](auto const&... ic) { (output(lc, ic), ...); }, ics);
847 res += "\n";
848 };
849
850 std::apply([&](auto const&... lc) { (output_ics(lc, ics), ...); }, lcs);
851
852 return res;
853 };
854
855 std::ofstream(fname) << output_table(print_res);
856
857 std::string ter_fname{std::tmpnam(nullptr)};
858 std::cout << "ter output for matrix test: " << ter_fname << "\n";
859
860 std::ofstream ofs(ter_fname);
861 for (auto& t : test_result)
862 {
863 ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", " << std::string(transToken(std::get<1>(t)))
864 << "}\n,";
865 }
866#endif
867 }
868
869 void
871 {
872 using namespace jtx;
873 testcase("Modify Bridge");
874
875 // Changing a non-existent bridge should fail
876 XEnv(*this).tx(
879
880 // must change something
881 // XEnv(*this)
882 // .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
883 // .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(1)),
884 // ter(temMALFORMED));
885
886 // must change something
887 XEnv(*this)
888 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
889 .close()
891
892 // Reward amount is non-xrp
894
895 // Reward amount is XRP and negative
897
898 // Min create amount is non-xrp
899 XEnv(*this).tx(
901
902 // Min create amount is zero
904
905 // Min create amount is negative
906 XEnv(*this).tx(
908
909 // First check the regular claim process (without bridge_modify)
910 for (auto withClaim : {false, true})
911 {
912 XEnv mcEnv(*this);
913 XEnv scEnv(*this, true);
914
915 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
916
919 .close()
921 .close();
922
923 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
924 auto const amt = XRP(1000);
925 std::uint32_t const claimID = 1;
926 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
927
928 BalanceTransfer transfer(
930
931 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
932 .close();
933
934 if (withClaim)
935 {
936 BEAST_EXPECT(transfer.has_not_happened());
937
938 // need to submit a claim transactions
939 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
940 }
941
942 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
943 }
944
945 // Check that the reward paid from a claim Id was the reward when
946 // the claim id was created, not the reward since the bridge was
947 // modified.
948 for (auto withClaim : {false, true})
949 {
950 XEnv mcEnv(*this);
951 XEnv scEnv(*this, true);
952
953 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
954
957 .close()
959 .close();
960
961 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
962 auto const amt = XRP(1000);
963 std::uint32_t const claimID = 1;
964 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
965
966 // Now modify the reward on the bridge
967 mcEnv.tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(10))).close();
968 scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10))).close();
969
970 BalanceTransfer transfer(
972
973 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
974 .close();
975
976 if (withClaim)
977 {
978 BEAST_EXPECT(transfer.has_not_happened());
979
980 // need to submit a claim transactions
981 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
982 }
983
984 // make sure the reward accounts indeed received the original
985 // split reward (1 split 5 ways) instead of the updated 2 XRP.
986 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
987 }
988
989 // Check that the signatures used to verify attestations and decide
990 // if there is a quorum are the current signer's list on the door
991 // account, not the signer's list that was in effect when the claim
992 // id was created.
993 for (auto withClaim : {false, true})
994 {
995 XEnv mcEnv(*this);
996 XEnv scEnv(*this, true);
997
998 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
999
1002 .close()
1004 .close();
1005
1006 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1007 auto const amt = XRP(1000);
1008 std::uint32_t const claimID = 1;
1009 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1010
1011 // change signers - claim should not be processed is the batch
1012 // is signed by original signers
1014
1015 BalanceTransfer transfer(
1016 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
1017
1018 // submit claim using outdated signers - should fail
1019 scEnv
1020 .multiTx(
1021 claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers),
1023 .close();
1024 if (withClaim)
1025 {
1026 // need to submit a claim transactions
1027 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close();
1028 }
1029
1030 // make sure transfer has not happened as we sent attestations
1031 // using outdated signers
1032 BEAST_EXPECT(transfer.has_not_happened());
1033
1034 // submit claim using current signers - should succeed
1035 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, alt_signers))
1036 .close();
1037 if (withClaim)
1038 {
1039 BEAST_EXPECT(transfer.has_not_happened());
1040
1041 // need to submit a claim transactions
1042 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1043 }
1044
1045 // make sure the transfer went through as we sent attestations
1046 // using new signers
1047 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum, false));
1048 }
1049
1050 // coverage test: bridge_modify transaction with incorrect flag
1051 XEnv(*this)
1053 .close()
1055
1056 // coverage test: bridge_modify transaction with xchain feature
1057 // disabled
1058 XEnv(*this)
1060 .disableFeature(featureXChainBridge)
1061 .close()
1063
1064 // coverage test: bridge_modify return temSIDECHAIN_NONDOOR_OWNER;
1065 XEnv(*this)
1067 .close()
1069
1076 XEnv(*this)
1077 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20)))
1078 .close()
1080 .close()
1082 .close()
1084 .close()
1087 .close();
1088 }
1089
1090 void
1092 {
1093 using namespace jtx;
1094 XRPAmount res1 = reserve(1);
1095 XRPAmount tx_fee = txFee();
1096
1097 testcase("Create ClaimID");
1098
1099 // normal bridge create for sanity check with the exact necessary
1100 // account balance
1101 XEnv(*this, true)
1103 .fund(res1, scuAlice) // acct reserve + 1 object
1104 .close()
1106 .close();
1107
1108 // check reward not deducted when claim id is created
1109 {
1110 XEnv xenv(*this, true);
1111
1112 Balance scAlice_bal(xenv, scAlice);
1113
1116 .close();
1117
1118 BEAST_EXPECT(scAlice_bal.diff() == -tx_fee);
1119 }
1120
1121 // Non-existent bridge
1122 XEnv(*this, true)
1125 .close();
1126
1127 // Creating the new object would put the account below the reserve
1128 XEnv(*this, true)
1130 .fund(res1 - xrp_dust, scuAlice) // barely not enough
1131 .close()
1133 .close();
1134
1135 // The specified reward doesn't match the reward on the bridge (test
1136 // by giving the reward amount for the other side, as well as a
1137 // completely non-matching reward)
1138 XEnv(*this, true)
1140 .close()
1142 .close();
1143
1144 // A reward amount that isn't XRP
1145 XEnv(*this, true)
1147 .close()
1149 .close();
1150
1151 // coverage test: xchain_create_claim_id transaction with incorrect
1152 // flag
1153 XEnv(*this, true)
1155 .close()
1157 .close();
1158
1159 // coverage test: xchain_create_claim_id transaction with xchain
1160 // feature disabled
1161 XEnv(*this, true)
1163 .disableFeature(featureXChainBridge)
1164 .close()
1166 .close();
1167 }
1168
1169 void
1171 {
1172 using namespace jtx;
1173 XRPAmount res0 = reserve(0);
1174 XRPAmount tx_fee = txFee();
1175
1176 testcase("Commit");
1177
1178 // Commit to a non-existent bridge
1180
1181 // check that reward not deducted when doing the commit
1182 {
1183 XEnv xenv(*this);
1184
1185 Balance alice_bal(xenv, mcAlice);
1186 auto const amt = XRP(1000);
1187
1189
1190 STAmount claim_cost = amt;
1191 BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee));
1192 }
1193
1194 // Commit a negative amount
1195 XEnv(*this)
1197 .close()
1199
1200 // Commit an amount whose issue that does not match the expected
1201 // issue on the bridge (either LockingChainIssue or
1202 // IssuingChainIssue, depending on the chain).
1203 XEnv(*this)
1205 .close()
1207
1208 // Commit an amount that would put the sender below the required
1209 // reserve (if XRP)
1210 XEnv(*this)
1212 .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough
1213 .close()
1215
1216 XEnv(*this)
1218 .fund(
1219 res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees
1220 mcuAlice) // exactly enough => should succeed
1221 .close()
1223
1224 // Commit an amount above the account's balance (for both XRP and
1225 // IOUs)
1226 XEnv(*this)
1228 .fund(res0, mcuAlice) // barely not enough
1229 .close()
1231
1232 auto jvb_USD = bridge(mcDoor, mcUSD, scGw, scUSD);
1233
1234 // commit sent from iou issuer (mcGw) succeeds - should it?
1235 XEnv(*this)
1236 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1237 .tx(create_bridge(mcDoor, jvb_USD))
1238 .close()
1239 .tx(xchain_commit(mcGw, jvb_USD, 1, mcUSD(1), scBob));
1240
1241 // commit to a door account from the door account. This should fail.
1242 XEnv(*this)
1243 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1244 .tx(create_bridge(mcDoor, jvb_USD))
1245 .close()
1247
1248 // commit sent from mcAlice which has no IOU balance => should fail
1249 XEnv(*this)
1250 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1251 .tx(create_bridge(mcDoor, jvb_USD))
1252 .close()
1253 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scBob), ter(terNO_LINE));
1254
1255 // commit sent from mcAlice which has no IOU balance => should fail
1256 // just changed the destination to scGw (which is the door account
1257 // and may not make much sense)
1258 XEnv(*this)
1259 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1260 .tx(create_bridge(mcDoor, jvb_USD))
1261 .close()
1262 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scGw), ter(terNO_LINE));
1263
1264 // commit sent from mcAlice which has a IOU balance => should
1265 // succeed
1266 XEnv(*this)
1267 .tx(trust(mcDoor, mcUSD(10000)))
1268 .tx(trust(mcAlice, mcUSD(10000)))
1269 .close()
1270 .tx(pay(mcGw, mcAlice, mcUSD(10)))
1271 .tx(create_bridge(mcDoor, jvb_USD))
1272 .close()
1273 //.tx(pay(mcAlice, mcDoor, mcUSD(10)));
1274 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(10), scAlice));
1275
1276 // coverage test: xchain_commit transaction with incorrect flag
1277 XEnv(*this)
1279 .close()
1281
1282 // coverage test: xchain_commit transaction with xchain feature
1283 // disabled
1284 XEnv(*this)
1286 .disableFeature(featureXChainBridge)
1287 .close()
1289 }
1290
1291 void
1293 {
1294 using namespace jtx;
1295
1296 testcase("Add Attestation");
1297 XRPAmount res0 = reserve(0);
1298 XRPAmount tx_fee = txFee();
1299
1300 auto multiTtxFee = [&](std::uint32_t m) -> STAmount { return multiply(tx_fee, STAmount(m), xrpIssue()); };
1301
1302 // Add an attestation to a claim id that has already reached quorum.
1303 // This should succeed and share in the reward.
1304 // note: this is true only when either:
1305 // 1. dest account is not specified, so transfer requires a claim
1306 // 2. or the extra attestation is sent in the same batch as the
1307 // one reaching quorum
1308 for (auto withClaim : {true})
1309 {
1310 XEnv mcEnv(*this);
1311 XEnv scEnv(*this, true);
1312 std::uint32_t const claimID = 1;
1313
1314 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1315
1318 .close()
1320 .close();
1321
1322 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1323
1324 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1325 auto const amt = XRP(1000);
1326 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1327
1328 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
1329
1330 scEnv
1332 scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers, UT_XCHAIN_DEFAULT_QUORUM))
1333 .close();
1334 scEnv
1336 scAttester,
1337 jvb,
1338 mcAlice,
1339 amt,
1341 true,
1342 claimID,
1343 dst,
1345 .close();
1346
1347 if (withClaim)
1348 {
1349 BEAST_EXPECT(transfer.has_not_happened());
1350
1351 // need to submit a claim transactions
1352 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1353 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1354 BEAST_EXPECT(scEnv.claimID(jvb) == claimID);
1355 }
1356
1357 BEAST_EXPECT(transfer.has_happened(amt, split_reward_everyone));
1358 }
1359
1360 // Test that signature weights are correctly handled. Assign
1361 // signature weights of 1,2,4,4 and a quorum of 7. Check that the
1362 // 4,4 signatures reach a quorum, the 1,2,4, reach a quorum, but the
1363 // 4,2, 4,1 and 1,2 do not.
1364
1365 // 1,2,4 => should succeed
1366 for (auto withClaim : {false, true})
1367 {
1368 XEnv mcEnv(*this);
1369 XEnv scEnv(*this, true);
1370
1371 std::uint32_t const quorum_7 = 7;
1372 std::vector<signer> const signers_ = [] {
1373 constexpr int numSigners = 4;
1374 std::uint32_t weights[] = {1, 2, 4, 4};
1375
1376 std::vector<signer> result;
1377 result.reserve(numSigners);
1378 for (int i = 0; i < numSigners; ++i)
1379 {
1380 using namespace std::literals;
1381 auto const a = Account("signer_"s + std::to_string(i));
1382 result.emplace_back(a, weights[i]);
1383 }
1384 return result;
1385 }();
1386
1387 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1388
1390 .tx(jtx::signers(Account::master, quorum_7, signers_))
1391 .close()
1393 .close();
1394 std::uint32_t const claimID = 1;
1395 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1396
1397 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1398 auto const amt = XRP(1000);
1399
1400 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1401
1402 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 3, withClaim);
1403
1404 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 3))
1405 .close();
1406
1407 if (withClaim)
1408 {
1409 BEAST_EXPECT(transfer.has_not_happened());
1410
1411 // need to submit a claim transactions
1412 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1413 }
1414
1415 BEAST_EXPECT(!scEnv.claimID(jvb, 1)); // claim id deleted
1416
1417 BEAST_EXPECT(transfer.has_happened(amt, divide(reward, STAmount(3), reward.issue())));
1418 }
1419
1420 // 4,4 => should succeed
1421 for (auto withClaim : {false, true})
1422 {
1423 XEnv mcEnv(*this);
1424 XEnv scEnv(*this, true);
1425
1426 std::uint32_t const quorum_7 = 7;
1427 std::vector<signer> const signers_ = [] {
1428 constexpr int numSigners = 4;
1429 std::uint32_t weights[] = {1, 2, 4, 4};
1430
1431 std::vector<signer> result;
1432 result.reserve(numSigners);
1433 for (int i = 0; i < numSigners; ++i)
1434 {
1435 using namespace std::literals;
1436 auto const a = Account("signer_"s + std::to_string(i));
1437 result.emplace_back(a, weights[i]);
1438 }
1439 return result;
1440 }();
1441 STAmount const split_reward_ = divide(reward, STAmount(signers_.size()), reward.issue());
1442
1443 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1444
1446 .tx(jtx::signers(Account::master, quorum_7, signers_))
1447 .close()
1449 .close();
1450 std::uint32_t const claimID = 1;
1451 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1452
1453 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1454 auto const amt = XRP(1000);
1455
1456 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1457
1458 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[2], 2, withClaim);
1459
1460 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 2))
1461 .close();
1462
1463 if (withClaim)
1464 {
1465 BEAST_EXPECT(transfer.has_not_happened());
1466
1467 // need to submit a claim transactions
1468 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
1469 }
1470
1471 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1472
1473 BEAST_EXPECT(transfer.has_happened(amt, divide(reward, STAmount(2), reward.issue())));
1474 }
1475
1476 // 1,2 => should fail
1477 for (auto withClaim : {false, true})
1478 {
1479 XEnv mcEnv(*this);
1480 XEnv scEnv(*this, true);
1481
1482 std::uint32_t const quorum_7 = 7;
1483 std::vector<signer> const signers_ = [] {
1484 constexpr int numSigners = 4;
1485 std::uint32_t weights[] = {1, 2, 4, 4};
1486
1487 std::vector<signer> result;
1488 result.reserve(numSigners);
1489 for (int i = 0; i < numSigners; ++i)
1490 {
1491 using namespace std::literals;
1492 auto const a = Account("signer_"s + std::to_string(i));
1493 result.emplace_back(a, weights[i]);
1494 }
1495 return result;
1496 }();
1497
1498 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1499
1501 .tx(jtx::signers(Account::master, quorum_7, signers_))
1502 .close()
1504 .close();
1505
1506 std::uint32_t const claimID = 1;
1507 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1508
1509 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1510 auto const amt = XRP(1000);
1511 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1512
1513 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 2, withClaim);
1514
1515 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2))
1516 .close();
1517 if (withClaim)
1518 {
1519 BEAST_EXPECT(transfer.has_not_happened());
1520
1521 // need to submit a claim transactions
1522 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close();
1523 }
1524
1525 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id still present
1526 BEAST_EXPECT(transfer.has_not_happened());
1527 }
1528
1529 // 2,4 => should fail
1530 for (auto withClaim : {false, true})
1531 {
1532 XEnv mcEnv(*this);
1533 XEnv scEnv(*this, true);
1534
1535 std::uint32_t const quorum_7 = 7;
1536 std::vector<signer> const signers_ = [] {
1537 constexpr int numSigners = 4;
1538 std::uint32_t weights[] = {1, 2, 4, 4};
1539
1540 std::vector<signer> result;
1541 result.reserve(numSigners);
1542 for (int i = 0; i < numSigners; ++i)
1543 {
1544 using namespace std::literals;
1545 auto const a = Account("signer_"s + std::to_string(i));
1546 result.emplace_back(a, weights[i]);
1547 }
1548 return result;
1549 }();
1550
1551 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1552
1554 .tx(jtx::signers(Account::master, quorum_7, signers_))
1555 .close()
1557 .close();
1558
1559 std::uint32_t const claimID = 1;
1560 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1561
1562 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1563 auto const amt = XRP(1000);
1564
1565 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1566
1567 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[1], 2, withClaim);
1568
1569 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers_, 2, 1))
1570 .close();
1571
1572 if (withClaim)
1573 {
1574 BEAST_EXPECT(transfer.has_not_happened());
1575
1576 // need to submit a claim transactions
1577 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close();
1578 }
1579
1580 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id still present
1581 BEAST_EXPECT(transfer.has_not_happened());
1582 }
1583
1584 // Confirm that account create transactions happen in the correct
1585 // order. If they reach quorum out of order they should not execute
1586 // until all the previous created transactions have occurred.
1587 // Re-adding an attestation should move funds.
1588 {
1589 XEnv mcEnv(*this);
1590 XEnv scEnv(*this, true);
1591 auto const amt = XRP(1000);
1592 auto const amt_plus_reward = amt + reward;
1593
1594 {
1595 Balance door(mcEnv, mcDoor);
1596 Balance carol(mcEnv, mcCarol);
1597
1598 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1599 .close()
1603 .close();
1604
1605 BEAST_EXPECT(door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee));
1606 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1607 }
1608
1611 .close();
1612
1613 {
1614 // send first batch of account create attest for all 3
1615 // account create
1616 Balance attester(scEnv, scAttester);
1617 Balance door(scEnv, Account::master);
1618
1619 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2))
1621 .multiTx(att_create_acct_vec(2, amt, scuBob, 2))
1622 .close();
1623
1624 BEAST_EXPECT(door.diff() == STAmount(0));
1625 // att_create_acct_vec return vectors of size 2, so 2*3 txns
1626 BEAST_EXPECT(attester.diff() == -multiTtxFee(6));
1627
1628 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // ca claim id present
1629 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1630 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1631 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count still 0
1632 }
1633
1634 {
1635 // complete attestations for 2nd account create => should
1636 // not complete
1637 Balance attester(scEnv, scAttester);
1638 Balance door(scEnv, Account::master);
1639
1640 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2)).close();
1641
1642 BEAST_EXPECT(door.diff() == STAmount(0));
1643 // att_create_acct_vec return vectors of size 3, so 3 txns
1644 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1645
1646 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1647 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count still 0
1648 }
1649
1650 {
1651 // complete attestations for 3rd account create => should
1652 // not complete
1653 Balance attester(scEnv, scAttester);
1654 Balance door(scEnv, Account::master);
1655
1656 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2)).close();
1657
1658 BEAST_EXPECT(door.diff() == STAmount(0));
1659 // att_create_acct_vec return vectors of size 3, so 3 txns
1660 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1661
1662 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1663 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count still 0
1664 }
1665
1666 {
1667 // complete attestations for 1st account create => account
1668 // should be created
1669 Balance attester(scEnv, scAttester);
1670 Balance door(scEnv, Account::master);
1671
1672 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1)).close();
1673
1674 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1675 // att_create_acct_vec return vectors of size 3, so 3 txns
1676 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1677 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
1678
1679 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id 1 deleted
1680 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
1681 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1682 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count now 1
1683 }
1684
1685 {
1686 // resend attestations for 3rd account create => still
1687 // should not complete
1688 Balance attester(scEnv, scAttester);
1689 Balance door(scEnv, Account::master);
1690
1691 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2)).close();
1692
1693 BEAST_EXPECT(door.diff() == STAmount(0));
1694 // att_create_acct_vec return vectors of size 3, so 3 txns
1695 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1696
1697 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
1698 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1699 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count still 1
1700 }
1701
1702 {
1703 // resend attestations for 2nd account create => account
1704 // should be created
1705 Balance attester(scEnv, scAttester);
1706 Balance door(scEnv, Account::master);
1707
1708 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close();
1709
1710 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1711 BEAST_EXPECT(attester.diff() == -tx_fee);
1712 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
1713
1714 BEAST_EXPECT(!scEnv.caClaimID(jvb, 2)); // claim id 2 deleted
1715 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
1716 BEAST_EXPECT(scEnv.claimCount(jvb) == 2); // claim count now 2
1717 }
1718 {
1719 // resend attestations for 3rc account create => account
1720 // should be created
1721 Balance attester(scEnv, scAttester);
1722 Balance door(scEnv, Account::master);
1723
1724 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close();
1725
1726 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1727 BEAST_EXPECT(attester.diff() == -tx_fee);
1728 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
1729
1730 BEAST_EXPECT(!scEnv.caClaimID(jvb, 3)); // claim id 3 deleted
1731 BEAST_EXPECT(scEnv.claimCount(jvb) == 3); // claim count now 3
1732 }
1733 }
1734
1735 // Check that creating an account with less than the minimum reserve
1736 // fails.
1737 {
1738 XEnv mcEnv(*this);
1739 XEnv scEnv(*this, true);
1740
1741 auto const amt = res0 - XRP(1);
1742 auto const amt_plus_reward = amt + reward;
1743
1744 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
1745
1746 {
1747 Balance door(mcEnv, mcDoor);
1748 Balance carol(mcEnv, mcCarol);
1749
1751
1752 BEAST_EXPECT(door.diff() == amt_plus_reward);
1753 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
1754 }
1755
1758 .close();
1759
1760 Balance attester(scEnv, scAttester);
1761 Balance door(scEnv, Account::master);
1762
1763 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close();
1764 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
1765 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
1766
1767 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2, 2)).close();
1768 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
1769 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
1770
1771 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
1772 BEAST_EXPECT(door.diff() == -reward);
1773 BEAST_EXPECT(!scEnv.account(scuAlice));
1774 }
1775
1776 // Check that sending funds with an account create txn to an
1777 // existing account works.
1778 {
1779 XEnv mcEnv(*this);
1780 XEnv scEnv(*this, true);
1781
1782 auto const amt = XRP(111);
1783 auto const amt_plus_reward = amt + reward;
1784
1785 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
1786
1787 {
1788 Balance door(mcEnv, mcDoor);
1789 Balance carol(mcEnv, mcCarol);
1790
1792
1793 BEAST_EXPECT(door.diff() == amt_plus_reward);
1794 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
1795 }
1796
1799 .close();
1800
1801 Balance attester(scEnv, scAttester);
1802 Balance door(scEnv, Account::master);
1803 Balance alice(scEnv, scAlice);
1804
1805 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
1806 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
1807 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
1808
1809 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
1810 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
1811 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
1812
1813 BEAST_EXPECT(door.diff() == -amt_plus_reward);
1814 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
1815 BEAST_EXPECT(alice.diff() == amt);
1816 }
1817
1818 // Check that sending funds to an existing account with deposit auth
1819 // set fails for account create transactions.
1820 {
1821 XEnv mcEnv(*this);
1822 XEnv scEnv(*this, true);
1823
1824 auto const amt = XRP(1000);
1825 auto const amt_plus_reward = amt + reward;
1826
1827 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
1828
1829 {
1830 Balance door(mcEnv, mcDoor);
1831 Balance carol(mcEnv, mcCarol);
1832
1834
1835 BEAST_EXPECT(door.diff() == amt_plus_reward);
1836 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
1837 }
1838
1841 .tx(fset("scAlice", asfDepositAuth)) // set deposit auth
1842 .close();
1843
1844 Balance attester(scEnv, scAttester);
1845 Balance door(scEnv, Account::master);
1846 Balance alice(scEnv, scAlice);
1847
1848 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
1849 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
1850 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
1851
1852 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
1853 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
1854 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
1855
1856 BEAST_EXPECT(door.diff() == -reward);
1857 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
1858 BEAST_EXPECT(alice.diff() == STAmount(0));
1859 }
1860
1861 // If an account is unable to pay the reserve, check that it fails.
1862 // [greg todo] I don't know what this should test??
1863
1864 // If an attestation already exists for that server and claim id,
1865 // the new attestation should replace the old attestation
1866 {
1867 XEnv mcEnv(*this);
1868 XEnv scEnv(*this, true);
1869 auto const amt = XRP(1000);
1870 auto const amt_plus_reward = amt + reward;
1871
1872 {
1873 Balance door(mcEnv, mcDoor);
1874 Balance carol(mcEnv, mcCarol);
1875
1876 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1877 .close()
1879 .close() // make sure Alice gets claim #1
1881 .close() // make sure Bob gets claim #2
1883 .close(); // and Carol will get claim #3
1884
1885 BEAST_EXPECT(door.diff() == (multiply(amt_plus_reward, STAmount(3), xrpIssue()) - tx_fee));
1886 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1887 }
1888
1889 std::uint32_t const red_quorum = 2;
1891 .tx(jtx::signers(Account::master, red_quorum, signers))
1892 .close();
1893
1894 {
1895 Balance attester(scEnv, scAttester);
1896 Balance door(scEnv, Account::master);
1897 auto const bad_amt = XRP(10);
1898 std::uint32_t txCount = 0;
1899
1900 // send attestations with incorrect amounts to for all 3
1901 // AccountCreate. They will be replaced later
1902 scEnv.multiTx(att_create_acct_vec(1, bad_amt, scuAlice, 1))
1903 .multiTx(att_create_acct_vec(2, bad_amt, scuBob, 1, 2))
1904 .multiTx(att_create_acct_vec(3, bad_amt, scuCarol, 1, 1))
1905 .close();
1906 txCount += 3;
1907
1908 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 created");
1909 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 created");
1910 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 created");
1911
1912 // note: if we send inconsistent attestations in the same
1913 // batch, the transaction errors.
1914
1915 // from now on we send correct attestations
1916 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 0))
1917 .multiTx(att_create_acct_vec(2, amt, scuBob, 1, 2))
1918 .multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 4))
1919 .close();
1920 txCount += 3;
1921
1922 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 still there");
1923 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 still there");
1924 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
1925 BEAST_EXPECTS(scEnv.claimCount(jvb) == 0, "No account created yet");
1926
1927 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 1)).close();
1928 txCount += 1;
1929
1930 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
1931 BEAST_EXPECTS(scEnv.claimCount(jvb) == 0, "No account created yet");
1932
1933 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 2)).close();
1934 txCount += 1;
1935
1936 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 deleted");
1937 BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created");
1938
1939 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3))
1941 .close();
1942 txCount += 2;
1943
1944 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 2), "claim id 2 deleted");
1945 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 not added");
1946 BEAST_EXPECTS(scEnv.claimCount(jvb) == 2, "scuAlice & scuBob created");
1947
1948 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 0)).close();
1949 txCount += 1;
1950
1951 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 3), "claim id 3 deleted");
1952 BEAST_EXPECTS(scEnv.claimCount(jvb) == 3, "All 3 accounts created");
1953
1954 // because of the division of the rewards among attesters,
1955 // sometimes a couple drops are left over unspent in the
1956 // door account (here 2 drops)
1957 BEAST_EXPECT(multiply(amt_plus_reward, STAmount(3), xrpIssue()) + door.diff() < drops(3));
1958 BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount));
1959 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
1960 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
1961 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
1962 }
1963 }
1964
1965 // If attestation moves funds, confirm the claim ledger objects are
1966 // removed (for both account create and "regular" transactions)
1967 // [greg] we do this in all attestation tests
1968
1969 // coverage test: add_attestation transaction with incorrect flag
1970 {
1971 XEnv scEnv(*this, true);
1974 .close()
1975 .tx(claim_attestation(scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]),
1978 .close();
1979 }
1980
1981 // coverage test: add_attestation with xchain feature
1982 // disabled
1983 {
1984 XEnv scEnv(*this, true);
1987 .disableFeature(featureXChainBridge)
1988 .close()
1989 .tx(claim_attestation(scAttester, jvb, mcAlice, XRP(1000), payees[0], true, 1, {}, signers[0]),
1991 .close();
1992 }
1993 }
1994
1995 void
1997 {
1998 using namespace jtx;
1999
2000 testcase("Add Non Batch Claim Attestation");
2001
2002 {
2003 XEnv mcEnv(*this);
2004 XEnv scEnv(*this, true);
2005 std::uint32_t const claimID = 1;
2006
2007 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2008
2011 .close()
2013 .close();
2014
2015 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
2016
2017 Account const dst{scBob};
2018 auto const amt = XRP(1000);
2019 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2020
2021 auto const dstStartBalance = scEnv.env_.balance(dst);
2022
2023 for (int i = 0; i < signers.size(); ++i)
2024 {
2025 auto const att =
2026 claim_attestation(scAttester, jvb, mcAlice, amt, payees[i], true, claimID, dst, signers[i]);
2027
2028 TER const expectedTER = i < quorum ? tesSUCCESS : TER{tecXCHAIN_NO_CLAIM_ID};
2029 if (i + 1 == quorum)
2030 scEnv.tx(att, ter(expectedTER)).close();
2031 else
2032 scEnv.tx(att, ter(expectedTER)).close();
2033
2034 if (i + 1 < quorum)
2035 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2036 else
2037 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2038 }
2039 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2040 }
2041
2042 {
2059 XEnv mcEnv(*this);
2060 XEnv scEnv(*this, true);
2061 auto const amt = XRP(1000);
2062 std::uint32_t const claimID = 1;
2063
2064 for (auto i = 0; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2; ++i)
2065 scEnv.fund(amt, alt_signers[i].account);
2066
2067 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2068
2071 .close()
2073 .close();
2074
2075 Account const dst{scBob};
2076 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2077 auto const dstStartBalance = scEnv.env_.balance(dst);
2078
2079 {
2080 // G1: master key
2081 auto att =
2082 claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, alt_signers[0]);
2083 scEnv.tx(att).close();
2084 }
2085 {
2086 // G2: regular key
2087 // alt_signers[0] is the regular key of alt_signers[1]
2088 // There should be 2 attestations after the transaction
2089 scEnv.tx(jtx::regkey(alt_signers[1].account, alt_signers[0].account)).close();
2090 auto att =
2091 claim_attestation(scAttester, jvb, mcAlice, amt, payees[1], true, claimID, dst, alt_signers[0]);
2092 att[sfAttestationSignerAccount.getJsonName()] = alt_signers[1].account.human();
2093 scEnv.tx(att).close();
2094 }
2095 {
2096 // B3: public key and non-exist (unfunded) account mismatch
2097 // G3: public key and non-exist (unfunded) account match
2098 auto const unfundedSigner1 = alt_signers[UT_XCHAIN_DEFAULT_NUM_SIGNERS - 1];
2099 auto const unfundedSigner2 = alt_signers[UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2];
2100 auto att = claim_attestation(
2101 scAttester,
2102 jvb,
2103 mcAlice,
2104 amt,
2106 true,
2107 claimID,
2108 dst,
2109 unfundedSigner1);
2110 att[sfAttestationSignerAccount.getJsonName()] = unfundedSigner2.account.human();
2112 att[sfAttestationSignerAccount.getJsonName()] = unfundedSigner1.account.human();
2113 scEnv.tx(att).close();
2114 }
2115 {
2116 // B2: single item signer list
2117 std::vector<signer> tempSignerList = {signers[0]};
2118 scEnv.tx(jtx::signers(alt_signers[2].account, 1, tempSignerList));
2119 auto att = claim_attestation(
2120 scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, tempSignerList.front());
2121 att[sfAttestationSignerAccount.getJsonName()] = alt_signers[2].account.human();
2123 }
2124 {
2125 // B1: disabled master key
2126 scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0)).close();
2127 auto att =
2128 claim_attestation(scAttester, jvb, mcAlice, amt, payees[2], true, claimID, dst, alt_signers[2]);
2130 }
2131 {
2132 // --B4: not on signer list
2133 auto att = claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]);
2134 scEnv.tx(att, ter(tecNO_PERMISSION)).close();
2135 }
2136 {
2137 // --B5: missing sfAttestationSignerAccount field
2138 // Then submit the one with the field. Should reach quorum.
2139 auto att =
2140 claim_attestation(scAttester, jvb, mcAlice, amt, payees[3], true, claimID, dst, alt_signers[3]);
2141 att.removeMember(sfAttestationSignerAccount.getJsonName());
2142 scEnv.tx(att, ter(temMALFORMED)).close();
2143 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2144 att[sfAttestationSignerAccount.getJsonName()] = alt_signers[3].account.human();
2145 scEnv.tx(att).close();
2146 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2147 }
2148 }
2149 }
2150
2151 void
2153 {
2154 using namespace jtx;
2155
2156 testcase("Add Non Batch Account Create Attestation");
2157
2158 XEnv mcEnv(*this);
2159 XEnv scEnv(*this, true);
2160
2161 XRPAmount tx_fee = mcEnv.txFee();
2162
2163 Account a{"a"};
2164 Account doorA{"doorA"};
2165
2166 STAmount funds{XRP(10000)};
2167 mcEnv.fund(funds, a);
2168 mcEnv.fund(funds, doorA);
2169
2170 Account ua{"ua"}; // unfunded account we want to create
2171
2172 BridgeDef xrp_b{
2173 doorA,
2174 xrpIssue(),
2176 xrpIssue(),
2177 XRP(1), // reward
2178 XRP(20), // minAccountCreate
2179 4, // quorum
2180 signers,
2182
2183 xrp_b.initBridge(mcEnv, scEnv);
2184
2185 auto const amt = XRP(777);
2186 auto const amt_plus_reward = amt + xrp_b.reward;
2187 {
2188 Balance bal_doorA(mcEnv, doorA);
2189 Balance bal_a(mcEnv, a);
2190
2191 mcEnv.tx(sidechain_xchain_account_create(a, xrp_b.jvb, ua, amt, xrp_b.reward)).close();
2192
2193 BEAST_EXPECT(bal_doorA.diff() == amt_plus_reward);
2194 BEAST_EXPECT(bal_a.diff() == -(amt_plus_reward + tx_fee));
2195 }
2196
2197 for (int i = 0; i < signers.size(); ++i)
2198 {
2199 auto const att = create_account_attestation(
2200 signers[0].account, xrp_b.jvb, a, amt, xrp_b.reward, signers[i].account, true, 1, ua, signers[i]);
2201 TER const expectedTER = i < xrp_b.quorum ? tesSUCCESS : TER{tecXCHAIN_ACCOUNT_CREATE_PAST};
2202
2203 scEnv.tx(att, ter(expectedTER)).close();
2204 if (i + 1 < xrp_b.quorum)
2205 BEAST_EXPECT(!scEnv.env_.le(ua));
2206 else
2207 BEAST_EXPECT(scEnv.env_.le(ua));
2208 }
2209 BEAST_EXPECT(scEnv.env_.le(ua));
2210 }
2211
2212 void
2214 {
2215 using namespace jtx;
2216
2217 XRPAmount res0 = reserve(0);
2218 XRPAmount tx_fee = txFee();
2219
2220 testcase("Claim");
2221
2222 // Claim where the amount matches what is attested to, to an account
2223 // that exists, and there are enough attestations to reach a quorum
2224 // => should succeed
2225 // -----------------------------------------------------------------
2226 for (auto withClaim : {false, true})
2227 {
2228 XEnv mcEnv(*this);
2229 XEnv scEnv(*this, true);
2230
2231 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2232
2235 .close()
2237 .close();
2238
2239 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2240 auto const amt = XRP(1000);
2241 std::uint32_t const claimID = 1;
2242 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2243
2244 BalanceTransfer transfer(
2245 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2246
2247 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2248 .close();
2249 if (withClaim)
2250 {
2251 BEAST_EXPECT(transfer.has_not_happened());
2252
2253 // need to submit a claim transactions
2254 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
2255 }
2256
2257 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2258 }
2259
2260 // Claim with just one attestation signed by the Master key
2261 // => should not succeed
2262 // -----------------------------------------------------------------
2263 for (auto withClaim : {false, true})
2264 {
2265 XEnv mcEnv(*this);
2266 XEnv scEnv(*this, true);
2267
2268 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2269
2270 scEnv
2272 //.tx(jtx::signers(Account::master, quorum, signers))
2273 .close()
2275 .close();
2276
2277 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2278 auto const amt = XRP(1000);
2279 std::uint32_t const claimID = 1;
2280 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2281
2282 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim);
2283
2284 jtx::signer master_signer(Account::master);
2285 scEnv
2286 .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, master_signer),
2288 .close();
2289
2290 BEAST_EXPECT(transfer.has_not_happened());
2291 }
2292
2293 // Claim with just one attestation signed by a regular key
2294 // associated to the master account
2295 // => should not succeed
2296 // -----------------------------------------------------------------
2297 for (auto withClaim : {false, true})
2298 {
2299 XEnv mcEnv(*this);
2300 XEnv scEnv(*this, true);
2301
2302 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2303
2304 scEnv
2306 //.tx(jtx::signers(Account::master, quorum, signers))
2308 .close()
2310 .close();
2311
2312 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2313 auto const amt = XRP(1000);
2314 std::uint32_t const claimID = 1;
2315 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2316
2317 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, &payees[0], 1, withClaim);
2318
2319 jtx::signer master_signer(payees[0]);
2320 scEnv
2321 .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, master_signer),
2323 .close();
2324
2325 BEAST_EXPECT(transfer.has_not_happened());
2326 }
2327
2328 // Claim against non-existent bridge
2329 // ---------------------------------
2330 for (auto withClaim : {false, true})
2331 {
2332 XEnv mcEnv(*this);
2333 XEnv scEnv(*this, true);
2334
2335 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2336
2337 auto jvb_unknown = bridge(mcBob, xrpIssue(), Account::master, xrpIssue());
2338
2341 .close()
2343 .close();
2344
2345 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2346 auto const amt = XRP(1000);
2347 std::uint32_t const claimID = 1;
2348 mcEnv.tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst), ter(tecNO_ENTRY)).close();
2349
2350 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2351 scEnv
2352 .tx(claim_attestation(scAttester, jvb_unknown, mcAlice, amt, payees[0], true, claimID, dst, signers[0]),
2354 .close();
2355
2356 if (withClaim)
2357 {
2358 BEAST_EXPECT(transfer.has_not_happened());
2359
2360 // need to submit a claim transactions
2361 scEnv.tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob), ter(tecNO_ENTRY)).close();
2362 }
2363
2364 BEAST_EXPECT(transfer.has_not_happened());
2365 }
2366
2367 // Claim against non-existent claim id
2368 // -----------------------------------
2369 for (auto withClaim : {false, true})
2370 {
2371 XEnv mcEnv(*this);
2372 XEnv scEnv(*this, true);
2373
2374 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2375
2378 .close()
2380 .close();
2381
2382 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2383 auto const amt = XRP(1000);
2384 std::uint32_t const claimID = 1;
2385 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2386
2387 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2388
2389 // attest using non-existent claim id
2390 scEnv
2391 .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, 999, dst, signers[0]),
2393 .close();
2394 if (withClaim)
2395 {
2396 BEAST_EXPECT(transfer.has_not_happened());
2397
2398 // claim using non-existent claim id
2400 }
2401
2402 BEAST_EXPECT(transfer.has_not_happened());
2403 }
2404
2405 // Claim against a claim id owned by another account
2406 // -------------------------------------------------
2407 for (auto withClaim : {false, true})
2408 {
2409 XEnv mcEnv(*this);
2410 XEnv scEnv(*this, true);
2411
2412 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2413
2416 .close()
2418 .close();
2419
2420 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2421 auto const amt = XRP(1000);
2422 std::uint32_t const claimID = 1;
2423 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2424
2425 BalanceTransfer transfer(
2426 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2427
2428 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2429 .close();
2430 if (withClaim)
2431 {
2432 BEAST_EXPECT(transfer.has_not_happened());
2433
2434 // submit a claim transaction with the wrong account (scGw
2435 // instead of scAlice)
2436 scEnv.tx(xchain_claim(scGw, jvb, claimID, amt, scBob), ter(tecXCHAIN_BAD_CLAIM_ID)).close();
2437 BEAST_EXPECT(transfer.has_not_happened());
2438 }
2439 else
2440 {
2441 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2442 }
2443 }
2444
2445 // Claim against a claim id with no attestations
2446 // ---------------------------------------------
2447 for (auto withClaim : {false, true})
2448 {
2449 XEnv mcEnv(*this);
2450 XEnv scEnv(*this, true);
2451
2452 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2453
2456 .close()
2458 .close();
2459
2460 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2461 auto const amt = XRP(1000);
2462 std::uint32_t const claimID = 1;
2463 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2464
2465 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2466
2467 // don't send any attestations
2468
2469 if (withClaim)
2470 {
2471 BEAST_EXPECT(transfer.has_not_happened());
2472
2473 // need to submit a claim transactions
2474 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close();
2475 }
2476
2477 BEAST_EXPECT(transfer.has_not_happened());
2478 }
2479
2480 // Claim against a claim id with attestations, but not enough to
2481 // make a quorum
2482 // --------------------------------------------------------------------
2483 for (auto withClaim : {false, true})
2484 {
2485 XEnv mcEnv(*this);
2486 XEnv scEnv(*this, true);
2487
2488 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2489
2492 .close()
2494 .close();
2495
2496 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2497 auto const amt = XRP(1000);
2498 std::uint32_t const claimID = 1;
2499 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2500
2501 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2502
2503 auto tooFew = quorum - 1;
2504 scEnv
2505 .multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers, tooFew))
2506 .close();
2507 if (withClaim)
2508 {
2509 BEAST_EXPECT(transfer.has_not_happened());
2510
2511 // need to submit a claim transactions
2512 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_CLAIM_NO_QUORUM)).close();
2513 }
2514
2515 BEAST_EXPECT(transfer.has_not_happened());
2516 }
2517
2518 // Claim id of zero
2519 // ----------------
2520 for (auto withClaim : {false, true})
2521 {
2522 XEnv mcEnv(*this);
2523 XEnv scEnv(*this, true);
2524
2525 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2526
2529 .close()
2531 .close();
2532
2533 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2534 auto const amt = XRP(1000);
2535 std::uint32_t const claimID = 1;
2536 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2537
2538 BalanceTransfer transfer(scEnv, Account::master, scBob, scAlice, payees, withClaim);
2539
2540 scEnv
2541 .multiTx(
2542 claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, 0, dst, signers),
2544 .close();
2545 if (withClaim)
2546 {
2547 BEAST_EXPECT(transfer.has_not_happened());
2548
2549 // need to submit a claim transactions
2551 }
2552
2553 BEAST_EXPECT(transfer.has_not_happened());
2554 }
2555
2556 // Claim issue that does not match the expected issue on the bridge
2557 // (either LockingChainIssue or IssuingChainIssue, depending on the
2558 // chain). The claim id should already have enough attestations to
2559 // reach a quorum for this amount (for a different issuer).
2560 // ---------------------------------------------------------------------
2561 for (auto withClaim : {true})
2562 {
2563 XEnv mcEnv(*this);
2564 XEnv scEnv(*this, true);
2565
2566 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2567
2570 .close()
2572 .close();
2573
2574 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2575 auto const amt = XRP(1000);
2576 std::uint32_t const claimID = 1;
2577 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2578
2579 BalanceTransfer transfer(
2580 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2581
2582 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2583 .close();
2584
2585 if (withClaim)
2586 {
2587 BEAST_EXPECT(transfer.has_not_happened());
2588
2589 // need to submit a claim transactions
2590 scEnv.tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob), ter(temBAD_AMOUNT)).close();
2591 }
2592
2593 BEAST_EXPECT(transfer.has_not_happened());
2594 }
2595
2596 // Claim to a destination that does not already exist on the chain
2597 // -----------------------------------------------------------------
2598 for (auto withClaim : {true})
2599 {
2600 XEnv mcEnv(*this);
2601 XEnv scEnv(*this, true);
2602
2603 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2604
2607 .close()
2609 .close();
2610
2611 auto dst(withClaim ? std::nullopt : std::optional<Account>{scuBob});
2612 auto const amt = XRP(1000);
2613 std::uint32_t const claimID = 1;
2614 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2615
2616 BalanceTransfer transfer(
2617 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2618
2619 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2620 .close();
2621 if (withClaim)
2622 {
2623 BEAST_EXPECT(transfer.has_not_happened());
2624
2625 // need to submit a claim transactions
2626 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scuBob), ter(tecNO_DST)).close();
2627 }
2628
2629 BEAST_EXPECT(transfer.has_not_happened());
2630 }
2631
2632 // Claim where the claim id owner does not have enough XRP to pay
2633 // the reward
2634 // ------------------------------------------------------------------
2635 for (auto withClaim : {false, true})
2636 {
2637 XEnv mcEnv(*this);
2638 XEnv scEnv(*this, true);
2639
2640 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2641 STAmount huge_reward{XRP(20000)};
2642 BEAST_EXPECT(huge_reward > scEnv.balance(scAlice));
2643
2644 scEnv.tx(create_bridge(Account::master, jvb, huge_reward))
2646 .close()
2647 .tx(xchain_create_claim_id(scAlice, jvb, huge_reward, mcAlice))
2648 .close();
2649
2650 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2651 auto const amt = XRP(1000);
2652 std::uint32_t const claimID = 1;
2653 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2654
2655 BalanceTransfer transfer(
2656 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2657
2658 if (withClaim)
2659 {
2660 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers))
2661 .close();
2662 BEAST_EXPECT(transfer.has_not_happened());
2663
2664 // need to submit a claim transactions
2665 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT)).close();
2666 }
2667 else
2668 {
2669 auto txns = claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers);
2670 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
2671 {
2672 scEnv.tx(txns[i]).close();
2673 }
2674 scEnv.tx(txns.back());
2675 scEnv.close();
2676 // The attestation should succeed, because it adds an
2677 // attestation, but the claim should fail with insufficient
2678 // funds
2679 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecUNFUNDED_PAYMENT)).close();
2680 }
2681
2682 BEAST_EXPECT(transfer.has_not_happened());
2683 }
2684
2685 // Claim where the claim id owner has enough XRP to pay the reward,
2686 // but it would put his balance below the reserve
2687 // --------------------------------------------------------------------
2688 for (auto withClaim : {false, true})
2689 {
2690 XEnv mcEnv(*this);
2691 XEnv scEnv(*this, true);
2692
2693 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2694
2697 .fund(
2698 res0 + reward,
2699 scuAlice) // just not enough because of fees
2700 .close()
2702 .close();
2703
2704 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2705 auto const amt = XRP(1000);
2706 std::uint32_t const claimID = 1;
2707 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2708
2709 BalanceTransfer transfer(scEnv, Account::master, scBob, scuAlice, payees, withClaim);
2710
2711 scEnv
2712 .tx(claim_attestation(scAttester, jvb, mcAlice, amt, payees[0], true, claimID, dst, signers[0]),
2714 .close();
2715 if (withClaim)
2716 {
2717 BEAST_EXPECT(transfer.has_not_happened());
2718
2719 // need to submit a claim transactions
2720 scEnv.tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob), ter(tecXCHAIN_NO_CLAIM_ID)).close();
2721 }
2722
2723 BEAST_EXPECT(transfer.has_not_happened());
2724 }
2725
2726 // Pay to an account with deposit auth set
2727 // ---------------------------------------
2728 for (auto withClaim : {false, true})
2729 {
2730 XEnv mcEnv(*this);
2731 XEnv scEnv(*this, true);
2732
2733 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2734
2737 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
2738 .close()
2740 .close();
2741
2742 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2743 auto const amt = XRP(1000);
2744 std::uint32_t const claimID = 1;
2745 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2746
2747 BalanceTransfer transfer(
2748 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2749 auto txns = claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers);
2750 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
2751 {
2752 scEnv.tx(txns[i]).close();
2753 }
2754 if (withClaim)
2755 {
2756 scEnv.tx(txns.back()).close();
2757
2758 BEAST_EXPECT(transfer.has_not_happened());
2759
2760 // need to submit a claim transactions
2761 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION)).close();
2762
2763 // the transfer failed, but check that we can still use the
2764 // claimID with a different account
2765 Balance scCarol_bal(scEnv, scCarol);
2766
2767 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
2768 BEAST_EXPECT(scCarol_bal.diff() == amt);
2769 }
2770 else
2771 {
2772 scEnv.tx(txns.back()).close();
2773 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecNO_PERMISSION)).close();
2774 // A way would be to remove deposit auth and resubmit the
2775 // attestations (even though the witness servers won't do
2776 // it)
2777 scEnv
2778 .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth
2779 .close();
2780
2781 Balance scBob_bal(scEnv, scBob);
2782 scEnv.tx(txns.back()).close();
2783 BEAST_EXPECT(scBob_bal.diff() == amt);
2784 }
2785 }
2786
2787 // Pay to an account with Destination Tag set
2788 // ------------------------------------------
2789 for (auto withClaim : {false, true})
2790 {
2791 XEnv mcEnv(*this);
2792 XEnv scEnv(*this, true);
2793
2794 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2795
2798 .tx(fset("scBob", asfRequireDest)) // set dest tag
2799 .close()
2801 .close();
2802
2803 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2804 auto const amt = XRP(1000);
2805 std::uint32_t const claimID = 1;
2806 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2807
2808 BalanceTransfer transfer(
2809 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2810 auto txns = claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers);
2811 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
2812 {
2813 scEnv.tx(txns[i]).close();
2814 }
2815 if (withClaim)
2816 {
2817 scEnv.tx(txns.back()).close();
2818 BEAST_EXPECT(transfer.has_not_happened());
2819
2820 // need to submit a claim transactions
2821 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED)).close();
2822
2823 // the transfer failed, but check that we can still use the
2824 // claimID with a different account
2825 Balance scCarol_bal(scEnv, scCarol);
2826
2827 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
2828 BEAST_EXPECT(scCarol_bal.diff() == amt);
2829 }
2830 else
2831 {
2832 scEnv.tx(txns.back()).close();
2833 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob), ter(tecDST_TAG_NEEDED)).close();
2834 // A way would be to remove the destination tag requirement
2835 // and resubmit the attestations (even though the witness
2836 // servers won't do it)
2837 scEnv
2838 .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag
2839 .close();
2840
2841 Balance scBob_bal(scEnv, scBob);
2842
2843 scEnv.tx(txns.back()).close();
2844 BEAST_EXPECT(scBob_bal.diff() == amt);
2845 }
2846 }
2847
2848 // Pay to an account with deposit auth set. Check that the attestations
2849 // are still validated and that we can used the claimID to transfer the
2850 // funds to a different account (which doesn't have deposit auth set)
2851 // --------------------------------------------------------------------
2852 {
2853 XEnv mcEnv(*this);
2854 XEnv scEnv(*this, true);
2855
2856 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2857
2860 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
2861 .close()
2863 .close();
2864
2865 auto dst(std::optional<Account>{scBob});
2866 auto const amt = XRP(1000);
2867 std::uint32_t const claimID = 1;
2868 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2869
2870 // we should be able to submit the attestations, but the transfer
2871 // should not occur because dest account has deposit auth set
2872 Balance scBob_bal(scEnv, scBob);
2873
2874 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
2875 BEAST_EXPECT(scBob_bal.diff() == STAmount(0));
2876
2877 // Check that check that we still can use the claimID to transfer
2878 // the amount to a different account
2879 Balance scCarol_bal(scEnv, scCarol);
2880
2881 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
2882 BEAST_EXPECT(scCarol_bal.diff() == amt);
2883 }
2884
2885 // Claim where the amount different from what is attested to
2886 // ---------------------------------------------------------
2887 for (auto withClaim : {true})
2888 {
2889 XEnv mcEnv(*this);
2890 XEnv scEnv(*this, true);
2891
2892 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2893
2896 .close()
2898 .close();
2899
2900 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2901 auto const amt = XRP(1000);
2902 std::uint32_t const claimID = 1;
2903 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2904
2905 BalanceTransfer transfer(
2906 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2907 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
2908 if (withClaim)
2909 {
2910 BEAST_EXPECT(transfer.has_not_happened());
2911
2912 // claim wrong amount
2914 }
2915
2916 BEAST_EXPECT(transfer.has_not_happened());
2917 }
2918
2919 // Verify that rewards are paid from the account that owns the claim
2920 // id
2921 // --------------------------------------------------------------------
2922 for (auto withClaim : {false, true})
2923 {
2924 XEnv mcEnv(*this);
2925 XEnv scEnv(*this, true);
2926
2927 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2928
2931 .close()
2933 .close();
2934
2935 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2936 auto const amt = XRP(1000);
2937 std::uint32_t const claimID = 1;
2938 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2939
2940 BalanceTransfer transfer(
2941 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2942 Balance scAlice_bal(scEnv, scAlice);
2943 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
2944
2945 STAmount claim_cost = reward;
2946
2947 if (withClaim)
2948 {
2949 BEAST_EXPECT(transfer.has_not_happened());
2950
2951 // need to submit a claim transactions
2952 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
2953 claim_cost += tx_fee;
2954 }
2955
2956 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2957 BEAST_EXPECT(scAlice_bal.diff() == -claim_cost); // because reward % 4 == 0
2958 }
2959
2960 // Verify that if a reward is not evenly divisible among the reward
2961 // accounts, the remaining amount goes to the claim id owner.
2962 // ----------------------------------------------------------------
2963 for (auto withClaim : {false, true})
2964 {
2965 XEnv mcEnv(*this);
2966 XEnv scEnv(*this, true);
2967
2969
2972 .close()
2974 .close();
2975
2976 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2977 auto const amt = XRP(1000);
2978 std::uint32_t const claimID = 1;
2979 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2980
2981 BalanceTransfer transfer(
2982 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM, withClaim);
2983 Balance scAlice_bal(scEnv, scAlice);
2984 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
2985 STAmount claim_cost = tiny_reward;
2986
2987 if (withClaim)
2988 {
2989 BEAST_EXPECT(transfer.has_not_happened());
2990
2991 // need to submit a claim transactions
2992 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
2993 claim_cost += tx_fee;
2994 }
2995
2996 BEAST_EXPECT(transfer.has_happened(amt, tiny_reward_split));
2997 BEAST_EXPECT(scAlice_bal.diff() == -(claim_cost - tiny_reward_remainder));
2998 }
2999
3000 // If a reward distribution fails for one of the reward accounts
3001 // (the reward account doesn't exist or has deposit auth set), then
3002 // the txn should still succeed, but that portion should go to the
3003 // claim id owner.
3004 // -------------------------------------------------------------------
3005 for (auto withClaim : {false, true})
3006 {
3007 XEnv mcEnv(*this);
3008 XEnv scEnv(*this, true);
3009
3010 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3011
3012 std::vector<Account> alt_payees{payees.begin(), payees.end() - 1};
3013 alt_payees.back() = Account("inexistent");
3014
3017 .close()
3019 .close();
3020
3021 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3022 auto const amt = XRP(1000);
3023 std::uint32_t const claimID = 1;
3024 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3025
3026 BalanceTransfer transfer(
3027 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM - 1, withClaim);
3028 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, alt_payees, true, claimID, dst, signers));
3029
3030 if (withClaim)
3031 {
3032 BEAST_EXPECT(transfer.has_not_happened());
3033
3034 // need to submit a claim transactions
3035 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
3036 }
3037
3038 // this also checks that only 3 * split_reward was deducted from
3039 // scAlice (the payer account), since we passed alt_payees to
3040 // BalanceTransfer
3041 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3042 }
3043
3044 for (auto withClaim : {false, true})
3045 {
3046 XEnv mcEnv(*this);
3047 XEnv scEnv(*this, true);
3048
3049 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3050 auto& unpaid = payees[UT_XCHAIN_DEFAULT_QUORUM - 1];
3053 .tx(fset(unpaid, asfDepositAuth))
3054 .close()
3056 .close();
3057
3058 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3059 auto const amt = XRP(1000);
3060 std::uint32_t const claimID = 1;
3061 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3062
3063 // balance of last signer should not change (has deposit auth)
3064 Balance last_signer(scEnv, unpaid);
3065
3066 // make sure all signers except the last one get the
3067 // split_reward
3068
3069 BalanceTransfer transfer(
3070 scEnv, Account::master, scBob, scAlice, &payees[0], UT_XCHAIN_DEFAULT_QUORUM - 1, withClaim);
3071 scEnv.multiTx(claim_attestations(scAttester, jvb, mcAlice, amt, payees, true, claimID, dst, signers));
3072
3073 if (withClaim)
3074 {
3075 BEAST_EXPECT(transfer.has_not_happened());
3076
3077 // need to submit a claim transactions
3078 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob)).close();
3079 }
3080
3081 // this also checks that only 3 * split_reward was deducted from
3082 // scAlice (the payer account), since we passed payees.size() -
3083 // 1 to BalanceTransfer
3084 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3085
3086 // and make sure the account with deposit auth received nothing
3087 BEAST_EXPECT(last_signer.diff() == STAmount(0));
3088 }
3089
3090 // coverage test: xchain_claim transaction with incorrect flag
3091 XEnv(*this, true)
3093 .close()
3095 .close();
3096
3097 // coverage test: xchain_claim transaction with xchain feature
3098 // disabled
3099 XEnv(*this, true)
3101 .disableFeature(featureXChainBridge)
3102 .close()
3104 .close();
3105
3106 // coverage test: XChainClaim::preclaim - isLockingChain = true;
3107 XEnv(*this)
3109 .close()
3111 }
3112
3113 void
3115 {
3116 using namespace jtx;
3117
3118 testcase("Bridge Create Account");
3119 XRPAmount tx_fee = txFee();
3120
3121 // coverage test: transferHelper() - dst == src
3122 {
3123 XEnv scEnv(*this, true);
3124
3125 auto const amt = XRP(111);
3126 auto const amt_plus_reward = amt + reward;
3127
3129
3130 Balance door(scEnv, Account::master);
3131
3132 // scEnv.tx(att_create_acct_batch1(1, amt,
3133 // Account::master)).close();
3134 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2)).close();
3135 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
3136 BEAST_EXPECT(scEnv.claimCount(jvb) == 0); // claim count is one less
3137
3138 // scEnv.tx(att_create_acct_batch2(1, amt,
3139 // Account::master)).close();
3140 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2, 2)).close();
3141 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
3142 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count was incremented
3143
3144 BEAST_EXPECT(door.diff() == -reward);
3145 }
3146
3147 // Check that creating an account with less than the minimum create
3148 // amount fails.
3149 {
3150 XEnv mcEnv(*this);
3151
3152 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3153
3154 Balance door(mcEnv, mcDoor);
3155 Balance carol(mcEnv, mcCarol);
3156
3157 mcEnv
3160 .close();
3161
3162 BEAST_EXPECT(door.diff() == STAmount(0));
3163 BEAST_EXPECT(carol.diff() == -tx_fee);
3164 }
3165
3166 // Check that creating an account with invalid flags fails.
3167 {
3168 XEnv mcEnv(*this);
3169
3170 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3171
3172 Balance door(mcEnv, mcDoor);
3173
3174 mcEnv
3178 .close();
3179
3180 BEAST_EXPECT(door.diff() == STAmount(0));
3181 }
3182
3183 // Check that creating an account with the XChainBridge feature
3184 // disabled fails.
3185 {
3186 XEnv mcEnv(*this);
3187
3188 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3189
3190 Balance door(mcEnv, mcDoor);
3191
3192 mcEnv.disableFeature(featureXChainBridge)
3194 .close();
3195
3196 BEAST_EXPECT(door.diff() == STAmount(0));
3197 }
3198
3199 // Check that creating an account with a negative amount fails
3200 {
3201 XEnv mcEnv(*this);
3202
3203 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3204
3205 Balance door(mcEnv, mcDoor);
3206
3208 .close();
3209
3210 BEAST_EXPECT(door.diff() == STAmount(0));
3211 }
3212
3213 // Check that creating an account with a negative reward fails
3214 {
3215 XEnv mcEnv(*this);
3216
3217 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3218
3219 Balance door(mcEnv, mcDoor);
3220
3222 .close();
3223
3224 BEAST_EXPECT(door.diff() == STAmount(0));
3225 }
3226
3227 // Check that door account can't lock funds onto itself
3228 {
3229 XEnv mcEnv(*this);
3230
3231 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3232
3233 Balance door(mcEnv, mcDoor);
3234
3235 mcEnv
3237 .close();
3238
3239 BEAST_EXPECT(door.diff() == -tx_fee);
3240 }
3241
3242 // Check that reward matches the amount specified in bridge
3243 {
3244 XEnv mcEnv(*this);
3245
3246 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3247
3248 Balance door(mcEnv, mcDoor);
3249
3250 mcEnv
3253 .close();
3254
3255 BEAST_EXPECT(door.diff() == STAmount(0));
3256 }
3257 }
3258
3259 void
3261 {
3262 using namespace jtx;
3263 XRPAmount res0 = reserve(0);
3264 XRPAmount tx_fee = txFee();
3265
3266 testcase("Fee dips into reserve");
3267
3268 // commit where the fee dips into the reserve, this should succeed
3269 XEnv(*this)
3271 .fund(res0 + one_xrp + tx_fee - drops(1), mcuAlice)
3272 .close()
3274
3275 // commit where the commit amount drips into the reserve, this should
3276 // fail
3277 XEnv(*this)
3279 .fund(res0 + one_xrp - drops(1), mcuAlice)
3280 .close()
3282
3283 auto const minAccountCreate = XRP(20);
3284
3285 // account create commit where the fee dips into the reserve,
3286 // this should succeed
3287 XEnv(*this)
3288 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
3289 .fund(res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice)
3290 .close()
3292
3293 // account create commit where the commit dips into the reserve,
3294 // this should fail
3295 XEnv(*this)
3296 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
3297 .fund(res0 + minAccountCreate + reward - drops(1), mcuAlice)
3298 .close()
3301 }
3302
3303 void
3305 {
3306 using namespace jtx;
3307
3308 testcase("Bridge Delete Door Account");
3309
3310 auto const acctDelFee{drops(XEnv(*this).env_.current()->fees().increment)};
3311
3312 // Deleting an account that owns bridge should fail
3313 {
3314 XEnv mcEnv(*this);
3315
3316 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1))).close();
3317
3318 // We don't allow an account to be deleted if its sequence
3319 // number is within 256 of the current ledger.
3320 for (size_t i = 0; i < 256; ++i)
3321 mcEnv.close();
3322
3323 // try to delete mcDoor, send funds to mcAlice
3324 mcEnv.tx(acctdelete(mcDoor, mcAlice), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
3325 }
3326
3327 // Deleting an account that owns a claim id should fail
3328 {
3329 XEnv scEnv(*this, true);
3330
3332 .close()
3334 .close();
3335
3336 // We don't allow an account to be deleted if its sequence
3337 // number is within 256 of the current ledger.
3338 for (size_t i = 0; i < 256; ++i)
3339 scEnv.close();
3340
3341 // try to delete scAlice, send funds to scBob
3342 scEnv.tx(acctdelete(scAlice, scBob), fee(acctDelFee), ter(tecHAS_OBLIGATIONS));
3343 }
3344 }
3345
3346 void
3348 {
3349 using namespace jtx;
3350
3351 testcase("Bad attestations");
3352 {
3353 // Create a bridge and add an attestation with a bad public key
3354 XEnv scEnv(*this, true);
3355 std::uint32_t const claimID = 1;
3357 auto const amt = XRP(1000);
3360 auto jvAtt = claim_attestation(
3361 scAttester,
3362 jvb,
3363 mcAlice,
3364 amt,
3366 true,
3367 claimID,
3368 dst,
3370 {
3371 // Change to an invalid keytype
3372 auto k = jvAtt["PublicKey"].asString();
3373 k.at(1) = '9';
3374 jvAtt["PublicKey"] = k;
3375 }
3376 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
3377 }
3378 {
3379 // Create a bridge and add an create account attestation with a bad
3380 // public key
3381 XEnv scEnv(*this, true);
3382 std::uint32_t const createCount = 1;
3383 Account dst{scBob};
3384 auto const amt = XRP(1000);
3385 auto const rewardAmt = XRP(1);
3387 auto jvAtt = create_account_attestation(
3388 scAttester,
3389 jvb,
3390 mcAlice,
3391 amt,
3392 rewardAmt,
3394 true,
3395 createCount,
3396 dst,
3398 {
3399 // Change to an invalid keytype
3400 auto k = jvAtt["PublicKey"].asString();
3401 k.at(1) = '9';
3402 jvAtt["PublicKey"] = k;
3403 }
3404 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
3405 }
3406 }
3407
3408 void
3427};
3428
3429// -----------------------------------------------------------
3430// -----------------------------------------------------------
3432{
3433private:
3434 static constexpr size_t num_signers = 5;
3435
3436 // --------------------------------------------------
3437 enum class WithClaim { no, yes };
3449
3460
3462 using BridgeID = BridgeDef const*;
3463
3464 // tracking chain state
3465 // --------------------
3467 {
3470
3471 void
3472 init(ENV& env, jtx::Account const& acct)
3473 {
3474 startAmount = env.balance(acct);
3476 }
3477
3478 bool
3479 verify(ENV& env, jtx::Account const& acct) const
3480 {
3481 STAmount diff{env.balance(acct) - startAmount};
3482 bool check = diff == expectedDiff;
3483 return check;
3484 }
3485 };
3486
3487 // --------------------------------------------------
3489 {
3493
3494 ChainStateTrack(ENV& env) : env(env), tx_fee(env.env_.current()->fees().base)
3495 {
3496 }
3497
3498 void
3499 sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec& claims)
3500 {
3501 for (auto const& c : claims)
3502 {
3503 env.tx(c).close();
3504 spendFee(bridge->signers[signer_idx].account);
3505 }
3506 claims.clear();
3507 }
3508
3509 uint32_t
3511 {
3512 size_t num_successful = 0;
3513 for (auto const& c : claims)
3514 {
3515 env.tx(c).close();
3516 if (env.ter() == tesSUCCESS)
3517 {
3518 counters[bridge].signers.push_back(signer_idx);
3519 num_successful++;
3520 }
3521 spendFee(bridge->signers[signer_idx].account);
3522 }
3523 claims.clear();
3524 return num_successful;
3525 }
3526
3527 void
3529 {
3530 bool callback_called;
3531
3532 // we have this "do {} while" loop because we want to process
3533 // all the account create which can reach quorum at this time
3534 // stamp.
3535 do
3536 {
3537 callback_called = false;
3538 for (size_t i = 0; i < signers_attns.size(); ++i)
3539 {
3540 for (auto& [bridge, claims] : signers_attns[i])
3541 {
3542 sendAttestations(i, bridge, claims.xfer_claims);
3543
3544 auto& c = counters[bridge];
3545 auto& create_claims = claims.create_claims[c.claim_count];
3546 auto num_attns = create_claims.size();
3547 if (num_attns)
3548 {
3549 c.num_create_attn_sent += sendCreateAttestations(i, bridge, create_claims);
3550 }
3551 assert(claims.create_claims[c.claim_count].empty());
3552 }
3553 }
3554 for (auto& [bridge, c] : counters)
3555 {
3556 if (c.num_create_attn_sent >= bridge->quorum)
3557 {
3558 callback_called = true;
3559 c.create_callbacks[c.claim_count](c.signers);
3560 ++c.claim_count;
3561 c.num_create_attn_sent = 0;
3562 c.signers.clear();
3563 }
3564 }
3565 } while (callback_called);
3566 }
3567
3568 void
3569 init(jtx::Account const& acct)
3570 {
3571 accounts[acct].init(env, acct);
3572 }
3573
3574 void
3575 receive(jtx::Account const& acct, STAmount amt, std::uint64_t divisor = 1)
3576 {
3577 if (amt.issue() != xrpIssue())
3578 return;
3579 auto it = accounts.find(acct);
3580 if (it == accounts.end())
3581 {
3582 accounts[acct].init(env, acct);
3583 // we just looked up the account, so expectedDiff == 0
3584 }
3585 else
3586 {
3587 it->second.expectedDiff +=
3588 (divisor == 1 ? amt : divide(amt, STAmount(amt.issue(), divisor), amt.issue()));
3589 }
3590 }
3591
3592 void
3593 spend(jtx::Account const& acct, STAmount amt, std::uint64_t times = 1)
3594 {
3595 if (amt.issue() != xrpIssue())
3596 return;
3597 receive(acct, times == 1 ? -amt : -multiply(amt, STAmount(amt.issue(), times), amt.issue()));
3598 }
3599
3600 void
3601 transfer(jtx::Account const& from, jtx::Account const& to, STAmount amt)
3602 {
3603 spend(from, amt);
3604 receive(to, amt);
3605 }
3606
3607 void
3608 spendFee(jtx::Account const& acct, size_t times = 1)
3609 {
3610 spend(acct, tx_fee, times);
3611 }
3612
3613 bool
3614 verify() const
3615 {
3616 for (auto const& [acct, state] : accounts)
3617 if (!state.verify(env, acct))
3618 return false;
3619 return true;
3620 }
3621
3623 {
3625
3627 uint32_t create_count{0}; // for account create. First should be 1
3628 uint32_t claim_count{0}; // for account create. Increments after quorum for
3629 // current create_count (starts at 1) is reached.
3630
3631 uint32_t num_create_attn_sent{0}; // for current claim_count
3634 };
3635
3641
3644
3650 };
3651
3653 {
3654 ChainStateTracker(ENV& a_env, ENV& b_env) : a_(a_env), b_(b_env)
3655 {
3656 }
3657
3658 bool
3659 verify() const
3660 {
3661 return a_.verify() && b_.verify();
3662 }
3663
3664 void
3666 {
3669 }
3670
3671 void
3672 init(jtx::Account const& acct)
3673 {
3674 a_.init(acct);
3675 b_.init(acct);
3676 }
3677
3680 };
3681
3690
3691 enum Act_Flags { af_a2b = 1 << 0 };
3692
3693 // --------------------------------------------------
3694 template <class T>
3696 {
3697 public:
3699 : bridge_(bridge), st_(chainstate)
3700 {
3701 }
3702
3705 {
3706 return static_cast<T&>(*this).a2b() ? st_->a_ : st_->b_;
3707 }
3708
3711 {
3712 return static_cast<T&>(*this).a2b() ? st_->b_ : st_->a_;
3713 }
3714
3715 jtx::Account const&
3717 {
3718 return static_cast<T&>(*this).a2b() ? bridge_.doorA : bridge_.doorB;
3719 }
3720
3721 jtx::Account const&
3723 {
3724 return static_cast<T&>(*this).a2b() ? bridge_.doorB : bridge_.doorA;
3725 }
3726
3727 protected:
3730 };
3731
3732 // --------------------------------------------------
3733 class SmCreateAccount : public SmBase<SmCreateAccount>
3734 {
3735 public:
3737
3739 std::shared_ptr<ChainStateTracker> const& chainstate,
3740 BridgeDef const& bridge,
3742 : Base(chainstate, bridge), sm_state(st_initial), cr(std::move(create))
3743 {
3744 }
3745
3746 bool
3747 a2b() const
3748 {
3749 return cr.a2b;
3750 }
3751
3752 uint32_t
3754 {
3755 ChainStateTrack& st = srcState();
3756 jtx::Account const& srcdoor = srcDoor();
3757
3759 .close(); // needed for claim_id sequence to be correct'
3760 st.spendFee(cr.from);
3761 st.transfer(cr.from, srcdoor, cr.amt);
3762 st.transfer(cr.from, srcdoor, cr.reward);
3763
3764 return ++st.counters[&bridge_].create_count;
3765 }
3766
3767 void
3769 {
3770 ChainStateTrack& st = destState();
3771
3772 // check all signers, but start at a random one
3773 size_t i;
3774 for (i = 0; i < num_signers; ++i)
3775 {
3776 size_t signer_idx = (rnd + i) % num_signers;
3777
3778 if (!(cr.attested[signer_idx]))
3779 {
3780 // enqueue one attestation for this signer
3781 cr.attested[signer_idx] = true;
3782
3783 st.signers_attns[signer_idx][&bridge_].create_claims[cr.claim_id - 1].emplace_back(
3785 bridge_.signers[signer_idx].account,
3786 bridge_.jvb,
3787 cr.from,
3788 cr.amt,
3789 cr.reward,
3790 bridge_.signers[signer_idx].account,
3791 cr.a2b,
3792 cr.claim_id,
3793 cr.to,
3794 bridge_.signers[signer_idx]));
3795 break;
3796 }
3797 }
3798
3799 if (i == num_signers)
3800 return; // did not attest
3801
3802 auto& counters = st.counters[&bridge_];
3803 if (counters.create_callbacks.size() < cr.claim_id)
3804 counters.create_callbacks.resize(cr.claim_id);
3805
3806 auto complete_cb = [&](std::vector<size_t> const& signers) {
3807 auto num_attestors = signers.size();
3808 st.env.close();
3809 assert(num_attestors <= std::count(cr.attested.begin(), cr.attested.end(), true));
3810 assert(num_attestors >= bridge_.quorum);
3811 assert(cr.claim_id - 1 == counters.claim_count);
3812
3813 auto r = cr.reward;
3814 auto reward = divide(r, STAmount(num_attestors), r.issue());
3815
3816 for (auto i : signers)
3817 st.receive(bridge_.signers[i].account, reward);
3818
3819 st.spend(dstDoor(), reward, num_attestors);
3820 st.transfer(dstDoor(), cr.to, cr.amt);
3821 st.env.env_.memoize(cr.to);
3823 };
3824
3825 counters.create_callbacks[cr.claim_id - 1] = std::move(complete_cb);
3826 }
3827
3828 SmState
3830 {
3831 switch (sm_state)
3832 {
3833 case st_initial:
3836 break;
3837
3838 case st_attesting:
3839 attest(time, rnd);
3840 break;
3841
3842 default:
3843 assert(0);
3844 break;
3845
3846 case st_completed:
3847 break; // will get this once
3848 }
3849 return sm_state;
3850 }
3851
3852 private:
3855 };
3856
3857 // --------------------------------------------------
3858 class SmTransfer : public SmBase<SmTransfer>
3859 {
3860 public:
3862
3864 : Base(chainstate, bridge), xfer(std::move(xfer)), sm_state(st_initial)
3865 {
3866 }
3867
3868 bool
3869 a2b() const
3870 {
3871 return xfer.a2b;
3872 }
3873
3874 uint32_t
3876 {
3877 ChainStateTrack& st = destState();
3878
3880 .close(); // needed for claim_id sequence to be
3881 // correct'
3882 st.spendFee(xfer.to);
3883 return ++st.counters[&bridge_].claim_id;
3884 }
3885
3886 void
3888 {
3889 ChainStateTrack& st = srcState();
3890 jtx::Account const& srcdoor = srcDoor();
3891
3892 if (xfer.amt.issue() != xrpIssue())
3893 {
3894 st.env.tx(pay(srcdoor, xfer.from, xfer.amt));
3895 st.spendFee(srcdoor);
3896 }
3897 st.env.tx(xchain_commit(
3898 xfer.from,
3899 bridge_.jvb,
3900 xfer.claim_id,
3901 xfer.amt,
3903 st.spendFee(xfer.from);
3904 st.transfer(xfer.from, srcdoor, xfer.amt);
3905 }
3906
3907 void
3909 {
3910 auto r = bridge_.reward;
3911 auto reward = divide(r, STAmount(bridge_.quorum), r.issue());
3912
3913 for (size_t i = 0; i < num_signers; ++i)
3914 {
3915 if (xfer.attested[i])
3916 st.receive(bridge_.signers[i].account, reward);
3917 }
3919 }
3920
3921 bool
3923 {
3924 ChainStateTrack& st = destState();
3925
3926 // check all signers, but start at a random one
3927 for (size_t i = 0; i < num_signers; ++i)
3928 {
3929 size_t signer_idx = (rnd + i) % num_signers;
3930 if (!(xfer.attested[signer_idx]))
3931 {
3932 // enqueue one attestation for this signer
3933 xfer.attested[signer_idx] = true;
3934
3935 st.signers_attns[signer_idx][&bridge_].xfer_claims.emplace_back(claim_attestation(
3936 bridge_.signers[signer_idx].account,
3937 bridge_.jvb,
3938 xfer.from,
3939 xfer.amt,
3940 bridge_.signers[signer_idx].account,
3941 xfer.a2b,
3942 xfer.claim_id,
3944 bridge_.signers[signer_idx]));
3945 break;
3946 }
3947 }
3948
3949 // return true if quorum was reached, false otherwise
3952 {
3955 }
3956 return quorum;
3957 }
3958
3959 void
3968
3969 SmState
3971 {
3972 switch (sm_state)
3973 {
3974 case st_initial:
3977 break;
3978
3980 commit();
3982 break;
3983
3984 case st_attesting:
3986 : st_attesting;
3987 break;
3988
3989 case st_attested:
3990 assert(xfer.with_claim == WithClaim::yes);
3991 claim();
3993 break;
3994
3995 default:
3996 case st_completed:
3997 assert(0); // should have been removed
3998 break;
3999 }
4000 return sm_state;
4001 }
4002
4003 private:
4006 };
4007
4008 // --------------------------------------------------
4011
4013
4014 void
4016 uint64_t time,
4017 std::shared_ptr<ChainStateTracker> const& chainstate,
4018 BridgeDef const& bridge,
4019 Transfer transfer)
4020 {
4021 sm_.emplace_back(time, SmTransfer(chainstate, bridge, std::move(transfer)));
4022 }
4023
4024 void
4026 {
4027 sm_.emplace_back(time, SmCreateAccount(chainstate, bridge, std::move(ac)));
4028 }
4029
4030public:
4031 void
4032 runSimulation(std::shared_ptr<ChainStateTracker> const& st, bool verify_balances = true)
4033 {
4034 using namespace jtx;
4035 uint64_t time = 0;
4036 std::mt19937 gen(27); // Standard mersenne_twister_engine
4038
4039 while (!sm_.empty())
4040 {
4041 ++time;
4042 for (auto it = sm_.begin(); it != sm_.end();)
4043 {
4044 auto vis = [&](auto& sm) {
4045 uint32_t rnd = distrib(gen);
4046 return sm.advance(time, rnd);
4047 };
4048 auto& [t, sm] = *it;
4049 if (t <= time && std::visit(vis, sm) == st_completed)
4050 it = sm_.erase(it);
4051 else
4052 ++it;
4053 }
4054
4055 // send attestations
4056 st->sendAttestations();
4057
4058 // make sure all transactions have been applied
4059 st->a_.env.close();
4060 st->b_.env.close();
4061
4062 if (verify_balances)
4063 {
4064 BEAST_EXPECT(st->verify());
4065 }
4066 }
4067 }
4068
4069 void
4071 {
4072 using namespace jtx;
4073
4074 testcase("Bridge usage simulation");
4075
4076 XEnv mcEnv(*this);
4077 XEnv scEnv(*this, true);
4078
4079 auto st = std::make_shared<ChainStateTracker>(mcEnv, scEnv);
4080
4081 // create 10 accounts + door funded on both chains, and store
4082 // in ChainStateTracker the initial amount of these accounts
4083 Account doorXRPLocking("doorXRPLocking"), doorUSDLocking("doorUSDLocking"), doorUSDIssuing("doorUSDIssuing");
4084
4085 constexpr size_t num_acct = 10;
4086 auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() {
4087 using namespace std::literals;
4088 std::vector<Account> result;
4089 result.reserve(num_acct);
4090 for (int i = 0; i < num_acct; ++i)
4091 result.emplace_back("a"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
4092 result.emplace_back("doorXRPLocking");
4093 doorXRPLocking = result.back();
4094 result.emplace_back("doorUSDLocking");
4095 doorUSDLocking = result.back();
4096 result.emplace_back("doorUSDIssuing");
4097 doorUSDIssuing = result.back();
4098 return result;
4099 }();
4100
4101 for (auto& acct : a)
4102 {
4103 STAmount amt{XRP(100000)};
4104
4105 mcEnv.fund(amt, acct);
4106 scEnv.fund(amt, acct);
4107 }
4108 Account USDLocking{"USDLocking"};
4109 IOU usdLocking{USDLocking["USD"]};
4110 IOU usdIssuing{doorUSDIssuing["USD"]};
4111
4112 mcEnv.fund(XRP(100000), USDLocking);
4113 mcEnv.close();
4114 mcEnv.tx(trust(doorUSDLocking, usdLocking(100000)));
4115 mcEnv.close();
4116 mcEnv.tx(pay(USDLocking, doorUSDLocking, usdLocking(50000)));
4117
4118 for (int i = 0; i < a.size(); ++i)
4119 {
4120 auto& acct{a[i]};
4121 if (i < num_acct)
4122 {
4123 mcEnv.tx(trust(acct, usdLocking(100000)));
4124 scEnv.tx(trust(acct, usdIssuing(100000)));
4125 }
4126 st->init(acct);
4127 }
4128 for (auto& s : signers)
4129 st->init(s.account);
4130
4131 st->b_.init(Account::master);
4132
4133 // also create some unfunded accounts
4134 constexpr size_t num_ua = 20;
4135 auto ua = []() {
4136 using namespace std::literals;
4137 std::vector<Account> result;
4138 result.reserve(num_ua);
4139 for (int i = 0; i < num_ua; ++i)
4140 result.emplace_back("ua"s + std::to_string(i), (i % 2) ? KeyType::ed25519 : KeyType::secp256k1);
4141 return result;
4142 }();
4143
4144 // initialize a bridge from a BridgeDef
4145 auto initBridge = [&mcEnv, &scEnv, &st](BridgeDef& bd) {
4146 bd.initBridge(mcEnv, scEnv);
4147 st->a_.spendFee(bd.doorA, 2);
4148 st->b_.spendFee(bd.doorB, 2);
4149 };
4150
4151 // create XRP -> XRP bridge
4152 // ------------------------
4153 BridgeDef xrp_b{
4154 doorXRPLocking, xrpIssue(), Account::master, xrpIssue(), XRP(1), XRP(20), quorum, signers, Json::nullValue};
4155
4156 initBridge(xrp_b);
4157
4158 // create USD -> USD bridge
4159 // ------------------------
4160 BridgeDef usd_b{
4161 doorUSDLocking, usdLocking, doorUSDIssuing, usdIssuing, XRP(1), XRP(20), quorum, signers, Json::nullValue};
4162
4163 initBridge(usd_b);
4164
4165 // try a single account create + transfer to validate the simulation
4166 // engine. Do the transfer 8 time steps after the account create, to
4167 // give time enough for ua[0] to be funded now so it can reserve
4168 // the claimID
4169 // -----------------------------------------------------------------
4170 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, true});
4171 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), true});
4172 runSimulation(st);
4173
4174 // try the same thing in the other direction
4175 // -----------------------------------------
4176 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, false});
4177 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), false});
4178 runSimulation(st);
4179
4180 // run multiple XRP transfers
4181 // --------------------------
4182 xfer(0, st, xrp_b, {a[0], a[0], a[1], XRP(6), true, WithClaim::no});
4183 xfer(1, st, xrp_b, {a[0], a[0], a[1], XRP(8), false, WithClaim::no});
4184 xfer(1, st, xrp_b, {a[1], a[1], a[1], XRP(1), true});
4185 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(3), false});
4186 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(5), false});
4187 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(7), false, WithClaim::no});
4188 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(9), true});
4189 runSimulation(st);
4190
4191 // run one USD transfer
4192 // --------------------
4193 xfer(0, st, usd_b, {a[0], a[1], a[2], usdLocking(3), true});
4194 runSimulation(st);
4195
4196 // run multiple USD transfers
4197 // --------------------------
4198 xfer(0, st, usd_b, {a[0], a[0], a[1], usdLocking(6), true});
4199 xfer(1, st, usd_b, {a[0], a[0], a[1], usdIssuing(8), false});
4200 xfer(1, st, usd_b, {a[1], a[1], a[1], usdLocking(1), true});
4201 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(3), false});
4202 xfer(2, st, usd_b, {a[1], a[1], a[1], usdIssuing(5), false});
4203 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(7), false});
4204 xfer(2, st, usd_b, {a[1], a[1], a[1], usdLocking(9), true});
4205 runSimulation(st);
4206
4207 // run mixed transfers
4208 // -------------------
4209 xfer(0, st, xrp_b, {a[0], a[0], a[0], XRP(1), true});
4210 xfer(0, st, usd_b, {a[1], a[3], a[3], usdIssuing(3), false});
4211 xfer(0, st, usd_b, {a[3], a[2], a[1], usdIssuing(5), false});
4212
4213 xfer(1, st, xrp_b, {a[0], a[0], a[0], XRP(4), false});
4214 xfer(1, st, xrp_b, {a[1], a[1], a[0], XRP(8), true});
4215 xfer(1, st, usd_b, {a[4], a[1], a[1], usdLocking(7), true});
4216
4217 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(7), true});
4218 xfer(3, st, xrp_b, {a[0], a[4], a[3], XRP(2), false});
4219 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(9), true});
4220 xfer(3, st, usd_b, {a[3], a[1], a[1], usdIssuing(11), false});
4221 runSimulation(st);
4222
4223 // run multiple account create to stress attestation batching
4224 // ----------------------------------------------------------
4225 ac(0, st, xrp_b, {a[0], ua[1], XRP(301), xrp_b.reward, true});
4226 ac(0, st, xrp_b, {a[1], ua[2], XRP(302), xrp_b.reward, true});
4227 ac(1, st, xrp_b, {a[0], ua[3], XRP(303), xrp_b.reward, true});
4228 ac(2, st, xrp_b, {a[1], ua[4], XRP(304), xrp_b.reward, true});
4229 ac(3, st, xrp_b, {a[0], ua[5], XRP(305), xrp_b.reward, true});
4230 ac(4, st, xrp_b, {a[1], ua[6], XRP(306), xrp_b.reward, true});
4231 ac(6, st, xrp_b, {a[0], ua[7], XRP(307), xrp_b.reward, true});
4232 ac(7, st, xrp_b, {a[2], ua[8], XRP(308), xrp_b.reward, true});
4233 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
4234 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
4235 ac(10, st, xrp_b, {a[0], ua[10], XRP(310), xrp_b.reward, true});
4236 ac(12, st, xrp_b, {a[0], ua[11], XRP(311), xrp_b.reward, true});
4237 ac(12, st, xrp_b, {a[3], ua[12], XRP(312), xrp_b.reward, true});
4238 ac(12, st, xrp_b, {a[4], ua[13], XRP(313), xrp_b.reward, true});
4239 ac(12, st, xrp_b, {a[3], ua[14], XRP(314), xrp_b.reward, true});
4240 ac(12, st, xrp_b, {a[6], ua[15], XRP(315), xrp_b.reward, true});
4241 ac(13, st, xrp_b, {a[7], ua[16], XRP(316), xrp_b.reward, true});
4242 ac(15, st, xrp_b, {a[3], ua[17], XRP(317), xrp_b.reward, true});
4243 runSimulation(st, true); // balances verification working now.
4244 }
4245
4246 void
4247 run() override
4248 {
4250 }
4251};
4252
4253BEAST_DEFINE_TESTSUITE(XChain, app, xrpl);
4254BEAST_DEFINE_TESTSUITE(XChainSim, app, xrpl);
4255
4256} // namespace xrpl::test
T all_of(T... args)
T apply(T... args)
T back(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:131
A testsuite class.
Definition suite.h:52
void pass()
Record a successful test condition.
Definition suite.h:495
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:148
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:455
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)
void attest(uint64_t time, uint32_t rnd)
SmState advance(uint64_t time, uint32_t rnd)
void distribute_reward(ChainStateTrack &st)
SmState advance(uint64_t time, uint32_t rnd)
bool attest(uint64_t time, uint32_t rnd)
SmTransfer(std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer xfer)
Immutable cryptographic account descriptor.
Definition Account.h:20
static Account const master
The master account.
Definition Account.h:29
A transaction testing environment.
Definition Env.h:98
Application & app()
Definition Env.h:230
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:97
TER ter() const
Return the TER for the last JTx.
Definition Env.h:560
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:248
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:260
void enableFeature(uint256 const feature)
Definition Env.cpp:616
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition Env.cpp:157
void memoize(Account const &account)
Associate AccountID with account.
Definition Env.cpp:130
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:298
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:20
Severity
Severity level / threshold of a Journal message.
Definition Journal.h:13
STL namespace.
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition Indexes.cpp:412
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:436
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition Indexes.cpp:422
Json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount, NetClock::duration const &settleDelay, PublicKey const &pk, std::optional< NetClock::time_point > const &cancelAfter, std::optional< std::uint32_t > const &dstTag)
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
std::vector< Json::Value > JValueVec
Json::Value bridge_create(Account const &acc, Json::Value const &bridge, STAmount const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
JValueVec claim_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
Json::Value bridge_modify(Account const &acc, Json::Value const &bridge, std::optional< STAmount > const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
FeatureBitset testable_amendments()
Definition Env.h:55
Json::Value xchain_commit(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, std::optional< Account > const &dst)
Json::Value bridge(Account const &lockingChainDoor, Issue const &lockingChainIssue, Account const &issuingChainDoor, Issue const &issuingChainIssue)
Json::Value create_account_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, jtx::signer const &signer)
auto const amount
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value claim_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, jtx::signer const &signer)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_LINE
Definition TER.h:200
constexpr std::uint32_t asfRequireDest
Definition TxFlags.h:58
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:98
constexpr std::uint32_t asfDisableMaster
Definition TxFlags.h:61
constexpr std::uint32_t tfFillOrKill
Definition TxFlags.h:81
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
std::string transToken(TER code)
Definition TER.cpp:243
@ current
This was a new validation and was added.
constexpr std::uint32_t tfClearAccountCreateAmount
Definition TxFlags.h:247
@ temBAD_ISSUER
Definition TER.h:74
@ temINVALID_FLAG
Definition TER.h:92
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
Definition TER.h:116
@ temMALFORMED
Definition TER.h:68
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition TER.h:115
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition TER.h:114
@ temDISABLED
Definition TER.h:95
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
Definition TER.h:117
@ temBAD_AMOUNT
Definition TER.h:70
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition TER.h:112
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition TER.h:328
@ tecUNFUNDED_PAYMENT
Definition TER.h:267
@ tecNO_ENTRY
Definition TER.h:288
@ tecXCHAIN_NO_SIGNERS_LIST
Definition TER.h:326
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition TER.h:329
@ tecXCHAIN_NO_CLAIM_ID
Definition TER.h:319
@ tecXCHAIN_BAD_CLAIM_ID
Definition TER.h:320
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
Definition TER.h:333
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
Definition TER.h:334
@ tecINSUFFICIENT_RESERVE
Definition TER.h:289
@ tecXCHAIN_SELF_COMMIT
Definition TER.h:332
@ tecXCHAIN_CLAIM_NO_QUORUM
Definition TER.h:321
@ tecXCHAIN_REWARD_MISMATCH
Definition TER.h:325
@ tecNO_PERMISSION
Definition TER.h:287
@ tecDST_TAG_NEEDED
Definition TER.h:291
@ tecNO_ISSUER
Definition TER.h:281
@ tecDUPLICATE
Definition TER.h:297
@ tecHAS_OBLIGATIONS
Definition TER.h:299
@ tecNO_DST
Definition TER.h:272
@ tesSUCCESS
Definition TER.h:226
T reserve(T... args)
T size(T... args)
bool has_happened(STAmount const &amt, STAmount const &reward, bool check_payer=true)
bool check_most_balances(STAmount const &amt, STAmount const &reward)
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payer, jtx::Account const *payees, size_t num_payees, bool withClaim)
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payer, std::vector< jtx::Account > const &payees, bool withClaim)
bool payees_received(STAmount const &reward) const
std::vector< balance > reward_accounts
STAmount diff() const
Balance(T &env, jtx::Account const &account)
jtx::Account const & account_
std::vector< jtx::signer > const & signers
void initBridge(ENV &mcEnv, ENV &scEnv)
SEnv & enableFeature(uint256 const feature)
std::shared_ptr< SLE const > account(jtx::Account const &account)
std::shared_ptr< SLE const > caClaimID(Json::Value const &jvb, std::uint64_t seq)
STAmount balance(jtx::Account const &account, Issue const &issue) const
SEnv(T &s, std::unique_ptr< Config > config, FeatureBitset features, std::unique_ptr< Logs > logs=nullptr, beast::severities::Severity thresh=beast::severities::kError)
SEnv & tx(JsonValue &&jv, FN const &... fN)
SEnv & multiTx(jtx::JValueVec &&jvv, FN const &... fN)
XRPAmount reserve(std::uint32_t count)
XRPAmount txFee()
SEnv & fund(STAmount const &amount, Arg const &arg, Args const &... args)
SEnv & disableFeature(uint256 const feature)
TER ter() const
STAmount balance(jtx::Account const &account) const
std::shared_ptr< SLE const > bridge(Json::Value const &jvb)
std::uint64_t claimCount(Json::Value const &jvb)
std::shared_ptr< SLE const > claimID(Json::Value const &jvb, std::uint64_t seq)
std::uint64_t claimID(Json::Value const &jvb)
std::array< bool, num_signers > attested
bool verify(ENV &env, jtx::Account const &acct) const
void init(ENV &env, jtx::Account const &acct)
std::map< BridgeID, BridgeCounters > counters
void spend(jtx::Account const &acct, STAmount amt, std::uint64_t times=1)
void spendFee(jtx::Account const &acct, size_t times=1)
void sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec &claims)
uint32_t sendCreateAttestations(size_t signer_idx, BridgeID bridge, CreateClaimVec &claims)
std::map< jtx::Account, AccountStateTrack > accounts
void transfer(jtx::Account const &from, jtx::Account const &to, STAmount amt)
void init(jtx::Account const &acct)
void receive(jtx::Account const &acct, STAmount amt, std::uint64_t divisor=1)
std::array< bool, num_signers > attested
void xfer(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, Transfer transfer)
void ac(uint64_t time, std::shared_ptr< ChainStateTracker > const &chainstate, BridgeDef const &bridge, AccountCreate ac)
void run() override
Runs the suite.
static constexpr size_t num_signers
void runSimulation(std::shared_ptr< ChainStateTracker > const &st, bool verify_balances=true)
void run() override
Runs the suite.
XRPAmount reserve(std::uint32_t count)
void testXChainAddClaimNonBatchAttestation()
void testXChainAddAccountCreateNonBatchAttestation()
XEnv(T &s, bool side=false)
STAmount const & value() const
std::vector< signer > const signers
Json::Value create_bridge(Account const &acc, Json::Value const &bridge=Json::nullValue, STAmount const &_reward=XRP(1), std::optional< STAmount > const &minAccountCreate=std::nullopt)
std::vector< signer > const alt_signers
std::vector< Account > const payees
JValueVec att_create_acct_vec(std::uint64_t createCount, jtx::AnyAmount const &amt, jtx::Account const &dst, std::size_t const numAtts, std::size_t const fromIdx=0)
Set the sequence number on a JTx.
Definition seq.h:15
A signer in a SignerList.
Definition multisign.h:20
T tmpnam(T... args)
T to_string(T... args)
T visit(T... args)