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