rippled
Loading...
Searching...
No Matches
XChain_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2022 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/Env.h>
22#include <test/jtx/attester.h>
23#include <test/jtx/multisign.h>
24#include <test/jtx/xchain_bridge.h>
25
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/Indexes.h>
29#include <xrpl/protocol/Issue.h>
30#include <xrpl/protocol/SField.h>
31#include <xrpl/protocol/STXChainBridge.h>
32#include <xrpl/protocol/Serializer.h>
33#include <xrpl/protocol/TER.h>
34#include <xrpl/protocol/XChainAttestations.h>
35
36#include <functional>
37#include <limits>
38#include <optional>
39#include <random>
40#include <string>
41#include <tuple>
42#include <variant>
43#include <vector>
44
45namespace ripple::test {
46
47// SEnv class - encapsulate jtx::Env to make it more user-friendly,
48// for example having APIs that return a *this reference so that calls can be
49// chained (fluent interface) allowing to create an environment and use it
50// without encapsulating it in a curly brace block.
51// ---------------------------------------------------------------------------
52template <class T>
53struct SEnv
54{
56
58 T& s,
60 FeatureBitset features,
61 std::unique_ptr<Logs> logs = nullptr,
63 : env_(s, std::move(config), features, std::move(logs), thresh)
64 {
65 }
66
67 SEnv&
69 {
70 env_.close();
71 return *this;
72 }
73
74 SEnv&
75 enableFeature(uint256 const feature)
76 {
77 env_.enableFeature(feature);
78 return *this;
79 }
80
81 SEnv&
82 disableFeature(uint256 const feature)
83 {
84 env_.app().config().features.erase(feature);
85 return *this;
86 }
87
88 template <class Arg, class... Args>
89 SEnv&
90 fund(STAmount const& amount, Arg const& arg, Args const&... args)
91 {
92 env_.fund(amount, arg, args...);
93 return *this;
94 }
95
96 template <class JsonValue, class... FN>
97 SEnv&
98 tx(JsonValue&& jv, FN const&... fN)
99 {
100 env_(std::forward<JsonValue>(jv), fN...);
101 return *this;
102 }
103
104 template <class... FN>
105 SEnv&
106 multiTx(jtx::JValueVec&& jvv, FN const&... fN)
107 {
108 for (auto const& jv : jvv)
109 env_(jv, fN...);
110 return *this;
111 }
112
113 TER
114 ter() const
115 {
116 return env_.ter();
117 }
118
120 balance(jtx::Account const& account) const
121 {
122 return env_.balance(account).value();
123 }
124
126 balance(jtx::Account const& account, Issue const& issue) const
127 {
128 return env_.balance(account, issue).value();
129 }
130
133 {
134 return env_.current()->fees().accountReserve(count);
135 }
136
139 {
140 return env_.current()->fees().base;
141 }
142
144 account(jtx::Account const& account)
145 {
146 return env_.le(account);
147 }
148
150 bridge(Json::Value const& jvb)
151 {
152 STXChainBridge b(jvb);
153
154 auto tryGet =
156 if (auto r = env_.le(keylet::bridge(b, ct)))
157 {
158 if ((*r)[sfXChainBridge] == b)
159 return r;
160 }
161 return nullptr;
162 };
163 if (auto r = tryGet(STXChainBridge::ChainType::locking))
164 return r;
166 }
167
170 {
171 return (*bridge(jvb))[sfXChainAccountClaimCount];
172 }
173
176 {
177 return (*bridge(jvb))[sfXChainClaimID];
178 }
179
182 {
184 }
185
188 {
189 return env_.le(
191 }
192};
193
194// XEnv class used for XChain tests. The only difference with SEnv<T> is that it
195// funds some default accounts, and that it enables `supported_amendments() |
196// FeatureBitset{featureXChainBridge}` by default.
197// -----------------------------------------------------------------------------
198template <class T>
199struct XEnv : public jtx::XChainBridgeObjects, public SEnv<T>
200{
201 XEnv(T& s, bool side = false) : SEnv<T>(s, jtx::envconfig(), features)
202 {
203 using namespace jtx;
204 STAmount xrp_funds{XRP(10000)};
205
206 if (!side)
207 {
208 this->fund(xrp_funds, mcDoor, mcAlice, mcBob, mcCarol, mcGw);
209
210 // Signer's list must match the attestation signers
211 // env_(jtx::signers(mcDoor, quorum, signers));
212 for (auto& s : signers)
213 this->fund(xrp_funds, s.account);
214 }
215 else
216 {
217 this->fund(
218 xrp_funds,
219 scDoor,
220 scAlice,
221 scBob,
222 scCarol,
223 scGw,
225 scReward);
226
227 for (auto& ra : payees)
228 this->fund(xrp_funds, ra);
229
230 for (auto& s : signers)
231 this->fund(xrp_funds, s.account);
232
233 // Signer's list must match the attestation signers
234 // env_(jtx::signers(Account::master, quorum, signers));
235 }
236 this->close();
237 }
238};
239
240// Tracks the xrp balance for one account
241template <class T>
243{
247
248 Balance(T& env, jtx::Account const& account) : account_(account), env_(env)
249 {
250 startAmount = env_.balance(account_);
251 }
252
254 diff() const
255 {
256 return env_.balance(account_) - startAmount;
257 }
258};
259
260// Tracks the xrp balance for multiple accounts involved in a crosss-chain
261// transfer
262template <class T>
264{
266
269 balance payor_; // pays the rewards
270 std::vector<balance> reward_accounts; // receives the reward
272
274 T& env,
275 jtx::Account const& from_acct,
276 jtx::Account const& to_acct,
277 jtx::Account const& payor,
278 jtx::Account const* payees,
279 size_t num_payees,
280 bool withClaim)
281 : from_(env, from_acct)
282 , to_(env, to_acct)
283 , payor_(env, payor)
284 , reward_accounts([&]() {
286 r.reserve(num_payees);
287 for (size_t i = 0; i < num_payees; ++i)
288 r.emplace_back(env, payees[i]);
289 return r;
290 }())
291 , txFees_(withClaim ? env.env_.current()->fees().base : XRPAmount(0))
292 {
293 }
294
296 T& env,
297 jtx::Account const& from_acct,
298 jtx::Account const& to_acct,
299 jtx::Account const& payor,
300 std::vector<jtx::Account> const& payees,
301 bool withClaim)
303 env,
304 from_acct,
305 to_acct,
306 payor,
307 &payees[0],
308 payees.size(),
309 withClaim)
310 {
311 }
312
313 bool
314 payees_received(STAmount const& reward) const
315 {
316 return std::all_of(
317 reward_accounts.begin(),
318 reward_accounts.end(),
319 [&](const balance& b) { return b.diff() == reward; });
320 }
321
322 bool
323 check_most_balances(STAmount const& amt, STAmount const& reward)
324 {
325 return from_.diff() == -amt && to_.diff() == amt &&
326 payees_received(reward);
327 }
328
329 bool
331 STAmount const& amt,
332 STAmount const& reward,
333 bool check_payer = true)
334 {
335 auto reward_cost =
336 multiply(reward, STAmount(reward_accounts.size()), reward.issue());
337 return check_most_balances(amt, reward) &&
338 (!check_payer || payor_.diff() == -(reward_cost + txFees_));
339 }
340
341 bool
343 {
344 return check_most_balances(STAmount(0), STAmount(0)) &&
345 payor_.diff() <= txFees_; // could have paid fee for failed claim
346 }
347};
348
350{
357 uint32_t quorum;
360
361 template <class ENV>
362 void
363 initBridge(ENV& mcEnv, ENV& scEnv)
364 {
366
367 auto const optAccountCreate = [&]() -> std::optional<STAmount> {
368 if (issueA != xrpIssue() || issueB != xrpIssue())
369 return {};
370 return minAccountCreate;
371 }();
372 mcEnv.tx(bridge_create(doorA, jvb, reward, optAccountCreate))
374 .close();
375
376 scEnv.tx(bridge_create(doorB, jvb, reward, optAccountCreate))
378 .close();
379 }
380};
381
384{
387 {
388 return XEnv(*this).env_.current()->fees().accountReserve(count);
389 }
390
393 {
394 return XEnv(*this).env_.current()->fees().base;
395 }
396
397 void
399 {
400 auto jBridge = create_bridge(mcDoor)[sfXChainBridge.jsonName];
401 bool exceptionPresent = false;
402 try
403 {
404 exceptionPresent = false;
405 [[maybe_unused]] STXChainBridge testBridge1(jBridge);
406 }
407 catch (std::exception& ec)
408 {
409 exceptionPresent = true;
410 }
411
412 BEAST_EXPECT(!exceptionPresent);
413
414 try
415 {
416 exceptionPresent = false;
417 jBridge["Extra"] = 1;
418 [[maybe_unused]] STXChainBridge testBridge2(jBridge);
419 }
420 catch ([[maybe_unused]] std::exception& ec)
421 {
422 exceptionPresent = true;
423 }
424
425 BEAST_EXPECT(exceptionPresent);
426 }
427
428 void
430 {
431 XRPAmount res1 = reserve(1);
432
433 using namespace jtx;
434 testcase("Create Bridge");
435
436 // Normal create_bridge => should succeed
437 XEnv(*this).tx(create_bridge(mcDoor)).close();
438
439 // Bridge not owned by one of the door account.
440 XEnv(*this).tx(
442
443 // Create twice on the same account
444 XEnv(*this)
446 .close()
448
449 // Create USD bridge Alice -> Bob ... should succeed
450 XEnv(*this).tx(
452 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
453 ter(tesSUCCESS));
454
455 // Create USD bridge, Alice is both the locking door and locking issue,
456 // ... should fail.
457 XEnv(*this).tx(
459 mcAlice, bridge(mcAlice, mcAlice["USD"], mcBob, mcBob["USD"])),
461
462 // Bridge where the two door accounts are equal.
463 XEnv(*this).tx(
465 mcBob, bridge(mcBob, mcGw["USD"], mcBob, mcGw["USD"])),
467
468 // Both door accounts are on the same chain. This is not allowed.
469 // Although it doesn't violate any invariants, it's not a useful thing
470 // to do and it complicates the "add claim" transactions.
471 XEnv(*this)
473 mcAlice, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])))
474 .close()
476 mcBob, bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"])),
478 .close();
479
480 // Create a bridge on an account with exactly enough balance to
481 // meet the new reserve should succeed
482 XEnv(*this)
483 .fund(res1, mcuDoor) // exact reserve for account + 1 object
484 .close()
486
487 // Create a bridge on an account with no enough balance to meet the
488 // new reserve
489 XEnv(*this)
490 .fund(res1 - 1, mcuDoor) // just short of required reserve
491 .close()
493
494 // Reward amount is non-xrp
495 XEnv(*this).tx(
498
499 // Reward amount is XRP and negative
500 XEnv(*this).tx(
503
504 // Reward amount is 1 xrp => should succeed
506
507 // Min create amount is 1 xrp, mincreate is 1 xrp => should succeed
508 XEnv(*this).tx(
510
511 // Min create amount is non-xrp
512 XEnv(*this).tx(
513 create_bridge(mcDoor, jvb, XRP(1), mcUSD(100)),
515
516 // Min create amount is zero (should fail, currently succeeds)
517 XEnv(*this).tx(
518 create_bridge(mcDoor, jvb, XRP(1), XRP(0)),
520
521 // Min create amount is negative
522 XEnv(*this).tx(
523 create_bridge(mcDoor, jvb, XRP(1), XRP(-1)),
525
526 // coverage test: BridgeCreate::preflight() - create bridge when feature
527 // disabled.
528 {
529 Env env(*this, supported_amendments() - featureXChainBridge);
531 }
532
533 // coverage test: BridgeCreate::preclaim() returns tecNO_ISSUER.
534 XEnv(*this).tx(
536 mcAlice, bridge(mcAlice, mcuAlice["USD"], mcBob, mcBob["USD"])),
538
539 // coverage test: create_bridge transaction with incorrect flag
540 XEnv(*this).tx(
544
545 // coverage test: create_bridge transaction with xchain feature disabled
546 XEnv(*this)
547 .disableFeature(featureXChainBridge)
549 }
550
551 void
553 {
599 using namespace jtx;
600 testcase("Bridge create constraints");
601 XEnv env(*this, true);
602 auto& A = scAlice;
603 auto& B = scBob;
604 auto& C = scCarol;
605 auto AUSD = A["USD"];
606 auto BUSD = B["USD"];
607 auto CUSD = C["USD"];
608 auto GUSD = scGw["USD"];
609 auto AEUR = A["EUR"];
610 auto BEUR = B["EUR"];
611 auto CEUR = C["EUR"];
612 auto GEUR = scGw["EUR"];
613
614 // Accounts to own single brdiges
615 Account const a1("a1");
616 Account const a2("a2");
617 Account const a3("a3");
618 Account const a4("a4");
619 Account const a5("a5");
620 Account const a6("a6");
621
622 env.fund(XRP(10000), a1, a2, a3, a4, a5, a6);
623 env.close();
624
625 // Add a bridge on two different accounts with the same locking and
626 // issuing assets
627 env.tx(create_bridge(a1, bridge(a1, GUSD, B, BUSD))).close();
628 env.tx(create_bridge(a2, bridge(a2, GUSD, B, BUSD))).close();
629
630 // Add the exact same bridge to two different accounts (one locking
631 // account and one issuing)
632 env.tx(create_bridge(a3, bridge(a3, GUSD, a4, a4["USD"]))).close();
633 env.tx(create_bridge(a4, bridge(a3, GUSD, a4, a4["USD"])),
635 .close();
636
637 // Add the exact same bridge to two different accounts (one issuing
638 // account and one locking - opposite order from the test above)
639 env.tx(create_bridge(a5, bridge(a6, GUSD, a5, a5["USD"]))).close();
640 env.tx(create_bridge(a6, bridge(a6, GUSD, a5, a5["USD"])),
642 .close();
643
644 // Test case 1 ~ 5, create bridges
645 auto const goodBridge1 = bridge(A, GUSD, B, BUSD);
646 auto const goodBridge2 = bridge(A, BUSD, C, CUSD);
647 env.tx(create_bridge(B, goodBridge1)).close();
648 // Issuing asset is the same, this is a duplicate
649 env.tx(create_bridge(B, bridge(A, GEUR, B, BUSD)), ter(tecDUPLICATE))
650 .close();
651 env.tx(create_bridge(A, goodBridge2), ter(tesSUCCESS)).close();
652 // Locking asset is the same - this is a duplicate
653 env.tx(create_bridge(A, bridge(A, BUSD, B, BEUR)), ter(tecDUPLICATE))
654 .close();
655 // Locking asset is USD - this is a duplicate even tho it has a
656 // different issuer
657 env.tx(create_bridge(A, bridge(A, CUSD, B, BEUR)), ter(tecDUPLICATE))
658 .close();
659
660 // Test case 6 and 7, commits
661 env.tx(trust(C, BUSD(1000)))
662 .tx(trust(A, BUSD(1000)))
663 .close()
664 .tx(pay(B, C, BUSD(1000)))
665 .close();
666 auto const aBalanceStart = env.balance(A, BUSD);
667 auto const cBalanceStart = env.balance(C, BUSD);
668 env.tx(xchain_commit(C, goodBridge1, 1, BUSD(50))).close();
669 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(0));
670 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50));
671 env.tx(xchain_commit(C, goodBridge2, 1, BUSD(60))).close();
672 BEAST_EXPECT(env.balance(A, BUSD) - aBalanceStart == BUSD(60));
673 BEAST_EXPECT(env.balance(C, BUSD) - cBalanceStart == BUSD(-50 - 60));
674
675 // bridge modify test cases
676 env.tx(bridge_modify(B, goodBridge1, XRP(33), std::nullopt)).close();
677 BEAST_EXPECT((*env.bridge(goodBridge1))[sfSignatureReward] == XRP(33));
678 env.tx(bridge_modify(A, goodBridge2, XRP(44), std::nullopt)).close();
679 BEAST_EXPECT((*env.bridge(goodBridge2))[sfSignatureReward] == XRP(44));
680 }
681
682 void
684 {
685 using namespace jtx;
686 testcase("Create Bridge Matrix");
687
688 // Test all combinations of the following:`
689 // --------------------------------------
690 // - Locking chain is IOU with locking chain door account as issuer
691 // - Locking chain is IOU with issuing chain door account that
692 // exists on the locking chain as issuer
693 // - Locking chain is IOU with issuing chain door account that does
694 // not exists on the locking chain as issuer
695 // - Locking chain is IOU with non-door account (that exists on the
696 // locking chain ledger) as issuer
697 // - Locking chain is IOU with non-door account (that does not exist
698 // exists on the locking chain ledger) as issuer
699 // - Locking chain is XRP
700 // ---------------------------------------------------------------------
701 // - Issuing chain is IOU with issuing chain door account as the
702 // issuer
703 // - Issuing chain is IOU with locking chain door account (that
704 // exists on the issuing chain ledger) as the issuer
705 // - Issuing chain is IOU with locking chain door account (that does
706 // not exist on the issuing chain ledger) as the issuer
707 // - Issuing chain is IOU with non-door account (that exists on the
708 // issuing chain ledger) as the issuer
709 // - Issuing chain is IOU with non-door account (that does not
710 // exists on the issuing chain ledger) as the issuer
711 // - Issuing chain is XRP and issuing chain door account is not the
712 // root account
713 // - Issuing chain is XRP and issuing chain door account is the root
714 // account
715 // ---------------------------------------------------------------------
716 // That's 42 combinations. The only combinations that should succeed
717 // are:
718 // - Locking chain is any IOU,
719 // - Issuing chain is IOU with issuing chain door account as the
720 // issuer
721 // Locking chain is XRP,
722 // - Issuing chain is XRP with issuing chain is the root account.
723 // ---------------------------------------------------------------------
724 Account a("a"), b("b");
725 Issue ia, ib;
726
727 std::tuple lcs{
729 "Locking chain is IOU(locking chain door)",
730 [&](auto& env, bool) {
731 a = mcDoor;
732 ia = mcDoor["USD"];
733 }),
735 "Locking chain is IOU(issuing chain door funded on locking "
736 "chain)",
737 [&](auto& env, bool shouldFund) {
738 a = mcDoor;
739 ia = scDoor["USD"];
740 if (shouldFund)
741 env.fund(XRP(10000), scDoor);
742 }),
744 "Locking chain is IOU(issuing chain door account unfunded "
745 "on locking chain)",
746 [&](auto& env, bool) {
747 a = mcDoor;
748 ia = scDoor["USD"];
749 }),
751 "Locking chain is IOU(bob funded on locking chain)",
752 [&](auto& env, bool) {
753 a = mcDoor;
754 ia = mcGw["USD"];
755 }),
757 "Locking chain is IOU(bob unfunded on locking chain)",
758 [&](auto& env, bool) {
759 a = mcDoor;
760 ia = mcuGw["USD"];
761 }),
762 std::make_pair("Locking chain is XRP", [&](auto& env, bool) {
763 a = mcDoor;
764 ia = xrpIssue();
765 })};
766
767 std::tuple ics{
769 "Issuing chain is IOU(issuing chain door account)",
770 [&](auto& env, bool) {
771 b = scDoor;
772 ib = scDoor["USD"];
773 }),
775 "Issuing chain is IOU(locking chain door funded on issuing "
776 "chain)",
777 [&](auto& env, bool shouldFund) {
778 b = scDoor;
779 ib = mcDoor["USD"];
780 if (shouldFund)
781 env.fund(XRP(10000), mcDoor);
782 }),
784 "Issuing chain is IOU(locking chain door unfunded on "
785 "issuing chain)",
786 [&](auto& env, bool) {
787 b = scDoor;
788 ib = mcDoor["USD"];
789 }),
791 "Issuing chain is IOU(bob funded on issuing chain)",
792 [&](auto& env, bool) {
793 b = scDoor;
794 ib = mcGw["USD"];
795 }),
797 "Issuing chain is IOU(bob unfunded on issuing chain)",
798 [&](auto& env, bool) {
799 b = scDoor;
800 ib = mcuGw["USD"];
801 }),
803 "Issuing chain is XRP and issuing chain door account is "
804 "not the root account",
805 [&](auto& env, bool) {
806 b = scDoor;
807 ib = xrpIssue();
808 }),
810 "Issuing chain is XRP and issuing chain door account is "
811 "the root account ",
812 [&](auto& env, bool) {
813 b = Account::master;
814 ib = xrpIssue();
815 })};
816
817 std::vector<std::pair<int, int>> expected_result{
860
862
863 auto testcase = [&](auto const& lc, auto const& ic) {
864 XEnv mcEnv(*this);
865 XEnv scEnv(*this, true);
866
867 lc.second(mcEnv, true);
868 lc.second(scEnv, false);
869
870 ic.second(mcEnv, false);
871 ic.second(scEnv, true);
872
873 auto const& expected = expected_result[test_result.size()];
874
875 mcEnv.tx(
876 create_bridge(a, bridge(a, ia, b, ib)),
877 ter(TER::fromInt(expected.first)));
878 TER mcTER = mcEnv.env_.ter();
879
880 scEnv.tx(
881 create_bridge(b, bridge(a, ia, b, ib)),
882 ter(TER::fromInt(expected.second)));
883 TER scTER = scEnv.env_.ter();
884
885 bool pass = mcTER == tesSUCCESS && scTER == tesSUCCESS;
886
887 test_result.emplace_back(mcTER, scTER, pass);
888 };
889
890 auto apply_ics = [&](auto const& lc, auto const& ics) {
892 [&](auto const&... ic) { (testcase(lc, ic), ...); }, ics);
893 };
894
895 std::apply([&](auto const&... lc) { (apply_ics(lc, ics), ...); }, lcs);
896
897#if GENERATE_MTX_OUTPUT
898 // optional output of matrix results in markdown format
899 // ----------------------------------------------------
900 std::string fname{std::tmpnam(nullptr)};
901 fname += ".md";
902 std::cout << "Markdown output for matrix test: " << fname << "\n";
903
904 auto print_res = [](auto tup) -> std::string {
905 std::string status = std::string(transToken(std::get<0>(tup))) +
906 " / " + transToken(std::get<1>(tup));
907
908 if (std::get<2>(tup))
909 return status;
910 else
911 {
912 // red
913 return std::string("`") + status + "`";
914 }
915 };
916
917 auto output_table = [&](auto print_res) {
918 size_t test_idx = 0;
919 std::string res;
920 res.reserve(10000); // should be enough :-)
921
922 // first two header lines
923 res += "| `issuing ->` | ";
925 [&](auto const&... ic) {
926 ((res += ic.first, res += " | "), ...);
927 },
928 ics);
929 res += "\n";
930
931 res += "| :--- | ";
933 [&](auto const&... ic) {
934 (((void)ic.first, res += ":---: | "), ...);
935 },
936 ics);
937 res += "\n";
938
939 auto output = [&](auto const& lc, auto const& ic) {
940 res += print_res(test_result[test_idx]);
941 res += " | ";
942 ++test_idx;
943 };
944
945 auto output_ics = [&](auto const& lc, auto const& ics) {
946 res += "| ";
947 res += lc.first;
948 res += " | ";
950 [&](auto const&... ic) { (output(lc, ic), ...); }, ics);
951 res += "\n";
952 };
953
955 [&](auto const&... lc) { (output_ics(lc, ics), ...); }, lcs);
956
957 return res;
958 };
959
960 std::ofstream(fname) << output_table(print_res);
961
962 std::string ter_fname{std::tmpnam(nullptr)};
963 std::cout << "ter output for matrix test: " << ter_fname << "\n";
964
965 std::ofstream ofs(ter_fname);
966 for (auto& t : test_result)
967 {
968 ofs << "{ " << std::string(transToken(std::get<0>(t))) << ", "
969 << std::string(transToken(std::get<1>(t))) << "}\n,";
970 }
971#endif
972 }
973
974 void
976 {
977 using namespace jtx;
978 testcase("Modify Bridge");
979
980 // Changing a non-existent bridge should fail
981 XEnv(*this).tx(
983 mcAlice,
984 bridge(mcAlice, mcGw["USD"], mcBob, mcBob["USD"]),
985 XRP(2),
986 std::nullopt),
988
989 // must change something
990 // XEnv(*this)
991 // .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
992 // .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(1)),
993 // ter(temMALFORMED));
994
995 // must change something
996 XEnv(*this)
997 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1)))
998 .close()
1000
1001 // Reward amount is non-xrp
1002 XEnv(*this).tx(
1003 bridge_modify(mcDoor, jvb, mcUSD(2), XRP(10)),
1005
1006 // Reward amount is XRP and negative
1007 XEnv(*this).tx(
1008 bridge_modify(mcDoor, jvb, XRP(-2), XRP(10)),
1010
1011 // Min create amount is non-xrp
1012 XEnv(*this).tx(
1013 bridge_modify(mcDoor, jvb, XRP(2), mcUSD(10)),
1015
1016 // Min create amount is zero
1017 XEnv(*this).tx(
1018 bridge_modify(mcDoor, jvb, XRP(2), XRP(0)),
1020
1021 // Min create amount is negative
1022 XEnv(*this).tx(
1023 bridge_modify(mcDoor, jvb, XRP(2), XRP(-10)),
1025
1026 // First check the regular claim process (without bridge_modify)
1027 for (auto withClaim : {false, true})
1028 {
1029 XEnv mcEnv(*this);
1030 XEnv scEnv(*this, true);
1031
1032 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1033
1036 .close()
1038 .close();
1039
1040 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1041 auto const amt = XRP(1000);
1042 std::uint32_t const claimID = 1;
1043 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1044
1045 BalanceTransfer transfer(
1046 scEnv,
1048 scBob,
1049 scAlice,
1050 &payees[0],
1052 withClaim);
1053
1054 scEnv
1056 scAttester,
1057 jvb,
1058 mcAlice,
1059 amt,
1060 payees,
1061 true,
1062 claimID,
1063 dst,
1064 signers))
1065 .close();
1066
1067 if (withClaim)
1068 {
1069 BEAST_EXPECT(transfer.has_not_happened());
1070
1071 // need to submit a claim transactions
1072 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1073 .close();
1074 }
1075
1076 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1077 }
1078
1079 // Check that the reward paid from a claim Id was the reward when
1080 // the claim id was created, not the reward since the bridge was
1081 // modified.
1082 for (auto withClaim : {false, true})
1083 {
1084 XEnv mcEnv(*this);
1085 XEnv scEnv(*this, true);
1086
1087 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1088
1091 .close()
1093 .close();
1094
1095 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1096 auto const amt = XRP(1000);
1097 std::uint32_t const claimID = 1;
1098 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1099
1100 // Now modify the reward on the bridge
1101 mcEnv.tx(bridge_modify(mcDoor, jvb, XRP(2), XRP(10))).close();
1102 scEnv.tx(bridge_modify(Account::master, jvb, XRP(2), XRP(10)))
1103 .close();
1104
1105 BalanceTransfer transfer(
1106 scEnv,
1108 scBob,
1109 scAlice,
1110 &payees[0],
1112 withClaim);
1113
1114 scEnv
1116 scAttester,
1117 jvb,
1118 mcAlice,
1119 amt,
1120 payees,
1121 true,
1122 claimID,
1123 dst,
1124 signers))
1125 .close();
1126
1127 if (withClaim)
1128 {
1129 BEAST_EXPECT(transfer.has_not_happened());
1130
1131 // need to submit a claim transactions
1132 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1133 .close();
1134 }
1135
1136 // make sure the reward accounts indeed received the original
1137 // split reward (1 split 5 ways) instead of the updated 2 XRP.
1138 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
1139 }
1140
1141 // Check that the signatures used to verify attestations and decide
1142 // if there is a quorum are the current signer's list on the door
1143 // account, not the signer's list that was in effect when the claim
1144 // id was created.
1145 for (auto withClaim : {false, true})
1146 {
1147 XEnv mcEnv(*this);
1148 XEnv scEnv(*this, true);
1149
1150 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1151
1154 .close()
1156 .close();
1157
1158 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1159 auto const amt = XRP(1000);
1160 std::uint32_t const claimID = 1;
1161 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1162
1163 // change signers - claim should not be processed is the batch
1164 // is signed by original signers
1166 .close();
1167
1168 BalanceTransfer transfer(
1169 scEnv,
1171 scBob,
1172 scAlice,
1173 &payees[0],
1175 withClaim);
1176
1177 // submit claim using outdated signers - should fail
1178 scEnv
1179 .multiTx(
1181 scAttester,
1182 jvb,
1183 mcAlice,
1184 amt,
1185 payees,
1186 true,
1187 claimID,
1188 dst,
1189 signers),
1191 .close();
1192 if (withClaim)
1193 {
1194 // need to submit a claim transactions
1195 scEnv
1196 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1198 .close();
1199 }
1200
1201 // make sure transfer has not happened as we sent attestations
1202 // using outdated signers
1203 BEAST_EXPECT(transfer.has_not_happened());
1204
1205 // submit claim using current signers - should succeed
1206 scEnv
1208 scAttester,
1209 jvb,
1210 mcAlice,
1211 amt,
1212 payees,
1213 true,
1214 claimID,
1215 dst,
1216 alt_signers))
1217 .close();
1218 if (withClaim)
1219 {
1220 BEAST_EXPECT(transfer.has_not_happened());
1221
1222 // need to submit a claim transactions
1223 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1224 .close();
1225 }
1226
1227 // make sure the transfer went through as we sent attestations
1228 // using new signers
1229 BEAST_EXPECT(
1230 transfer.has_happened(amt, split_reward_quorum, false));
1231 }
1232
1233 // coverage test: bridge_modify transaction with incorrect flag
1234 XEnv(*this)
1236 .close()
1237 .tx(bridge_modify(mcDoor, jvb, XRP(1), XRP(2)),
1240
1241 // coverage test: bridge_modify transaction with xchain feature
1242 // disabled
1243 XEnv(*this)
1245 .disableFeature(featureXChainBridge)
1246 .close()
1248
1249 // coverage test: bridge_modify return temSIDECHAIN_NONDOOR_OWNER;
1250 XEnv(*this)
1252 .close()
1253 .tx(bridge_modify(mcAlice, jvb, XRP(1), XRP(2)),
1255
1262 XEnv(*this)
1263 .tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20)))
1264 .close()
1266 mcAlice, jvb, scuAlice, XRP(100), reward))
1267 .close()
1268 .tx(bridge_modify(mcDoor, jvb, {}, XRP(2)),
1271 .close()
1272 .tx(bridge_modify(mcDoor, jvb, XRP(3), {}),
1274 .close()
1276 mcAlice, jvb, scuBob, XRP(100), XRP(3)),
1278 .close();
1279 }
1280
1281 void
1283 {
1284 using namespace jtx;
1285 XRPAmount res1 = reserve(1);
1286 XRPAmount tx_fee = txFee();
1287
1288 testcase("Create ClaimID");
1289
1290 // normal bridge create for sanity check with the exact necessary
1291 // account balance
1292 XEnv(*this, true)
1294 .fund(res1, scuAlice) // acct reserve + 1 object
1295 .close()
1297 .close();
1298
1299 // check reward not deducted when claim id is created
1300 {
1301 XEnv xenv(*this, true);
1302
1303 Balance scAlice_bal(xenv, scAlice);
1304
1307 .close();
1308
1309 BEAST_EXPECT(scAlice_bal.diff() == -tx_fee);
1310 }
1311
1312 // Non-existent bridge
1313 XEnv(*this, true)
1315 scAlice,
1316 bridge(mcAlice, mcAlice["USD"], scBob, scBob["USD"]),
1317 reward,
1318 mcAlice),
1320 .close();
1321
1322 // Creating the new object would put the account below the reserve
1323 XEnv(*this, true)
1325 .fund(res1 - xrp_dust, scuAlice) // barely not enough
1326 .close()
1329 .close();
1330
1331 // The specified reward doesn't match the reward on the bridge (test
1332 // by giving the reward amount for the other side, as well as a
1333 // completely non-matching reward)
1334 XEnv(*this, true)
1336 .close()
1340 .close();
1341
1342 // A reward amount that isn't XRP
1343 XEnv(*this, true)
1345 .close()
1348 .close();
1349
1350 // coverage test: xchain_create_claim_id transaction with incorrect
1351 // flag
1352 XEnv(*this, true)
1354 .close()
1358 .close();
1359
1360 // coverage test: xchain_create_claim_id transaction with xchain
1361 // feature disabled
1362 XEnv(*this, true)
1364 .disableFeature(featureXChainBridge)
1365 .close()
1368 .close();
1369 }
1370
1371 void
1373 {
1374 using namespace jtx;
1375 XRPAmount res0 = reserve(0);
1376 XRPAmount tx_fee = txFee();
1377
1378 testcase("Commit");
1379
1380 // Commit to a non-existent bridge
1381 XEnv(*this).tx(
1383
1384 // check that reward not deducted when doing the commit
1385 {
1386 XEnv xenv(*this);
1387
1388 Balance alice_bal(xenv, mcAlice);
1389 auto const amt = XRP(1000);
1390
1391 xenv.tx(create_bridge(mcDoor, jvb))
1392 .close()
1393 .tx(xchain_commit(mcAlice, jvb, 1, amt, scBob))
1394 .close();
1395
1396 STAmount claim_cost = amt;
1397 BEAST_EXPECT(alice_bal.diff() == -(claim_cost + tx_fee));
1398 }
1399
1400 // Commit a negative amount
1401 XEnv(*this)
1403 .close()
1404 .tx(xchain_commit(mcAlice, jvb, 1, XRP(-1), scBob),
1406
1407 // Commit an amount whose issue that does not match the expected
1408 // issue on the bridge (either LockingChainIssue or
1409 // IssuingChainIssue, depending on the chain).
1410 XEnv(*this)
1412 .close()
1413 .tx(xchain_commit(mcAlice, jvb, 1, mcUSD(100), scBob),
1415
1416 // Commit an amount that would put the sender below the required
1417 // reserve (if XRP)
1418 XEnv(*this)
1420 .fund(res0 + one_xrp - xrp_dust, mcuAlice) // barely not enough
1421 .close()
1424
1425 XEnv(*this)
1427 .fund(
1428 res0 + one_xrp + xrp_dust, // "xrp_dust" for tx fees
1429 mcuAlice) // exactly enough => should succeed
1430 .close()
1432
1433 // Commit an amount above the account's balance (for both XRP and
1434 // IOUs)
1435 XEnv(*this)
1437 .fund(res0, mcuAlice) // barely not enough
1438 .close()
1439 .tx(xchain_commit(mcuAlice, jvb, 1, res0 + one_xrp, scBob),
1441
1442 auto jvb_USD = bridge(mcDoor, mcUSD, scGw, scUSD);
1443
1444 // commit sent from iou issuer (mcGw) succeeds - should it?
1445 XEnv(*this)
1446 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1447 .tx(create_bridge(mcDoor, jvb_USD))
1448 .close()
1449 .tx(xchain_commit(mcGw, jvb_USD, 1, mcUSD(1), scBob));
1450
1451 // commit to a door account from the door account. This should fail.
1452 XEnv(*this)
1453 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1454 .tx(create_bridge(mcDoor, jvb_USD))
1455 .close()
1456 .tx(xchain_commit(mcDoor, jvb_USD, 1, mcUSD(1), scBob),
1458
1459 // commit sent from mcAlice which has no IOU balance => should fail
1460 XEnv(*this)
1461 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1462 .tx(create_bridge(mcDoor, jvb_USD))
1463 .close()
1464 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scBob),
1465 ter(terNO_LINE));
1466
1467 // commit sent from mcAlice which has no IOU balance => should fail
1468 // just changed the destination to scGw (which is the door account
1469 // and may not make much sense)
1470 XEnv(*this)
1471 .tx(trust(mcDoor, mcUSD(10000))) // door needs to have a trustline
1472 .tx(create_bridge(mcDoor, jvb_USD))
1473 .close()
1474 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(1), scGw),
1475 ter(terNO_LINE));
1476
1477 // commit sent from mcAlice which has a IOU balance => should
1478 // succeed
1479 XEnv(*this)
1480 .tx(trust(mcDoor, mcUSD(10000)))
1481 .tx(trust(mcAlice, mcUSD(10000)))
1482 .close()
1483 .tx(pay(mcGw, mcAlice, mcUSD(10)))
1484 .tx(create_bridge(mcDoor, jvb_USD))
1485 .close()
1486 //.tx(pay(mcAlice, mcDoor, mcUSD(10)));
1487 .tx(xchain_commit(mcAlice, jvb_USD, 1, mcUSD(10), scAlice));
1488
1489 // coverage test: xchain_commit transaction with incorrect flag
1490 XEnv(*this)
1492 .close()
1496
1497 // coverage test: xchain_commit transaction with xchain feature
1498 // disabled
1499 XEnv(*this)
1501 .disableFeature(featureXChainBridge)
1502 .close()
1504 ter(temDISABLED));
1505 }
1506
1507 void
1509 {
1510 using namespace jtx;
1511
1512 testcase("Add Attestation");
1513 XRPAmount res0 = reserve(0);
1514 XRPAmount tx_fee = txFee();
1515
1516 auto multiTtxFee = [&](std::uint32_t m) -> STAmount {
1517 return multiply(tx_fee, STAmount(m), xrpIssue());
1518 };
1519
1520 // Add an attestation to a claim id that has already reached quorum.
1521 // This should succeed and share in the reward.
1522 // note: this is true only when either:
1523 // 1. dest account is not specified, so transfer requires a claim
1524 // 2. or the extra attestation is sent in the same batch as the
1525 // one reaching quorum
1526 for (auto withClaim : {true})
1527 {
1528 XEnv mcEnv(*this);
1529 XEnv scEnv(*this, true);
1530 std::uint32_t const claimID = 1;
1531
1532 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1533
1536 .close()
1538 .close();
1539
1540 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1541
1542 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1543 auto const amt = XRP(1000);
1544 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1545
1546 BalanceTransfer transfer(
1547 scEnv, Account::master, scBob, scAlice, payees, withClaim);
1548
1549 scEnv
1551 scAttester,
1552 jvb,
1553 mcAlice,
1554 amt,
1555 payees,
1556 true,
1557 claimID,
1558 dst,
1559 signers,
1561 .close();
1562 scEnv
1564 scAttester,
1565 jvb,
1566 mcAlice,
1567 amt,
1569 true,
1570 claimID,
1571 dst,
1573 .close();
1574
1575 if (withClaim)
1576 {
1577 BEAST_EXPECT(transfer.has_not_happened());
1578
1579 // need to submit a claim transactions
1580 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1581 .close();
1582 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1583 BEAST_EXPECT(scEnv.claimID(jvb) == claimID);
1584 }
1585
1586 BEAST_EXPECT(transfer.has_happened(amt, split_reward_everyone));
1587 }
1588
1589 // Test that signature weights are correctly handled. Assign
1590 // signature weights of 1,2,4,4 and a quorum of 7. Check that the
1591 // 4,4 signatures reach a quorum, the 1,2,4, reach a quorum, but the
1592 // 4,2, 4,1 and 1,2 do not.
1593
1594 // 1,2,4 => should succeed
1595 for (auto withClaim : {false, true})
1596 {
1597 XEnv mcEnv(*this);
1598 XEnv scEnv(*this, true);
1599
1600 std::uint32_t const quorum_7 = 7;
1601 std::vector<signer> const signers_ = [] {
1602 constexpr int numSigners = 4;
1603 std::uint32_t weights[] = {1, 2, 4, 4};
1604
1605 std::vector<signer> result;
1606 result.reserve(numSigners);
1607 for (int i = 0; i < numSigners; ++i)
1608 {
1609 using namespace std::literals;
1610 auto const a = Account("signer_"s + std::to_string(i));
1611 result.emplace_back(a, weights[i]);
1612 }
1613 return result;
1614 }();
1615
1616 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1617
1619 .tx(jtx::signers(Account::master, quorum_7, signers_))
1620 .close()
1622 .close();
1623 std::uint32_t const claimID = 1;
1624 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1625
1626 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1627 auto const amt = XRP(1000);
1628
1629 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1630
1631 BalanceTransfer transfer(
1632 scEnv,
1634 scBob,
1635 scAlice,
1636 &payees[0],
1637 3,
1638 withClaim);
1639
1640 scEnv
1642 scAttester,
1643 jvb,
1644 mcAlice,
1645 amt,
1646 payees,
1647 true,
1648 claimID,
1649 dst,
1650 signers_,
1651 3))
1652 .close();
1653
1654 if (withClaim)
1655 {
1656 BEAST_EXPECT(transfer.has_not_happened());
1657
1658 // need to submit a claim transactions
1659 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1660 .close();
1661 }
1662
1663 BEAST_EXPECT(!scEnv.claimID(jvb, 1)); // claim id deleted
1664
1665 BEAST_EXPECT(transfer.has_happened(
1666 amt, divide(reward, STAmount(3), reward.issue())));
1667 }
1668
1669 // 4,4 => should succeed
1670 for (auto withClaim : {false, true})
1671 {
1672 XEnv mcEnv(*this);
1673 XEnv scEnv(*this, true);
1674
1675 std::uint32_t const quorum_7 = 7;
1676 std::vector<signer> const signers_ = [] {
1677 constexpr int numSigners = 4;
1678 std::uint32_t weights[] = {1, 2, 4, 4};
1679
1680 std::vector<signer> result;
1681 result.reserve(numSigners);
1682 for (int i = 0; i < numSigners; ++i)
1683 {
1684 using namespace std::literals;
1685 auto const a = Account("signer_"s + std::to_string(i));
1686 result.emplace_back(a, weights[i]);
1687 }
1688 return result;
1689 }();
1690 STAmount const split_reward_ =
1691 divide(reward, STAmount(signers_.size()), reward.issue());
1692
1693 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1694
1696 .tx(jtx::signers(Account::master, quorum_7, signers_))
1697 .close()
1699 .close();
1700 std::uint32_t const claimID = 1;
1701 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1702
1703 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1704 auto const amt = XRP(1000);
1705
1706 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1707
1708 BalanceTransfer transfer(
1709 scEnv,
1711 scBob,
1712 scAlice,
1713 &payees[2],
1714 2,
1715 withClaim);
1716
1717 scEnv
1719 scAttester,
1720 jvb,
1721 mcAlice,
1722 amt,
1723 payees,
1724 true,
1725 claimID,
1726 dst,
1727 signers_,
1728 2,
1729 2))
1730 .close();
1731
1732 if (withClaim)
1733 {
1734 BEAST_EXPECT(transfer.has_not_happened());
1735
1736 // need to submit a claim transactions
1737 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
1738 .close();
1739 }
1740
1741 BEAST_EXPECT(!scEnv.claimID(jvb, claimID)); // claim id deleted
1742
1743 BEAST_EXPECT(transfer.has_happened(
1744 amt, divide(reward, STAmount(2), reward.issue())));
1745 }
1746
1747 // 1,2 => should fail
1748 for (auto withClaim : {false, true})
1749 {
1750 XEnv mcEnv(*this);
1751 XEnv scEnv(*this, true);
1752
1753 std::uint32_t const quorum_7 = 7;
1754 std::vector<signer> const signers_ = [] {
1755 constexpr int numSigners = 4;
1756 std::uint32_t weights[] = {1, 2, 4, 4};
1757
1758 std::vector<signer> result;
1759 result.reserve(numSigners);
1760 for (int i = 0; i < numSigners; ++i)
1761 {
1762 using namespace std::literals;
1763 auto const a = Account("signer_"s + std::to_string(i));
1764 result.emplace_back(a, weights[i]);
1765 }
1766 return result;
1767 }();
1768
1769 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1770
1772 .tx(jtx::signers(Account::master, quorum_7, signers_))
1773 .close()
1775 .close();
1776
1777 std::uint32_t const claimID = 1;
1778 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1779
1780 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1781 auto const amt = XRP(1000);
1782 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1783
1784 BalanceTransfer transfer(
1785 scEnv,
1787 scBob,
1788 scAlice,
1789 &payees[0],
1790 2,
1791 withClaim);
1792
1793 scEnv
1795 scAttester,
1796 jvb,
1797 mcAlice,
1798 amt,
1799 payees,
1800 true,
1801 claimID,
1802 dst,
1803 signers_,
1804 2))
1805 .close();
1806 if (withClaim)
1807 {
1808 BEAST_EXPECT(transfer.has_not_happened());
1809
1810 // need to submit a claim transactions
1811 scEnv
1812 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1814 .close();
1815 }
1816
1817 BEAST_EXPECT(
1818 !!scEnv.claimID(jvb, claimID)); // claim id still present
1819 BEAST_EXPECT(transfer.has_not_happened());
1820 }
1821
1822 // 2,4 => should fail
1823 for (auto withClaim : {false, true})
1824 {
1825 XEnv mcEnv(*this);
1826 XEnv scEnv(*this, true);
1827
1828 std::uint32_t const quorum_7 = 7;
1829 std::vector<signer> const signers_ = [] {
1830 constexpr int numSigners = 4;
1831 std::uint32_t weights[] = {1, 2, 4, 4};
1832
1833 std::vector<signer> result;
1834 result.reserve(numSigners);
1835 for (int i = 0; i < numSigners; ++i)
1836 {
1837 using namespace std::literals;
1838 auto const a = Account("signer_"s + std::to_string(i));
1839 result.emplace_back(a, weights[i]);
1840 }
1841 return result;
1842 }();
1843
1844 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
1845
1847 .tx(jtx::signers(Account::master, quorum_7, signers_))
1848 .close()
1850 .close();
1851
1852 std::uint32_t const claimID = 1;
1853 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
1854
1855 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
1856 auto const amt = XRP(1000);
1857
1858 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
1859
1860 BalanceTransfer transfer(
1861 scEnv,
1863 scBob,
1864 scAlice,
1865 &payees[1],
1866 2,
1867 withClaim);
1868
1869 scEnv
1871 scAttester,
1872 jvb,
1873 mcAlice,
1874 amt,
1875 payees,
1876 true,
1877 claimID,
1878 dst,
1879 signers_,
1880 2,
1881 1))
1882 .close();
1883
1884 if (withClaim)
1885 {
1886 BEAST_EXPECT(transfer.has_not_happened());
1887
1888 // need to submit a claim transactions
1889 scEnv
1890 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
1892 .close();
1893 }
1894
1895 BEAST_EXPECT(
1896 !!scEnv.claimID(jvb, claimID)); // claim id still present
1897 BEAST_EXPECT(transfer.has_not_happened());
1898 }
1899
1900 // Confirm that account create transactions happen in the correct
1901 // order. If they reach quorum out of order they should not execute
1902 // until all the previous created transactions have occurred.
1903 // Re-adding an attestation should move funds.
1904 {
1905 XEnv mcEnv(*this);
1906 XEnv scEnv(*this, true);
1907 auto const amt = XRP(1000);
1908 auto const amt_plus_reward = amt + reward;
1909
1910 {
1911 Balance door(mcEnv, mcDoor);
1912 Balance carol(mcEnv, mcCarol);
1913
1914 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
1915 .close()
1917 mcAlice, jvb, scuAlice, amt, reward))
1919 mcBob, jvb, scuBob, amt, reward))
1921 mcCarol, jvb, scuCarol, amt, reward))
1922 .close();
1923
1924 BEAST_EXPECT(
1925 door.diff() ==
1926 (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
1927 tx_fee));
1928 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
1929 }
1930
1933 .close();
1934
1935 {
1936 // send first batch of account create attest for all 3
1937 // account create
1938 Balance attester(scEnv, scAttester);
1939 Balance door(scEnv, Account::master);
1940
1941 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2))
1943 .multiTx(att_create_acct_vec(2, amt, scuBob, 2))
1944 .close();
1945
1946 BEAST_EXPECT(door.diff() == STAmount(0));
1947 // att_create_acct_vec return vectors of size 2, so 2*3 txns
1948 BEAST_EXPECT(attester.diff() == -multiTtxFee(6));
1949
1950 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // ca claim id present
1951 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1952 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1953 BEAST_EXPECT(
1954 scEnv.claimCount(jvb) == 0); // claim count still 0
1955 }
1956
1957 {
1958 // complete attestations for 2nd account create => should
1959 // not complete
1960 Balance attester(scEnv, scAttester);
1961 Balance door(scEnv, Account::master);
1962
1963 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 3, 2))
1964 .close();
1965
1966 BEAST_EXPECT(door.diff() == STAmount(0));
1967 // att_create_acct_vec return vectors of size 3, so 3 txns
1968 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1969
1970 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // ca claim id present
1971 BEAST_EXPECT(
1972 scEnv.claimCount(jvb) == 0); // claim count still 0
1973 }
1974
1975 {
1976 // complete attestations for 3rd account create => should
1977 // not complete
1978 Balance attester(scEnv, scAttester);
1979 Balance door(scEnv, Account::master);
1980
1981 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
1982 .close();
1983
1984 BEAST_EXPECT(door.diff() == STAmount(0));
1985 // att_create_acct_vec return vectors of size 3, so 3 txns
1986 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
1987
1988 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // ca claim id present
1989 BEAST_EXPECT(
1990 scEnv.claimCount(jvb) == 0); // claim count still 0
1991 }
1992
1993 {
1994 // complete attestations for 1st account create => account
1995 // should be created
1996 Balance attester(scEnv, scAttester);
1997 Balance door(scEnv, Account::master);
1998
1999 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 3, 1))
2000 .close();
2001
2002 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2003 // att_create_acct_vec return vectors of size 3, so 3 txns
2004 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2005 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2006
2007 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id 1 deleted
2008 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2009 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2010 BEAST_EXPECT(scEnv.claimCount(jvb) == 1); // claim count now 1
2011 }
2012
2013 {
2014 // resend attestations for 3rd account create => still
2015 // should not complete
2016 Balance attester(scEnv, scAttester);
2017 Balance door(scEnv, Account::master);
2018
2019 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 3, 2))
2020 .close();
2021
2022 BEAST_EXPECT(door.diff() == STAmount(0));
2023 // att_create_acct_vec return vectors of size 3, so 3 txns
2024 BEAST_EXPECT(attester.diff() == -multiTtxFee(3));
2025
2026 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 2)); // claim id 2 present
2027 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2028 BEAST_EXPECT(
2029 scEnv.claimCount(jvb) == 1); // claim count still 1
2030 }
2031
2032 {
2033 // resend attestations for 2nd account create => account
2034 // should be created
2035 Balance attester(scEnv, scAttester);
2036 Balance door(scEnv, Account::master);
2037
2038 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1)).close();
2039
2040 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2041 BEAST_EXPECT(attester.diff() == -tx_fee);
2042 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2043
2044 BEAST_EXPECT(!scEnv.caClaimID(jvb, 2)); // claim id 2 deleted
2045 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 3)); // claim id 3 present
2046 BEAST_EXPECT(scEnv.claimCount(jvb) == 2); // claim count now 2
2047 }
2048 {
2049 // resend attestations for 3rc account create => account
2050 // should be created
2051 Balance attester(scEnv, scAttester);
2052 Balance door(scEnv, Account::master);
2053
2054 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1)).close();
2055
2056 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2057 BEAST_EXPECT(attester.diff() == -tx_fee);
2058 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2059
2060 BEAST_EXPECT(!scEnv.caClaimID(jvb, 3)); // claim id 3 deleted
2061 BEAST_EXPECT(scEnv.claimCount(jvb) == 3); // claim count now 3
2062 }
2063 }
2064
2065 // Check that creating an account with less than the minimum reserve
2066 // fails.
2067 {
2068 XEnv mcEnv(*this);
2069 XEnv scEnv(*this, true);
2070
2071 auto const amt = res0 - XRP(1);
2072 auto const amt_plus_reward = amt + reward;
2073
2074 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2075
2076 {
2077 Balance door(mcEnv, mcDoor);
2078 Balance carol(mcEnv, mcCarol);
2079
2080 mcEnv
2082 mcCarol, jvb, scuAlice, amt, reward))
2083 .close();
2084
2085 BEAST_EXPECT(door.diff() == amt_plus_reward);
2086 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2087 }
2088
2091 .close();
2092
2093 Balance attester(scEnv, scAttester);
2094 Balance door(scEnv, Account::master);
2095
2096 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2)).close();
2097 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2098 BEAST_EXPECT(
2099 scEnv.claimCount(jvb) == 0); // claim count is one less
2100
2101 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 2, 2)).close();
2102 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2103 BEAST_EXPECT(
2104 scEnv.claimCount(jvb) == 1); // claim count was incremented
2105
2106 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2107 BEAST_EXPECT(door.diff() == -reward);
2108 BEAST_EXPECT(!scEnv.account(scuAlice));
2109 }
2110
2111 // Check that sending funds with an account create txn to an
2112 // existing account works.
2113 {
2114 XEnv mcEnv(*this);
2115 XEnv scEnv(*this, true);
2116
2117 auto const amt = XRP(111);
2118 auto const amt_plus_reward = amt + reward;
2119
2120 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2121
2122 {
2123 Balance door(mcEnv, mcDoor);
2124 Balance carol(mcEnv, mcCarol);
2125
2126 mcEnv
2128 mcCarol, jvb, scAlice, amt, reward))
2129 .close();
2130
2131 BEAST_EXPECT(door.diff() == amt_plus_reward);
2132 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2133 }
2134
2137 .close();
2138
2139 Balance attester(scEnv, scAttester);
2140 Balance door(scEnv, Account::master);
2141 Balance alice(scEnv, scAlice);
2142
2143 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2144 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2145 BEAST_EXPECT(
2146 scEnv.claimCount(jvb) == 0); // claim count is one less
2147
2148 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2149 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2150 BEAST_EXPECT(
2151 scEnv.claimCount(jvb) == 1); // claim count was incremented
2152
2153 BEAST_EXPECT(door.diff() == -amt_plus_reward);
2154 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2155 BEAST_EXPECT(alice.diff() == amt);
2156 }
2157
2158 // Check that sending funds to an existing account with deposit auth
2159 // set fails for account create transactions.
2160 {
2161 XEnv mcEnv(*this);
2162 XEnv scEnv(*this, true);
2163
2164 auto const amt = XRP(1000);
2165 auto const amt_plus_reward = amt + reward;
2166
2167 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20))).close();
2168
2169 {
2170 Balance door(mcEnv, mcDoor);
2171 Balance carol(mcEnv, mcCarol);
2172
2173 mcEnv
2175 mcCarol, jvb, scAlice, amt, reward))
2176 .close();
2177
2178 BEAST_EXPECT(door.diff() == amt_plus_reward);
2179 BEAST_EXPECT(carol.diff() == -(amt_plus_reward + tx_fee));
2180 }
2181
2184 .tx(fset("scAlice", asfDepositAuth)) // set deposit auth
2185 .close();
2186
2187 Balance attester(scEnv, scAttester);
2188 Balance door(scEnv, Account::master);
2189 Balance alice(scEnv, scAlice);
2190
2191 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2)).close();
2192 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
2193 BEAST_EXPECT(
2194 scEnv.claimCount(jvb) == 0); // claim count is one less
2195
2196 scEnv.multiTx(att_create_acct_vec(1, amt, scAlice, 2, 2)).close();
2197 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
2198 BEAST_EXPECT(
2199 scEnv.claimCount(jvb) == 1); // claim count was incremented
2200
2201 BEAST_EXPECT(door.diff() == -reward);
2202 BEAST_EXPECT(attester.diff() == -multiTtxFee(4));
2203 BEAST_EXPECT(alice.diff() == STAmount(0));
2204 }
2205
2206 // If an account is unable to pay the reserve, check that it fails.
2207 // [greg todo] I don't know what this should test??
2208
2209 // If an attestation already exists for that server and claim id,
2210 // the new attestation should replace the old attestation
2211 {
2212 XEnv mcEnv(*this);
2213 XEnv scEnv(*this, true);
2214 auto const amt = XRP(1000);
2215 auto const amt_plus_reward = amt + reward;
2216
2217 {
2218 Balance door(mcEnv, mcDoor);
2219 Balance carol(mcEnv, mcCarol);
2220
2221 mcEnv.tx(create_bridge(mcDoor, jvb, reward, XRP(20)))
2222 .close()
2224 mcAlice, jvb, scuAlice, amt, reward))
2225 .close() // make sure Alice gets claim #1
2227 mcBob, jvb, scuBob, amt, reward))
2228 .close() // make sure Bob gets claim #2
2230 mcCarol, jvb, scuCarol, amt, reward))
2231 .close(); // and Carol will get claim #3
2232
2233 BEAST_EXPECT(
2234 door.diff() ==
2235 (multiply(amt_plus_reward, STAmount(3), xrpIssue()) -
2236 tx_fee));
2237 BEAST_EXPECT(carol.diff() == -(amt + reward + tx_fee));
2238 }
2239
2240 std::uint32_t const red_quorum = 2;
2242 .tx(jtx::signers(Account::master, red_quorum, signers))
2243 .close();
2244
2245 {
2246 Balance attester(scEnv, scAttester);
2247 Balance door(scEnv, Account::master);
2248 auto const bad_amt = XRP(10);
2249 std::uint32_t txCount = 0;
2250
2251 // send attestations with incorrect amounts to for all 3
2252 // AccountCreate. They will be replaced later
2253 scEnv.multiTx(att_create_acct_vec(1, bad_amt, scuAlice, 1))
2254 .multiTx(att_create_acct_vec(2, bad_amt, scuBob, 1, 2))
2255 .multiTx(att_create_acct_vec(3, bad_amt, scuCarol, 1, 1))
2256 .close();
2257 txCount += 3;
2258
2259 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 1), "claim id 1 created");
2260 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 2), "claim id 2 created");
2261 BEAST_EXPECTS(!!scEnv.caClaimID(jvb, 3), "claim id 3 created");
2262
2263 // note: if we send inconsistent attestations in the same
2264 // batch, the transaction errors.
2265
2266 // from now on we send correct attestations
2267 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 0))
2268 .multiTx(att_create_acct_vec(2, amt, scuBob, 1, 2))
2269 .multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 4))
2270 .close();
2271 txCount += 3;
2272
2273 BEAST_EXPECTS(
2274 !!scEnv.caClaimID(jvb, 1), "claim id 1 still there");
2275 BEAST_EXPECTS(
2276 !!scEnv.caClaimID(jvb, 2), "claim id 2 still there");
2277 BEAST_EXPECTS(
2278 !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2279 BEAST_EXPECTS(
2280 scEnv.claimCount(jvb) == 0, "No account created yet");
2281
2282 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 1))
2283 .close();
2284 txCount += 1;
2285
2286 BEAST_EXPECTS(
2287 !!scEnv.caClaimID(jvb, 3), "claim id 3 still there");
2288 BEAST_EXPECTS(
2289 scEnv.claimCount(jvb) == 0, "No account created yet");
2290
2291 scEnv.multiTx(att_create_acct_vec(1, amt, scuAlice, 1, 2))
2292 .close();
2293 txCount += 1;
2294
2295 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 deleted");
2296 BEAST_EXPECTS(scEnv.claimCount(jvb) == 1, "scuAlice created");
2297
2298 scEnv.multiTx(att_create_acct_vec(2, amt, scuBob, 1, 3))
2299 .multiTx(
2300 att_create_acct_vec(1, amt, scuAlice, 1, 3),
2302 .close();
2303 txCount += 2;
2304
2305 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 2), "claim id 2 deleted");
2306 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 1), "claim id 1 not added");
2307 BEAST_EXPECTS(
2308 scEnv.claimCount(jvb) == 2, "scuAlice & scuBob created");
2309
2310 scEnv.multiTx(att_create_acct_vec(3, amt, scuCarol, 1, 0))
2311 .close();
2312 txCount += 1;
2313
2314 BEAST_EXPECTS(!scEnv.caClaimID(jvb, 3), "claim id 3 deleted");
2315 BEAST_EXPECTS(
2316 scEnv.claimCount(jvb) == 3, "All 3 accounts created");
2317
2318 // because of the division of the rewards among attesters,
2319 // sometimes a couple drops are left over unspent in the
2320 // door account (here 2 drops)
2321 BEAST_EXPECT(
2322 multiply(amt_plus_reward, STAmount(3), xrpIssue()) +
2323 door.diff() <
2324 drops(3));
2325 BEAST_EXPECT(attester.diff() == -multiTtxFee(txCount));
2326 BEAST_EXPECT(scEnv.balance(scuAlice) == amt);
2327 BEAST_EXPECT(scEnv.balance(scuBob) == amt);
2328 BEAST_EXPECT(scEnv.balance(scuCarol) == amt);
2329 }
2330 }
2331
2332 // If attestation moves funds, confirm the claim ledger objects are
2333 // removed (for both account create and "regular" transactions)
2334 // [greg] we do this in all attestation tests
2335
2336 // coverage test: add_attestation transaction with incorrect flag
2337 {
2338 XEnv scEnv(*this, true);
2341 .close()
2343 scAttester,
2344 jvb,
2345 mcAlice,
2346 XRP(1000),
2347 payees[0],
2348 true,
2349 1,
2350 {},
2351 signers[0]),
2354 .close();
2355 }
2356
2357 // coverage test: add_attestation with xchain feature
2358 // disabled
2359 {
2360 XEnv scEnv(*this, true);
2363 .disableFeature(featureXChainBridge)
2364 .close()
2366 scAttester,
2367 jvb,
2368 mcAlice,
2369 XRP(1000),
2370 payees[0],
2371 true,
2372 1,
2373 {},
2374 signers[0]),
2376 .close();
2377 }
2378 }
2379
2380 void
2382 {
2383 using namespace jtx;
2384
2385 testcase("Add Non Batch Claim Attestation");
2386
2387 {
2388 XEnv mcEnv(*this);
2389 XEnv scEnv(*this, true);
2390 std::uint32_t const claimID = 1;
2391
2392 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2393
2396 .close()
2398 .close();
2399
2400 BEAST_EXPECT(!!scEnv.claimID(jvb, claimID)); // claim id present
2401
2402 Account const dst{scBob};
2403 auto const amt = XRP(1000);
2404 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2405
2406 auto const dstStartBalance = scEnv.env_.balance(dst);
2407
2408 for (int i = 0; i < signers.size(); ++i)
2409 {
2410 auto const att = claim_attestation(
2411 scAttester,
2412 jvb,
2413 mcAlice,
2414 amt,
2415 payees[i],
2416 true,
2417 claimID,
2418 dst,
2419 signers[i]);
2420
2421 TER const expectedTER =
2423 if (i + 1 == quorum)
2424 scEnv.tx(att, ter(expectedTER)).close();
2425 else
2426 scEnv.tx(att, ter(expectedTER)).close();
2427
2428 if (i + 1 < quorum)
2429 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2430 else
2431 BEAST_EXPECT(
2432 dstStartBalance + amt == scEnv.env_.balance(dst));
2433 }
2434 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2435 }
2436
2437 {
2454 XEnv mcEnv(*this);
2455 XEnv scEnv(*this, true);
2456 auto const amt = XRP(1000);
2457 std::uint32_t const claimID = 1;
2458
2459 for (auto i = 0; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS - 2; ++i)
2460 scEnv.fund(amt, alt_signers[i].account);
2461
2462 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2463
2466 .close()
2468 .close();
2469
2470 Account const dst{scBob};
2471 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2472 auto const dstStartBalance = scEnv.env_.balance(dst);
2473
2474 {
2475 // G1: master key
2476 auto att = claim_attestation(
2477 scAttester,
2478 jvb,
2479 mcAlice,
2480 amt,
2481 payees[0],
2482 true,
2483 claimID,
2484 dst,
2485 alt_signers[0]);
2486 scEnv.tx(att).close();
2487 }
2488 {
2489 // G2: regular key
2490 // alt_signers[0] is the regular key of alt_signers[1]
2491 // There should be 2 attestations after the transaction
2492 scEnv
2493 .tx(jtx::regkey(
2494 alt_signers[1].account, alt_signers[0].account))
2495 .close();
2496 auto att = claim_attestation(
2497 scAttester,
2498 jvb,
2499 mcAlice,
2500 amt,
2501 payees[1],
2502 true,
2503 claimID,
2504 dst,
2505 alt_signers[0]);
2506 att[sfAttestationSignerAccount.getJsonName()] =
2507 alt_signers[1].account.human();
2508 scEnv.tx(att).close();
2509 }
2510 {
2511 // B3: public key and non-exist (unfunded) account mismatch
2512 // G3: public key and non-exist (unfunded) account match
2513 auto const unfundedSigner1 =
2515 auto const unfundedSigner2 =
2517 auto att = claim_attestation(
2518 scAttester,
2519 jvb,
2520 mcAlice,
2521 amt,
2523 true,
2524 claimID,
2525 dst,
2526 unfundedSigner1);
2527 att[sfAttestationSignerAccount.getJsonName()] =
2528 unfundedSigner2.account.human();
2530 .close();
2531 att[sfAttestationSignerAccount.getJsonName()] =
2532 unfundedSigner1.account.human();
2533 scEnv.tx(att).close();
2534 }
2535 {
2536 // B2: single item signer list
2537 std::vector<signer> tempSignerList = {signers[0]};
2538 scEnv.tx(
2539 jtx::signers(alt_signers[2].account, 1, tempSignerList));
2540 auto att = claim_attestation(
2541 scAttester,
2542 jvb,
2543 mcAlice,
2544 amt,
2545 payees[2],
2546 true,
2547 claimID,
2548 dst,
2549 tempSignerList.front());
2550 att[sfAttestationSignerAccount.getJsonName()] =
2551 alt_signers[2].account.human();
2553 .close();
2554 }
2555 {
2556 // B1: disabled master key
2557 scEnv.tx(fset(alt_signers[2].account, asfDisableMaster, 0));
2558 auto att = claim_attestation(
2559 scAttester,
2560 jvb,
2561 mcAlice,
2562 amt,
2563 payees[2],
2564 true,
2565 claimID,
2566 dst,
2567 alt_signers[2]);
2569 .close();
2570 }
2571 {
2572 // --B4: not on signer list
2573 auto att = claim_attestation(
2574 scAttester,
2575 jvb,
2576 mcAlice,
2577 amt,
2578 payees[0],
2579 true,
2580 claimID,
2581 dst,
2582 signers[0]);
2583 scEnv.tx(att, ter(tecNO_PERMISSION)).close();
2584 }
2585 {
2586 // --B5: missing sfAttestationSignerAccount field
2587 // Then submit the one with the field. Should rearch quorum.
2588 auto att = claim_attestation(
2589 scAttester,
2590 jvb,
2591 mcAlice,
2592 amt,
2593 payees[3],
2594 true,
2595 claimID,
2596 dst,
2597 alt_signers[3]);
2598 att.removeMember(sfAttestationSignerAccount.getJsonName());
2599 scEnv.tx(att, ter(temMALFORMED)).close();
2600 BEAST_EXPECT(dstStartBalance == scEnv.env_.balance(dst));
2601 att[sfAttestationSignerAccount.getJsonName()] =
2602 alt_signers[3].account.human();
2603 scEnv.tx(att).close();
2604 BEAST_EXPECT(dstStartBalance + amt == scEnv.env_.balance(dst));
2605 }
2606 }
2607 }
2608
2609 void
2611 {
2612 using namespace jtx;
2613
2614 testcase("Add Non Batch Account Create Attestation");
2615
2616 XEnv mcEnv(*this);
2617 XEnv scEnv(*this, true);
2618
2619 XRPAmount tx_fee = mcEnv.txFee();
2620
2621 Account a{"a"};
2622 Account doorA{"doorA"};
2623
2624 STAmount funds{XRP(10000)};
2625 mcEnv.fund(funds, a);
2626 mcEnv.fund(funds, doorA);
2627
2628 Account ua{"ua"}; // unfunded account we want to create
2629
2630 BridgeDef xrp_b{
2631 doorA,
2632 xrpIssue(),
2634 xrpIssue(),
2635 XRP(1), // reward
2636 XRP(20), // minAccountCreate
2637 4, // quorum
2638 signers,
2640
2641 xrp_b.initBridge(mcEnv, scEnv);
2642
2643 auto const amt = XRP(777);
2644 auto const amt_plus_reward = amt + xrp_b.reward;
2645 {
2646 Balance bal_doorA(mcEnv, doorA);
2647 Balance bal_a(mcEnv, a);
2648
2649 mcEnv
2651 a, xrp_b.jvb, ua, amt, xrp_b.reward))
2652 .close();
2653
2654 BEAST_EXPECT(bal_doorA.diff() == amt_plus_reward);
2655 BEAST_EXPECT(bal_a.diff() == -(amt_plus_reward + tx_fee));
2656 }
2657
2658 for (int i = 0; i < signers.size(); ++i)
2659 {
2660 auto const att = create_account_attestation(
2661 signers[0].account,
2662 xrp_b.jvb,
2663 a,
2664 amt,
2665 xrp_b.reward,
2666 signers[i].account,
2667 true,
2668 1,
2669 ua,
2670 signers[i]);
2671 TER const expectedTER = i < xrp_b.quorum
2672 ? tesSUCCESS
2674
2675 scEnv.tx(att, ter(expectedTER)).close();
2676 if (i + 1 < xrp_b.quorum)
2677 BEAST_EXPECT(!scEnv.env_.le(ua));
2678 else
2679 BEAST_EXPECT(scEnv.env_.le(ua));
2680 }
2681 BEAST_EXPECT(scEnv.env_.le(ua));
2682 }
2683
2684 void
2686 {
2687 using namespace jtx;
2688
2689 XRPAmount res0 = reserve(0);
2690 XRPAmount tx_fee = txFee();
2691
2692 testcase("Claim");
2693
2694 // Claim where the amount matches what is attested to, to an account
2695 // that exists, and there are enough attestations to reach a quorum
2696 // => should succeed
2697 // -----------------------------------------------------------------
2698 for (auto withClaim : {false, true})
2699 {
2700 XEnv mcEnv(*this);
2701 XEnv scEnv(*this, true);
2702
2703 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2704
2707 .close()
2709 .close();
2710
2711 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2712 auto const amt = XRP(1000);
2713 std::uint32_t const claimID = 1;
2714 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2715
2716 BalanceTransfer transfer(
2717 scEnv,
2719 scBob,
2720 scAlice,
2721 &payees[0],
2723 withClaim);
2724
2725 scEnv
2727 scAttester,
2728 jvb,
2729 mcAlice,
2730 amt,
2731 payees,
2732 true,
2733 claimID,
2734 dst,
2735 signers))
2736 .close();
2737 if (withClaim)
2738 {
2739 BEAST_EXPECT(transfer.has_not_happened());
2740
2741 // need to submit a claim transactions
2742 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
2743 .close();
2744 }
2745
2746 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
2747 }
2748
2749 // Claim with just one attestation signed by the Master key
2750 // => should not succeed
2751 // -----------------------------------------------------------------
2752 for (auto withClaim : {false, true})
2753 {
2754 XEnv mcEnv(*this);
2755 XEnv scEnv(*this, true);
2756
2757 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2758
2759 scEnv
2761 //.tx(jtx::signers(Account::master, quorum, signers))
2762 .close()
2764 .close();
2765
2766 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2767 auto const amt = XRP(1000);
2768 std::uint32_t const claimID = 1;
2769 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2770
2771 BalanceTransfer transfer(
2772 scEnv,
2774 scBob,
2775 scAlice,
2776 &payees[0],
2777 1,
2778 withClaim);
2779
2780 jtx::signer master_signer(Account::master);
2781 scEnv
2783 scAttester,
2784 jvb,
2785 mcAlice,
2786 amt,
2787 payees[0],
2788 true,
2789 claimID,
2790 dst,
2791 master_signer),
2793 .close();
2794
2795 BEAST_EXPECT(transfer.has_not_happened());
2796 }
2797
2798 // Claim with just one attestation signed by a regular key
2799 // associated to the master account
2800 // => should not succeed
2801 // -----------------------------------------------------------------
2802 for (auto withClaim : {false, true})
2803 {
2804 XEnv mcEnv(*this);
2805 XEnv scEnv(*this, true);
2806
2807 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2808
2809 scEnv
2811 //.tx(jtx::signers(Account::master, quorum, signers))
2813 .close()
2815 .close();
2816
2817 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2818 auto const amt = XRP(1000);
2819 std::uint32_t const claimID = 1;
2820 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2821
2822 BalanceTransfer transfer(
2823 scEnv,
2825 scBob,
2826 scAlice,
2827 &payees[0],
2828 1,
2829 withClaim);
2830
2831 jtx::signer master_signer(payees[0]);
2832 scEnv
2834 scAttester,
2835 jvb,
2836 mcAlice,
2837 amt,
2838 payees[0],
2839 true,
2840 claimID,
2841 dst,
2842 master_signer),
2844 .close();
2845
2846 BEAST_EXPECT(transfer.has_not_happened());
2847 }
2848
2849 // Claim against non-existent bridge
2850 // ---------------------------------
2851 for (auto withClaim : {false, true})
2852 {
2853 XEnv mcEnv(*this);
2854 XEnv scEnv(*this, true);
2855
2856 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2857
2858 auto jvb_unknown =
2860
2863 .close()
2865 scAlice, jvb_unknown, reward, mcAlice),
2867 .close();
2868
2869 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2870 auto const amt = XRP(1000);
2871 std::uint32_t const claimID = 1;
2872 mcEnv
2873 .tx(xchain_commit(mcAlice, jvb_unknown, claimID, amt, dst),
2875 .close();
2876
2877 BalanceTransfer transfer(
2878 scEnv, Account::master, scBob, scAlice, payees, withClaim);
2879 scEnv
2881 scAttester,
2882 jvb_unknown,
2883 mcAlice,
2884 amt,
2885 payees[0],
2886 true,
2887 claimID,
2888 dst,
2889 signers[0]),
2891 .close();
2892
2893 if (withClaim)
2894 {
2895 BEAST_EXPECT(transfer.has_not_happened());
2896
2897 // need to submit a claim transactions
2898 scEnv
2899 .tx(xchain_claim(scAlice, jvb_unknown, claimID, amt, scBob),
2901 .close();
2902 }
2903
2904 BEAST_EXPECT(transfer.has_not_happened());
2905 }
2906
2907 // Claim against non-existent claim id
2908 // -----------------------------------
2909 for (auto withClaim : {false, true})
2910 {
2911 XEnv mcEnv(*this);
2912 XEnv scEnv(*this, true);
2913
2914 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2915
2918 .close()
2920 .close();
2921
2922 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2923 auto const amt = XRP(1000);
2924 std::uint32_t const claimID = 1;
2925 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2926
2927 BalanceTransfer transfer(
2928 scEnv, Account::master, scBob, scAlice, payees, withClaim);
2929
2930 // attest using non-existent claim id
2931 scEnv
2933 scAttester,
2934 jvb,
2935 mcAlice,
2936 amt,
2937 payees[0],
2938 true,
2939 999,
2940 dst,
2941 signers[0]),
2943 .close();
2944 if (withClaim)
2945 {
2946 BEAST_EXPECT(transfer.has_not_happened());
2947
2948 // claim using non-existent claim id
2949 scEnv
2950 .tx(xchain_claim(scAlice, jvb, 999, amt, scBob),
2952 .close();
2953 }
2954
2955 BEAST_EXPECT(transfer.has_not_happened());
2956 }
2957
2958 // Claim against a claim id owned by another account
2959 // -------------------------------------------------
2960 for (auto withClaim : {false, true})
2961 {
2962 XEnv mcEnv(*this);
2963 XEnv scEnv(*this, true);
2964
2965 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
2966
2969 .close()
2971 .close();
2972
2973 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
2974 auto const amt = XRP(1000);
2975 std::uint32_t const claimID = 1;
2976 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
2977
2978 BalanceTransfer transfer(
2979 scEnv,
2981 scBob,
2982 scAlice,
2983 &payees[0],
2985 withClaim);
2986
2987 scEnv
2989 scAttester,
2990 jvb,
2991 mcAlice,
2992 amt,
2993 payees,
2994 true,
2995 claimID,
2996 dst,
2997 signers))
2998 .close();
2999 if (withClaim)
3000 {
3001 BEAST_EXPECT(transfer.has_not_happened());
3002
3003 // submit a claim transaction with the wrong account (scGw
3004 // instead of scAlice)
3005 scEnv
3006 .tx(xchain_claim(scGw, jvb, claimID, amt, scBob),
3008 .close();
3009 BEAST_EXPECT(transfer.has_not_happened());
3010 }
3011 else
3012 {
3013 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3014 }
3015 }
3016
3017 // Claim against a claim id with no attestations
3018 // ---------------------------------------------
3019 for (auto withClaim : {false, true})
3020 {
3021 XEnv mcEnv(*this);
3022 XEnv scEnv(*this, true);
3023
3024 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3025
3028 .close()
3030 .close();
3031
3032 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3033 auto const amt = XRP(1000);
3034 std::uint32_t const claimID = 1;
3035 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3036
3037 BalanceTransfer transfer(
3038 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3039
3040 // don't send any attestations
3041
3042 if (withClaim)
3043 {
3044 BEAST_EXPECT(transfer.has_not_happened());
3045
3046 // need to submit a claim transactions
3047 scEnv
3048 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3050 .close();
3051 }
3052
3053 BEAST_EXPECT(transfer.has_not_happened());
3054 }
3055
3056 // Claim against a claim id with attestations, but not enough to
3057 // make a quorum
3058 // --------------------------------------------------------------------
3059 for (auto withClaim : {false, true})
3060 {
3061 XEnv mcEnv(*this);
3062 XEnv scEnv(*this, true);
3063
3064 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3065
3068 .close()
3070 .close();
3071
3072 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3073 auto const amt = XRP(1000);
3074 std::uint32_t const claimID = 1;
3075 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3076
3077 BalanceTransfer transfer(
3078 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3079
3080 auto tooFew = quorum - 1;
3081 scEnv
3083 scAttester,
3084 jvb,
3085 mcAlice,
3086 amt,
3087 payees,
3088 true,
3089 claimID,
3090 dst,
3091 signers,
3092 tooFew))
3093 .close();
3094 if (withClaim)
3095 {
3096 BEAST_EXPECT(transfer.has_not_happened());
3097
3098 // need to submit a claim transactions
3099 scEnv
3100 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3102 .close();
3103 }
3104
3105 BEAST_EXPECT(transfer.has_not_happened());
3106 }
3107
3108 // Claim id of zero
3109 // ----------------
3110 for (auto withClaim : {false, true})
3111 {
3112 XEnv mcEnv(*this);
3113 XEnv scEnv(*this, true);
3114
3115 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3116
3119 .close()
3121 .close();
3122
3123 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3124 auto const amt = XRP(1000);
3125 std::uint32_t const claimID = 1;
3126 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3127
3128 BalanceTransfer transfer(
3129 scEnv, Account::master, scBob, scAlice, payees, withClaim);
3130
3131 scEnv
3132 .multiTx(
3134 scAttester,
3135 jvb,
3136 mcAlice,
3137 amt,
3138 payees,
3139 true,
3140 0,
3141 dst,
3142 signers),
3144 .close();
3145 if (withClaim)
3146 {
3147 BEAST_EXPECT(transfer.has_not_happened());
3148
3149 // need to submit a claim transactions
3150 scEnv
3151 .tx(xchain_claim(scAlice, jvb, 0, amt, scBob),
3153 .close();
3154 }
3155
3156 BEAST_EXPECT(transfer.has_not_happened());
3157 }
3158
3159 // Claim issue that does not match the expected issue on the bridge
3160 // (either LockingChainIssue or IssuingChainIssue, depending on the
3161 // chain). The claim id should already have enough attestations to
3162 // reach a quorum for this amount (for a different issuer).
3163 // ---------------------------------------------------------------------
3164 for (auto withClaim : {true})
3165 {
3166 XEnv mcEnv(*this);
3167 XEnv scEnv(*this, true);
3168
3169 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3170
3173 .close()
3175 .close();
3176
3177 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3178 auto const amt = XRP(1000);
3179 std::uint32_t const claimID = 1;
3180 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3181
3182 BalanceTransfer transfer(
3183 scEnv,
3185 scBob,
3186 scAlice,
3187 &payees[0],
3189 withClaim);
3190
3191 scEnv
3193 scAttester,
3194 jvb,
3195 mcAlice,
3196 amt,
3197 payees,
3198 true,
3199 claimID,
3200 dst,
3201 signers))
3202 .close();
3203
3204 if (withClaim)
3205 {
3206 BEAST_EXPECT(transfer.has_not_happened());
3207
3208 // need to submit a claim transactions
3209 scEnv
3210 .tx(xchain_claim(scAlice, jvb, claimID, scUSD(1000), scBob),
3212 .close();
3213 }
3214
3215 BEAST_EXPECT(transfer.has_not_happened());
3216 }
3217
3218 // Claim to a destination that does not already exist on the chain
3219 // -----------------------------------------------------------------
3220 for (auto withClaim : {true})
3221 {
3222 XEnv mcEnv(*this);
3223 XEnv scEnv(*this, true);
3224
3225 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3226
3229 .close()
3231 .close();
3232
3233 auto dst(withClaim ? std::nullopt : std::optional<Account>{scuBob});
3234 auto const amt = XRP(1000);
3235 std::uint32_t const claimID = 1;
3236 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3237
3238 BalanceTransfer transfer(
3239 scEnv,
3241 scBob,
3242 scAlice,
3243 &payees[0],
3245 withClaim);
3246
3247 scEnv
3249 scAttester,
3250 jvb,
3251 mcAlice,
3252 amt,
3253 payees,
3254 true,
3255 claimID,
3256 dst,
3257 signers))
3258 .close();
3259 if (withClaim)
3260 {
3261 BEAST_EXPECT(transfer.has_not_happened());
3262
3263 // need to submit a claim transactions
3264 scEnv
3265 .tx(xchain_claim(scAlice, jvb, claimID, amt, scuBob),
3266 ter(tecNO_DST))
3267 .close();
3268 }
3269
3270 BEAST_EXPECT(transfer.has_not_happened());
3271 }
3272
3273 // Claim where the claim id owner does not have enough XRP to pay
3274 // the reward
3275 // ------------------------------------------------------------------
3276 for (auto withClaim : {false, true})
3277 {
3278 XEnv mcEnv(*this);
3279 XEnv scEnv(*this, true);
3280
3281 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3282 STAmount huge_reward{XRP(20000)};
3283 BEAST_EXPECT(huge_reward > scEnv.balance(scAlice));
3284
3285 scEnv.tx(create_bridge(Account::master, jvb, huge_reward))
3287 .close()
3288 .tx(xchain_create_claim_id(scAlice, jvb, huge_reward, mcAlice))
3289 .close();
3290
3291 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3292 auto const amt = XRP(1000);
3293 std::uint32_t const claimID = 1;
3294 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3295
3296 BalanceTransfer transfer(
3297 scEnv,
3299 scBob,
3300 scAlice,
3301 &payees[0],
3303 withClaim);
3304
3305 if (withClaim)
3306 {
3307 scEnv
3309 scAttester,
3310 jvb,
3311 mcAlice,
3312 amt,
3313 payees,
3314 true,
3315 claimID,
3316 dst,
3317 signers))
3318 .close();
3319 BEAST_EXPECT(transfer.has_not_happened());
3320
3321 // need to submit a claim transactions
3322 scEnv
3323 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3325 .close();
3326 }
3327 else
3328 {
3329 auto txns = claim_attestations(
3330 scAttester,
3331 jvb,
3332 mcAlice,
3333 amt,
3334 payees,
3335 true,
3336 claimID,
3337 dst,
3338 signers);
3339 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3340 {
3341 scEnv.tx(txns[i]).close();
3342 }
3343 scEnv.tx(txns.back());
3344 scEnv.close();
3345 // The attestation should succeed, because it adds an
3346 // attestation, but the claim should fail with insufficient
3347 // funds
3348 scEnv
3349 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3351 .close();
3352 }
3353
3354 BEAST_EXPECT(transfer.has_not_happened());
3355 }
3356
3357 // Claim where the claim id owner has enough XRP to pay the reward,
3358 // but it would put his balance below the reserve
3359 // --------------------------------------------------------------------
3360 for (auto withClaim : {false, true})
3361 {
3362 XEnv mcEnv(*this);
3363 XEnv scEnv(*this, true);
3364
3365 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3366
3369 .fund(
3370 res0 + reward,
3371 scuAlice) // just not enough because of fees
3372 .close()
3375 .close();
3376
3377 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3378 auto const amt = XRP(1000);
3379 std::uint32_t const claimID = 1;
3380 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3381
3382 BalanceTransfer transfer(
3383 scEnv, Account::master, scBob, scuAlice, payees, withClaim);
3384
3385 scEnv
3387 scAttester,
3388 jvb,
3389 mcAlice,
3390 amt,
3391 payees[0],
3392 true,
3393 claimID,
3394 dst,
3395 signers[0]),
3397 .close();
3398 if (withClaim)
3399 {
3400 BEAST_EXPECT(transfer.has_not_happened());
3401
3402 // need to submit a claim transactions
3403 scEnv
3404 .tx(xchain_claim(scuAlice, jvb, claimID, amt, scBob),
3406 .close();
3407 }
3408
3409 BEAST_EXPECT(transfer.has_not_happened());
3410 }
3411
3412 // Pay to an account with deposit auth set
3413 // ---------------------------------------
3414 for (auto withClaim : {false, true})
3415 {
3416 XEnv mcEnv(*this);
3417 XEnv scEnv(*this, true);
3418
3419 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3420
3423 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3424 .close()
3426 .close();
3427
3428 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3429 auto const amt = XRP(1000);
3430 std::uint32_t const claimID = 1;
3431 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3432
3433 BalanceTransfer transfer(
3434 scEnv,
3436 scBob,
3437 scAlice,
3438 &payees[0],
3440 withClaim);
3441 auto txns = claim_attestations(
3442 scAttester,
3443 jvb,
3444 mcAlice,
3445 amt,
3446 payees,
3447 true,
3448 claimID,
3449 dst,
3450 signers);
3451 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3452 {
3453 scEnv.tx(txns[i]).close();
3454 }
3455 if (withClaim)
3456 {
3457 scEnv.tx(txns.back()).close();
3458
3459 BEAST_EXPECT(transfer.has_not_happened());
3460
3461 // need to submit a claim transactions
3462 scEnv
3463 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3465 .close();
3466
3467 // the transfer failed, but check that we can still use the
3468 // claimID with a different account
3469 Balance scCarol_bal(scEnv, scCarol);
3470
3471 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3472 .close();
3473 BEAST_EXPECT(scCarol_bal.diff() == amt);
3474 }
3475 else
3476 {
3477 scEnv.tx(txns.back()).close();
3478 scEnv
3479 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3481 .close();
3482 // A way would be to remove deposit auth and resubmit the
3483 // attestations (even though the witness servers won't do
3484 // it)
3485 scEnv
3486 .tx(fset("scBob", 0, asfDepositAuth)) // clear deposit auth
3487 .close();
3488
3489 Balance scBob_bal(scEnv, scBob);
3490 scEnv.tx(txns.back()).close();
3491 BEAST_EXPECT(scBob_bal.diff() == amt);
3492 }
3493 }
3494
3495 // Pay to an account with Destination Tag set
3496 // ------------------------------------------
3497 for (auto withClaim : {false, true})
3498 {
3499 XEnv mcEnv(*this);
3500 XEnv scEnv(*this, true);
3501
3502 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3503
3506 .tx(fset("scBob", asfRequireDest)) // set dest tag
3507 .close()
3509 .close();
3510
3511 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3512 auto const amt = XRP(1000);
3513 std::uint32_t const claimID = 1;
3514 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3515
3516 BalanceTransfer transfer(
3517 scEnv,
3519 scBob,
3520 scAlice,
3521 &payees[0],
3523 withClaim);
3524 auto txns = claim_attestations(
3525 scAttester,
3526 jvb,
3527 mcAlice,
3528 amt,
3529 payees,
3530 true,
3531 claimID,
3532 dst,
3533 signers);
3534 for (int i = 0; i < UT_XCHAIN_DEFAULT_QUORUM - 1; ++i)
3535 {
3536 scEnv.tx(txns[i]).close();
3537 }
3538 if (withClaim)
3539 {
3540 scEnv.tx(txns.back()).close();
3541 BEAST_EXPECT(transfer.has_not_happened());
3542
3543 // need to submit a claim transactions
3544 scEnv
3545 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3547 .close();
3548
3549 // the transfer failed, but check that we can still use the
3550 // claimID with a different account
3551 Balance scCarol_bal(scEnv, scCarol);
3552
3553 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol))
3554 .close();
3555 BEAST_EXPECT(scCarol_bal.diff() == amt);
3556 }
3557 else
3558 {
3559 scEnv.tx(txns.back()).close();
3560 scEnv
3561 .tx(xchain_claim(scAlice, jvb, claimID, amt, scBob),
3563 .close();
3564 // A way would be to remove the destination tag requirement
3565 // and resubmit the attestations (even though the witness
3566 // servers won't do it)
3567 scEnv
3568 .tx(fset("scBob", 0, asfRequireDest)) // clear dest tag
3569 .close();
3570
3571 Balance scBob_bal(scEnv, scBob);
3572
3573 scEnv.tx(txns.back()).close();
3574 BEAST_EXPECT(scBob_bal.diff() == amt);
3575 }
3576 }
3577
3578 // Pay to an account with deposit auth set. Check that the attestations
3579 // are still validated and that we can used the claimID to transfer the
3580 // funds to a different account (which doesn't have deposit auth set)
3581 // --------------------------------------------------------------------
3582 {
3583 XEnv mcEnv(*this);
3584 XEnv scEnv(*this, true);
3585
3586 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3587
3590 .tx(fset("scBob", asfDepositAuth)) // set deposit auth
3591 .close()
3593 .close();
3594
3595 auto dst(std::optional<Account>{scBob});
3596 auto const amt = XRP(1000);
3597 std::uint32_t const claimID = 1;
3598 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3599
3600 // we should be able to submit the attestations, but the transfer
3601 // should not occur because dest account has deposit auth set
3602 Balance scBob_bal(scEnv, scBob);
3603
3605 scAttester,
3606 jvb,
3607 mcAlice,
3608 amt,
3609 payees,
3610 true,
3611 claimID,
3612 dst,
3613 signers));
3614 BEAST_EXPECT(scBob_bal.diff() == STAmount(0));
3615
3616 // Check that check that we still can use the claimID to transfer
3617 // the amount to a different account
3618 Balance scCarol_bal(scEnv, scCarol);
3619
3620 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scCarol)).close();
3621 BEAST_EXPECT(scCarol_bal.diff() == amt);
3622 }
3623
3624 // Claim where the amount different from what is attested to
3625 // ---------------------------------------------------------
3626 for (auto withClaim : {true})
3627 {
3628 XEnv mcEnv(*this);
3629 XEnv scEnv(*this, true);
3630
3631 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3632
3635 .close()
3637 .close();
3638
3639 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3640 auto const amt = XRP(1000);
3641 std::uint32_t const claimID = 1;
3642 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3643
3644 BalanceTransfer transfer(
3645 scEnv,
3647 scBob,
3648 scAlice,
3649 &payees[0],
3651 withClaim);
3653 scAttester,
3654 jvb,
3655 mcAlice,
3656 amt,
3657 payees,
3658 true,
3659 claimID,
3660 dst,
3661 signers));
3662 if (withClaim)
3663 {
3664 BEAST_EXPECT(transfer.has_not_happened());
3665
3666 // claim wrong amount
3667 scEnv
3668 .tx(xchain_claim(scAlice, jvb, claimID, one_xrp, scBob),
3670 .close();
3671 }
3672
3673 BEAST_EXPECT(transfer.has_not_happened());
3674 }
3675
3676 // Verify that rewards are paid from the account that owns the claim
3677 // id
3678 // --------------------------------------------------------------------
3679 for (auto withClaim : {false, true})
3680 {
3681 XEnv mcEnv(*this);
3682 XEnv scEnv(*this, true);
3683
3684 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3685
3688 .close()
3690 .close();
3691
3692 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3693 auto const amt = XRP(1000);
3694 std::uint32_t const claimID = 1;
3695 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3696
3697 BalanceTransfer transfer(
3698 scEnv,
3700 scBob,
3701 scAlice,
3702 &payees[0],
3704 withClaim);
3705 Balance scAlice_bal(scEnv, scAlice);
3707 scAttester,
3708 jvb,
3709 mcAlice,
3710 amt,
3711 payees,
3712 true,
3713 claimID,
3714 dst,
3715 signers));
3716
3717 STAmount claim_cost = reward;
3718
3719 if (withClaim)
3720 {
3721 BEAST_EXPECT(transfer.has_not_happened());
3722
3723 // need to submit a claim transactions
3724 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3725 .close();
3726 claim_cost += tx_fee;
3727 }
3728
3729 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3730 BEAST_EXPECT(
3731 scAlice_bal.diff() == -claim_cost); // because reward % 4 == 0
3732 }
3733
3734 // Verify that if a reward is not evenly divisible among the reward
3735 // accounts, the remaining amount goes to the claim id owner.
3736 // ----------------------------------------------------------------
3737 for (auto withClaim : {false, true})
3738 {
3739 XEnv mcEnv(*this);
3740 XEnv scEnv(*this, true);
3741
3743
3746 .close()
3748 .close();
3749
3750 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3751 auto const amt = XRP(1000);
3752 std::uint32_t const claimID = 1;
3753 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3754
3755 BalanceTransfer transfer(
3756 scEnv,
3758 scBob,
3759 scAlice,
3760 &payees[0],
3762 withClaim);
3763 Balance scAlice_bal(scEnv, scAlice);
3765 scAttester,
3766 jvb,
3767 mcAlice,
3768 amt,
3769 payees,
3770 true,
3771 claimID,
3772 dst,
3773 signers));
3774 STAmount claim_cost = tiny_reward;
3775
3776 if (withClaim)
3777 {
3778 BEAST_EXPECT(transfer.has_not_happened());
3779
3780 // need to submit a claim transactions
3781 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3782 .close();
3783 claim_cost += tx_fee;
3784 }
3785
3786 BEAST_EXPECT(transfer.has_happened(amt, tiny_reward_split));
3787 BEAST_EXPECT(
3788 scAlice_bal.diff() == -(claim_cost - tiny_reward_remainder));
3789 }
3790
3791 // If a reward distribution fails for one of the reward accounts
3792 // (the reward account doesn't exist or has deposit auth set), then
3793 // the txn should still succeed, but that portion should go to the
3794 // claim id owner.
3795 // -------------------------------------------------------------------
3796 for (auto withClaim : {false, true})
3797 {
3798 XEnv mcEnv(*this);
3799 XEnv scEnv(*this, true);
3800
3801 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3802
3803 std::vector<Account> alt_payees{payees.begin(), payees.end() - 1};
3804 alt_payees.back() = Account("inexistent");
3805
3808 .close()
3810 .close();
3811
3812 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3813 auto const amt = XRP(1000);
3814 std::uint32_t const claimID = 1;
3815 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3816
3817 BalanceTransfer transfer(
3818 scEnv,
3820 scBob,
3821 scAlice,
3822 &payees[0],
3824 withClaim);
3826 scAttester,
3827 jvb,
3828 mcAlice,
3829 amt,
3830 alt_payees,
3831 true,
3832 claimID,
3833 dst,
3834 signers));
3835
3836 if (withClaim)
3837 {
3838 BEAST_EXPECT(transfer.has_not_happened());
3839
3840 // need to submit a claim transactions
3841 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3842 .close();
3843 }
3844
3845 // this also checks that only 3 * split_reward was deducted from
3846 // scAlice (the payor account), since we passed alt_payees to
3847 // BalanceTransfer
3848 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3849 }
3850
3851 for (auto withClaim : {false, true})
3852 {
3853 XEnv mcEnv(*this);
3854 XEnv scEnv(*this, true);
3855
3856 mcEnv.tx(create_bridge(mcDoor, jvb)).close();
3857 auto& unpaid = payees[UT_XCHAIN_DEFAULT_QUORUM - 1];
3860 .tx(fset(unpaid, asfDepositAuth))
3861 .close()
3863 .close();
3864
3865 auto dst(withClaim ? std::nullopt : std::optional<Account>{scBob});
3866 auto const amt = XRP(1000);
3867 std::uint32_t const claimID = 1;
3868 mcEnv.tx(xchain_commit(mcAlice, jvb, claimID, amt, dst)).close();
3869
3870 // balance of last signer should not change (has deposit auth)
3871 Balance last_signer(scEnv, unpaid);
3872
3873 // make sure all signers except the last one get the
3874 // split_reward
3875
3876 BalanceTransfer transfer(
3877 scEnv,
3879 scBob,
3880 scAlice,
3881 &payees[0],
3883 withClaim);
3885 scAttester,
3886 jvb,
3887 mcAlice,
3888 amt,
3889 payees,
3890 true,
3891 claimID,
3892 dst,
3893 signers));
3894
3895 if (withClaim)
3896 {
3897 BEAST_EXPECT(transfer.has_not_happened());
3898
3899 // need to submit a claim transactions
3900 scEnv.tx(xchain_claim(scAlice, jvb, claimID, amt, scBob))
3901 .close();
3902 }
3903
3904 // this also checks that only 3 * split_reward was deducted from
3905 // scAlice (the payor account), since we passed payees.size() -
3906 // 1 to BalanceTransfer
3907 BEAST_EXPECT(transfer.has_happened(amt, split_reward_quorum));
3908
3909 // and make sure the account with deposit auth received nothing
3910 BEAST_EXPECT(last_signer.diff() == STAmount(0));
3911 }
3912
3913 // coverage test: xchain_claim transaction with incorrect flag
3914 XEnv(*this, true)
3916 .close()
3917 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3920 .close();
3921
3922 // coverage test: xchain_claim transaction with xchain feature
3923 // disabled
3924 XEnv(*this, true)
3926 .disableFeature(featureXChainBridge)
3927 .close()
3928 .tx(xchain_claim(scAlice, jvb, 1, XRP(1000), scBob),
3930 .close();
3931
3932 // coverage test: XChainClaim::preclaim - isLockingChain = true;
3933 XEnv(*this)
3935 .close()
3936 .tx(xchain_claim(mcAlice, jvb, 1, XRP(1000), mcBob),
3938 }
3939
3940 void
3942 {
3943 using namespace jtx;
3944
3945 testcase("Bridge Create Account");
3946 XRPAmount tx_fee = txFee();
3947
3948 // coverage test: transferHelper() - dst == src
3949 {
3950 XEnv scEnv(*this, true);
3951
3952 auto const amt = XRP(111);
3953 auto const amt_plus_reward = amt + reward;
3954
3957 .close();
3958
3959 Balance door(scEnv, Account::master);
3960
3961 // scEnv.tx(att_create_acct_batch1(1, amt,
3962 // Account::master)).close();
3964 .close();
3965 BEAST_EXPECT(!!scEnv.caClaimID(jvb, 1)); // claim id present
3966 BEAST_EXPECT(
3967 scEnv.claimCount(jvb) == 0); // claim count is one less
3968
3969 // scEnv.tx(att_create_acct_batch2(1, amt,
3970 // Account::master)).close();
3971 scEnv.multiTx(att_create_acct_vec(1, amt, Account::master, 2, 2))
3972 .close();
3973 BEAST_EXPECT(!scEnv.caClaimID(jvb, 1)); // claim id deleted
3974 BEAST_EXPECT(
3975 scEnv.claimCount(jvb) == 1); // claim count was incremented
3976
3977 BEAST_EXPECT(door.diff() == -reward);
3978 }
3979
3980 // Check that creating an account with less than the minimum create
3981 // amount fails.
3982 {
3983 XEnv mcEnv(*this);
3984
3985 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
3986
3987 Balance door(mcEnv, mcDoor);
3988 Balance carol(mcEnv, mcCarol);
3989
3990 mcEnv
3992 mcCarol, jvb, scuAlice, XRP(19), reward),
3994 .close();
3995
3996 BEAST_EXPECT(door.diff() == STAmount(0));
3997 BEAST_EXPECT(carol.diff() == -tx_fee);
3998 }
3999
4000 // Check that creating an account with invalid flags fails.
4001 {
4002 XEnv mcEnv(*this);
4003
4004 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4005
4006 Balance door(mcEnv, mcDoor);
4007
4008 mcEnv
4010 mcCarol, jvb, scuAlice, XRP(20), reward),
4013 .close();
4014
4015 BEAST_EXPECT(door.diff() == STAmount(0));
4016 }
4017
4018 // Check that creating an account with the XChainBridge feature
4019 // disabled fails.
4020 {
4021 XEnv mcEnv(*this);
4022
4023 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4024
4025 Balance door(mcEnv, mcDoor);
4026
4027 mcEnv.disableFeature(featureXChainBridge)
4029 mcCarol, jvb, scuAlice, XRP(20), reward),
4031 .close();
4032
4033 BEAST_EXPECT(door.diff() == STAmount(0));
4034 }
4035
4036 // Check that creating an account with a negative amount fails
4037 {
4038 XEnv mcEnv(*this);
4039
4040 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4041
4042 Balance door(mcEnv, mcDoor);
4043
4044 mcEnv
4046 mcCarol, jvb, scuAlice, XRP(-20), reward),
4048 .close();
4049
4050 BEAST_EXPECT(door.diff() == STAmount(0));
4051 }
4052
4053 // Check that creating an account with a negative reward fails
4054 {
4055 XEnv mcEnv(*this);
4056
4057 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4058
4059 Balance door(mcEnv, mcDoor);
4060
4061 mcEnv
4063 mcCarol, jvb, scuAlice, XRP(20), XRP(-1)),
4065 .close();
4066
4067 BEAST_EXPECT(door.diff() == STAmount(0));
4068 }
4069
4070 // Check that door account can't lock funds onto itself
4071 {
4072 XEnv mcEnv(*this);
4073
4074 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4075
4076 Balance door(mcEnv, mcDoor);
4077
4078 mcEnv
4080 mcDoor, jvb, scuAlice, XRP(20), XRP(1)),
4082 .close();
4083
4084 BEAST_EXPECT(door.diff() == -tx_fee);
4085 }
4086
4087 // Check that reward matches the amount specified in bridge
4088 {
4089 XEnv mcEnv(*this);
4090
4091 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(20))).close();
4092
4093 Balance door(mcEnv, mcDoor);
4094
4095 mcEnv
4097 mcCarol, jvb, scuAlice, XRP(20), XRP(2)),
4099 .close();
4100
4101 BEAST_EXPECT(door.diff() == STAmount(0));
4102 }
4103 }
4104
4105 void
4107 {
4108 using namespace jtx;
4109 XRPAmount res0 = reserve(0);
4110 XRPAmount tx_fee = txFee();
4111
4112 testcase("Fee dips into reserve");
4113
4114 // commit where the fee dips into the reserve, this should succeed
4115 XEnv(*this)
4117 .fund(res0 + one_xrp + tx_fee - drops(1), mcuAlice)
4118 .close()
4120 ter(tesSUCCESS));
4121
4122 // commit where the commit amount drips into the reserve, this should
4123 // fail
4124 XEnv(*this)
4126 .fund(res0 + one_xrp - drops(1), mcuAlice)
4127 .close()
4130
4131 auto const minAccountCreate = XRP(20);
4132
4133 // account create commit where the fee dips into the reserve,
4134 // this should succeed
4135 XEnv(*this)
4136 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4137 .fund(
4138 res0 + tx_fee + minAccountCreate + reward - drops(1), mcuAlice)
4139 .close()
4141 mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4142 ter(tesSUCCESS));
4143
4144 // account create commit where the commit dips into the reserve,
4145 // this should fail
4146 XEnv(*this)
4147 .tx(create_bridge(mcDoor, jvb, reward, minAccountCreate))
4148 .fund(res0 + minAccountCreate + reward - drops(1), mcuAlice)
4149 .close()
4151 mcuAlice, jvb, scuAlice, minAccountCreate, reward),
4153 }
4154
4155 void
4157 {
4158 using namespace jtx;
4159
4160 testcase("Bridge Delete Door Account");
4161
4162 auto const acctDelFee{
4163 drops(XEnv(*this).env_.current()->fees().increment)};
4164
4165 // Deleting an account that owns bridge should fail
4166 {
4167 XEnv mcEnv(*this);
4168
4169 mcEnv.tx(create_bridge(mcDoor, jvb, XRP(1), XRP(1))).close();
4170
4171 // We don't allow an account to be deleted if its sequence
4172 // number is within 256 of the current ledger.
4173 for (size_t i = 0; i < 256; ++i)
4174 mcEnv.close();
4175
4176 // try to delete mcDoor, send funds to mcAlice
4177 mcEnv.tx(
4179 fee(acctDelFee),
4181 }
4182
4183 // Deleting an account that owns a claim id should fail
4184 {
4185 XEnv scEnv(*this, true);
4186
4188 .close()
4190 .close();
4191
4192 // We don't allow an account to be deleted if its sequence
4193 // number is within 256 of the current ledger.
4194 for (size_t i = 0; i < 256; ++i)
4195 scEnv.close();
4196
4197 // try to delete scAlice, send funds to scBob
4198 scEnv.tx(
4200 fee(acctDelFee),
4202 }
4203 }
4204
4205 void
4207 {
4208 using namespace jtx;
4209
4210 testcase("Bad attestations");
4211 {
4212 // Create a bridge and add an attestation with a bad public key
4213 XEnv scEnv(*this, true);
4214 std::uint32_t const claimID = 1;
4216 auto const amt = XRP(1000);
4219 .close();
4221 .close();
4222 auto jvAtt = claim_attestation(
4223 scAttester,
4224 jvb,
4225 mcAlice,
4226 amt,
4228 true,
4229 claimID,
4230 dst,
4232 {
4233 // Change to an invalid keytype
4234 auto k = jvAtt["PublicKey"].asString();
4235 k.at(1) = '9';
4236 jvAtt["PublicKey"] = k;
4237 }
4238 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
4239 }
4240 {
4241 // Create a bridge and add an create account attestation with a bad
4242 // public key
4243 XEnv scEnv(*this, true);
4244 std::uint32_t const createCount = 1;
4245 Account dst{scBob};
4246 auto const amt = XRP(1000);
4247 auto const rewardAmt = XRP(1);
4250 .close();
4251 auto jvAtt = create_account_attestation(
4252 scAttester,
4253 jvb,
4254 mcAlice,
4255 amt,
4256 rewardAmt,
4258 true,
4259 createCount,
4260 dst,
4262 {
4263 // Change to an invalid keytype
4264 auto k = jvAtt["PublicKey"].asString();
4265 k.at(1) = '9';
4266 jvAtt["PublicKey"] = k;
4267 }
4268 scEnv.tx(jvAtt, ter(temMALFORMED)).close();
4269 }
4270 }
4271
4272 void
4273 run() override
4274 {
4290 }
4291};
4292
4293// -----------------------------------------------------------
4294// -----------------------------------------------------------
4297{
4298private:
4299 static constexpr size_t num_signers = 5;
4300
4301 // --------------------------------------------------
4302 enum class WithClaim { no, yes };
4304 {
4309 bool a2b; // direction of transfer
4311 uint32_t claim_id{0};
4313 };
4314
4316 {
4321 bool a2b;
4322 uint32_t claim_id{0};
4324 };
4325
4327 using BridgeID = BridgeDef const*;
4328
4329 // tracking chain state
4330 // --------------------
4332 {
4335
4336 void
4337 init(ENV& env, jtx::Account const& acct)
4338 {
4339 startAmount = env.balance(acct);
4341 }
4342
4343 bool
4344 verify(ENV& env, jtx::Account const& acct) const
4345 {
4346 STAmount diff{env.balance(acct) - startAmount};
4347 bool check = diff == expectedDiff;
4348 return check;
4349 }
4350 };
4351
4352 // --------------------------------------------------
4354 {
4358
4360 : env(env), tx_fee(env.env_.current()->fees().base)
4361 {
4362 }
4363
4364 void
4365 sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec& claims)
4366 {
4367 for (auto const& c : claims)
4368 {
4369 env.tx(c).close();
4370 spendFee(bridge->signers[signer_idx].account);
4371 }
4372 claims.clear();
4373 }
4374
4375 uint32_t
4377 size_t signer_idx,
4379 CreateClaimVec& claims)
4380 {
4381 size_t num_successful = 0;
4382 for (auto const& c : claims)
4383 {
4384 env.tx(c).close();
4385 if (env.ter() == tesSUCCESS)
4386 {
4387 counters[bridge].signers.push_back(signer_idx);
4388 num_successful++;
4389 }
4390 spendFee(bridge->signers[signer_idx].account);
4391 }
4392 claims.clear();
4393 return num_successful;
4394 }
4395
4396 void
4398 {
4399 bool callback_called;
4400
4401 // we have this "do {} while" loop because we want to process
4402 // all the account create which can reach quorum at this time
4403 // stamp.
4404 do
4405 {
4406 callback_called = false;
4407 for (size_t i = 0; i < signers_attns.size(); ++i)
4408 {
4409 for (auto& [bridge, claims] : signers_attns[i])
4410 {
4411 sendAttestations(i, bridge, claims.xfer_claims);
4412
4413 auto& c = counters[bridge];
4414 auto& create_claims =
4415 claims.create_claims[c.claim_count];
4416 auto num_attns = create_claims.size();
4417 if (num_attns)
4418 {
4419 c.num_create_attn_sent += sendCreateAttestations(
4420 i, bridge, create_claims);
4421 }
4422 assert(claims.create_claims[c.claim_count].empty());
4423 }
4424 }
4425 for (auto& [bridge, c] : counters)
4426 {
4427 if (c.num_create_attn_sent >= bridge->quorum)
4428 {
4429 callback_called = true;
4430 c.create_callbacks[c.claim_count](c.signers);
4431 ++c.claim_count;
4432 c.num_create_attn_sent = 0;
4433 c.signers.clear();
4434 }
4435 }
4436 } while (callback_called);
4437 }
4438
4439 void
4440 init(jtx::Account const& acct)
4441 {
4442 accounts[acct].init(env, acct);
4443 }
4444
4445 void
4447 jtx::Account const& acct,
4448 STAmount amt,
4449 std::uint64_t divisor = 1)
4450 {
4451 if (amt.issue() != xrpIssue())
4452 return;
4453 auto it = accounts.find(acct);
4454 if (it == accounts.end())
4455 {
4456 accounts[acct].init(env, acct);
4457 // we just looked up the account, so expectedDiff == 0
4458 }
4459 else
4460 {
4461 it->second.expectedDiff +=
4462 (divisor == 1 ? amt
4463 : divide(
4464 amt,
4465 STAmount(amt.issue(), divisor),
4466 amt.issue()));
4467 }
4468 }
4469
4470 void
4471 spend(jtx::Account const& acct, STAmount amt, std::uint64_t times = 1)
4472 {
4473 if (amt.issue() != xrpIssue())
4474 return;
4475 receive(
4476 acct,
4477 times == 1
4478 ? -amt
4479 : -multiply(
4480 amt, STAmount(amt.issue(), times), amt.issue()));
4481 }
4482
4483 void
4484 transfer(jtx::Account const& from, jtx::Account const& to, STAmount amt)
4485 {
4486 spend(from, amt);
4487 receive(to, amt);
4488 }
4489
4490 void
4491 spendFee(jtx::Account const& acct, size_t times = 1)
4492 {
4493 spend(acct, tx_fee, times);
4494 }
4495
4496 bool
4497 verify() const
4498 {
4499 for (auto const& [acct, state] : accounts)
4500 if (!state.verify(env, acct))
4501 return false;
4502 return true;
4503 }
4504
4506 {
4509
4510 uint32_t claim_id{0};
4511 uint32_t create_count{0}; // for account create. First should be 1
4512 uint32_t claim_count{
4513 0}; // for account create. Increments after quorum for
4514 // current create_count (starts at 1) is reached.
4515
4516 uint32_t num_create_attn_sent{0}; // for current claim_count
4519 };
4520
4521 struct Claims
4522 {
4525 };
4526
4529
4535 };
4536
4538 {
4539 ChainStateTracker(ENV& a_env, ENV& b_env) : a_(a_env), b_(b_env)
4540 {
4541 }
4542
4543 bool
4544 verify() const
4545 {
4546 return a_.verify() && b_.verify();
4547 }
4548
4549 void
4551 {
4554 }
4555
4556 void
4557 init(jtx::Account const& acct)
4558 {
4559 a_.init(acct);
4560 b_.init(acct);
4561 }
4562
4565 };
4566
4567 enum SmState {
4574 };
4575
4576 enum Act_Flags { af_a2b = 1 << 0 };
4577
4578 // --------------------------------------------------
4579 template <class T>
4581 {
4582 public:
4584 const std::shared_ptr<ChainStateTracker>& chainstate,
4585 const BridgeDef& bridge)
4586 : bridge_(bridge), st_(chainstate)
4587 {
4588 }
4589
4592 {
4593 return static_cast<T&>(*this).a2b() ? st_->a_ : st_->b_;
4594 }
4595
4598 {
4599 return static_cast<T&>(*this).a2b() ? st_->b_ : st_->a_;
4600 }
4601
4602 jtx::Account const&
4604 {
4605 return static_cast<T&>(*this).a2b() ? bridge_.doorA : bridge_.doorB;
4606 }
4607
4608 jtx::Account const&
4610 {
4611 return static_cast<T&>(*this).a2b() ? bridge_.doorB : bridge_.doorA;
4612 }
4613
4614 protected:
4617 };
4618
4619 // --------------------------------------------------
4620 class SmCreateAccount : public SmBase<SmCreateAccount>
4621 {
4622 public:
4624
4626 const std::shared_ptr<ChainStateTracker>& chainstate,
4627 const BridgeDef& bridge,
4629 : Base(chainstate, bridge)
4631 , cr(std::move(create))
4632 {
4633 }
4634
4635 bool
4636 a2b() const
4637 {
4638 return cr.a2b;
4639 }
4640
4641 uint32_t
4643 {
4644 ChainStateTrack& st = srcState();
4645 jtx::Account const& srcdoor = srcDoor();
4646
4647 st.env
4650 .close(); // needed for claim_id sequence to be correct'
4651 st.spendFee(cr.from);
4652 st.transfer(cr.from, srcdoor, cr.amt);
4653 st.transfer(cr.from, srcdoor, cr.reward);
4654
4655 return ++st.counters[&bridge_].create_count;
4656 }
4657
4658 void
4659 attest(uint64_t time, uint32_t rnd)
4660 {
4661 ChainStateTrack& st = destState();
4662
4663 // check all signers, but start at a random one
4664 size_t i;
4665 for (i = 0; i < num_signers; ++i)
4666 {
4667 size_t signer_idx = (rnd + i) % num_signers;
4668
4669 if (!(cr.attested[signer_idx]))
4670 {
4671 // enqueue one attestation for this signer
4672 cr.attested[signer_idx] = true;
4673
4674 st.signers_attns[signer_idx][&bridge_]
4675 .create_claims[cr.claim_id - 1]
4676 .emplace_back(create_account_attestation(
4677 bridge_.signers[signer_idx].account,
4678 bridge_.jvb,
4679 cr.from,
4680 cr.amt,
4681 cr.reward,
4682 bridge_.signers[signer_idx].account,
4683 cr.a2b,
4684 cr.claim_id,
4685 cr.to,
4686 bridge_.signers[signer_idx]));
4687 break;
4688 }
4689 }
4690
4691 if (i == num_signers)
4692 return; // did not attest
4693
4694 auto& counters = st.counters[&bridge_];
4695 if (counters.create_callbacks.size() < cr.claim_id)
4696 counters.create_callbacks.resize(cr.claim_id);
4697
4698 auto complete_cb = [&](std::vector<size_t> const& signers) {
4699 auto num_attestors = signers.size();
4700 st.env.close();
4701 assert(
4702 num_attestors <=
4703 std::count(cr.attested.begin(), cr.attested.end(), true));
4704 assert(num_attestors >= bridge_.quorum);
4705 assert(cr.claim_id - 1 == counters.claim_count);
4706
4707 auto r = cr.reward;
4708 auto reward = divide(r, STAmount(num_attestors), r.issue());
4709
4710 for (auto i : signers)
4711 st.receive(bridge_.signers[i].account, reward);
4712
4713 st.spend(dstDoor(), reward, num_attestors);
4714 st.transfer(dstDoor(), cr.to, cr.amt);
4715 st.env.env_.memoize(cr.to);
4717 };
4718
4719 counters.create_callbacks[cr.claim_id - 1] = std::move(complete_cb);
4720 }
4721
4722 SmState
4723 advance(uint64_t time, uint32_t rnd)
4724 {
4725 switch (sm_state)
4726 {
4727 case st_initial:
4730 break;
4731
4732 case st_attesting:
4733 attest(time, rnd);
4734 break;
4735
4736 default:
4737 assert(0);
4738 break;
4739
4740 case st_completed:
4741 break; // will get this once
4742 }
4743 return sm_state;
4744 }
4745
4746 private:
4749 };
4750
4751 // --------------------------------------------------
4752 class SmTransfer : public SmBase<SmTransfer>
4753 {
4754 public:
4756
4758 const std::shared_ptr<ChainStateTracker>& chainstate,
4759 const BridgeDef& bridge,
4760 Transfer xfer)
4761 : Base(chainstate, bridge)
4762 , xfer(std::move(xfer))
4764 {
4765 }
4766
4767 bool
4768 a2b() const
4769 {
4770 return xfer.a2b;
4771 }
4772
4773 uint32_t
4775 {
4776 ChainStateTrack& st = destState();
4777
4778 st.env
4781 .close(); // needed for claim_id sequence to be
4782 // correct'
4783 st.spendFee(xfer.to);
4784 return ++st.counters[&bridge_].claim_id;
4785 }
4786
4787 void
4789 {
4790 ChainStateTrack& st = srcState();
4791 jtx::Account const& srcdoor = srcDoor();
4792
4793 if (xfer.amt.issue() != xrpIssue())
4794 {
4795 st.env.tx(pay(srcdoor, xfer.from, xfer.amt));
4796 st.spendFee(srcdoor);
4797 }
4798 st.env.tx(xchain_commit(
4799 xfer.from,
4800 bridge_.jvb,
4801 xfer.claim_id,
4802 xfer.amt,
4804 ? std::nullopt
4806 st.spendFee(xfer.from);
4807 st.transfer(xfer.from, srcdoor, xfer.amt);
4808 }
4809
4810 void
4812 {
4813 auto r = bridge_.reward;
4814 auto reward = divide(r, STAmount(bridge_.quorum), r.issue());
4815
4816 for (size_t i = 0; i < num_signers; ++i)
4817 {
4818 if (xfer.attested[i])
4819 st.receive(bridge_.signers[i].account, reward);
4820 }
4822 }
4823
4824 bool
4825 attest(uint64_t time, uint32_t rnd)
4826 {
4827 ChainStateTrack& st = destState();
4828
4829 // check all signers, but start at a random one
4830 for (size_t i = 0; i < num_signers; ++i)
4831 {
4832 size_t signer_idx = (rnd + i) % num_signers;
4833 if (!(xfer.attested[signer_idx]))
4834 {
4835 // enqueue one attestation for this signer
4836 xfer.attested[signer_idx] = true;
4837
4838 st.signers_attns[signer_idx][&bridge_]
4839 .xfer_claims.emplace_back(claim_attestation(
4840 bridge_.signers[signer_idx].account,
4841 bridge_.jvb,
4842 xfer.from,
4843 xfer.amt,
4844 bridge_.signers[signer_idx].account,
4845 xfer.a2b,
4846 xfer.claim_id,
4848 ? std::nullopt
4850 bridge_.signers[signer_idx]));
4851 break;
4852 }
4853 }
4854
4855 // return true if quorum was reached, false otherwise
4856 bool quorum =
4860 {
4863 }
4864 return quorum;
4865 }
4866
4867 void
4869 {
4870 ChainStateTrack& st = destState();
4871 st.env.tx(xchain_claim(
4875 st.spendFee(xfer.to);
4876 }
4877
4878 SmState
4879 advance(uint64_t time, uint32_t rnd)
4880 {
4881 switch (sm_state)
4882 {
4883 case st_initial:
4886 break;
4887
4888 case st_claimid_created:
4889 commit();
4891 break;
4892
4893 case st_attesting:
4894 sm_state = attest(time, rnd)
4896 : st_completed)
4897 : st_attesting;
4898 break;
4899
4900 case st_attested:
4901 assert(xfer.with_claim == WithClaim::yes);
4902 claim();
4904 break;
4905
4906 default:
4907 case st_completed:
4908 assert(0); // should have been removed
4909 break;
4910 }
4911 return sm_state;
4912 }
4913
4914 private:
4917 };
4918
4919 // --------------------------------------------------
4922
4924
4925 void
4927 uint64_t time,
4928 const std::shared_ptr<ChainStateTracker>& chainstate,
4929 BridgeDef const& bridge,
4930 Transfer transfer)
4931 {
4933 time, SmTransfer(chainstate, bridge, std::move(transfer)));
4934 }
4935
4936 void
4937 ac(uint64_t time,
4938 const std::shared_ptr<ChainStateTracker>& chainstate,
4939 BridgeDef const& bridge,
4941 {
4943 time, SmCreateAccount(chainstate, bridge, std::move(ac)));
4944 }
4945
4946public:
4947 void
4950 bool verify_balances = true)
4951 {
4952 using namespace jtx;
4953 uint64_t time = 0;
4954 std::mt19937 gen(27); // Standard mersenne_twister_engine
4956
4957 while (!sm_.empty())
4958 {
4959 ++time;
4960 for (auto it = sm_.begin(); it != sm_.end();)
4961 {
4962 auto vis = [&](auto& sm) {
4963 uint32_t rnd = distrib(gen);
4964 return sm.advance(time, rnd);
4965 };
4966 auto& [t, sm] = *it;
4967 if (t <= time && std::visit(vis, sm) == st_completed)
4968 it = sm_.erase(it);
4969 else
4970 ++it;
4971 }
4972
4973 // send attestations
4974 st->sendAttestations();
4975
4976 // make sure all transactions have been applied
4977 st->a_.env.close();
4978 st->b_.env.close();
4979
4980 if (verify_balances)
4981 {
4982 BEAST_EXPECT(st->verify());
4983 }
4984 }
4985 }
4986
4987 void
4989 {
4990 using namespace jtx;
4991
4992 testcase("Bridge usage simulation");
4993
4994 XEnv mcEnv(*this);
4995 XEnv scEnv(*this, true);
4996
4997 auto st = std::make_shared<ChainStateTracker>(mcEnv, scEnv);
4998
4999 // create 10 accounts + door funded on both chains, and store
5000 // in ChainStateTracker the initial amount of these accounts
5001 Account doorXRPLocking("doorXRPLocking"),
5002 doorUSDLocking("doorUSDLocking"), doorUSDIssuing("doorUSDIssuing");
5003
5004 constexpr size_t num_acct = 10;
5005 auto a = [&doorXRPLocking, &doorUSDLocking, &doorUSDIssuing]() {
5006 using namespace std::literals;
5007 std::vector<Account> result;
5008 result.reserve(num_acct);
5009 for (int i = 0; i < num_acct; ++i)
5010 result.emplace_back(
5011 "a"s + std::to_string(i),
5013 result.emplace_back("doorXRPLocking");
5014 doorXRPLocking = result.back();
5015 result.emplace_back("doorUSDLocking");
5016 doorUSDLocking = result.back();
5017 result.emplace_back("doorUSDIssuing");
5018 doorUSDIssuing = result.back();
5019 return result;
5020 }();
5021
5022 for (auto& acct : a)
5023 {
5024 STAmount amt{XRP(100000)};
5025
5026 mcEnv.fund(amt, acct);
5027 scEnv.fund(amt, acct);
5028 }
5029 Account USDLocking{"USDLocking"};
5030 IOU usdLocking{USDLocking["USD"]};
5031 IOU usdIssuing{doorUSDIssuing["USD"]};
5032
5033 mcEnv.fund(XRP(100000), USDLocking);
5034 mcEnv.close();
5035 mcEnv.tx(trust(doorUSDLocking, usdLocking(100000)));
5036 mcEnv.close();
5037 mcEnv.tx(pay(USDLocking, doorUSDLocking, usdLocking(50000)));
5038
5039 for (int i = 0; i < a.size(); ++i)
5040 {
5041 auto& acct{a[i]};
5042 if (i < num_acct)
5043 {
5044 mcEnv.tx(trust(acct, usdLocking(100000)));
5045 scEnv.tx(trust(acct, usdIssuing(100000)));
5046 }
5047 st->init(acct);
5048 }
5049 for (auto& s : signers)
5050 st->init(s.account);
5051
5052 st->b_.init(Account::master);
5053
5054 // also create some unfunded accounts
5055 constexpr size_t num_ua = 20;
5056 auto ua = []() {
5057 using namespace std::literals;
5058 std::vector<Account> result;
5059 result.reserve(num_ua);
5060 for (int i = 0; i < num_ua; ++i)
5061 result.emplace_back(
5062 "ua"s + std::to_string(i),
5064 return result;
5065 }();
5066
5067 // initialize a bridge from a BridgeDef
5068 auto initBridge = [&mcEnv, &scEnv, &st](BridgeDef& bd) {
5069 bd.initBridge(mcEnv, scEnv);
5070 st->a_.spendFee(bd.doorA, 2);
5071 st->b_.spendFee(bd.doorB, 2);
5072 };
5073
5074 // create XRP -> XRP bridge
5075 // ------------------------
5076 BridgeDef xrp_b{
5077 doorXRPLocking,
5078 xrpIssue(),
5080 xrpIssue(),
5081 XRP(1),
5082 XRP(20),
5083 quorum,
5084 signers,
5086
5087 initBridge(xrp_b);
5088
5089 // create USD -> USD bridge
5090 // ------------------------
5091 BridgeDef usd_b{
5092 doorUSDLocking,
5093 usdLocking,
5094 doorUSDIssuing,
5095 usdIssuing,
5096 XRP(1),
5097 XRP(20),
5098 quorum,
5099 signers,
5101
5102 initBridge(usd_b);
5103
5104 // try a single account create + transfer to validate the simulation
5105 // engine. Do the transfer 8 time steps after the account create, to
5106 // give time enough for ua[0] to be funded now so it can reserve
5107 // the claimID
5108 // -----------------------------------------------------------------
5109 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, true});
5110 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), true});
5111 runSimulation(st);
5112
5113 // try the same thing in the other direction
5114 // -----------------------------------------
5115 ac(0, st, xrp_b, {a[0], ua[0], XRP(777), xrp_b.reward, false});
5116 xfer(8, st, xrp_b, {a[0], ua[0], a[2], XRP(3), false});
5117 runSimulation(st);
5118
5119 // run multiple XRP transfers
5120 // --------------------------
5121 xfer(0, st, xrp_b, {a[0], a[0], a[1], XRP(6), true, WithClaim::no});
5122 xfer(1, st, xrp_b, {a[0], a[0], a[1], XRP(8), false, WithClaim::no});
5123 xfer(1, st, xrp_b, {a[1], a[1], a[1], XRP(1), true});
5124 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(3), false});
5125 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(5), false});
5126 xfer(2, st, xrp_b, {a[0], a[0], a[1], XRP(7), false, WithClaim::no});
5127 xfer(2, st, xrp_b, {a[1], a[1], a[1], XRP(9), true});
5128 runSimulation(st);
5129
5130 // run one USD transfer
5131 // --------------------
5132 xfer(0, st, usd_b, {a[0], a[1], a[2], usdLocking(3), true});
5133 runSimulation(st);
5134
5135 // run multiple USD transfers
5136 // --------------------------
5137 xfer(0, st, usd_b, {a[0], a[0], a[1], usdLocking(6), true});
5138 xfer(1, st, usd_b, {a[0], a[0], a[1], usdIssuing(8), false});
5139 xfer(1, st, usd_b, {a[1], a[1], a[1], usdLocking(1), true});
5140 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(3), false});
5141 xfer(2, st, usd_b, {a[1], a[1], a[1], usdIssuing(5), false});
5142 xfer(2, st, usd_b, {a[0], a[0], a[1], usdIssuing(7), false});
5143 xfer(2, st, usd_b, {a[1], a[1], a[1], usdLocking(9), true});
5144 runSimulation(st);
5145
5146 // run mixed transfers
5147 // -------------------
5148 xfer(0, st, xrp_b, {a[0], a[0], a[0], XRP(1), true});
5149 xfer(0, st, usd_b, {a[1], a[3], a[3], usdIssuing(3), false});
5150 xfer(0, st, usd_b, {a[3], a[2], a[1], usdIssuing(5), false});
5151
5152 xfer(1, st, xrp_b, {a[0], a[0], a[0], XRP(4), false});
5153 xfer(1, st, xrp_b, {a[1], a[1], a[0], XRP(8), true});
5154 xfer(1, st, usd_b, {a[4], a[1], a[1], usdLocking(7), true});
5155
5156 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(7), true});
5157 xfer(3, st, xrp_b, {a[0], a[4], a[3], XRP(2), false});
5158 xfer(3, st, xrp_b, {a[1], a[1], a[0], XRP(9), true});
5159 xfer(3, st, usd_b, {a[3], a[1], a[1], usdIssuing(11), false});
5160 runSimulation(st);
5161
5162 // run multiple account create to stress attestation batching
5163 // ----------------------------------------------------------
5164 ac(0, st, xrp_b, {a[0], ua[1], XRP(301), xrp_b.reward, true});
5165 ac(0, st, xrp_b, {a[1], ua[2], XRP(302), xrp_b.reward, true});
5166 ac(1, st, xrp_b, {a[0], ua[3], XRP(303), xrp_b.reward, true});
5167 ac(2, st, xrp_b, {a[1], ua[4], XRP(304), xrp_b.reward, true});
5168 ac(3, st, xrp_b, {a[0], ua[5], XRP(305), xrp_b.reward, true});
5169 ac(4, st, xrp_b, {a[1], ua[6], XRP(306), xrp_b.reward, true});
5170 ac(6, st, xrp_b, {a[0], ua[7], XRP(307), xrp_b.reward, true});
5171 ac(7, st, xrp_b, {a[2], ua[8], XRP(308), xrp_b.reward, true});
5172 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5173 ac(9, st, xrp_b, {a[0], ua[9], XRP(309), xrp_b.reward, true});
5174 ac(10, st, xrp_b, {a[0], ua[10], XRP(310), xrp_b.reward, true});
5175 ac(12, st, xrp_b, {a[0], ua[11], XRP(311), xrp_b.reward, true});
5176 ac(12, st, xrp_b, {a[3], ua[12], XRP(312), xrp_b.reward, true});
5177 ac(12, st, xrp_b, {a[4], ua[13], XRP(313), xrp_b.reward, true});
5178 ac(12, st, xrp_b, {a[3], ua[14], XRP(314), xrp_b.reward, true});
5179 ac(12, st, xrp_b, {a[6], ua[15], XRP(315), xrp_b.reward, true});
5180 ac(13, st, xrp_b, {a[7], ua[16], XRP(316), xrp_b.reward, true});
5181 ac(15, st, xrp_b, {a[3], ua[17], XRP(317), xrp_b.reward, true});
5182 runSimulation(st, true); // balances verification working now.
5183 }
5184
5185 void
5186 run() override
5187 {
5189 }
5190};
5191
5192BEAST_DEFINE_TESTSUITE(XChain, app, ripple);
5193BEAST_DEFINE_TESTSUITE(XChainSim, app, ripple);
5194
5195} // namespace ripple::test
T all_of(T... args)
T apply(T... args)
T back(T... args)
T begin(T... args)
Represents a JSON value.
Definition: json_value.h:148
A testsuite class.
Definition: suite.h:55
void pass()
Record a successful test condition.
Definition: suite.h:511
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
virtual Config & config()=0
std::unordered_set< uint256, beast::uhash<> > features
Definition: Config.h:277
A currency issued by an account.
Definition: Issue.h:36
Issue const & issue() const
Definition: STAmount.h:487
static constexpr TERSubset fromInt(int from)
Definition: TER.h:411
std::shared_ptr< ChainStateTracker > st_
SmBase(const std::shared_ptr< ChainStateTracker > &chainstate, const BridgeDef &bridge)
SmCreateAccount(const std::shared_ptr< ChainStateTracker > &chainstate, const BridgeDef &bridge, AccountCreate create)
SmState advance(uint64_t time, uint32_t rnd)
void attest(uint64_t time, uint32_t rnd)
void distribute_reward(ChainStateTrack &st)
SmState advance(uint64_t time, uint32_t rnd)
SmTransfer(const std::shared_ptr< ChainStateTracker > &chainstate, const BridgeDef &bridge, Transfer xfer)
bool attest(uint64_t time, uint32_t rnd)
Immutable cryptographic account descriptor.
Definition: Account.h:39
static Account const master
The master account.
Definition: Account.h:48
A transaction testing environment.
Definition: Env.h:120
TER ter() const
Return the TER for the last JTx.
Definition: Env.h:583
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:328
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
void enableFeature(uint256 const feature)
Definition: Env.cpp:588
Application & app()
Definition: Env.h:258
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:233
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition: Env.cpp:179
void memoize(Account const &account)
Associate AccountID with account.
Definition: Env.cpp:152
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:221
Converts to IOU Issue or STAmount.
Set the fee on a JTx.
Definition: fee.h:37
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
Set the flags on a JTx.
Definition: txflags.h:31
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 make_pair(T... args)
@ nullValue
'null' value
Definition: json_value.h:37
Severity
Severity level / threshold of a Journal message.
Definition: Journal.h:32
Keylet xChainClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:469
Keylet bridge(STXChainBridge const &bridge, STXChainBridge::ChainType chainType)
Definition: Indexes.cpp:456
Keylet xChainCreateAccountClaimID(STXChainBridge const &bridge, std::uint64_t seq)
Definition: Indexes.cpp:483
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM
Definition: xchain_bridge.h:38
Json::Value create_account_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, jtx::signer const &signer)
JValueVec claim_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
Json::Value bridge_create(Account const &acc, Json::Value const &bridge, STAmount const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value claim_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t claimID, std::optional< jtx::Account > const &dst, jtx::signer const &signer)
Json::Value bridge(Account const &lockingChainDoor, Issue const &lockingChainIssue, Account const &issuingChainDoor, Issue const &issuingChainIssue)
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition: regkey.cpp:29
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition: multisign.cpp:34
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:32
std::vector< Json::Value > JValueVec
Definition: xchain_bridge.h:35
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:29
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
Json::Value xchain_claim(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, Account const &dst)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
Definition: xchain_bridge.h:37
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:30
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
Json::Value bridge_modify(Account const &acc, Json::Value const &bridge, std::optional< STAmount > const &reward, std::optional< STAmount > const &minAccountCreate)
Json::Value 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 acctdelete(Account const &account, Account const &dest)
Delete account.
Definition: acctdelete.cpp:30
Json::Value xchain_commit(Account const &acc, Json::Value const &bridge, std::uint32_t claimID, AnyAmount const &amt, std::optional< Account > const &dst)
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
FeatureBitset supported_amendments()
Definition: Env.h:73
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:118
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:93
constexpr std::uint32_t asfDepositAuth
Definition: TxFlags.h:84
constexpr std::uint32_t asfRequireDest
Definition: TxFlags.h:76
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:53
constexpr std::uint32_t tfFillOrKill
Definition: TxFlags.h:98
@ current
This was a new validation and was added.
constexpr std::uint32_t asfDisableMaster
Definition: TxFlags.h:79
std::string transToken(TER code)
Definition: TER.cpp:257
constexpr std::uint32_t tfClearAccountCreateAmount
Definition: TxFlags.h:225
@ tecNO_ENTRY
Definition: TER.h:293
@ tecNO_DST
Definition: TER.h:277
@ tecXCHAIN_SELF_COMMIT
Definition: TER.h:337
@ tecXCHAIN_INSUFF_CREATE_AMOUNT
Definition: TER.h:333
@ tecXCHAIN_CREATE_ACCOUNT_DISABLED
Definition: TER.h:339
@ tecNO_ISSUER
Definition: TER.h:286
@ tecXCHAIN_NO_CLAIM_ID
Definition: TER.h:324
@ tecXCHAIN_CLAIM_NO_QUORUM
Definition: TER.h:326
@ tecXCHAIN_BAD_CLAIM_ID
Definition: TER.h:325
@ tecDUPLICATE
Definition: TER.h:302
@ tecXCHAIN_ACCOUNT_CREATE_PAST
Definition: TER.h:334
@ tecNO_PERMISSION
Definition: TER.h:292
@ tecDST_TAG_NEEDED
Definition: TER.h:296
@ tecHAS_OBLIGATIONS
Definition: TER.h:304
@ tecXCHAIN_REWARD_MISMATCH
Definition: TER.h:330
@ tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR
Definition: TER.h:338
@ tecUNFUNDED_PAYMENT
Definition: TER.h:272
@ tecXCHAIN_NO_SIGNERS_LIST
Definition: TER.h:331
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:294
@ tesSUCCESS
Definition: TER.h:242
@ terNO_LINE
Definition: TER.h:219
@ temBAD_ISSUER
Definition: TER.h:93
@ temBAD_AMOUNT
Definition: TER.h:89
@ temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT
Definition: TER.h:135
@ temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT
Definition: TER.h:136
@ temXCHAIN_EQUAL_DOOR_ACCOUNTS
Definition: TER.h:131
@ temXCHAIN_BRIDGE_NONDOOR_OWNER
Definition: TER.h:134
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temXCHAIN_BRIDGE_BAD_ISSUES
Definition: TER.h:133
@ temDISABLED
Definition: TER.h:114
STL namespace.
T reserve(T... args)
T size(T... args)
bool payees_received(STAmount const &reward) const
std::vector< balance > reward_accounts
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payor, jtx::Account const *payees, size_t num_payees, bool withClaim)
bool check_most_balances(STAmount const &amt, STAmount const &reward)
BalanceTransfer(T &env, jtx::Account const &from_acct, jtx::Account const &to_acct, jtx::Account const &payor, std::vector< jtx::Account > const &payees, bool withClaim)
bool has_happened(STAmount const &amt, STAmount const &reward, bool check_payer=true)
Balance(T &env, jtx::Account const &account)
jtx::Account const & account_
STAmount diff() const
void initBridge(ENV &mcEnv, ENV &scEnv)
std::vector< jtx::signer > const & signers
std::shared_ptr< SLE const > bridge(Json::Value const &jvb)
SEnv(T &s, std::unique_ptr< Config > config, FeatureBitset features, std::unique_ptr< Logs > logs=nullptr, beast::severities::Severity thresh=beast::severities::kError)
Definition: XChain_test.cpp:57
SEnv & enableFeature(uint256 const feature)
Definition: XChain_test.cpp:75
std::uint64_t claimCount(Json::Value const &jvb)
std::shared_ptr< SLE const > account(jtx::Account const &account)
SEnv & multiTx(jtx::JValueVec &&jvv, FN const &... fN)
std::shared_ptr< SLE const > caClaimID(Json::Value const &jvb, std::uint64_t seq)
std::uint64_t claimID(Json::Value const &jvb)
std::shared_ptr< SLE const > claimID(Json::Value const &jvb, std::uint64_t seq)
SEnv & tx(JsonValue &&jv, FN const &... fN)
Definition: XChain_test.cpp:98
XRPAmount reserve(std::uint32_t count)
STAmount balance(jtx::Account const &account) const
SEnv & disableFeature(uint256 const feature)
Definition: XChain_test.cpp:82
STAmount balance(jtx::Account const &account, Issue const &issue) const
SEnv & fund(STAmount const &amount, Arg const &arg, Args const &... args)
Definition: XChain_test.cpp:90
std::array< bool, num_signers > attested
void init(ENV &env, jtx::Account const &acct)
bool verify(ENV &env, jtx::Account const &acct) const
void spend(jtx::Account const &acct, STAmount amt, std::uint64_t times=1)
void transfer(jtx::Account const &from, jtx::Account const &to, STAmount amt)
uint32_t sendCreateAttestations(size_t signer_idx, BridgeID bridge, CreateClaimVec &claims)
void spendFee(jtx::Account const &acct, size_t times=1)
std::map< BridgeID, BridgeCounters > counters
void sendAttestations(size_t signer_idx, BridgeID bridge, ClaimVec &claims)
void receive(jtx::Account const &acct, STAmount amt, std::uint64_t divisor=1)
std::map< jtx::Account, AccountStateTrack > accounts
std::array< bool, num_signers > attested
void ac(uint64_t time, const std::shared_ptr< ChainStateTracker > &chainstate, BridgeDef const &bridge, AccountCreate ac)
void xfer(uint64_t time, const std::shared_ptr< ChainStateTracker > &chainstate, BridgeDef const &bridge, Transfer transfer)
void run() override
Runs the suite.
static constexpr size_t num_signers
void runSimulation(std::shared_ptr< ChainStateTracker > const &st, bool verify_balances=true)
void run() override
Runs the suite.
void testXChainAddAccountCreateNonBatchAttestation()
XRPAmount reserve(std::uint32_t count)
void testXChainAddClaimNonBatchAttestation()
XEnv(T &s, bool side=false)
STAmount const & value() const
JValueVec att_create_acct_vec(std::uint64_t createCount, jtx::AnyAmount const &amt, jtx::Account const &dst, std::size_t const numAtts, std::size_t const fromIdx=0)
std::vector< signer > const alt_signers
Json::Value create_bridge(Account const &acc, Json::Value const &bridge=Json::nullValue, STAmount const &_reward=XRP(1), std::optional< STAmount > const &minAccountCreate=std::nullopt)
std::vector< Account > const payees
std::vector< signer > const signers
Set the sequence number on a JTx.
Definition: seq.h:34
A signer in a SignerList.
Definition: multisign.h:38
T tmpnam(T... args)
T to_string(T... args)
T visit(T... args)