From 2a15aed979ce70f53be810bb101fcc17a2f4fba9 Mon Sep 17 00:00:00 2001 From: bthomee Date: Wed, 16 Jul 2025 10:57:31 +0000 Subject: [PATCH] deploy: c9135a63cd553a3276f51047d95c461e0d972a64 --- BookDirs__test_8cpp_source.html | 19 +- BookStep_8cpp_source.html | 1525 ++-- ClosureCounter__test_8cpp_source.html | 2 +- CreateOffer_8cpp_source.html | 2589 +++--- CreateOffer_8h_source.html | 130 +- CrossingLimits__test_8cpp_source.html | 937 +- DeliverMin__test_8cpp_source.html | 18 +- Discrepancy__test_8cpp_source.html | 17 +- Flow__test_8cpp_source.html | 70 +- Freeze__test_8cpp_source.html | 2211 +++-- GatewayBalances__test_8cpp_source.html | 31 +- Handler__test_8cpp_source.html | 2 +- Livecache__test_8cpp_source.html | 2 +- NoRipple__test_8cpp_source.html | 19 +- Offer__test_8cpp_source.html | 8100 ++++++++--------- PaySteps_8cpp_source.html | 6 +- PayStrand__test_8cpp_source.html | 39 +- PaymentSandbox__test_8cpp_source.html | 19 +- PermissionedDEX__test_8cpp_source.html | 2650 +++--- SetAuth__test_8cpp_source.html | 19 +- Steps_8h_source.html | 8 +- StrandFlow_8h_source.html | 2 +- TrustAndBalance__test_8cpp_source.html | 17 +- annotated.html | 201 +- ...d__associative__container__test__base.html | 2 +- classbeast_1_1unit__test_1_1suite.html | 2 +- classes.html | 2 +- classripple_1_1BookStep.html | 18 +- classripple_1_1CreateOffer-members.html | 15 +- classripple_1_1CreateOffer.html | 80 +- classripple_1_1Freeze__test.html | 18 +- classripple_1_1NodeStore_1_1TestBase.html | 2 +- classripple_1_1TestSuite.html | 2 +- classripple_1_1test_1_1AccountInfo__test.html | 2 +- ...1test_1_1CrossingLimits__test-members.html | 25 +- ...ipple_1_1test_1_1CrossingLimits__test.html | 32 +- ...ple_1_1test_1_1OfferAllFeatures__test.html | 106 +- ...ripple_1_1test_1_1OfferBaseUtil__test.html | 119 +- ...1_1OfferBaseUtil__test__inherit__graph.map | 13 +- ...1_1OfferBaseUtil__test__inherit__graph.md5 | 2 +- ...1_1OfferBaseUtil__test__inherit__graph.png | Bin 39296 -> 33513 bytes ...le_1_1test_1_1OfferWOFillOrKill__test.html | 106 +- ...est_1_1OfferWOFlowCross__test-members.html | 187 - ...ple_1_1test_1_1OfferWOFlowCross__test.html | 3286 ------- ...1_1OfferWOFlowCross__test__coll__graph.map | 21 - ...1_1OfferWOFlowCross__test__coll__graph.md5 | 1 - ...1_1OfferWOFlowCross__test__coll__graph.png | Bin 74229 -> 0 bytes ...OfferWOFlowCross__test__inherit__graph.map | 5 - ...OfferWOFlowCross__test__inherit__graph.md5 | 1 - ...OfferWOFlowCross__test__inherit__graph.png | Bin 7150 -> 0 bytes ...ipple_1_1test_1_1OfferWOPermDEX__test.html | 106 +- ..._1_1test_1_1OfferWOSmallQOffers__test.html | 106 +- ..._1_1test_1_1OfferWTakerDryOffer__test.html | 106 +- ...ripple_1_1test_1_1Offer__manual__test.html | 106 +- ...pple_1_1test_1_1PermissionedDEX__test.html | 24 +- classripple_1_1test_1_1TestOutputSuite.html | 2 +- classripple_1_1test_1_1jtx_1_1AMMTest.html | 2 +- ...sripple_1_1test_1_1jtx_1_1AMMTestBase.html | 2 +- functions_a.html | 4 +- functions_c.html | 14 +- functions_d.html | 14 +- functions_e.html | 6 +- functions_func_b.html | 2 +- functions_func_c.html | 18 +- functions_func_e.html | 6 +- functions_func_l.html | 4 +- functions_func_m.html | 2 +- functions_func_r.html | 6 +- functions_func_s.html | 12 +- functions_func_t.html | 10 +- functions_func_v.html | 12 +- functions_k.html | 2 +- functions_l.html | 4 +- functions_m.html | 4 +- functions_q.html | 3 +- functions_r.html | 22 +- functions_s.html | 27 +- functions_t.html | 38 +- functions_v.html | 9 +- functions_w.html | 8 +- hierarchy.html | 9 +- inherit_graph_94.map | 207 +- inherit_graph_94.md5 | 2 +- inherit_graph_94.png | Bin 2292992 -> 2291122 bytes inherits.html | 79 +- namespacemembers_b.html | 2 +- namespacemembers_func_b.html | 2 +- namespaceripple.html | 12 +- namespaceripple_1_1test.html | 90 +- namespaces.html | 201 +- search/all_10.js | 834 +- search/all_11.js | 942 +- search/all_12.js | 19 +- search/all_13.js | 650 +- search/all_14.js | 2507 ++--- search/all_15.js | 4113 ++++----- search/all_17.js | 491 +- search/all_18.js | 168 +- search/all_1b.js | 4 +- search/all_1c.js | 453 +- search/all_2.js | 982 +- search/all_3.js | 662 +- search/all_4.js | 1388 ++- search/all_5.js | 454 +- search/all_6.js | 256 +- search/all_7.js | 639 +- search/all_a.js | 1504 +-- search/all_b.js | 235 +- search/all_c.js | 8 +- search/all_d.js | 308 +- search/all_e.js | 1366 +-- search/all_f.js | 174 +- search/classes_e.js | 213 +- search/enums_2.js | 4 +- search/enumvalues_e.js | 63 +- search/functions_1.js | 31 +- search/functions_10.js | 77 +- search/functions_12.js | 14 +- search/functions_13.js | 496 +- search/functions_14.js | 2934 +++--- search/functions_16.js | 152 +- search/functions_17.js | 4 +- search/functions_1a.js | 13 +- search/functions_1b.js | 453 +- search/functions_2.js | 317 +- search/functions_3.js | 18 +- search/functions_4.js | 8 +- search/functions_5.js | 10 +- search/functions_6.js | 4 +- search/functions_9.js | 687 +- search/functions_a.js | 10 +- search/functions_b.js | 37 +- search/functions_c.js | 4 +- search/functions_d.js | 8 +- search/functions_f.js | 6 +- search/typedefs_c.js | 4 +- search/variables_13.js | 8 +- search/variables_16.js | 6 +- search/variables_3.js | 4 +- search/variables_c.js | 4 +- search/variables_d.js | 8 +- ...tripple_1_1test_1_1Flow__manual__test.html | 4 +- 142 files changed, 21936 insertions(+), 25787 deletions(-) delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test-members.html delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test.html delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test__coll__graph.map delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test__coll__graph.md5 delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test__coll__graph.png delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test__inherit__graph.map delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test__inherit__graph.md5 delete mode 100644 classripple_1_1test_1_1OfferWOFlowCross__test__inherit__graph.png diff --git a/BookDirs__test_8cpp_source.html b/BookDirs__test_8cpp_source.html index afc9709371..737e46b5f5 100644 --- a/BookDirs__test_8cpp_source.html +++ b/BookDirs__test_8cpp_source.html @@ -182,16 +182,15 @@ $(function() {
104 {
105 using namespace jtx;
106 auto const sa = supported_amendments();
-
107 test_bookdir(sa - featureFlowCross - featurePermissionedDEX);
-
108 test_bookdir(sa - featurePermissionedDEX);
-
109 test_bookdir(sa);
-
110 }
-
111};
-
112
-
113BEAST_DEFINE_TESTSUITE(BookDirs, ledger, ripple);
-
114
-
115} // namespace test
-
116} // namespace ripple
+
107 test_bookdir(sa - featurePermissionedDEX);
+
108 test_bookdir(sa);
+
109 }
+
110};
+
111
+
112BEAST_DEFINE_TESTSUITE(BookDirs, ledger, ripple);
+
113
+
114} // namespace test
+
115} // namespace ripple
T begin(T... args)
A testsuite class.
Definition: suite.h:55
Definition: BookDirs.h:30
diff --git a/BookStep_8cpp_source.html b/BookStep_8cpp_source.html index 358c7e96f8..0edb3dadd8 100644 --- a/BookStep_8cpp_source.html +++ b/BookStep_8cpp_source.html @@ -814,754 +814,753 @@ $(function() {
743 FlowOfferStream<TIn, TOut> offers(
744 sb, afView, book_, sb.parentCloseTime(), counter, j_);
745
-
746 bool const flowCross = afView.rules().enabled(featureFlowCross);
-
747 bool offerAttempted = false;
-
748 std::optional<Quality> ofrQ;
-
749 auto execOffer = [&](auto& offer) {
-
750 // Note that offer.quality() returns a (non-optional) Quality. So
-
751 // ofrQ is always safe to use below this point in the lambda.
-
752 if (!ofrQ)
-
753 ofrQ = offer.quality();
-
754 else if (*ofrQ != offer.quality())
-
755 return false;
-
756
-
757 if (static_cast<TDerived const*>(this)->limitSelfCrossQuality(
-
758 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
-
759 return true;
-
760
-
761 // Make sure offer owner has authorization to own IOUs from issuer.
-
762 // An account can always own XRP or their own IOUs.
-
763 if (flowCross && (!isXRP(offer.issueIn().currency)) &&
-
764 (offer.owner() != offer.issueIn().account))
-
765 {
-
766 auto const& issuerID = offer.issueIn().account;
-
767 auto const issuer = afView.read(keylet::account(issuerID));
-
768 if (issuer && ((*issuer)[sfFlags] & lsfRequireAuth))
-
769 {
-
770 // Issuer requires authorization. See if offer owner has that.
-
771 auto const& ownerID = offer.owner();
-
772 auto const authFlag =
-
773 issuerID > ownerID ? lsfHighAuth : lsfLowAuth;
-
774
-
775 auto const line = afView.read(
-
776 keylet::line(ownerID, issuerID, offer.issueIn().currency));
-
777
-
778 if (!line || (((*line)[sfFlags] & authFlag) == 0))
-
779 {
-
780 // Offer owner not authorized to hold IOU from issuer.
-
781 // Remove this offer even if no crossing occurs.
-
782 if (auto const key = offer.key())
-
783 offers.permRmOffer(*key);
-
784 if (!offerAttempted)
-
785 // Change quality only if no previous offers were tried.
-
786 ofrQ = std::nullopt;
-
787 // Returning true causes offers.step() to delete the offer.
-
788 return true;
-
789 }
-
790 }
-
791 }
-
792
-
793 if (!static_cast<TDerived const*>(this)->checkQualityThreshold(
-
794 offer.quality()))
-
795 return false;
-
796
-
797 auto const [ofrInRate, ofrOutRate] = offer.adjustRates(
-
798 static_cast<TDerived const*>(this)->getOfrInRate(
-
799 prevStep_, offer.owner(), trIn),
-
800 static_cast<TDerived const*>(this)->getOfrOutRate(
-
801 prevStep_, offer.owner(), strandDst_, trOut));
-
802
-
803 auto ofrAmt = offer.amount();
-
804 TAmounts stpAmt{
-
805 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true),
-
806 ofrAmt.out};
-
807
-
808 // owner pays the transfer fee.
-
809 auto ownerGives =
-
810 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE, /*roundUp*/ false);
-
811
-
812 auto const funds = offer.isFunded()
-
813 ? ownerGives // Offer owner is issuer; they have unlimited funds
-
814 : offers.ownerFunds();
-
815
-
816 // Only if CLOB offer
-
817 if (funds < ownerGives)
-
818 {
-
819 // We already know offer.owner()!=offer.issueOut().account
-
820 ownerGives = funds;
-
821 stpAmt.out = mulRatio(
-
822 ownerGives, QUALITY_ONE, ofrOutRate, /*roundUp*/ false);
-
823
-
824 // It turns out we can prevent order book blocking by (strictly)
-
825 // rounding down the ceil_out() result. This adjustment changes
-
826 // transaction outcomes, so it must be made under an amendment.
-
827 ofrAmt = offer.limitOut(ofrAmt, stpAmt.out, /*roundUp*/ false);
-
828
-
829 stpAmt.in =
-
830 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true);
-
831 }
-
832
-
833 offerAttempted = true;
-
834 return callback(
-
835 offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate);
-
836 };
-
837
-
838 // At any payment engine iteration, AMM offer can only be consumed once.
-
839 auto tryAMM = [&](std::optional<Quality> const& lobQuality) -> bool {
-
840 // amm doesn't support domain yet
-
841 if (book_.domain)
-
842 return true;
-
843
-
844 // If offer crossing then use either LOB quality or nullopt
-
845 // to prevent AMM being blocked by a lower quality LOB.
-
846 auto const qualityThreshold = [&]() -> std::optional<Quality> {
-
847 if (sb.rules().enabled(fixAMMv1_1) && lobQuality)
-
848 return static_cast<TDerived const*>(this)->qualityThreshold(
-
849 *lobQuality);
-
850 return lobQuality;
-
851 }();
-
852 auto ammOffer = getAMMOffer(sb, qualityThreshold);
-
853 return !ammOffer || execOffer(*ammOffer);
-
854 };
-
855
-
856 if (offers.step())
-
857 {
-
858 if (tryAMM(offers.tip().quality()))
-
859 {
-
860 do
-
861 {
-
862 if (!execOffer(offers.tip()))
-
863 break;
-
864 } while (offers.step());
-
865 }
-
866 }
-
867 else
-
868 {
-
869 // Might have AMM offer if there are no LOB offers.
-
870 tryAMM(std::nullopt);
-
871 }
-
872
-
873 return {offers.permToRemove(), counter.count()};
-
874}
-
875
-
876template <class TIn, class TOut, class TDerived>
-
877template <template <typename, typename> typename Offer>
-
878void
-
879BookStep<TIn, TOut, TDerived>::consumeOffer(
-
880 PaymentSandbox& sb,
-
881 Offer<TIn, TOut>& offer,
-
882 TAmounts<TIn, TOut> const& ofrAmt,
-
883 TAmounts<TIn, TOut> const& stepAmt,
-
884 TOut const& ownerGives) const
-
885{
-
886 if (!offer.checkInvariant(ofrAmt, j_))
-
887 {
-
888 // purposely written as separate if statements so we get logging even
-
889 // when the amendment isn't active.
-
890 if (sb.rules().enabled(fixAMMOverflowOffer))
-
891 {
-
892 Throw<FlowException>(
-
893 tecINVARIANT_FAILED, "AMM pool product invariant failed.");
-
894 }
-
895 }
-
896
-
897 // The offer owner gets the ofrAmt. The difference between ofrAmt and
-
898 // stepAmt is a transfer fee that goes to book_.in.account
-
899 {
-
900 auto const dr = offer.send(
-
901 sb,
-
902 book_.in.account,
-
903 offer.owner(),
-
904 toSTAmount(ofrAmt.in, book_.in),
-
905 j_);
-
906 if (dr != tesSUCCESS)
-
907 Throw<FlowException>(dr);
-
908 }
-
909
-
910 // The offer owner pays `ownerGives`. The difference between ownerGives and
-
911 // stepAmt is a transfer fee that goes to book_.out.account
-
912 {
-
913 auto const cr = offer.send(
-
914 sb,
-
915 offer.owner(),
-
916 book_.out.account,
-
917 toSTAmount(ownerGives, book_.out),
-
918 j_);
-
919 if (cr != tesSUCCESS)
-
920 Throw<FlowException>(cr);
-
921 }
-
922
-
923 offer.consume(sb, ofrAmt);
-
924}
-
925
-
926template <class TIn, class TOut, class TDerived>
-
927std::optional<AMMOffer<TIn, TOut>>
-
928BookStep<TIn, TOut, TDerived>::getAMMOffer(
-
929 ReadView const& view,
-
930 std::optional<Quality> const& clobQuality) const
-
931{
-
932 if (ammLiquidity_)
-
933 return ammLiquidity_->getOffer(view, clobQuality);
-
934 return std::nullopt;
-
935}
-
936
-
937template <class TIn, class TOut, class TDerived>
-
938std::optional<std::variant<Quality, AMMOffer<TIn, TOut>>>
-
939BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
-
940{
-
941 // This can be simplified (and sped up) if directories are never empty.
-
942 Sandbox sb(&view, tapNONE);
-
943 BookTip bt(sb, book_);
-
944 auto const lobQuality =
-
945 bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
-
946 // Multi-path offer generates an offer with the quality
-
947 // calculated from the offer size and the quality is constant in this case.
-
948 // Single path offer quality changes with the offer size. Spot price quality
-
949 // (SPQ) can't be used in this case as the upper bound quality because
-
950 // even if SPQ quality is better than LOB quality, it might not be possible
-
951 // to generate AMM offer at or better quality than LOB quality. Another
-
952 // factor to consider is limit quality on offer crossing. If LOB quality
-
953 // is greater than limit quality then use LOB quality when generating AMM
-
954 // offer, otherwise don't use quality threshold when generating AMM offer.
-
955 // AMM or LOB offer, whether multi-path or single path then can be selected
-
956 // based on the best offer quality. Using the quality to generate AMM offer
-
957 // in this case also prevents the payment engine from going into multiple
-
958 // iterations to cross a LOB offer. This happens when AMM changes
-
959 // the out amount at the start of iteration to match the limitQuality
-
960 // on offer crossing but AMM can't generate the offer at this quality,
-
961 // as the result a LOB offer is partially crossed, and it might take a few
-
962 // iterations to fully cross the offer.
-
963 auto const qualityThreshold = [&]() -> std::optional<Quality> {
-
964 if (view.rules().enabled(fixAMMv1_1) && lobQuality)
-
965 return static_cast<TDerived const*>(this)->qualityThreshold(
-
966 *lobQuality);
-
967 return std::nullopt;
-
968 }();
-
969 // AMM quality is better or no LOB offer
-
970 if (auto const ammOffer = getAMMOffer(view, qualityThreshold); ammOffer &&
-
971 ((lobQuality && ammOffer->quality() > lobQuality) || !lobQuality))
-
972 return ammOffer;
-
973 // LOB quality is better or nullopt
-
974 return lobQuality;
-
975}
-
976
-
977template <class TIn, class TOut, class TDerived>
-
978auto
-
979BookStep<TIn, TOut, TDerived>::tipOfferQuality(ReadView const& view) const
-
980 -> std::optional<std::pair<Quality, OfferType>>
-
981{
-
982 if (auto const res = tip(view); !res)
-
983 return std::nullopt;
-
984 else if (auto const q = std::get_if<Quality>(&(*res)))
-
985 return std::make_pair(*q, OfferType::CLOB);
-
986 else
-
987 return std::make_pair(
-
988 std::get<AMMOffer<TIn, TOut>>(*res).quality(), OfferType::AMM);
-
989}
-
990
-
991template <class TIn, class TOut, class TDerived>
-
992std::optional<QualityFunction>
-
993BookStep<TIn, TOut, TDerived>::tipOfferQualityF(ReadView const& view) const
-
994{
-
995 if (auto const res = tip(view); !res)
-
996 return std::nullopt;
-
997 else if (auto const q = std::get_if<Quality>(&(*res)))
-
998 return QualityFunction{*q, QualityFunction::CLOBLikeTag{}};
-
999 else
-
1000 return std::get<AMMOffer<TIn, TOut>>(*res).getQualityFunc();
-
1001}
-
1002
-
1003template <class TCollection>
-
1004static auto
-
1005sum(TCollection const& col)
-
1006{
-
1007 using TResult = std::decay_t<decltype(*col.begin())>;
-
1008 if (col.empty())
-
1009 return TResult{beast::zero};
-
1010 return std::accumulate(col.begin() + 1, col.end(), *col.begin());
-
1011};
-
1012
-
1013template <class TIn, class TOut, class TDerived>
-
1014std::pair<TIn, TOut>
-
1015BookStep<TIn, TOut, TDerived>::revImp(
-
1016 PaymentSandbox& sb,
-
1017 ApplyView& afView,
-
1018 boost::container::flat_set<uint256>& ofrsToRm,
-
1019 TOut const& out)
-
1020{
-
1021 cache_.reset();
-
1022
-
1023 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
-
1024
-
1025 auto remainingOut = out;
-
1026
-
1027 boost::container::flat_multiset<TIn> savedIns;
-
1028 savedIns.reserve(64);
-
1029 boost::container::flat_multiset<TOut> savedOuts;
-
1030 savedOuts.reserve(64);
-
1031
-
1032 /* amt fed will be adjusted by owner funds (and may differ from the offer's
-
1033 amounts - tho always <=)
-
1034 Return true to continue to receive offers, false to stop receiving offers.
-
1035 */
-
1036 auto eachOffer = [&](auto& offer,
-
1037 TAmounts<TIn, TOut> const& ofrAmt,
-
1038 TAmounts<TIn, TOut> const& stpAmt,
-
1039 TOut const& ownerGives,
-
1040 std::uint32_t transferRateIn,
-
1041 std::uint32_t transferRateOut) mutable -> bool {
-
1042 if (remainingOut <= beast::zero)
-
1043 return false;
-
1044
-
1045 if (stpAmt.out <= remainingOut)
-
1046 {
-
1047 savedIns.insert(stpAmt.in);
-
1048 savedOuts.insert(stpAmt.out);
-
1049 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
-
1050 remainingOut = out - result.out;
-
1051 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
-
1052 // return true b/c even if the payment is satisfied,
-
1053 // we need to consume the offer
-
1054 return true;
-
1055 }
-
1056 else
-
1057 {
-
1058 auto ofrAdjAmt = ofrAmt;
-
1059 auto stpAdjAmt = stpAmt;
-
1060 auto ownerGivesAdj = ownerGives;
-
1061 limitStepOut(
-
1062 offer,
-
1063 ofrAdjAmt,
-
1064 stpAdjAmt,
-
1065 ownerGivesAdj,
-
1066 transferRateIn,
-
1067 transferRateOut,
-
1068 remainingOut);
-
1069 remainingOut = beast::zero;
-
1070 savedIns.insert(stpAdjAmt.in);
-
1071 savedOuts.insert(remainingOut);
-
1072 result.in = sum(savedIns);
-
1073 result.out = out;
-
1074 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
-
1075
-
1076 // Explicitly check whether the offer is funded. Given that we have
-
1077 // (stpAmt.out > remainingOut), it's natural to assume the offer
-
1078 // will still be funded after consuming remainingOut but that is
-
1079 // not always the case. If the mantissas of two IOU amounts differ
-
1080 // by less than ten, then subtracting them leaves a zero.
-
1081 return offer.fully_consumed();
-
1082 }
-
1083 };
-
1084
-
1085 {
-
1086 auto const prevStepDebtDir = [&] {
-
1087 if (prevStep_)
-
1088 return prevStep_->debtDirection(sb, StrandDirection::reverse);
-
1089 return DebtDirection::issues;
-
1090 }();
-
1091 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
-
1092 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
-
1093 std::uint32_t const offersConsumed = std::get<1>(r);
-
1094 offersUsed_ = offersConsumed;
-
1095 SetUnion(ofrsToRm, toRm);
-
1096
-
1097 if (offersConsumed >= maxOffersToConsume_)
-
1098 {
-
1099 // Too many iterations, mark this strand as inactive
-
1100 if (!afView.rules().enabled(fix1515))
-
1101 {
-
1102 // Don't use the liquidity
-
1103 cache_.emplace(beast::zero, beast::zero);
-
1104 return {beast::zero, beast::zero};
-
1105 }
-
1106
-
1107 // Use the liquidity, but use this to mark the strand as inactive so
-
1108 // it's not used further
-
1109 inactive_ = true;
-
1110 }
-
1111 }
-
1112
-
1113 switch (remainingOut.signum())
-
1114 {
-
1115 case -1: {
-
1116 // something went very wrong
-
1117 JLOG(j_.error())
-
1118 << "BookStep remainingOut < 0 " << to_string(remainingOut);
-
1119 UNREACHABLE("ripple::BookStep::revImp : remaining less than zero");
-
1120 cache_.emplace(beast::zero, beast::zero);
-
1121 return {beast::zero, beast::zero};
-
1122 }
-
1123 case 0: {
-
1124 // due to normalization, remainingOut can be zero without
-
1125 // result.out == out. Force result.out == out for this case
-
1126 result.out = out;
-
1127 }
-
1128 }
-
1129
-
1130 cache_.emplace(result.in, result.out);
-
1131 return {result.in, result.out};
-
1132}
-
1133
-
1134template <class TIn, class TOut, class TDerived>
-
1135std::pair<TIn, TOut>
-
1136BookStep<TIn, TOut, TDerived>::fwdImp(
-
1137 PaymentSandbox& sb,
-
1138 ApplyView& afView,
-
1139 boost::container::flat_set<uint256>& ofrsToRm,
-
1140 TIn const& in)
-
1141{
-
1142 XRPL_ASSERT(cache_, "ripple::BookStep::fwdImp : cache is set");
-
1143
-
1144 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
-
1145
-
1146 auto remainingIn = in;
-
1147
-
1148 boost::container::flat_multiset<TIn> savedIns;
-
1149 savedIns.reserve(64);
-
1150 boost::container::flat_multiset<TOut> savedOuts;
-
1151 savedOuts.reserve(64);
-
1152
-
1153 // amt fed will be adjusted by owner funds (and may differ from the offer's
-
1154 // amounts - tho always <=)
-
1155 auto eachOffer = [&](auto& offer,
-
1156 TAmounts<TIn, TOut> const& ofrAmt,
-
1157 TAmounts<TIn, TOut> const& stpAmt,
-
1158 TOut const& ownerGives,
-
1159 std::uint32_t transferRateIn,
-
1160 std::uint32_t transferRateOut) mutable -> bool {
-
1161 XRPL_ASSERT(
-
1162 cache_, "ripple::BookStep::fwdImp::eachOffer : cache is set");
-
1163
-
1164 if (remainingIn <= beast::zero)
-
1165 return false;
-
1166
-
1167 bool processMore = true;
-
1168 auto ofrAdjAmt = ofrAmt;
-
1169 auto stpAdjAmt = stpAmt;
-
1170 auto ownerGivesAdj = ownerGives;
-
1171
-
1172 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
-
1173 if (stpAmt.in <= remainingIn)
-
1174 {
-
1175 savedIns.insert(stpAmt.in);
-
1176 lastOut = savedOuts.insert(stpAmt.out);
-
1177 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
-
1178 // consume the offer even if stepAmt.in == remainingIn
-
1179 processMore = true;
-
1180 }
-
1181 else
-
1182 {
-
1183 limitStepIn(
-
1184 offer,
-
1185 ofrAdjAmt,
-
1186 stpAdjAmt,
-
1187 ownerGivesAdj,
-
1188 transferRateIn,
-
1189 transferRateOut,
-
1190 remainingIn);
-
1191 savedIns.insert(remainingIn);
-
1192 lastOut = savedOuts.insert(stpAdjAmt.out);
-
1193 result.out = sum(savedOuts);
-
1194 result.in = in;
-
1195
-
1196 processMore = false;
-
1197 }
-
1198
-
1199 if (result.out > cache_->out && result.in <= cache_->in)
-
1200 {
-
1201 // The step produced more output in the forward pass than the
-
1202 // reverse pass while consuming the same input (or less). If we
-
1203 // compute the input required to produce the cached output
-
1204 // (produced in the reverse step) and the input is equal to
-
1205 // the input consumed in the forward step, then consume the
-
1206 // input provided in the forward step and produce the output
-
1207 // requested from the reverse step.
-
1208 auto const lastOutAmt = *lastOut;
-
1209 savedOuts.erase(lastOut);
-
1210 auto const remainingOut = cache_->out - sum(savedOuts);
-
1211 auto ofrAdjAmtRev = ofrAmt;
-
1212 auto stpAdjAmtRev = stpAmt;
-
1213 auto ownerGivesAdjRev = ownerGives;
-
1214 limitStepOut(
-
1215 offer,
-
1216 ofrAdjAmtRev,
-
1217 stpAdjAmtRev,
-
1218 ownerGivesAdjRev,
-
1219 transferRateIn,
-
1220 transferRateOut,
-
1221 remainingOut);
-
1222
-
1223 if (stpAdjAmtRev.in == remainingIn)
-
1224 {
-
1225 result.in = in;
-
1226 result.out = cache_->out;
-
1227
-
1228 savedIns.clear();
-
1229 savedIns.insert(result.in);
-
1230 savedOuts.clear();
-
1231 savedOuts.insert(result.out);
-
1232
-
1233 ofrAdjAmt = ofrAdjAmtRev;
-
1234 stpAdjAmt.in = remainingIn;
-
1235 stpAdjAmt.out = remainingOut;
-
1236 ownerGivesAdj = ownerGivesAdjRev;
-
1237 }
-
1238 else
-
1239 {
-
1240 // This is (likely) a problem case, and will be caught
-
1241 // with later checks
-
1242 savedOuts.insert(lastOutAmt);
-
1243 }
-
1244 }
-
1245
-
1246 remainingIn = in - result.in;
-
1247 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
-
1248
-
1249 // When the mantissas of two iou amounts differ by less than ten, then
-
1250 // subtracting them leaves a result of zero. This can cause the check
-
1251 // for (stpAmt.in > remainingIn) to incorrectly think an offer will be
-
1252 // funded after subtracting remainingIn.
-
1253 return processMore || offer.fully_consumed();
-
1254 };
-
1255
-
1256 {
-
1257 auto const prevStepDebtDir = [&] {
-
1258 if (prevStep_)
-
1259 return prevStep_->debtDirection(sb, StrandDirection::forward);
-
1260 return DebtDirection::issues;
-
1261 }();
-
1262 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
-
1263 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
-
1264 std::uint32_t const offersConsumed = std::get<1>(r);
-
1265 offersUsed_ = offersConsumed;
-
1266 SetUnion(ofrsToRm, toRm);
-
1267
-
1268 if (offersConsumed >= maxOffersToConsume_)
-
1269 {
-
1270 // Too many iterations, mark this strand as inactive (dry)
-
1271 if (!afView.rules().enabled(fix1515))
-
1272 {
-
1273 // Don't use the liquidity
-
1274 cache_.emplace(beast::zero, beast::zero);
-
1275 return {beast::zero, beast::zero};
-
1276 }
-
1277
-
1278 // Use the liquidity, but use this to mark the strand as inactive so
-
1279 // it's not used further
-
1280 inactive_ = true;
-
1281 }
-
1282 }
-
1283
-
1284 switch (remainingIn.signum())
-
1285 {
-
1286 case -1: {
-
1287 // something went very wrong
-
1288 JLOG(j_.error())
-
1289 << "BookStep remainingIn < 0 " << to_string(remainingIn);
-
1290 UNREACHABLE("ripple::BookStep::fwdImp : remaining less than zero");
-
1291 cache_.emplace(beast::zero, beast::zero);
-
1292 return {beast::zero, beast::zero};
-
1293 }
-
1294 case 0: {
-
1295 // due to normalization, remainingIn can be zero without
-
1296 // result.in == in. Force result.in == in for this case
-
1297 result.in = in;
-
1298 }
-
1299 }
-
1300
-
1301 cache_.emplace(result.in, result.out);
-
1302 return {result.in, result.out};
-
1303}
-
1304
-
1305template <class TIn, class TOut, class TDerived>
-
1306std::pair<bool, EitherAmount>
-
1307BookStep<TIn, TOut, TDerived>::validFwd(
-
1308 PaymentSandbox& sb,
-
1309 ApplyView& afView,
-
1310 EitherAmount const& in)
-
1311{
-
1312 if (!cache_)
-
1313 {
-
1314 JLOG(j_.trace()) << "Expected valid cache in validFwd";
-
1315 return {false, EitherAmount(TOut(beast::zero))};
-
1316 }
-
1317
-
1318 auto const savCache = *cache_;
-
1319
-
1320 try
-
1321 {
-
1322 boost::container::flat_set<uint256> dummy;
-
1323 fwdImp(sb, afView, dummy, get<TIn>(in)); // changes cache
-
1324 }
-
1325 catch (FlowException const&)
-
1326 {
-
1327 return {false, EitherAmount(TOut(beast::zero))};
-
1328 }
-
1329
-
1330 if (!(checkNear(savCache.in, cache_->in) &&
-
1331 checkNear(savCache.out, cache_->out)))
-
1332 {
-
1333 JLOG(j_.warn()) << "Strand re-execute check failed."
-
1334 << " ExpectedIn: " << to_string(savCache.in)
-
1335 << " CachedIn: " << to_string(cache_->in)
-
1336 << " ExpectedOut: " << to_string(savCache.out)
-
1337 << " CachedOut: " << to_string(cache_->out);
-
1338 return {false, EitherAmount(cache_->out)};
-
1339 }
-
1340 return {true, EitherAmount(cache_->out)};
-
1341}
-
1342
-
1343template <class TIn, class TOut, class TDerived>
-
1344TER
-
1345BookStep<TIn, TOut, TDerived>::check(StrandContext const& ctx) const
-
1346{
-
1347 if (book_.in == book_.out)
-
1348 {
-
1349 JLOG(j_.debug()) << "BookStep: Book with same in and out issuer "
-
1350 << *this;
-
1351 return temBAD_PATH;
-
1352 }
-
1353 if (!isConsistent(book_.in) || !isConsistent(book_.out))
-
1354 {
-
1355 JLOG(j_.debug()) << "Book: currency is inconsistent with issuer."
-
1356 << *this;
-
1357 return temBAD_PATH;
-
1358 }
-
1359
-
1360 // Do not allow two books to output the same issue. This may cause offers on
-
1361 // one step to unfund offers in another step.
-
1362 if (!ctx.seenBookOuts.insert(book_.out).second ||
-
1363 ctx.seenDirectIssues[0].count(book_.out))
-
1364 {
-
1365 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
-
1366 return temBAD_PATH_LOOP;
-
1367 }
-
1368
-
1369 if (ctx.seenDirectIssues[1].count(book_.out))
-
1370 {
-
1371 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
-
1372 return temBAD_PATH_LOOP;
-
1373 }
-
1374
-
1375 auto issuerExists = [](ReadView const& view, Issue const& iss) -> bool {
-
1376 return isXRP(iss.account) || view.read(keylet::account(iss.account));
-
1377 };
-
1378
-
1379 if (!issuerExists(ctx.view, book_.in) || !issuerExists(ctx.view, book_.out))
-
1380 {
-
1381 JLOG(j_.debug()) << "BookStep: deleted issuer detected: " << *this;
-
1382 return tecNO_ISSUER;
-
1383 }
-
1384
-
1385 if (ctx.prevStep)
-
1386 {
-
1387 if (auto const prev = ctx.prevStep->directStepSrcAcct())
-
1388 {
-
1389 auto const& view = ctx.view;
-
1390 auto const& cur = book_.in.account;
-
1391
-
1392 auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
-
1393 if (!sle)
-
1394 return terNO_LINE;
-
1395 if ((*sle)[sfFlags] &
-
1396 ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
-
1397 return terNO_RIPPLE;
-
1398 }
-
1399 }
-
1400
-
1401 return tesSUCCESS;
-
1402}
-
1403
-
1404//------------------------------------------------------------------------------
-
1405
-
1406namespace test {
-
1407// Needed for testing
-
1408
-
1409template <class TIn, class TOut, class TDerived>
-
1410static bool
-
1411equalHelper(Step const& step, ripple::Book const& book)
-
1412{
-
1413 if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&step))
-
1414 return book == bs->book();
-
1415 return false;
-
1416}
-
1417
-
1418bool
-
1419bookStepEqual(Step const& step, ripple::Book const& book)
-
1420{
-
1421 bool const inXRP = isXRP(book.in.currency);
-
1422 bool const outXRP = isXRP(book.out.currency);
-
1423 if (inXRP && outXRP)
-
1424 {
-
1425 UNREACHABLE("ripple::test::bookStepEqual : no XRP to XRP book step");
-
1426 return false; // no such thing as xrp/xrp book step
-
1427 }
-
1428 if (inXRP && !outXRP)
-
1429 return equalHelper<
-
1430 XRPAmount,
-
1431 IOUAmount,
-
1432 BookPaymentStep<XRPAmount, IOUAmount>>(step, book);
-
1433 if (!inXRP && outXRP)
-
1434 return equalHelper<
-
1435 IOUAmount,
-
1436 XRPAmount,
-
1437 BookPaymentStep<IOUAmount, XRPAmount>>(step, book);
-
1438 if (!inXRP && !outXRP)
-
1439 return equalHelper<
+
746 bool offerAttempted = false;
+
747 std::optional<Quality> ofrQ;
+
748 auto execOffer = [&](auto& offer) {
+
749 // Note that offer.quality() returns a (non-optional) Quality. So
+
750 // ofrQ is always safe to use below this point in the lambda.
+
751 if (!ofrQ)
+
752 ofrQ = offer.quality();
+
753 else if (*ofrQ != offer.quality())
+
754 return false;
+
755
+
756 if (static_cast<TDerived const*>(this)->limitSelfCrossQuality(
+
757 strandSrc_, strandDst_, offer, ofrQ, offers, offerAttempted))
+
758 return true;
+
759
+
760 // Make sure offer owner has authorization to own IOUs from issuer.
+
761 // An account can always own XRP or their own IOUs.
+
762 if (!isXRP(offer.issueIn().currency) &&
+
763 offer.owner() != offer.issueIn().account)
+
764 {
+
765 auto const& issuerID = offer.issueIn().account;
+
766 auto const issuer = afView.read(keylet::account(issuerID));
+
767 if (issuer && ((*issuer)[sfFlags] & lsfRequireAuth))
+
768 {
+
769 // Issuer requires authorization. See if offer owner has that.
+
770 auto const& ownerID = offer.owner();
+
771 auto const authFlag =
+
772 issuerID > ownerID ? lsfHighAuth : lsfLowAuth;
+
773
+
774 auto const line = afView.read(
+
775 keylet::line(ownerID, issuerID, offer.issueIn().currency));
+
776
+
777 if (!line || (((*line)[sfFlags] & authFlag) == 0))
+
778 {
+
779 // Offer owner not authorized to hold IOU from issuer.
+
780 // Remove this offer even if no crossing occurs.
+
781 if (auto const key = offer.key())
+
782 offers.permRmOffer(*key);
+
783 if (!offerAttempted)
+
784 // Change quality only if no previous offers were tried.
+
785 ofrQ = std::nullopt;
+
786 // Returning true causes offers.step() to delete the offer.
+
787 return true;
+
788 }
+
789 }
+
790 }
+
791
+
792 if (!static_cast<TDerived const*>(this)->checkQualityThreshold(
+
793 offer.quality()))
+
794 return false;
+
795
+
796 auto const [ofrInRate, ofrOutRate] = offer.adjustRates(
+
797 static_cast<TDerived const*>(this)->getOfrInRate(
+
798 prevStep_, offer.owner(), trIn),
+
799 static_cast<TDerived const*>(this)->getOfrOutRate(
+
800 prevStep_, offer.owner(), strandDst_, trOut));
+
801
+
802 auto ofrAmt = offer.amount();
+
803 TAmounts stpAmt{
+
804 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true),
+
805 ofrAmt.out};
+
806
+
807 // owner pays the transfer fee.
+
808 auto ownerGives =
+
809 mulRatio(ofrAmt.out, ofrOutRate, QUALITY_ONE, /*roundUp*/ false);
+
810
+
811 auto const funds = offer.isFunded()
+
812 ? ownerGives // Offer owner is issuer; they have unlimited funds
+
813 : offers.ownerFunds();
+
814
+
815 // Only if CLOB offer
+
816 if (funds < ownerGives)
+
817 {
+
818 // We already know offer.owner()!=offer.issueOut().account
+
819 ownerGives = funds;
+
820 stpAmt.out = mulRatio(
+
821 ownerGives, QUALITY_ONE, ofrOutRate, /*roundUp*/ false);
+
822
+
823 // It turns out we can prevent order book blocking by (strictly)
+
824 // rounding down the ceil_out() result. This adjustment changes
+
825 // transaction outcomes, so it must be made under an amendment.
+
826 ofrAmt = offer.limitOut(ofrAmt, stpAmt.out, /*roundUp*/ false);
+
827
+
828 stpAmt.in =
+
829 mulRatio(ofrAmt.in, ofrInRate, QUALITY_ONE, /*roundUp*/ true);
+
830 }
+
831
+
832 offerAttempted = true;
+
833 return callback(
+
834 offer, ofrAmt, stpAmt, ownerGives, ofrInRate, ofrOutRate);
+
835 };
+
836
+
837 // At any payment engine iteration, AMM offer can only be consumed once.
+
838 auto tryAMM = [&](std::optional<Quality> const& lobQuality) -> bool {
+
839 // amm doesn't support domain yet
+
840 if (book_.domain)
+
841 return true;
+
842
+
843 // If offer crossing then use either LOB quality or nullopt
+
844 // to prevent AMM being blocked by a lower quality LOB.
+
845 auto const qualityThreshold = [&]() -> std::optional<Quality> {
+
846 if (sb.rules().enabled(fixAMMv1_1) && lobQuality)
+
847 return static_cast<TDerived const*>(this)->qualityThreshold(
+
848 *lobQuality);
+
849 return lobQuality;
+
850 }();
+
851 auto ammOffer = getAMMOffer(sb, qualityThreshold);
+
852 return !ammOffer || execOffer(*ammOffer);
+
853 };
+
854
+
855 if (offers.step())
+
856 {
+
857 if (tryAMM(offers.tip().quality()))
+
858 {
+
859 do
+
860 {
+
861 if (!execOffer(offers.tip()))
+
862 break;
+
863 } while (offers.step());
+
864 }
+
865 }
+
866 else
+
867 {
+
868 // Might have AMM offer if there are no LOB offers.
+
869 tryAMM(std::nullopt);
+
870 }
+
871
+
872 return {offers.permToRemove(), counter.count()};
+
873}
+
874
+
875template <class TIn, class TOut, class TDerived>
+
876template <template <typename, typename> typename Offer>
+
877void
+
878BookStep<TIn, TOut, TDerived>::consumeOffer(
+
879 PaymentSandbox& sb,
+
880 Offer<TIn, TOut>& offer,
+
881 TAmounts<TIn, TOut> const& ofrAmt,
+
882 TAmounts<TIn, TOut> const& stepAmt,
+
883 TOut const& ownerGives) const
+
884{
+
885 if (!offer.checkInvariant(ofrAmt, j_))
+
886 {
+
887 // purposely written as separate if statements so we get logging even
+
888 // when the amendment isn't active.
+
889 if (sb.rules().enabled(fixAMMOverflowOffer))
+
890 {
+
891 Throw<FlowException>(
+
892 tecINVARIANT_FAILED, "AMM pool product invariant failed.");
+
893 }
+
894 }
+
895
+
896 // The offer owner gets the ofrAmt. The difference between ofrAmt and
+
897 // stepAmt is a transfer fee that goes to book_.in.account
+
898 {
+
899 auto const dr = offer.send(
+
900 sb,
+
901 book_.in.account,
+
902 offer.owner(),
+
903 toSTAmount(ofrAmt.in, book_.in),
+
904 j_);
+
905 if (dr != tesSUCCESS)
+
906 Throw<FlowException>(dr);
+
907 }
+
908
+
909 // The offer owner pays `ownerGives`. The difference between ownerGives and
+
910 // stepAmt is a transfer fee that goes to book_.out.account
+
911 {
+
912 auto const cr = offer.send(
+
913 sb,
+
914 offer.owner(),
+
915 book_.out.account,
+
916 toSTAmount(ownerGives, book_.out),
+
917 j_);
+
918 if (cr != tesSUCCESS)
+
919 Throw<FlowException>(cr);
+
920 }
+
921
+
922 offer.consume(sb, ofrAmt);
+
923}
+
924
+
925template <class TIn, class TOut, class TDerived>
+
926std::optional<AMMOffer<TIn, TOut>>
+
927BookStep<TIn, TOut, TDerived>::getAMMOffer(
+
928 ReadView const& view,
+
929 std::optional<Quality> const& clobQuality) const
+
930{
+
931 if (ammLiquidity_)
+
932 return ammLiquidity_->getOffer(view, clobQuality);
+
933 return std::nullopt;
+
934}
+
935
+
936template <class TIn, class TOut, class TDerived>
+
937std::optional<std::variant<Quality, AMMOffer<TIn, TOut>>>
+
938BookStep<TIn, TOut, TDerived>::tip(ReadView const& view) const
+
939{
+
940 // This can be simplified (and sped up) if directories are never empty.
+
941 Sandbox sb(&view, tapNONE);
+
942 BookTip bt(sb, book_);
+
943 auto const lobQuality =
+
944 bt.step(j_) ? std::optional<Quality>(bt.quality()) : std::nullopt;
+
945 // Multi-path offer generates an offer with the quality
+
946 // calculated from the offer size and the quality is constant in this case.
+
947 // Single path offer quality changes with the offer size. Spot price quality
+
948 // (SPQ) can't be used in this case as the upper bound quality because
+
949 // even if SPQ quality is better than LOB quality, it might not be possible
+
950 // to generate AMM offer at or better quality than LOB quality. Another
+
951 // factor to consider is limit quality on offer crossing. If LOB quality
+
952 // is greater than limit quality then use LOB quality when generating AMM
+
953 // offer, otherwise don't use quality threshold when generating AMM offer.
+
954 // AMM or LOB offer, whether multi-path or single path then can be selected
+
955 // based on the best offer quality. Using the quality to generate AMM offer
+
956 // in this case also prevents the payment engine from going into multiple
+
957 // iterations to cross a LOB offer. This happens when AMM changes
+
958 // the out amount at the start of iteration to match the limitQuality
+
959 // on offer crossing but AMM can't generate the offer at this quality,
+
960 // as the result a LOB offer is partially crossed, and it might take a few
+
961 // iterations to fully cross the offer.
+
962 auto const qualityThreshold = [&]() -> std::optional<Quality> {
+
963 if (view.rules().enabled(fixAMMv1_1) && lobQuality)
+
964 return static_cast<TDerived const*>(this)->qualityThreshold(
+
965 *lobQuality);
+
966 return std::nullopt;
+
967 }();
+
968 // AMM quality is better or no LOB offer
+
969 if (auto const ammOffer = getAMMOffer(view, qualityThreshold); ammOffer &&
+
970 ((lobQuality && ammOffer->quality() > lobQuality) || !lobQuality))
+
971 return ammOffer;
+
972 // LOB quality is better or nullopt
+
973 return lobQuality;
+
974}
+
975
+
976template <class TIn, class TOut, class TDerived>
+
977auto
+
978BookStep<TIn, TOut, TDerived>::tipOfferQuality(ReadView const& view) const
+
979 -> std::optional<std::pair<Quality, OfferType>>
+
980{
+
981 if (auto const res = tip(view); !res)
+
982 return std::nullopt;
+
983 else if (auto const q = std::get_if<Quality>(&(*res)))
+
984 return std::make_pair(*q, OfferType::CLOB);
+
985 else
+
986 return std::make_pair(
+
987 std::get<AMMOffer<TIn, TOut>>(*res).quality(), OfferType::AMM);
+
988}
+
989
+
990template <class TIn, class TOut, class TDerived>
+
991std::optional<QualityFunction>
+
992BookStep<TIn, TOut, TDerived>::tipOfferQualityF(ReadView const& view) const
+
993{
+
994 if (auto const res = tip(view); !res)
+
995 return std::nullopt;
+
996 else if (auto const q = std::get_if<Quality>(&(*res)))
+
997 return QualityFunction{*q, QualityFunction::CLOBLikeTag{}};
+
998 else
+
999 return std::get<AMMOffer<TIn, TOut>>(*res).getQualityFunc();
+
1000}
+
1001
+
1002template <class TCollection>
+
1003static auto
+
1004sum(TCollection const& col)
+
1005{
+
1006 using TResult = std::decay_t<decltype(*col.begin())>;
+
1007 if (col.empty())
+
1008 return TResult{beast::zero};
+
1009 return std::accumulate(col.begin() + 1, col.end(), *col.begin());
+
1010};
+
1011
+
1012template <class TIn, class TOut, class TDerived>
+
1013std::pair<TIn, TOut>
+
1014BookStep<TIn, TOut, TDerived>::revImp(
+
1015 PaymentSandbox& sb,
+
1016 ApplyView& afView,
+
1017 boost::container::flat_set<uint256>& ofrsToRm,
+
1018 TOut const& out)
+
1019{
+
1020 cache_.reset();
+
1021
+
1022 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
+
1023
+
1024 auto remainingOut = out;
+
1025
+
1026 boost::container::flat_multiset<TIn> savedIns;
+
1027 savedIns.reserve(64);
+
1028 boost::container::flat_multiset<TOut> savedOuts;
+
1029 savedOuts.reserve(64);
+
1030
+
1031 /* amt fed will be adjusted by owner funds (and may differ from the offer's
+
1032 amounts - tho always <=)
+
1033 Return true to continue to receive offers, false to stop receiving offers.
+
1034 */
+
1035 auto eachOffer = [&](auto& offer,
+
1036 TAmounts<TIn, TOut> const& ofrAmt,
+
1037 TAmounts<TIn, TOut> const& stpAmt,
+
1038 TOut const& ownerGives,
+
1039 std::uint32_t transferRateIn,
+
1040 std::uint32_t transferRateOut) mutable -> bool {
+
1041 if (remainingOut <= beast::zero)
+
1042 return false;
+
1043
+
1044 if (stpAmt.out <= remainingOut)
+
1045 {
+
1046 savedIns.insert(stpAmt.in);
+
1047 savedOuts.insert(stpAmt.out);
+
1048 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
+
1049 remainingOut = out - result.out;
+
1050 this->consumeOffer(sb, offer, ofrAmt, stpAmt, ownerGives);
+
1051 // return true b/c even if the payment is satisfied,
+
1052 // we need to consume the offer
+
1053 return true;
+
1054 }
+
1055 else
+
1056 {
+
1057 auto ofrAdjAmt = ofrAmt;
+
1058 auto stpAdjAmt = stpAmt;
+
1059 auto ownerGivesAdj = ownerGives;
+
1060 limitStepOut(
+
1061 offer,
+
1062 ofrAdjAmt,
+
1063 stpAdjAmt,
+
1064 ownerGivesAdj,
+
1065 transferRateIn,
+
1066 transferRateOut,
+
1067 remainingOut);
+
1068 remainingOut = beast::zero;
+
1069 savedIns.insert(stpAdjAmt.in);
+
1070 savedOuts.insert(remainingOut);
+
1071 result.in = sum(savedIns);
+
1072 result.out = out;
+
1073 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
+
1074
+
1075 // Explicitly check whether the offer is funded. Given that we have
+
1076 // (stpAmt.out > remainingOut), it's natural to assume the offer
+
1077 // will still be funded after consuming remainingOut but that is
+
1078 // not always the case. If the mantissas of two IOU amounts differ
+
1079 // by less than ten, then subtracting them leaves a zero.
+
1080 return offer.fully_consumed();
+
1081 }
+
1082 };
+
1083
+
1084 {
+
1085 auto const prevStepDebtDir = [&] {
+
1086 if (prevStep_)
+
1087 return prevStep_->debtDirection(sb, StrandDirection::reverse);
+
1088 return DebtDirection::issues;
+
1089 }();
+
1090 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
+
1091 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
+
1092 std::uint32_t const offersConsumed = std::get<1>(r);
+
1093 offersUsed_ = offersConsumed;
+
1094 SetUnion(ofrsToRm, toRm);
+
1095
+
1096 if (offersConsumed >= maxOffersToConsume_)
+
1097 {
+
1098 // Too many iterations, mark this strand as inactive
+
1099 if (!afView.rules().enabled(fix1515))
+
1100 {
+
1101 // Don't use the liquidity
+
1102 cache_.emplace(beast::zero, beast::zero);
+
1103 return {beast::zero, beast::zero};
+
1104 }
+
1105
+
1106 // Use the liquidity, but use this to mark the strand as inactive so
+
1107 // it's not used further
+
1108 inactive_ = true;
+
1109 }
+
1110 }
+
1111
+
1112 switch (remainingOut.signum())
+
1113 {
+
1114 case -1: {
+
1115 // something went very wrong
+
1116 JLOG(j_.error())
+
1117 << "BookStep remainingOut < 0 " << to_string(remainingOut);
+
1118 UNREACHABLE("ripple::BookStep::revImp : remaining less than zero");
+
1119 cache_.emplace(beast::zero, beast::zero);
+
1120 return {beast::zero, beast::zero};
+
1121 }
+
1122 case 0: {
+
1123 // due to normalization, remainingOut can be zero without
+
1124 // result.out == out. Force result.out == out for this case
+
1125 result.out = out;
+
1126 }
+
1127 }
+
1128
+
1129 cache_.emplace(result.in, result.out);
+
1130 return {result.in, result.out};
+
1131}
+
1132
+
1133template <class TIn, class TOut, class TDerived>
+
1134std::pair<TIn, TOut>
+
1135BookStep<TIn, TOut, TDerived>::fwdImp(
+
1136 PaymentSandbox& sb,
+
1137 ApplyView& afView,
+
1138 boost::container::flat_set<uint256>& ofrsToRm,
+
1139 TIn const& in)
+
1140{
+
1141 XRPL_ASSERT(cache_, "ripple::BookStep::fwdImp : cache is set");
+
1142
+
1143 TAmounts<TIn, TOut> result(beast::zero, beast::zero);
+
1144
+
1145 auto remainingIn = in;
+
1146
+
1147 boost::container::flat_multiset<TIn> savedIns;
+
1148 savedIns.reserve(64);
+
1149 boost::container::flat_multiset<TOut> savedOuts;
+
1150 savedOuts.reserve(64);
+
1151
+
1152 // amt fed will be adjusted by owner funds (and may differ from the offer's
+
1153 // amounts - tho always <=)
+
1154 auto eachOffer = [&](auto& offer,
+
1155 TAmounts<TIn, TOut> const& ofrAmt,
+
1156 TAmounts<TIn, TOut> const& stpAmt,
+
1157 TOut const& ownerGives,
+
1158 std::uint32_t transferRateIn,
+
1159 std::uint32_t transferRateOut) mutable -> bool {
+
1160 XRPL_ASSERT(
+
1161 cache_, "ripple::BookStep::fwdImp::eachOffer : cache is set");
+
1162
+
1163 if (remainingIn <= beast::zero)
+
1164 return false;
+
1165
+
1166 bool processMore = true;
+
1167 auto ofrAdjAmt = ofrAmt;
+
1168 auto stpAdjAmt = stpAmt;
+
1169 auto ownerGivesAdj = ownerGives;
+
1170
+
1171 typename boost::container::flat_multiset<TOut>::const_iterator lastOut;
+
1172 if (stpAmt.in <= remainingIn)
+
1173 {
+
1174 savedIns.insert(stpAmt.in);
+
1175 lastOut = savedOuts.insert(stpAmt.out);
+
1176 result = TAmounts<TIn, TOut>(sum(savedIns), sum(savedOuts));
+
1177 // consume the offer even if stepAmt.in == remainingIn
+
1178 processMore = true;
+
1179 }
+
1180 else
+
1181 {
+
1182 limitStepIn(
+
1183 offer,
+
1184 ofrAdjAmt,
+
1185 stpAdjAmt,
+
1186 ownerGivesAdj,
+
1187 transferRateIn,
+
1188 transferRateOut,
+
1189 remainingIn);
+
1190 savedIns.insert(remainingIn);
+
1191 lastOut = savedOuts.insert(stpAdjAmt.out);
+
1192 result.out = sum(savedOuts);
+
1193 result.in = in;
+
1194
+
1195 processMore = false;
+
1196 }
+
1197
+
1198 if (result.out > cache_->out && result.in <= cache_->in)
+
1199 {
+
1200 // The step produced more output in the forward pass than the
+
1201 // reverse pass while consuming the same input (or less). If we
+
1202 // compute the input required to produce the cached output
+
1203 // (produced in the reverse step) and the input is equal to
+
1204 // the input consumed in the forward step, then consume the
+
1205 // input provided in the forward step and produce the output
+
1206 // requested from the reverse step.
+
1207 auto const lastOutAmt = *lastOut;
+
1208 savedOuts.erase(lastOut);
+
1209 auto const remainingOut = cache_->out - sum(savedOuts);
+
1210 auto ofrAdjAmtRev = ofrAmt;
+
1211 auto stpAdjAmtRev = stpAmt;
+
1212 auto ownerGivesAdjRev = ownerGives;
+
1213 limitStepOut(
+
1214 offer,
+
1215 ofrAdjAmtRev,
+
1216 stpAdjAmtRev,
+
1217 ownerGivesAdjRev,
+
1218 transferRateIn,
+
1219 transferRateOut,
+
1220 remainingOut);
+
1221
+
1222 if (stpAdjAmtRev.in == remainingIn)
+
1223 {
+
1224 result.in = in;
+
1225 result.out = cache_->out;
+
1226
+
1227 savedIns.clear();
+
1228 savedIns.insert(result.in);
+
1229 savedOuts.clear();
+
1230 savedOuts.insert(result.out);
+
1231
+
1232 ofrAdjAmt = ofrAdjAmtRev;
+
1233 stpAdjAmt.in = remainingIn;
+
1234 stpAdjAmt.out = remainingOut;
+
1235 ownerGivesAdj = ownerGivesAdjRev;
+
1236 }
+
1237 else
+
1238 {
+
1239 // This is (likely) a problem case, and will be caught
+
1240 // with later checks
+
1241 savedOuts.insert(lastOutAmt);
+
1242 }
+
1243 }
+
1244
+
1245 remainingIn = in - result.in;
+
1246 this->consumeOffer(sb, offer, ofrAdjAmt, stpAdjAmt, ownerGivesAdj);
+
1247
+
1248 // When the mantissas of two iou amounts differ by less than ten, then
+
1249 // subtracting them leaves a result of zero. This can cause the check
+
1250 // for (stpAmt.in > remainingIn) to incorrectly think an offer will be
+
1251 // funded after subtracting remainingIn.
+
1252 return processMore || offer.fully_consumed();
+
1253 };
+
1254
+
1255 {
+
1256 auto const prevStepDebtDir = [&] {
+
1257 if (prevStep_)
+
1258 return prevStep_->debtDirection(sb, StrandDirection::forward);
+
1259 return DebtDirection::issues;
+
1260 }();
+
1261 auto const r = forEachOffer(sb, afView, prevStepDebtDir, eachOffer);
+
1262 boost::container::flat_set<uint256> toRm = std::move(std::get<0>(r));
+
1263 std::uint32_t const offersConsumed = std::get<1>(r);
+
1264 offersUsed_ = offersConsumed;
+
1265 SetUnion(ofrsToRm, toRm);
+
1266
+
1267 if (offersConsumed >= maxOffersToConsume_)
+
1268 {
+
1269 // Too many iterations, mark this strand as inactive (dry)
+
1270 if (!afView.rules().enabled(fix1515))
+
1271 {
+
1272 // Don't use the liquidity
+
1273 cache_.emplace(beast::zero, beast::zero);
+
1274 return {beast::zero, beast::zero};
+
1275 }
+
1276
+
1277 // Use the liquidity, but use this to mark the strand as inactive so
+
1278 // it's not used further
+
1279 inactive_ = true;
+
1280 }
+
1281 }
+
1282
+
1283 switch (remainingIn.signum())
+
1284 {
+
1285 case -1: {
+
1286 // something went very wrong
+
1287 JLOG(j_.error())
+
1288 << "BookStep remainingIn < 0 " << to_string(remainingIn);
+
1289 UNREACHABLE("ripple::BookStep::fwdImp : remaining less than zero");
+
1290 cache_.emplace(beast::zero, beast::zero);
+
1291 return {beast::zero, beast::zero};
+
1292 }
+
1293 case 0: {
+
1294 // due to normalization, remainingIn can be zero without
+
1295 // result.in == in. Force result.in == in for this case
+
1296 result.in = in;
+
1297 }
+
1298 }
+
1299
+
1300 cache_.emplace(result.in, result.out);
+
1301 return {result.in, result.out};
+
1302}
+
1303
+
1304template <class TIn, class TOut, class TDerived>
+
1305std::pair<bool, EitherAmount>
+
1306BookStep<TIn, TOut, TDerived>::validFwd(
+
1307 PaymentSandbox& sb,
+
1308 ApplyView& afView,
+
1309 EitherAmount const& in)
+
1310{
+
1311 if (!cache_)
+
1312 {
+
1313 JLOG(j_.trace()) << "Expected valid cache in validFwd";
+
1314 return {false, EitherAmount(TOut(beast::zero))};
+
1315 }
+
1316
+
1317 auto const savCache = *cache_;
+
1318
+
1319 try
+
1320 {
+
1321 boost::container::flat_set<uint256> dummy;
+
1322 fwdImp(sb, afView, dummy, get<TIn>(in)); // changes cache
+
1323 }
+
1324 catch (FlowException const&)
+
1325 {
+
1326 return {false, EitherAmount(TOut(beast::zero))};
+
1327 }
+
1328
+
1329 if (!(checkNear(savCache.in, cache_->in) &&
+
1330 checkNear(savCache.out, cache_->out)))
+
1331 {
+
1332 JLOG(j_.warn()) << "Strand re-execute check failed."
+
1333 << " ExpectedIn: " << to_string(savCache.in)
+
1334 << " CachedIn: " << to_string(cache_->in)
+
1335 << " ExpectedOut: " << to_string(savCache.out)
+
1336 << " CachedOut: " << to_string(cache_->out);
+
1337 return {false, EitherAmount(cache_->out)};
+
1338 }
+
1339 return {true, EitherAmount(cache_->out)};
+
1340}
+
1341
+
1342template <class TIn, class TOut, class TDerived>
+
1343TER
+
1344BookStep<TIn, TOut, TDerived>::check(StrandContext const& ctx) const
+
1345{
+
1346 if (book_.in == book_.out)
+
1347 {
+
1348 JLOG(j_.debug()) << "BookStep: Book with same in and out issuer "
+
1349 << *this;
+
1350 return temBAD_PATH;
+
1351 }
+
1352 if (!isConsistent(book_.in) || !isConsistent(book_.out))
+
1353 {
+
1354 JLOG(j_.debug()) << "Book: currency is inconsistent with issuer."
+
1355 << *this;
+
1356 return temBAD_PATH;
+
1357 }
+
1358
+
1359 // Do not allow two books to output the same issue. This may cause offers on
+
1360 // one step to unfund offers in another step.
+
1361 if (!ctx.seenBookOuts.insert(book_.out).second ||
+
1362 ctx.seenDirectIssues[0].count(book_.out))
+
1363 {
+
1364 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
+
1365 return temBAD_PATH_LOOP;
+
1366 }
+
1367
+
1368 if (ctx.seenDirectIssues[1].count(book_.out))
+
1369 {
+
1370 JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
+
1371 return temBAD_PATH_LOOP;
+
1372 }
+
1373
+
1374 auto issuerExists = [](ReadView const& view, Issue const& iss) -> bool {
+
1375 return isXRP(iss.account) || view.read(keylet::account(iss.account));
+
1376 };
+
1377
+
1378 if (!issuerExists(ctx.view, book_.in) || !issuerExists(ctx.view, book_.out))
+
1379 {
+
1380 JLOG(j_.debug()) << "BookStep: deleted issuer detected: " << *this;
+
1381 return tecNO_ISSUER;
+
1382 }
+
1383
+
1384 if (ctx.prevStep)
+
1385 {
+
1386 if (auto const prev = ctx.prevStep->directStepSrcAcct())
+
1387 {
+
1388 auto const& view = ctx.view;
+
1389 auto const& cur = book_.in.account;
+
1390
+
1391 auto sle = view.read(keylet::line(*prev, cur, book_.in.currency));
+
1392 if (!sle)
+
1393 return terNO_LINE;
+
1394 if ((*sle)[sfFlags] &
+
1395 ((cur > *prev) ? lsfHighNoRipple : lsfLowNoRipple))
+
1396 return terNO_RIPPLE;
+
1397 }
+
1398 }
+
1399
+
1400 return tesSUCCESS;
+
1401}
+
1402
+
1403//------------------------------------------------------------------------------
+
1404
+
1405namespace test {
+
1406// Needed for testing
+
1407
+
1408template <class TIn, class TOut, class TDerived>
+
1409static bool
+
1410equalHelper(Step const& step, ripple::Book const& book)
+
1411{
+
1412 if (auto bs = dynamic_cast<BookStep<TIn, TOut, TDerived> const*>(&step))
+
1413 return book == bs->book();
+
1414 return false;
+
1415}
+
1416
+
1417bool
+
1418bookStepEqual(Step const& step, ripple::Book const& book)
+
1419{
+
1420 bool const inXRP = isXRP(book.in.currency);
+
1421 bool const outXRP = isXRP(book.out.currency);
+
1422 if (inXRP && outXRP)
+
1423 {
+
1424 UNREACHABLE("ripple::test::bookStepEqual : no XRP to XRP book step");
+
1425 return false; // no such thing as xrp/xrp book step
+
1426 }
+
1427 if (inXRP && !outXRP)
+
1428 return equalHelper<
+
1429 XRPAmount,
+
1430 IOUAmount,
+
1431 BookPaymentStep<XRPAmount, IOUAmount>>(step, book);
+
1432 if (!inXRP && outXRP)
+
1433 return equalHelper<
+
1434 IOUAmount,
+
1435 XRPAmount,
+
1436 BookPaymentStep<IOUAmount, XRPAmount>>(step, book);
+
1437 if (!inXRP && !outXRP)
+
1438 return equalHelper<
+
1439 IOUAmount,
1440 IOUAmount,
-
1441 IOUAmount,
-
1442 BookPaymentStep<IOUAmount, IOUAmount>>(step, book);
-
1443 return false;
-
1444}
-
1445} // namespace test
-
1446
-
1447//------------------------------------------------------------------------------
-
1448
-
1449template <class TIn, class TOut>
-
1450static std::pair<TER, std::unique_ptr<Step>>
-
1451make_BookStepHelper(StrandContext const& ctx, Issue const& in, Issue const& out)
-
1452{
-
1453 TER ter = tefINTERNAL;
-
1454 std::unique_ptr<Step> r;
-
1455 if (ctx.offerCrossing)
-
1456 {
-
1457 auto offerCrossingStep =
-
1458 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx, in, out);
-
1459 ter = offerCrossingStep->check(ctx);
-
1460 r = std::move(offerCrossingStep);
-
1461 }
-
1462 else // payment
-
1463 {
-
1464 auto paymentStep =
-
1465 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx, in, out);
-
1466 ter = paymentStep->check(ctx);
-
1467 r = std::move(paymentStep);
-
1468 }
-
1469 if (ter != tesSUCCESS)
-
1470 return {ter, nullptr};
-
1471
-
1472 return {tesSUCCESS, std::move(r)};
-
1473}
-
1474
-
1475std::pair<TER, std::unique_ptr<Step>>
-
1476make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out)
-
1477{
-
1478 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx, in, out);
-
1479}
-
1480
-
1481std::pair<TER, std::unique_ptr<Step>>
-
1482make_BookStepIX(StrandContext const& ctx, Issue const& in)
-
1483{
-
1484 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx, in, xrpIssue());
-
1485}
-
1486
-
1487std::pair<TER, std::unique_ptr<Step>>
-
1488make_BookStepXI(StrandContext const& ctx, Issue const& out)
-
1489{
-
1490 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx, xrpIssue(), out);
-
1491}
-
1492
-
1493} // namespace ripple
+
1441 BookPaymentStep<IOUAmount, IOUAmount>>(step, book);
+
1442 return false;
+
1443}
+
1444} // namespace test
+
1445
+
1446//------------------------------------------------------------------------------
+
1447
+
1448template <class TIn, class TOut>
+
1449static std::pair<TER, std::unique_ptr<Step>>
+
1450make_BookStepHelper(StrandContext const& ctx, Issue const& in, Issue const& out)
+
1451{
+
1452 TER ter = tefINTERNAL;
+
1453 std::unique_ptr<Step> r;
+
1454 if (ctx.offerCrossing)
+
1455 {
+
1456 auto offerCrossingStep =
+
1457 std::make_unique<BookOfferCrossingStep<TIn, TOut>>(ctx, in, out);
+
1458 ter = offerCrossingStep->check(ctx);
+
1459 r = std::move(offerCrossingStep);
+
1460 }
+
1461 else // payment
+
1462 {
+
1463 auto paymentStep =
+
1464 std::make_unique<BookPaymentStep<TIn, TOut>>(ctx, in, out);
+
1465 ter = paymentStep->check(ctx);
+
1466 r = std::move(paymentStep);
+
1467 }
+
1468 if (ter != tesSUCCESS)
+
1469 return {ter, nullptr};
+
1470
+
1471 return {tesSUCCESS, std::move(r)};
+
1472}
+
1473
+
1474std::pair<TER, std::unique_ptr<Step>>
+
1475make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out)
+
1476{
+
1477 return make_BookStepHelper<IOUAmount, IOUAmount>(ctx, in, out);
+
1478}
+
1479
+
1480std::pair<TER, std::unique_ptr<Step>>
+
1481make_BookStepIX(StrandContext const& ctx, Issue const& in)
+
1482{
+
1483 return make_BookStepHelper<IOUAmount, XRPAmount>(ctx, in, xrpIssue());
+
1484}
+
1485
+
1486std::pair<TER, std::unique_ptr<Step>>
+
1487make_BookStepXI(StrandContext const& ctx, Issue const& out)
+
1488{
+
1489 return make_BookStepHelper<XRPAmount, IOUAmount>(ctx, xrpIssue(), out);
+
1490}
+
1491
+
1492} // namespace ripple
T accumulate(T... args)
@@ -1596,38 +1595,38 @@ $(function() {
std::optional< Quality > qualityThreshold(Quality const &lobQuality) const
Definition: BookStep.cpp:305
std::string logString() const override
Definition: BookStep.cpp:362
Definition: BookStep.cpp:46
-
std::optional< AMMOffer< TIn, TOut > > getAMMOffer(ReadView const &view, std::optional< Quality > const &clobQuality) const
Definition: BookStep.cpp:928
+
std::optional< AMMOffer< TIn, TOut > > getAMMOffer(ReadView const &view, std::optional< Quality > const &clobQuality) const
Definition: BookStep.cpp:927
std::optional< AMMLiquidity< TIn, TOut > > ammLiquidity_
Definition: BookStep.cpp:70
friend bool operator==(BookStep const &lhs, BookStep const &rhs)
Definition: BookStep.cpp:203
std::uint32_t offersUsed() const override
Definition: BookStep.cpp:652
std::optional< Book > bookStepBook() const override
Definition: BookStep.cpp:145
std::uint32_t offersUsed_
Number of offers consumed or partially consumed the last time the step ran, including expired and unf...
Definition: BookStep.cpp:66
std::optional< EitherAmount > cachedOut() const override
Definition: BookStep.cpp:130
-
std::optional< std::pair< Quality, OfferType > > tipOfferQuality(ReadView const &view) const
Definition: BookStep.cpp:979
+
std::optional< std::pair< Quality, OfferType > > tipOfferQuality(ReadView const &view) const
Definition: BookStep.cpp:978
OfferType
Definition: BookStep.cpp:48
@ CLOB
@ AMM
-
void consumeOffer(PaymentSandbox &sb, Offer< TIn, TOut > &offer, TAmounts< TIn, TOut > const &ofrAmt, TAmounts< TIn, TOut > const &stepAmt, TOut const &ownerGives) const
Definition: BookStep.cpp:879
+
void consumeOffer(PaymentSandbox &sb, Offer< TIn, TOut > &offer, TAmounts< TIn, TOut > const &ofrAmt, TAmounts< TIn, TOut > const &stepAmt, TOut const &ownerGives) const
Definition: BookStep.cpp:878
std::pair< std::optional< QualityFunction >, DebtDirection > getQualityFunc(ReadView const &v, DebtDirection prevStepDir) const override
Definition: BookStep.cpp:610
-
TER check(StrandContext const &ctx) const
Definition: BookStep.cpp:1345
+
TER check(StrandContext const &ctx) const
Definition: BookStep.cpp:1344
std::pair< boost::container::flat_set< uint256 >, std::uint32_t > forEachOffer(PaymentSandbox &sb, ApplyView &afView, DebtDirection prevStepDebtDir, Callback &callback) const
Definition: BookStep.cpp:717
-
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: BookStep.cpp:1307
+
std::pair< bool, EitherAmount > validFwd(PaymentSandbox &sb, ApplyView &afView, EitherAmount const &in) override
Definition: BookStep.cpp:1306
Book const & book() const
Definition: BookStep.cpp:116
std::optional< EitherAmount > cachedIn() const override
Definition: BookStep.cpp:122
uint32_t const maxOffersToConsume_
Definition: BookStep.cpp:50
bool equal(Step const &rhs) const override
Definition: BookStep.cpp:575
DebtDirection debtDirection(ReadView const &sb, StrandDirection dir) const override
Definition: BookStep.cpp:138
-
std::pair< TIn, TOut > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TOut const &out)
Definition: BookStep.cpp:1015
+
std::pair< TIn, TOut > revImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TOut const &out)
Definition: BookStep.cpp:1014
Step const *const prevStep_
Definition: BookStep.cpp:55
friend bool operator!=(BookStep const &lhs, BookStep const &rhs)
Definition: BookStep.cpp:209
std::string logStringImpl(char const *name) const
Definition: BookStep.cpp:190
BookStep(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:94
-
std::pair< TIn, TOut > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TIn const &in)
Definition: BookStep.cpp:1136
-
std::optional< QualityFunction > tipOfferQualityF(ReadView const &view) const
Definition: BookStep.cpp:993
+
std::pair< TIn, TOut > fwdImp(PaymentSandbox &sb, ApplyView &afView, boost::container::flat_set< uint256 > &ofrsToRm, TIn const &in)
Definition: BookStep.cpp:1135
+
std::optional< QualityFunction > tipOfferQualityF(ReadView const &view) const
Definition: BookStep.cpp:992
AccountID strandDst_
Definition: BookStep.cpp:53
bool inactive() const override
Definition: BookStep.cpp:183
static uint32_t getMaxOffersToConsume(StrandContext const &ctx)
Definition: BookStep.cpp:86
-
std::optional< std::variant< Quality, AMMOffer< TIn, TOut > > > tip(ReadView const &view) const
Definition: BookStep.cpp:939
+
std::optional< std::variant< Quality, AMMOffer< TIn, TOut > > > tip(ReadView const &view) const
Definition: BookStep.cpp:938
Book book_
Definition: BookStep.cpp:51
std::pair< std::optional< Quality >, DebtDirection > qualityUpperBound(ReadView const &v, DebtDirection prevStepDir) const override
Definition: BookStep.cpp:584
bool inactive_
Definition: BookStep.cpp:58
@@ -1673,11 +1672,11 @@ $(function() {
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:446
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:244
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:184
-
static bool equalHelper(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1411
-
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1419
+
static bool equalHelper(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1410
+
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1418
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition: Issue.h:115
-
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1005
+
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1004
bool isConsistent(Book const &book)
Definition: Book.cpp:29
bool isXRP(AccountID const &c)
Definition: AccountID.h:90
static void limitStepIn(Offer const &offer, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TIn const &limit)
Definition: BookStep.cpp:660
@@ -1699,11 +1698,11 @@ $(function() {
@ in
@ out
void SetUnion(boost::container::flat_set< T > &dst, boost::container::flat_set< T > const &src)
Given two flat sets dst and src, compute dst = dst union src.
Definition: FlatSets.h:35
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1488
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1487
DebtDirection
Definition: Steps.h:42
@ redeems
@ issues
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1482
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1481
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:34
WaiveTransferFee
Definition: View.h:44
@ Yes
@@ -1711,9 +1710,9 @@ $(function() {
static void limitStepOut(Offer const &offer, TAmounts< TIn, TOut > &ofrAmt, TAmounts< TIn, TOut > &stpAmt, TOut &ownerGives, std::uint32_t transferRateIn, std::uint32_t transferRateOut, TOut const &limit)
Definition: BookStep.cpp:691
@ tecNO_ISSUER
Definition: TER.h:299
@ tecINVARIANT_FAILED
Definition: TER.h:313
-
static std::pair< TER, std::unique_ptr< Step > > make_BookStepHelper(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1451
+
static std::pair< TER, std::unique_ptr< Step > > make_BookStepHelper(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1450
@ tesSUCCESS
Definition: TER.h:244
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1476
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1475
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:190
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ tapNONE
Definition: ApplyView.h:32
diff --git a/ClosureCounter__test_8cpp_source.html b/ClosureCounter__test_8cpp_source.html index a6ec51b59f..5b0a0374a1 100644 --- a/ClosureCounter__test_8cpp_source.html +++ b/ClosureCounter__test_8cpp_source.html @@ -455,7 +455,7 @@ $(function() {
@ kDisabled
Definition: Journal.h:42
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
-
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1005
+
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1004
@ in
T size(T... args)
T sleep_for(T... args)
diff --git a/CreateOffer_8cpp_source.html b/CreateOffer_8cpp_source.html index ad47ca145d..1cb1ce030d 100644 --- a/CreateOffer_8cpp_source.html +++ b/CreateOffer_8cpp_source.html @@ -128,1370 +128,1304 @@ $(function() {
50 !ctx.rules.enabled(featurePermissionedDEX))
51 return temDISABLED;
52
-
53 // Permissioned offers should use the PE (which must be enabled by
-
54 // featureFlowCross amendment)
-
55 if (ctx.rules.enabled(featurePermissionedDEX) &&
-
56 !ctx.rules.enabled(featureFlowCross))
-
57 return temDISABLED;
+
53 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
+
54 return ret;
+
55
+
56 auto& tx = ctx.tx;
+
57 auto& j = ctx.j;
58
-
59 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
-
60 return ret;
-
61
-
62 auto& tx = ctx.tx;
-
63 auto& j = ctx.j;
-
64
-
65 std::uint32_t const uTxFlags = tx.getFlags();
+
59 std::uint32_t const uTxFlags = tx.getFlags();
+
60
+
61 if (uTxFlags & tfOfferCreateMask)
+
62 {
+
63 JLOG(j.debug()) << "Malformed transaction: Invalid flags set.";
+
64 return temINVALID_FLAG;
+
65 }
66
-
67 if (uTxFlags & tfOfferCreateMask)
-
68 {
-
69 JLOG(j.debug()) << "Malformed transaction: Invalid flags set.";
-
70 return temINVALID_FLAG;
-
71 }
+
67 if (!ctx.rules.enabled(featurePermissionedDEX) && tx.isFlag(tfHybrid))
+
68 return temINVALID_FLAG;
+
69
+
70 if (tx.isFlag(tfHybrid) && !tx.isFieldPresent(sfDomainID))
+
71 return temINVALID_FLAG;
72
-
73 if (!ctx.rules.enabled(featurePermissionedDEX) && tx.isFlag(tfHybrid))
-
74 return temINVALID_FLAG;
+
73 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
+
74 bool const bFillOrKill(uTxFlags & tfFillOrKill);
75
-
76 if (tx.isFlag(tfHybrid) && !tx.isFieldPresent(sfDomainID))
-
77 return temINVALID_FLAG;
-
78
-
79 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
-
80 bool const bFillOrKill(uTxFlags & tfFillOrKill);
+
76 if (bImmediateOrCancel && bFillOrKill)
+
77 {
+
78 JLOG(j.debug()) << "Malformed transaction: both IoC and FoK set.";
+
79 return temINVALID_FLAG;
+
80 }
81
-
82 if (bImmediateOrCancel && bFillOrKill)
-
83 {
-
84 JLOG(j.debug()) << "Malformed transaction: both IoC and FoK set.";
-
85 return temINVALID_FLAG;
-
86 }
-
87
-
88 bool const bHaveExpiration(tx.isFieldPresent(sfExpiration));
+
82 bool const bHaveExpiration(tx.isFieldPresent(sfExpiration));
+
83
+
84 if (bHaveExpiration && (tx.getFieldU32(sfExpiration) == 0))
+
85 {
+
86 JLOG(j.debug()) << "Malformed offer: bad expiration";
+
87 return temBAD_EXPIRATION;
+
88 }
89
-
90 if (bHaveExpiration && (tx.getFieldU32(sfExpiration) == 0))
-
91 {
-
92 JLOG(j.debug()) << "Malformed offer: bad expiration";
-
93 return temBAD_EXPIRATION;
-
94 }
-
95
-
96 if (auto const cancelSequence = tx[~sfOfferSequence];
-
97 cancelSequence && *cancelSequence == 0)
-
98 {
-
99 JLOG(j.debug()) << "Malformed offer: bad cancel sequence";
-
100 return temBAD_SEQUENCE;
-
101 }
+
90 if (auto const cancelSequence = tx[~sfOfferSequence];
+
91 cancelSequence && *cancelSequence == 0)
+
92 {
+
93 JLOG(j.debug()) << "Malformed offer: bad cancel sequence";
+
94 return temBAD_SEQUENCE;
+
95 }
+
96
+
97 STAmount saTakerPays = tx[sfTakerPays];
+
98 STAmount saTakerGets = tx[sfTakerGets];
+
99
+
100 if (!isLegalNet(saTakerPays) || !isLegalNet(saTakerGets))
+
101 return temBAD_AMOUNT;
102
-
103 STAmount saTakerPays = tx[sfTakerPays];
-
104 STAmount saTakerGets = tx[sfTakerGets];
-
105
-
106 if (!isLegalNet(saTakerPays) || !isLegalNet(saTakerGets))
-
107 return temBAD_AMOUNT;
-
108
-
109 if (saTakerPays.native() && saTakerGets.native())
-
110 {
-
111 JLOG(j.debug()) << "Malformed offer: redundant (XRP for XRP)";
-
112 return temBAD_OFFER;
-
113 }
-
114 if (saTakerPays <= beast::zero || saTakerGets <= beast::zero)
-
115 {
-
116 JLOG(j.debug()) << "Malformed offer: bad amount";
-
117 return temBAD_OFFER;
-
118 }
+
103 if (saTakerPays.native() && saTakerGets.native())
+
104 {
+
105 JLOG(j.debug()) << "Malformed offer: redundant (XRP for XRP)";
+
106 return temBAD_OFFER;
+
107 }
+
108 if (saTakerPays <= beast::zero || saTakerGets <= beast::zero)
+
109 {
+
110 JLOG(j.debug()) << "Malformed offer: bad amount";
+
111 return temBAD_OFFER;
+
112 }
+
113
+
114 auto const& uPaysIssuerID = saTakerPays.getIssuer();
+
115 auto const& uPaysCurrency = saTakerPays.getCurrency();
+
116
+
117 auto const& uGetsIssuerID = saTakerGets.getIssuer();
+
118 auto const& uGetsCurrency = saTakerGets.getCurrency();
119
-
120 auto const& uPaysIssuerID = saTakerPays.getIssuer();
-
121 auto const& uPaysCurrency = saTakerPays.getCurrency();
-
122
-
123 auto const& uGetsIssuerID = saTakerGets.getIssuer();
-
124 auto const& uGetsCurrency = saTakerGets.getCurrency();
-
125
-
126 if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
+
120 if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
+
121 {
+
122 JLOG(j.debug()) << "Malformed offer: redundant (IOU for IOU)";
+
123 return temREDUNDANT;
+
124 }
+
125 // We don't allow a non-native currency to use the currency code XRP.
+
126 if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency)
127 {
-
128 JLOG(j.debug()) << "Malformed offer: redundant (IOU for IOU)";
-
129 return temREDUNDANT;
+
128 JLOG(j.debug()) << "Malformed offer: bad currency";
+
129 return temBAD_CURRENCY;
130 }
-
131 // We don't allow a non-native currency to use the currency code XRP.
-
132 if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency)
-
133 {
-
134 JLOG(j.debug()) << "Malformed offer: bad currency";
-
135 return temBAD_CURRENCY;
-
136 }
-
137
-
138 if (saTakerPays.native() != !uPaysIssuerID ||
-
139 saTakerGets.native() != !uGetsIssuerID)
-
140 {
-
141 JLOG(j.debug()) << "Malformed offer: bad issuer";
-
142 return temBAD_ISSUER;
-
143 }
-
144
-
145 return preflight2(ctx);
-
146}
-
147
-
148TER
-
149CreateOffer::preclaim(PreclaimContext const& ctx)
-
150{
-
151 auto const id = ctx.tx[sfAccount];
+
131
+
132 if (saTakerPays.native() != !uPaysIssuerID ||
+
133 saTakerGets.native() != !uGetsIssuerID)
+
134 {
+
135 JLOG(j.debug()) << "Malformed offer: bad issuer";
+
136 return temBAD_ISSUER;
+
137 }
+
138
+
139 return preflight2(ctx);
+
140}
+
141
+
142TER
+
143CreateOffer::preclaim(PreclaimContext const& ctx)
+
144{
+
145 auto const id = ctx.tx[sfAccount];
+
146
+
147 auto saTakerPays = ctx.tx[sfTakerPays];
+
148 auto saTakerGets = ctx.tx[sfTakerGets];
+
149
+
150 auto const& uPaysIssuerID = saTakerPays.getIssuer();
+
151 auto const& uPaysCurrency = saTakerPays.getCurrency();
152
-
153 auto saTakerPays = ctx.tx[sfTakerPays];
-
154 auto saTakerGets = ctx.tx[sfTakerGets];
-
155
-
156 auto const& uPaysIssuerID = saTakerPays.getIssuer();
-
157 auto const& uPaysCurrency = saTakerPays.getCurrency();
-
158
-
159 auto const& uGetsIssuerID = saTakerGets.getIssuer();
+
153 auto const& uGetsIssuerID = saTakerGets.getIssuer();
+
154
+
155 auto const cancelSequence = ctx.tx[~sfOfferSequence];
+
156
+
157 auto const sleCreator = ctx.view.read(keylet::account(id));
+
158 if (!sleCreator)
+
159 return terNO_ACCOUNT;
160
-
161 auto const cancelSequence = ctx.tx[~sfOfferSequence];
+
161 std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence);
162
-
163 auto const sleCreator = ctx.view.read(keylet::account(id));
-
164 if (!sleCreator)
-
165 return terNO_ACCOUNT;
-
166
-
167 std::uint32_t const uAccountSequence = sleCreator->getFieldU32(sfSequence);
-
168
-
169 auto viewJ = ctx.app.journal("View");
-
170
-
171 if (isGlobalFrozen(ctx.view, uPaysIssuerID) ||
-
172 isGlobalFrozen(ctx.view, uGetsIssuerID))
-
173 {
-
174 JLOG(ctx.j.debug()) << "Offer involves frozen asset";
-
175 return tecFROZEN;
-
176 }
-
177
-
178 if (accountFunds(ctx.view, id, saTakerGets, fhZERO_IF_FROZEN, viewJ) <=
-
179 beast::zero)
-
180 {
-
181 JLOG(ctx.j.debug())
-
182 << "delay: Offers must be at least partially funded.";
-
183 return tecUNFUNDED_OFFER;
-
184 }
-
185
-
186 // This can probably be simplified to make sure that you cancel sequences
-
187 // before the transaction sequence number.
-
188 if (cancelSequence && (uAccountSequence <= *cancelSequence))
-
189 {
-
190 JLOG(ctx.j.debug()) << "uAccountSequenceNext=" << uAccountSequence
-
191 << " uOfferSequence=" << *cancelSequence;
-
192 return temBAD_SEQUENCE;
-
193 }
-
194
-
195 if (hasExpired(ctx.view, ctx.tx[~sfExpiration]))
-
196 {
-
197 // Note that this will get checked again in applyGuts, but it saves
-
198 // us a call to checkAcceptAsset and possible false negative.
-
199 //
-
200 // The return code change is attached to featureDepositPreauth as a
-
201 // convenience, as the change is not big enough to deserve its own
-
202 // amendment.
-
203 return ctx.view.rules().enabled(featureDepositPreauth)
-
204 ? TER{tecEXPIRED}
-
205 : TER{tesSUCCESS};
-
206 }
-
207
-
208 // Make sure that we are authorized to hold what the taker will pay us.
-
209 if (!saTakerPays.native())
-
210 {
-
211 auto result = checkAcceptAsset(
-
212 ctx.view,
-
213 ctx.flags,
-
214 id,
-
215 ctx.j,
-
216 Issue(uPaysCurrency, uPaysIssuerID));
-
217 if (result != tesSUCCESS)
-
218 return result;
-
219 }
-
220
-
221 // if domain is specified, make sure that domain exists and the offer create
-
222 // is part of the domain
-
223 if (ctx.tx.isFieldPresent(sfDomainID))
-
224 {
-
225 if (!permissioned_dex::accountInDomain(
-
226 ctx.view, id, ctx.tx[sfDomainID]))
-
227 return tecNO_PERMISSION;
-
228 }
-
229
-
230 return tesSUCCESS;
-
231}
-
232
-
233TER
-
234CreateOffer::checkAcceptAsset(
-
235 ReadView const& view,
-
236 ApplyFlags const flags,
-
237 AccountID const id,
-
238 beast::Journal const j,
-
239 Issue const& issue)
-
240{
-
241 // Only valid for custom currencies
-
242 XRPL_ASSERT(
-
243 !isXRP(issue.currency),
-
244 "ripple::CreateOffer::checkAcceptAsset : input is not XRP");
-
245
-
246 auto const issuerAccount = view.read(keylet::account(issue.account));
+
163 auto viewJ = ctx.app.journal("View");
+
164
+
165 if (isGlobalFrozen(ctx.view, uPaysIssuerID) ||
+
166 isGlobalFrozen(ctx.view, uGetsIssuerID))
+
167 {
+
168 JLOG(ctx.j.debug()) << "Offer involves frozen asset";
+
169 return tecFROZEN;
+
170 }
+
171
+
172 if (accountFunds(ctx.view, id, saTakerGets, fhZERO_IF_FROZEN, viewJ) <=
+
173 beast::zero)
+
174 {
+
175 JLOG(ctx.j.debug())
+
176 << "delay: Offers must be at least partially funded.";
+
177 return tecUNFUNDED_OFFER;
+
178 }
+
179
+
180 // This can probably be simplified to make sure that you cancel sequences
+
181 // before the transaction sequence number.
+
182 if (cancelSequence && (uAccountSequence <= *cancelSequence))
+
183 {
+
184 JLOG(ctx.j.debug()) << "uAccountSequenceNext=" << uAccountSequence
+
185 << " uOfferSequence=" << *cancelSequence;
+
186 return temBAD_SEQUENCE;
+
187 }
+
188
+
189 if (hasExpired(ctx.view, ctx.tx[~sfExpiration]))
+
190 {
+
191 // Note that this will get checked again in applyGuts, but it saves
+
192 // us a call to checkAcceptAsset and possible false negative.
+
193 //
+
194 // The return code change is attached to featureDepositPreauth as a
+
195 // convenience, as the change is not big enough to deserve its own
+
196 // amendment.
+
197 return ctx.view.rules().enabled(featureDepositPreauth)
+
198 ? TER{tecEXPIRED}
+
199 : TER{tesSUCCESS};
+
200 }
+
201
+
202 // Make sure that we are authorized to hold what the taker will pay us.
+
203 if (!saTakerPays.native())
+
204 {
+
205 auto result = checkAcceptAsset(
+
206 ctx.view,
+
207 ctx.flags,
+
208 id,
+
209 ctx.j,
+
210 Issue(uPaysCurrency, uPaysIssuerID));
+
211 if (result != tesSUCCESS)
+
212 return result;
+
213 }
+
214
+
215 // if domain is specified, make sure that domain exists and the offer create
+
216 // is part of the domain
+
217 if (ctx.tx.isFieldPresent(sfDomainID))
+
218 {
+
219 if (!permissioned_dex::accountInDomain(
+
220 ctx.view, id, ctx.tx[sfDomainID]))
+
221 return tecNO_PERMISSION;
+
222 }
+
223
+
224 return tesSUCCESS;
+
225}
+
226
+
227TER
+
228CreateOffer::checkAcceptAsset(
+
229 ReadView const& view,
+
230 ApplyFlags const flags,
+
231 AccountID const id,
+
232 beast::Journal const j,
+
233 Issue const& issue)
+
234{
+
235 // Only valid for custom currencies
+
236 XRPL_ASSERT(
+
237 !isXRP(issue.currency),
+
238 "ripple::CreateOffer::checkAcceptAsset : input is not XRP");
+
239
+
240 auto const issuerAccount = view.read(keylet::account(issue.account));
+
241
+
242 if (!issuerAccount)
+
243 {
+
244 JLOG(j.debug())
+
245 << "delay: can't receive IOUs from non-existent issuer: "
+
246 << to_string(issue.account);
247
-
248 if (!issuerAccount)
-
249 {
-
250 JLOG(j.debug())
-
251 << "delay: can't receive IOUs from non-existent issuer: "
-
252 << to_string(issue.account);
-
253
-
254 return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
-
255 }
-
256
-
257 // This code is attached to the DepositPreauth amendment as a matter of
-
258 // convenience. The change is not significant enough to deserve its
-
259 // own amendment.
-
260 if (view.rules().enabled(featureDepositPreauth) && (issue.account == id))
-
261 // An account can always accept its own issuance.
-
262 return tesSUCCESS;
-
263
-
264 if ((*issuerAccount)[sfFlags] & lsfRequireAuth)
-
265 {
-
266 auto const trustLine =
-
267 view.read(keylet::line(id, issue.account, issue.currency));
-
268
-
269 if (!trustLine)
-
270 {
-
271 return (flags & tapRETRY) ? TER{terNO_LINE} : TER{tecNO_LINE};
-
272 }
-
273
-
274 // Entries have a canonical representation, determined by a
-
275 // lexicographical "greater than" comparison employing strict weak
-
276 // ordering. Determine which entry we need to access.
-
277 bool const canonical_gt(id > issue.account);
-
278
-
279 bool const is_authorized(
-
280 (*trustLine)[sfFlags] & (canonical_gt ? lsfLowAuth : lsfHighAuth));
-
281
-
282 if (!is_authorized)
-
283 {
-
284 JLOG(j.debug())
-
285 << "delay: can't receive IOUs from issuer without auth.";
-
286
-
287 return (flags & tapRETRY) ? TER{terNO_AUTH} : TER{tecNO_AUTH};
-
288 }
-
289 }
-
290
-
291 // An account can not create a trustline to itself, so no line can exist
-
292 // to be frozen. Additionally, an issuer can always accept its own
-
293 // issuance.
-
294 if (issue.account == id)
-
295 {
-
296 return tesSUCCESS;
-
297 }
-
298
-
299 auto const trustLine =
-
300 view.read(keylet::line(id, issue.account, issue.currency));
-
301
-
302 if (!trustLine)
-
303 {
-
304 return tesSUCCESS;
-
305 }
-
306
-
307 // There's no difference which side enacted deep freeze, accepting
-
308 // tokens shouldn't be possible.
-
309 bool const deepFrozen =
-
310 (*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze);
-
311
-
312 if (deepFrozen)
-
313 {
-
314 return tecFROZEN;
-
315 }
-
316
-
317 return tesSUCCESS;
-
318}
-
319
-
320bool
-
321CreateOffer::dry_offer(ApplyView& view, Offer const& offer)
-
322{
-
323 if (offer.fully_consumed())
-
324 return true;
-
325 auto const amount = accountFunds(
-
326 view,
-
327 offer.owner(),
-
328 offer.amount().out,
-
329 fhZERO_IF_FROZEN,
-
330 ctx_.app.journal("View"));
-
331 return (amount <= beast::zero);
-
332}
-
333
-
334std::pair<bool, Quality>
-
335CreateOffer::select_path(
-
336 bool have_direct,
-
337 OfferStream const& direct,
-
338 bool have_bridge,
-
339 OfferStream const& leg1,
-
340 OfferStream const& leg2)
-
341{
-
342 // If we don't have any viable path, why are we here?!
-
343 XRPL_ASSERT(
-
344 have_direct || have_bridge,
-
345 "ripple::CreateOffer::select_path : valid inputs");
-
346
-
347 // If there's no bridged path, the direct is the best by default.
-
348 if (!have_bridge)
-
349 return std::make_pair(true, direct.tip().quality());
-
350
-
351 Quality const bridged_quality(
-
352 composed_quality(leg1.tip().quality(), leg2.tip().quality()));
+
248 return (flags & tapRETRY) ? TER{terNO_ACCOUNT} : TER{tecNO_ISSUER};
+
249 }
+
250
+
251 // This code is attached to the DepositPreauth amendment as a matter of
+
252 // convenience. The change is not significant enough to deserve its
+
253 // own amendment.
+
254 if (view.rules().enabled(featureDepositPreauth) && (issue.account == id))
+
255 // An account can always accept its own issuance.
+
256 return tesSUCCESS;
+
257
+
258 if ((*issuerAccount)[sfFlags] & lsfRequireAuth)
+
259 {
+
260 auto const trustLine =
+
261 view.read(keylet::line(id, issue.account, issue.currency));
+
262
+
263 if (!trustLine)
+
264 {
+
265 return (flags & tapRETRY) ? TER{terNO_LINE} : TER{tecNO_LINE};
+
266 }
+
267
+
268 // Entries have a canonical representation, determined by a
+
269 // lexicographical "greater than" comparison employing strict weak
+
270 // ordering. Determine which entry we need to access.
+
271 bool const canonical_gt(id > issue.account);
+
272
+
273 bool const is_authorized(
+
274 (*trustLine)[sfFlags] & (canonical_gt ? lsfLowAuth : lsfHighAuth));
+
275
+
276 if (!is_authorized)
+
277 {
+
278 JLOG(j.debug())
+
279 << "delay: can't receive IOUs from issuer without auth.";
+
280
+
281 return (flags & tapRETRY) ? TER{terNO_AUTH} : TER{tecNO_AUTH};
+
282 }
+
283 }
+
284
+
285 // An account can not create a trustline to itself, so no line can exist
+
286 // to be frozen. Additionally, an issuer can always accept its own
+
287 // issuance.
+
288 if (issue.account == id)
+
289 {
+
290 return tesSUCCESS;
+
291 }
+
292
+
293 auto const trustLine =
+
294 view.read(keylet::line(id, issue.account, issue.currency));
+
295
+
296 if (!trustLine)
+
297 {
+
298 return tesSUCCESS;
+
299 }
+
300
+
301 // There's no difference which side enacted deep freeze, accepting
+
302 // tokens shouldn't be possible.
+
303 bool const deepFrozen =
+
304 (*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze);
+
305
+
306 if (deepFrozen)
+
307 {
+
308 return tecFROZEN;
+
309 }
+
310
+
311 return tesSUCCESS;
+
312}
+
313
+
314bool
+
315CreateOffer::dry_offer(ApplyView& view, Offer const& offer)
+
316{
+
317 if (offer.fully_consumed())
+
318 return true;
+
319 auto const amount = accountFunds(
+
320 view,
+
321 offer.owner(),
+
322 offer.amount().out,
+
323 fhZERO_IF_FROZEN,
+
324 ctx_.app.journal("View"));
+
325 return (amount <= beast::zero);
+
326}
+
327
+
328std::pair<bool, Quality>
+
329CreateOffer::select_path(
+
330 bool have_direct,
+
331 OfferStream const& direct,
+
332 bool have_bridge,
+
333 OfferStream const& leg1,
+
334 OfferStream const& leg2)
+
335{
+
336 // If we don't have any viable path, why are we here?!
+
337 XRPL_ASSERT(
+
338 have_direct || have_bridge,
+
339 "ripple::CreateOffer::select_path : valid inputs");
+
340
+
341 // If there's no bridged path, the direct is the best by default.
+
342 if (!have_bridge)
+
343 return std::make_pair(true, direct.tip().quality());
+
344
+
345 Quality const bridged_quality(
+
346 composed_quality(leg1.tip().quality(), leg2.tip().quality()));
+
347
+
348 if (have_direct)
+
349 {
+
350 // We compare the quality of the composed quality of the bridged
+
351 // offers and compare it against the direct offer to pick the best.
+
352 Quality const direct_quality(direct.tip().quality());
353
-
354 if (have_direct)
-
355 {
-
356 // We compare the quality of the composed quality of the bridged
-
357 // offers and compare it against the direct offer to pick the best.
-
358 Quality const direct_quality(direct.tip().quality());
-
359
-
360 if (bridged_quality < direct_quality)
-
361 return std::make_pair(true, direct_quality);
-
362 }
-
363
-
364 // Either there was no direct offer, or it didn't have a better quality
-
365 // than the bridge.
-
366 return std::make_pair(false, bridged_quality);
-
367}
+
354 if (bridged_quality < direct_quality)
+
355 return std::make_pair(true, direct_quality);
+
356 }
+
357
+
358 // Either there was no direct offer, or it didn't have a better quality
+
359 // than the bridge.
+
360 return std::make_pair(false, bridged_quality);
+
361}
+
362
+
363bool
+
364CreateOffer::reachedOfferCrossingLimit(Taker const& taker) const
+
365{
+
366 auto const crossings =
+
367 taker.get_direct_crossings() + (2 * taker.get_bridge_crossings());
368
-
369bool
-
370CreateOffer::reachedOfferCrossingLimit(Taker const& taker) const
-
371{
-
372 auto const crossings =
-
373 taker.get_direct_crossings() + (2 * taker.get_bridge_crossings());
-
374
-
375 // The crossing limit is part of the Ripple protocol and
-
376 // changing it is a transaction-processing change.
-
377 return crossings >= 850;
-
378}
-
379
-
380std::pair<TER, Amounts>
-
381CreateOffer::bridged_cross(
-
382 Taker& taker,
-
383 ApplyView& view,
-
384 ApplyView& view_cancel,
-
385 NetClock::time_point const when)
-
386{
-
387 auto const& takerAmount = taker.original_offer();
-
388
-
389 XRPL_ASSERT(
-
390 !isXRP(takerAmount.in) && !isXRP(takerAmount.out),
-
391 "ripple::CreateOffer::bridged_cross : neither is XRP");
-
392
-
393 if (isXRP(takerAmount.in) || isXRP(takerAmount.out))
-
394 Throw<std::logic_error>("Bridging with XRP and an endpoint.");
-
395
-
396 OfferStream offers_direct(
-
397 view,
-
398 view_cancel,
-
399 Book(taker.issue_in(), taker.issue_out(), std::nullopt),
-
400 when,
-
401 stepCounter_,
-
402 j_);
-
403
-
404 OfferStream offers_leg1(
-
405 view,
-
406 view_cancel,
-
407 Book(taker.issue_in(), xrpIssue(), std::nullopt),
-
408 when,
-
409 stepCounter_,
-
410 j_);
-
411
-
412 OfferStream offers_leg2(
-
413 view,
-
414 view_cancel,
-
415 Book(xrpIssue(), taker.issue_out(), std::nullopt),
-
416 when,
-
417 stepCounter_,
-
418 j_);
-
419
-
420 TER cross_result = tesSUCCESS;
-
421
-
422 // Note the subtle distinction here: self-offers encountered in the
-
423 // bridge are taken, but self-offers encountered in the direct book
-
424 // are not.
-
425 bool have_bridge = offers_leg1.step() && offers_leg2.step();
-
426 bool have_direct = step_account(offers_direct, taker);
-
427 int count = 0;
-
428
-
429 auto viewJ = ctx_.app.journal("View");
-
430
-
431 // Modifying the order or logic of the operations in the loop will cause
-
432 // a protocol breaking change.
-
433 while (have_direct || have_bridge)
-
434 {
-
435 bool leg1_consumed = false;
-
436 bool leg2_consumed = false;
-
437 bool direct_consumed = false;
-
438
-
439 auto const [use_direct, quality] = select_path(
-
440 have_direct, offers_direct, have_bridge, offers_leg1, offers_leg2);
-
441
-
442 // We are always looking at the best quality; we are done with
-
443 // crossing as soon as we cross the quality boundary.
-
444 if (taker.reject(quality))
-
445 break;
-
446
-
447 count++;
-
448
-
449 if (use_direct)
-
450 {
-
451 if (auto stream = j_.debug())
-
452 {
-
453 stream << count << " Direct:";
-
454 stream << " offer: " << offers_direct.tip();
-
455 stream << " in: " << offers_direct.tip().amount().in;
-
456 stream << " out: " << offers_direct.tip().amount().out;
-
457 stream << " owner: " << offers_direct.tip().owner();
-
458 stream << " funds: "
-
459 << accountFunds(
-
460 view,
-
461 offers_direct.tip().owner(),
-
462 offers_direct.tip().amount().out,
-
463 fhIGNORE_FREEZE,
-
464 viewJ);
-
465 }
-
466
-
467 cross_result = taker.cross(offers_direct.tip());
-
468
-
469 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
-
470
-
471 if (dry_offer(view, offers_direct.tip()))
-
472 {
-
473 direct_consumed = true;
-
474 have_direct = step_account(offers_direct, taker);
-
475 }
-
476 }
-
477 else
-
478 {
-
479 if (auto stream = j_.debug())
-
480 {
-
481 auto const owner1_funds_before = accountFunds(
-
482 view,
-
483 offers_leg1.tip().owner(),
-
484 offers_leg1.tip().amount().out,
-
485 fhIGNORE_FREEZE,
-
486 viewJ);
-
487
-
488 auto const owner2_funds_before = accountFunds(
-
489 view,
-
490 offers_leg2.tip().owner(),
-
491 offers_leg2.tip().amount().out,
-
492 fhIGNORE_FREEZE,
-
493 viewJ);
-
494
-
495 stream << count << " Bridge:";
-
496 stream << " offer1: " << offers_leg1.tip();
-
497 stream << " in: " << offers_leg1.tip().amount().in;
-
498 stream << " out: " << offers_leg1.tip().amount().out;
-
499 stream << " owner: " << offers_leg1.tip().owner();
-
500 stream << " funds: " << owner1_funds_before;
-
501 stream << " offer2: " << offers_leg2.tip();
-
502 stream << " in: " << offers_leg2.tip().amount().in;
-
503 stream << " out: " << offers_leg2.tip().amount().out;
-
504 stream << " owner: " << offers_leg2.tip().owner();
-
505 stream << " funds: " << owner2_funds_before;
-
506 }
-
507
-
508 cross_result = taker.cross(offers_leg1.tip(), offers_leg2.tip());
-
509
-
510 JLOG(j_.debug()) << "Bridge Result: " << transToken(cross_result);
-
511
-
512 if (view.rules().enabled(fixTakerDryOfferRemoval))
-
513 {
-
514 // have_bridge can be true the next time 'round only if
-
515 // neither of the OfferStreams are dry.
-
516 leg1_consumed = dry_offer(view, offers_leg1.tip());
-
517 if (leg1_consumed)
-
518 have_bridge &= offers_leg1.step();
-
519
-
520 leg2_consumed = dry_offer(view, offers_leg2.tip());
-
521 if (leg2_consumed)
-
522 have_bridge &= offers_leg2.step();
-
523 }
-
524 else
-
525 {
-
526 // This old behavior may leave an empty offer in the book for
-
527 // the second leg.
-
528 if (dry_offer(view, offers_leg1.tip()))
-
529 {
-
530 leg1_consumed = true;
-
531 have_bridge = (have_bridge && offers_leg1.step());
-
532 }
-
533 if (dry_offer(view, offers_leg2.tip()))
-
534 {
-
535 leg2_consumed = true;
-
536 have_bridge = (have_bridge && offers_leg2.step());
-
537 }
-
538 }
+
369 // The crossing limit is part of the Ripple protocol and
+
370 // changing it is a transaction-processing change.
+
371 return crossings >= 850;
+
372}
+
373
+
374std::pair<TER, Amounts>
+
375CreateOffer::bridged_cross(
+
376 Taker& taker,
+
377 ApplyView& view,
+
378 ApplyView& view_cancel,
+
379 NetClock::time_point const when)
+
380{
+
381 auto const& takerAmount = taker.original_offer();
+
382
+
383 XRPL_ASSERT(
+
384 !isXRP(takerAmount.in) && !isXRP(takerAmount.out),
+
385 "ripple::CreateOffer::bridged_cross : neither is XRP");
+
386
+
387 if (isXRP(takerAmount.in) || isXRP(takerAmount.out))
+
388 Throw<std::logic_error>("Bridging with XRP and an endpoint.");
+
389
+
390 OfferStream offers_direct(
+
391 view,
+
392 view_cancel,
+
393 Book(taker.issue_in(), taker.issue_out(), std::nullopt),
+
394 when,
+
395 stepCounter_,
+
396 j_);
+
397
+
398 OfferStream offers_leg1(
+
399 view,
+
400 view_cancel,
+
401 Book(taker.issue_in(), xrpIssue(), std::nullopt),
+
402 when,
+
403 stepCounter_,
+
404 j_);
+
405
+
406 OfferStream offers_leg2(
+
407 view,
+
408 view_cancel,
+
409 Book(xrpIssue(), taker.issue_out(), std::nullopt),
+
410 when,
+
411 stepCounter_,
+
412 j_);
+
413
+
414 TER cross_result = tesSUCCESS;
+
415
+
416 // Note the subtle distinction here: self-offers encountered in the
+
417 // bridge are taken, but self-offers encountered in the direct book
+
418 // are not.
+
419 bool have_bridge = offers_leg1.step() && offers_leg2.step();
+
420 bool have_direct = step_account(offers_direct, taker);
+
421 int count = 0;
+
422
+
423 auto viewJ = ctx_.app.journal("View");
+
424
+
425 // Modifying the order or logic of the operations in the loop will cause
+
426 // a protocol breaking change.
+
427 while (have_direct || have_bridge)
+
428 {
+
429 bool leg1_consumed = false;
+
430 bool leg2_consumed = false;
+
431 bool direct_consumed = false;
+
432
+
433 auto const [use_direct, quality] = select_path(
+
434 have_direct, offers_direct, have_bridge, offers_leg1, offers_leg2);
+
435
+
436 // We are always looking at the best quality; we are done with
+
437 // crossing as soon as we cross the quality boundary.
+
438 if (taker.reject(quality))
+
439 break;
+
440
+
441 count++;
+
442
+
443 if (use_direct)
+
444 {
+
445 if (auto stream = j_.debug())
+
446 {
+
447 stream << count << " Direct:";
+
448 stream << " offer: " << offers_direct.tip();
+
449 stream << " in: " << offers_direct.tip().amount().in;
+
450 stream << " out: " << offers_direct.tip().amount().out;
+
451 stream << " owner: " << offers_direct.tip().owner();
+
452 stream << " funds: "
+
453 << accountFunds(
+
454 view,
+
455 offers_direct.tip().owner(),
+
456 offers_direct.tip().amount().out,
+
457 fhIGNORE_FREEZE,
+
458 viewJ);
+
459 }
+
460
+
461 cross_result = taker.cross(offers_direct.tip());
+
462
+
463 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
+
464
+
465 if (dry_offer(view, offers_direct.tip()))
+
466 {
+
467 direct_consumed = true;
+
468 have_direct = step_account(offers_direct, taker);
+
469 }
+
470 }
+
471 else
+
472 {
+
473 if (auto stream = j_.debug())
+
474 {
+
475 auto const owner1_funds_before = accountFunds(
+
476 view,
+
477 offers_leg1.tip().owner(),
+
478 offers_leg1.tip().amount().out,
+
479 fhIGNORE_FREEZE,
+
480 viewJ);
+
481
+
482 auto const owner2_funds_before = accountFunds(
+
483 view,
+
484 offers_leg2.tip().owner(),
+
485 offers_leg2.tip().amount().out,
+
486 fhIGNORE_FREEZE,
+
487 viewJ);
+
488
+
489 stream << count << " Bridge:";
+
490 stream << " offer1: " << offers_leg1.tip();
+
491 stream << " in: " << offers_leg1.tip().amount().in;
+
492 stream << " out: " << offers_leg1.tip().amount().out;
+
493 stream << " owner: " << offers_leg1.tip().owner();
+
494 stream << " funds: " << owner1_funds_before;
+
495 stream << " offer2: " << offers_leg2.tip();
+
496 stream << " in: " << offers_leg2.tip().amount().in;
+
497 stream << " out: " << offers_leg2.tip().amount().out;
+
498 stream << " owner: " << offers_leg2.tip().owner();
+
499 stream << " funds: " << owner2_funds_before;
+
500 }
+
501
+
502 cross_result = taker.cross(offers_leg1.tip(), offers_leg2.tip());
+
503
+
504 JLOG(j_.debug()) << "Bridge Result: " << transToken(cross_result);
+
505
+
506 if (view.rules().enabled(fixTakerDryOfferRemoval))
+
507 {
+
508 // have_bridge can be true the next time 'round only if
+
509 // neither of the OfferStreams are dry.
+
510 leg1_consumed = dry_offer(view, offers_leg1.tip());
+
511 if (leg1_consumed)
+
512 have_bridge &= offers_leg1.step();
+
513
+
514 leg2_consumed = dry_offer(view, offers_leg2.tip());
+
515 if (leg2_consumed)
+
516 have_bridge &= offers_leg2.step();
+
517 }
+
518 else
+
519 {
+
520 // This old behavior may leave an empty offer in the book for
+
521 // the second leg.
+
522 if (dry_offer(view, offers_leg1.tip()))
+
523 {
+
524 leg1_consumed = true;
+
525 have_bridge = (have_bridge && offers_leg1.step());
+
526 }
+
527 if (dry_offer(view, offers_leg2.tip()))
+
528 {
+
529 leg2_consumed = true;
+
530 have_bridge = (have_bridge && offers_leg2.step());
+
531 }
+
532 }
+
533 }
+
534
+
535 if (cross_result != tesSUCCESS)
+
536 {
+
537 cross_result = tecFAILED_PROCESSING;
+
538 break;
539 }
540
-
541 if (cross_result != tesSUCCESS)
+
541 if (taker.done())
542 {
-
543 cross_result = tecFAILED_PROCESSING;
+
543 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
544 break;
545 }
546
-
547 if (taker.done())
+
547 if (reachedOfferCrossingLimit(taker))
548 {
-
549 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
+
549 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
550 break;
551 }
552
-
553 if (reachedOfferCrossingLimit(taker))
-
554 {
-
555 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
-
556 break;
-
557 }
+
553 // Postcondition: If we aren't done, then we *must* have consumed at
+
554 // least one offer fully.
+
555 XRPL_ASSERT(
+
556 direct_consumed || leg1_consumed || leg2_consumed,
+
557 "ripple::CreateOffer::bridged_cross : consumed an offer");
558
-
559 // Postcondition: If we aren't done, then we *must* have consumed at
-
560 // least one offer fully.
-
561 XRPL_ASSERT(
-
562 direct_consumed || leg1_consumed || leg2_consumed,
-
563 "ripple::CreateOffer::bridged_cross : consumed an offer");
-
564
-
565 if (!direct_consumed && !leg1_consumed && !leg2_consumed)
-
566 Throw<std::logic_error>(
-
567 "bridged crossing: nothing was fully consumed.");
-
568 }
-
569
-
570 return std::make_pair(cross_result, taker.remaining_offer());
-
571}
-
572
-
573std::pair<TER, Amounts>
-
574CreateOffer::direct_cross(
-
575 Taker& taker,
-
576 ApplyView& view,
-
577 ApplyView& view_cancel,
-
578 NetClock::time_point const when)
-
579{
-
580 OfferStream offers(
-
581 view,
-
582 view_cancel,
-
583 Book(taker.issue_in(), taker.issue_out(), std::nullopt),
-
584 when,
-
585 stepCounter_,
-
586 j_);
-
587
-
588 TER cross_result(tesSUCCESS);
-
589 int count = 0;
-
590
-
591 bool have_offer = step_account(offers, taker);
-
592
-
593 // Modifying the order or logic of the operations in the loop will cause
-
594 // a protocol breaking change.
-
595 while (have_offer)
-
596 {
-
597 bool direct_consumed = false;
-
598 auto& offer(offers.tip());
+
559 if (!direct_consumed && !leg1_consumed && !leg2_consumed)
+
560 Throw<std::logic_error>(
+
561 "bridged crossing: nothing was fully consumed.");
+
562 }
+
563
+
564 return std::make_pair(cross_result, taker.remaining_offer());
+
565}
+
566
+
567std::pair<TER, Amounts>
+
568CreateOffer::direct_cross(
+
569 Taker& taker,
+
570 ApplyView& view,
+
571 ApplyView& view_cancel,
+
572 NetClock::time_point const when)
+
573{
+
574 OfferStream offers(
+
575 view,
+
576 view_cancel,
+
577 Book(taker.issue_in(), taker.issue_out(), std::nullopt),
+
578 when,
+
579 stepCounter_,
+
580 j_);
+
581
+
582 TER cross_result(tesSUCCESS);
+
583 int count = 0;
+
584
+
585 bool have_offer = step_account(offers, taker);
+
586
+
587 // Modifying the order or logic of the operations in the loop will cause
+
588 // a protocol breaking change.
+
589 while (have_offer)
+
590 {
+
591 bool direct_consumed = false;
+
592 auto& offer(offers.tip());
+
593
+
594 // We are done with crossing as soon as we cross the quality boundary
+
595 if (taker.reject(offer.quality()))
+
596 break;
+
597
+
598 count++;
599
-
600 // We are done with crossing as soon as we cross the quality boundary
-
601 if (taker.reject(offer.quality()))
-
602 break;
-
603
-
604 count++;
-
605
-
606 if (auto stream = j_.debug())
-
607 {
-
608 stream << count << " Direct:";
-
609 stream << " offer: " << offer;
-
610 stream << " in: " << offer.amount().in;
-
611 stream << " out: " << offer.amount().out;
-
612 stream << "quality: " << offer.quality();
-
613 stream << " owner: " << offer.owner();
-
614 stream << " funds: "
-
615 << accountFunds(
-
616 view,
-
617 offer.owner(),
-
618 offer.amount().out,
-
619 fhIGNORE_FREEZE,
-
620 ctx_.app.journal("View"));
-
621 }
-
622
-
623 cross_result = taker.cross(offer);
-
624
-
625 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
+
600 if (auto stream = j_.debug())
+
601 {
+
602 stream << count << " Direct:";
+
603 stream << " offer: " << offer;
+
604 stream << " in: " << offer.amount().in;
+
605 stream << " out: " << offer.amount().out;
+
606 stream << "quality: " << offer.quality();
+
607 stream << " owner: " << offer.owner();
+
608 stream << " funds: "
+
609 << accountFunds(
+
610 view,
+
611 offer.owner(),
+
612 offer.amount().out,
+
613 fhIGNORE_FREEZE,
+
614 ctx_.app.journal("View"));
+
615 }
+
616
+
617 cross_result = taker.cross(offer);
+
618
+
619 JLOG(j_.debug()) << "Direct Result: " << transToken(cross_result);
+
620
+
621 if (dry_offer(view, offer))
+
622 {
+
623 direct_consumed = true;
+
624 have_offer = step_account(offers, taker);
+
625 }
626
-
627 if (dry_offer(view, offer))
+
627 if (cross_result != tesSUCCESS)
628 {
-
629 direct_consumed = true;
-
630 have_offer = step_account(offers, taker);
+
629 cross_result = tecFAILED_PROCESSING;
+
630 break;
631 }
632
-
633 if (cross_result != tesSUCCESS)
+
633 if (taker.done())
634 {
-
635 cross_result = tecFAILED_PROCESSING;
+
635 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
636 break;
637 }
638
-
639 if (taker.done())
+
639 if (reachedOfferCrossingLimit(taker))
640 {
-
641 JLOG(j_.debug()) << "The taker reports he's done during crossing!";
+
641 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
642 break;
643 }
644
-
645 if (reachedOfferCrossingLimit(taker))
-
646 {
-
647 JLOG(j_.debug()) << "The offer crossing limit has been exceeded!";
-
648 break;
-
649 }
+
645 // Postcondition: If we aren't done, then we *must* have consumed the
+
646 // offer on the books fully!
+
647 XRPL_ASSERT(
+
648 direct_consumed,
+
649 "ripple::CreateOffer::direct_cross : consumed an offer");
650
-
651 // Postcondition: If we aren't done, then we *must* have consumed the
-
652 // offer on the books fully!
-
653 XRPL_ASSERT(
-
654 direct_consumed,
-
655 "ripple::CreateOffer::direct_cross : consumed an offer");
-
656
-
657 if (!direct_consumed)
-
658 Throw<std::logic_error>(
-
659 "direct crossing: nothing was fully consumed.");
-
660 }
-
661
-
662 return std::make_pair(cross_result, taker.remaining_offer());
-
663}
-
664
-
665// Step through the stream for as long as possible, skipping any offers
-
666// that are from the taker or which cross the taker's threshold.
-
667// Return false if the is no offer in the book, true otherwise.
-
668bool
-
669CreateOffer::step_account(OfferStream& stream, Taker const& taker)
-
670{
-
671 while (stream.step())
-
672 {
-
673 auto const& offer = stream.tip();
-
674
-
675 // This offer at the tip crosses the taker's threshold. We're done.
-
676 if (taker.reject(offer.quality()))
-
677 return true;
-
678
-
679 // This offer at the tip is not from the taker. We're done.
-
680 if (offer.owner() != taker.account())
-
681 return true;
-
682 }
-
683
-
684 // We ran out of offers. Can't advance.
-
685 return false;
-
686}
-
687
-
688// Fill as much of the offer as possible by consuming offers
-
689// already on the books. Return the status and the amount of
-
690// the offer to left unfilled.
-
691std::pair<TER, Amounts>
-
692CreateOffer::takerCross(
-
693 Sandbox& sb,
-
694 Sandbox& sbCancel,
-
695 Amounts const& takerAmount)
-
696{
-
697 NetClock::time_point const when = sb.parentCloseTime();
-
698
-
699 beast::WrappedSink takerSink(j_, "Taker ");
-
700
-
701 Taker taker(
-
702 cross_type_,
-
703 sb,
-
704 account_,
-
705 takerAmount,
-
706 ctx_.tx.getFlags(),
-
707 beast::Journal(takerSink));
-
708
-
709 // If the taker is unfunded before we begin crossing
-
710 // there's nothing to do - just return an error.
-
711 //
-
712 // We check this in preclaim, but when selling XRP
-
713 // charged fees can cause a user's available balance
-
714 // to go to 0 (by causing it to dip below the reserve)
-
715 // so we check this case again.
-
716 if (taker.unfunded())
-
717 {
-
718 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
-
719 return {tecUNFUNDED_OFFER, takerAmount};
-
720 }
-
721
-
722 try
-
723 {
-
724 if (cross_type_ == CrossType::IouToIou)
-
725 return bridged_cross(taker, sb, sbCancel, when);
-
726
-
727 return direct_cross(taker, sb, sbCancel, when);
-
728 }
-
729 catch (std::exception const& e)
-
730 {
-
731 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
-
732 return {tecINTERNAL, taker.remaining_offer()};
-
733 }
-
734}
-
735
-
736std::pair<TER, Amounts>
-
737CreateOffer::flowCross(
-
738 PaymentSandbox& psb,
-
739 PaymentSandbox& psbCancel,
-
740 Amounts const& takerAmount,
-
741 std::optional<uint256> const& domainID)
-
742{
-
743 try
-
744 {
-
745 // If the taker is unfunded before we begin crossing there's nothing
-
746 // to do - just return an error.
-
747 //
-
748 // We check this in preclaim, but when selling XRP charged fees can
-
749 // cause a user's available balance to go to 0 (by causing it to dip
-
750 // below the reserve) so we check this case again.
-
751 STAmount const inStartBalance =
-
752 accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
-
753 if (inStartBalance <= beast::zero)
-
754 {
-
755 // The account balance can't cover even part of the offer.
-
756 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
-
757 return {tecUNFUNDED_OFFER, takerAmount};
-
758 }
-
759
-
760 // If the gateway has a transfer rate, accommodate that. The
-
761 // gateway takes its cut without any special consent from the
-
762 // offer taker. Set sendMax to allow for the gateway's cut.
-
763 Rate gatewayXferRate{QUALITY_ONE};
-
764 STAmount sendMax = takerAmount.in;
-
765 if (!sendMax.native() && (account_ != sendMax.getIssuer()))
-
766 {
-
767 gatewayXferRate = transferRate(psb, sendMax.getIssuer());
-
768 if (gatewayXferRate.value != QUALITY_ONE)
-
769 {
-
770 sendMax = multiplyRound(
-
771 takerAmount.in,
-
772 gatewayXferRate,
-
773 takerAmount.in.issue(),
-
774 true);
-
775 }
-
776 }
-
777
-
778 // Payment flow code compares quality after the transfer rate is
-
779 // included. Since transfer rate is incorporated compute threshold.
-
780 Quality threshold{takerAmount.out, sendMax};
-
781
-
782 // If we're creating a passive offer adjust the threshold so we only
-
783 // cross offers that have a better quality than this one.
-
784 std::uint32_t const txFlags = ctx_.tx.getFlags();
-
785 if (txFlags & tfPassive)
-
786 ++threshold;
-
787
-
788 // Don't send more than our balance.
-
789 if (sendMax > inStartBalance)
-
790 sendMax = inStartBalance;
-
791
-
792 // Always invoke flow() with the default path. However if neither
-
793 // of the takerAmount currencies are XRP then we cross through an
-
794 // additional path with XRP as the intermediate between two books.
-
795 // This second path we have to build ourselves.
-
796 STPathSet paths;
-
797 if (!takerAmount.in.native() && !takerAmount.out.native())
-
798 {
-
799 STPath path;
-
800 path.emplace_back(std::nullopt, xrpCurrency(), std::nullopt);
-
801 paths.emplace_back(std::move(path));
-
802 }
-
803 // Special handling for the tfSell flag.
-
804 STAmount deliver = takerAmount.out;
-
805 OfferCrossing offerCrossing = OfferCrossing::yes;
-
806 if (txFlags & tfSell)
-
807 {
-
808 offerCrossing = OfferCrossing::sell;
-
809 // We are selling, so we will accept *more* than the offer
-
810 // specified. Since we don't know how much they might offer,
-
811 // we allow delivery of the largest possible amount.
-
812 if (deliver.native())
-
813 deliver = STAmount{STAmount::cMaxNative};
-
814 else
-
815 // We can't use the maximum possible currency here because
-
816 // there might be a gateway transfer rate to account for.
-
817 // Since the transfer rate cannot exceed 200%, we use 1/2
-
818 // maxValue for our limit.
-
819 deliver = STAmount{
-
820 takerAmount.out.issue(),
-
821 STAmount::cMaxValue / 2,
-
822 STAmount::cMaxOffset};
-
823 }
-
824
-
825 // Call the payment engine's flow() to do the actual work.
-
826 auto const result = flow(
-
827 psb,
-
828 deliver,
-
829 account_,
-
830 account_,
-
831 paths,
-
832 true, // default path
-
833 !(txFlags & tfFillOrKill), // partial payment
-
834 true, // owner pays transfer fee
-
835 offerCrossing,
-
836 threshold,
-
837 sendMax,
-
838 domainID,
-
839 j_);
-
840
-
841 // If stale offers were found remove them.
-
842 for (auto const& toRemove : result.removableOffers)
-
843 {
-
844 if (auto otr = psb.peek(keylet::offer(toRemove)))
-
845 offerDelete(psb, otr, j_);
-
846 if (auto otr = psbCancel.peek(keylet::offer(toRemove)))
-
847 offerDelete(psbCancel, otr, j_);
-
848 }
-
849
-
850 // Determine the size of the final offer after crossing.
-
851 auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged
-
852 if (isTesSuccess(result.result()))
-
853 {
-
854 STAmount const takerInBalance = accountFunds(
-
855 psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
-
856
-
857 if (takerInBalance <= beast::zero)
-
858 {
-
859 // If offer crossing exhausted the account's funds don't
-
860 // create the offer.
-
861 afterCross.in.clear();
-
862 afterCross.out.clear();
-
863 }
-
864 else
-
865 {
-
866 STAmount const rate{
-
867 Quality{takerAmount.out, takerAmount.in}.rate()};
-
868
-
869 if (txFlags & tfSell)
-
870 {
-
871 // If selling then scale the new out amount based on how
-
872 // much we sold during crossing. This preserves the offer
-
873 // Quality,
-
874
-
875 // Reduce the offer that is placed by the crossed amount.
-
876 // Note that we must ignore the portion of the
-
877 // actualAmountIn that may have been consumed by a
-
878 // gateway's transfer rate.
-
879 STAmount nonGatewayAmountIn = result.actualAmountIn;
-
880 if (gatewayXferRate.value != QUALITY_ONE)
-
881 nonGatewayAmountIn = divideRound(
-
882 result.actualAmountIn,
-
883 gatewayXferRate,
-
884 takerAmount.in.issue(),
-
885 true);
-
886
-
887 afterCross.in -= nonGatewayAmountIn;
-
888
-
889 // It's possible that the divRound will cause our subtract
-
890 // to go slightly negative. So limit afterCross.in to zero.
-
891 if (afterCross.in < beast::zero)
-
892 // We should verify that the difference *is* small, but
-
893 // what is a good threshold to check?
-
894 afterCross.in.clear();
-
895
-
896 afterCross.out = [&]() {
-
897 // Careful analysis showed that rounding up this
-
898 // divRound result could lead to placing a reduced
-
899 // offer in the ledger that blocks order books. So
-
900 // the fixReducedOffersV1 amendment changes the
-
901 // behavior to round down instead.
-
902 if (psb.rules().enabled(fixReducedOffersV1))
-
903 return divRoundStrict(
-
904 afterCross.in,
-
905 rate,
-
906 takerAmount.out.issue(),
-
907 false);
-
908
-
909 return divRound(
-
910 afterCross.in, rate, takerAmount.out.issue(), true);
-
911 }();
-
912 }
-
913 else
-
914 {
-
915 // If not selling, we scale the input based on the
-
916 // remaining output. This too preserves the offer
-
917 // Quality.
-
918 afterCross.out -= result.actualAmountOut;
-
919 XRPL_ASSERT(
-
920 afterCross.out >= beast::zero,
-
921 "ripple::CreateOffer::flowCross : minimum offer");
-
922 if (afterCross.out < beast::zero)
-
923 afterCross.out.clear();
-
924 afterCross.in = mulRound(
-
925 afterCross.out, rate, takerAmount.in.issue(), true);
-
926 }
-
927 }
-
928 }
-
929
-
930 // Return how much of the offer is left.
-
931 return {tesSUCCESS, afterCross};
-
932 }
-
933 catch (std::exception const& e)
-
934 {
-
935 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
-
936 }
-
937 return {tecINTERNAL, takerAmount};
-
938}
-
939
-
940std::pair<TER, Amounts>
-
941CreateOffer::cross(
-
942 Sandbox& sb,
-
943 Sandbox& sbCancel,
-
944 Amounts const& takerAmount,
-
945 std::optional<uint256> const& domainID)
-
946{
-
947 if (sb.rules().enabled(featureFlowCross))
-
948 {
-
949 PaymentSandbox psbFlow{&sb};
-
950 PaymentSandbox psbCancelFlow{&sbCancel};
-
951 auto const ret =
-
952 flowCross(psbFlow, psbCancelFlow, takerAmount, domainID);
-
953 psbFlow.apply(sb);
-
954 psbCancelFlow.apply(sbCancel);
-
955 return ret;
-
956 }
-
957
-
958 Sandbox sbTaker{&sb};
-
959 Sandbox sbCancelTaker{&sbCancel};
-
960 auto const ret = takerCross(sbTaker, sbCancelTaker, takerAmount);
-
961 sbTaker.apply(sb);
-
962 sbCancelTaker.apply(sbCancel);
-
963 return ret;
-
964}
-
965
-
966std::string
-
967CreateOffer::format_amount(STAmount const& amount)
-
968{
-
969 std::string txt = amount.getText();
-
970 txt += "/";
-
971 txt += to_string(amount.issue().currency);
-
972 return txt;
-
973}
-
974
-
975void
-
976CreateOffer::preCompute()
-
977{
-
978 cross_type_ = CrossType::IouToIou;
-
979 bool const pays_xrp = ctx_.tx.getFieldAmount(sfTakerPays).native();
-
980 bool const gets_xrp = ctx_.tx.getFieldAmount(sfTakerGets).native();
-
981 if (pays_xrp && !gets_xrp)
-
982 cross_type_ = CrossType::IouToXrp;
-
983 else if (gets_xrp && !pays_xrp)
-
984 cross_type_ = CrossType::XrpToIou;
-
985
-
986 return Transactor::preCompute();
-
987}
+
651 if (!direct_consumed)
+
652 Throw<std::logic_error>(
+
653 "direct crossing: nothing was fully consumed.");
+
654 }
+
655
+
656 return std::make_pair(cross_result, taker.remaining_offer());
+
657}
+
658
+
659// Step through the stream for as long as possible, skipping any offers
+
660// that are from the taker or which cross the taker's threshold.
+
661// Return false if the is no offer in the book, true otherwise.
+
662bool
+
663CreateOffer::step_account(OfferStream& stream, Taker const& taker)
+
664{
+
665 while (stream.step())
+
666 {
+
667 auto const& offer = stream.tip();
+
668
+
669 // This offer at the tip crosses the taker's threshold. We're done.
+
670 if (taker.reject(offer.quality()))
+
671 return true;
+
672
+
673 // This offer at the tip is not from the taker. We're done.
+
674 if (offer.owner() != taker.account())
+
675 return true;
+
676 }
+
677
+
678 // We ran out of offers. Can't advance.
+
679 return false;
+
680}
+
681
+
682std::pair<TER, Amounts>
+
683CreateOffer::flowCross(
+
684 PaymentSandbox& psb,
+
685 PaymentSandbox& psbCancel,
+
686 Amounts const& takerAmount,
+
687 std::optional<uint256> const& domainID)
+
688{
+
689 try
+
690 {
+
691 // If the taker is unfunded before we begin crossing there's nothing
+
692 // to do - just return an error.
+
693 //
+
694 // We check this in preclaim, but when selling XRP charged fees can
+
695 // cause a user's available balance to go to 0 (by causing it to dip
+
696 // below the reserve) so we check this case again.
+
697 STAmount const inStartBalance =
+
698 accountFunds(psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
+
699 if (inStartBalance <= beast::zero)
+
700 {
+
701 // The account balance can't cover even part of the offer.
+
702 JLOG(j_.debug()) << "Not crossing: taker is unfunded.";
+
703 return {tecUNFUNDED_OFFER, takerAmount};
+
704 }
+
705
+
706 // If the gateway has a transfer rate, accommodate that. The
+
707 // gateway takes its cut without any special consent from the
+
708 // offer taker. Set sendMax to allow for the gateway's cut.
+
709 Rate gatewayXferRate{QUALITY_ONE};
+
710 STAmount sendMax = takerAmount.in;
+
711 if (!sendMax.native() && (account_ != sendMax.getIssuer()))
+
712 {
+
713 gatewayXferRate = transferRate(psb, sendMax.getIssuer());
+
714 if (gatewayXferRate.value != QUALITY_ONE)
+
715 {
+
716 sendMax = multiplyRound(
+
717 takerAmount.in,
+
718 gatewayXferRate,
+
719 takerAmount.in.issue(),
+
720 true);
+
721 }
+
722 }
+
723
+
724 // Payment flow code compares quality after the transfer rate is
+
725 // included. Since transfer rate is incorporated compute threshold.
+
726 Quality threshold{takerAmount.out, sendMax};
+
727
+
728 // If we're creating a passive offer adjust the threshold so we only
+
729 // cross offers that have a better quality than this one.
+
730 std::uint32_t const txFlags = ctx_.tx.getFlags();
+
731 if (txFlags & tfPassive)
+
732 ++threshold;
+
733
+
734 // Don't send more than our balance.
+
735 if (sendMax > inStartBalance)
+
736 sendMax = inStartBalance;
+
737
+
738 // Always invoke flow() with the default path. However if neither
+
739 // of the takerAmount currencies are XRP then we cross through an
+
740 // additional path with XRP as the intermediate between two books.
+
741 // This second path we have to build ourselves.
+
742 STPathSet paths;
+
743 if (!takerAmount.in.native() && !takerAmount.out.native())
+
744 {
+
745 STPath path;
+
746 path.emplace_back(std::nullopt, xrpCurrency(), std::nullopt);
+
747 paths.emplace_back(std::move(path));
+
748 }
+
749 // Special handling for the tfSell flag.
+
750 STAmount deliver = takerAmount.out;
+
751 OfferCrossing offerCrossing = OfferCrossing::yes;
+
752 if (txFlags & tfSell)
+
753 {
+
754 offerCrossing = OfferCrossing::sell;
+
755 // We are selling, so we will accept *more* than the offer
+
756 // specified. Since we don't know how much they might offer,
+
757 // we allow delivery of the largest possible amount.
+
758 if (deliver.native())
+
759 deliver = STAmount{STAmount::cMaxNative};
+
760 else
+
761 // We can't use the maximum possible currency here because
+
762 // there might be a gateway transfer rate to account for.
+
763 // Since the transfer rate cannot exceed 200%, we use 1/2
+
764 // maxValue for our limit.
+
765 deliver = STAmount{
+
766 takerAmount.out.issue(),
+
767 STAmount::cMaxValue / 2,
+
768 STAmount::cMaxOffset};
+
769 }
+
770
+
771 // Call the payment engine's flow() to do the actual work.
+
772 auto const result = flow(
+
773 psb,
+
774 deliver,
+
775 account_,
+
776 account_,
+
777 paths,
+
778 true, // default path
+
779 !(txFlags & tfFillOrKill), // partial payment
+
780 true, // owner pays transfer fee
+
781 offerCrossing,
+
782 threshold,
+
783 sendMax,
+
784 domainID,
+
785 j_);
+
786
+
787 // If stale offers were found remove them.
+
788 for (auto const& toRemove : result.removableOffers)
+
789 {
+
790 if (auto otr = psb.peek(keylet::offer(toRemove)))
+
791 offerDelete(psb, otr, j_);
+
792 if (auto otr = psbCancel.peek(keylet::offer(toRemove)))
+
793 offerDelete(psbCancel, otr, j_);
+
794 }
+
795
+
796 // Determine the size of the final offer after crossing.
+
797 auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged
+
798 if (isTesSuccess(result.result()))
+
799 {
+
800 STAmount const takerInBalance = accountFunds(
+
801 psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);
+
802
+
803 if (takerInBalance <= beast::zero)
+
804 {
+
805 // If offer crossing exhausted the account's funds don't
+
806 // create the offer.
+
807 afterCross.in.clear();
+
808 afterCross.out.clear();
+
809 }
+
810 else
+
811 {
+
812 STAmount const rate{
+
813 Quality{takerAmount.out, takerAmount.in}.rate()};
+
814
+
815 if (txFlags & tfSell)
+
816 {
+
817 // If selling then scale the new out amount based on how
+
818 // much we sold during crossing. This preserves the offer
+
819 // Quality,
+
820
+
821 // Reduce the offer that is placed by the crossed amount.
+
822 // Note that we must ignore the portion of the
+
823 // actualAmountIn that may have been consumed by a
+
824 // gateway's transfer rate.
+
825 STAmount nonGatewayAmountIn = result.actualAmountIn;
+
826 if (gatewayXferRate.value != QUALITY_ONE)
+
827 nonGatewayAmountIn = divideRound(
+
828 result.actualAmountIn,
+
829 gatewayXferRate,
+
830 takerAmount.in.issue(),
+
831 true);
+
832
+
833 afterCross.in -= nonGatewayAmountIn;
+
834
+
835 // It's possible that the divRound will cause our subtract
+
836 // to go slightly negative. So limit afterCross.in to zero.
+
837 if (afterCross.in < beast::zero)
+
838 // We should verify that the difference *is* small, but
+
839 // what is a good threshold to check?
+
840 afterCross.in.clear();
+
841
+
842 afterCross.out = [&]() {
+
843 // Careful analysis showed that rounding up this
+
844 // divRound result could lead to placing a reduced
+
845 // offer in the ledger that blocks order books. So
+
846 // the fixReducedOffersV1 amendment changes the
+
847 // behavior to round down instead.
+
848 if (psb.rules().enabled(fixReducedOffersV1))
+
849 return divRoundStrict(
+
850 afterCross.in,
+
851 rate,
+
852 takerAmount.out.issue(),
+
853 false);
+
854
+
855 return divRound(
+
856 afterCross.in, rate, takerAmount.out.issue(), true);
+
857 }();
+
858 }
+
859 else
+
860 {
+
861 // If not selling, we scale the input based on the
+
862 // remaining output. This too preserves the offer
+
863 // Quality.
+
864 afterCross.out -= result.actualAmountOut;
+
865 XRPL_ASSERT(
+
866 afterCross.out >= beast::zero,
+
867 "ripple::CreateOffer::flowCross : minimum offer");
+
868 if (afterCross.out < beast::zero)
+
869 afterCross.out.clear();
+
870 afterCross.in = mulRound(
+
871 afterCross.out, rate, takerAmount.in.issue(), true);
+
872 }
+
873 }
+
874 }
+
875
+
876 // Return how much of the offer is left.
+
877 return {tesSUCCESS, afterCross};
+
878 }
+
879 catch (std::exception const& e)
+
880 {
+
881 JLOG(j_.error()) << "Exception during offer crossing: " << e.what();
+
882 }
+
883 return {tecINTERNAL, takerAmount};
+
884}
+
885
+
886std::pair<TER, Amounts>
+
887CreateOffer::cross(
+
888 Sandbox& sb,
+
889 Sandbox& sbCancel,
+
890 Amounts const& takerAmount,
+
891 std::optional<uint256> const& domainID)
+
892{
+
893 PaymentSandbox psbFlow{&sb};
+
894 PaymentSandbox psbCancelFlow{&sbCancel};
+
895 auto const ret = flowCross(psbFlow, psbCancelFlow, takerAmount, domainID);
+
896 psbFlow.apply(sb);
+
897 psbCancelFlow.apply(sbCancel);
+
898 return ret;
+
899}
+
900
+
901std::string
+
902CreateOffer::format_amount(STAmount const& amount)
+
903{
+
904 std::string txt = amount.getText();
+
905 txt += "/";
+
906 txt += to_string(amount.issue().currency);
+
907 return txt;
+
908}
+
909
+
910void
+
911CreateOffer::preCompute()
+
912{
+
913 cross_type_ = CrossType::IouToIou;
+
914 bool const pays_xrp = ctx_.tx.getFieldAmount(sfTakerPays).native();
+
915 bool const gets_xrp = ctx_.tx.getFieldAmount(sfTakerGets).native();
+
916 if (pays_xrp && !gets_xrp)
+
917 cross_type_ = CrossType::IouToXrp;
+
918 else if (gets_xrp && !pays_xrp)
+
919 cross_type_ = CrossType::XrpToIou;
+
920
+
921 return Transactor::preCompute();
+
922}
+
923
+
924TER
+
925CreateOffer::applyHybrid(
+
926 Sandbox& sb,
+
927 std::shared_ptr<STLedgerEntry> sleOffer,
+
928 Keylet const& offerKey,
+
929 STAmount const& saTakerPays,
+
930 STAmount const& saTakerGets,
+
931 std::function<void(SLE::ref, std::optional<uint256>)> const& setDir)
+
932{
+
933 if (!sleOffer->isFieldPresent(sfDomainID))
+
934 return tecINTERNAL; // LCOV_EXCL_LINE
+
935
+
936 // set hybrid flag
+
937 sleOffer->setFlag(lsfHybrid);
+
938
+
939 // if offer is hybrid, need to also place into open offer dir
+
940 Book const book{saTakerPays.issue(), saTakerGets.issue(), std::nullopt};
+
941
+
942 auto dir =
+
943 keylet::quality(keylet::book(book), getRate(saTakerGets, saTakerPays));
+
944 bool const bookExists = sb.exists(dir);
+
945
+
946 auto const bookNode = sb.dirAppend(dir, offerKey, [&](SLE::ref sle) {
+
947 // don't set domainID on the directory object since this directory is
+
948 // for open book
+
949 setDir(sle, std::nullopt);
+
950 });
+
951
+
952 if (!bookNode)
+
953 {
+
954 JLOG(j_.debug())
+
955 << "final result: failed to add hybrid offer to open book";
+
956 return tecDIR_FULL; // LCOV_EXCL_LINE
+
957 }
+
958
+
959 STArray bookArr(sfAdditionalBooks, 1);
+
960 auto bookInfo = STObject::makeInnerObject(sfBook);
+
961 bookInfo.setFieldH256(sfBookDirectory, dir.key);
+
962 bookInfo.setFieldU64(sfBookNode, *bookNode);
+
963 bookArr.push_back(std::move(bookInfo));
+
964
+
965 if (!bookExists)
+
966 ctx_.app.getOrderBookDB().addOrderBook(book);
+
967
+
968 sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
+
969 return tesSUCCESS;
+
970}
+
971
+
972std::pair<TER, bool>
+
973CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel)
+
974{
+
975 using beast::zero;
+
976
+
977 std::uint32_t const uTxFlags = ctx_.tx.getFlags();
+
978
+
979 bool const bPassive(uTxFlags & tfPassive);
+
980 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
+
981 bool const bFillOrKill(uTxFlags & tfFillOrKill);
+
982 bool const bSell(uTxFlags & tfSell);
+
983 bool const bHybrid(uTxFlags & tfHybrid);
+
984
+
985 auto saTakerPays = ctx_.tx[sfTakerPays];
+
986 auto saTakerGets = ctx_.tx[sfTakerGets];
+
987 auto const domainID = ctx_.tx[~sfDomainID];
988
-
989TER
-
990CreateOffer::applyHybrid(
-
991 Sandbox& sb,
-
992 std::shared_ptr<STLedgerEntry> sleOffer,
-
993 Keylet const& offerKey,
-
994 STAmount const& saTakerPays,
-
995 STAmount const& saTakerGets,
-
996 std::function<void(SLE::ref, std::optional<uint256>)> const& setDir)
-
997{
-
998 if (!sleOffer->isFieldPresent(sfDomainID))
-
999 return tecINTERNAL; // LCOV_EXCL_LINE
-
1000
-
1001 // set hybrid flag
-
1002 sleOffer->setFlag(lsfHybrid);
+
989 auto const cancelSequence = ctx_.tx[~sfOfferSequence];
+
990
+
991 // Note that we we use the value from the sequence or ticket as the
+
992 // offer sequence. For more explanation see comments in SeqProxy.h.
+
993 auto const offerSequence = ctx_.tx.getSeqValue();
+
994
+
995 // This is the original rate of the offer, and is the rate at which
+
996 // it will be placed, even if crossing offers change the amounts that
+
997 // end up on the books.
+
998 auto uRate = getRate(saTakerGets, saTakerPays);
+
999
+
1000 auto viewJ = ctx_.app.journal("View");
+
1001
+
1002 TER result = tesSUCCESS;
1003
-
1004 // if offer is hybrid, need to also place into open offer dir
-
1005 Book const book{saTakerPays.issue(), saTakerGets.issue(), std::nullopt};
-
1006
-
1007 auto dir =
-
1008 keylet::quality(keylet::book(book), getRate(saTakerGets, saTakerPays));
-
1009 bool const bookExists = sb.exists(dir);
-
1010
-
1011 auto const bookNode = sb.dirAppend(dir, offerKey, [&](SLE::ref sle) {
-
1012 // don't set domainID on the directory object since this directory is
-
1013 // for open book
-
1014 setDir(sle, std::nullopt);
-
1015 });
-
1016
-
1017 if (!bookNode)
-
1018 {
-
1019 JLOG(j_.debug())
-
1020 << "final result: failed to add hybrid offer to open book";
-
1021 return tecDIR_FULL; // LCOV_EXCL_LINE
-
1022 }
-
1023
-
1024 STArray bookArr(sfAdditionalBooks, 1);
-
1025 auto bookInfo = STObject::makeInnerObject(sfBook);
-
1026 bookInfo.setFieldH256(sfBookDirectory, dir.key);
-
1027 bookInfo.setFieldU64(sfBookNode, *bookNode);
-
1028 bookArr.push_back(std::move(bookInfo));
-
1029
-
1030 if (!bookExists)
-
1031 ctx_.app.getOrderBookDB().addOrderBook(book);
-
1032
-
1033 sleOffer->setFieldArray(sfAdditionalBooks, bookArr);
-
1034 return tesSUCCESS;
-
1035}
-
1036
-
1037std::pair<TER, bool>
-
1038CreateOffer::applyGuts(Sandbox& sb, Sandbox& sbCancel)
-
1039{
-
1040 using beast::zero;
-
1041
-
1042 std::uint32_t const uTxFlags = ctx_.tx.getFlags();
+
1004 // Process a cancellation request that's passed along with an offer.
+
1005 if (cancelSequence)
+
1006 {
+
1007 auto const sleCancel =
+
1008 sb.peek(keylet::offer(account_, *cancelSequence));
+
1009
+
1010 // It's not an error to not find the offer to cancel: it might have
+
1011 // been consumed or removed. If it is found, however, it's an error
+
1012 // to fail to delete it.
+
1013 if (sleCancel)
+
1014 {
+
1015 JLOG(j_.debug()) << "Create cancels order " << *cancelSequence;
+
1016 result = offerDelete(sb, sleCancel, viewJ);
+
1017 }
+
1018 }
+
1019
+
1020 auto const expiration = ctx_.tx[~sfExpiration];
+
1021
+
1022 if (hasExpired(sb, expiration))
+
1023 {
+
1024 // If the offer has expired, the transaction has successfully
+
1025 // done nothing, so short circuit from here.
+
1026 //
+
1027 // The return code change is attached to featureDepositPreauth as a
+
1028 // convenience. The change is not big enough to deserve a fix code.
+
1029 TER const ter{
+
1030 sb.rules().enabled(featureDepositPreauth) ? TER{tecEXPIRED}
+
1031 : TER{tesSUCCESS}};
+
1032 return {ter, true};
+
1033 }
+
1034
+
1035 bool const bOpenLedger = sb.open();
+
1036 bool crossed = false;
+
1037
+
1038 if (result == tesSUCCESS)
+
1039 {
+
1040 // If a tick size applies, round the offer to the tick size
+
1041 auto const& uPaysIssuerID = saTakerPays.getIssuer();
+
1042 auto const& uGetsIssuerID = saTakerGets.getIssuer();
1043
-
1044 bool const bPassive(uTxFlags & tfPassive);
-
1045 bool const bImmediateOrCancel(uTxFlags & tfImmediateOrCancel);
-
1046 bool const bFillOrKill(uTxFlags & tfFillOrKill);
-
1047 bool const bSell(uTxFlags & tfSell);
-
1048 bool const bHybrid(uTxFlags & tfHybrid);
-
1049
-
1050 auto saTakerPays = ctx_.tx[sfTakerPays];
-
1051 auto saTakerGets = ctx_.tx[sfTakerGets];
-
1052 auto const domainID = ctx_.tx[~sfDomainID];
-
1053
-
1054 auto const cancelSequence = ctx_.tx[~sfOfferSequence];
-
1055
-
1056 // Note that we we use the value from the sequence or ticket as the
-
1057 // offer sequence. For more explanation see comments in SeqProxy.h.
-
1058 auto const offerSequence = ctx_.tx.getSeqValue();
-
1059
-
1060 // This is the original rate of the offer, and is the rate at which
-
1061 // it will be placed, even if crossing offers change the amounts that
-
1062 // end up on the books.
-
1063 auto uRate = getRate(saTakerGets, saTakerPays);
-
1064
-
1065 auto viewJ = ctx_.app.journal("View");
-
1066
-
1067 TER result = tesSUCCESS;
-
1068
-
1069 // Process a cancellation request that's passed along with an offer.
-
1070 if (cancelSequence)
-
1071 {
-
1072 auto const sleCancel =
-
1073 sb.peek(keylet::offer(account_, *cancelSequence));
-
1074
-
1075 // It's not an error to not find the offer to cancel: it might have
-
1076 // been consumed or removed. If it is found, however, it's an error
-
1077 // to fail to delete it.
-
1078 if (sleCancel)
-
1079 {
-
1080 JLOG(j_.debug()) << "Create cancels order " << *cancelSequence;
-
1081 result = offerDelete(sb, sleCancel, viewJ);
+
1044 std::uint8_t uTickSize = Quality::maxTickSize;
+
1045 if (!isXRP(uPaysIssuerID))
+
1046 {
+
1047 auto const sle = sb.read(keylet::account(uPaysIssuerID));
+
1048 if (sle && sle->isFieldPresent(sfTickSize))
+
1049 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
+
1050 }
+
1051 if (!isXRP(uGetsIssuerID))
+
1052 {
+
1053 auto const sle = sb.read(keylet::account(uGetsIssuerID));
+
1054 if (sle && sle->isFieldPresent(sfTickSize))
+
1055 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
+
1056 }
+
1057 if (uTickSize < Quality::maxTickSize)
+
1058 {
+
1059 auto const rate =
+
1060 Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
+
1061
+
1062 // We round the side that's not exact,
+
1063 // just as if the offer happened to execute
+
1064 // at a slightly better (for the placer) rate
+
1065 if (bSell)
+
1066 {
+
1067 // this is a sell, round taker pays
+
1068 saTakerPays = multiply(saTakerGets, rate, saTakerPays.issue());
+
1069 }
+
1070 else
+
1071 {
+
1072 // this is a buy, round taker gets
+
1073 saTakerGets = divide(saTakerPays, rate, saTakerGets.issue());
+
1074 }
+
1075 if (!saTakerGets || !saTakerPays)
+
1076 {
+
1077 JLOG(j_.debug()) << "Offer rounded to zero";
+
1078 return {result, true};
+
1079 }
+
1080
+
1081 uRate = getRate(saTakerGets, saTakerPays);
1082 }
-
1083 }
-
1084
-
1085 auto const expiration = ctx_.tx[~sfExpiration];
+
1083
+
1084 // We reverse pays and gets because during crossing we are taking.
+
1085 Amounts const takerAmount(saTakerGets, saTakerPays);
1086
-
1087 if (hasExpired(sb, expiration))
-
1088 {
-
1089 // If the offer has expired, the transaction has successfully
-
1090 // done nothing, so short circuit from here.
-
1091 //
-
1092 // The return code change is attached to featureDepositPreauth as a
-
1093 // convenience. The change is not big enough to deserve a fix code.
-
1094 TER const ter{
-
1095 sb.rules().enabled(featureDepositPreauth) ? TER{tecEXPIRED}
-
1096 : TER{tesSUCCESS}};
-
1097 return {ter, true};
-
1098 }
-
1099
-
1100 bool const bOpenLedger = sb.open();
-
1101 bool crossed = false;
-
1102
-
1103 if (result == tesSUCCESS)
-
1104 {
-
1105 // If a tick size applies, round the offer to the tick size
-
1106 auto const& uPaysIssuerID = saTakerPays.getIssuer();
-
1107 auto const& uGetsIssuerID = saTakerGets.getIssuer();
-
1108
-
1109 std::uint8_t uTickSize = Quality::maxTickSize;
-
1110 if (!isXRP(uPaysIssuerID))
-
1111 {
-
1112 auto const sle = sb.read(keylet::account(uPaysIssuerID));
-
1113 if (sle && sle->isFieldPresent(sfTickSize))
-
1114 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
-
1115 }
-
1116 if (!isXRP(uGetsIssuerID))
-
1117 {
-
1118 auto const sle = sb.read(keylet::account(uGetsIssuerID));
-
1119 if (sle && sle->isFieldPresent(sfTickSize))
-
1120 uTickSize = std::min(uTickSize, (*sle)[sfTickSize]);
-
1121 }
-
1122 if (uTickSize < Quality::maxTickSize)
-
1123 {
-
1124 auto const rate =
-
1125 Quality{saTakerGets, saTakerPays}.round(uTickSize).rate();
-
1126
-
1127 // We round the side that's not exact,
-
1128 // just as if the offer happened to execute
-
1129 // at a slightly better (for the placer) rate
-
1130 if (bSell)
-
1131 {
-
1132 // this is a sell, round taker pays
-
1133 saTakerPays = multiply(saTakerGets, rate, saTakerPays.issue());
-
1134 }
-
1135 else
-
1136 {
-
1137 // this is a buy, round taker gets
-
1138 saTakerGets = divide(saTakerPays, rate, saTakerGets.issue());
-
1139 }
-
1140 if (!saTakerGets || !saTakerPays)
-
1141 {
-
1142 JLOG(j_.debug()) << "Offer rounded to zero";
-
1143 return {result, true};
-
1144 }
-
1145
-
1146 uRate = getRate(saTakerGets, saTakerPays);
-
1147 }
-
1148
-
1149 // We reverse pays and gets because during crossing we are taking.
-
1150 Amounts const takerAmount(saTakerGets, saTakerPays);
-
1151
-
1152 // The amount of the offer that is unfilled after crossing has been
-
1153 // performed. It may be equal to the original amount (didn't cross),
-
1154 // empty (fully crossed), or something in-between.
-
1155 Amounts place_offer;
-
1156
-
1157 JLOG(j_.debug()) << "Attempting cross: "
-
1158 << to_string(takerAmount.in.issue()) << " -> "
-
1159 << to_string(takerAmount.out.issue());
-
1160
-
1161 if (auto stream = j_.trace())
-
1162 {
-
1163 stream << " mode: " << (bPassive ? "passive " : "")
-
1164 << (bSell ? "sell" : "buy");
-
1165 stream << " in: " << format_amount(takerAmount.in);
-
1166 stream << " out: " << format_amount(takerAmount.out);
-
1167 }
-
1168
-
1169 std::tie(result, place_offer) =
-
1170 cross(sb, sbCancel, takerAmount, domainID);
-
1171
-
1172 // We expect the implementation of cross to succeed
-
1173 // or give a tec.
-
1174 XRPL_ASSERT(
-
1175 result == tesSUCCESS || isTecClaim(result),
-
1176 "ripple::CreateOffer::applyGuts : result is tesSUCCESS or "
-
1177 "tecCLAIM");
-
1178
-
1179 if (auto stream = j_.trace())
-
1180 {
-
1181 stream << "Cross result: " << transToken(result);
-
1182 stream << " in: " << format_amount(place_offer.in);
-
1183 stream << " out: " << format_amount(place_offer.out);
-
1184 }
-
1185
-
1186 if (result == tecFAILED_PROCESSING && bOpenLedger)
-
1187 result = telFAILED_PROCESSING;
-
1188
-
1189 if (result != tesSUCCESS)
-
1190 {
-
1191 JLOG(j_.debug()) << "final result: " << transToken(result);
-
1192 return {result, true};
-
1193 }
-
1194
-
1195 XRPL_ASSERT(
-
1196 saTakerGets.issue() == place_offer.in.issue(),
-
1197 "ripple::CreateOffer::applyGuts : taker gets issue match");
-
1198 XRPL_ASSERT(
-
1199 saTakerPays.issue() == place_offer.out.issue(),
-
1200 "ripple::CreateOffer::applyGuts : taker pays issue match");
-
1201
-
1202 if (takerAmount != place_offer)
-
1203 crossed = true;
-
1204
-
1205 // The offer that we need to place after offer crossing should
-
1206 // never be negative. If it is, something went very very wrong.
-
1207 if (place_offer.in < zero || place_offer.out < zero)
-
1208 {
-
1209 JLOG(j_.fatal()) << "Cross left offer negative!"
-
1210 << " in: " << format_amount(place_offer.in)
-
1211 << " out: " << format_amount(place_offer.out);
-
1212 return {tefINTERNAL, true};
-
1213 }
-
1214
-
1215 if (place_offer.in == zero || place_offer.out == zero)
-
1216 {
-
1217 JLOG(j_.debug()) << "Offer fully crossed!";
-
1218 return {result, true};
-
1219 }
-
1220
-
1221 // We now need to adjust the offer to reflect the amount left after
-
1222 // crossing. We reverse in and out here, since during crossing we
-
1223 // were the taker.
-
1224 saTakerPays = place_offer.out;
-
1225 saTakerGets = place_offer.in;
-
1226 }
-
1227
-
1228 XRPL_ASSERT(
-
1229 saTakerPays > zero && saTakerGets > zero,
-
1230 "ripple::CreateOffer::applyGuts : taker pays and gets positive");
+
1087 // The amount of the offer that is unfilled after crossing has been
+
1088 // performed. It may be equal to the original amount (didn't cross),
+
1089 // empty (fully crossed), or something in-between.
+
1090 Amounts place_offer;
+
1091
+
1092 JLOG(j_.debug()) << "Attempting cross: "
+
1093 << to_string(takerAmount.in.issue()) << " -> "
+
1094 << to_string(takerAmount.out.issue());
+
1095
+
1096 if (auto stream = j_.trace())
+
1097 {
+
1098 stream << " mode: " << (bPassive ? "passive " : "")
+
1099 << (bSell ? "sell" : "buy");
+
1100 stream << " in: " << format_amount(takerAmount.in);
+
1101 stream << " out: " << format_amount(takerAmount.out);
+
1102 }
+
1103
+
1104 std::tie(result, place_offer) =
+
1105 cross(sb, sbCancel, takerAmount, domainID);
+
1106
+
1107 // We expect the implementation of cross to succeed
+
1108 // or give a tec.
+
1109 XRPL_ASSERT(
+
1110 result == tesSUCCESS || isTecClaim(result),
+
1111 "ripple::CreateOffer::applyGuts : result is tesSUCCESS or "
+
1112 "tecCLAIM");
+
1113
+
1114 if (auto stream = j_.trace())
+
1115 {
+
1116 stream << "Cross result: " << transToken(result);
+
1117 stream << " in: " << format_amount(place_offer.in);
+
1118 stream << " out: " << format_amount(place_offer.out);
+
1119 }
+
1120
+
1121 if (result == tecFAILED_PROCESSING && bOpenLedger)
+
1122 result = telFAILED_PROCESSING;
+
1123
+
1124 if (result != tesSUCCESS)
+
1125 {
+
1126 JLOG(j_.debug()) << "final result: " << transToken(result);
+
1127 return {result, true};
+
1128 }
+
1129
+
1130 XRPL_ASSERT(
+
1131 saTakerGets.issue() == place_offer.in.issue(),
+
1132 "ripple::CreateOffer::applyGuts : taker gets issue match");
+
1133 XRPL_ASSERT(
+
1134 saTakerPays.issue() == place_offer.out.issue(),
+
1135 "ripple::CreateOffer::applyGuts : taker pays issue match");
+
1136
+
1137 if (takerAmount != place_offer)
+
1138 crossed = true;
+
1139
+
1140 // The offer that we need to place after offer crossing should
+
1141 // never be negative. If it is, something went very very wrong.
+
1142 if (place_offer.in < zero || place_offer.out < zero)
+
1143 {
+
1144 JLOG(j_.fatal()) << "Cross left offer negative!"
+
1145 << " in: " << format_amount(place_offer.in)
+
1146 << " out: " << format_amount(place_offer.out);
+
1147 return {tefINTERNAL, true};
+
1148 }
+
1149
+
1150 if (place_offer.in == zero || place_offer.out == zero)
+
1151 {
+
1152 JLOG(j_.debug()) << "Offer fully crossed!";
+
1153 return {result, true};
+
1154 }
+
1155
+
1156 // We now need to adjust the offer to reflect the amount left after
+
1157 // crossing. We reverse in and out here, since during crossing we
+
1158 // were the taker.
+
1159 saTakerPays = place_offer.out;
+
1160 saTakerGets = place_offer.in;
+
1161 }
+
1162
+
1163 XRPL_ASSERT(
+
1164 saTakerPays > zero && saTakerGets > zero,
+
1165 "ripple::CreateOffer::applyGuts : taker pays and gets positive");
+
1166
+
1167 if (result != tesSUCCESS)
+
1168 {
+
1169 JLOG(j_.debug()) << "final result: " << transToken(result);
+
1170 return {result, true};
+
1171 }
+
1172
+
1173 if (auto stream = j_.trace())
+
1174 {
+
1175 stream << "Place" << (crossed ? " remaining " : " ") << "offer:";
+
1176 stream << " Pays: " << saTakerPays.getFullText();
+
1177 stream << " Gets: " << saTakerGets.getFullText();
+
1178 }
+
1179
+
1180 // For 'fill or kill' offers, failure to fully cross means that the
+
1181 // entire operation should be aborted, with only fees paid.
+
1182 if (bFillOrKill)
+
1183 {
+
1184 JLOG(j_.trace()) << "Fill or Kill: offer killed";
+
1185 if (sb.rules().enabled(fix1578))
+
1186 return {tecKILLED, false};
+
1187 return {tesSUCCESS, false};
+
1188 }
+
1189
+
1190 // For 'immediate or cancel' offers, the amount remaining doesn't get
+
1191 // placed - it gets canceled and the operation succeeds.
+
1192 if (bImmediateOrCancel)
+
1193 {
+
1194 JLOG(j_.trace()) << "Immediate or cancel: offer canceled";
+
1195 if (!crossed && sb.rules().enabled(featureImmediateOfferKilled))
+
1196 // If the ImmediateOfferKilled amendment is enabled, any
+
1197 // ImmediateOrCancel offer that transfers absolutely no funds
+
1198 // returns tecKILLED rather than tesSUCCESS. Motivation for the
+
1199 // change is here: https://github.com/ripple/rippled/issues/4115
+
1200 return {tecKILLED, false};
+
1201 return {tesSUCCESS, true};
+
1202 }
+
1203
+
1204 auto const sleCreator = sb.peek(keylet::account(account_));
+
1205 if (!sleCreator)
+
1206 return {tefINTERNAL, false};
+
1207
+
1208 {
+
1209 XRPAmount reserve =
+
1210 sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
+
1211
+
1212 if (mPriorBalance < reserve)
+
1213 {
+
1214 // If we are here, the signing account had an insufficient reserve
+
1215 // *prior* to our processing. If something actually crossed, then
+
1216 // we allow this; otherwise, we just claim a fee.
+
1217 if (!crossed)
+
1218 result = tecINSUF_RESERVE_OFFER;
+
1219
+
1220 if (result != tesSUCCESS)
+
1221 {
+
1222 JLOG(j_.debug()) << "final result: " << transToken(result);
+
1223 }
+
1224
+
1225 return {result, true};
+
1226 }
+
1227 }
+
1228
+
1229 // We need to place the remainder of the offer into its order book.
+
1230 auto const offer_index = keylet::offer(account_, offerSequence);
1231
-
1232 if (result != tesSUCCESS)
-
1233 {
-
1234 JLOG(j_.debug()) << "final result: " << transToken(result);
-
1235 return {result, true};
-
1236 }
-
1237
-
1238 if (auto stream = j_.trace())
-
1239 {
-
1240 stream << "Place" << (crossed ? " remaining " : " ") << "offer:";
-
1241 stream << " Pays: " << saTakerPays.getFullText();
-
1242 stream << " Gets: " << saTakerGets.getFullText();
-
1243 }
-
1244
-
1245 // For 'fill or kill' offers, failure to fully cross means that the
-
1246 // entire operation should be aborted, with only fees paid.
-
1247 if (bFillOrKill)
-
1248 {
-
1249 JLOG(j_.trace()) << "Fill or Kill: offer killed";
-
1250 if (sb.rules().enabled(fix1578))
-
1251 return {tecKILLED, false};
-
1252 return {tesSUCCESS, false};
-
1253 }
-
1254
-
1255 // For 'immediate or cancel' offers, the amount remaining doesn't get
-
1256 // placed - it gets canceled and the operation succeeds.
-
1257 if (bImmediateOrCancel)
-
1258 {
-
1259 JLOG(j_.trace()) << "Immediate or cancel: offer canceled";
-
1260 if (!crossed && sb.rules().enabled(featureImmediateOfferKilled))
-
1261 // If the ImmediateOfferKilled amendment is enabled, any
-
1262 // ImmediateOrCancel offer that transfers absolutely no funds
-
1263 // returns tecKILLED rather than tesSUCCESS. Motivation for the
-
1264 // change is here: https://github.com/ripple/rippled/issues/4115
-
1265 return {tecKILLED, false};
-
1266 return {tesSUCCESS, true};
-
1267 }
-
1268
-
1269 auto const sleCreator = sb.peek(keylet::account(account_));
-
1270 if (!sleCreator)
-
1271 return {tefINTERNAL, false};
-
1272
-
1273 {
-
1274 XRPAmount reserve =
-
1275 sb.fees().accountReserve(sleCreator->getFieldU32(sfOwnerCount) + 1);
+
1232 // Add offer to owner's directory.
+
1233 auto const ownerNode = sb.dirInsert(
+
1234 keylet::ownerDir(account_), offer_index, describeOwnerDir(account_));
+
1235
+
1236 if (!ownerNode)
+
1237 {
+
1238 JLOG(j_.debug())
+
1239 << "final result: failed to add offer to owner's directory";
+
1240 return {tecDIR_FULL, true};
+
1241 }
+
1242
+
1243 // Update owner count.
+
1244 adjustOwnerCount(sb, sleCreator, 1, viewJ);
+
1245
+
1246 JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue())
+
1247 << " : " << to_string(saTakerGets.issue())
+
1248 << (domainID ? (" : " + to_string(*domainID)) : "");
+
1249
+
1250 Book const book{saTakerPays.issue(), saTakerGets.issue(), domainID};
+
1251
+
1252 // Add offer to order book, using the original rate
+
1253 // before any crossing occured.
+
1254 //
+
1255 // Regular offer - BookDirectory points to open directory
+
1256 //
+
1257 // Domain offer (w/o hyrbid) - BookDirectory points to domain
+
1258 // directory
+
1259 //
+
1260 // Hybrid domain offer - BookDirectory points to domain directory,
+
1261 // and AdditionalBooks field stores one entry that points to the open
+
1262 // directory
+
1263 auto dir = keylet::quality(keylet::book(book), uRate);
+
1264 bool const bookExisted = static_cast<bool>(sb.peek(dir));
+
1265
+
1266 auto setBookDir = [&](SLE::ref sle,
+
1267 std::optional<uint256> const& maybeDomain) {
+
1268 sle->setFieldH160(sfTakerPaysCurrency, saTakerPays.issue().currency);
+
1269 sle->setFieldH160(sfTakerPaysIssuer, saTakerPays.issue().account);
+
1270 sle->setFieldH160(sfTakerGetsCurrency, saTakerGets.issue().currency);
+
1271 sle->setFieldH160(sfTakerGetsIssuer, saTakerGets.issue().account);
+
1272 sle->setFieldU64(sfExchangeRate, uRate);
+
1273 if (maybeDomain)
+
1274 sle->setFieldH256(sfDomainID, *maybeDomain);
+
1275 };
1276
-
1277 if (mPriorBalance < reserve)
-
1278 {
-
1279 // If we are here, the signing account had an insufficient reserve
-
1280 // *prior* to our processing. If something actually crossed, then
-
1281 // we allow this; otherwise, we just claim a fee.
-
1282 if (!crossed)
-
1283 result = tecINSUF_RESERVE_OFFER;
-
1284
-
1285 if (result != tesSUCCESS)
-
1286 {
-
1287 JLOG(j_.debug()) << "final result: " << transToken(result);
-
1288 }
-
1289
-
1290 return {result, true};
-
1291 }
-
1292 }
-
1293
-
1294 // We need to place the remainder of the offer into its order book.
-
1295 auto const offer_index = keylet::offer(account_, offerSequence);
-
1296
-
1297 // Add offer to owner's directory.
-
1298 auto const ownerNode = sb.dirInsert(
-
1299 keylet::ownerDir(account_), offer_index, describeOwnerDir(account_));
-
1300
-
1301 if (!ownerNode)
-
1302 {
-
1303 JLOG(j_.debug())
-
1304 << "final result: failed to add offer to owner's directory";
-
1305 return {tecDIR_FULL, true};
-
1306 }
-
1307
-
1308 // Update owner count.
-
1309 adjustOwnerCount(sb, sleCreator, 1, viewJ);
-
1310
-
1311 JLOG(j_.trace()) << "adding to book: " << to_string(saTakerPays.issue())
-
1312 << " : " << to_string(saTakerGets.issue())
-
1313 << (domainID ? (" : " + to_string(*domainID)) : "");
-
1314
-
1315 Book const book{saTakerPays.issue(), saTakerGets.issue(), domainID};
-
1316
-
1317 // Add offer to order book, using the original rate
-
1318 // before any crossing occured.
-
1319 //
-
1320 // Regular offer - BookDirectory points to open directory
-
1321 //
-
1322 // Domain offer (w/o hyrbid) - BookDirectory points to domain
-
1323 // directory
-
1324 //
-
1325 // Hybrid domain offer - BookDirectory points to domain directory,
-
1326 // and AdditionalBooks field stores one entry that points to the open
-
1327 // directory
-
1328 auto dir = keylet::quality(keylet::book(book), uRate);
-
1329 bool const bookExisted = static_cast<bool>(sb.peek(dir));
+
1277 auto const bookNode = sb.dirAppend(dir, offer_index, [&](SLE::ref sle) {
+
1278 // sets domainID on book directory if it's a domain offer
+
1279 setBookDir(sle, domainID);
+
1280 });
+
1281
+
1282 if (!bookNode)
+
1283 {
+
1284 JLOG(j_.debug()) << "final result: failed to add offer to book";
+
1285 return {tecDIR_FULL, true};
+
1286 }
+
1287
+
1288 auto sleOffer = std::make_shared<SLE>(offer_index);
+
1289 sleOffer->setAccountID(sfAccount, account_);
+
1290 sleOffer->setFieldU32(sfSequence, offerSequence);
+
1291 sleOffer->setFieldH256(sfBookDirectory, dir.key);
+
1292 sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
+
1293 sleOffer->setFieldAmount(sfTakerGets, saTakerGets);
+
1294 sleOffer->setFieldU64(sfOwnerNode, *ownerNode);
+
1295 sleOffer->setFieldU64(sfBookNode, *bookNode);
+
1296 if (expiration)
+
1297 sleOffer->setFieldU32(sfExpiration, *expiration);
+
1298 if (bPassive)
+
1299 sleOffer->setFlag(lsfPassive);
+
1300 if (bSell)
+
1301 sleOffer->setFlag(lsfSell);
+
1302 if (domainID)
+
1303 sleOffer->setFieldH256(sfDomainID, *domainID);
+
1304
+
1305 // if it's a hybrid offer, set hybrid flag, and create an open dir
+
1306 if (bHybrid)
+
1307 {
+
1308 auto const res = applyHybrid(
+
1309 sb, sleOffer, offer_index, saTakerPays, saTakerGets, setBookDir);
+
1310 if (res != tesSUCCESS)
+
1311 return {res, true}; // LCOV_EXCL_LINE
+
1312 }
+
1313
+
1314 sb.insert(sleOffer);
+
1315
+
1316 if (!bookExisted)
+
1317 ctx_.app.getOrderBookDB().addOrderBook(book);
+
1318
+
1319 JLOG(j_.debug()) << "final result: success";
+
1320
+
1321 return {tesSUCCESS, true};
+
1322}
+
1323
+
1324TER
+
1325CreateOffer::doApply()
+
1326{
+
1327 // This is the ledger view that we work against. Transactions are applied
+
1328 // as we go on processing transactions.
+
1329 Sandbox sb(&ctx_.view());
1330
-
1331 auto setBookDir = [&](SLE::ref sle,
-
1332 std::optional<uint256> const& maybeDomain) {
-
1333 sle->setFieldH160(sfTakerPaysCurrency, saTakerPays.issue().currency);
-
1334 sle->setFieldH160(sfTakerPaysIssuer, saTakerPays.issue().account);
-
1335 sle->setFieldH160(sfTakerGetsCurrency, saTakerGets.issue().currency);
-
1336 sle->setFieldH160(sfTakerGetsIssuer, saTakerGets.issue().account);
-
1337 sle->setFieldU64(sfExchangeRate, uRate);
-
1338 if (maybeDomain)
-
1339 sle->setFieldH256(sfDomainID, *maybeDomain);
-
1340 };
-
1341
-
1342 auto const bookNode = sb.dirAppend(dir, offer_index, [&](SLE::ref sle) {
-
1343 // sets domainID on book directory if it's a domain offer
-
1344 setBookDir(sle, domainID);
-
1345 });
-
1346
-
1347 if (!bookNode)
-
1348 {
-
1349 JLOG(j_.debug()) << "final result: failed to add offer to book";
-
1350 return {tecDIR_FULL, true};
-
1351 }
-
1352
-
1353 auto sleOffer = std::make_shared<SLE>(offer_index);
-
1354 sleOffer->setAccountID(sfAccount, account_);
-
1355 sleOffer->setFieldU32(sfSequence, offerSequence);
-
1356 sleOffer->setFieldH256(sfBookDirectory, dir.key);
-
1357 sleOffer->setFieldAmount(sfTakerPays, saTakerPays);
-
1358 sleOffer->setFieldAmount(sfTakerGets, saTakerGets);
-
1359 sleOffer->setFieldU64(sfOwnerNode, *ownerNode);
-
1360 sleOffer->setFieldU64(sfBookNode, *bookNode);
-
1361 if (expiration)
-
1362 sleOffer->setFieldU32(sfExpiration, *expiration);
-
1363 if (bPassive)
-
1364 sleOffer->setFlag(lsfPassive);
-
1365 if (bSell)
-
1366 sleOffer->setFlag(lsfSell);
-
1367 if (domainID)
-
1368 sleOffer->setFieldH256(sfDomainID, *domainID);
-
1369
-
1370 // if it's a hybrid offer, set hybrid flag, and create an open dir
-
1371 if (bHybrid)
-
1372 {
-
1373 auto const res = applyHybrid(
-
1374 sb, sleOffer, offer_index, saTakerPays, saTakerGets, setBookDir);
-
1375 if (res != tesSUCCESS)
-
1376 return {res, true}; // LCOV_EXCL_LINE
-
1377 }
-
1378
-
1379 sb.insert(sleOffer);
-
1380
-
1381 if (!bookExisted)
-
1382 ctx_.app.getOrderBookDB().addOrderBook(book);
-
1383
-
1384 JLOG(j_.debug()) << "final result: success";
-
1385
-
1386 return {tesSUCCESS, true};
-
1387}
-
1388
-
1389TER
-
1390CreateOffer::doApply()
-
1391{
-
1392 // This is the ledger view that we work against. Transactions are applied
-
1393 // as we go on processing transactions.
-
1394 Sandbox sb(&ctx_.view());
-
1395
-
1396 // This is a ledger with just the fees paid and any unfunded or expired
-
1397 // offers we encounter removed. It's used when handling Fill-or-Kill offers,
-
1398 // if the order isn't going to be placed, to avoid wasting the work we did.
-
1399 Sandbox sbCancel(&ctx_.view());
-
1400
-
1401 auto const result = applyGuts(sb, sbCancel);
-
1402 if (result.second)
-
1403 sb.apply(ctx_.rawView());
-
1404 else
-
1405 sbCancel.apply(ctx_.rawView());
-
1406 return result.first;
-
1407}
-
1408
-
1409} // namespace ripple
+
1331 // This is a ledger with just the fees paid and any unfunded or expired
+
1332 // offers we encounter removed. It's used when handling Fill-or-Kill offers,
+
1333 // if the order isn't going to be placed, to avoid wasting the work we did.
+
1334 Sandbox sbCancel(&ctx_.view());
+
1335
+
1336 auto const result = applyGuts(sb, sbCancel);
+
1337 if (result.second)
+
1338 sb.apply(ctx_.rawView());
+
1339 else
+
1340 sbCancel.apply(ctx_.rawView());
+
1341 return result.first;
+
1342}
+
1343
+
1344} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
Stream fatal() const
Definition: Journal.h:352
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
Stream trace() const
Severity stream access functions.
Definition: Journal.h:322
-
Wraps a Journal::Sink to prefix its output with a string.
Definition: WrappedSink.h:34
virtual beast::Journal journal(std::string const &name)=0
virtual OrderBookDB & getOrderBookDB()=0
RawView & rawView()
Definition: ApplyContext.h:91
@@ -1508,28 +1442,26 @@ $(function() {
Amounts remaining_offer() const
Returns the amount remaining on the offer.
Definition: Taker.cpp:152
Amounts const & original_offer() const
Returns the amount that the offer was originally placed at.
Definition: Taker.cpp:185
Issue const & issue_out() const
Returns the Issue associated with the output of the offer.
Definition: Taker.h:199
-
bool unfunded() const
Returns true if the taker has run out of funds.
Definition: Taker.cpp:113
Specifies an order book.
Definition: Book.h:36
-
std::pair< TER, Amounts > flowCross(PaymentSandbox &psb, PaymentSandbox &psbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
-
std::pair< TER, Amounts > bridged_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
-
OfferStream::StepCounter stepCounter_
Definition: CreateOffer.h:155
-
static TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
-
void preCompute() override
Gather information beyond what the Transactor base class gathers.
-
bool dry_offer(ApplyView &view, Offer const &offer)
-
static TER preclaim(PreclaimContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
-
static bool step_account(OfferStream &stream, Taker const &taker)
-
static std::string format_amount(STAmount const &amount)
-
bool reachedOfferCrossingLimit(Taker const &taker) const
-
std::pair< TER, Amounts > takerCross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)
-
std::pair< TER, Amounts > direct_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
+
std::pair< TER, Amounts > flowCross(PaymentSandbox &psb, PaymentSandbox &psbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
+
std::pair< TER, Amounts > bridged_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
+
OfferStream::StepCounter stepCounter_
Definition: CreateOffer.h:148
+
static TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
+
void preCompute() override
Gather information beyond what the Transactor base class gathers.
+
bool dry_offer(ApplyView &view, Offer const &offer)
+
static TER preclaim(PreclaimContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
+
static bool step_account(OfferStream &stream, Taker const &taker)
+
static std::string format_amount(STAmount const &amount)
+
bool reachedOfferCrossingLimit(Taker const &taker) const
+
std::pair< TER, Amounts > direct_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
static NotTEC preflight(PreflightContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
Definition: CreateOffer.cpp:47
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition: CreateOffer.cpp:36
-
TER applyHybrid(Sandbox &sb, std::shared_ptr< STLedgerEntry > sleOffer, Keylet const &offer_index, STAmount const &saTakerPays, STAmount const &saTakerGets, std::function< void(SLE::ref, std::optional< uint256 >)> const &setDir)
-
CrossType cross_type_
Definition: CreateOffer.h:152
-
TER doApply() override
Precondition: fee collection is likely.
-
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
-
static std::pair< bool, Quality > select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)
-
std::pair< TER, Amounts > cross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
+
TER applyHybrid(Sandbox &sb, std::shared_ptr< STLedgerEntry > sleOffer, Keylet const &offer_index, STAmount const &saTakerPays, STAmount const &saTakerGets, std::function< void(SLE::ref, std::optional< uint256 >)> const &setDir)
+
CrossType cross_type_
Definition: CreateOffer.h:145
+
TER doApply() override
Precondition: fee collection is likely.
+
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
+
static std::pair< bool, Quality > select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)
+
std::pair< TER, Amounts > cross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
A currency issued by an account.
Definition: Issue.h:33
AccountID account
Definition: Issue.h:36
Currency currency
Definition: Issue.h:35
@@ -1538,7 +1470,6 @@ $(function() {
A wrapper which makes credits unavailable to balances.
A view into a ledger.
Definition: ReadView.h:52
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
-
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
Definition: ReadView.h:112
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:130
Definition: STAmount.h:50
diff --git a/CreateOffer_8h_source.html b/CreateOffer_8h_source.html index 3732d129e9..51df045260 100644 --- a/CreateOffer_8h_source.html +++ b/CreateOffer_8h_source.html @@ -181,84 +181,76 @@ $(function() {
109 bool
110 reachedOfferCrossingLimit(Taker const& taker) const;
111
-
112 // Fill offer as much as possible by consuming offers already on the books,
-
113 // and adjusting account balances accordingly.
-
114 //
-
115 // Charges fees on top to taker.
-
116 std::pair<TER, Amounts>
-
117 takerCross(Sandbox& sb, Sandbox& sbCancel, Amounts const& takerAmount);
-
118
-
119 // Use the payment flow code to perform offer crossing.
-
120 std::pair<TER, Amounts>
-
121 flowCross(
-
122 PaymentSandbox& psb,
-
123 PaymentSandbox& psbCancel,
-
124 Amounts const& takerAmount,
-
125 std::optional<uint256> const& domainID);
-
126
-
127 // Temporary
-
128 // This is a central location that invokes both versions of cross
-
129 // so the results can be compared. Eventually this layer will be
-
130 // removed once flowCross is determined to be stable.
-
131 std::pair<TER, Amounts>
-
132 cross(
-
133 Sandbox& sb,
-
134 Sandbox& sbCancel,
-
135 Amounts const& takerAmount,
-
136 std::optional<uint256> const& domainID);
-
137
-
138 static std::string
-
139 format_amount(STAmount const& amount);
-
140
-
141 TER
-
142 applyHybrid(
-
143 Sandbox& sb,
-
144 std::shared_ptr<STLedgerEntry> sleOffer,
-
145 Keylet const& offer_index,
-
146 STAmount const& saTakerPays,
-
147 STAmount const& saTakerGets,
-
148 std::function<void(SLE::ref, std::optional<uint256>)> const& setDir);
-
149
-
150private:
-
151 // What kind of offer we are placing
-
152 CrossType cross_type_;
-
153
-
154 // The number of steps to take through order books while crossing
-
155 OfferStream::StepCounter stepCounter_;
-
156};
-
157
-
158using OfferCreate = CreateOffer;
-
159
-
160} // namespace ripple
-
161
-
162#endif
+
112 // Use the payment flow code to perform offer crossing.
+
113 std::pair<TER, Amounts>
+
114 flowCross(
+
115 PaymentSandbox& psb,
+
116 PaymentSandbox& psbCancel,
+
117 Amounts const& takerAmount,
+
118 std::optional<uint256> const& domainID);
+
119
+
120 // Temporary
+
121 // This is a central location that invokes both versions of cross
+
122 // so the results can be compared. Eventually this layer will be
+
123 // removed once flowCross is determined to be stable.
+
124 std::pair<TER, Amounts>
+
125 cross(
+
126 Sandbox& sb,
+
127 Sandbox& sbCancel,
+
128 Amounts const& takerAmount,
+
129 std::optional<uint256> const& domainID);
+
130
+
131 static std::string
+
132 format_amount(STAmount const& amount);
+
133
+
134 TER
+
135 applyHybrid(
+
136 Sandbox& sb,
+
137 std::shared_ptr<STLedgerEntry> sleOffer,
+
138 Keylet const& offer_index,
+
139 STAmount const& saTakerPays,
+
140 STAmount const& saTakerGets,
+
141 std::function<void(SLE::ref, std::optional<uint256>)> const& setDir);
+
142
+
143private:
+
144 // What kind of offer we are placing
+
145 CrossType cross_type_;
+
146
+
147 // The number of steps to take through order books while crossing
+
148 OfferStream::StepCounter stepCounter_;
+
149};
+
150
+
151using OfferCreate = CreateOffer;
+
152
+
153} // namespace ripple
+
154
+
155#endif
A generic endpoint for log messages.
Definition: Journal.h:60
State information when applying a tx.
Definition: ApplyContext.h:37
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:144
Transactor specialized for creating offers in the ledger.
Definition: CreateOffer.h:34
-
std::pair< TER, Amounts > flowCross(PaymentSandbox &psb, PaymentSandbox &psbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
-
std::pair< TER, Amounts > bridged_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
-
OfferStream::StepCounter stepCounter_
Definition: CreateOffer.h:155
-
static TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
+
std::pair< TER, Amounts > flowCross(PaymentSandbox &psb, PaymentSandbox &psbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
+
std::pair< TER, Amounts > bridged_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
+
OfferStream::StepCounter stepCounter_
Definition: CreateOffer.h:148
+
static TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
CreateOffer(ApplyContext &ctx)
Construct a Transactor subclass that creates an offer in the ledger.
Definition: CreateOffer.h:39
-
void preCompute() override
Gather information beyond what the Transactor base class gathers.
-
bool dry_offer(ApplyView &view, Offer const &offer)
-
static TER preclaim(PreclaimContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
-
static bool step_account(OfferStream &stream, Taker const &taker)
-
static std::string format_amount(STAmount const &amount)
+
void preCompute() override
Gather information beyond what the Transactor base class gathers.
+
bool dry_offer(ApplyView &view, Offer const &offer)
+
static TER preclaim(PreclaimContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
+
static bool step_account(OfferStream &stream, Taker const &taker)
+
static std::string format_amount(STAmount const &amount)
static constexpr ConsequencesFactoryType ConsequencesFactory
Definition: CreateOffer.h:36
-
bool reachedOfferCrossingLimit(Taker const &taker) const
-
std::pair< TER, Amounts > takerCross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)
-
std::pair< TER, Amounts > direct_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
+
bool reachedOfferCrossingLimit(Taker const &taker) const
+
std::pair< TER, Amounts > direct_cross(Taker &taker, ApplyView &view, ApplyView &view_cancel, NetClock::time_point const when)
static NotTEC preflight(PreflightContext const &ctx)
Enforce constraints beyond those of the Transactor base class.
Definition: CreateOffer.cpp:47
static TxConsequences makeTxConsequences(PreflightContext const &ctx)
Definition: CreateOffer.cpp:36
-
TER applyHybrid(Sandbox &sb, std::shared_ptr< STLedgerEntry > sleOffer, Keylet const &offer_index, STAmount const &saTakerPays, STAmount const &saTakerGets, std::function< void(SLE::ref, std::optional< uint256 >)> const &setDir)
-
CrossType cross_type_
Definition: CreateOffer.h:152
-
TER doApply() override
Precondition: fee collection is likely.
-
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
-
static std::pair< bool, Quality > select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)
-
std::pair< TER, Amounts > cross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
+
TER applyHybrid(Sandbox &sb, std::shared_ptr< STLedgerEntry > sleOffer, Keylet const &offer_index, STAmount const &saTakerPays, STAmount const &saTakerGets, std::function< void(SLE::ref, std::optional< uint256 >)> const &setDir)
+
CrossType cross_type_
Definition: CreateOffer.h:145
+
TER doApply() override
Precondition: fee collection is likely.
+
std::pair< TER, bool > applyGuts(Sandbox &view, Sandbox &view_cancel)
+
static std::pair< bool, Quality > select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)
+
std::pair< TER, Amounts > cross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount, std::optional< uint256 > const &domainID)
A currency issued by an account.
Definition: Issue.h:33
Presents and consumes the offers in an order book.
Definition: OfferStream.h:148
A wrapper which makes credits unavailable to balances.
diff --git a/CrossingLimits__test_8cpp_source.html b/CrossingLimits__test_8cpp_source.html index 8676dc242d..6f27488b8e 100644 --- a/CrossingLimits__test_8cpp_source.html +++ b/CrossingLimits__test_8cpp_source.html @@ -155,512 +155,475 @@ $(function() {
77 auto const gw = Account("gateway");
78 auto const USD = gw["USD"];
79
-
80 // The number of allowed offers to cross is different between
-
81 // Taker and FlowCross. Taker allows 850 and FlowCross allows 1000.
-
82 // Accommodate that difference in the test.
-
83 int const maxConsumed = features[featureFlowCross] ? 1000 : 850;
-
84
-
85 env.fund(XRP(100000000), gw, "alice", "bob", "carol");
-
86 int const bobsOfferCount = maxConsumed + 150;
-
87 env.trust(USD(bobsOfferCount), "bob");
-
88 env(pay(gw, "bob", USD(bobsOfferCount)));
-
89 env.close();
-
90 n_offers(env, bobsOfferCount, "bob", XRP(1), USD(1));
-
91
-
92 // Alice offers to buy Bob's offers. However she hits the offer
-
93 // crossing limit, so she can't buy them all at once.
-
94 env(offer("alice", USD(bobsOfferCount), XRP(bobsOfferCount)));
-
95 env.close();
-
96 env.require(balance("alice", USD(maxConsumed)));
-
97 env.require(balance("bob", USD(150)));
-
98 env.require(owners("bob", 150 + 1));
-
99
-
100 // Carol offers to buy 1000 XRP for 1000 USD. She takes Bob's
-
101 // remaining 150 offers without hitting a limit.
-
102 env(offer("carol", USD(1000), XRP(1000)));
-
103 env.close();
-
104 env.require(balance("carol", USD(150)));
-
105 env.require(balance("bob", USD(0)));
-
106 env.require(owners("bob", 1));
-
107 }
-
108
-
109 void
-
110 testStepAndCrossingLimit(FeatureBitset features)
-
111 {
-
112 testcase("Step And Crossing Limit");
-
113
-
114 using namespace jtx;
-
115 Env env(*this, features);
-
116
-
117 auto const gw = Account("gateway");
-
118 auto const USD = gw["USD"];
+
80 // The payment engine allows 1000 offers to cross.
+
81 int const maxConsumed = 1000;
+
82
+
83 env.fund(XRP(100000000), gw, "alice", "bob", "carol");
+
84 int const bobsOfferCount = maxConsumed + 150;
+
85 env.trust(USD(bobsOfferCount), "bob");
+
86 env(pay(gw, "bob", USD(bobsOfferCount)));
+
87 env.close();
+
88 n_offers(env, bobsOfferCount, "bob", XRP(1), USD(1));
+
89
+
90 // Alice offers to buy Bob's offers. However she hits the offer
+
91 // crossing limit, so she can't buy them all at once.
+
92 env(offer("alice", USD(bobsOfferCount), XRP(bobsOfferCount)));
+
93 env.close();
+
94 env.require(balance("alice", USD(maxConsumed)));
+
95 env.require(balance("bob", USD(150)));
+
96 env.require(owners("bob", 150 + 1));
+
97
+
98 // Carol offers to buy 1000 XRP for 1000 USD. She takes Bob's
+
99 // remaining 150 offers without hitting a limit.
+
100 env(offer("carol", USD(1000), XRP(1000)));
+
101 env.close();
+
102 env.require(balance("carol", USD(150)));
+
103 env.require(balance("bob", USD(0)));
+
104 env.require(owners("bob", 1));
+
105 }
+
106
+
107 void
+
108 testStepAndCrossingLimit(FeatureBitset features)
+
109 {
+
110 testcase("Step And Crossing Limit");
+
111
+
112 using namespace jtx;
+
113 Env env(*this, features);
+
114
+
115 auto const gw = Account("gateway");
+
116 auto const USD = gw["USD"];
+
117
+
118 env.fund(XRP(100000000), gw, "alice", "bob", "carol", "dan", "evita");
119
-
120 env.fund(XRP(100000000), gw, "alice", "bob", "carol", "dan", "evita");
-
121
-
122 // The number of offers allowed to cross is different between
-
123 // Taker and FlowCross. Taker allows 850 and FlowCross allows 1000.
-
124 // Accommodate that difference in the test.
-
125 bool const isFlowCross{features[featureFlowCross]};
-
126 int const maxConsumed = isFlowCross ? 1000 : 850;
-
127
-
128 int const evitasOfferCount{maxConsumed + 49};
-
129 env.trust(USD(1000), "alice");
-
130 env(pay(gw, "alice", USD(1000)));
-
131 env.trust(USD(1000), "carol");
-
132 env(pay(gw, "carol", USD(1)));
-
133 env.trust(USD(evitasOfferCount + 1), "evita");
-
134 env(pay(gw, "evita", USD(evitasOfferCount + 1)));
-
135
-
136 // Taker and FlowCross have another difference we must accommodate.
-
137 // Taker allows a total of 1000 unfunded offers to be consumed
-
138 // beyond the 850 offers it can take. FlowCross draws no such
-
139 // distinction; its limit is 1000 funded or unfunded.
-
140 //
-
141 // Give carol an extra 150 (unfunded) offers when we're using Taker
-
142 // to accommodate that difference.
-
143 int const carolsOfferCount{isFlowCross ? 700 : 850};
-
144 n_offers(env, 400, "alice", XRP(1), USD(1));
-
145 n_offers(env, carolsOfferCount, "carol", XRP(1), USD(1));
-
146 n_offers(env, evitasOfferCount, "evita", XRP(1), USD(1));
-
147
-
148 // Bob offers to buy 1000 XRP for 1000 USD. He takes all 400 USD from
-
149 // Alice's offers, 1 USD from Carol's and then removes 599 of Carol's
-
150 // offers as unfunded, before hitting the step limit.
-
151 env(offer("bob", USD(1000), XRP(1000)));
-
152 env.require(balance("bob", USD(401)));
-
153 env.require(balance("alice", USD(600)));
-
154 env.require(owners("alice", 1));
-
155 env.require(balance("carol", USD(0)));
-
156 env.require(owners("carol", carolsOfferCount - 599));
-
157 env.require(balance("evita", USD(evitasOfferCount + 1)));
-
158 env.require(owners("evita", evitasOfferCount + 1));
-
159
-
160 // Dan offers to buy maxConsumed + 50 XRP USD. He removes all of
-
161 // Carol's remaining offers as unfunded, then takes
-
162 // (maxConsumed - 100) USD from Evita's, hitting the crossing limit.
-
163 env(offer("dan", USD(maxConsumed + 50), XRP(maxConsumed + 50)));
-
164 env.require(balance("dan", USD(maxConsumed - 100)));
-
165 env.require(owners("dan", 2));
-
166 env.require(balance("alice", USD(600)));
-
167 env.require(owners("alice", 1));
-
168 env.require(balance("carol", USD(0)));
-
169 env.require(owners("carol", 1));
-
170 env.require(balance("evita", USD(150)));
-
171 env.require(owners("evita", 150));
-
172 }
-
173
-
174 void
-
175 testAutoBridgedLimitsTaker(FeatureBitset features)
-
176 {
-
177 testcase("Auto Bridged Limits Taker");
-
178
-
179 using namespace jtx;
-
180 Env env(*this, features);
-
181
-
182 auto const gw = Account("gateway");
-
183 auto const USD = gw["USD"];
-
184 auto const EUR = gw["EUR"];
-
185
-
186 env.fund(XRP(100000000), gw, "alice", "bob", "carol", "dan", "evita");
-
187
-
188 env.trust(USD(2000), "alice");
-
189 env(pay(gw, "alice", USD(2000)));
-
190 env.trust(USD(1000), "carol");
-
191 env(pay(gw, "carol", USD(3)));
-
192 env.trust(USD(1000), "evita");
-
193 env(pay(gw, "evita", USD(1000)));
-
194
-
195 n_offers(env, 302, "alice", EUR(2), XRP(1));
-
196 n_offers(env, 300, "alice", XRP(1), USD(4));
-
197 n_offers(env, 497, "carol", XRP(1), USD(3));
-
198 n_offers(env, 1001, "evita", EUR(1), USD(1));
-
199
-
200 // Bob offers to buy 2000 USD for 2000 EUR, even though he only has
-
201 // 1000 EUR.
-
202 // 1. He spends 600 EUR taking Alice's auto-bridged offers and
-
203 // gets 1200 USD for that.
-
204 // 2. He spends another 2 EUR taking one of Alice's EUR->XRP and
-
205 // one of Carol's XRP-USD offers. He gets 3 USD for that.
-
206 // 3. The remainder of Carol's offers are now unfunded. We've
-
207 // consumed 602 offers so far. We now chew through 398 more
-
208 // of Carol's unfunded offers until we hit the 1000 offer limit.
-
209 // This sets have_bridge to false -- we will handle no more
-
210 // bridged offers.
-
211 // 4. However, have_direct is still true. So we go around one more
-
212 // time and take one of Evita's offers.
-
213 // 5. After taking one of Evita's offers we notice (again) that our
-
214 // offer count was exceeded. So we completely stop after taking
-
215 // one of Evita's offers.
-
216 env.trust(EUR(10000), "bob");
-
217 env.close();
-
218 env(pay(gw, "bob", EUR(1000)));
-
219 env.close();
-
220 env(offer("bob", USD(2000), EUR(2000)));
-
221 env.require(balance("bob", USD(1204)));
-
222 env.require(balance("bob", EUR(397)));
-
223
-
224 env.require(balance("alice", USD(800)));
-
225 env.require(balance("alice", EUR(602)));
-
226 env.require(offers("alice", 1));
-
227 env.require(owners("alice", 3));
-
228
-
229 env.require(balance("carol", USD(0)));
-
230 env.require(balance("carol", EUR(none)));
-
231 env.require(offers("carol", 100));
-
232 env.require(owners("carol", 101));
-
233
-
234 env.require(balance("evita", USD(999)));
-
235 env.require(balance("evita", EUR(1)));
-
236 env.require(offers("evita", 1000));
-
237 env.require(owners("evita", 1002));
+
120 // The payment engine allows 1000 offers to cross.
+
121 int const maxConsumed = 1000;
+
122
+
123 int const evitasOfferCount{maxConsumed + 49};
+
124 env.trust(USD(1000), "alice");
+
125 env(pay(gw, "alice", USD(1000)));
+
126 env.trust(USD(1000), "carol");
+
127 env(pay(gw, "carol", USD(1)));
+
128 env.trust(USD(evitasOfferCount + 1), "evita");
+
129 env(pay(gw, "evita", USD(evitasOfferCount + 1)));
+
130
+
131 // The payment engine has a limit of 1000 funded or unfunded offers.
+
132 int const carolsOfferCount{700};
+
133 n_offers(env, 400, "alice", XRP(1), USD(1));
+
134 n_offers(env, carolsOfferCount, "carol", XRP(1), USD(1));
+
135 n_offers(env, evitasOfferCount, "evita", XRP(1), USD(1));
+
136
+
137 // Bob offers to buy 1000 XRP for 1000 USD. He takes all 400 USD from
+
138 // Alice's offers, 1 USD from Carol's and then removes 599 of Carol's
+
139 // offers as unfunded, before hitting the step limit.
+
140 env(offer("bob", USD(1000), XRP(1000)));
+
141 env.require(balance("bob", USD(401)));
+
142 env.require(balance("alice", USD(600)));
+
143 env.require(owners("alice", 1));
+
144 env.require(balance("carol", USD(0)));
+
145 env.require(owners("carol", carolsOfferCount - 599));
+
146 env.require(balance("evita", USD(evitasOfferCount + 1)));
+
147 env.require(owners("evita", evitasOfferCount + 1));
+
148
+
149 // Dan offers to buy maxConsumed + 50 XRP USD. He removes all of
+
150 // Carol's remaining offers as unfunded, then takes
+
151 // (maxConsumed - 100) USD from Evita's, hitting the crossing limit.
+
152 env(offer("dan", USD(maxConsumed + 50), XRP(maxConsumed + 50)));
+
153 env.require(balance("dan", USD(maxConsumed - 100)));
+
154 env.require(owners("dan", 2));
+
155 env.require(balance("alice", USD(600)));
+
156 env.require(owners("alice", 1));
+
157 env.require(balance("carol", USD(0)));
+
158 env.require(owners("carol", 1));
+
159 env.require(balance("evita", USD(150)));
+
160 env.require(owners("evita", 150));
+
161 }
+
162
+
163 void
+
164 testAutoBridgedLimitsTaker(FeatureBitset features)
+
165 {
+
166 testcase("Auto Bridged Limits Taker");
+
167
+
168 using namespace jtx;
+
169 Env env(*this, features);
+
170
+
171 auto const gw = Account("gateway");
+
172 auto const USD = gw["USD"];
+
173 auto const EUR = gw["EUR"];
+
174
+
175 env.fund(XRP(100000000), gw, "alice", "bob", "carol", "dan", "evita");
+
176
+
177 env.trust(USD(2000), "alice");
+
178 env(pay(gw, "alice", USD(2000)));
+
179 env.trust(USD(1000), "carol");
+
180 env(pay(gw, "carol", USD(3)));
+
181 env.trust(USD(1000), "evita");
+
182 env(pay(gw, "evita", USD(1000)));
+
183
+
184 n_offers(env, 302, "alice", EUR(2), XRP(1));
+
185 n_offers(env, 300, "alice", XRP(1), USD(4));
+
186 n_offers(env, 497, "carol", XRP(1), USD(3));
+
187 n_offers(env, 1001, "evita", EUR(1), USD(1));
+
188
+
189 // Bob offers to buy 2000 USD for 2000 EUR, even though he only has
+
190 // 1000 EUR.
+
191 // 1. He spends 600 EUR taking Alice's auto-bridged offers and
+
192 // gets 1200 USD for that.
+
193 // 2. He spends another 2 EUR taking one of Alice's EUR->XRP and
+
194 // one of Carol's XRP-USD offers. He gets 3 USD for that.
+
195 // 3. The remainder of Carol's offers are now unfunded. We've
+
196 // consumed 602 offers so far. We now chew through 398 more
+
197 // of Carol's unfunded offers until we hit the 1000 offer limit.
+
198 // This sets have_bridge to false -- we will handle no more
+
199 // bridged offers.
+
200 // 4. However, have_direct is still true. So we go around one more
+
201 // time and take one of Evita's offers.
+
202 // 5. After taking one of Evita's offers we notice (again) that our
+
203 // offer count was exceeded. So we completely stop after taking
+
204 // one of Evita's offers.
+
205 env.trust(EUR(10000), "bob");
+
206 env.close();
+
207 env(pay(gw, "bob", EUR(1000)));
+
208 env.close();
+
209 env(offer("bob", USD(2000), EUR(2000)));
+
210 env.require(balance("bob", USD(1204)));
+
211 env.require(balance("bob", EUR(397)));
+
212
+
213 env.require(balance("alice", USD(800)));
+
214 env.require(balance("alice", EUR(602)));
+
215 env.require(offers("alice", 1));
+
216 env.require(owners("alice", 3));
+
217
+
218 env.require(balance("carol", USD(0)));
+
219 env.require(balance("carol", EUR(none)));
+
220 env.require(offers("carol", 100));
+
221 env.require(owners("carol", 101));
+
222
+
223 env.require(balance("evita", USD(999)));
+
224 env.require(balance("evita", EUR(1)));
+
225 env.require(offers("evita", 1000));
+
226 env.require(owners("evita", 1002));
+
227
+
228 // Dan offers to buy 900 EUR for 900 USD.
+
229 // 1. He removes all 100 of Carol's remaining unfunded offers.
+
230 // 2. Then takes 850 USD from Evita's offers.
+
231 // 3. Consuming 850 of Evita's funded offers hits the crossing
+
232 // limit. So Dan's offer crossing stops even though he would
+
233 // be willing to take another 50 of Evita's offers.
+
234 env.trust(EUR(10000), "dan");
+
235 env.close();
+
236 env(pay(gw, "dan", EUR(1000)));
+
237 env.close();
238
-
239 // Dan offers to buy 900 EUR for 900 USD.
-
240 // 1. He removes all 100 of Carol's remaining unfunded offers.
-
241 // 2. Then takes 850 USD from Evita's offers.
-
242 // 3. Consuming 850 of Evita's funded offers hits the crossing
-
243 // limit. So Dan's offer crossing stops even though he would
-
244 // be willing to take another 50 of Evita's offers.
-
245 env.trust(EUR(10000), "dan");
-
246 env.close();
-
247 env(pay(gw, "dan", EUR(1000)));
-
248 env.close();
-
249
-
250 env(offer("dan", USD(900), EUR(900)));
-
251 env.require(balance("dan", USD(850)));
-
252 env.require(balance("dan", EUR(150)));
-
253
-
254 env.require(balance("alice", USD(800)));
-
255 env.require(balance("alice", EUR(602)));
-
256 env.require(offers("alice", 1));
-
257 env.require(owners("alice", 3));
+
239 env(offer("dan", USD(900), EUR(900)));
+
240 env.require(balance("dan", USD(850)));
+
241 env.require(balance("dan", EUR(150)));
+
242
+
243 env.require(balance("alice", USD(800)));
+
244 env.require(balance("alice", EUR(602)));
+
245 env.require(offers("alice", 1));
+
246 env.require(owners("alice", 3));
+
247
+
248 env.require(balance("carol", USD(0)));
+
249 env.require(balance("carol", EUR(none)));
+
250 env.require(offers("carol", 0));
+
251 env.require(owners("carol", 1));
+
252
+
253 env.require(balance("evita", USD(149)));
+
254 env.require(balance("evita", EUR(851)));
+
255 env.require(offers("evita", 150));
+
256 env.require(owners("evita", 152));
+
257 }
258
-
259 env.require(balance("carol", USD(0)));
-
260 env.require(balance("carol", EUR(none)));
-
261 env.require(offers("carol", 0));
-
262 env.require(owners("carol", 1));
+
259 void
+
260 testAutoBridgedLimits(FeatureBitset features)
+
261 {
+
262 testcase("Auto Bridged Limits");
263
-
264 env.require(balance("evita", USD(149)));
-
265 env.require(balance("evita", EUR(851)));
-
266 env.require(offers("evita", 150));
-
267 env.require(owners("evita", 152));
-
268 }
+
264 // If any book step in a payment strand consumes 1000 offers, the
+
265 // liquidity from the offers is used, but that strand will be marked as
+
266 // dry for the remainder of the transaction.
+
267
+
268 using namespace jtx;
269
-
270 void
-
271 testAutoBridgedLimitsFlowCross(FeatureBitset features)
-
272 {
-
273 testcase("Auto Bridged Limits FlowCross");
+
270 auto const gw = Account("gateway");
+
271 auto const alice = Account("alice");
+
272 auto const bob = Account("bob");
+
273 auto const carol = Account("carol");
274
-
275 // If any book step in a payment strand consumes 1000 offers, the
-
276 // liquidity from the offers is used, but that strand will be marked as
-
277 // dry for the remainder of the transaction.
-
278
-
279 using namespace jtx;
-
280
-
281 auto const gw = Account("gateway");
-
282 auto const alice = Account("alice");
-
283 auto const bob = Account("bob");
-
284 auto const carol = Account("carol");
+
275 auto const USD = gw["USD"];
+
276 auto const EUR = gw["EUR"];
+
277
+
278 // There are two almost identical tests. There is a strand with a large
+
279 // number of unfunded offers that will cause the strand to be marked dry
+
280 // even though there will still be liquidity available on that strand.
+
281 // In the first test, the strand has the best initial quality. In the
+
282 // second test the strand does not have the best quality (the
+
283 // implementation has to handle this case correct and not mark the
+
284 // strand dry until the liquidity is actually used)
285
-
286 auto const USD = gw["USD"];
-
287 auto const EUR = gw["EUR"];
-
288
-
289 // There are two almost identical tests. There is a strand with a large
-
290 // number of unfunded offers that will cause the strand to be marked dry
-
291 // even though there will still be liquidity available on that strand.
-
292 // In the first test, the strand has the best initial quality. In the
-
293 // second test the strand does not have the best quality (the
-
294 // implementation has to handle this case correct and not mark the
-
295 // strand dry until the liquidity is actually used)
-
296
-
297 // The implementation allows any single step to consume at most 1000
-
298 // offers. With the `FlowSortStrands` feature enabled, if the total
-
299 // number of offers consumed by all the steps combined exceeds 1500, the
-
300 // payment stops.
-
301 {
-
302 Env env(*this, features);
-
303
-
304 env.fund(XRP(100000000), gw, alice, bob, carol);
-
305
-
306 env.trust(USD(4000), alice);
-
307 env(pay(gw, alice, USD(4000)));
-
308 env.trust(USD(1000), carol);
-
309 env(pay(gw, carol, USD(3)));
-
310
-
311 // Notice the strand with the 800 unfunded offers has the initial
-
312 // best quality
-
313 n_offers(env, 2000, alice, EUR(2), XRP(1));
-
314 n_offers(env, 100, alice, XRP(1), USD(4));
-
315 n_offers(
-
316 env, 801, carol, XRP(1), USD(3)); // only one offer is funded
-
317 n_offers(env, 1000, alice, XRP(1), USD(3));
-
318
-
319 n_offers(env, 1, alice, EUR(500), USD(500));
-
320
-
321 // Bob offers to buy 2000 USD for 2000 EUR; He starts with 2000 EUR
-
322 // 1. The best quality is the autobridged offers that take 2 EUR
-
323 // and give 4 USD.
-
324 // Bob spends 200 EUR and receives 400 USD.
-
325 // 100 EUR->XRP offers consumed.
-
326 // 100 XRP->USD offers consumed.
-
327 // 200 total offers consumed.
-
328 //
-
329 // 2. The best quality is the autobridged offers that take 2 EUR
-
330 // and give 3 USD.
-
331 // a. One of Carol's offers is taken. This leaves her other
-
332 // offers unfunded.
-
333 // b. Carol's remaining 800 offers are consumed as unfunded.
-
334 // c. 199 of alice's XRP(1) to USD(3) offers are consumed.
-
335 // A book step is allowed to consume a maxium of 1000 offers
-
336 // at a given quality, and that limit is now reached.
-
337 // d. Now the strand is dry, even though there are still funded
-
338 // XRP(1) to USD(3) offers available.
-
339 // Bob has spent 400 EUR and received 600 USD in this step.
-
340 // 200 EUR->XRP offers consumed
-
341 // 800 unfunded XRP->USD offers consumed
-
342 // 200 funded XRP->USD offers consumed (1 carol, 199 alice)
-
343 // 1400 total offers consumed so far (100 left before the
-
344 // limit)
-
345 // 3. The best is the non-autobridged offers that takes 500 EUR and
-
346 // gives 500 USD.
-
347 // Bob started with 2000 EUR
-
348 // Bob spent 500 EUR (100+400)
-
349 // Bob has 1500 EUR left
-
350 // In this step:
-
351 // Bob spents 500 EUR and receives 500 USD.
-
352 // In total:
-
353 // Bob spent 1100 EUR (200 + 400 + 500)
-
354 // Bob has 900 EUR remaining (2000 - 1100)
-
355 // Bob received 1500 USD (400 + 600 + 500)
-
356 // Alice spent 1497 USD (100*4 + 199*3 + 500)
-
357 // Alice has 2503 remaining (4000 - 1497)
-
358 // Alice received 1100 EUR (200 + 400 + 500)
-
359 env.trust(EUR(10000), bob);
-
360 env.close();
-
361 env(pay(gw, bob, EUR(2000)));
-
362 env.close();
-
363 env(offer(bob, USD(4000), EUR(4000)));
-
364 env.close();
-
365
-
366 env.require(balance(bob, USD(1500)));
-
367 env.require(balance(bob, EUR(900)));
-
368 env.require(offers(bob, 1));
-
369 env.require(owners(bob, 3));
-
370
-
371 env.require(balance(alice, USD(2503)));
-
372 env.require(balance(alice, EUR(1100)));
-
373 auto const numAOffers =
-
374 2000 + 100 + 1000 + 1 - (2 * 100 + 2 * 199 + 1 + 1);
-
375 env.require(offers(alice, numAOffers));
-
376 env.require(owners(alice, numAOffers + 2));
-
377
-
378 env.require(offers(carol, 0));
-
379 }
-
380 {
-
381 Env env(*this, features);
-
382
-
383 env.fund(XRP(100000000), gw, alice, bob, carol);
-
384
-
385 env.trust(USD(4000), alice);
-
386 env(pay(gw, alice, USD(4000)));
-
387 env.trust(USD(1000), carol);
-
388 env(pay(gw, carol, USD(3)));
+
286 // The implementation allows any single step to consume at most 1000
+
287 // offers. With the `FlowSortStrands` feature enabled, if the total
+
288 // number of offers consumed by all the steps combined exceeds 1500, the
+
289 // payment stops.
+
290 {
+
291 Env env(*this, features);
+
292
+
293 env.fund(XRP(100000000), gw, alice, bob, carol);
+
294
+
295 env.trust(USD(4000), alice);
+
296 env(pay(gw, alice, USD(4000)));
+
297 env.trust(USD(1000), carol);
+
298 env(pay(gw, carol, USD(3)));
+
299
+
300 // Notice the strand with the 800 unfunded offers has the initial
+
301 // best quality
+
302 n_offers(env, 2000, alice, EUR(2), XRP(1));
+
303 n_offers(env, 100, alice, XRP(1), USD(4));
+
304 n_offers(
+
305 env, 801, carol, XRP(1), USD(3)); // only one offer is funded
+
306 n_offers(env, 1000, alice, XRP(1), USD(3));
+
307
+
308 n_offers(env, 1, alice, EUR(500), USD(500));
+
309
+
310 // Bob offers to buy 2000 USD for 2000 EUR; He starts with 2000 EUR
+
311 // 1. The best quality is the autobridged offers that take 2 EUR
+
312 // and give 4 USD.
+
313 // Bob spends 200 EUR and receives 400 USD.
+
314 // 100 EUR->XRP offers consumed.
+
315 // 100 XRP->USD offers consumed.
+
316 // 200 total offers consumed.
+
317 //
+
318 // 2. The best quality is the autobridged offers that take 2 EUR
+
319 // and give 3 USD.
+
320 // a. One of Carol's offers is taken. This leaves her other
+
321 // offers unfunded.
+
322 // b. Carol's remaining 800 offers are consumed as unfunded.
+
323 // c. 199 of alice's XRP(1) to USD(3) offers are consumed.
+
324 // A book step is allowed to consume a maxium of 1000 offers
+
325 // at a given quality, and that limit is now reached.
+
326 // d. Now the strand is dry, even though there are still funded
+
327 // XRP(1) to USD(3) offers available.
+
328 // Bob has spent 400 EUR and received 600 USD in this step.
+
329 // 200 EUR->XRP offers consumed
+
330 // 800 unfunded XRP->USD offers consumed
+
331 // 200 funded XRP->USD offers consumed (1 carol, 199 alice)
+
332 // 1400 total offers consumed so far (100 left before the
+
333 // limit)
+
334 // 3. The best is the non-autobridged offers that takes 500 EUR and
+
335 // gives 500 USD.
+
336 // Bob started with 2000 EUR
+
337 // Bob spent 500 EUR (100+400)
+
338 // Bob has 1500 EUR left
+
339 // In this step:
+
340 // Bob spents 500 EUR and receives 500 USD.
+
341 // In total:
+
342 // Bob spent 1100 EUR (200 + 400 + 500)
+
343 // Bob has 900 EUR remaining (2000 - 1100)
+
344 // Bob received 1500 USD (400 + 600 + 500)
+
345 // Alice spent 1497 USD (100*4 + 199*3 + 500)
+
346 // Alice has 2503 remaining (4000 - 1497)
+
347 // Alice received 1100 EUR (200 + 400 + 500)
+
348 env.trust(EUR(10000), bob);
+
349 env.close();
+
350 env(pay(gw, bob, EUR(2000)));
+
351 env.close();
+
352 env(offer(bob, USD(4000), EUR(4000)));
+
353 env.close();
+
354
+
355 env.require(balance(bob, USD(1500)));
+
356 env.require(balance(bob, EUR(900)));
+
357 env.require(offers(bob, 1));
+
358 env.require(owners(bob, 3));
+
359
+
360 env.require(balance(alice, USD(2503)));
+
361 env.require(balance(alice, EUR(1100)));
+
362 auto const numAOffers =
+
363 2000 + 100 + 1000 + 1 - (2 * 100 + 2 * 199 + 1 + 1);
+
364 env.require(offers(alice, numAOffers));
+
365 env.require(owners(alice, numAOffers + 2));
+
366
+
367 env.require(offers(carol, 0));
+
368 }
+
369 {
+
370 Env env(*this, features);
+
371
+
372 env.fund(XRP(100000000), gw, alice, bob, carol);
+
373
+
374 env.trust(USD(4000), alice);
+
375 env(pay(gw, alice, USD(4000)));
+
376 env.trust(USD(1000), carol);
+
377 env(pay(gw, carol, USD(3)));
+
378
+
379 // Notice the strand with the 800 unfunded offers does not have the
+
380 // initial best quality
+
381 n_offers(env, 1, alice, EUR(1), USD(10));
+
382 n_offers(env, 2000, alice, EUR(2), XRP(1));
+
383 n_offers(env, 100, alice, XRP(1), USD(4));
+
384 n_offers(
+
385 env, 801, carol, XRP(1), USD(3)); // only one offer is funded
+
386 n_offers(env, 1000, alice, XRP(1), USD(3));
+
387
+
388 n_offers(env, 1, alice, EUR(499), USD(499));
389
-
390 // Notice the strand with the 800 unfunded offers does not have the
-
391 // initial best quality
-
392 n_offers(env, 1, alice, EUR(1), USD(10));
-
393 n_offers(env, 2000, alice, EUR(2), XRP(1));
-
394 n_offers(env, 100, alice, XRP(1), USD(4));
-
395 n_offers(
-
396 env, 801, carol, XRP(1), USD(3)); // only one offer is funded
-
397 n_offers(env, 1000, alice, XRP(1), USD(3));
-
398
-
399 n_offers(env, 1, alice, EUR(499), USD(499));
-
400
-
401 // Bob offers to buy 2000 USD for 2000 EUR; He starts with 2000 EUR
-
402 // 1. The best quality is the offer that takes 1 EUR and gives 10
-
403 // USD
-
404 // Bob spends 1 EUR and receives 10 USD.
-
405 //
-
406 // 2. The best quality is the autobridged offers that takes 2 EUR
-
407 // and gives 4 USD.
-
408 // Bob spends 200 EUR and receives 400 USD.
-
409 //
-
410 // 3. The best quality is the autobridged offers that takes 2 EUR
-
411 // and gives 3 USD.
-
412 // a. One of Carol's offers is taken. This leaves her other
-
413 // offers unfunded.
-
414 // b. Carol's remaining 800 offers are consumed as unfunded.
-
415 // c. 199 of alice's XRP(1) to USD(3) offers are consumed.
-
416 // A book step is allowed to consume a maxium of 1000 offers
-
417 // at a given quality, and that limit is now reached.
-
418 // d. Now the strand is dry, even though there are still funded
-
419 // XRP(1) to USD(3) offers available. Bob has spent 400 EUR and
-
420 // received 600 USD in this step. (200 funded offers consumed
-
421 // 800 unfunded offers)
-
422 // 4. The best is the non-autobridged offers that takes 499 EUR and
-
423 // gives 499 USD.
-
424 // Bob has 2000 EUR, and has spent 1+200+400=601 EUR. He has
-
425 // 1399 left. Bob spent 499 EUR and receives 499 USD.
-
426 // In total: Bob spent EUR(1 + 200 + 400 + 499) = EUR(1100). He
-
427 // started with 2000 so has 900 remaining
-
428 // Bob received USD(10 + 400 + 600 + 499) = USD(1509).
-
429 // Alice spent 10 + 100*4 + 199*3 + 499 = 1506 USD. She
-
430 // started with 4000 so has 2494 USD remaining. Alice
-
431 // received 200 + 400 + 500 = 1100 EUR
-
432 env.trust(EUR(10000), bob);
-
433 env.close();
-
434 env(pay(gw, bob, EUR(2000)));
-
435 env.close();
-
436 env(offer(bob, USD(4000), EUR(4000)));
-
437 env.close();
-
438
-
439 env.require(balance(bob, USD(1509)));
-
440 env.require(balance(bob, EUR(900)));
-
441 env.require(offers(bob, 1));
-
442 env.require(owners(bob, 3));
+
390 // Bob offers to buy 2000 USD for 2000 EUR; He starts with 2000 EUR
+
391 // 1. The best quality is the offer that takes 1 EUR and gives 10
+
392 // USD
+
393 // Bob spends 1 EUR and receives 10 USD.
+
394 //
+
395 // 2. The best quality is the autobridged offers that takes 2 EUR
+
396 // and gives 4 USD.
+
397 // Bob spends 200 EUR and receives 400 USD.
+
398 //
+
399 // 3. The best quality is the autobridged offers that takes 2 EUR
+
400 // and gives 3 USD.
+
401 // a. One of Carol's offers is taken. This leaves her other
+
402 // offers unfunded.
+
403 // b. Carol's remaining 800 offers are consumed as unfunded.
+
404 // c. 199 of alice's XRP(1) to USD(3) offers are consumed.
+
405 // A book step is allowed to consume a maxium of 1000 offers
+
406 // at a given quality, and that limit is now reached.
+
407 // d. Now the strand is dry, even though there are still funded
+
408 // XRP(1) to USD(3) offers available. Bob has spent 400 EUR and
+
409 // received 600 USD in this step. (200 funded offers consumed
+
410 // 800 unfunded offers)
+
411 // 4. The best is the non-autobridged offers that takes 499 EUR and
+
412 // gives 499 USD.
+
413 // Bob has 2000 EUR, and has spent 1+200+400=601 EUR. He has
+
414 // 1399 left. Bob spent 499 EUR and receives 499 USD.
+
415 // In total: Bob spent EUR(1 + 200 + 400 + 499) = EUR(1100). He
+
416 // started with 2000 so has 900 remaining
+
417 // Bob received USD(10 + 400 + 600 + 499) = USD(1509).
+
418 // Alice spent 10 + 100*4 + 199*3 + 499 = 1506 USD. She
+
419 // started with 4000 so has 2494 USD remaining. Alice
+
420 // received 200 + 400 + 500 = 1100 EUR
+
421 env.trust(EUR(10000), bob);
+
422 env.close();
+
423 env(pay(gw, bob, EUR(2000)));
+
424 env.close();
+
425 env(offer(bob, USD(4000), EUR(4000)));
+
426 env.close();
+
427
+
428 env.require(balance(bob, USD(1509)));
+
429 env.require(balance(bob, EUR(900)));
+
430 env.require(offers(bob, 1));
+
431 env.require(owners(bob, 3));
+
432
+
433 env.require(balance(alice, USD(2494)));
+
434 env.require(balance(alice, EUR(1100)));
+
435 auto const numAOffers =
+
436 1 + 2000 + 100 + 1000 + 1 - (1 + 2 * 100 + 2 * 199 + 1 + 1);
+
437 env.require(offers(alice, numAOffers));
+
438 env.require(owners(alice, numAOffers + 2));
+
439
+
440 env.require(offers(carol, 0));
+
441 }
+
442 }
443
-
444 env.require(balance(alice, USD(2494)));
-
445 env.require(balance(alice, EUR(1100)));
-
446 auto const numAOffers =
-
447 1 + 2000 + 100 + 1000 + 1 - (1 + 2 * 100 + 2 * 199 + 1 + 1);
-
448 env.require(offers(alice, numAOffers));
-
449 env.require(owners(alice, numAOffers + 2));
+
444 void
+
445 testOfferOverflow(FeatureBitset features)
+
446 {
+
447 testcase("Offer Overflow");
+
448
+
449 using namespace jtx;
450
-
451 env.require(offers(carol, 0));
-
452 }
-
453 }
+
451 auto const gw = Account("gateway");
+
452 auto const alice = Account("alice");
+
453 auto const bob = Account("bob");
454
-
455 void
-
456 testAutoBridgedLimits(FeatureBitset features)
-
457 {
-
458 // Taker and FlowCross are too different in the way they handle
-
459 // autobridging to make one test suit both approaches.
-
460 //
-
461 // o Taker alternates between books, completing one full increment
-
462 // before returning to make another pass.
-
463 //
-
464 // o FlowCross extracts as much as possible in one book at one Quality
-
465 // before proceeding to the other book. This reduces the number of
-
466 // times we change books.
-
467 //
-
468 // So the tests for the two forms of autobridging are separate.
-
469 if (features[featureFlowCross])
-
470 testAutoBridgedLimitsFlowCross(features);
-
471 else
-
472 testAutoBridgedLimitsTaker(features);
-
473 }
-
474
-
475 void
-
476 testOfferOverflow(FeatureBitset features)
-
477 {
-
478 testcase("Offer Overflow");
-
479
-
480 using namespace jtx;
-
481
-
482 auto const gw = Account("gateway");
-
483 auto const alice = Account("alice");
-
484 auto const bob = Account("bob");
-
485
-
486 auto const USD = gw["USD"];
-
487
-
488 Env env(*this, features);
-
489
-
490 env.fund(XRP(100000000), gw, alice, bob);
-
491
-
492 env.trust(USD(8000), alice);
-
493 env.trust(USD(8000), bob);
-
494 env.close();
+
455 auto const USD = gw["USD"];
+
456
+
457 Env env(*this, features);
+
458
+
459 env.fund(XRP(100000000), gw, alice, bob);
+
460
+
461 env.trust(USD(8000), alice);
+
462 env.trust(USD(8000), bob);
+
463 env.close();
+
464
+
465 env(pay(gw, alice, USD(8000)));
+
466 env.close();
+
467
+
468 // The new flow cross handles consuming excessive offers differently
+
469 // than the old offer crossing code. In the old code, the total number
+
470 // of consumed offers is tracked, and the crossings will stop after this
+
471 // limit is hit. In the new code, the number of offers is tracked per
+
472 // offerbook and per quality. This test shows how they can differ. Set
+
473 // up a book with many offers. At each quality keep the number of offers
+
474 // below the limit. However, if all the offers are consumed it would
+
475 // create a tecOVERSIZE error.
+
476
+
477 // The featureFlowSortStrands introduces a way of tracking the total
+
478 // number of consumed offers; with this feature the transaction no
+
479 // longer fails with a tecOVERSIZE error.
+
480 // The implementation allows any single step to consume at most 1000
+
481 // offers. With the `FlowSortStrands` feature enabled, if the total
+
482 // number of offers consumed by all the steps combined exceeds 1500, the
+
483 // payment stops. Since the first set of offers consumes 998 offers, the
+
484 // second set will consume 998, which is not over the limit and the
+
485 // payment stops. So 2*998, or 1996 is the expected value when
+
486 // `FlowSortStrands` is enabled.
+
487 n_offers(env, 998, alice, XRP(1.00), USD(1));
+
488 n_offers(env, 998, alice, XRP(0.99), USD(1));
+
489 n_offers(env, 998, alice, XRP(0.98), USD(1));
+
490 n_offers(env, 998, alice, XRP(0.97), USD(1));
+
491 n_offers(env, 998, alice, XRP(0.96), USD(1));
+
492 n_offers(env, 998, alice, XRP(0.95), USD(1));
+
493
+
494 bool const withSortStrands = features[featureFlowSortStrands];
495
-
496 env(pay(gw, alice, USD(8000)));
-
497 env.close();
-
498
-
499 // The new flow cross handles consuming excessive offers differently
-
500 // than the old offer crossing code. In the old code, the total number
-
501 // of consumed offers is tracked, and the crossings will stop after this
-
502 // limit is hit. In the new code, the number of offers is tracked per
-
503 // offerbook and per quality. This test shows how they can differ. Set
-
504 // up a book with many offers. At each quality keep the number of offers
-
505 // below the limit. However, if all the offers are consumed it would
-
506 // create a tecOVERSIZE error.
-
507
-
508 // The featureFlowSortStrands introduces a way of tracking the total
-
509 // number of consumed offers; with this feature the transaction no
-
510 // longer fails with a tecOVERSIZE error.
-
511 // The implementation allows any single step to consume at most 1000
-
512 // offers. With the `FlowSortStrands` feature enabled, if the total
-
513 // number of offers consumed by all the steps combined exceeds 1500, the
-
514 // payment stops. Since the first set of offers consumes 998 offers, the
-
515 // second set will consume 998, which is not over the limit and the
-
516 // payment stops. So 2*998, or 1996 is the expected value when
-
517 // `FlowSortStrands` is enabled.
-
518 n_offers(env, 998, alice, XRP(1.00), USD(1));
-
519 n_offers(env, 998, alice, XRP(0.99), USD(1));
-
520 n_offers(env, 998, alice, XRP(0.98), USD(1));
-
521 n_offers(env, 998, alice, XRP(0.97), USD(1));
-
522 n_offers(env, 998, alice, XRP(0.96), USD(1));
-
523 n_offers(env, 998, alice, XRP(0.95), USD(1));
-
524
-
525 bool const withFlowCross = features[featureFlowCross];
-
526 bool const withSortStrands = features[featureFlowSortStrands];
-
527
-
528 auto const expectedTER = [&]() -> TER {
-
529 if (withFlowCross && !withSortStrands)
-
530 return TER{tecOVERSIZE};
-
531 return tesSUCCESS;
-
532 }();
-
533
-
534 env(offer(bob, USD(8000), XRP(8000)), ter(expectedTER));
-
535 env.close();
-
536
-
537 auto const expectedUSD = [&] {
-
538 if (!withFlowCross)
-
539 return USD(850);
-
540 if (!withSortStrands)
-
541 return USD(0);
-
542 return USD(1996);
-
543 }();
-
544
-
545 env.require(balance(bob, expectedUSD));
-
546 }
-
547
-
548 void
-
549 run() override
-
550 {
-
551 auto testAll = [this](FeatureBitset features) {
-
552 testStepLimit(features);
-
553 testCrossingLimit(features);
-
554 testStepAndCrossingLimit(features);
-
555 testAutoBridgedLimits(features);
-
556 testOfferOverflow(features);
-
557 };
-
558 using namespace jtx;
-
559 auto const sa = supported_amendments();
-
560 testAll(sa);
-
561 testAll(sa - featurePermissionedDEX);
-
562 testAll(sa - featureFlowSortStrands - featurePermissionedDEX);
-
563 testAll(
-
564 sa - featureFlowCross - featureFlowSortStrands -
-
565 featurePermissionedDEX);
-
566 }
-
567};
-
568
-
569BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(CrossingLimits, tx, ripple, 10);
-
570
-
571} // namespace test
-
572} // namespace ripple
+
496 auto const expectedTER = [&]() -> TER {
+
497 if (!withSortStrands)
+
498 return TER{tecOVERSIZE};
+
499 return tesSUCCESS;
+
500 }();
+
501
+
502 env(offer(bob, USD(8000), XRP(8000)), ter(expectedTER));
+
503 env.close();
+
504
+
505 auto const expectedUSD = [&] {
+
506 if (!withSortStrands)
+
507 return USD(0);
+
508 return USD(1996);
+
509 }();
+
510
+
511 env.require(balance(bob, expectedUSD));
+
512 }
+
513
+
514 void
+
515 run() override
+
516 {
+
517 auto testAll = [this](FeatureBitset features) {
+
518 testStepLimit(features);
+
519 testCrossingLimit(features);
+
520 testStepAndCrossingLimit(features);
+
521 testAutoBridgedLimits(features);
+
522 testOfferOverflow(features);
+
523 };
+
524 using namespace jtx;
+
525 auto const sa = supported_amendments();
+
526 testAll(sa);
+
527 testAll(sa - featureFlowSortStrands);
+
528 testAll(sa - featurePermissionedDEX);
+
529 testAll(sa - featureFlowSortStrands - featurePermissionedDEX);
+
530 }
+
531};
+
532
+
533BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(CrossingLimits, tx, ripple, 10);
+
534
+
535} // namespace test
+
536} // namespace ripple
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
Definition: Feature.h:163
-
void testStepAndCrossingLimit(FeatureBitset features)
-
void testAutoBridgedLimitsTaker(FeatureBitset features)
-
void testAutoBridgedLimits(FeatureBitset features)
+
void testStepAndCrossingLimit(FeatureBitset features)
+
void testAutoBridgedLimitsTaker(FeatureBitset features)
+
void testAutoBridgedLimits(FeatureBitset features)
void testStepLimit(FeatureBitset features)
void testCrossingLimit(FeatureBitset features)
-
void run() override
Runs the suite.
-
void testAutoBridgedLimitsFlowCross(FeatureBitset features)
-
void testOfferOverflow(FeatureBitset features)
+
void run() override
Runs the suite.
+
void testOfferOverflow(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:121
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:544
diff --git a/DeliverMin__test_8cpp_source.html b/DeliverMin__test_8cpp_source.html index c72a895625..782a01df54 100644 --- a/DeliverMin__test_8cpp_source.html +++ b/DeliverMin__test_8cpp_source.html @@ -221,17 +221,15 @@ $(function() {
143 {
144 using namespace jtx;
145 auto const sa = supported_amendments();
-
146 test_convert_all_of_an_asset(
-
147 sa - featureFlowCross - featurePermissionedDEX);
-
148 test_convert_all_of_an_asset(sa - featurePermissionedDEX);
-
149 test_convert_all_of_an_asset(sa);
-
150 }
-
151};
+
146 test_convert_all_of_an_asset(sa - featurePermissionedDEX);
+
147 test_convert_all_of_an_asset(sa);
+
148 }
+
149};
+
150
+
151BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple);
152
-
153BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple);
-
154
-
155} // namespace test
-
156} // namespace ripple
+
153} // namespace test
+
154} // namespace ripple
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
Definition: Feature.h:163
diff --git a/Discrepancy__test_8cpp_source.html b/Discrepancy__test_8cpp_source.html index b60dd65d13..480c92350c 100644 --- a/Discrepancy__test_8cpp_source.html +++ b/Discrepancy__test_8cpp_source.html @@ -225,15 +225,14 @@ $(function() {
147 {
148 using namespace test::jtx;
149 auto const sa = supported_amendments();
-
150 testXRPDiscrepancy(sa - featureFlowCross - featurePermissionedDEX);
-
151 testXRPDiscrepancy(sa - featurePermissionedDEX);
-
152 testXRPDiscrepancy(sa);
-
153 }
-
154};
-
155
-
156BEAST_DEFINE_TESTSUITE(Discrepancy, app, ripple);
-
157
-
158} // namespace ripple
+
150 testXRPDiscrepancy(sa - featurePermissionedDEX);
+
151 testXRPDiscrepancy(sa);
+
152 }
+
153};
+
154
+
155BEAST_DEFINE_TESTSUITE(Discrepancy, app, ripple);
+
156
+
157} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
UInt asUInt() const
Definition: json_value.cpp:558
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:482
diff --git a/Flow__test_8cpp_source.html b/Flow__test_8cpp_source.html index 5dc58cd7f6..4372a17509 100644 --- a/Flow__test_8cpp_source.html +++ b/Flow__test_8cpp_source.html @@ -1411,41 +1411,37 @@ $(function() {
1333
1334 using namespace jtx;
1335 auto const sa = supported_amendments();
-
1336 testWithFeats(sa - featureFlowCross - featurePermissionedDEX);
-
1337 testWithFeats(sa - featurePermissionedDEX);
-
1338 testWithFeats(sa);
-
1339 testEmptyStrand(sa);
-
1340 }
-
1341};
-
1342
-
1343struct Flow_manual_test : public Flow_test
-
1344{
-
1345 void
-
1346 run() override
-
1347 {
-
1348 using namespace jtx;
-
1349 auto const all = supported_amendments();
-
1350 FeatureBitset const flowCross{featureFlowCross};
-
1351 FeatureBitset const f1513{fix1513};
-
1352 FeatureBitset const permDex{featurePermissionedDEX};
-
1353
-
1354 testWithFeats(all - flowCross - f1513 - permDex);
-
1355 testWithFeats(all - flowCross - permDex);
-
1356 testWithFeats(all - f1513 - permDex);
-
1357 testWithFeats(all - permDex);
-
1358 testWithFeats(all);
-
1359
-
1360 testEmptyStrand(all - f1513 - permDex);
-
1361 testEmptyStrand(all - permDex);
-
1362 testEmptyStrand(all);
-
1363 }
-
1364};
-
1365
-
1366BEAST_DEFINE_TESTSUITE_PRIO(Flow, app, ripple, 2);
-
1367BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Flow_manual, app, ripple, 4);
-
1368
-
1369} // namespace test
-
1370} // namespace ripple
+
1336 testWithFeats(sa - featurePermissionedDEX);
+
1337 testWithFeats(sa);
+
1338 testEmptyStrand(sa);
+
1339 }
+
1340};
+
1341
+
1342struct Flow_manual_test : public Flow_test
+
1343{
+
1344 void
+
1345 run() override
+
1346 {
+
1347 using namespace jtx;
+
1348 auto const all = supported_amendments();
+
1349 FeatureBitset const f1513{fix1513};
+
1350 FeatureBitset const permDex{featurePermissionedDEX};
+
1351
+
1352 testWithFeats(all - f1513 - permDex);
+
1353 testWithFeats(all - permDex);
+
1354 testWithFeats(all);
+
1355
+
1356 testEmptyStrand(all - f1513 - permDex);
+
1357 testEmptyStrand(all - permDex);
+
1358 testEmptyStrand(all);
+
1359 }
+
1360};
+
1361
+
1362BEAST_DEFINE_TESTSUITE_PRIO(Flow, app, ripple, 2);
+
1363BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Flow_manual, app, ripple, 4);
+
1364
+
1365} // namespace test
+
1366} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
A testsuite class.
Definition: suite.h:55
@@ -1537,8 +1533,8 @@ $(function() {
T stoull(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition: Keylet.h:39
Definition: STAmount.h:86
-
Definition: Flow_test.cpp:1344
-
void run() override
Runs the suite.
Definition: Flow_test.cpp:1346
+
Definition: Flow_test.cpp:1343
+
void run() override
Runs the suite.
Definition: Flow_test.cpp:1345
Definition: Flow_test.cpp:53
void run() override
Runs the suite.
Definition: Flow_test.cpp:1327
void testRIPD1443()
Definition: Flow_test.cpp:1036
diff --git a/Freeze__test_8cpp_source.html b/Freeze__test_8cpp_source.html index 3170268e97..ea07bb78e5 100644 --- a/Freeze__test_8cpp_source.html +++ b/Freeze__test_8cpp_source.html @@ -1039,1169 +1039,1154 @@ $(function() {
961 env.close();
962
963 // test: A1 wants to buy, must fail
-
964 if (features[featureFlowCross])
-
965 {
-
966 env(offer(A1, USD(1), XRP(2)),
-
967 txflags(tfFillOrKill),
-
968 ter(tecKILLED));
-
969 env.close();
-
970 env.require(
-
971 balance(A1, USD(1002)),
-
972 balance(A2, USD(997)),
-
973 offers(A1, 0));
-
974 }
-
975 else
-
976 {
-
977 // The transaction that should be here would succeed.
-
978 // I don't want to adjust balances in following tests. Flow
-
979 // cross feature flag is not relevant to this particular test
-
980 // case so we're not missing out some corner cases checks.
-
981 }
-
982
-
983 // test: A1 can create passive sell offer
-
984 env(offer(A1, XRP(2), USD(1)), txflags(tfPassive));
-
985 env.close();
-
986 env.require(balance(A1, USD(1002)), offers(A1, 1));
-
987 // Cleanup
-
988 env(offer_cancel(A1, env.seq(A1) - 1));
-
989 env.require(offers(A1, 0));
-
990 env.close();
-
991
-
992 // test: A1 can sell to A3
-
993 env(offer(A1, XRP(1), USD(1)), txflags(tfFillOrKill));
+
964 env(offer(A1, USD(1), XRP(2)),
+
965 txflags(tfFillOrKill),
+
966 ter(tecKILLED));
+
967 env.close();
+
968 env.require(
+
969 balance(A1, USD(1002)), balance(A2, USD(997)), offers(A1, 0));
+
970
+
971 // test: A1 can create passive sell offer
+
972 env(offer(A1, XRP(2), USD(1)), txflags(tfPassive));
+
973 env.close();
+
974 env.require(balance(A1, USD(1002)), offers(A1, 1));
+
975 // Cleanup
+
976 env(offer_cancel(A1, env.seq(A1) - 1));
+
977 env.require(offers(A1, 0));
+
978 env.close();
+
979
+
980 // test: A1 can sell to A3
+
981 env(offer(A1, XRP(1), USD(1)), txflags(tfFillOrKill));
+
982 env.close();
+
983 env.require(balance(A1, USD(1001)), offers(A1, 0));
+
984
+
985 env(trust(A1, limit, tfClearFreeze));
+
986 env.close();
+
987 }
+
988
+
989 // Testing aggressive and passive offer placing, trustline deep frozen
+
990 // by the holder
+
991 if (features[featureDeepFreeze])
+
992 {
+
993 env(trust(A1, limit, tfSetFreeze | tfSetDeepFreeze));
994 env.close();
-
995 env.require(balance(A1, USD(1001)), offers(A1, 0));
-
996
-
997 env(trust(A1, limit, tfClearFreeze));
-
998 env.close();
-
999 }
-
1000
-
1001 // Testing aggressive and passive offer placing, trustline deep frozen
-
1002 // by the holder
-
1003 if (features[featureDeepFreeze])
-
1004 {
-
1005 env(trust(A1, limit, tfSetFreeze | tfSetDeepFreeze));
+
995
+
996 // test: A1 cannot create passive buy offer
+
997 env(offer(A1, USD(1), XRP(0.5)),
+
998 txflags(tfPassive),
+
999 ter(tecFROZEN));
+
1000 env.close();
+
1001
+
1002 // test: A1 cannot buy, must fail
+
1003 env(offer(A1, USD(1), XRP(2)),
+
1004 txflags(tfFillOrKill),
+
1005 ter(tecFROZEN));
1006 env.close();
1007
-
1008 // test: A1 cannot create passive buy offer
-
1009 env(offer(A1, USD(1), XRP(0.5)),
+
1008 // test: A1 cannot create passive sell offer
+
1009 env(offer(A1, XRP(2), USD(1)),
1010 txflags(tfPassive),
-
1011 ter(tecFROZEN));
+
1011 ter(tecUNFUNDED_OFFER));
1012 env.close();
1013
-
1014 // test: A1 cannot buy, must fail
-
1015 env(offer(A1, USD(1), XRP(2)),
+
1014 // test: A1 cannot sell to A3
+
1015 env(offer(A1, XRP(1), USD(1)),
1016 txflags(tfFillOrKill),
-
1017 ter(tecFROZEN));
+
1017 ter(tecUNFUNDED_OFFER));
1018 env.close();
1019
-
1020 // test: A1 cannot create passive sell offer
-
1021 env(offer(A1, XRP(2), USD(1)),
-
1022 txflags(tfPassive),
-
1023 ter(tecUNFUNDED_OFFER));
-
1024 env.close();
-
1025
-
1026 // test: A1 cannot sell to A3
-
1027 env(offer(A1, XRP(1), USD(1)),
-
1028 txflags(tfFillOrKill),
-
1029 ter(tecUNFUNDED_OFFER));
-
1030 env.close();
+
1020 env(trust(A1, limit, tfClearFreeze | tfClearDeepFreeze));
+
1021 env.close();
+
1022 }
+
1023 }
+
1024
+
1025 void
+
1026 testPathsWhenFrozen(FeatureBitset features)
+
1027 {
+
1028 testcase("Longer paths payment on frozen trust lines");
+
1029 using namespace test::jtx;
+
1030 using path = test::jtx::path;
1031
-
1032 env(trust(A1, limit, tfClearFreeze | tfClearDeepFreeze));
-
1033 env.close();
-
1034 }
-
1035 }
-
1036
-
1037 void
-
1038 testPathsWhenFrozen(FeatureBitset features)
-
1039 {
-
1040 testcase("Longer paths payment on frozen trust lines");
-
1041 using namespace test::jtx;
-
1042 using path = test::jtx::path;
-
1043
-
1044 Env env(*this, features);
-
1045 Account G1{"G1"};
-
1046 Account A1{"A1"};
-
1047 Account A2{"A2"};
-
1048 auto const USD{G1["USD"]};
-
1049
-
1050 env.fund(XRP(10000), G1, A1, A2);
-
1051 env.close();
-
1052
-
1053 auto const limit = USD(10000);
-
1054 env.trust(limit, A1, A2);
-
1055 env.close();
+
1032 Env env(*this, features);
+
1033 Account G1{"G1"};
+
1034 Account A1{"A1"};
+
1035 Account A2{"A2"};
+
1036 auto const USD{G1["USD"]};
+
1037
+
1038 env.fund(XRP(10000), G1, A1, A2);
+
1039 env.close();
+
1040
+
1041 auto const limit = USD(10000);
+
1042 env.trust(limit, A1, A2);
+
1043 env.close();
+
1044
+
1045 env(pay(G1, A1, USD(1000)));
+
1046 env(pay(G1, A2, USD(1000)));
+
1047 env.close();
+
1048
+
1049 env(offer(A2, XRP(100), USD(100)), txflags(tfPassive));
+
1050 env.close();
+
1051
+
1052 // Testing payments A1 <-> G1 using offer from A2 frozen by issuer.
+
1053 {
+
1054 env(trust(G1, A2["USD"](0), tfSetFreeze));
+
1055 env.close();
1056
-
1057 env(pay(G1, A1, USD(1000)));
-
1058 env(pay(G1, A2, USD(1000)));
-
1059 env.close();
-
1060
-
1061 env(offer(A2, XRP(100), USD(100)), txflags(tfPassive));
-
1062 env.close();
-
1063
-
1064 // Testing payments A1 <-> G1 using offer from A2 frozen by issuer.
-
1065 {
-
1066 env(trust(G1, A2["USD"](0), tfSetFreeze));
-
1067 env.close();
-
1068
-
1069 // test: A1 cannot send USD using XRP through A2 offer
-
1070 env(pay(A1, G1, USD(10)),
-
1071 path(~USD),
-
1072 sendmax(XRP(11)),
-
1073 txflags(tfNoRippleDirect),
-
1074 ter(tecPATH_PARTIAL));
-
1075 env.close();
+
1057 // test: A1 cannot send USD using XRP through A2 offer
+
1058 env(pay(A1, G1, USD(10)),
+
1059 path(~USD),
+
1060 sendmax(XRP(11)),
+
1061 txflags(tfNoRippleDirect),
+
1062 ter(tecPATH_PARTIAL));
+
1063 env.close();
+
1064
+
1065 // test: G1 cannot send USD using XRP through A2 offer
+
1066 env(pay(G1, A1, USD(10)),
+
1067 path(~USD),
+
1068 sendmax(XRP(11)),
+
1069 txflags(tfNoRippleDirect),
+
1070 ter(tecPATH_PARTIAL));
+
1071 env.close();
+
1072
+
1073 env(trust(G1, A2["USD"](0), tfClearFreeze));
+
1074 env.close();
+
1075 }
1076
-
1077 // test: G1 cannot send USD using XRP through A2 offer
-
1078 env(pay(G1, A1, USD(10)),
-
1079 path(~USD),
-
1080 sendmax(XRP(11)),
-
1081 txflags(tfNoRippleDirect),
-
1082 ter(tecPATH_PARTIAL));
-
1083 env.close();
-
1084
-
1085 env(trust(G1, A2["USD"](0), tfClearFreeze));
-
1086 env.close();
-
1087 }
-
1088
-
1089 // Testing payments A1 <-> G1 using offer from A2 deep frozen by issuer.
-
1090 if (features[featureDeepFreeze])
-
1091 {
-
1092 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1093 env.close();
-
1094
-
1095 // test: A1 cannot send USD using XRP through A2 offer
-
1096 env(pay(A1, G1, USD(10)),
-
1097 path(~USD),
-
1098 sendmax(XRP(11)),
-
1099 txflags(tfNoRippleDirect),
-
1100 ter(tecPATH_PARTIAL));
-
1101 env.close();
+
1077 // Testing payments A1 <-> G1 using offer from A2 deep frozen by issuer.
+
1078 if (features[featureDeepFreeze])
+
1079 {
+
1080 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1081 env.close();
+
1082
+
1083 // test: A1 cannot send USD using XRP through A2 offer
+
1084 env(pay(A1, G1, USD(10)),
+
1085 path(~USD),
+
1086 sendmax(XRP(11)),
+
1087 txflags(tfNoRippleDirect),
+
1088 ter(tecPATH_PARTIAL));
+
1089 env.close();
+
1090
+
1091 // test: G1 cannot send USD using XRP through A2 offer
+
1092 env(pay(G1, A1, USD(10)),
+
1093 path(~USD),
+
1094 sendmax(XRP(11)),
+
1095 txflags(tfNoRippleDirect),
+
1096 ter(tecPATH_PARTIAL));
+
1097 env.close();
+
1098
+
1099 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1100 env.close();
+
1101 }
1102
-
1103 // test: G1 cannot send USD using XRP through A2 offer
-
1104 env(pay(G1, A1, USD(10)),
-
1105 path(~USD),
-
1106 sendmax(XRP(11)),
-
1107 txflags(tfNoRippleDirect),
-
1108 ter(tecPATH_PARTIAL));
-
1109 env.close();
-
1110
-
1111 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
-
1112 env.close();
-
1113 }
-
1114
-
1115 // Testing payments A1 <-> G1 using offer from A2 frozen by currency
-
1116 // holder.
-
1117 {
-
1118 env(trust(A2, limit, tfSetFreeze));
-
1119 env.close();
-
1120
-
1121 // test: A1 can send USD using XRP through A2 offer
-
1122 env(pay(A1, G1, USD(10)),
-
1123 path(~USD),
-
1124 sendmax(XRP(11)),
-
1125 txflags(tfNoRippleDirect));
-
1126 env.close();
-
1127
-
1128 // test: G1 can send USD using XRP through A2 offer
-
1129 env(pay(G1, A1, USD(10)),
-
1130 path(~USD),
-
1131 sendmax(XRP(11)),
-
1132 txflags(tfNoRippleDirect));
-
1133 env.close();
-
1134
-
1135 env(trust(A2, limit, tfClearFreeze));
-
1136 env.close();
-
1137 }
-
1138
-
1139 // Testing payments A1 <-> G1 using offer from A2 deep frozen by
-
1140 // currency holder.
-
1141 if (features[featureDeepFreeze])
-
1142 {
-
1143 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
-
1144 env.close();
-
1145
-
1146 // test: A1 cannot send USD using XRP through A2 offer
-
1147 env(pay(A1, G1, USD(10)),
-
1148 path(~USD),
-
1149 sendmax(XRP(11)),
-
1150 txflags(tfNoRippleDirect),
-
1151 ter(tecPATH_PARTIAL));
-
1152 env.close();
+
1103 // Testing payments A1 <-> G1 using offer from A2 frozen by currency
+
1104 // holder.
+
1105 {
+
1106 env(trust(A2, limit, tfSetFreeze));
+
1107 env.close();
+
1108
+
1109 // test: A1 can send USD using XRP through A2 offer
+
1110 env(pay(A1, G1, USD(10)),
+
1111 path(~USD),
+
1112 sendmax(XRP(11)),
+
1113 txflags(tfNoRippleDirect));
+
1114 env.close();
+
1115
+
1116 // test: G1 can send USD using XRP through A2 offer
+
1117 env(pay(G1, A1, USD(10)),
+
1118 path(~USD),
+
1119 sendmax(XRP(11)),
+
1120 txflags(tfNoRippleDirect));
+
1121 env.close();
+
1122
+
1123 env(trust(A2, limit, tfClearFreeze));
+
1124 env.close();
+
1125 }
+
1126
+
1127 // Testing payments A1 <-> G1 using offer from A2 deep frozen by
+
1128 // currency holder.
+
1129 if (features[featureDeepFreeze])
+
1130 {
+
1131 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
+
1132 env.close();
+
1133
+
1134 // test: A1 cannot send USD using XRP through A2 offer
+
1135 env(pay(A1, G1, USD(10)),
+
1136 path(~USD),
+
1137 sendmax(XRP(11)),
+
1138 txflags(tfNoRippleDirect),
+
1139 ter(tecPATH_PARTIAL));
+
1140 env.close();
+
1141
+
1142 // test: G1 cannot send USD using XRP through A2 offer
+
1143 env(pay(G1, A1, USD(10)),
+
1144 path(~USD),
+
1145 sendmax(XRP(11)),
+
1146 txflags(tfNoRippleDirect),
+
1147 ter(tecPATH_PARTIAL));
+
1148 env.close();
+
1149
+
1150 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
+
1151 env.close();
+
1152 }
1153
-
1154 // test: G1 cannot send USD using XRP through A2 offer
-
1155 env(pay(G1, A1, USD(10)),
-
1156 path(~USD),
-
1157 sendmax(XRP(11)),
-
1158 txflags(tfNoRippleDirect),
-
1159 ter(tecPATH_PARTIAL));
-
1160 env.close();
+
1154 // Cleanup
+
1155 env(offer_cancel(A1, env.seq(A1) - 1));
+
1156 env.require(offers(A1, 0));
+
1157 env.close();
+
1158
+
1159 env(offer(A2, USD(100), XRP(100)), txflags(tfPassive));
+
1160 env.close();
1161
-
1162 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
-
1163 env.close();
-
1164 }
-
1165
-
1166 // Cleanup
-
1167 env(offer_cancel(A1, env.seq(A1) - 1));
-
1168 env.require(offers(A1, 0));
-
1169 env.close();
-
1170
-
1171 env(offer(A2, USD(100), XRP(100)), txflags(tfPassive));
-
1172 env.close();
+
1162 // Testing payments A1 <-> G1 using offer from A2 frozen by issuer.
+
1163 {
+
1164 env(trust(G1, A2["USD"](0), tfSetFreeze));
+
1165 env.close();
+
1166
+
1167 // test: A1 can send XRP using USD through A2 offer
+
1168 env(pay(A1, G1, XRP(10)),
+
1169 path(~XRP),
+
1170 sendmax(USD(11)),
+
1171 txflags(tfNoRippleDirect));
+
1172 env.close();
1173
-
1174 // Testing payments A1 <-> G1 using offer from A2 frozen by issuer.
-
1175 {
-
1176 env(trust(G1, A2["USD"](0), tfSetFreeze));
-
1177 env.close();
-
1178
-
1179 // test: A1 can send XRP using USD through A2 offer
-
1180 env(pay(A1, G1, XRP(10)),
-
1181 path(~XRP),
-
1182 sendmax(USD(11)),
-
1183 txflags(tfNoRippleDirect));
-
1184 env.close();
-
1185
-
1186 // test: G1 can send XRP using USD through A2 offer
-
1187 env(pay(G1, A1, XRP(10)),
-
1188 path(~XRP),
-
1189 sendmax(USD(11)),
-
1190 txflags(tfNoRippleDirect));
-
1191 env.close();
-
1192
-
1193 env(trust(G1, A2["USD"](0), tfClearFreeze));
-
1194 env.close();
-
1195 }
-
1196
-
1197 // Testing payments A1 <-> G1 using offer from A2 deep frozen by
-
1198 // issuer.
-
1199 if (features[featureDeepFreeze])
-
1200 {
-
1201 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1202 env.close();
-
1203
-
1204 // test: A1 cannot send XRP using USD through A2 offer
-
1205 env(pay(A1, G1, XRP(10)),
-
1206 path(~XRP),
-
1207 sendmax(USD(11)),
-
1208 txflags(tfNoRippleDirect),
-
1209 ter(tecPATH_PARTIAL));
-
1210 env.close();
+
1174 // test: G1 can send XRP using USD through A2 offer
+
1175 env(pay(G1, A1, XRP(10)),
+
1176 path(~XRP),
+
1177 sendmax(USD(11)),
+
1178 txflags(tfNoRippleDirect));
+
1179 env.close();
+
1180
+
1181 env(trust(G1, A2["USD"](0), tfClearFreeze));
+
1182 env.close();
+
1183 }
+
1184
+
1185 // Testing payments A1 <-> G1 using offer from A2 deep frozen by
+
1186 // issuer.
+
1187 if (features[featureDeepFreeze])
+
1188 {
+
1189 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1190 env.close();
+
1191
+
1192 // test: A1 cannot send XRP using USD through A2 offer
+
1193 env(pay(A1, G1, XRP(10)),
+
1194 path(~XRP),
+
1195 sendmax(USD(11)),
+
1196 txflags(tfNoRippleDirect),
+
1197 ter(tecPATH_PARTIAL));
+
1198 env.close();
+
1199
+
1200 // test: G1 cannot send XRP using USD through A2 offer
+
1201 env(pay(G1, A1, XRP(10)),
+
1202 path(~XRP),
+
1203 sendmax(USD(11)),
+
1204 txflags(tfNoRippleDirect),
+
1205 ter(tecPATH_PARTIAL));
+
1206 env.close();
+
1207
+
1208 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1209 env.close();
+
1210 }
1211
-
1212 // test: G1 cannot send XRP using USD through A2 offer
-
1213 env(pay(G1, A1, XRP(10)),
-
1214 path(~XRP),
-
1215 sendmax(USD(11)),
-
1216 txflags(tfNoRippleDirect),
-
1217 ter(tecPATH_PARTIAL));
-
1218 env.close();
-
1219
-
1220 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
-
1221 env.close();
-
1222 }
-
1223
-
1224 // Testing payments A1 <-> G1 using offer from A2 frozen by currency
-
1225 // holder.
-
1226 {
-
1227 env(trust(A2, limit, tfSetFreeze));
-
1228 env.close();
-
1229
-
1230 // test: A1 can send XRP using USD through A2 offer
-
1231 env(pay(A1, G1, XRP(10)),
-
1232 path(~XRP),
-
1233 sendmax(USD(11)),
-
1234 txflags(tfNoRippleDirect));
-
1235 env.close();
-
1236
-
1237 // test: G1 can send XRP using USD through A2 offer
-
1238 env(pay(G1, A1, XRP(10)),
-
1239 path(~XRP),
-
1240 sendmax(USD(11)),
-
1241 txflags(tfNoRippleDirect));
-
1242 env.close();
-
1243
-
1244 env(trust(A2, limit, tfClearFreeze));
-
1245 env.close();
-
1246 }
-
1247
-
1248 // Testing payments A1 <-> G1 using offer from A2 deep frozen by
-
1249 // currency holder.
-
1250 if (features[featureDeepFreeze])
-
1251 {
-
1252 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
-
1253 env.close();
-
1254
-
1255 // test: A1 cannot send XRP using USD through A2 offer
-
1256 env(pay(A1, G1, XRP(10)),
-
1257 path(~XRP),
-
1258 sendmax(USD(11)),
-
1259 txflags(tfNoRippleDirect),
-
1260 ter(tecPATH_PARTIAL));
-
1261 env.close();
+
1212 // Testing payments A1 <-> G1 using offer from A2 frozen by currency
+
1213 // holder.
+
1214 {
+
1215 env(trust(A2, limit, tfSetFreeze));
+
1216 env.close();
+
1217
+
1218 // test: A1 can send XRP using USD through A2 offer
+
1219 env(pay(A1, G1, XRP(10)),
+
1220 path(~XRP),
+
1221 sendmax(USD(11)),
+
1222 txflags(tfNoRippleDirect));
+
1223 env.close();
+
1224
+
1225 // test: G1 can send XRP using USD through A2 offer
+
1226 env(pay(G1, A1, XRP(10)),
+
1227 path(~XRP),
+
1228 sendmax(USD(11)),
+
1229 txflags(tfNoRippleDirect));
+
1230 env.close();
+
1231
+
1232 env(trust(A2, limit, tfClearFreeze));
+
1233 env.close();
+
1234 }
+
1235
+
1236 // Testing payments A1 <-> G1 using offer from A2 deep frozen by
+
1237 // currency holder.
+
1238 if (features[featureDeepFreeze])
+
1239 {
+
1240 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
+
1241 env.close();
+
1242
+
1243 // test: A1 cannot send XRP using USD through A2 offer
+
1244 env(pay(A1, G1, XRP(10)),
+
1245 path(~XRP),
+
1246 sendmax(USD(11)),
+
1247 txflags(tfNoRippleDirect),
+
1248 ter(tecPATH_PARTIAL));
+
1249 env.close();
+
1250
+
1251 // test: G1 cannot send XRP using USD through A2 offer
+
1252 env(pay(G1, A1, XRP(10)),
+
1253 path(~XRP),
+
1254 sendmax(USD(11)),
+
1255 txflags(tfNoRippleDirect),
+
1256 ter(tecPATH_PARTIAL));
+
1257 env.close();
+
1258
+
1259 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
+
1260 env.close();
+
1261 }
1262
-
1263 // test: G1 cannot send XRP using USD through A2 offer
-
1264 env(pay(G1, A1, XRP(10)),
-
1265 path(~XRP),
-
1266 sendmax(USD(11)),
-
1267 txflags(tfNoRippleDirect),
-
1268 ter(tecPATH_PARTIAL));
-
1269 env.close();
-
1270
-
1271 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
-
1272 env.close();
-
1273 }
-
1274
-
1275 // Cleanup
-
1276 env(offer_cancel(A1, env.seq(A1) - 1));
-
1277 env.require(offers(A1, 0));
-
1278 env.close();
-
1279 }
-
1280
-
1281 void
-
1282 testPaymentsWhenDeepFrozen(FeatureBitset features)
-
1283 {
-
1284 testcase("Direct payments on frozen trust lines");
-
1285
-
1286 using namespace test::jtx;
-
1287 Env env(*this, features);
+
1263 // Cleanup
+
1264 env(offer_cancel(A1, env.seq(A1) - 1));
+
1265 env.require(offers(A1, 0));
+
1266 env.close();
+
1267 }
+
1268
+
1269 void
+
1270 testPaymentsWhenDeepFrozen(FeatureBitset features)
+
1271 {
+
1272 testcase("Direct payments on frozen trust lines");
+
1273
+
1274 using namespace test::jtx;
+
1275 Env env(*this, features);
+
1276
+
1277 Account G1{"G1"};
+
1278 Account A1{"A1"};
+
1279 Account A2{"A2"};
+
1280 auto const USD{G1["USD"]};
+
1281
+
1282 env.fund(XRP(10000), G1, A1, A2);
+
1283 env.close();
+
1284
+
1285 auto const limit = USD(10000);
+
1286 env.trust(limit, A1, A2);
+
1287 env.close();
1288
-
1289 Account G1{"G1"};
-
1290 Account A1{"A1"};
-
1291 Account A2{"A2"};
-
1292 auto const USD{G1["USD"]};
-
1293
-
1294 env.fund(XRP(10000), G1, A1, A2);
-
1295 env.close();
-
1296
-
1297 auto const limit = USD(10000);
-
1298 env.trust(limit, A1, A2);
-
1299 env.close();
-
1300
-
1301 env(pay(G1, A1, USD(1000)));
-
1302 env(pay(G1, A2, USD(1000)));
-
1303 env.close();
-
1304
-
1305 // Checking payments before freeze
-
1306 // To issuer:
-
1307 env(pay(A1, G1, USD(1)));
-
1308 env(pay(A2, G1, USD(1)));
-
1309 env.close();
-
1310
-
1311 // To each other:
-
1312 env(pay(A1, A2, USD(1)));
-
1313 env(pay(A2, A1, USD(1)));
-
1314 env.close();
+
1289 env(pay(G1, A1, USD(1000)));
+
1290 env(pay(G1, A2, USD(1000)));
+
1291 env.close();
+
1292
+
1293 // Checking payments before freeze
+
1294 // To issuer:
+
1295 env(pay(A1, G1, USD(1)));
+
1296 env(pay(A2, G1, USD(1)));
+
1297 env.close();
+
1298
+
1299 // To each other:
+
1300 env(pay(A1, A2, USD(1)));
+
1301 env(pay(A2, A1, USD(1)));
+
1302 env.close();
+
1303
+
1304 // Freeze A1
+
1305 env(trust(G1, A1["USD"](0), tfSetFreeze));
+
1306 env.close();
+
1307
+
1308 // Issuer and A1 can send payments to each other
+
1309 env(pay(A1, G1, USD(1)));
+
1310 env(pay(G1, A1, USD(1)));
+
1311 env.close();
+
1312
+
1313 // A1 cannot send tokens to A2
+
1314 env(pay(A1, A2, USD(1)), ter(tecPATH_DRY));
1315
-
1316 // Freeze A1
-
1317 env(trust(G1, A1["USD"](0), tfSetFreeze));
+
1316 // A2 can still send to A1
+
1317 env(pay(A2, A1, USD(1)));
1318 env.close();
1319
-
1320 // Issuer and A1 can send payments to each other
-
1321 env(pay(A1, G1, USD(1)));
-
1322 env(pay(G1, A1, USD(1)));
-
1323 env.close();
-
1324
-
1325 // A1 cannot send tokens to A2
-
1326 env(pay(A1, A2, USD(1)), ter(tecPATH_DRY));
-
1327
-
1328 // A2 can still send to A1
-
1329 env(pay(A2, A1, USD(1)));
-
1330 env.close();
-
1331
-
1332 if (features[featureDeepFreeze])
-
1333 {
-
1334 // Deep freeze A1
-
1335 env(trust(G1, A1["USD"](0), tfSetDeepFreeze));
-
1336 env.close();
-
1337
-
1338 // Issuer and A1 can send payments to each other
-
1339 env(pay(A1, G1, USD(1)));
-
1340 env(pay(G1, A1, USD(1)));
-
1341 env.close();
-
1342
-
1343 // A1 cannot send tokens to A2
-
1344 env(pay(A1, A2, USD(1)), ter(tecPATH_DRY));
+
1320 if (features[featureDeepFreeze])
+
1321 {
+
1322 // Deep freeze A1
+
1323 env(trust(G1, A1["USD"](0), tfSetDeepFreeze));
+
1324 env.close();
+
1325
+
1326 // Issuer and A1 can send payments to each other
+
1327 env(pay(A1, G1, USD(1)));
+
1328 env(pay(G1, A1, USD(1)));
+
1329 env.close();
+
1330
+
1331 // A1 cannot send tokens to A2
+
1332 env(pay(A1, A2, USD(1)), ter(tecPATH_DRY));
+
1333
+
1334 // A2 cannot send tokens to A1
+
1335 env(pay(A2, A1, USD(1)), ter(tecPATH_DRY));
+
1336
+
1337 // Clear deep freeze on A1
+
1338 env(trust(G1, A1["USD"](0), tfClearDeepFreeze));
+
1339 env.close();
+
1340 }
+
1341
+
1342 // Clear freeze on A1
+
1343 env(trust(G1, A1["USD"](0), tfClearFreeze));
+
1344 env.close();
1345
-
1346 // A2 cannot send tokens to A1
-
1347 env(pay(A2, A1, USD(1)), ter(tecPATH_DRY));
-
1348
-
1349 // Clear deep freeze on A1
-
1350 env(trust(G1, A1["USD"](0), tfClearDeepFreeze));
-
1351 env.close();
-
1352 }
-
1353
-
1354 // Clear freeze on A1
-
1355 env(trust(G1, A1["USD"](0), tfClearFreeze));
-
1356 env.close();
-
1357
-
1358 // A1 freezes trust line
-
1359 env(trust(A1, limit, tfSetFreeze));
+
1346 // A1 freezes trust line
+
1347 env(trust(A1, limit, tfSetFreeze));
+
1348 env.close();
+
1349
+
1350 // Issuer and A2 must not be affected
+
1351 env(pay(A2, G1, USD(1)));
+
1352 env(pay(G1, A2, USD(1)));
+
1353 env.close();
+
1354
+
1355 // A1 can send tokens to the issuer
+
1356 env(pay(A1, G1, USD(1)));
+
1357 env.close();
+
1358 // A1 can send tokens to A2
+
1359 env(pay(A1, A2, USD(1)));
1360 env.close();
1361
-
1362 // Issuer and A2 must not be affected
-
1363 env(pay(A2, G1, USD(1)));
-
1364 env(pay(G1, A2, USD(1)));
-
1365 env.close();
+
1362 // Issuer can sent tokens to A1
+
1363 env(pay(G1, A1, USD(1)));
+
1364 // A2 cannot send tokens to A1
+
1365 env(pay(A2, A1, USD(1)), ter(tecPATH_DRY));
1366
-
1367 // A1 can send tokens to the issuer
-
1368 env(pay(A1, G1, USD(1)));
-
1369 env.close();
-
1370 // A1 can send tokens to A2
-
1371 env(pay(A1, A2, USD(1)));
-
1372 env.close();
-
1373
-
1374 // Issuer can sent tokens to A1
-
1375 env(pay(G1, A1, USD(1)));
-
1376 // A2 cannot send tokens to A1
-
1377 env(pay(A2, A1, USD(1)), ter(tecPATH_DRY));
-
1378
-
1379 if (features[featureDeepFreeze])
-
1380 {
-
1381 // A1 deep freezes trust line
-
1382 env(trust(A1, limit, tfSetDeepFreeze));
-
1383 env.close();
-
1384
-
1385 // Issuer and A2 must not be affected
-
1386 env(pay(A2, G1, USD(1)));
-
1387 env(pay(G1, A2, USD(1)));
-
1388 env.close();
-
1389
-
1390 // A1 can still send token to issuer
-
1391 env(pay(A1, G1, USD(1)));
-
1392 env.close();
-
1393
-
1394 // Issuer can send tokens to A1
-
1395 env(pay(G1, A1, USD(1)));
-
1396 // A2 cannot send tokens to A1
-
1397 env(pay(A2, A1, USD(1)), ter(tecPATH_DRY));
-
1398 // A1 cannot send tokens to A2
-
1399 env(pay(A1, A2, USD(1)), ter(tecPATH_DRY));
-
1400 }
-
1401 }
-
1402
-
1403 void
-
1404 testChecksWhenFrozen(FeatureBitset features)
-
1405 {
-
1406 testcase("Checks on frozen trust lines");
-
1407
-
1408 using namespace test::jtx;
-
1409 Env env(*this, features);
+
1367 if (features[featureDeepFreeze])
+
1368 {
+
1369 // A1 deep freezes trust line
+
1370 env(trust(A1, limit, tfSetDeepFreeze));
+
1371 env.close();
+
1372
+
1373 // Issuer and A2 must not be affected
+
1374 env(pay(A2, G1, USD(1)));
+
1375 env(pay(G1, A2, USD(1)));
+
1376 env.close();
+
1377
+
1378 // A1 can still send token to issuer
+
1379 env(pay(A1, G1, USD(1)));
+
1380 env.close();
+
1381
+
1382 // Issuer can send tokens to A1
+
1383 env(pay(G1, A1, USD(1)));
+
1384 // A2 cannot send tokens to A1
+
1385 env(pay(A2, A1, USD(1)), ter(tecPATH_DRY));
+
1386 // A1 cannot send tokens to A2
+
1387 env(pay(A1, A2, USD(1)), ter(tecPATH_DRY));
+
1388 }
+
1389 }
+
1390
+
1391 void
+
1392 testChecksWhenFrozen(FeatureBitset features)
+
1393 {
+
1394 testcase("Checks on frozen trust lines");
+
1395
+
1396 using namespace test::jtx;
+
1397 Env env(*this, features);
+
1398
+
1399 Account G1{"G1"};
+
1400 Account A1{"A1"};
+
1401 Account A2{"A2"};
+
1402 auto const USD{G1["USD"]};
+
1403
+
1404 env.fund(XRP(10000), G1, A1, A2);
+
1405 env.close();
+
1406
+
1407 auto const limit = USD(10000);
+
1408 env.trust(limit, A1, A2);
+
1409 env.close();
1410
-
1411 Account G1{"G1"};
-
1412 Account A1{"A1"};
-
1413 Account A2{"A2"};
-
1414 auto const USD{G1["USD"]};
-
1415
-
1416 env.fund(XRP(10000), G1, A1, A2);
-
1417 env.close();
-
1418
-
1419 auto const limit = USD(10000);
-
1420 env.trust(limit, A1, A2);
-
1421 env.close();
-
1422
-
1423 env(pay(G1, A1, USD(1000)));
-
1424 env(pay(G1, A2, USD(1000)));
-
1425 env.close();
-
1426
-
1427 // Confirming we can write and cash checks
-
1428 {
-
1429 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
-
1430 env(check::create(G1, A1, USD(10)));
-
1431 env.close();
-
1432 env(check::cash(A1, checkId, USD(10)));
-
1433 env.close();
-
1434 }
-
1435
-
1436 {
-
1437 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
-
1438 env(check::create(G1, A2, USD(10)));
-
1439 env.close();
-
1440 env(check::cash(A2, checkId, USD(10)));
-
1441 env.close();
-
1442 }
-
1443
-
1444 {
-
1445 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
-
1446 env(check::create(A1, G1, USD(10)));
-
1447 env.close();
-
1448 env(check::cash(G1, checkId, USD(10)));
-
1449 env.close();
-
1450 }
-
1451
-
1452 {
-
1453 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
-
1454 env(check::create(A1, A2, USD(10)));
-
1455 env.close();
-
1456 env(check::cash(A2, checkId, USD(10)));
-
1457 env.close();
-
1458 }
-
1459
-
1460 {
-
1461 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
-
1462 env(check::create(A2, G1, USD(10)));
-
1463 env.close();
-
1464 env(check::cash(G1, checkId, USD(10)));
-
1465 env.close();
-
1466 }
-
1467
-
1468 {
-
1469 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
-
1470 env(check::create(A2, A1, USD(10)));
-
1471 env.close();
-
1472 env(check::cash(A1, checkId, USD(10)));
-
1473 env.close();
-
1474 }
-
1475
-
1476 // Testing creation and cashing of checks on a trustline frozen by
-
1477 // issuer
-
1478 {
-
1479 env(trust(G1, A1["USD"](0), tfSetFreeze));
-
1480 env.close();
-
1481
-
1482 // test: issuer writes check to A1.
-
1483 {
-
1484 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
-
1485 env(check::create(G1, A1, USD(10)));
+
1411 env(pay(G1, A1, USD(1000)));
+
1412 env(pay(G1, A2, USD(1000)));
+
1413 env.close();
+
1414
+
1415 // Confirming we can write and cash checks
+
1416 {
+
1417 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
+
1418 env(check::create(G1, A1, USD(10)));
+
1419 env.close();
+
1420 env(check::cash(A1, checkId, USD(10)));
+
1421 env.close();
+
1422 }
+
1423
+
1424 {
+
1425 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
+
1426 env(check::create(G1, A2, USD(10)));
+
1427 env.close();
+
1428 env(check::cash(A2, checkId, USD(10)));
+
1429 env.close();
+
1430 }
+
1431
+
1432 {
+
1433 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
+
1434 env(check::create(A1, G1, USD(10)));
+
1435 env.close();
+
1436 env(check::cash(G1, checkId, USD(10)));
+
1437 env.close();
+
1438 }
+
1439
+
1440 {
+
1441 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
+
1442 env(check::create(A1, A2, USD(10)));
+
1443 env.close();
+
1444 env(check::cash(A2, checkId, USD(10)));
+
1445 env.close();
+
1446 }
+
1447
+
1448 {
+
1449 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
+
1450 env(check::create(A2, G1, USD(10)));
+
1451 env.close();
+
1452 env(check::cash(G1, checkId, USD(10)));
+
1453 env.close();
+
1454 }
+
1455
+
1456 {
+
1457 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
+
1458 env(check::create(A2, A1, USD(10)));
+
1459 env.close();
+
1460 env(check::cash(A1, checkId, USD(10)));
+
1461 env.close();
+
1462 }
+
1463
+
1464 // Testing creation and cashing of checks on a trustline frozen by
+
1465 // issuer
+
1466 {
+
1467 env(trust(G1, A1["USD"](0), tfSetFreeze));
+
1468 env.close();
+
1469
+
1470 // test: issuer writes check to A1.
+
1471 {
+
1472 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
+
1473 env(check::create(G1, A1, USD(10)));
+
1474 env.close();
+
1475 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
+
1476 env.close();
+
1477 }
+
1478
+
1479 // test: A2 writes check to A1.
+
1480 {
+
1481 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
+
1482 env(check::create(A2, A1, USD(10)));
+
1483 env.close();
+
1484 // Same as previous test
+
1485 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
1486 env.close();
-
1487 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
-
1488 env.close();
-
1489 }
-
1490
-
1491 // test: A2 writes check to A1.
-
1492 {
-
1493 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
-
1494 env(check::create(A2, A1, USD(10)));
-
1495 env.close();
-
1496 // Same as previous test
-
1497 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
-
1498 env.close();
-
1499 }
-
1500
-
1501 // test: A1 writes check to issuer
-
1502 {
-
1503 env(check::create(A1, G1, USD(10)), ter(tecFROZEN));
-
1504 env.close();
-
1505 }
+
1487 }
+
1488
+
1489 // test: A1 writes check to issuer
+
1490 {
+
1491 env(check::create(A1, G1, USD(10)), ter(tecFROZEN));
+
1492 env.close();
+
1493 }
+
1494
+
1495 // test: A1 writes check to A2
+
1496 {
+
1497 // Same as previous test
+
1498 env(check::create(A1, A2, USD(10)), ter(tecFROZEN));
+
1499 env.close();
+
1500 }
+
1501
+
1502 // Unfreeze the trustline to create a couple of checks so that we
+
1503 // could try to cash them later when the trustline is frozen again.
+
1504 env(trust(G1, A1["USD"](0), tfClearFreeze));
+
1505 env.close();
1506
-
1507 // test: A1 writes check to A2
-
1508 {
-
1509 // Same as previous test
-
1510 env(check::create(A1, A2, USD(10)), ter(tecFROZEN));
-
1511 env.close();
-
1512 }
+
1507 uint256 const checkId1{getCheckIndex(A1, env.seq(A1))};
+
1508 env(check::create(A1, G1, USD(10)));
+
1509 env.close();
+
1510 uint256 const checkId2{getCheckIndex(A1, env.seq(A1))};
+
1511 env(check::create(A1, A2, USD(10)));
+
1512 env.close();
1513
-
1514 // Unfreeze the trustline to create a couple of checks so that we
-
1515 // could try to cash them later when the trustline is frozen again.
-
1516 env(trust(G1, A1["USD"](0), tfClearFreeze));
-
1517 env.close();
-
1518
-
1519 uint256 const checkId1{getCheckIndex(A1, env.seq(A1))};
-
1520 env(check::create(A1, G1, USD(10)));
-
1521 env.close();
-
1522 uint256 const checkId2{getCheckIndex(A1, env.seq(A1))};
-
1523 env(check::create(A1, A2, USD(10)));
-
1524 env.close();
-
1525
-
1526 env(trust(G1, A1["USD"](0), tfSetFreeze));
-
1527 env.close();
+
1514 env(trust(G1, A1["USD"](0), tfSetFreeze));
+
1515 env.close();
+
1516
+
1517 // test: issuer tries to cash the check from A1
+
1518 {
+
1519 env(check::cash(G1, checkId1, USD(10)), ter(tecPATH_PARTIAL));
+
1520 env.close();
+
1521 }
+
1522
+
1523 // test: A2 tries to cash the check from A1
+
1524 {
+
1525 env(check::cash(A2, checkId2, USD(10)), ter(tecPATH_PARTIAL));
+
1526 env.close();
+
1527 }
1528
-
1529 // test: issuer tries to cash the check from A1
-
1530 {
-
1531 env(check::cash(G1, checkId1, USD(10)), ter(tecPATH_PARTIAL));
-
1532 env.close();
-
1533 }
-
1534
-
1535 // test: A2 tries to cash the check from A1
-
1536 {
-
1537 env(check::cash(A2, checkId2, USD(10)), ter(tecPATH_PARTIAL));
-
1538 env.close();
-
1539 }
-
1540
-
1541 env(trust(G1, A1["USD"](0), tfClearFreeze));
-
1542 env.close();
-
1543 }
-
1544
-
1545 // Testing creation and cashing of checks on a trustline deep frozen by
-
1546 // issuer
-
1547 if (features[featureDeepFreeze])
-
1548 {
-
1549 env(trust(G1, A1["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1550 env.close();
-
1551
-
1552 // test: issuer writes check to A1.
-
1553 {
-
1554 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
-
1555 env(check::create(G1, A1, USD(10)));
-
1556 env.close();
-
1557
-
1558 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
-
1559 env.close();
-
1560 }
-
1561
-
1562 // test: A2 writes check to A1.
-
1563 {
-
1564 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
-
1565 env(check::create(A2, A1, USD(10)));
-
1566 env.close();
-
1567 // Same as previous test
-
1568 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
-
1569 env.close();
-
1570 }
-
1571
-
1572 // test: A1 writes check to issuer
-
1573 {
-
1574 env(check::create(A1, G1, USD(10)), ter(tecFROZEN));
-
1575 env.close();
-
1576 }
+
1529 env(trust(G1, A1["USD"](0), tfClearFreeze));
+
1530 env.close();
+
1531 }
+
1532
+
1533 // Testing creation and cashing of checks on a trustline deep frozen by
+
1534 // issuer
+
1535 if (features[featureDeepFreeze])
+
1536 {
+
1537 env(trust(G1, A1["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1538 env.close();
+
1539
+
1540 // test: issuer writes check to A1.
+
1541 {
+
1542 uint256 const checkId{getCheckIndex(G1, env.seq(G1))};
+
1543 env(check::create(G1, A1, USD(10)));
+
1544 env.close();
+
1545
+
1546 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
+
1547 env.close();
+
1548 }
+
1549
+
1550 // test: A2 writes check to A1.
+
1551 {
+
1552 uint256 const checkId{getCheckIndex(A2, env.seq(A2))};
+
1553 env(check::create(A2, A1, USD(10)));
+
1554 env.close();
+
1555 // Same as previous test
+
1556 env(check::cash(A1, checkId, USD(10)), ter(tecFROZEN));
+
1557 env.close();
+
1558 }
+
1559
+
1560 // test: A1 writes check to issuer
+
1561 {
+
1562 env(check::create(A1, G1, USD(10)), ter(tecFROZEN));
+
1563 env.close();
+
1564 }
+
1565
+
1566 // test: A1 writes check to A2
+
1567 {
+
1568 // Same as previous test
+
1569 env(check::create(A1, A2, USD(10)), ter(tecFROZEN));
+
1570 env.close();
+
1571 }
+
1572
+
1573 // Unfreeze the trustline to create a couple of checks so that we
+
1574 // could try to cash them later when the trustline is frozen again.
+
1575 env(trust(G1, A1["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1576 env.close();
1577
-
1578 // test: A1 writes check to A2
-
1579 {
-
1580 // Same as previous test
-
1581 env(check::create(A1, A2, USD(10)), ter(tecFROZEN));
-
1582 env.close();
-
1583 }
+
1578 uint256 const checkId1{getCheckIndex(A1, env.seq(A1))};
+
1579 env(check::create(A1, G1, USD(10)));
+
1580 env.close();
+
1581 uint256 const checkId2{getCheckIndex(A1, env.seq(A1))};
+
1582 env(check::create(A1, A2, USD(10)));
+
1583 env.close();
1584
-
1585 // Unfreeze the trustline to create a couple of checks so that we
-
1586 // could try to cash them later when the trustline is frozen again.
-
1587 env(trust(G1, A1["USD"](0), tfClearFreeze | tfClearDeepFreeze));
-
1588 env.close();
-
1589
-
1590 uint256 const checkId1{getCheckIndex(A1, env.seq(A1))};
-
1591 env(check::create(A1, G1, USD(10)));
-
1592 env.close();
-
1593 uint256 const checkId2{getCheckIndex(A1, env.seq(A1))};
-
1594 env(check::create(A1, A2, USD(10)));
-
1595 env.close();
-
1596
-
1597 env(trust(G1, A1["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1598 env.close();
+
1585 env(trust(G1, A1["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1586 env.close();
+
1587
+
1588 // test: issuer tries to cash the check from A1
+
1589 {
+
1590 env(check::cash(G1, checkId1, USD(10)), ter(tecPATH_PARTIAL));
+
1591 env.close();
+
1592 }
+
1593
+
1594 // test: A2 tries to cash the check from A1
+
1595 {
+
1596 env(check::cash(A2, checkId2, USD(10)), ter(tecPATH_PARTIAL));
+
1597 env.close();
+
1598 }
1599
-
1600 // test: issuer tries to cash the check from A1
-
1601 {
-
1602 env(check::cash(G1, checkId1, USD(10)), ter(tecPATH_PARTIAL));
-
1603 env.close();
-
1604 }
-
1605
-
1606 // test: A2 tries to cash the check from A1
-
1607 {
-
1608 env(check::cash(A2, checkId2, USD(10)), ter(tecPATH_PARTIAL));
-
1609 env.close();
-
1610 }
-
1611
-
1612 env(trust(G1, A1["USD"](0), tfClearFreeze | tfClearDeepFreeze));
-
1613 env.close();
-
1614 }
+
1600 env(trust(G1, A1["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1601 env.close();
+
1602 }
+
1603
+
1604 // Testing creation and cashing of checks on a trustline frozen by
+
1605 // a currency holder
+
1606 {
+
1607 env(trust(A1, limit, tfSetFreeze));
+
1608 env.close();
+
1609
+
1610 // test: issuer writes check to A1.
+
1611 {
+
1612 env(check::create(G1, A1, USD(10)), ter(tecFROZEN));
+
1613 env.close();
+
1614 }
1615
-
1616 // Testing creation and cashing of checks on a trustline frozen by
-
1617 // a currency holder
-
1618 {
-
1619 env(trust(A1, limit, tfSetFreeze));
-
1620 env.close();
+
1616 // test: A2 writes check to A1.
+
1617 {
+
1618 env(check::create(A2, A1, USD(10)), ter(tecFROZEN));
+
1619 env.close();
+
1620 }
1621
-
1622 // test: issuer writes check to A1.
+
1622 // test: A1 writes check to issuer
1623 {
-
1624 env(check::create(G1, A1, USD(10)), ter(tecFROZEN));
-
1625 env.close();
-
1626 }
-
1627
-
1628 // test: A2 writes check to A1.
-
1629 {
-
1630 env(check::create(A2, A1, USD(10)), ter(tecFROZEN));
-
1631 env.close();
-
1632 }
-
1633
-
1634 // test: A1 writes check to issuer
-
1635 {
-
1636 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
-
1637 env(check::create(A1, G1, USD(10)));
-
1638 env.close();
-
1639 env(check::cash(G1, checkId, USD(10)));
-
1640 env.close();
-
1641 }
-
1642
-
1643 // test: A1 writes check to A2
-
1644 {
-
1645 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
-
1646 env(check::create(A1, A2, USD(10)));
-
1647 env.close();
-
1648 env(check::cash(A2, checkId, USD(10)));
-
1649 env.close();
-
1650 }
-
1651
-
1652 env(trust(A1, limit, tfClearFreeze));
-
1653 env.close();
-
1654 }
-
1655
-
1656 // Testing creation and cashing of checks on a trustline deep frozen by
-
1657 // a currency holder
-
1658 if (features[featureDeepFreeze])
-
1659 {
-
1660 env(trust(A1, limit, tfSetFreeze | tfSetDeepFreeze));
-
1661 env.close();
+
1624 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
+
1625 env(check::create(A1, G1, USD(10)));
+
1626 env.close();
+
1627 env(check::cash(G1, checkId, USD(10)));
+
1628 env.close();
+
1629 }
+
1630
+
1631 // test: A1 writes check to A2
+
1632 {
+
1633 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
+
1634 env(check::create(A1, A2, USD(10)));
+
1635 env.close();
+
1636 env(check::cash(A2, checkId, USD(10)));
+
1637 env.close();
+
1638 }
+
1639
+
1640 env(trust(A1, limit, tfClearFreeze));
+
1641 env.close();
+
1642 }
+
1643
+
1644 // Testing creation and cashing of checks on a trustline deep frozen by
+
1645 // a currency holder
+
1646 if (features[featureDeepFreeze])
+
1647 {
+
1648 env(trust(A1, limit, tfSetFreeze | tfSetDeepFreeze));
+
1649 env.close();
+
1650
+
1651 // test: issuer writes check to A1.
+
1652 {
+
1653 env(check::create(G1, A1, USD(10)), ter(tecFROZEN));
+
1654 env.close();
+
1655 }
+
1656
+
1657 // test: A2 writes check to A1.
+
1658 {
+
1659 env(check::create(A2, A1, USD(10)), ter(tecFROZEN));
+
1660 env.close();
+
1661 }
1662
-
1663 // test: issuer writes check to A1.
+
1663 // test: A1 writes check to issuer
1664 {
-
1665 env(check::create(G1, A1, USD(10)), ter(tecFROZEN));
-
1666 env.close();
-
1667 }
-
1668
-
1669 // test: A2 writes check to A1.
-
1670 {
-
1671 env(check::create(A2, A1, USD(10)), ter(tecFROZEN));
-
1672 env.close();
-
1673 }
-
1674
-
1675 // test: A1 writes check to issuer
-
1676 {
-
1677 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
-
1678 env(check::create(A1, G1, USD(10)));
-
1679 env.close();
-
1680 env(check::cash(G1, checkId, USD(10)), ter(tecPATH_PARTIAL));
-
1681 env.close();
-
1682 }
-
1683
-
1684 // test: A1 writes check to A2
-
1685 {
-
1686 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
-
1687 env(check::create(A1, A2, USD(10)));
-
1688 env.close();
-
1689 env(check::cash(A2, checkId, USD(10)), ter(tecPATH_PARTIAL));
-
1690 env.close();
-
1691 }
+
1665 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
+
1666 env(check::create(A1, G1, USD(10)));
+
1667 env.close();
+
1668 env(check::cash(G1, checkId, USD(10)), ter(tecPATH_PARTIAL));
+
1669 env.close();
+
1670 }
+
1671
+
1672 // test: A1 writes check to A2
+
1673 {
+
1674 uint256 const checkId{getCheckIndex(A1, env.seq(A1))};
+
1675 env(check::create(A1, A2, USD(10)));
+
1676 env.close();
+
1677 env(check::cash(A2, checkId, USD(10)), ter(tecPATH_PARTIAL));
+
1678 env.close();
+
1679 }
+
1680
+
1681 env(trust(A1, limit, tfClearFreeze | tfClearDeepFreeze));
+
1682 env.close();
+
1683 }
+
1684 }
+
1685
+
1686 void
+
1687 testAMMWhenFreeze(FeatureBitset features)
+
1688 {
+
1689 testcase("AMM payments on frozen trust lines");
+
1690 using namespace test::jtx;
+
1691 using path = test::jtx::path;
1692
-
1693 env(trust(A1, limit, tfClearFreeze | tfClearDeepFreeze));
-
1694 env.close();
-
1695 }
-
1696 }
-
1697
-
1698 void
-
1699 testAMMWhenFreeze(FeatureBitset features)
-
1700 {
-
1701 testcase("AMM payments on frozen trust lines");
-
1702 using namespace test::jtx;
-
1703 using path = test::jtx::path;
+
1693 Env env(*this, features);
+
1694 Account G1{"G1"};
+
1695 Account A1{"A1"};
+
1696 Account A2{"A2"};
+
1697 auto const USD{G1["USD"]};
+
1698
+
1699 env.fund(XRP(10000), G1, A1, A2);
+
1700 env.close();
+
1701
+
1702 env.trust(G1["USD"](10000), A1, A2);
+
1703 env.close();
1704
-
1705 Env env(*this, features);
-
1706 Account G1{"G1"};
-
1707 Account A1{"A1"};
-
1708 Account A2{"A2"};
-
1709 auto const USD{G1["USD"]};
-
1710
-
1711 env.fund(XRP(10000), G1, A1, A2);
-
1712 env.close();
-
1713
-
1714 env.trust(G1["USD"](10000), A1, A2);
-
1715 env.close();
+
1705 env(pay(G1, A1, USD(1000)));
+
1706 env(pay(G1, A2, USD(1000)));
+
1707 env.close();
+
1708
+
1709 AMM ammG1(env, G1, XRP(1'000), USD(1'000));
+
1710 env.close();
+
1711
+
1712 // Testing basic payment using AMM when freezing one of the trust lines.
+
1713 {
+
1714 env(trust(G1, A1["USD"](0), tfSetFreeze));
+
1715 env.close();
1716
-
1717 env(pay(G1, A1, USD(1000)));
-
1718 env(pay(G1, A2, USD(1000)));
-
1719 env.close();
-
1720
-
1721 AMM ammG1(env, G1, XRP(1'000), USD(1'000));
-
1722 env.close();
+
1717 // test: can still use XRP to make payment
+
1718 env(pay(A1, A2, USD(10)),
+
1719 path(~USD),
+
1720 sendmax(XRP(11)),
+
1721 txflags(tfNoRippleDirect));
+
1722 env.close();
1723
-
1724 // Testing basic payment using AMM when freezing one of the trust lines.
-
1725 {
-
1726 env(trust(G1, A1["USD"](0), tfSetFreeze));
-
1727 env.close();
-
1728
-
1729 // test: can still use XRP to make payment
-
1730 env(pay(A1, A2, USD(10)),
-
1731 path(~USD),
-
1732 sendmax(XRP(11)),
-
1733 txflags(tfNoRippleDirect));
-
1734 env.close();
-
1735
-
1736 // test: cannot use USD to make payment
-
1737 env(pay(A1, A2, XRP(10)),
-
1738 path(~XRP),
-
1739 sendmax(USD(11)),
-
1740 txflags(tfNoRippleDirect),
-
1741 ter(tecPATH_DRY));
-
1742 env.close();
-
1743
-
1744 // test: can still receive USD payments.
-
1745 env(pay(A2, A1, USD(10)),
-
1746 path(~USD),
-
1747 sendmax(XRP(11)),
-
1748 txflags(tfNoRippleDirect));
-
1749 env.close();
-
1750
-
1751 // test: can still receive XRP payments.
-
1752 env(pay(A2, A1, XRP(10)),
-
1753 path(~XRP),
-
1754 sendmax(USD(11)),
-
1755 txflags(tfNoRippleDirect));
-
1756 env.close();
-
1757
-
1758 env(trust(G1, A1["USD"](0), tfClearFreeze));
-
1759 env.close();
-
1760 }
-
1761
-
1762 // Testing basic payment using AMM when deep freezing one of the trust
-
1763 // lines.
-
1764 if (features[featureDeepFreeze])
-
1765 {
-
1766 env(trust(G1, A1["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1767 env.close();
-
1768
-
1769 // test: can still use XRP to make payment
-
1770 env(pay(A1, A2, USD(10)),
-
1771 path(~USD),
-
1772 sendmax(XRP(11)),
-
1773 txflags(tfNoRippleDirect));
-
1774 env.close();
-
1775
-
1776 // test: cannot use USD to make payment
-
1777 env(pay(A1, A2, XRP(10)),
-
1778 path(~XRP),
-
1779 sendmax(USD(11)),
-
1780 txflags(tfNoRippleDirect),
-
1781 ter(tecPATH_DRY));
-
1782 env.close();
-
1783
-
1784 // test: cannot receive USD payments.
-
1785 env(pay(A2, A1, USD(10)),
-
1786 path(~USD),
-
1787 sendmax(XRP(11)),
-
1788 txflags(tfNoRippleDirect),
-
1789 ter(tecPATH_DRY));
-
1790 env.close();
+
1724 // test: cannot use USD to make payment
+
1725 env(pay(A1, A2, XRP(10)),
+
1726 path(~XRP),
+
1727 sendmax(USD(11)),
+
1728 txflags(tfNoRippleDirect),
+
1729 ter(tecPATH_DRY));
+
1730 env.close();
+
1731
+
1732 // test: can still receive USD payments.
+
1733 env(pay(A2, A1, USD(10)),
+
1734 path(~USD),
+
1735 sendmax(XRP(11)),
+
1736 txflags(tfNoRippleDirect));
+
1737 env.close();
+
1738
+
1739 // test: can still receive XRP payments.
+
1740 env(pay(A2, A1, XRP(10)),
+
1741 path(~XRP),
+
1742 sendmax(USD(11)),
+
1743 txflags(tfNoRippleDirect));
+
1744 env.close();
+
1745
+
1746 env(trust(G1, A1["USD"](0), tfClearFreeze));
+
1747 env.close();
+
1748 }
+
1749
+
1750 // Testing basic payment using AMM when deep freezing one of the trust
+
1751 // lines.
+
1752 if (features[featureDeepFreeze])
+
1753 {
+
1754 env(trust(G1, A1["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1755 env.close();
+
1756
+
1757 // test: can still use XRP to make payment
+
1758 env(pay(A1, A2, USD(10)),
+
1759 path(~USD),
+
1760 sendmax(XRP(11)),
+
1761 txflags(tfNoRippleDirect));
+
1762 env.close();
+
1763
+
1764 // test: cannot use USD to make payment
+
1765 env(pay(A1, A2, XRP(10)),
+
1766 path(~XRP),
+
1767 sendmax(USD(11)),
+
1768 txflags(tfNoRippleDirect),
+
1769 ter(tecPATH_DRY));
+
1770 env.close();
+
1771
+
1772 // test: cannot receive USD payments.
+
1773 env(pay(A2, A1, USD(10)),
+
1774 path(~USD),
+
1775 sendmax(XRP(11)),
+
1776 txflags(tfNoRippleDirect),
+
1777 ter(tecPATH_DRY));
+
1778 env.close();
+
1779
+
1780 // test: can still receive XRP payments.
+
1781 env(pay(A2, A1, XRP(10)),
+
1782 path(~XRP),
+
1783 sendmax(USD(11)),
+
1784 txflags(tfNoRippleDirect));
+
1785 env.close();
+
1786
+
1787 env(trust(G1, A1["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1788 env.close();
+
1789 }
+
1790 }
1791
-
1792 // test: can still receive XRP payments.
-
1793 env(pay(A2, A1, XRP(10)),
-
1794 path(~XRP),
-
1795 sendmax(USD(11)),
-
1796 txflags(tfNoRippleDirect));
-
1797 env.close();
-
1798
-
1799 env(trust(G1, A1["USD"](0), tfClearFreeze | tfClearDeepFreeze));
-
1800 env.close();
-
1801 }
-
1802 }
+
1792 void
+
1793 testNFTOffersWhenFreeze(FeatureBitset features)
+
1794 {
+
1795 testcase("NFT offers on frozen trust lines");
+
1796 using namespace test::jtx;
+
1797
+
1798 Env env(*this, features);
+
1799 Account G1{"G1"};
+
1800 Account A1{"A1"};
+
1801 Account A2{"A2"};
+
1802 auto const USD{G1["USD"]};
1803
-
1804 void
-
1805 testNFTOffersWhenFreeze(FeatureBitset features)
-
1806 {
-
1807 testcase("NFT offers on frozen trust lines");
-
1808 using namespace test::jtx;
-
1809
-
1810 Env env(*this, features);
-
1811 Account G1{"G1"};
-
1812 Account A1{"A1"};
-
1813 Account A2{"A2"};
-
1814 auto const USD{G1["USD"]};
-
1815
-
1816 env.fund(XRP(10000), G1, A1, A2);
-
1817 env.close();
-
1818
-
1819 auto const limit = USD(10000);
-
1820 env.trust(limit, A1, A2);
-
1821 env.close();
-
1822
-
1823 env(pay(G1, A1, USD(1000)));
-
1824 env(pay(G1, A2, USD(1000)));
-
1825 env.close();
-
1826
-
1827 // Testing A2 nft offer sell when A2 frozen by issuer
-
1828 {
-
1829 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
-
1830 env(trust(G1, A2["USD"](0), tfSetFreeze));
-
1831 env.close();
-
1832
-
1833 // test: A2 can still receive USD for his NFT
-
1834 env(token::acceptSellOffer(A1, sellOfferIndex));
+
1804 env.fund(XRP(10000), G1, A1, A2);
+
1805 env.close();
+
1806
+
1807 auto const limit = USD(10000);
+
1808 env.trust(limit, A1, A2);
+
1809 env.close();
+
1810
+
1811 env(pay(G1, A1, USD(1000)));
+
1812 env(pay(G1, A2, USD(1000)));
+
1813 env.close();
+
1814
+
1815 // Testing A2 nft offer sell when A2 frozen by issuer
+
1816 {
+
1817 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
+
1818 env(trust(G1, A2["USD"](0), tfSetFreeze));
+
1819 env.close();
+
1820
+
1821 // test: A2 can still receive USD for his NFT
+
1822 env(token::acceptSellOffer(A1, sellOfferIndex));
+
1823 env.close();
+
1824
+
1825 env(trust(G1, A2["USD"](0), tfClearFreeze));
+
1826 env.close();
+
1827 }
+
1828
+
1829 // Testing A2 nft offer sell when A2 deep frozen by issuer
+
1830 if (features[featureDeepFreeze])
+
1831 {
+
1832 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
+
1833
+
1834 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
1835 env.close();
1836
-
1837 env(trust(G1, A2["USD"](0), tfClearFreeze));
-
1838 env.close();
-
1839 }
+
1837 // test: A2 cannot receive USD for his NFT
+
1838 env(token::acceptSellOffer(A1, sellOfferIndex), ter(tecFROZEN));
+
1839 env.close();
1840
-
1841 // Testing A2 nft offer sell when A2 deep frozen by issuer
-
1842 if (features[featureDeepFreeze])
-
1843 {
-
1844 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
-
1845
-
1846 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1847 env.close();
-
1848
-
1849 // test: A2 cannot receive USD for his NFT
-
1850 env(token::acceptSellOffer(A1, sellOfferIndex), ter(tecFROZEN));
-
1851 env.close();
-
1852
-
1853 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1841 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1842 env.close();
+
1843 }
+
1844
+
1845 // Testing A1 nft offer sell when A2 frozen by issuer
+
1846 {
+
1847 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
+
1848 env(trust(G1, A2["USD"](0), tfSetFreeze));
+
1849 env.close();
+
1850
+
1851 // test: A2 cannot send USD for NFT
+
1852 env(token::acceptSellOffer(A2, sellOfferIndex),
+
1853 ter(tecINSUFFICIENT_FUNDS));
1854 env.close();
-
1855 }
-
1856
-
1857 // Testing A1 nft offer sell when A2 frozen by issuer
-
1858 {
-
1859 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
-
1860 env(trust(G1, A2["USD"](0), tfSetFreeze));
-
1861 env.close();
-
1862
-
1863 // test: A2 cannot send USD for NFT
-
1864 env(token::acceptSellOffer(A2, sellOfferIndex),
-
1865 ter(tecINSUFFICIENT_FUNDS));
-
1866 env.close();
-
1867
-
1868 env(trust(G1, A2["USD"](0), tfClearFreeze));
-
1869 env.close();
-
1870 }
+
1855
+
1856 env(trust(G1, A2["USD"](0), tfClearFreeze));
+
1857 env.close();
+
1858 }
+
1859
+
1860 // Testing A1 nft offer sell when A2 deep frozen by issuer
+
1861 if (features[featureDeepFreeze])
+
1862 {
+
1863 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
+
1864 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1865 env.close();
+
1866
+
1867 // test: A2 cannot send USD for NFT
+
1868 env(token::acceptSellOffer(A2, sellOfferIndex),
+
1869 ter(tecINSUFFICIENT_FUNDS));
+
1870 env.close();
1871
-
1872 // Testing A1 nft offer sell when A2 deep frozen by issuer
-
1873 if (features[featureDeepFreeze])
-
1874 {
-
1875 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
-
1876 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1877 env.close();
-
1878
-
1879 // test: A2 cannot send USD for NFT
-
1880 env(token::acceptSellOffer(A2, sellOfferIndex),
-
1881 ter(tecINSUFFICIENT_FUNDS));
-
1882 env.close();
-
1883
-
1884 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1872 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1873 env.close();
+
1874 }
+
1875
+
1876 // Testing A1 nft buy offer when A2 deep frozen by issuer
+
1877 if (features[featureDeepFreeze] &&
+
1878 features[fixEnforceNFTokenTrustlineV2])
+
1879 {
+
1880 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
+
1881 env.close();
+
1882
+
1883 uint256 const nftID{token::getNextID(env, A2, 0u, tfTransferable)};
+
1884 env(token::mint(A2, 0), txflags(tfTransferable));
1885 env.close();
-
1886 }
-
1887
-
1888 // Testing A1 nft buy offer when A2 deep frozen by issuer
-
1889 if (features[featureDeepFreeze] &&
-
1890 features[fixEnforceNFTokenTrustlineV2])
-
1891 {
-
1892 env(trust(G1, A2["USD"](0), tfSetFreeze | tfSetDeepFreeze));
-
1893 env.close();
-
1894
-
1895 uint256 const nftID{token::getNextID(env, A2, 0u, tfTransferable)};
-
1896 env(token::mint(A2, 0), txflags(tfTransferable));
-
1897 env.close();
-
1898
-
1899 auto const buyIdx = keylet::nftoffer(A1, env.seq(A1)).key;
-
1900 env(token::createOffer(A1, nftID, USD(10)), token::owner(A2));
-
1901 env.close();
-
1902
-
1903 env(token::acceptBuyOffer(A2, buyIdx), ter(tecFROZEN));
-
1904 env.close();
-
1905
-
1906 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
-
1907 env.close();
-
1908
-
1909 env(token::acceptBuyOffer(A2, buyIdx));
-
1910 env.close();
-
1911 }
-
1912
-
1913 // Testing A2 nft offer sell when A2 frozen by currency holder
-
1914 {
-
1915 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
-
1916 env(trust(A2, limit, tfSetFreeze));
-
1917 env.close();
-
1918
-
1919 // test: offer can still be accepted.
-
1920 env(token::acceptSellOffer(A1, sellOfferIndex));
+
1886
+
1887 auto const buyIdx = keylet::nftoffer(A1, env.seq(A1)).key;
+
1888 env(token::createOffer(A1, nftID, USD(10)), token::owner(A2));
+
1889 env.close();
+
1890
+
1891 env(token::acceptBuyOffer(A2, buyIdx), ter(tecFROZEN));
+
1892 env.close();
+
1893
+
1894 env(trust(G1, A2["USD"](0), tfClearFreeze | tfClearDeepFreeze));
+
1895 env.close();
+
1896
+
1897 env(token::acceptBuyOffer(A2, buyIdx));
+
1898 env.close();
+
1899 }
+
1900
+
1901 // Testing A2 nft offer sell when A2 frozen by currency holder
+
1902 {
+
1903 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
+
1904 env(trust(A2, limit, tfSetFreeze));
+
1905 env.close();
+
1906
+
1907 // test: offer can still be accepted.
+
1908 env(token::acceptSellOffer(A1, sellOfferIndex));
+
1909 env.close();
+
1910
+
1911 env(trust(A2, limit, tfClearFreeze));
+
1912 env.close();
+
1913 }
+
1914
+
1915 // Testing A2 nft offer sell when A2 deep frozen by currency holder
+
1916 if (features[featureDeepFreeze])
+
1917 {
+
1918 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
+
1919
+
1920 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
1921 env.close();
1922
-
1923 env(trust(A2, limit, tfClearFreeze));
-
1924 env.close();
-
1925 }
+
1923 // test: A2 cannot receive USD for his NFT
+
1924 env(token::acceptSellOffer(A1, sellOfferIndex), ter(tecFROZEN));
+
1925 env.close();
1926
-
1927 // Testing A2 nft offer sell when A2 deep frozen by currency holder
-
1928 if (features[featureDeepFreeze])
-
1929 {
-
1930 auto const sellOfferIndex = createNFTSellOffer(env, A2, USD(10));
-
1931
-
1932 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
-
1933 env.close();
-
1934
-
1935 // test: A2 cannot receive USD for his NFT
-
1936 env(token::acceptSellOffer(A1, sellOfferIndex), ter(tecFROZEN));
-
1937 env.close();
-
1938
-
1939 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
-
1940 env.close();
-
1941 }
-
1942
-
1943 // Testing A1 nft offer sell when A2 frozen by currency holder
-
1944 {
-
1945 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
-
1946 env(trust(A2, limit, tfSetFreeze));
-
1947 env.close();
-
1948
-
1949 // test: A2 cannot send USD for NFT
-
1950 env(token::acceptSellOffer(A2, sellOfferIndex));
-
1951 env.close();
-
1952
-
1953 env(trust(A2, limit, tfClearFreeze));
-
1954 env.close();
-
1955 }
+
1927 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
+
1928 env.close();
+
1929 }
+
1930
+
1931 // Testing A1 nft offer sell when A2 frozen by currency holder
+
1932 {
+
1933 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
+
1934 env(trust(A2, limit, tfSetFreeze));
+
1935 env.close();
+
1936
+
1937 // test: A2 cannot send USD for NFT
+
1938 env(token::acceptSellOffer(A2, sellOfferIndex));
+
1939 env.close();
+
1940
+
1941 env(trust(A2, limit, tfClearFreeze));
+
1942 env.close();
+
1943 }
+
1944
+
1945 // Testing A1 nft offer sell when A2 deep frozen by currency holder
+
1946 if (features[featureDeepFreeze])
+
1947 {
+
1948 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
+
1949 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
+
1950 env.close();
+
1951
+
1952 // test: A2 cannot send USD for NFT
+
1953 env(token::acceptSellOffer(A2, sellOfferIndex),
+
1954 ter(tecINSUFFICIENT_FUNDS));
+
1955 env.close();
1956
-
1957 // Testing A1 nft offer sell when A2 deep frozen by currency holder
-
1958 if (features[featureDeepFreeze])
-
1959 {
-
1960 auto const sellOfferIndex = createNFTSellOffer(env, A1, USD(10));
-
1961 env(trust(A2, limit, tfSetFreeze | tfSetDeepFreeze));
-
1962 env.close();
-
1963
-
1964 // test: A2 cannot send USD for NFT
-
1965 env(token::acceptSellOffer(A2, sellOfferIndex),
-
1966 ter(tecINSUFFICIENT_FUNDS));
+
1957 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
+
1958 env.close();
+
1959 }
+
1960
+
1961 // Testing brokered offer acceptance
+
1962 if (features[featureDeepFreeze] &&
+
1963 features[fixEnforceNFTokenTrustlineV2])
+
1964 {
+
1965 Account broker{"broker"};
+
1966 env.fund(XRP(10000), broker);
1967 env.close();
-
1968
-
1969 env(trust(A2, limit, tfClearFreeze | tfClearDeepFreeze));
-
1970 env.close();
-
1971 }
-
1972
-
1973 // Testing brokered offer acceptance
-
1974 if (features[featureDeepFreeze] &&
-
1975 features[fixEnforceNFTokenTrustlineV2])
-
1976 {
-
1977 Account broker{"broker"};
-
1978 env.fund(XRP(10000), broker);
-
1979 env.close();
-
1980 env(trust(G1, broker["USD"](1000), tfSetFreeze | tfSetDeepFreeze));
-
1981 env.close();
-
1982
-
1983 uint256 const nftID{token::getNextID(env, A2, 0u, tfTransferable)};
-
1984 env(token::mint(A2, 0), txflags(tfTransferable));
+
1968 env(trust(G1, broker["USD"](1000), tfSetFreeze | tfSetDeepFreeze));
+
1969 env.close();
+
1970
+
1971 uint256 const nftID{token::getNextID(env, A2, 0u, tfTransferable)};
+
1972 env(token::mint(A2, 0), txflags(tfTransferable));
+
1973 env.close();
+
1974
+
1975 uint256 const sellIdx = keylet::nftoffer(A2, env.seq(A2)).key;
+
1976 env(token::createOffer(A2, nftID, USD(10)), txflags(tfSellNFToken));
+
1977 env.close();
+
1978 auto const buyIdx = keylet::nftoffer(A1, env.seq(A1)).key;
+
1979 env(token::createOffer(A1, nftID, USD(11)), token::owner(A2));
+
1980 env.close();
+
1981
+
1982 env(token::brokerOffers(broker, buyIdx, sellIdx),
+
1983 token::brokerFee(USD(1)),
+
1984 ter(tecFROZEN));
1985 env.close();
-
1986
-
1987 uint256 const sellIdx = keylet::nftoffer(A2, env.seq(A2)).key;
-
1988 env(token::createOffer(A2, nftID, USD(10)), txflags(tfSellNFToken));
-
1989 env.close();
-
1990 auto const buyIdx = keylet::nftoffer(A1, env.seq(A1)).key;
-
1991 env(token::createOffer(A1, nftID, USD(11)), token::owner(A2));
-
1992 env.close();
-
1993
-
1994 env(token::brokerOffers(broker, buyIdx, sellIdx),
-
1995 token::brokerFee(USD(1)),
-
1996 ter(tecFROZEN));
-
1997 env.close();
-
1998 }
-
1999
-
2000 // Testing transfer fee
-
2001 if (features[featureDeepFreeze] &&
-
2002 features[fixEnforceNFTokenTrustlineV2])
-
2003 {
-
2004 Account minter{"minter"};
-
2005 env.fund(XRP(10000), minter);
-
2006 env.close();
-
2007 env(trust(G1, minter["USD"](1000)));
-
2008 env.close();
-
2009
-
2010 uint256 const nftID{
-
2011 token::getNextID(env, minter, 0u, tfTransferable, 1u)};
-
2012 env(token::mint(minter, 0),
-
2013 token::xferFee(1u),
-
2014 txflags(tfTransferable));
-
2015 env.close();
-
2016
-
2017 uint256 const minterSellIdx =
-
2018 keylet::nftoffer(minter, env.seq(minter)).key;
-
2019 env(token::createOffer(minter, nftID, drops(1)),
-
2020 txflags(tfSellNFToken));
-
2021 env.close();
-
2022 env(token::acceptSellOffer(A2, minterSellIdx));
-
2023 env.close();
-
2024
-
2025 uint256 const sellIdx = keylet::nftoffer(A2, env.seq(A2)).key;
-
2026 env(token::createOffer(A2, nftID, USD(100)),
-
2027 txflags(tfSellNFToken));
-
2028 env.close();
-
2029 env(trust(G1, minter["USD"](1000), tfSetFreeze | tfSetDeepFreeze));
-
2030 env.close();
-
2031 env(token::acceptSellOffer(A1, sellIdx), ter(tecFROZEN));
-
2032 env.close();
-
2033 }
-
2034 }
-
2035
-
2036 // Helper function to extract trustline flags from open ledger
-
2037 uint32_t
-
2038 getTrustlineFlags(
-
2039 test::jtx::Env& env,
-
2040 size_t expectedArraySize,
-
2041 size_t expectedArrayIndex,
-
2042 bool modified = true)
-
2043 {
-
2044 using namespace test::jtx;
-
2045 auto const affected =
-
2046 env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
-
2047 if (!BEAST_EXPECT(checkArraySize(affected, expectedArraySize)))
-
2048 return 0;
+
1986 }
+
1987
+
1988 // Testing transfer fee
+
1989 if (features[featureDeepFreeze] &&
+
1990 features[fixEnforceNFTokenTrustlineV2])
+
1991 {
+
1992 Account minter{"minter"};
+
1993 env.fund(XRP(10000), minter);
+
1994 env.close();
+
1995 env(trust(G1, minter["USD"](1000)));
+
1996 env.close();
+
1997
+
1998 uint256 const nftID{
+
1999 token::getNextID(env, minter, 0u, tfTransferable, 1u)};
+
2000 env(token::mint(minter, 0),
+
2001 token::xferFee(1u),
+
2002 txflags(tfTransferable));
+
2003 env.close();
+
2004
+
2005 uint256 const minterSellIdx =
+
2006 keylet::nftoffer(minter, env.seq(minter)).key;
+
2007 env(token::createOffer(minter, nftID, drops(1)),
+
2008 txflags(tfSellNFToken));
+
2009 env.close();
+
2010 env(token::acceptSellOffer(A2, minterSellIdx));
+
2011 env.close();
+
2012
+
2013 uint256 const sellIdx = keylet::nftoffer(A2, env.seq(A2)).key;
+
2014 env(token::createOffer(A2, nftID, USD(100)),
+
2015 txflags(tfSellNFToken));
+
2016 env.close();
+
2017 env(trust(G1, minter["USD"](1000), tfSetFreeze | tfSetDeepFreeze));
+
2018 env.close();
+
2019 env(token::acceptSellOffer(A1, sellIdx), ter(tecFROZEN));
+
2020 env.close();
+
2021 }
+
2022 }
+
2023
+
2024 // Helper function to extract trustline flags from open ledger
+
2025 uint32_t
+
2026 getTrustlineFlags(
+
2027 test::jtx::Env& env,
+
2028 size_t expectedArraySize,
+
2029 size_t expectedArrayIndex,
+
2030 bool modified = true)
+
2031 {
+
2032 using namespace test::jtx;
+
2033 auto const affected =
+
2034 env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
+
2035 if (!BEAST_EXPECT(checkArraySize(affected, expectedArraySize)))
+
2036 return 0;
+
2037
+
2038 if (modified)
+
2039 {
+
2040 return affected[expectedArrayIndex][sfModifiedNode.fieldName]
+
2041 [sfFinalFields.fieldName][jss::Flags]
+
2042 .asUInt();
+
2043 }
+
2044
+
2045 return affected[expectedArrayIndex][sfCreatedNode.fieldName]
+
2046 [sfNewFields.fieldName][jss::Flags]
+
2047 .asUInt();
+
2048 }
2049
-
2050 if (modified)
-
2051 {
-
2052 return affected[expectedArrayIndex][sfModifiedNode.fieldName]
-
2053 [sfFinalFields.fieldName][jss::Flags]
-
2054 .asUInt();
-
2055 }
+
2050 // Helper function that returns the index of the next check on account
+
2051 uint256
+
2052 getCheckIndex(AccountID const& account, std::uint32_t uSequence)
+
2053 {
+
2054 return keylet::check(account, uSequence).key;
+
2055 }
2056
-
2057 return affected[expectedArrayIndex][sfCreatedNode.fieldName]
-
2058 [sfNewFields.fieldName][jss::Flags]
-
2059 .asUInt();
-
2060 }
-
2061
-
2062 // Helper function that returns the index of the next check on account
-
2063 uint256
-
2064 getCheckIndex(AccountID const& account, std::uint32_t uSequence)
-
2065 {
-
2066 return keylet::check(account, uSequence).key;
-
2067 }
-
2068
-
2069 uint256
-
2070 createNFTSellOffer(
-
2071 test::jtx::Env& env,
-
2072 test::jtx::Account const& account,
-
2073 test::jtx::PrettyAmount const& currency)
-
2074 {
-
2075 using namespace test::jtx;
-
2076 uint256 const nftID{token::getNextID(env, account, 0u, tfTransferable)};
-
2077 env(token::mint(account, 0), txflags(tfTransferable));
-
2078 env.close();
-
2079
-
2080 uint256 const sellOfferIndex =
-
2081 keylet::nftoffer(account, env.seq(account)).key;
-
2082 env(token::createOffer(account, nftID, currency),
-
2083 txflags(tfSellNFToken));
-
2084 env.close();
-
2085
-
2086 return sellOfferIndex;
-
2087 }
-
2088
-
2089public:
-
2090 void
-
2091 run() override
-
2092 {
-
2093 auto testAll = [this](FeatureBitset features) {
-
2094 testRippleState(features);
-
2095 testDeepFreeze(features);
-
2096 testCreateFrozenTrustline(features);
-
2097 testSetAndClear(features);
-
2098 testGlobalFreeze(features);
-
2099 testNoFreeze(features);
-
2100 testOffersWhenFrozen(features);
-
2101 testOffersWhenDeepFrozen(features);
-
2102 testPaymentsWhenDeepFrozen(features);
-
2103 testChecksWhenFrozen(features);
-
2104 testAMMWhenFreeze(features);
-
2105 testPathsWhenFrozen(features);
-
2106 testNFTOffersWhenFreeze(features);
-
2107 };
-
2108 using namespace test::jtx;
-
2109 auto const sa = supported_amendments();
-
2110 testAll(
-
2111 sa - featureFlowCross - featureDeepFreeze - featurePermissionedDEX -
-
2112 fixEnforceNFTokenTrustlineV2);
-
2113 testAll(
-
2114 sa - featureFlowCross - featurePermissionedDEX -
-
2115 fixEnforceNFTokenTrustlineV2);
-
2116 testAll(
-
2117 sa - featureDeepFreeze - featurePermissionedDEX -
-
2118 fixEnforceNFTokenTrustlineV2);
-
2119 testAll(sa - featurePermissionedDEX - fixEnforceNFTokenTrustlineV2);
-
2120 testAll(sa - fixEnforceNFTokenTrustlineV2);
-
2121 testAll(sa);
-
2122 }
-
2123};
-
2124
-
2125BEAST_DEFINE_TESTSUITE(Freeze, app, ripple);
-
2126} // namespace ripple
+
2057 uint256
+
2058 createNFTSellOffer(
+
2059 test::jtx::Env& env,
+
2060 test::jtx::Account const& account,
+
2061 test::jtx::PrettyAmount const& currency)
+
2062 {
+
2063 using namespace test::jtx;
+
2064 uint256 const nftID{token::getNextID(env, account, 0u, tfTransferable)};
+
2065 env(token::mint(account, 0), txflags(tfTransferable));
+
2066 env.close();
+
2067
+
2068 uint256 const sellOfferIndex =
+
2069 keylet::nftoffer(account, env.seq(account)).key;
+
2070 env(token::createOffer(account, nftID, currency),
+
2071 txflags(tfSellNFToken));
+
2072 env.close();
+
2073
+
2074 return sellOfferIndex;
+
2075 }
+
2076
+
2077public:
+
2078 void
+
2079 run() override
+
2080 {
+
2081 auto testAll = [this](FeatureBitset features) {
+
2082 testRippleState(features);
+
2083 testDeepFreeze(features);
+
2084 testCreateFrozenTrustline(features);
+
2085 testSetAndClear(features);
+
2086 testGlobalFreeze(features);
+
2087 testNoFreeze(features);
+
2088 testOffersWhenFrozen(features);
+
2089 testOffersWhenDeepFrozen(features);
+
2090 testPaymentsWhenDeepFrozen(features);
+
2091 testChecksWhenFrozen(features);
+
2092 testAMMWhenFreeze(features);
+
2093 testPathsWhenFrozen(features);
+
2094 testNFTOffersWhenFreeze(features);
+
2095 };
+
2096 using namespace test::jtx;
+
2097 auto const sa = supported_amendments();
+
2098 testAll(
+
2099 sa - featureDeepFreeze - featurePermissionedDEX -
+
2100 fixEnforceNFTokenTrustlineV2);
+
2101 testAll(sa - featurePermissionedDEX - fixEnforceNFTokenTrustlineV2);
+
2102 testAll(sa - featureDeepFreeze - featurePermissionedDEX);
+
2103 testAll(sa - featurePermissionedDEX);
+
2104 testAll(sa - fixEnforceNFTokenTrustlineV2);
+
2105 testAll(sa - featureDeepFreeze);
+
2106 testAll(sa);
+
2107 }
+
2108};
+
2109
+
2110BEAST_DEFINE_TESTSUITE(Freeze, app, ripple);
+
2111} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
A testsuite class.
Definition: suite.h:55
@@ -2209,22 +2194,22 @@ $(function() {
Definition: Feature.h:163
Definition: Freeze_test.cpp:32
void testOffersWhenDeepFrozen(FeatureBitset features)
-
uint256 createNFTSellOffer(test::jtx::Env &env, test::jtx::Account const &account, test::jtx::PrettyAmount const &currency)
-
uint32_t getTrustlineFlags(test::jtx::Env &env, size_t expectedArraySize, size_t expectedArrayIndex, bool modified=true)
+
uint256 createNFTSellOffer(test::jtx::Env &env, test::jtx::Account const &account, test::jtx::PrettyAmount const &currency)
+
uint32_t getTrustlineFlags(test::jtx::Env &env, size_t expectedArraySize, size_t expectedArrayIndex, bool modified=true)
void testGlobalFreeze(FeatureBitset features)
void testDeepFreeze(FeatureBitset features)
-
void testPaymentsWhenDeepFrozen(FeatureBitset features)
+
void testPaymentsWhenDeepFrozen(FeatureBitset features)
void testNoFreeze(FeatureBitset features)
-
void testNFTOffersWhenFreeze(FeatureBitset features)
-
void testAMMWhenFreeze(FeatureBitset features)
-
void testPathsWhenFrozen(FeatureBitset features)
-
void run() override
Runs the suite.
-
uint256 getCheckIndex(AccountID const &account, std::uint32_t uSequence)
+
void testNFTOffersWhenFreeze(FeatureBitset features)
+
void testAMMWhenFreeze(FeatureBitset features)
+
void testPathsWhenFrozen(FeatureBitset features)
+
void run() override
Runs the suite.
+
uint256 getCheckIndex(AccountID const &account, std::uint32_t uSequence)
void testCreateFrozenTrustline(FeatureBitset features)
void testRippleState(FeatureBitset features)
Definition: Freeze_test.cpp:34
void testSetAndClear(FeatureBitset features)
void testOffersWhenFrozen(FeatureBitset features)
-
void testChecksWhenFrozen(FeatureBitset features)
+
void testChecksWhenFrozen(FeatureBitset features)
A currency issued by an account.
Definition: Issue.h:33
Definition: STAmount.h:50
diff --git a/GatewayBalances__test_8cpp_source.html b/GatewayBalances__test_8cpp_source.html index 6270a591f0..957c242750 100644 --- a/GatewayBalances__test_8cpp_source.html +++ b/GatewayBalances__test_8cpp_source.html @@ -330,23 +330,20 @@ $(function() {
252 {
253 using namespace jtx;
254 auto const sa = supported_amendments();
-
255 for (auto feature :
-
256 {sa - featureFlowCross - featurePermissionedDEX,
-
257 sa - featurePermissionedDEX,
-
258 sa})
-
259 {
-
260 testGWB(feature);
-
261 testGWBApiVersions(feature);
-
262 }
-
263
-
264 testGWBOverflow();
-
265 }
-
266};
-
267
-
268BEAST_DEFINE_TESTSUITE(GatewayBalances, app, ripple);
-
269
-
270} // namespace test
-
271} // namespace ripple
+
255 for (auto feature : {sa - featurePermissionedDEX, sa})
+
256 {
+
257 testGWB(feature);
+
258 testGWBApiVersions(feature);
+
259 }
+
260
+
261 testGWBOverflow();
+
262 }
+
263};
+
264
+
265BEAST_DEFINE_TESTSUITE(GatewayBalances, app, ripple);
+
266
+
267} // namespace test
+
268} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
A testsuite class.
Definition: suite.h:55
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition: suite.h:229
diff --git a/Handler__test_8cpp_source.html b/Handler__test_8cpp_source.html index aa94740761..d1c0de8ab0 100644 --- a/Handler__test_8cpp_source.html +++ b/Handler__test_8cpp_source.html @@ -233,7 +233,7 @@ $(function() {
std::ostream & operator<<(std::ostream &os, PrettyAmount const &amount)
Definition: amount.cpp:73
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
-
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1005
+
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1004
T now(T... args)
diff --git a/Livecache__test_8cpp_source.html b/Livecache__test_8cpp_source.html index 608881149d..b11fd78252 100644 --- a/Livecache__test_8cpp_source.html +++ b/Livecache__test_8cpp_source.html @@ -358,7 +358,7 @@ $(function() {
std::uint32_t constexpr maxHops
bool operator==(Endpoint const &a, Endpoint const &b)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
-
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1005
+
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1004
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition: View.cpp:2984
T sort(T... args)
diff --git a/NoRipple__test_8cpp_source.html b/NoRipple__test_8cpp_source.html index 74af925ced..b5ea952c0d 100644 --- a/NoRipple__test_8cpp_source.html +++ b/NoRipple__test_8cpp_source.html @@ -372,16 +372,15 @@ $(function() {
294 };
295 using namespace jtx;
296 auto const sa = supported_amendments();
-
297 withFeatsTests(sa - featureFlowCross - featurePermissionedDEX);
-
298 withFeatsTests(sa - featurePermissionedDEX);
-
299 withFeatsTests(sa);
-
300 }
-
301};
-
302
-
303BEAST_DEFINE_TESTSUITE(NoRipple, app, ripple);
-
304
-
305} // namespace test
-
306} // namespace ripple
+
297 withFeatsTests(sa - featurePermissionedDEX);
+
298 withFeatsTests(sa);
+
299 }
+
300};
+
301
+
302BEAST_DEFINE_TESTSUITE(NoRipple, app, ripple);
+
303
+
304} // namespace test
+
305} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
diff --git a/Offer__test_8cpp_source.html b/Offer__test_8cpp_source.html index ffdb1df29c..49e9126213 100644 --- a/Offer__test_8cpp_source.html +++ b/Offer__test_8cpp_source.html @@ -1421,4197 +1421,4139 @@ $(function() {
1343
1344 // NOTE :
1345 // At this point, all offers are expected to be consumed.
-
1346 // Alas, they are not - because of a bug in the Taker auto-bridging
-
1347 // implementation which is addressed by fixTakerDryOfferRemoval.
-
1348 // The pre-fixTakerDryOfferRemoval implementation (incorrect) leaves
-
1349 // an empty offer in the second leg of the bridge. Validate both the
-
1350 // old and the new behavior.
-
1351 {
-
1352 auto acctOffers = offersOnAccount(env, account_to_test);
-
1353 bool const noStaleOffers{
-
1354 features[featureFlowCross] ||
-
1355 features[fixTakerDryOfferRemoval]};
-
1356
-
1357 BEAST_EXPECT(acctOffers.size() == (noStaleOffers ? 0 : 1));
-
1358 for (auto const& offerPtr : acctOffers)
-
1359 {
-
1360 auto const& offer = *offerPtr;
-
1361 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
1362 BEAST_EXPECT(offer[sfTakerGets] == USD(0));
-
1363 BEAST_EXPECT(offer[sfTakerPays] == XRP(0));
-
1364 }
-
1365 }
-
1366
-
1367 // cancel that lingering second offer so that it doesn't interfere
-
1368 // with the next set of offers we test. This will not be needed once
-
1369 // the bridging bug is fixed
-
1370 env(offer_cancel(account_to_test, secondLegSeq));
-
1371 env.require(offers(account_to_test, 0));
-
1372
-
1373 // PART 2:
-
1374 // simple direct crossing BTC to USD and then USD to BTC which causes
-
1375 // the first offer to be replaced
-
1376 env(offer(account_to_test, BTC(250), USD(50)));
-
1377 env.require(offers(account_to_test, 1));
+
1346 {
+
1347 auto acctOffers = offersOnAccount(env, account_to_test);
+
1348
+
1349 BEAST_EXPECT(acctOffers.size() == 0);
+
1350 for (auto const& offerPtr : acctOffers)
+
1351 {
+
1352 auto const& offer = *offerPtr;
+
1353 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
1354 BEAST_EXPECT(offer[sfTakerGets] == USD(0));
+
1355 BEAST_EXPECT(offer[sfTakerPays] == XRP(0));
+
1356 }
+
1357 }
+
1358
+
1359 // cancel that lingering second offer so that it doesn't interfere
+
1360 // with the next set of offers we test. This will not be needed once
+
1361 // the bridging bug is fixed
+
1362 env(offer_cancel(account_to_test, secondLegSeq));
+
1363 env.require(offers(account_to_test, 0));
+
1364
+
1365 // PART 2:
+
1366 // simple direct crossing BTC to USD and then USD to BTC which causes
+
1367 // the first offer to be replaced
+
1368 env(offer(account_to_test, BTC(250), USD(50)));
+
1369 env.require(offers(account_to_test, 1));
+
1370
+
1371 // validate that the book shows one BTC for USD offer and no USD for
+
1372 // BTC offers
+
1373 BEAST_EXPECT(isOffer(env, account_to_test, BTC(250), USD(50)));
+
1374
+
1375 jrr = getBookOffers(env, USD, BTC);
+
1376 BEAST_EXPECT(jrr[jss::offers].isArray());
+
1377 BEAST_EXPECT(jrr[jss::offers].size() == 0);
1378
-
1379 // validate that the book shows one BTC for USD offer and no USD for
-
1380 // BTC offers
-
1381 BEAST_EXPECT(isOffer(env, account_to_test, BTC(250), USD(50)));
-
1382
-
1383 jrr = getBookOffers(env, USD, BTC);
-
1384 BEAST_EXPECT(jrr[jss::offers].isArray());
-
1385 BEAST_EXPECT(jrr[jss::offers].size() == 0);
-
1386
-
1387 // this second offer would self-cross directly, so it causes the first
-
1388 // offer by the same owner/taker to be removed
-
1389 env(offer(account_to_test, USD(50), BTC(250)));
-
1390 env.require(offers(account_to_test, 1));
-
1391
-
1392 // validate that we now have just the second offer...the first
-
1393 // was removed
-
1394 jrr = getBookOffers(env, BTC, USD);
-
1395 BEAST_EXPECT(jrr[jss::offers].isArray());
-
1396 BEAST_EXPECT(jrr[jss::offers].size() == 0);
-
1397
-
1398 BEAST_EXPECT(isOffer(env, account_to_test, USD(50), BTC(250)));
-
1399 }
-
1400
-
1401 void
-
1402 testNegativeBalance(FeatureBitset features)
-
1403 {
-
1404 // This test creates an offer test for negative balance
-
1405 // with transfer fees and miniscule funds.
-
1406 testcase("Negative Balance");
-
1407
-
1408 using namespace jtx;
-
1409
-
1410 // This is one of the few tests where fixReducedOffersV2 changes the
-
1411 // results. So test both with and without fixReducedOffersV2.
-
1412 for (FeatureBitset localFeatures :
-
1413 {features - fixReducedOffersV2, features | fixReducedOffersV2})
-
1414 {
-
1415 Env env{*this, localFeatures};
-
1416
-
1417 auto const gw = Account{"gateway"};
-
1418 auto const alice = Account{"alice"};
-
1419 auto const bob = Account{"bob"};
-
1420 auto const USD = gw["USD"];
-
1421 auto const BTC = gw["BTC"];
+
1379 // this second offer would self-cross directly, so it causes the first
+
1380 // offer by the same owner/taker to be removed
+
1381 env(offer(account_to_test, USD(50), BTC(250)));
+
1382 env.require(offers(account_to_test, 1));
+
1383
+
1384 // validate that we now have just the second offer...the first
+
1385 // was removed
+
1386 jrr = getBookOffers(env, BTC, USD);
+
1387 BEAST_EXPECT(jrr[jss::offers].isArray());
+
1388 BEAST_EXPECT(jrr[jss::offers].size() == 0);
+
1389
+
1390 BEAST_EXPECT(isOffer(env, account_to_test, USD(50), BTC(250)));
+
1391 }
+
1392
+
1393 void
+
1394 testNegativeBalance(FeatureBitset features)
+
1395 {
+
1396 // This test creates an offer test for negative balance
+
1397 // with transfer fees and miniscule funds.
+
1398 testcase("Negative Balance");
+
1399
+
1400 using namespace jtx;
+
1401
+
1402 // This is one of the few tests where fixReducedOffersV2 changes the
+
1403 // results. So test both with and without fixReducedOffersV2.
+
1404 for (FeatureBitset localFeatures :
+
1405 {features - fixReducedOffersV2, features | fixReducedOffersV2})
+
1406 {
+
1407 Env env{*this, localFeatures};
+
1408
+
1409 auto const gw = Account{"gateway"};
+
1410 auto const alice = Account{"alice"};
+
1411 auto const bob = Account{"bob"};
+
1412 auto const USD = gw["USD"];
+
1413 auto const BTC = gw["BTC"];
+
1414
+
1415 // these *interesting* amounts were taken
+
1416 // from the original JS test that was ported here
+
1417 auto const gw_initial_balance = drops(1149999730);
+
1418 auto const alice_initial_balance = drops(499946999680);
+
1419 auto const bob_initial_balance = drops(10199999920);
+
1420 auto const small_amount =
+
1421 STAmount{bob["USD"].issue(), UINT64_C(2710505431213761), -33};
1422
-
1423 // these *interesting* amounts were taken
-
1424 // from the original JS test that was ported here
-
1425 auto const gw_initial_balance = drops(1149999730);
-
1426 auto const alice_initial_balance = drops(499946999680);
-
1427 auto const bob_initial_balance = drops(10199999920);
-
1428 auto const small_amount =
-
1429 STAmount{bob["USD"].issue(), UINT64_C(2710505431213761), -33};
-
1430
-
1431 env.fund(gw_initial_balance, gw);
-
1432 env.fund(alice_initial_balance, alice);
-
1433 env.fund(bob_initial_balance, bob);
-
1434 env.close();
-
1435
-
1436 env(rate(gw, 1.005));
-
1437
-
1438 env(trust(alice, USD(500)));
-
1439 env(trust(bob, USD(50)));
-
1440 env(trust(gw, alice["USD"](100)));
+
1423 env.fund(gw_initial_balance, gw);
+
1424 env.fund(alice_initial_balance, alice);
+
1425 env.fund(bob_initial_balance, bob);
+
1426 env.close();
+
1427
+
1428 env(rate(gw, 1.005));
+
1429
+
1430 env(trust(alice, USD(500)));
+
1431 env(trust(bob, USD(50)));
+
1432 env(trust(gw, alice["USD"](100)));
+
1433
+
1434 env(pay(gw, alice, alice["USD"](50)));
+
1435 env(pay(gw, bob, small_amount));
+
1436
+
1437 env(offer(alice, USD(50), XRP(150000)));
+
1438
+
1439 // unfund the offer
+
1440 env(pay(alice, gw, USD(100)));
1441
-
1442 env(pay(gw, alice, alice["USD"](50)));
-
1443 env(pay(gw, bob, small_amount));
+
1442 // drop the trust line (set to 0)
+
1443 env(trust(gw, alice["USD"](0)));
1444
-
1445 env(offer(alice, USD(50), XRP(150000)));
-
1446
-
1447 // unfund the offer
-
1448 env(pay(alice, gw, USD(100)));
+
1445 // verify balances
+
1446 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
1447 BEAST_EXPECT(
+
1448 jrr[jss::node][sfBalance.fieldName][jss::value] == "50");
1449
-
1450 // drop the trust line (set to 0)
-
1451 env(trust(gw, alice["USD"](0)));
-
1452
-
1453 // verify balances
-
1454 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
1455 BEAST_EXPECT(
-
1456 jrr[jss::node][sfBalance.fieldName][jss::value] == "50");
-
1457
-
1458 jrr = ledgerEntryState(env, bob, gw, "USD");
-
1459 BEAST_EXPECT(
-
1460 jrr[jss::node][sfBalance.fieldName][jss::value] ==
-
1461 "-2710505431213761e-33");
-
1462
-
1463 // create crossing offer
-
1464 std::uint32_t const bobOfferSeq = env.seq(bob);
-
1465 env(offer(bob, XRP(2000), USD(1)));
-
1466
-
1467 if (localFeatures[featureFlowCross] &&
-
1468 localFeatures[fixReducedOffersV2])
-
1469 {
-
1470 // With the rounding introduced by fixReducedOffersV2, bob's
-
1471 // offer does not cross alice's offer and goes straight into
-
1472 // the ledger.
-
1473 jrr = ledgerEntryState(env, bob, gw, "USD");
-
1474 BEAST_EXPECT(
-
1475 jrr[jss::node][sfBalance.fieldName][jss::value] ==
-
1476 "-2710505431213761e-33");
-
1477
-
1478 Json::Value const bobOffer =
-
1479 ledgerEntryOffer(env, bob, bobOfferSeq)[jss::node];
-
1480 BEAST_EXPECT(bobOffer[sfTakerGets.jsonName][jss::value] == "1");
-
1481 BEAST_EXPECT(bobOffer[sfTakerPays.jsonName] == "2000000000");
-
1482 return;
-
1483 }
+
1450 jrr = ledgerEntryState(env, bob, gw, "USD");
+
1451 BEAST_EXPECT(
+
1452 jrr[jss::node][sfBalance.fieldName][jss::value] ==
+
1453 "-2710505431213761e-33");
+
1454
+
1455 // create crossing offer
+
1456 std::uint32_t const bobOfferSeq = env.seq(bob);
+
1457 env(offer(bob, XRP(2000), USD(1)));
+
1458
+
1459 if (localFeatures[fixReducedOffersV2])
+
1460 {
+
1461 // With the rounding introduced by fixReducedOffersV2, bob's
+
1462 // offer does not cross alice's offer and goes straight into
+
1463 // the ledger.
+
1464 jrr = ledgerEntryState(env, bob, gw, "USD");
+
1465 BEAST_EXPECT(
+
1466 jrr[jss::node][sfBalance.fieldName][jss::value] ==
+
1467 "-2710505431213761e-33");
+
1468
+
1469 Json::Value const bobOffer =
+
1470 ledgerEntryOffer(env, bob, bobOfferSeq)[jss::node];
+
1471 BEAST_EXPECT(bobOffer[sfTakerGets.jsonName][jss::value] == "1");
+
1472 BEAST_EXPECT(bobOffer[sfTakerPays.jsonName] == "2000000000");
+
1473 return;
+
1474 }
+
1475
+
1476 // verify balances again.
+
1477 //
+
1478 // NOTE:
+
1479 // Here a difference in the rounding modes of our two offer
+
1480 // crossing algorithms becomes apparent. The old offer crossing
+
1481 // would consume small_amount and transfer no XRP. The new offer
+
1482 // crossing transfers a single drop, rather than no drops.
+
1483 auto const crossingDelta = drops(1);
1484
-
1485 // verify balances again.
-
1486 //
-
1487 // NOTE:
-
1488 // Here a difference in the rounding modes of our two offer
-
1489 // crossing algorithms becomes apparent. The old offer crossing
-
1490 // would consume small_amount and transfer no XRP. The new offer
-
1491 // crossing transfers a single drop, rather than no drops.
-
1492 auto const crossingDelta =
-
1493 localFeatures[featureFlowCross] ? drops(1) : drops(0);
-
1494
-
1495 jrr = ledgerEntryState(env, alice, gw, "USD");
+
1485 jrr = ledgerEntryState(env, alice, gw, "USD");
+
1486 BEAST_EXPECT(
+
1487 jrr[jss::node][sfBalance.fieldName][jss::value] == "50");
+
1488 BEAST_EXPECT(
+
1489 env.balance(alice, xrpIssue()) ==
+
1490 alice_initial_balance - env.current()->fees().base * 3 -
+
1491 crossingDelta);
+
1492
+
1493 jrr = ledgerEntryState(env, bob, gw, "USD");
+
1494 BEAST_EXPECT(
+
1495 jrr[jss::node][sfBalance.fieldName][jss::value] == "0");
1496 BEAST_EXPECT(
-
1497 jrr[jss::node][sfBalance.fieldName][jss::value] == "50");
-
1498 BEAST_EXPECT(
-
1499 env.balance(alice, xrpIssue()) ==
-
1500 alice_initial_balance - env.current()->fees().base * 3 -
-
1501 crossingDelta);
+
1497 env.balance(bob, xrpIssue()) ==
+
1498 bob_initial_balance - env.current()->fees().base * 2 +
+
1499 crossingDelta);
+
1500 }
+
1501 }
1502
-
1503 jrr = ledgerEntryState(env, bob, gw, "USD");
-
1504 BEAST_EXPECT(
-
1505 jrr[jss::node][sfBalance.fieldName][jss::value] == "0");
-
1506 BEAST_EXPECT(
-
1507 env.balance(bob, xrpIssue()) ==
-
1508 bob_initial_balance - env.current()->fees().base * 2 +
-
1509 crossingDelta);
-
1510 }
-
1511 }
-
1512
-
1513 void
-
1514 testOfferCrossWithXRP(bool reverse_order, FeatureBitset features)
-
1515 {
-
1516 testcase(
-
1517 std::string("Offer Crossing with XRP, ") +
-
1518 (reverse_order ? "Reverse" : "Normal") + " order");
-
1519
-
1520 using namespace jtx;
+
1503 void
+
1504 testOfferCrossWithXRP(bool reverse_order, FeatureBitset features)
+
1505 {
+
1506 testcase(
+
1507 std::string("Offer Crossing with XRP, ") +
+
1508 (reverse_order ? "Reverse" : "Normal") + " order");
+
1509
+
1510 using namespace jtx;
+
1511
+
1512 Env env{*this, features};
+
1513
+
1514 auto const gw = Account{"gateway"};
+
1515 auto const alice = Account{"alice"};
+
1516 auto const bob = Account{"bob"};
+
1517 auto const USD = gw["USD"];
+
1518
+
1519 env.fund(XRP(10000), gw, alice, bob);
+
1520 env.close();
1521
-
1522 Env env{*this, features};
-
1523
-
1524 auto const gw = Account{"gateway"};
-
1525 auto const alice = Account{"alice"};
-
1526 auto const bob = Account{"bob"};
-
1527 auto const USD = gw["USD"];
-
1528
-
1529 env.fund(XRP(10000), gw, alice, bob);
-
1530 env.close();
+
1522 env(trust(alice, USD(1000)));
+
1523 env(trust(bob, USD(1000)));
+
1524
+
1525 env(pay(gw, alice, alice["USD"](500)));
+
1526
+
1527 if (reverse_order)
+
1528 env(offer(bob, USD(1), XRP(4000)));
+
1529
+
1530 env(offer(alice, XRP(150000), USD(50)));
1531
-
1532 env(trust(alice, USD(1000)));
-
1533 env(trust(bob, USD(1000)));
+
1532 if (!reverse_order)
+
1533 env(offer(bob, USD(1), XRP(4000)));
1534
-
1535 env(pay(gw, alice, alice["USD"](500)));
-
1536
-
1537 if (reverse_order)
-
1538 env(offer(bob, USD(1), XRP(4000)));
-
1539
-
1540 env(offer(alice, XRP(150000), USD(50)));
-
1541
-
1542 if (!reverse_order)
-
1543 env(offer(bob, USD(1), XRP(4000)));
-
1544
-
1545 // Existing offer pays better than this wants.
-
1546 // Fully consume existing offer.
-
1547 // Pay 1 USD, get 4000 XRP.
-
1548
-
1549 auto jrr = ledgerEntryState(env, bob, gw, "USD");
-
1550 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-1");
-
1551 jrr = ledgerEntryRoot(env, bob);
-
1552 BEAST_EXPECT(
-
1553 jrr[jss::node][sfBalance.fieldName] ==
-
1554 to_string((XRP(10000) - XRP(reverse_order ? 4000 : 3000) -
-
1555 env.current()->fees().base * 2)
-
1556 .xrp()));
+
1535 // Existing offer pays better than this wants.
+
1536 // Fully consume existing offer.
+
1537 // Pay 1 USD, get 4000 XRP.
+
1538
+
1539 auto jrr = ledgerEntryState(env, bob, gw, "USD");
+
1540 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-1");
+
1541 jrr = ledgerEntryRoot(env, bob);
+
1542 BEAST_EXPECT(
+
1543 jrr[jss::node][sfBalance.fieldName] ==
+
1544 to_string((XRP(10000) - XRP(reverse_order ? 4000 : 3000) -
+
1545 env.current()->fees().base * 2)
+
1546 .xrp()));
+
1547
+
1548 jrr = ledgerEntryState(env, alice, gw, "USD");
+
1549 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499");
+
1550 jrr = ledgerEntryRoot(env, alice);
+
1551 BEAST_EXPECT(
+
1552 jrr[jss::node][sfBalance.fieldName] ==
+
1553 to_string((XRP(10000) + XRP(reverse_order ? 4000 : 3000) -
+
1554 env.current()->fees().base * 2)
+
1555 .xrp()));
+
1556 }
1557
-
1558 jrr = ledgerEntryState(env, alice, gw, "USD");
-
1559 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499");
-
1560 jrr = ledgerEntryRoot(env, alice);
-
1561 BEAST_EXPECT(
-
1562 jrr[jss::node][sfBalance.fieldName] ==
-
1563 to_string((XRP(10000) + XRP(reverse_order ? 4000 : 3000) -
-
1564 env.current()->fees().base * 2)
-
1565 .xrp()));
-
1566 }
-
1567
-
1568 void
-
1569 testOfferCrossWithLimitOverride(FeatureBitset features)
-
1570 {
-
1571 testcase("Offer Crossing with Limit Override");
-
1572
-
1573 using namespace jtx;
+
1558 void
+
1559 testOfferCrossWithLimitOverride(FeatureBitset features)
+
1560 {
+
1561 testcase("Offer Crossing with Limit Override");
+
1562
+
1563 using namespace jtx;
+
1564
+
1565 Env env{*this, features};
+
1566
+
1567 auto const gw = Account{"gateway"};
+
1568 auto const alice = Account{"alice"};
+
1569 auto const bob = Account{"bob"};
+
1570 auto const USD = gw["USD"];
+
1571
+
1572 env.fund(XRP(100000), gw, alice, bob);
+
1573 env.close();
1574
-
1575 Env env{*this, features};
+
1575 env(trust(alice, USD(1000)));
1576
-
1577 auto const gw = Account{"gateway"};
-
1578 auto const alice = Account{"alice"};
-
1579 auto const bob = Account{"bob"};
-
1580 auto const USD = gw["USD"];
+
1577 env(pay(gw, alice, alice["USD"](500)));
+
1578
+
1579 env(offer(alice, XRP(150000), USD(50)));
+
1580 env(offer(bob, USD(1), XRP(3000)));
1581
-
1582 env.fund(XRP(100000), gw, alice, bob);
-
1583 env.close();
-
1584
-
1585 env(trust(alice, USD(1000)));
-
1586
-
1587 env(pay(gw, alice, alice["USD"](500)));
-
1588
-
1589 env(offer(alice, XRP(150000), USD(50)));
-
1590 env(offer(bob, USD(1), XRP(3000)));
-
1591
-
1592 auto jrr = ledgerEntryState(env, bob, gw, "USD");
-
1593 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-1");
-
1594 jrr = ledgerEntryRoot(env, bob);
-
1595 BEAST_EXPECT(
-
1596 jrr[jss::node][sfBalance.fieldName] ==
-
1597 to_string((XRP(100000) - XRP(3000) - env.current()->fees().base * 1)
-
1598 .xrp()));
-
1599
-
1600 jrr = ledgerEntryState(env, alice, gw, "USD");
-
1601 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499");
-
1602 jrr = ledgerEntryRoot(env, alice);
-
1603 BEAST_EXPECT(
-
1604 jrr[jss::node][sfBalance.fieldName] ==
-
1605 to_string((XRP(100000) + XRP(3000) - env.current()->fees().base * 2)
-
1606 .xrp()));
-
1607 }
-
1608
-
1609 void
-
1610 testOfferAcceptThenCancel(FeatureBitset features)
-
1611 {
-
1612 testcase("Offer Accept then Cancel.");
+
1582 auto jrr = ledgerEntryState(env, bob, gw, "USD");
+
1583 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-1");
+
1584 jrr = ledgerEntryRoot(env, bob);
+
1585 BEAST_EXPECT(
+
1586 jrr[jss::node][sfBalance.fieldName] ==
+
1587 to_string((XRP(100000) - XRP(3000) - env.current()->fees().base * 1)
+
1588 .xrp()));
+
1589
+
1590 jrr = ledgerEntryState(env, alice, gw, "USD");
+
1591 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-499");
+
1592 jrr = ledgerEntryRoot(env, alice);
+
1593 BEAST_EXPECT(
+
1594 jrr[jss::node][sfBalance.fieldName] ==
+
1595 to_string((XRP(100000) + XRP(3000) - env.current()->fees().base * 2)
+
1596 .xrp()));
+
1597 }
+
1598
+
1599 void
+
1600 testOfferAcceptThenCancel(FeatureBitset features)
+
1601 {
+
1602 testcase("Offer Accept then Cancel.");
+
1603
+
1604 using namespace jtx;
+
1605
+
1606 Env env{*this, features};
+
1607
+
1608 auto const USD = env.master["USD"];
+
1609
+
1610 auto const nextOfferSeq = env.seq(env.master);
+
1611 env(offer(env.master, XRP(500), USD(100)));
+
1612 env.close();
1613
-
1614 using namespace jtx;
-
1615
-
1616 Env env{*this, features};
-
1617
-
1618 auto const USD = env.master["USD"];
-
1619
-
1620 auto const nextOfferSeq = env.seq(env.master);
-
1621 env(offer(env.master, XRP(500), USD(100)));
-
1622 env.close();
-
1623
-
1624 env(offer_cancel(env.master, nextOfferSeq));
-
1625 BEAST_EXPECT(env.seq(env.master) == nextOfferSeq + 2);
-
1626
-
1627 // ledger_accept, call twice and verify no odd behavior
-
1628 env.close();
-
1629 env.close();
-
1630 BEAST_EXPECT(env.seq(env.master) == nextOfferSeq + 2);
-
1631 }
-
1632
-
1633 void
-
1634 testOfferCancelPastAndFuture(FeatureBitset features)
-
1635 {
-
1636 testcase("Offer Cancel Past and Future Sequence.");
+
1614 env(offer_cancel(env.master, nextOfferSeq));
+
1615 BEAST_EXPECT(env.seq(env.master) == nextOfferSeq + 2);
+
1616
+
1617 // ledger_accept, call twice and verify no odd behavior
+
1618 env.close();
+
1619 env.close();
+
1620 BEAST_EXPECT(env.seq(env.master) == nextOfferSeq + 2);
+
1621 }
+
1622
+
1623 void
+
1624 testOfferCancelPastAndFuture(FeatureBitset features)
+
1625 {
+
1626 testcase("Offer Cancel Past and Future Sequence.");
+
1627
+
1628 using namespace jtx;
+
1629
+
1630 Env env{*this, features};
+
1631
+
1632 auto const alice = Account{"alice"};
+
1633
+
1634 auto const nextOfferSeq = env.seq(env.master);
+
1635 env.fund(XRP(10000), alice);
+
1636 env.close();
1637
-
1638 using namespace jtx;
+
1638 env(offer_cancel(env.master, nextOfferSeq));
1639
-
1640 Env env{*this, features};
-
1641
-
1642 auto const alice = Account{"alice"};
-
1643
-
1644 auto const nextOfferSeq = env.seq(env.master);
-
1645 env.fund(XRP(10000), alice);
+
1640 env(offer_cancel(env.master, env.seq(env.master)),
+
1641 ter(temBAD_SEQUENCE));
+
1642
+
1643 env(offer_cancel(env.master, env.seq(env.master) + 1),
+
1644 ter(temBAD_SEQUENCE));
+
1645
1646 env.close();
-
1647
-
1648 env(offer_cancel(env.master, nextOfferSeq));
-
1649
-
1650 env(offer_cancel(env.master, env.seq(env.master)),
-
1651 ter(temBAD_SEQUENCE));
-
1652
-
1653 env(offer_cancel(env.master, env.seq(env.master) + 1),
-
1654 ter(temBAD_SEQUENCE));
+
1647 }
+
1648
+
1649 void
+
1650 testCurrencyConversionEntire(FeatureBitset features)
+
1651 {
+
1652 testcase("Currency Conversion: Entire Offer");
+
1653
+
1654 using namespace jtx;
1655
-
1656 env.close();
-
1657 }
-
1658
-
1659 void
-
1660 testCurrencyConversionEntire(FeatureBitset features)
-
1661 {
-
1662 testcase("Currency Conversion: Entire Offer");
-
1663
-
1664 using namespace jtx;
-
1665
-
1666 Env env{*this, features};
-
1667
-
1668 auto const gw = Account{"gateway"};
-
1669 auto const alice = Account{"alice"};
-
1670 auto const bob = Account{"bob"};
-
1671 auto const USD = gw["USD"];
-
1672
-
1673 env.fund(XRP(10000), gw, alice, bob);
-
1674 env.close();
-
1675 env.require(owners(bob, 0));
-
1676
-
1677 env(trust(alice, USD(100)));
-
1678 env(trust(bob, USD(1000)));
-
1679
-
1680 env.require(owners(alice, 1), owners(bob, 1));
-
1681
-
1682 env(pay(gw, alice, alice["USD"](100)));
-
1683 auto const bobOfferSeq = env.seq(bob);
-
1684 env(offer(bob, USD(100), XRP(500)));
+
1656 Env env{*this, features};
+
1657
+
1658 auto const gw = Account{"gateway"};
+
1659 auto const alice = Account{"alice"};
+
1660 auto const bob = Account{"bob"};
+
1661 auto const USD = gw["USD"];
+
1662
+
1663 env.fund(XRP(10000), gw, alice, bob);
+
1664 env.close();
+
1665 env.require(owners(bob, 0));
+
1666
+
1667 env(trust(alice, USD(100)));
+
1668 env(trust(bob, USD(1000)));
+
1669
+
1670 env.require(owners(alice, 1), owners(bob, 1));
+
1671
+
1672 env(pay(gw, alice, alice["USD"](100)));
+
1673 auto const bobOfferSeq = env.seq(bob);
+
1674 env(offer(bob, USD(100), XRP(500)));
+
1675
+
1676 env.require(owners(alice, 1), owners(bob, 2));
+
1677 auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
+
1678 BEAST_EXPECT(
+
1679 jro[jss::node][jss::TakerGets] == XRP(500).value().getText());
+
1680 BEAST_EXPECT(
+
1681 jro[jss::node][jss::TakerPays] ==
+
1682 USD(100).value().getJson(JsonOptions::none));
+
1683
+
1684 env(pay(alice, alice, XRP(500)), sendmax(USD(100)));
1685
-
1686 env.require(owners(alice, 1), owners(bob, 2));
-
1687 auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
-
1688 BEAST_EXPECT(
-
1689 jro[jss::node][jss::TakerGets] == XRP(500).value().getText());
-
1690 BEAST_EXPECT(
-
1691 jro[jss::node][jss::TakerPays] ==
-
1692 USD(100).value().getJson(JsonOptions::none));
+
1686 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
1687 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "0");
+
1688 jrr = ledgerEntryRoot(env, alice);
+
1689 BEAST_EXPECT(
+
1690 jrr[jss::node][sfBalance.fieldName] ==
+
1691 to_string((XRP(10000) + XRP(500) - env.current()->fees().base * 2)
+
1692 .xrp()));
1693
-
1694 env(pay(alice, alice, XRP(500)), sendmax(USD(100)));
-
1695
-
1696 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
1697 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "0");
-
1698 jrr = ledgerEntryRoot(env, alice);
-
1699 BEAST_EXPECT(
-
1700 jrr[jss::node][sfBalance.fieldName] ==
-
1701 to_string((XRP(10000) + XRP(500) - env.current()->fees().base * 2)
-
1702 .xrp()));
-
1703
-
1704 jrr = ledgerEntryState(env, bob, gw, "USD");
-
1705 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
-
1706
-
1707 jro = ledgerEntryOffer(env, bob, bobOfferSeq);
-
1708 BEAST_EXPECT(jro[jss::error] == "entryNotFound");
+
1694 jrr = ledgerEntryState(env, bob, gw, "USD");
+
1695 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
+
1696
+
1697 jro = ledgerEntryOffer(env, bob, bobOfferSeq);
+
1698 BEAST_EXPECT(jro[jss::error] == "entryNotFound");
+
1699
+
1700 env.require(owners(alice, 1), owners(bob, 1));
+
1701 }
+
1702
+
1703 void
+
1704 testCurrencyConversionIntoDebt(FeatureBitset features)
+
1705 {
+
1706 testcase("Currency Conversion: Offerer Into Debt");
+
1707
+
1708 using namespace jtx;
1709
-
1710 env.require(owners(alice, 1), owners(bob, 1));
-
1711 }
-
1712
-
1713 void
-
1714 testCurrencyConversionIntoDebt(FeatureBitset features)
-
1715 {
-
1716 testcase("Currency Conversion: Offerer Into Debt");
-
1717
-
1718 using namespace jtx;
-
1719
-
1720 Env env{*this, features};
-
1721
-
1722 auto const alice = Account{"alice"};
-
1723 auto const bob = Account{"bob"};
-
1724 auto const carol = Account{"carol"};
-
1725
-
1726 env.fund(XRP(10000), alice, bob, carol);
-
1727 env.close();
+
1710 Env env{*this, features};
+
1711
+
1712 auto const alice = Account{"alice"};
+
1713 auto const bob = Account{"bob"};
+
1714 auto const carol = Account{"carol"};
+
1715
+
1716 env.fund(XRP(10000), alice, bob, carol);
+
1717 env.close();
+
1718
+
1719 env(trust(alice, carol["EUR"](2000)));
+
1720 env(trust(bob, alice["USD"](100)));
+
1721 env(trust(carol, bob["EUR"](1000)));
+
1722
+
1723 auto const bobOfferSeq = env.seq(bob);
+
1724 env(offer(bob, alice["USD"](50), carol["EUR"](200)),
+
1725 ter(tecUNFUNDED_OFFER));
+
1726
+
1727 env(offer(alice, carol["EUR"](200), alice["USD"](50)));
1728
-
1729 env(trust(alice, carol["EUR"](2000)));
-
1730 env(trust(bob, alice["USD"](100)));
-
1731 env(trust(carol, bob["EUR"](1000)));
+
1729 auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
+
1730 BEAST_EXPECT(jro[jss::error] == "entryNotFound");
+
1731 }
1732
-
1733 auto const bobOfferSeq = env.seq(bob);
-
1734 env(offer(bob, alice["USD"](50), carol["EUR"](200)),
-
1735 ter(tecUNFUNDED_OFFER));
-
1736
-
1737 env(offer(alice, carol["EUR"](200), alice["USD"](50)));
-
1738
-
1739 auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
-
1740 BEAST_EXPECT(jro[jss::error] == "entryNotFound");
-
1741 }
-
1742
-
1743 void
-
1744 testCurrencyConversionInParts(FeatureBitset features)
-
1745 {
-
1746 testcase("Currency Conversion: In Parts");
-
1747
-
1748 using namespace jtx;
+
1733 void
+
1734 testCurrencyConversionInParts(FeatureBitset features)
+
1735 {
+
1736 testcase("Currency Conversion: In Parts");
+
1737
+
1738 using namespace jtx;
+
1739
+
1740 Env env{*this, features};
+
1741
+
1742 auto const gw = Account{"gateway"};
+
1743 auto const alice = Account{"alice"};
+
1744 auto const bob = Account{"bob"};
+
1745 auto const USD = gw["USD"];
+
1746
+
1747 env.fund(XRP(10000), gw, alice, bob);
+
1748 env.close();
1749
-
1750 Env env{*this, features};
-
1751
-
1752 auto const gw = Account{"gateway"};
-
1753 auto const alice = Account{"alice"};
-
1754 auto const bob = Account{"bob"};
-
1755 auto const USD = gw["USD"];
-
1756
-
1757 env.fund(XRP(10000), gw, alice, bob);
-
1758 env.close();
+
1750 env(trust(alice, USD(200)));
+
1751 env(trust(bob, USD(1000)));
+
1752
+
1753 env(pay(gw, alice, alice["USD"](200)));
+
1754
+
1755 auto const bobOfferSeq = env.seq(bob);
+
1756 env(offer(bob, USD(100), XRP(500)));
+
1757
+
1758 env(pay(alice, alice, XRP(200)), sendmax(USD(100)));
1759
-
1760 env(trust(alice, USD(200)));
-
1761 env(trust(bob, USD(1000)));
-
1762
-
1763 env(pay(gw, alice, alice["USD"](200)));
-
1764
-
1765 auto const bobOfferSeq = env.seq(bob);
-
1766 env(offer(bob, USD(100), XRP(500)));
+
1760 // The previous payment reduced the remaining offer amount by 200 XRP
+
1761 auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
+
1762 BEAST_EXPECT(
+
1763 jro[jss::node][jss::TakerGets] == XRP(300).value().getText());
+
1764 BEAST_EXPECT(
+
1765 jro[jss::node][jss::TakerPays] ==
+
1766 USD(60).value().getJson(JsonOptions::none));
1767
-
1768 env(pay(alice, alice, XRP(200)), sendmax(USD(100)));
-
1769
-
1770 // The previous payment reduced the remaining offer amount by 200 XRP
-
1771 auto jro = ledgerEntryOffer(env, bob, bobOfferSeq);
-
1772 BEAST_EXPECT(
-
1773 jro[jss::node][jss::TakerGets] == XRP(300).value().getText());
+
1768 // the balance between alice and gw is 160 USD..200 less the 40 taken
+
1769 // by the offer
+
1770 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
1771 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-160");
+
1772 // alice now has 200 more XRP from the payment
+
1773 jrr = ledgerEntryRoot(env, alice);
1774 BEAST_EXPECT(
-
1775 jro[jss::node][jss::TakerPays] ==
-
1776 USD(60).value().getJson(JsonOptions::none));
-
1777
-
1778 // the balance between alice and gw is 160 USD..200 less the 40 taken
-
1779 // by the offer
-
1780 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
1781 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-160");
-
1782 // alice now has 200 more XRP from the payment
-
1783 jrr = ledgerEntryRoot(env, alice);
-
1784 BEAST_EXPECT(
-
1785 jrr[jss::node][sfBalance.fieldName] ==
-
1786 to_string((XRP(10000) + XRP(200) - env.current()->fees().base * 2)
-
1787 .xrp()));
+
1775 jrr[jss::node][sfBalance.fieldName] ==
+
1776 to_string((XRP(10000) + XRP(200) - env.current()->fees().base * 2)
+
1777 .xrp()));
+
1778
+
1779 // bob got 40 USD from partial consumption of the offer
+
1780 jrr = ledgerEntryState(env, bob, gw, "USD");
+
1781 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-40");
+
1782
+
1783 // Alice converts USD to XRP which should fail
+
1784 // due to PartialPayment.
+
1785 env(pay(alice, alice, XRP(600)),
+
1786 sendmax(USD(100)),
+
1787 ter(tecPATH_PARTIAL));
1788
-
1789 // bob got 40 USD from partial consumption of the offer
-
1790 jrr = ledgerEntryState(env, bob, gw, "USD");
-
1791 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-40");
-
1792
-
1793 // Alice converts USD to XRP which should fail
-
1794 // due to PartialPayment.
-
1795 env(pay(alice, alice, XRP(600)),
-
1796 sendmax(USD(100)),
-
1797 ter(tecPATH_PARTIAL));
+
1789 // Alice converts USD to XRP, should succeed because
+
1790 // we permit partial payment
+
1791 env(pay(alice, alice, XRP(600)),
+
1792 sendmax(USD(100)),
+
1793 txflags(tfPartialPayment));
+
1794
+
1795 // Verify the offer was consumed
+
1796 jro = ledgerEntryOffer(env, bob, bobOfferSeq);
+
1797 BEAST_EXPECT(jro[jss::error] == "entryNotFound");
1798
-
1799 // Alice converts USD to XRP, should succeed because
-
1800 // we permit partial payment
-
1801 env(pay(alice, alice, XRP(600)),
-
1802 sendmax(USD(100)),
-
1803 txflags(tfPartialPayment));
-
1804
-
1805 // Verify the offer was consumed
-
1806 jro = ledgerEntryOffer(env, bob, bobOfferSeq);
-
1807 BEAST_EXPECT(jro[jss::error] == "entryNotFound");
-
1808
-
1809 // verify balances look right after the partial payment
-
1810 // only 300 XRP should be have been payed since that's all
-
1811 // that remained in the offer from bob. The alice balance is now
-
1812 // 100 USD because another 60 USD were transferred to bob in the second
-
1813 // payment
-
1814 jrr = ledgerEntryState(env, alice, gw, "USD");
-
1815 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
-
1816 jrr = ledgerEntryRoot(env, alice);
-
1817 BEAST_EXPECT(
-
1818 jrr[jss::node][sfBalance.fieldName] ==
-
1819 to_string((XRP(10000) + XRP(200) + XRP(300) -
-
1820 env.current()->fees().base * 4)
-
1821 .xrp()));
-
1822
-
1823 // bob now has 100 USD - 40 from the first payment and 60 from the
-
1824 // second (partial) payment
-
1825 jrr = ledgerEntryState(env, bob, gw, "USD");
-
1826 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
-
1827 }
-
1828
-
1829 void
-
1830 testCrossCurrencyStartXRP(FeatureBitset features)
-
1831 {
-
1832 testcase("Cross Currency Payment: Start with XRP");
+
1799 // verify balances look right after the partial payment
+
1800 // only 300 XRP should be have been payed since that's all
+
1801 // that remained in the offer from bob. The alice balance is now
+
1802 // 100 USD because another 60 USD were transferred to bob in the second
+
1803 // payment
+
1804 jrr = ledgerEntryState(env, alice, gw, "USD");
+
1805 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
+
1806 jrr = ledgerEntryRoot(env, alice);
+
1807 BEAST_EXPECT(
+
1808 jrr[jss::node][sfBalance.fieldName] ==
+
1809 to_string((XRP(10000) + XRP(200) + XRP(300) -
+
1810 env.current()->fees().base * 4)
+
1811 .xrp()));
+
1812
+
1813 // bob now has 100 USD - 40 from the first payment and 60 from the
+
1814 // second (partial) payment
+
1815 jrr = ledgerEntryState(env, bob, gw, "USD");
+
1816 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
+
1817 }
+
1818
+
1819 void
+
1820 testCrossCurrencyStartXRP(FeatureBitset features)
+
1821 {
+
1822 testcase("Cross Currency Payment: Start with XRP");
+
1823
+
1824 using namespace jtx;
+
1825
+
1826 Env env{*this, features};
+
1827
+
1828 auto const gw = Account{"gateway"};
+
1829 auto const alice = Account{"alice"};
+
1830 auto const bob = Account{"bob"};
+
1831 auto const carol = Account{"carol"};
+
1832 auto const USD = gw["USD"];
1833
-
1834 using namespace jtx;
-
1835
-
1836 Env env{*this, features};
-
1837
-
1838 auto const gw = Account{"gateway"};
-
1839 auto const alice = Account{"alice"};
-
1840 auto const bob = Account{"bob"};
-
1841 auto const carol = Account{"carol"};
-
1842 auto const USD = gw["USD"];
-
1843
-
1844 env.fund(XRP(10000), gw, alice, bob, carol);
-
1845 env.close();
+
1834 env.fund(XRP(10000), gw, alice, bob, carol);
+
1835 env.close();
+
1836
+
1837 env(trust(carol, USD(1000)));
+
1838 env(trust(bob, USD(2000)));
+
1839
+
1840 env(pay(gw, carol, carol["USD"](500)));
+
1841
+
1842 auto const carolOfferSeq = env.seq(carol);
+
1843 env(offer(carol, XRP(500), USD(50)));
+
1844
+
1845 env(pay(alice, bob, USD(25)), sendmax(XRP(333)));
1846
-
1847 env(trust(carol, USD(1000)));
-
1848 env(trust(bob, USD(2000)));
+
1847 auto jrr = ledgerEntryState(env, bob, gw, "USD");
+
1848 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-25");
1849
-
1850 env(pay(gw, carol, carol["USD"](500)));
-
1851
-
1852 auto const carolOfferSeq = env.seq(carol);
-
1853 env(offer(carol, XRP(500), USD(50)));
-
1854
-
1855 env(pay(alice, bob, USD(25)), sendmax(XRP(333)));
-
1856
-
1857 auto jrr = ledgerEntryState(env, bob, gw, "USD");
-
1858 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-25");
-
1859
-
1860 jrr = ledgerEntryState(env, carol, gw, "USD");
-
1861 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-475");
-
1862
-
1863 auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
-
1864 BEAST_EXPECT(
-
1865 jro[jss::node][jss::TakerGets] ==
-
1866 USD(25).value().getJson(JsonOptions::none));
-
1867 BEAST_EXPECT(
-
1868 jro[jss::node][jss::TakerPays] == XRP(250).value().getText());
-
1869 }
-
1870
-
1871 void
-
1872 testCrossCurrencyEndXRP(FeatureBitset features)
-
1873 {
-
1874 testcase("Cross Currency Payment: End with XRP");
+
1850 jrr = ledgerEntryState(env, carol, gw, "USD");
+
1851 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-475");
+
1852
+
1853 auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
+
1854 BEAST_EXPECT(
+
1855 jro[jss::node][jss::TakerGets] ==
+
1856 USD(25).value().getJson(JsonOptions::none));
+
1857 BEAST_EXPECT(
+
1858 jro[jss::node][jss::TakerPays] == XRP(250).value().getText());
+
1859 }
+
1860
+
1861 void
+
1862 testCrossCurrencyEndXRP(FeatureBitset features)
+
1863 {
+
1864 testcase("Cross Currency Payment: End with XRP");
+
1865
+
1866 using namespace jtx;
+
1867
+
1868 Env env{*this, features};
+
1869
+
1870 auto const gw = Account{"gateway"};
+
1871 auto const alice = Account{"alice"};
+
1872 auto const bob = Account{"bob"};
+
1873 auto const carol = Account{"carol"};
+
1874 auto const USD = gw["USD"];
1875
-
1876 using namespace jtx;
-
1877
-
1878 Env env{*this, features};
-
1879
-
1880 auto const gw = Account{"gateway"};
-
1881 auto const alice = Account{"alice"};
-
1882 auto const bob = Account{"bob"};
-
1883 auto const carol = Account{"carol"};
-
1884 auto const USD = gw["USD"];
-
1885
-
1886 env.fund(XRP(10000), gw, alice, bob, carol);
-
1887 env.close();
+
1876 env.fund(XRP(10000), gw, alice, bob, carol);
+
1877 env.close();
+
1878
+
1879 env(trust(alice, USD(1000)));
+
1880 env(trust(carol, USD(2000)));
+
1881
+
1882 env(pay(gw, alice, alice["USD"](500)));
+
1883
+
1884 auto const carolOfferSeq = env.seq(carol);
+
1885 env(offer(carol, USD(50), XRP(500)));
+
1886
+
1887 env(pay(alice, bob, XRP(250)), sendmax(USD(333)));
1888
-
1889 env(trust(alice, USD(1000)));
-
1890 env(trust(carol, USD(2000)));
+
1889 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
1890 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-475");
1891
-
1892 env(pay(gw, alice, alice["USD"](500)));
-
1893
-
1894 auto const carolOfferSeq = env.seq(carol);
-
1895 env(offer(carol, USD(50), XRP(500)));
-
1896
-
1897 env(pay(alice, bob, XRP(250)), sendmax(USD(333)));
-
1898
-
1899 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
1900 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-475");
-
1901
-
1902 jrr = ledgerEntryState(env, carol, gw, "USD");
-
1903 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-25");
-
1904
-
1905 jrr = ledgerEntryRoot(env, bob);
-
1906 BEAST_EXPECT(
-
1907 jrr[jss::node][sfBalance.fieldName] ==
-
1908 std::to_string(
-
1909 XRP(10000).value().mantissa() + XRP(250).value().mantissa()));
-
1910
-
1911 auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
-
1912 BEAST_EXPECT(
-
1913 jro[jss::node][jss::TakerGets] == XRP(250).value().getText());
-
1914 BEAST_EXPECT(
-
1915 jro[jss::node][jss::TakerPays] ==
-
1916 USD(25).value().getJson(JsonOptions::none));
-
1917 }
-
1918
-
1919 void
-
1920 testCrossCurrencyBridged(FeatureBitset features)
-
1921 {
-
1922 testcase("Cross Currency Payment: Bridged");
-
1923
-
1924 using namespace jtx;
-
1925
-
1926 Env env{*this, features};
-
1927
-
1928 auto const gw1 = Account{"gateway_1"};
-
1929 auto const gw2 = Account{"gateway_2"};
-
1930 auto const alice = Account{"alice"};
-
1931 auto const bob = Account{"bob"};
-
1932 auto const carol = Account{"carol"};
-
1933 auto const dan = Account{"dan"};
-
1934 auto const USD = gw1["USD"];
-
1935 auto const EUR = gw2["EUR"];
-
1936
-
1937 env.fund(XRP(10000), gw1, gw2, alice, bob, carol, dan);
-
1938 env.close();
-
1939
-
1940 env(trust(alice, USD(1000)));
-
1941 env(trust(bob, EUR(1000)));
-
1942 env(trust(carol, USD(1000)));
-
1943 env(trust(dan, EUR(1000)));
-
1944
-
1945 env(pay(gw1, alice, alice["USD"](500)));
-
1946 env(pay(gw2, dan, dan["EUR"](400)));
+
1892 jrr = ledgerEntryState(env, carol, gw, "USD");
+
1893 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-25");
+
1894
+
1895 jrr = ledgerEntryRoot(env, bob);
+
1896 BEAST_EXPECT(
+
1897 jrr[jss::node][sfBalance.fieldName] ==
+
1898 std::to_string(
+
1899 XRP(10000).value().mantissa() + XRP(250).value().mantissa()));
+
1900
+
1901 auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
+
1902 BEAST_EXPECT(
+
1903 jro[jss::node][jss::TakerGets] == XRP(250).value().getText());
+
1904 BEAST_EXPECT(
+
1905 jro[jss::node][jss::TakerPays] ==
+
1906 USD(25).value().getJson(JsonOptions::none));
+
1907 }
+
1908
+
1909 void
+
1910 testCrossCurrencyBridged(FeatureBitset features)
+
1911 {
+
1912 testcase("Cross Currency Payment: Bridged");
+
1913
+
1914 using namespace jtx;
+
1915
+
1916 Env env{*this, features};
+
1917
+
1918 auto const gw1 = Account{"gateway_1"};
+
1919 auto const gw2 = Account{"gateway_2"};
+
1920 auto const alice = Account{"alice"};
+
1921 auto const bob = Account{"bob"};
+
1922 auto const carol = Account{"carol"};
+
1923 auto const dan = Account{"dan"};
+
1924 auto const USD = gw1["USD"];
+
1925 auto const EUR = gw2["EUR"];
+
1926
+
1927 env.fund(XRP(10000), gw1, gw2, alice, bob, carol, dan);
+
1928 env.close();
+
1929
+
1930 env(trust(alice, USD(1000)));
+
1931 env(trust(bob, EUR(1000)));
+
1932 env(trust(carol, USD(1000)));
+
1933 env(trust(dan, EUR(1000)));
+
1934
+
1935 env(pay(gw1, alice, alice["USD"](500)));
+
1936 env(pay(gw2, dan, dan["EUR"](400)));
+
1937
+
1938 auto const carolOfferSeq = env.seq(carol);
+
1939 env(offer(carol, USD(50), XRP(500)));
+
1940
+
1941 auto const danOfferSeq = env.seq(dan);
+
1942 env(offer(dan, XRP(500), EUR(50)));
+
1943
+
1944 Json::Value jtp{Json::arrayValue};
+
1945 jtp[0u][0u][jss::currency] = "XRP";
+
1946 env(pay(alice, bob, EUR(30)), json(jss::Paths, jtp), sendmax(USD(333)));
1947
-
1948 auto const carolOfferSeq = env.seq(carol);
-
1949 env(offer(carol, USD(50), XRP(500)));
+
1948 auto jrr = ledgerEntryState(env, alice, gw1, "USD");
+
1949 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "470");
1950
-
1951 auto const danOfferSeq = env.seq(dan);
-
1952 env(offer(dan, XRP(500), EUR(50)));
+
1951 jrr = ledgerEntryState(env, bob, gw2, "EUR");
+
1952 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-30");
1953
-
1954 Json::Value jtp{Json::arrayValue};
-
1955 jtp[0u][0u][jss::currency] = "XRP";
-
1956 env(pay(alice, bob, EUR(30)), json(jss::Paths, jtp), sendmax(USD(333)));
-
1957
-
1958 auto jrr = ledgerEntryState(env, alice, gw1, "USD");
-
1959 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "470");
-
1960
-
1961 jrr = ledgerEntryState(env, bob, gw2, "EUR");
-
1962 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-30");
-
1963
-
1964 jrr = ledgerEntryState(env, carol, gw1, "USD");
-
1965 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-30");
+
1954 jrr = ledgerEntryState(env, carol, gw1, "USD");
+
1955 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-30");
+
1956
+
1957 jrr = ledgerEntryState(env, dan, gw2, "EUR");
+
1958 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-370");
+
1959
+
1960 auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
+
1961 BEAST_EXPECT(
+
1962 jro[jss::node][jss::TakerGets] == XRP(200).value().getText());
+
1963 BEAST_EXPECT(
+
1964 jro[jss::node][jss::TakerPays] ==
+
1965 USD(20).value().getJson(JsonOptions::none));
1966
-
1967 jrr = ledgerEntryState(env, dan, gw2, "EUR");
-
1968 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-370");
-
1969
-
1970 auto jro = ledgerEntryOffer(env, carol, carolOfferSeq);
+
1967 jro = ledgerEntryOffer(env, dan, danOfferSeq);
+
1968 BEAST_EXPECT(
+
1969 jro[jss::node][jss::TakerGets] ==
+
1970 gw2["EUR"](20).value().getJson(JsonOptions::none));
1971 BEAST_EXPECT(
-
1972 jro[jss::node][jss::TakerGets] == XRP(200).value().getText());
-
1973 BEAST_EXPECT(
-
1974 jro[jss::node][jss::TakerPays] ==
-
1975 USD(20).value().getJson(JsonOptions::none));
-
1976
-
1977 jro = ledgerEntryOffer(env, dan, danOfferSeq);
-
1978 BEAST_EXPECT(
-
1979 jro[jss::node][jss::TakerGets] ==
-
1980 gw2["EUR"](20).value().getJson(JsonOptions::none));
-
1981 BEAST_EXPECT(
-
1982 jro[jss::node][jss::TakerPays] == XRP(200).value().getText());
-
1983 }
-
1984
-
1985 void
-
1986 testBridgedSecondLegDry(FeatureBitset features)
-
1987 {
-
1988 // At least with Taker bridging, a sensitivity was identified if the
-
1989 // second leg goes dry before the first one. This test exercises that
-
1990 // case.
-
1991 testcase("Auto Bridged Second Leg Dry");
+
1972 jro[jss::node][jss::TakerPays] == XRP(200).value().getText());
+
1973 }
+
1974
+
1975 void
+
1976 testBridgedSecondLegDry(FeatureBitset features)
+
1977 {
+
1978 // At least with Taker bridging, a sensitivity was identified if the
+
1979 // second leg goes dry before the first one. This test exercises that
+
1980 // case.
+
1981 testcase("Auto Bridged Second Leg Dry");
+
1982
+
1983 using namespace jtx;
+
1984 Env env(*this, features);
+
1985
+
1986 Account const alice{"alice"};
+
1987 Account const bob{"bob"};
+
1988 Account const carol{"carol"};
+
1989 Account const gw{"gateway"};
+
1990 auto const USD = gw["USD"];
+
1991 auto const EUR = gw["EUR"];
1992
-
1993 using namespace jtx;
-
1994 Env env(*this, features);
+
1993 env.fund(XRP(100000000), alice, bob, carol, gw);
+
1994 env.close();
1995
-
1996 Account const alice{"alice"};
-
1997 Account const bob{"bob"};
-
1998 Account const carol{"carol"};
-
1999 Account const gw{"gateway"};
-
2000 auto const USD = gw["USD"];
-
2001 auto const EUR = gw["EUR"];
+
1996 env.trust(USD(10), alice);
+
1997 env.close();
+
1998 env(pay(gw, alice, USD(10)));
+
1999 env.trust(USD(10), carol);
+
2000 env.close();
+
2001 env(pay(gw, carol, USD(3)));
2002
-
2003 env.fund(XRP(100000000), alice, bob, carol, gw);
-
2004 env.close();
+
2003 env(offer(alice, EUR(2), XRP(1)));
+
2004 env(offer(alice, EUR(2), XRP(1)));
2005
-
2006 env.trust(USD(10), alice);
-
2007 env.close();
-
2008 env(pay(gw, alice, USD(10)));
-
2009 env.trust(USD(10), carol);
-
2010 env.close();
-
2011 env(pay(gw, carol, USD(3)));
-
2012
-
2013 env(offer(alice, EUR(2), XRP(1)));
-
2014 env(offer(alice, EUR(2), XRP(1)));
-
2015
-
2016 env(offer(alice, XRP(1), USD(4)));
-
2017 env(offer(carol, XRP(1), USD(3)));
-
2018 env.close();
-
2019
-
2020 // Bob offers to buy 10 USD for 10 EUR.
-
2021 // 1. He spends 2 EUR taking Alice's auto-bridged offers and
-
2022 // gets 4 USD for that.
-
2023 // 2. He spends another 2 EUR taking Alice's last EUR->XRP offer and
-
2024 // Carol's XRP-USD offer. He gets 3 USD for that.
-
2025 // The key for this test is that Alice's XRP->USD leg goes dry before
-
2026 // Alice's EUR->XRP. The XRP->USD leg is the second leg which showed
-
2027 // some sensitivity.
-
2028 env.trust(EUR(10), bob);
-
2029 env.close();
-
2030 env(pay(gw, bob, EUR(10)));
-
2031 env.close();
-
2032 env(offer(bob, USD(10), EUR(10)));
-
2033 env.close();
+
2006 env(offer(alice, XRP(1), USD(4)));
+
2007 env(offer(carol, XRP(1), USD(3)));
+
2008 env.close();
+
2009
+
2010 // Bob offers to buy 10 USD for 10 EUR.
+
2011 // 1. He spends 2 EUR taking Alice's auto-bridged offers and
+
2012 // gets 4 USD for that.
+
2013 // 2. He spends another 2 EUR taking Alice's last EUR->XRP offer and
+
2014 // Carol's XRP-USD offer. He gets 3 USD for that.
+
2015 // The key for this test is that Alice's XRP->USD leg goes dry before
+
2016 // Alice's EUR->XRP. The XRP->USD leg is the second leg which showed
+
2017 // some sensitivity.
+
2018 env.trust(EUR(10), bob);
+
2019 env.close();
+
2020 env(pay(gw, bob, EUR(10)));
+
2021 env.close();
+
2022 env(offer(bob, USD(10), EUR(10)));
+
2023 env.close();
+
2024
+
2025 env.require(balance(bob, USD(7)));
+
2026 env.require(balance(bob, EUR(6)));
+
2027 env.require(offers(bob, 1));
+
2028 env.require(owners(bob, 3));
+
2029
+
2030 env.require(balance(alice, USD(6)));
+
2031 env.require(balance(alice, EUR(4)));
+
2032 env.require(offers(alice, 0));
+
2033 env.require(owners(alice, 2));
2034
-
2035 env.require(balance(bob, USD(7)));
-
2036 env.require(balance(bob, EUR(6)));
-
2037 env.require(offers(bob, 1));
-
2038 env.require(owners(bob, 3));
-
2039
-
2040 env.require(balance(alice, USD(6)));
-
2041 env.require(balance(alice, EUR(4)));
-
2042 env.require(offers(alice, 0));
-
2043 env.require(owners(alice, 2));
-
2044
-
2045 env.require(balance(carol, USD(0)));
-
2046 env.require(balance(carol, EUR(none)));
-
2047 // If neither featureFlowCross nor fixTakerDryOfferRemoval are defined
-
2048 // then carol's offer will be left on the books, but with zero value.
-
2049 int const emptyOfferCount{
-
2050 features[featureFlowCross] || features[fixTakerDryOfferRemoval]
-
2051 ? 0
-
2052 : 1};
-
2053
-
2054 env.require(offers(carol, 0 + emptyOfferCount));
-
2055 env.require(owners(carol, 1 + emptyOfferCount));
-
2056 }
-
2057
-
2058 void
-
2059 testOfferFeesConsumeFunds(FeatureBitset features)
-
2060 {
-
2061 testcase("Offer Fees Consume Funds");
-
2062
-
2063 using namespace jtx;
-
2064
-
2065 Env env{*this, features};
-
2066
-
2067 auto const gw1 = Account{"gateway_1"};
-
2068 auto const gw2 = Account{"gateway_2"};
-
2069 auto const gw3 = Account{"gateway_3"};
-
2070 auto const alice = Account{"alice"};
-
2071 auto const bob = Account{"bob"};
-
2072 auto const USD1 = gw1["USD"];
-
2073 auto const USD2 = gw2["USD"];
-
2074 auto const USD3 = gw3["USD"];
-
2075
-
2076 // Provide micro amounts to compensate for fees to make results round
-
2077 // nice.
-
2078 // reserve: Alice has 3 entries in the ledger, via trust lines
-
2079 // fees:
-
2080 // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) +
-
2081 // 1 for payment == 4
-
2082 auto const starting_xrp = XRP(100) +
-
2083 env.current()->fees().accountReserve(3) +
-
2084 env.current()->fees().base * 4;
+
2035 env.require(balance(carol, USD(0)));
+
2036 env.require(balance(carol, EUR(none)));
+
2037
+
2038 env.require(offers(carol, 0));
+
2039 env.require(owners(carol, 1));
+
2040 }
+
2041
+
2042 void
+
2043 testOfferFeesConsumeFunds(FeatureBitset features)
+
2044 {
+
2045 testcase("Offer Fees Consume Funds");
+
2046
+
2047 using namespace jtx;
+
2048
+
2049 Env env{*this, features};
+
2050
+
2051 auto const gw1 = Account{"gateway_1"};
+
2052 auto const gw2 = Account{"gateway_2"};
+
2053 auto const gw3 = Account{"gateway_3"};
+
2054 auto const alice = Account{"alice"};
+
2055 auto const bob = Account{"bob"};
+
2056 auto const USD1 = gw1["USD"];
+
2057 auto const USD2 = gw2["USD"];
+
2058 auto const USD3 = gw3["USD"];
+
2059
+
2060 // Provide micro amounts to compensate for fees to make results round
+
2061 // nice.
+
2062 // reserve: Alice has 3 entries in the ledger, via trust lines
+
2063 // fees:
+
2064 // 1 for each trust limit == 3 (alice < mtgox/amazon/bitstamp) +
+
2065 // 1 for payment == 4
+
2066 auto const starting_xrp = XRP(100) +
+
2067 env.current()->fees().accountReserve(3) +
+
2068 env.current()->fees().base * 4;
+
2069
+
2070 env.fund(starting_xrp, gw1, gw2, gw3, alice, bob);
+
2071 env.close();
+
2072
+
2073 env(trust(alice, USD1(1000)));
+
2074 env(trust(alice, USD2(1000)));
+
2075 env(trust(alice, USD3(1000)));
+
2076 env(trust(bob, USD1(1000)));
+
2077 env(trust(bob, USD2(1000)));
+
2078
+
2079 env(pay(gw1, bob, bob["USD"](500)));
+
2080
+
2081 env(offer(bob, XRP(200), USD1(200)));
+
2082 // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
+
2083 // Ask for more than available to prove reserve works.
+
2084 env(offer(alice, USD1(200), XRP(200)));
2085
-
2086 env.fund(starting_xrp, gw1, gw2, gw3, alice, bob);
-
2087 env.close();
-
2088
-
2089 env(trust(alice, USD1(1000)));
-
2090 env(trust(alice, USD2(1000)));
-
2091 env(trust(alice, USD3(1000)));
-
2092 env(trust(bob, USD1(1000)));
-
2093 env(trust(bob, USD2(1000)));
-
2094
-
2095 env(pay(gw1, bob, bob["USD"](500)));
+
2086 auto jrr = ledgerEntryState(env, alice, gw1, "USD");
+
2087 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "100");
+
2088 jrr = ledgerEntryRoot(env, alice);
+
2089 BEAST_EXPECT(
+
2090 jrr[jss::node][sfBalance.fieldName] ==
+
2091 STAmount(env.current()->fees().accountReserve(3)).getText());
+
2092
+
2093 jrr = ledgerEntryState(env, bob, gw1, "USD");
+
2094 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
+
2095 }
2096
-
2097 env(offer(bob, XRP(200), USD1(200)));
-
2098 // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
-
2099 // Ask for more than available to prove reserve works.
-
2100 env(offer(alice, USD1(200), XRP(200)));
+
2097 void
+
2098 testOfferCreateThenCross(FeatureBitset features)
+
2099 {
+
2100 testcase("Offer Create, then Cross");
2101
-
2102 auto jrr = ledgerEntryState(env, alice, gw1, "USD");
-
2103 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "100");
-
2104 jrr = ledgerEntryRoot(env, alice);
-
2105 BEAST_EXPECT(
-
2106 jrr[jss::node][sfBalance.fieldName] ==
-
2107 STAmount(env.current()->fees().accountReserve(3)).getText());
-
2108
-
2109 jrr = ledgerEntryState(env, bob, gw1, "USD");
-
2110 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
-
2111 }
-
2112
-
2113 void
-
2114 testOfferCreateThenCross(FeatureBitset features)
-
2115 {
-
2116 testcase("Offer Create, then Cross");
-
2117
-
2118 using namespace jtx;
+
2102 using namespace jtx;
+
2103
+
2104 for (auto NumberSwitchOver : {false, true})
+
2105 {
+
2106 Env env{*this, features};
+
2107 if (NumberSwitchOver)
+
2108 env.enableFeature(fixUniversalNumber);
+
2109 else
+
2110 env.disableFeature(fixUniversalNumber);
+
2111
+
2112 auto const gw = Account{"gateway"};
+
2113 auto const alice = Account{"alice"};
+
2114 auto const bob = Account{"bob"};
+
2115 auto const USD = gw["USD"];
+
2116
+
2117 env.fund(XRP(10000), gw, alice, bob);
+
2118 env.close();
2119
-
2120 for (auto NumberSwitchOver : {false, true})
-
2121 {
-
2122 Env env{*this, features};
-
2123 if (NumberSwitchOver)
-
2124 env.enableFeature(fixUniversalNumber);
-
2125 else
-
2126 env.disableFeature(fixUniversalNumber);
-
2127
-
2128 auto const gw = Account{"gateway"};
-
2129 auto const alice = Account{"alice"};
-
2130 auto const bob = Account{"bob"};
-
2131 auto const USD = gw["USD"];
-
2132
-
2133 env.fund(XRP(10000), gw, alice, bob);
-
2134 env.close();
-
2135
-
2136 env(rate(gw, 1.005));
-
2137
-
2138 env(trust(alice, USD(1000)));
-
2139 env(trust(bob, USD(1000)));
-
2140 env(trust(gw, alice["USD"](50)));
-
2141
-
2142 env(pay(gw, bob, bob["USD"](1)));
-
2143 env(pay(alice, gw, USD(50)));
-
2144
-
2145 env(trust(gw, alice["USD"](0)));
-
2146
-
2147 env(offer(alice, USD(50), XRP(150000)));
-
2148 env(offer(bob, XRP(100), USD(0.1)));
-
2149
-
2150 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
2151 BEAST_EXPECT(
-
2152 jrr[jss::node][sfBalance.fieldName][jss::value] ==
-
2153 "49.96666666666667");
-
2154
-
2155 jrr = ledgerEntryState(env, bob, gw, "USD");
-
2156 Json::Value const bobsUSD =
-
2157 jrr[jss::node][sfBalance.fieldName][jss::value];
-
2158 if (!NumberSwitchOver)
-
2159 {
-
2160 BEAST_EXPECT(bobsUSD == "-0.966500000033334");
-
2161 }
-
2162 else
-
2163 {
-
2164 BEAST_EXPECT(bobsUSD == "-0.9665000000333333");
-
2165 }
-
2166 }
-
2167 }
-
2168
-
2169 void
-
2170 testSellFlagBasic(FeatureBitset features)
-
2171 {
-
2172 testcase("Offer tfSell: Basic Sell");
+
2120 env(rate(gw, 1.005));
+
2121
+
2122 env(trust(alice, USD(1000)));
+
2123 env(trust(bob, USD(1000)));
+
2124 env(trust(gw, alice["USD"](50)));
+
2125
+
2126 env(pay(gw, bob, bob["USD"](1)));
+
2127 env(pay(alice, gw, USD(50)));
+
2128
+
2129 env(trust(gw, alice["USD"](0)));
+
2130
+
2131 env(offer(alice, USD(50), XRP(150000)));
+
2132 env(offer(bob, XRP(100), USD(0.1)));
+
2133
+
2134 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
2135 BEAST_EXPECT(
+
2136 jrr[jss::node][sfBalance.fieldName][jss::value] ==
+
2137 "49.96666666666667");
+
2138
+
2139 jrr = ledgerEntryState(env, bob, gw, "USD");
+
2140 Json::Value const bobsUSD =
+
2141 jrr[jss::node][sfBalance.fieldName][jss::value];
+
2142 if (!NumberSwitchOver)
+
2143 {
+
2144 BEAST_EXPECT(bobsUSD == "-0.966500000033334");
+
2145 }
+
2146 else
+
2147 {
+
2148 BEAST_EXPECT(bobsUSD == "-0.9665000000333333");
+
2149 }
+
2150 }
+
2151 }
+
2152
+
2153 void
+
2154 testSellFlagBasic(FeatureBitset features)
+
2155 {
+
2156 testcase("Offer tfSell: Basic Sell");
+
2157
+
2158 using namespace jtx;
+
2159
+
2160 Env env{*this, features};
+
2161
+
2162 auto const gw = Account{"gateway"};
+
2163 auto const alice = Account{"alice"};
+
2164 auto const bob = Account{"bob"};
+
2165 auto const USD = gw["USD"];
+
2166
+
2167 auto const starting_xrp = XRP(100) +
+
2168 env.current()->fees().accountReserve(1) +
+
2169 env.current()->fees().base * 2;
+
2170
+
2171 env.fund(starting_xrp, gw, alice, bob);
+
2172 env.close();
2173
-
2174 using namespace jtx;
-
2175
-
2176 Env env{*this, features};
-
2177
-
2178 auto const gw = Account{"gateway"};
-
2179 auto const alice = Account{"alice"};
-
2180 auto const bob = Account{"bob"};
-
2181 auto const USD = gw["USD"];
-
2182
-
2183 auto const starting_xrp = XRP(100) +
-
2184 env.current()->fees().accountReserve(1) +
-
2185 env.current()->fees().base * 2;
-
2186
-
2187 env.fund(starting_xrp, gw, alice, bob);
-
2188 env.close();
-
2189
-
2190 env(trust(alice, USD(1000)));
-
2191 env(trust(bob, USD(1000)));
-
2192
-
2193 env(pay(gw, bob, bob["USD"](500)));
-
2194
-
2195 env(offer(bob, XRP(200), USD(200)), json(jss::Flags, tfSell));
-
2196 // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
-
2197 // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
-
2198 // Ask for more than available to prove reserve works.
-
2199 env(offer(alice, USD(200), XRP(200)), json(jss::Flags, tfSell));
+
2174 env(trust(alice, USD(1000)));
+
2175 env(trust(bob, USD(1000)));
+
2176
+
2177 env(pay(gw, bob, bob["USD"](500)));
+
2178
+
2179 env(offer(bob, XRP(200), USD(200)), json(jss::Flags, tfSell));
+
2180 // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
+
2181 // Alice has 350 + fees - a reserve of 50 = 250 reserve = 100 available.
+
2182 // Ask for more than available to prove reserve works.
+
2183 env(offer(alice, USD(200), XRP(200)), json(jss::Flags, tfSell));
+
2184
+
2185 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
2186 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
+
2187 jrr = ledgerEntryRoot(env, alice);
+
2188 BEAST_EXPECT(
+
2189 jrr[jss::node][sfBalance.fieldName] ==
+
2190 STAmount(env.current()->fees().accountReserve(1)).getText());
+
2191
+
2192 jrr = ledgerEntryState(env, bob, gw, "USD");
+
2193 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
+
2194 }
+
2195
+
2196 void
+
2197 testSellFlagExceedLimit(FeatureBitset features)
+
2198 {
+
2199 testcase("Offer tfSell: 2x Sell Exceed Limit");
2200
-
2201 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
2202 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-100");
-
2203 jrr = ledgerEntryRoot(env, alice);
-
2204 BEAST_EXPECT(
-
2205 jrr[jss::node][sfBalance.fieldName] ==
-
2206 STAmount(env.current()->fees().accountReserve(1)).getText());
-
2207
-
2208 jrr = ledgerEntryState(env, bob, gw, "USD");
-
2209 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-400");
-
2210 }
-
2211
-
2212 void
-
2213 testSellFlagExceedLimit(FeatureBitset features)
-
2214 {
-
2215 testcase("Offer tfSell: 2x Sell Exceed Limit");
+
2201 using namespace jtx;
+
2202
+
2203 Env env{*this, features};
+
2204
+
2205 auto const gw = Account{"gateway"};
+
2206 auto const alice = Account{"alice"};
+
2207 auto const bob = Account{"bob"};
+
2208 auto const USD = gw["USD"];
+
2209
+
2210 auto const starting_xrp = XRP(100) +
+
2211 env.current()->fees().accountReserve(1) +
+
2212 env.current()->fees().base * 2;
+
2213
+
2214 env.fund(starting_xrp, gw, alice, bob);
+
2215 env.close();
2216
-
2217 using namespace jtx;
-
2218
-
2219 Env env{*this, features};
-
2220
-
2221 auto const gw = Account{"gateway"};
-
2222 auto const alice = Account{"alice"};
-
2223 auto const bob = Account{"bob"};
-
2224 auto const USD = gw["USD"];
-
2225
-
2226 auto const starting_xrp = XRP(100) +
-
2227 env.current()->fees().accountReserve(1) +
-
2228 env.current()->fees().base * 2;
+
2217 env(trust(alice, USD(150)));
+
2218 env(trust(bob, USD(1000)));
+
2219
+
2220 env(pay(gw, bob, bob["USD"](500)));
+
2221
+
2222 env(offer(bob, XRP(100), USD(200)));
+
2223 // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
+
2224 // Ask for more than available to prove reserve works.
+
2225 // Taker pays 100 USD for 100 XRP.
+
2226 // Selling XRP.
+
2227 // Will sell all 100 XRP and get more USD than asked for.
+
2228 env(offer(alice, USD(100), XRP(100)), json(jss::Flags, tfSell));
2229
-
2230 env.fund(starting_xrp, gw, alice, bob);
-
2231 env.close();
-
2232
-
2233 env(trust(alice, USD(150)));
-
2234 env(trust(bob, USD(1000)));
-
2235
-
2236 env(pay(gw, bob, bob["USD"](500)));
-
2237
-
2238 env(offer(bob, XRP(100), USD(200)));
-
2239 // Alice has 350 fees - a reserve of 50 = 250 reserve = 100 available.
-
2240 // Ask for more than available to prove reserve works.
-
2241 // Taker pays 100 USD for 100 XRP.
-
2242 // Selling XRP.
-
2243 // Will sell all 100 XRP and get more USD than asked for.
-
2244 env(offer(alice, USD(100), XRP(100)), json(jss::Flags, tfSell));
+
2230 auto jrr = ledgerEntryState(env, alice, gw, "USD");
+
2231 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-200");
+
2232 jrr = ledgerEntryRoot(env, alice);
+
2233 BEAST_EXPECT(
+
2234 jrr[jss::node][sfBalance.fieldName] ==
+
2235 STAmount(env.current()->fees().accountReserve(1)).getText());
+
2236
+
2237 jrr = ledgerEntryState(env, bob, gw, "USD");
+
2238 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300");
+
2239 }
+
2240
+
2241 void
+
2242 testGatewayCrossCurrency(FeatureBitset features)
+
2243 {
+
2244 testcase("Client Issue #535: Gateway Cross Currency");
2245
-
2246 auto jrr = ledgerEntryState(env, alice, gw, "USD");
-
2247 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-200");
-
2248 jrr = ledgerEntryRoot(env, alice);
-
2249 BEAST_EXPECT(
-
2250 jrr[jss::node][sfBalance.fieldName] ==
-
2251 STAmount(env.current()->fees().accountReserve(1)).getText());
-
2252
-
2253 jrr = ledgerEntryState(env, bob, gw, "USD");
-
2254 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-300");
-
2255 }
-
2256
-
2257 void
-
2258 testGatewayCrossCurrency(FeatureBitset features)
-
2259 {
-
2260 testcase("Client Issue #535: Gateway Cross Currency");
-
2261
-
2262 using namespace jtx;
-
2263
-
2264 Env env{*this, features};
-
2265
-
2266 auto const gw = Account{"gateway"};
-
2267 auto const alice = Account{"alice"};
-
2268 auto const bob = Account{"bob"};
-
2269 auto const XTS = gw["XTS"];
-
2270 auto const XXX = gw["XXX"];
-
2271
-
2272 auto const starting_xrp = XRP(100.1) +
-
2273 env.current()->fees().accountReserve(1) +
-
2274 env.current()->fees().base * 2;
-
2275
-
2276 env.fund(starting_xrp, gw, alice, bob);
-
2277 env.close();
-
2278
-
2279 env(trust(alice, XTS(1000)));
-
2280 env(trust(alice, XXX(1000)));
-
2281 env(trust(bob, XTS(1000)));
-
2282 env(trust(bob, XXX(1000)));
-
2283
-
2284 env(pay(gw, alice, alice["XTS"](100)));
-
2285 env(pay(gw, alice, alice["XXX"](100)));
-
2286 env(pay(gw, bob, bob["XTS"](100)));
-
2287 env(pay(gw, bob, bob["XXX"](100)));
-
2288
-
2289 env(offer(alice, XTS(100), XXX(100)));
-
2290
-
2291 // WS client is used here because the RPC client could not
-
2292 // be convinced to pass the build_path argument
-
2293 auto wsc = makeWSClient(env.app().config());
-
2294 Json::Value payment;
-
2295 payment[jss::secret] = toBase58(generateSeed("bob"));
-
2296 payment[jss::id] = env.seq(bob);
-
2297 payment[jss::build_path] = true;
-
2298 payment[jss::tx_json] = pay(bob, bob, bob["XXX"](1));
-
2299 payment[jss::tx_json][jss::Sequence] =
-
2300 env.current()
-
2301 ->read(keylet::account(bob.id()))
-
2302 ->getFieldU32(sfSequence);
-
2303 payment[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base);
-
2304 payment[jss::tx_json][jss::SendMax] =
-
2305 bob["XTS"](1.5).value().getJson(JsonOptions::none);
-
2306 auto jrr = wsc->invoke("submit", payment);
-
2307 BEAST_EXPECT(jrr[jss::status] == "success");
-
2308 BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
-
2309 if (wsc->version() == 2)
-
2310 {
-
2311 BEAST_EXPECT(
-
2312 jrr.isMember(jss::jsonrpc) && jrr[jss::jsonrpc] == "2.0");
-
2313 BEAST_EXPECT(
-
2314 jrr.isMember(jss::ripplerpc) && jrr[jss::ripplerpc] == "2.0");
-
2315 BEAST_EXPECT(jrr.isMember(jss::id) && jrr[jss::id] == 5);
-
2316 }
-
2317
-
2318 jrr = ledgerEntryState(env, alice, gw, "XTS");
-
2319 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
-
2320 jrr = ledgerEntryState(env, alice, gw, "XXX");
-
2321 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
-
2322
-
2323 jrr = ledgerEntryState(env, bob, gw, "XTS");
-
2324 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
-
2325 jrr = ledgerEntryState(env, bob, gw, "XXX");
-
2326 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
-
2327 }
-
2328
-
2329 // Helper function that validates a *defaulted* trustline: one that has
-
2330 // no unusual flags set and doesn't have high or low limits set. Such a
-
2331 // trustline may have an actual balance (it can be created automatically
-
2332 // if a user places an offer to acquire an IOU for which they don't have
-
2333 // a trust line defined). If the trustline is not defaulted then the tests
-
2334 // will not pass.
-
2335 void
-
2336 verifyDefaultTrustline(
-
2337 jtx::Env& env,
-
2338 jtx::Account const& account,
-
2339 jtx::PrettyAmount const& expectBalance)
-
2340 {
-
2341 auto const sleTrust =
-
2342 env.le(keylet::line(account.id(), expectBalance.value().issue()));
-
2343 BEAST_EXPECT(sleTrust);
-
2344 if (sleTrust)
-
2345 {
-
2346 Issue const issue = expectBalance.value().issue();
-
2347 bool const accountLow = account.id() < issue.account;
-
2348
-
2349 STAmount low{issue};
-
2350 STAmount high{issue};
-
2351
-
2352 low.setIssuer(accountLow ? account.id() : issue.account);
-
2353 high.setIssuer(accountLow ? issue.account : account.id());
-
2354
-
2355 BEAST_EXPECT(sleTrust->getFieldAmount(sfLowLimit) == low);
-
2356 BEAST_EXPECT(sleTrust->getFieldAmount(sfHighLimit) == high);
+
2246 using namespace jtx;
+
2247
+
2248 Env env{*this, features};
+
2249
+
2250 auto const gw = Account{"gateway"};
+
2251 auto const alice = Account{"alice"};
+
2252 auto const bob = Account{"bob"};
+
2253 auto const XTS = gw["XTS"];
+
2254 auto const XXX = gw["XXX"];
+
2255
+
2256 auto const starting_xrp = XRP(100.1) +
+
2257 env.current()->fees().accountReserve(1) +
+
2258 env.current()->fees().base * 2;
+
2259
+
2260 env.fund(starting_xrp, gw, alice, bob);
+
2261 env.close();
+
2262
+
2263 env(trust(alice, XTS(1000)));
+
2264 env(trust(alice, XXX(1000)));
+
2265 env(trust(bob, XTS(1000)));
+
2266 env(trust(bob, XXX(1000)));
+
2267
+
2268 env(pay(gw, alice, alice["XTS"](100)));
+
2269 env(pay(gw, alice, alice["XXX"](100)));
+
2270 env(pay(gw, bob, bob["XTS"](100)));
+
2271 env(pay(gw, bob, bob["XXX"](100)));
+
2272
+
2273 env(offer(alice, XTS(100), XXX(100)));
+
2274
+
2275 // WS client is used here because the RPC client could not
+
2276 // be convinced to pass the build_path argument
+
2277 auto wsc = makeWSClient(env.app().config());
+
2278 Json::Value payment;
+
2279 payment[jss::secret] = toBase58(generateSeed("bob"));
+
2280 payment[jss::id] = env.seq(bob);
+
2281 payment[jss::build_path] = true;
+
2282 payment[jss::tx_json] = pay(bob, bob, bob["XXX"](1));
+
2283 payment[jss::tx_json][jss::Sequence] =
+
2284 env.current()
+
2285 ->read(keylet::account(bob.id()))
+
2286 ->getFieldU32(sfSequence);
+
2287 payment[jss::tx_json][jss::Fee] = to_string(env.current()->fees().base);
+
2288 payment[jss::tx_json][jss::SendMax] =
+
2289 bob["XTS"](1.5).value().getJson(JsonOptions::none);
+
2290 auto jrr = wsc->invoke("submit", payment);
+
2291 BEAST_EXPECT(jrr[jss::status] == "success");
+
2292 BEAST_EXPECT(jrr[jss::result][jss::engine_result] == "tesSUCCESS");
+
2293 if (wsc->version() == 2)
+
2294 {
+
2295 BEAST_EXPECT(
+
2296 jrr.isMember(jss::jsonrpc) && jrr[jss::jsonrpc] == "2.0");
+
2297 BEAST_EXPECT(
+
2298 jrr.isMember(jss::ripplerpc) && jrr[jss::ripplerpc] == "2.0");
+
2299 BEAST_EXPECT(jrr.isMember(jss::id) && jrr[jss::id] == 5);
+
2300 }
+
2301
+
2302 jrr = ledgerEntryState(env, alice, gw, "XTS");
+
2303 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
+
2304 jrr = ledgerEntryState(env, alice, gw, "XXX");
+
2305 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
+
2306
+
2307 jrr = ledgerEntryState(env, bob, gw, "XTS");
+
2308 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-99");
+
2309 jrr = ledgerEntryState(env, bob, gw, "XXX");
+
2310 BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
+
2311 }
+
2312
+
2313 // Helper function that validates a *defaulted* trustline: one that has
+
2314 // no unusual flags set and doesn't have high or low limits set. Such a
+
2315 // trustline may have an actual balance (it can be created automatically
+
2316 // if a user places an offer to acquire an IOU for which they don't have
+
2317 // a trust line defined). If the trustline is not defaulted then the tests
+
2318 // will not pass.
+
2319 void
+
2320 verifyDefaultTrustline(
+
2321 jtx::Env& env,
+
2322 jtx::Account const& account,
+
2323 jtx::PrettyAmount const& expectBalance)
+
2324 {
+
2325 auto const sleTrust =
+
2326 env.le(keylet::line(account.id(), expectBalance.value().issue()));
+
2327 BEAST_EXPECT(sleTrust);
+
2328 if (sleTrust)
+
2329 {
+
2330 Issue const issue = expectBalance.value().issue();
+
2331 bool const accountLow = account.id() < issue.account;
+
2332
+
2333 STAmount low{issue};
+
2334 STAmount high{issue};
+
2335
+
2336 low.setIssuer(accountLow ? account.id() : issue.account);
+
2337 high.setIssuer(accountLow ? issue.account : account.id());
+
2338
+
2339 BEAST_EXPECT(sleTrust->getFieldAmount(sfLowLimit) == low);
+
2340 BEAST_EXPECT(sleTrust->getFieldAmount(sfHighLimit) == high);
+
2341
+
2342 STAmount actualBalance{sleTrust->getFieldAmount(sfBalance)};
+
2343 if (!accountLow)
+
2344 actualBalance.negate();
+
2345
+
2346 BEAST_EXPECT(actualBalance == expectBalance);
+
2347 }
+
2348 }
+
2349
+
2350 void
+
2351 testPartialCross(FeatureBitset features)
+
2352 {
+
2353 // Test a number of different corner cases regarding adding a
+
2354 // possibly crossable offer to an account. The test is table
+
2355 // driven so it should be easy to add or remove tests.
+
2356 testcase("Partial Crossing");
2357
-
2358 STAmount actualBalance{sleTrust->getFieldAmount(sfBalance)};
-
2359 if (!accountLow)
-
2360 actualBalance.negate();
-
2361
-
2362 BEAST_EXPECT(actualBalance == expectBalance);
-
2363 }
-
2364 }
-
2365
-
2366 void
-
2367 testPartialCross(FeatureBitset features)
-
2368 {
-
2369 // Test a number of different corner cases regarding adding a
-
2370 // possibly crossable offer to an account. The test is table
-
2371 // driven so it should be easy to add or remove tests.
-
2372 testcase("Partial Crossing");
-
2373
-
2374 using namespace jtx;
-
2375
-
2376 auto const gw = Account("gateway");
-
2377 auto const USD = gw["USD"];
-
2378
-
2379 Env env{*this, features};
-
2380
-
2381 env.fund(XRP(10000000), gw);
-
2382 env.close();
-
2383
-
2384 // The fee that's charged for transactions
-
2385 auto const f = env.current()->fees().base;
+
2358 using namespace jtx;
+
2359
+
2360 auto const gw = Account("gateway");
+
2361 auto const USD = gw["USD"];
+
2362
+
2363 Env env{*this, features};
+
2364
+
2365 env.fund(XRP(10000000), gw);
+
2366 env.close();
+
2367
+
2368 // The fee that's charged for transactions
+
2369 auto const f = env.current()->fees().base;
+
2370
+
2371 // To keep things simple all offers are 1 : 1 for XRP : USD.
+
2372 enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
+
2373 struct TestData
+
2374 {
+
2375 std::string account; // Account operated on
+
2376 STAmount fundXrp; // Account funded with
+
2377 int bookAmount; // USD -> XRP offer on the books
+
2378 preTrustType preTrust; // If true, pre-establish trust line
+
2379 int offerAmount; // Account offers this much XRP -> USD
+
2380 TER tec; // Returned tec code
+
2381 STAmount spentXrp; // Amount removed from fundXrp
+
2382 PrettyAmount balanceUsd; // Balance on account end
+
2383 int offers; // Offers on account
+
2384 int owners; // Owners on account
+
2385 };
2386
-
2387 // To keep things simple all offers are 1 : 1 for XRP : USD.
-
2388 enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
-
2389 struct TestData
-
2390 {
-
2391 std::string account; // Account operated on
-
2392 STAmount fundXrp; // Account funded with
-
2393 int bookAmount; // USD -> XRP offer on the books
-
2394 preTrustType preTrust; // If true, pre-establish trust line
-
2395 int offerAmount; // Account offers this much XRP -> USD
-
2396 TER tec; // Returned tec code
-
2397 STAmount spentXrp; // Amount removed from fundXrp
-
2398 PrettyAmount balanceUsd; // Balance on account end
-
2399 int offers; // Offers on account
-
2400 int owners; // Owners on account
-
2401 };
-
2402
-
2403 // clang-format off
-
2404 TestData const tests[]{
-
2405 // acct fundXrp bookAmt preTrust offerAmt tec spentXrp balanceUSD offers owners
-
2406 {"ann", reserve(env, 0) + 0 * f, 1, noPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0}, // Account is at the reserve, and will dip below once fees are subtracted.
-
2407 {"bev", reserve(env, 0) + 1 * f, 1, noPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0}, // Account has just enough for the reserve and the fee.
-
2408 {"cam", reserve(env, 0) + 2 * f, 0, noPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0}, // Account has enough for the reserve, the fee and the offer, and a bit more, but not enough for the reserve after the offer is placed.
-
2409 {"deb", drops(10) + reserve(env, 0) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, drops(10) + f, USD(0.00001), 0, 1}, // Account has enough to buy a little USD then the offer runs dry.
-
2410 {"eve", reserve(env, 1) + 0 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1}, // No offer to cross
-
2411 {"flo", reserve(env, 1) + 0 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
-
2412 {"gay", reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 50) + f, USD( 50), 0, 1},
-
2413 {"hye", XRP(1000) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 800) + f, USD( 800), 0, 1},
-
2414 {"ivy", XRP( 1) + reserve(env, 1) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
-
2415 {"joy", XRP( 1) + reserve(env, 2) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 2},
-
2416 {"kim", XRP( 900) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
-
2417 {"liz", XRP( 998) + reserve(env, 0) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 998) + f, USD( 998), 0, 1},
-
2418 {"meg", XRP( 998) + reserve(env, 1) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
-
2419 {"nia", XRP( 998) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 1, 2},
-
2420 {"ova", XRP( 999) + reserve(env, 0) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
-
2421 {"pam", XRP( 999) + reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
-
2422 {"rae", XRP( 999) + reserve(env, 2) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
-
2423 {"sue", XRP(1000) + reserve(env, 2) + 1 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
-
2424 //---------------- Pre-established trust lines ---------------------
-
2425 {"abe", reserve(env, 0) + 0 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
-
2426 {"bud", reserve(env, 0) + 1 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
-
2427 {"che", reserve(env, 0) + 2 * f, 0, gwPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0},
-
2428 {"dan", drops(10) + reserve(env, 0) + 1 * f, 1, gwPreTrust, 1000, tesSUCCESS, drops(10) + f, USD(0.00001), 0, 0},
-
2429 {"eli", XRP( 20) + reserve(env, 0) + 1 * f, 1000, gwPreTrust, 1000, tesSUCCESS, XRP(20) + 1 * f, USD( 20), 0, 0},
-
2430 {"fyn", reserve(env, 1) + 0 * f, 0, gwPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
-
2431 {"gar", reserve(env, 1) + 0 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
-
2432 {"hal", reserve(env, 1) + 1 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
-
2433
-
2434 {"ned", reserve(env, 1) + 0 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
-
2435 {"ole", reserve(env, 1) + 1 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
-
2436 {"pat", reserve(env, 1) + 2 * f, 0, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
-
2437 {"quy", reserve(env, 1) + 2 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
-
2438 {"ron", reserve(env, 1) + 3 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
-
2439 {"syd", drops(10) + reserve(env, 1) + 2 * f, 1, acctPreTrust, 1000, tesSUCCESS, drops(10) + 2 * f, USD(0.00001), 0, 1},
-
2440 {"ted", XRP( 20) + reserve(env, 1) + 2 * f, 1000, acctPreTrust, 1000, tesSUCCESS, XRP(20) + 2 * f, USD( 20), 0, 1},
-
2441 {"uli", reserve(env, 2) + 0 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
-
2442 {"vic", reserve(env, 2) + 0 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 0, 1},
-
2443 {"wes", reserve(env, 2) + 1 * f, 0, acctPreTrust, 1000, tesSUCCESS, 2 * f, USD( 0), 1, 2},
-
2444 {"xan", reserve(env, 2) + 1 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 1, 2},
-
2445 };
-
2446 // clang-format on
+
2387 // clang-format off
+
2388 TestData const tests[]{
+
2389 // acct fundXrp bookAmt preTrust offerAmt tec spentXrp balanceUSD offers owners
+
2390 {"ann", reserve(env, 0) + 0 * f, 1, noPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0}, // Account is at the reserve, and will dip below once fees are subtracted.
+
2391 {"bev", reserve(env, 0) + 1 * f, 1, noPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0}, // Account has just enough for the reserve and the fee.
+
2392 {"cam", reserve(env, 0) + 2 * f, 0, noPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0}, // Account has enough for the reserve, the fee and the offer, and a bit more, but not enough for the reserve after the offer is placed.
+
2393 {"deb", drops(10) + reserve(env, 0) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, drops(10) + f, USD(0.00001), 0, 1}, // Account has enough to buy a little USD then the offer runs dry.
+
2394 {"eve", reserve(env, 1) + 0 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1}, // No offer to cross
+
2395 {"flo", reserve(env, 1) + 0 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
+
2396 {"gay", reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 50) + f, USD( 50), 0, 1},
+
2397 {"hye", XRP(1000) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 800) + f, USD( 800), 0, 1},
+
2398 {"ivy", XRP( 1) + reserve(env, 1) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 0, 1},
+
2399 {"joy", XRP( 1) + reserve(env, 2) + 1 * f, 1, noPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 2},
+
2400 {"kim", XRP( 900) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
+
2401 {"liz", XRP( 998) + reserve(env, 0) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 998) + f, USD( 998), 0, 1},
+
2402 {"meg", XRP( 998) + reserve(env, 1) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
+
2403 {"nia", XRP( 998) + reserve(env, 2) + 1 * f, 999, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 1, 2},
+
2404 {"ova", XRP( 999) + reserve(env, 0) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP( 999) + f, USD( 999), 0, 1},
+
2405 {"pam", XRP( 999) + reserve(env, 1) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
+
2406 {"rae", XRP( 999) + reserve(env, 2) + 1 * f, 1000, noPreTrust, 1000, tesSUCCESS, XRP(1000) + f, USD( 1000), 0, 1},
+
2407 {"sue", XRP(1000) + reserve(env, 2) + 1 * f, 0, noPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
+
2408 //---------------- Pre-established trust lines ---------------------
+
2409 {"abe", reserve(env, 0) + 0 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
+
2410 {"bud", reserve(env, 0) + 1 * f, 1, gwPreTrust, 1000, tecUNFUNDED_OFFER, f, USD( 0), 0, 0},
+
2411 {"che", reserve(env, 0) + 2 * f, 0, gwPreTrust, 1000, tecINSUF_RESERVE_OFFER, f, USD( 0), 0, 0},
+
2412 {"dan", drops(10) + reserve(env, 0) + 1 * f, 1, gwPreTrust, 1000, tesSUCCESS, drops(10) + f, USD(0.00001), 0, 0},
+
2413 {"eli", XRP( 20) + reserve(env, 0) + 1 * f, 1000, gwPreTrust, 1000, tesSUCCESS, XRP(20) + 1 * f, USD( 20), 0, 0},
+
2414 {"fyn", reserve(env, 1) + 0 * f, 0, gwPreTrust, 1000, tesSUCCESS, f, USD( 0), 1, 1},
+
2415 {"gar", reserve(env, 1) + 0 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
+
2416 {"hal", reserve(env, 1) + 1 * f, 1, gwPreTrust, 1000, tesSUCCESS, XRP( 1) + f, USD( 1), 1, 1},
+
2417
+
2418 {"ned", reserve(env, 1) + 0 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
+
2419 {"ole", reserve(env, 1) + 1 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
+
2420 {"pat", reserve(env, 1) + 2 * f, 0, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
+
2421 {"quy", reserve(env, 1) + 2 * f, 1, acctPreTrust, 1000, tecUNFUNDED_OFFER, 2 * f, USD( 0), 0, 1},
+
2422 {"ron", reserve(env, 1) + 3 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
+
2423 {"syd", drops(10) + reserve(env, 1) + 2 * f, 1, acctPreTrust, 1000, tesSUCCESS, drops(10) + 2 * f, USD(0.00001), 0, 1},
+
2424 {"ted", XRP( 20) + reserve(env, 1) + 2 * f, 1000, acctPreTrust, 1000, tesSUCCESS, XRP(20) + 2 * f, USD( 20), 0, 1},
+
2425 {"uli", reserve(env, 2) + 0 * f, 0, acctPreTrust, 1000, tecINSUF_RESERVE_OFFER, 2 * f, USD( 0), 0, 1},
+
2426 {"vic", reserve(env, 2) + 0 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 0, 1},
+
2427 {"wes", reserve(env, 2) + 1 * f, 0, acctPreTrust, 1000, tesSUCCESS, 2 * f, USD( 0), 1, 2},
+
2428 {"xan", reserve(env, 2) + 1 * f, 1, acctPreTrust, 1000, tesSUCCESS, XRP( 1) + 2 * f, USD( 1), 1, 2},
+
2429 };
+
2430 // clang-format on
+
2431
+
2432 for (auto const& t : tests)
+
2433 {
+
2434 auto const acct = Account(t.account);
+
2435 env.fund(t.fundXrp, acct);
+
2436 env.close();
+
2437
+
2438 // Make sure gateway has no current offers.
+
2439 env.require(offers(gw, 0));
+
2440
+
2441 // The gateway optionally creates an offer that would be crossed.
+
2442 auto const book = t.bookAmount;
+
2443 if (book)
+
2444 env(offer(gw, XRP(book), USD(book)));
+
2445 env.close();
+
2446 std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
2447
-
2448 for (auto const& t : tests)
-
2449 {
-
2450 auto const acct = Account(t.account);
-
2451 env.fund(t.fundXrp, acct);
-
2452 env.close();
-
2453
-
2454 // Make sure gateway has no current offers.
-
2455 env.require(offers(gw, 0));
-
2456
-
2457 // The gateway optionally creates an offer that would be crossed.
-
2458 auto const book = t.bookAmount;
-
2459 if (book)
-
2460 env(offer(gw, XRP(book), USD(book)));
-
2461 env.close();
-
2462 std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
-
2463
-
2464 // Optionally pre-establish a trustline between gw and acct.
-
2465 if (t.preTrust == gwPreTrust)
-
2466 env(trust(gw, acct["USD"](1)));
-
2467 env.close();
-
2468
-
2469 // Optionally pre-establish a trustline between acct and gw.
-
2470 // Note this is not really part of the test, so we expect there
-
2471 // to be enough XRP reserve for acct to create the trust line.
-
2472 if (t.preTrust == acctPreTrust)
-
2473 env(trust(acct, USD(1)));
-
2474 env.close();
-
2475
-
2476 {
-
2477 // Acct creates an offer. This is the heart of the test.
-
2478 auto const acctOffer = t.offerAmount;
-
2479 env(offer(acct, USD(acctOffer), XRP(acctOffer)), ter(t.tec));
-
2480 env.close();
-
2481 }
-
2482 std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
-
2483
-
2484 BEAST_EXPECT(env.balance(acct, USD.issue()) == t.balanceUsd);
-
2485 BEAST_EXPECT(
-
2486 env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
-
2487 env.require(offers(acct, t.offers));
-
2488 env.require(owners(acct, t.owners));
-
2489
-
2490 auto acctOffers = offersOnAccount(env, acct);
-
2491 BEAST_EXPECT(acctOffers.size() == t.offers);
-
2492 if (acctOffers.size() && t.offers)
-
2493 {
-
2494 auto const& acctOffer = *(acctOffers.front());
-
2495
-
2496 auto const leftover = t.offerAmount - t.bookAmount;
-
2497 BEAST_EXPECT(acctOffer[sfTakerGets] == XRP(leftover));
-
2498 BEAST_EXPECT(acctOffer[sfTakerPays] == USD(leftover));
+
2448 // Optionally pre-establish a trustline between gw and acct.
+
2449 if (t.preTrust == gwPreTrust)
+
2450 env(trust(gw, acct["USD"](1)));
+
2451 env.close();
+
2452
+
2453 // Optionally pre-establish a trustline between acct and gw.
+
2454 // Note this is not really part of the test, so we expect there
+
2455 // to be enough XRP reserve for acct to create the trust line.
+
2456 if (t.preTrust == acctPreTrust)
+
2457 env(trust(acct, USD(1)));
+
2458 env.close();
+
2459
+
2460 {
+
2461 // Acct creates an offer. This is the heart of the test.
+
2462 auto const acctOffer = t.offerAmount;
+
2463 env(offer(acct, USD(acctOffer), XRP(acctOffer)), ter(t.tec));
+
2464 env.close();
+
2465 }
+
2466 std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
+
2467
+
2468 BEAST_EXPECT(env.balance(acct, USD.issue()) == t.balanceUsd);
+
2469 BEAST_EXPECT(
+
2470 env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
+
2471 env.require(offers(acct, t.offers));
+
2472 env.require(owners(acct, t.owners));
+
2473
+
2474 auto acctOffers = offersOnAccount(env, acct);
+
2475 BEAST_EXPECT(acctOffers.size() == t.offers);
+
2476 if (acctOffers.size() && t.offers)
+
2477 {
+
2478 auto const& acctOffer = *(acctOffers.front());
+
2479
+
2480 auto const leftover = t.offerAmount - t.bookAmount;
+
2481 BEAST_EXPECT(acctOffer[sfTakerGets] == XRP(leftover));
+
2482 BEAST_EXPECT(acctOffer[sfTakerPays] == USD(leftover));
+
2483 }
+
2484
+
2485 if (t.preTrust == noPreTrust)
+
2486 {
+
2487 if (t.balanceUsd.value().signum())
+
2488 {
+
2489 // Verify the correct contents of the trustline
+
2490 verifyDefaultTrustline(env, acct, t.balanceUsd);
+
2491 }
+
2492 else
+
2493 {
+
2494 // Verify that no trustline was created.
+
2495 auto const sleTrust =
+
2496 env.le(keylet::line(acct, USD.issue()));
+
2497 BEAST_EXPECT(!sleTrust);
+
2498 }
2499 }
2500
-
2501 if (t.preTrust == noPreTrust)
-
2502 {
-
2503 if (t.balanceUsd.value().signum())
-
2504 {
-
2505 // Verify the correct contents of the trustline
-
2506 verifyDefaultTrustline(env, acct, t.balanceUsd);
-
2507 }
-
2508 else
-
2509 {
-
2510 // Verify that no trustline was created.
-
2511 auto const sleTrust =
-
2512 env.le(keylet::line(acct, USD.issue()));
-
2513 BEAST_EXPECT(!sleTrust);
-
2514 }
-
2515 }
-
2516
-
2517 // Give the next loop a clean slate by canceling any left-overs
-
2518 // in the offers.
-
2519 env(offer_cancel(acct, acctOfferSeq));
-
2520 env(offer_cancel(gw, gwOfferSeq));
-
2521 env.close();
-
2522 }
-
2523 }
-
2524
-
2525 void
-
2526 testXRPDirectCross(FeatureBitset features)
-
2527 {
-
2528 testcase("XRP Direct Crossing");
-
2529
-
2530 using namespace jtx;
+
2501 // Give the next loop a clean slate by canceling any left-overs
+
2502 // in the offers.
+
2503 env(offer_cancel(acct, acctOfferSeq));
+
2504 env(offer_cancel(gw, gwOfferSeq));
+
2505 env.close();
+
2506 }
+
2507 }
+
2508
+
2509 void
+
2510 testXRPDirectCross(FeatureBitset features)
+
2511 {
+
2512 testcase("XRP Direct Crossing");
+
2513
+
2514 using namespace jtx;
+
2515
+
2516 auto const gw = Account("gateway");
+
2517 auto const alice = Account("alice");
+
2518 auto const bob = Account("bob");
+
2519 auto const USD = gw["USD"];
+
2520
+
2521 auto const usdOffer = USD(1000);
+
2522 auto const xrpOffer = XRP(1000);
+
2523
+
2524 Env env{*this, features};
+
2525
+
2526 env.fund(XRP(1000000), gw, bob);
+
2527 env.close();
+
2528
+
2529 // The fee that's charged for transactions.
+
2530 auto const fee = env.current()->fees().base;
2531
-
2532 auto const gw = Account("gateway");
-
2533 auto const alice = Account("alice");
-
2534 auto const bob = Account("bob");
-
2535 auto const USD = gw["USD"];
+
2532 // alice's account has enough for the reserve, one trust line plus two
+
2533 // offers, and two fees.
+
2534 env.fund(reserve(env, 2) + fee * 2, alice);
+
2535 env.close();
2536
-
2537 auto const usdOffer = USD(1000);
-
2538 auto const xrpOffer = XRP(1000);
-
2539
-
2540 Env env{*this, features};
-
2541
-
2542 env.fund(XRP(1000000), gw, bob);
-
2543 env.close();
+
2537 env(trust(alice, usdOffer));
+
2538
+
2539 env.close();
+
2540
+
2541 env(pay(gw, alice, usdOffer));
+
2542 env.close();
+
2543 env.require(balance(alice, usdOffer), offers(alice, 0), offers(bob, 0));
2544
-
2545 // The fee that's charged for transactions.
-
2546 auto const fee = env.current()->fees().base;
-
2547
-
2548 // alice's account has enough for the reserve, one trust line plus two
-
2549 // offers, and two fees.
-
2550 env.fund(reserve(env, 2) + fee * 2, alice);
-
2551 env.close();
-
2552
-
2553 env(trust(alice, usdOffer));
+
2545 // The scenario:
+
2546 // o alice has USD but wants XRP.
+
2547 // o bob has XRP but wants USD.
+
2548 auto const alicesXRP = env.balance(alice);
+
2549 auto const bobsXRP = env.balance(bob);
+
2550
+
2551 env(offer(alice, xrpOffer, usdOffer));
+
2552 env.close();
+
2553 env(offer(bob, usdOffer, xrpOffer));
2554
2555 env.close();
-
2556
-
2557 env(pay(gw, alice, usdOffer));
-
2558 env.close();
-
2559 env.require(balance(alice, usdOffer), offers(alice, 0), offers(bob, 0));
-
2560
-
2561 // The scenario:
-
2562 // o alice has USD but wants XRP.
-
2563 // o bob has XRP but wants USD.
-
2564 auto const alicesXRP = env.balance(alice);
-
2565 auto const bobsXRP = env.balance(bob);
-
2566
-
2567 env(offer(alice, xrpOffer, usdOffer));
-
2568 env.close();
-
2569 env(offer(bob, usdOffer, xrpOffer));
-
2570
-
2571 env.close();
-
2572 env.require(
-
2573 balance(alice, USD(0)),
-
2574 balance(bob, usdOffer),
-
2575 balance(alice, alicesXRP + xrpOffer - fee),
-
2576 balance(bob, bobsXRP - xrpOffer - fee),
-
2577 offers(alice, 0),
-
2578 offers(bob, 0));
+
2556 env.require(
+
2557 balance(alice, USD(0)),
+
2558 balance(bob, usdOffer),
+
2559 balance(alice, alicesXRP + xrpOffer - fee),
+
2560 balance(bob, bobsXRP - xrpOffer - fee),
+
2561 offers(alice, 0),
+
2562 offers(bob, 0));
+
2563
+
2564 verifyDefaultTrustline(env, bob, usdOffer);
+
2565
+
2566 // Make two more offers that leave one of the offers non-dry.
+
2567 env(offer(alice, USD(999), XRP(999)));
+
2568 env(offer(bob, xrpOffer, usdOffer));
+
2569
+
2570 env.close();
+
2571 env.require(balance(alice, USD(999)));
+
2572 env.require(balance(bob, USD(1)));
+
2573 env.require(offers(alice, 0));
+
2574 verifyDefaultTrustline(env, bob, USD(1));
+
2575 {
+
2576 auto const bobsOffers = offersOnAccount(env, bob);
+
2577 BEAST_EXPECT(bobsOffers.size() == 1);
+
2578 auto const& bobsOffer = *(bobsOffers.front());
2579
-
2580 verifyDefaultTrustline(env, bob, usdOffer);
-
2581
-
2582 // Make two more offers that leave one of the offers non-dry.
-
2583 env(offer(alice, USD(999), XRP(999)));
-
2584 env(offer(bob, xrpOffer, usdOffer));
+
2580 BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
+
2581 BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
+
2582 BEAST_EXPECT(bobsOffer[sfTakerPays] == XRP(1));
+
2583 }
+
2584 }
2585
-
2586 env.close();
-
2587 env.require(balance(alice, USD(999)));
-
2588 env.require(balance(bob, USD(1)));
-
2589 env.require(offers(alice, 0));
-
2590 verifyDefaultTrustline(env, bob, USD(1));
-
2591 {
-
2592 auto const bobsOffers = offersOnAccount(env, bob);
-
2593 BEAST_EXPECT(bobsOffers.size() == 1);
-
2594 auto const& bobsOffer = *(bobsOffers.front());
-
2595
-
2596 BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
-
2597 BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
-
2598 BEAST_EXPECT(bobsOffer[sfTakerPays] == XRP(1));
-
2599 }
-
2600 }
+
2586 void
+
2587 testDirectCross(FeatureBitset features)
+
2588 {
+
2589 testcase("Direct Crossing");
+
2590
+
2591 using namespace jtx;
+
2592
+
2593 auto const gw = Account("gateway");
+
2594 auto const alice = Account("alice");
+
2595 auto const bob = Account("bob");
+
2596 auto const USD = gw["USD"];
+
2597 auto const EUR = gw["EUR"];
+
2598
+
2599 auto const usdOffer = USD(1000);
+
2600 auto const eurOffer = EUR(1000);
2601
-
2602 void
-
2603 testDirectCross(FeatureBitset features)
-
2604 {
-
2605 testcase("Direct Crossing");
+
2602 Env env{*this, features};
+
2603
+
2604 env.fund(XRP(1000000), gw);
+
2605 env.close();
2606
-
2607 using namespace jtx;
-
2608
-
2609 auto const gw = Account("gateway");
-
2610 auto const alice = Account("alice");
-
2611 auto const bob = Account("bob");
-
2612 auto const USD = gw["USD"];
-
2613 auto const EUR = gw["EUR"];
-
2614
-
2615 auto const usdOffer = USD(1000);
-
2616 auto const eurOffer = EUR(1000);
-
2617
-
2618 Env env{*this, features};
+
2607 // The fee that's charged for transactions.
+
2608 auto const fee = env.current()->fees().base;
+
2609
+
2610 // Each account has enough for the reserve, two trust lines, one
+
2611 // offer, and two fees.
+
2612 env.fund(reserve(env, 3) + fee * 3, alice);
+
2613 env.fund(reserve(env, 3) + fee * 2, bob);
+
2614 env.close();
+
2615
+
2616 env(trust(alice, usdOffer));
+
2617 env(trust(bob, eurOffer));
+
2618 env.close();
2619
-
2620 env.fund(XRP(1000000), gw);
-
2621 env.close();
-
2622
-
2623 // The fee that's charged for transactions.
-
2624 auto const fee = env.current()->fees().base;
+
2620 env(pay(gw, alice, usdOffer));
+
2621 env(pay(gw, bob, eurOffer));
+
2622 env.close();
+
2623
+
2624 env.require(balance(alice, usdOffer), balance(bob, eurOffer));
2625
-
2626 // Each account has enough for the reserve, two trust lines, one
-
2627 // offer, and two fees.
-
2628 env.fund(reserve(env, 3) + fee * 3, alice);
-
2629 env.fund(reserve(env, 3) + fee * 2, bob);
-
2630 env.close();
+
2626 // The scenario:
+
2627 // o alice has USD but wants EUR.
+
2628 // o bob has EUR but wants USD.
+
2629 env(offer(alice, eurOffer, usdOffer));
+
2630 env(offer(bob, usdOffer, eurOffer));
2631
-
2632 env(trust(alice, usdOffer));
-
2633 env(trust(bob, eurOffer));
-
2634 env.close();
-
2635
-
2636 env(pay(gw, alice, usdOffer));
-
2637 env(pay(gw, bob, eurOffer));
-
2638 env.close();
-
2639
-
2640 env.require(balance(alice, usdOffer), balance(bob, eurOffer));
-
2641
-
2642 // The scenario:
-
2643 // o alice has USD but wants EUR.
-
2644 // o bob has EUR but wants USD.
-
2645 env(offer(alice, eurOffer, usdOffer));
-
2646 env(offer(bob, usdOffer, eurOffer));
-
2647
+
2632 env.close();
+
2633 env.require(
+
2634 balance(alice, eurOffer),
+
2635 balance(bob, usdOffer),
+
2636 offers(alice, 0),
+
2637 offers(bob, 0));
+
2638
+
2639 // Alice's offer crossing created a default EUR trustline and
+
2640 // Bob's offer crossing created a default USD trustline:
+
2641 verifyDefaultTrustline(env, alice, eurOffer);
+
2642 verifyDefaultTrustline(env, bob, usdOffer);
+
2643
+
2644 // Make two more offers that leave one of the offers non-dry.
+
2645 // Guarantee the order of application by putting a close()
+
2646 // between them.
+
2647 env(offer(bob, eurOffer, usdOffer));
2648 env.close();
-
2649 env.require(
-
2650 balance(alice, eurOffer),
-
2651 balance(bob, usdOffer),
-
2652 offers(alice, 0),
-
2653 offers(bob, 0));
-
2654
-
2655 // Alice's offer crossing created a default EUR trustline and
-
2656 // Bob's offer crossing created a default USD trustline:
-
2657 verifyDefaultTrustline(env, alice, eurOffer);
-
2658 verifyDefaultTrustline(env, bob, usdOffer);
-
2659
-
2660 // Make two more offers that leave one of the offers non-dry.
-
2661 // Guarantee the order of application by putting a close()
-
2662 // between them.
-
2663 env(offer(bob, eurOffer, usdOffer));
-
2664 env.close();
-
2665
-
2666 env(offer(alice, USD(999), eurOffer));
-
2667 env.close();
-
2668
-
2669 env.require(offers(alice, 0));
-
2670 env.require(offers(bob, 1));
+
2649
+
2650 env(offer(alice, USD(999), eurOffer));
+
2651 env.close();
+
2652
+
2653 env.require(offers(alice, 0));
+
2654 env.require(offers(bob, 1));
+
2655
+
2656 env.require(balance(alice, USD(999)));
+
2657 env.require(balance(alice, EUR(1)));
+
2658 env.require(balance(bob, USD(1)));
+
2659 env.require(balance(bob, EUR(999)));
+
2660
+
2661 {
+
2662 auto bobsOffers = offersOnAccount(env, bob);
+
2663 if (BEAST_EXPECT(bobsOffers.size() == 1))
+
2664 {
+
2665 auto const& bobsOffer = *(bobsOffers.front());
+
2666
+
2667 BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
+
2668 BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(1));
+
2669 }
+
2670 }
2671
-
2672 env.require(balance(alice, USD(999)));
-
2673 env.require(balance(alice, EUR(1)));
-
2674 env.require(balance(bob, USD(1)));
-
2675 env.require(balance(bob, EUR(999)));
-
2676
-
2677 {
-
2678 auto bobsOffers = offersOnAccount(env, bob);
-
2679 if (BEAST_EXPECT(bobsOffers.size() == 1))
-
2680 {
-
2681 auto const& bobsOffer = *(bobsOffers.front());
+
2672 // alice makes one more offer that cleans out bob's offer.
+
2673 env(offer(alice, USD(1), EUR(1)));
+
2674 env.close();
+
2675
+
2676 env.require(balance(alice, USD(1000)));
+
2677 env.require(balance(alice, EUR(none)));
+
2678 env.require(balance(bob, USD(none)));
+
2679 env.require(balance(bob, EUR(1000)));
+
2680 env.require(offers(alice, 0));
+
2681 env.require(offers(bob, 0));
2682
-
2683 BEAST_EXPECT(bobsOffer[sfTakerGets] == USD(1));
-
2684 BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(1));
-
2685 }
-
2686 }
-
2687
-
2688 // alice makes one more offer that cleans out bob's offer.
-
2689 env(offer(alice, USD(1), EUR(1)));
+
2683 // The two trustlines that were generated by offers should be gone.
+
2684 BEAST_EXPECT(!env.le(keylet::line(alice.id(), EUR.issue())));
+
2685 BEAST_EXPECT(!env.le(keylet::line(bob.id(), USD.issue())));
+
2686
+
2687 // Make two more offers that leave one of the offers non-dry. We
+
2688 // need to properly sequence the transactions:
+
2689 env(offer(alice, EUR(999), usdOffer));
2690 env.close();
2691
-
2692 env.require(balance(alice, USD(1000)));
-
2693 env.require(balance(alice, EUR(none)));
-
2694 env.require(balance(bob, USD(none)));
-
2695 env.require(balance(bob, EUR(1000)));
-
2696 env.require(offers(alice, 0));
-
2697 env.require(offers(bob, 0));
-
2698
-
2699 // The two trustlines that were generated by offers should be gone.
-
2700 BEAST_EXPECT(!env.le(keylet::line(alice.id(), EUR.issue())));
-
2701 BEAST_EXPECT(!env.le(keylet::line(bob.id(), USD.issue())));
-
2702
-
2703 // Make two more offers that leave one of the offers non-dry. We
-
2704 // need to properly sequence the transactions:
-
2705 env(offer(alice, EUR(999), usdOffer));
-
2706 env.close();
-
2707
-
2708 env(offer(bob, usdOffer, eurOffer));
-
2709 env.close();
+
2692 env(offer(bob, usdOffer, eurOffer));
+
2693 env.close();
+
2694
+
2695 env.require(offers(alice, 0));
+
2696 env.require(offers(bob, 0));
+
2697
+
2698 env.require(balance(alice, USD(0)));
+
2699 env.require(balance(alice, EUR(999)));
+
2700 env.require(balance(bob, USD(1000)));
+
2701 env.require(balance(bob, EUR(1)));
+
2702 }
+
2703
+
2704 void
+
2705 testBridgedCross(FeatureBitset features)
+
2706 {
+
2707 testcase("Bridged Crossing");
+
2708
+
2709 using namespace jtx;
2710
-
2711 env.require(offers(alice, 0));
-
2712 env.require(offers(bob, 0));
-
2713
-
2714 env.require(balance(alice, USD(0)));
-
2715 env.require(balance(alice, EUR(999)));
-
2716 env.require(balance(bob, USD(1000)));
-
2717 env.require(balance(bob, EUR(1)));
-
2718 }
-
2719
-
2720 void
-
2721 testBridgedCross(FeatureBitset features)
-
2722 {
-
2723 testcase("Bridged Crossing");
-
2724
-
2725 using namespace jtx;
-
2726
-
2727 auto const gw = Account("gateway");
-
2728 auto const alice = Account("alice");
-
2729 auto const bob = Account("bob");
-
2730 auto const carol = Account("carol");
-
2731 auto const USD = gw["USD"];
-
2732 auto const EUR = gw["EUR"];
-
2733
-
2734 auto const usdOffer = USD(1000);
-
2735 auto const eurOffer = EUR(1000);
-
2736
-
2737 Env env{*this, features};
-
2738
-
2739 env.fund(XRP(1000000), gw, alice, bob, carol);
-
2740 env.close();
-
2741
-
2742 env(trust(alice, usdOffer));
-
2743 env(trust(carol, eurOffer));
-
2744 env.close();
-
2745 env(pay(gw, alice, usdOffer));
-
2746 env(pay(gw, carol, eurOffer));
-
2747 env.close();
-
2748
-
2749 // The scenario:
-
2750 // o alice has USD but wants XRP.
-
2751 // o bob has XRP but wants EUR.
-
2752 // o carol has EUR but wants USD.
-
2753 // Note that carol's offer must come last. If carol's offer is placed
-
2754 // before bob's or alice's, then autobridging will not occur.
-
2755 env(offer(alice, XRP(1000), usdOffer));
-
2756 env(offer(bob, eurOffer, XRP(1000)));
-
2757 auto const bobXrpBalance = env.balance(bob);
-
2758 env.close();
-
2759
-
2760 // carol makes an offer that partially consumes alice and bob's offers.
-
2761 env(offer(carol, USD(400), EUR(400)));
-
2762 env.close();
-
2763
-
2764 env.require(
-
2765 balance(alice, USD(600)),
-
2766 balance(bob, EUR(400)),
-
2767 balance(carol, USD(400)),
-
2768 balance(bob, bobXrpBalance - XRP(400)),
-
2769 offers(carol, 0));
-
2770 verifyDefaultTrustline(env, bob, EUR(400));
-
2771 verifyDefaultTrustline(env, carol, USD(400));
-
2772 {
-
2773 auto const alicesOffers = offersOnAccount(env, alice);
-
2774 BEAST_EXPECT(alicesOffers.size() == 1);
-
2775 auto const& alicesOffer = *(alicesOffers.front());
-
2776
-
2777 BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
-
2778 BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(600));
-
2779 BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(600));
-
2780 }
-
2781 {
-
2782 auto const bobsOffers = offersOnAccount(env, bob);
-
2783 BEAST_EXPECT(bobsOffers.size() == 1);
-
2784 auto const& bobsOffer = *(bobsOffers.front());
-
2785
-
2786 BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
-
2787 BEAST_EXPECT(bobsOffer[sfTakerGets] == XRP(600));
-
2788 BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(600));
-
2789 }
-
2790
-
2791 // carol makes an offer that exactly consumes alice and bob's offers.
-
2792 env(offer(carol, USD(600), EUR(600)));
-
2793 env.close();
-
2794
-
2795 env.require(
-
2796 balance(alice, USD(0)),
-
2797 balance(bob, eurOffer),
-
2798 balance(carol, usdOffer),
-
2799 balance(bob, bobXrpBalance - XRP(1000)),
-
2800 offers(bob, 0),
-
2801 offers(carol, 0));
-
2802 verifyDefaultTrustline(env, bob, EUR(1000));
-
2803 verifyDefaultTrustline(env, carol, USD(1000));
-
2804
-
2805 // In pre-flow code alice's offer is left empty in the ledger.
-
2806 auto const alicesOffers = offersOnAccount(env, alice);
-
2807 if (alicesOffers.size() != 0)
-
2808 {
-
2809 BEAST_EXPECT(alicesOffers.size() == 1);
-
2810 auto const& alicesOffer = *(alicesOffers.front());
+
2711 auto const gw = Account("gateway");
+
2712 auto const alice = Account("alice");
+
2713 auto const bob = Account("bob");
+
2714 auto const carol = Account("carol");
+
2715 auto const USD = gw["USD"];
+
2716 auto const EUR = gw["EUR"];
+
2717
+
2718 auto const usdOffer = USD(1000);
+
2719 auto const eurOffer = EUR(1000);
+
2720
+
2721 Env env{*this, features};
+
2722
+
2723 env.fund(XRP(1000000), gw, alice, bob, carol);
+
2724 env.close();
+
2725
+
2726 env(trust(alice, usdOffer));
+
2727 env(trust(carol, eurOffer));
+
2728 env.close();
+
2729 env(pay(gw, alice, usdOffer));
+
2730 env(pay(gw, carol, eurOffer));
+
2731 env.close();
+
2732
+
2733 // The scenario:
+
2734 // o alice has USD but wants XRP.
+
2735 // o bob has XRP but wants EUR.
+
2736 // o carol has EUR but wants USD.
+
2737 // Note that carol's offer must come last. If carol's offer is placed
+
2738 // before bob's or alice's, then autobridging will not occur.
+
2739 env(offer(alice, XRP(1000), usdOffer));
+
2740 env(offer(bob, eurOffer, XRP(1000)));
+
2741 auto const bobXrpBalance = env.balance(bob);
+
2742 env.close();
+
2743
+
2744 // carol makes an offer that partially consumes alice and bob's offers.
+
2745 env(offer(carol, USD(400), EUR(400)));
+
2746 env.close();
+
2747
+
2748 env.require(
+
2749 balance(alice, USD(600)),
+
2750 balance(bob, EUR(400)),
+
2751 balance(carol, USD(400)),
+
2752 balance(bob, bobXrpBalance - XRP(400)),
+
2753 offers(carol, 0));
+
2754 verifyDefaultTrustline(env, bob, EUR(400));
+
2755 verifyDefaultTrustline(env, carol, USD(400));
+
2756 {
+
2757 auto const alicesOffers = offersOnAccount(env, alice);
+
2758 BEAST_EXPECT(alicesOffers.size() == 1);
+
2759 auto const& alicesOffer = *(alicesOffers.front());
+
2760
+
2761 BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
+
2762 BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(600));
+
2763 BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(600));
+
2764 }
+
2765 {
+
2766 auto const bobsOffers = offersOnAccount(env, bob);
+
2767 BEAST_EXPECT(bobsOffers.size() == 1);
+
2768 auto const& bobsOffer = *(bobsOffers.front());
+
2769
+
2770 BEAST_EXPECT(bobsOffer[sfLedgerEntryType] == ltOFFER);
+
2771 BEAST_EXPECT(bobsOffer[sfTakerGets] == XRP(600));
+
2772 BEAST_EXPECT(bobsOffer[sfTakerPays] == EUR(600));
+
2773 }
+
2774
+
2775 // carol makes an offer that exactly consumes alice and bob's offers.
+
2776 env(offer(carol, USD(600), EUR(600)));
+
2777 env.close();
+
2778
+
2779 env.require(
+
2780 balance(alice, USD(0)),
+
2781 balance(bob, eurOffer),
+
2782 balance(carol, usdOffer),
+
2783 balance(bob, bobXrpBalance - XRP(1000)),
+
2784 offers(bob, 0),
+
2785 offers(carol, 0));
+
2786 verifyDefaultTrustline(env, bob, EUR(1000));
+
2787 verifyDefaultTrustline(env, carol, USD(1000));
+
2788
+
2789 // In pre-flow code alice's offer is left empty in the ledger.
+
2790 auto const alicesOffers = offersOnAccount(env, alice);
+
2791 if (alicesOffers.size() != 0)
+
2792 {
+
2793 BEAST_EXPECT(alicesOffers.size() == 1);
+
2794 auto const& alicesOffer = *(alicesOffers.front());
+
2795
+
2796 BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
+
2797 BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(0));
+
2798 BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(0));
+
2799 }
+
2800 }
+
2801
+
2802 void
+
2803 testSellOffer(FeatureBitset features)
+
2804 {
+
2805 // Test a number of different corner cases regarding offer crossing
+
2806 // when the tfSell flag is set. The test is table driven so it
+
2807 // should be easy to add or remove tests.
+
2808 testcase("Sell Offer");
+
2809
+
2810 using namespace jtx;
2811
-
2812 BEAST_EXPECT(alicesOffer[sfLedgerEntryType] == ltOFFER);
-
2813 BEAST_EXPECT(alicesOffer[sfTakerGets] == USD(0));
-
2814 BEAST_EXPECT(alicesOffer[sfTakerPays] == XRP(0));
-
2815 }
-
2816 }
-
2817
-
2818 void
-
2819 testSellOffer(FeatureBitset features)
-
2820 {
-
2821 // Test a number of different corner cases regarding offer crossing
-
2822 // when the tfSell flag is set. The test is table driven so it
-
2823 // should be easy to add or remove tests.
-
2824 testcase("Sell Offer");
-
2825
-
2826 using namespace jtx;
-
2827
-
2828 auto const gw = Account("gateway");
-
2829 auto const USD = gw["USD"];
-
2830
-
2831 Env env{*this, features};
-
2832
-
2833 env.fund(XRP(10000000), gw);
-
2834 env.close();
-
2835
-
2836 // The fee that's charged for transactions
-
2837 auto const f = env.current()->fees().base;
-
2838
-
2839 // To keep things simple all offers are 1 : 1 for XRP : USD.
-
2840 enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
-
2841 struct TestData
-
2842 {
-
2843 std::string account; // Account operated on
-
2844 STAmount fundXrp; // XRP acct funded with
-
2845 STAmount fundUSD; // USD acct funded with
-
2846 STAmount gwGets; // gw's offer
-
2847 STAmount gwPays; //
-
2848 STAmount acctGets; // acct's offer
-
2849 STAmount acctPays; //
-
2850 TER tec; // Returned tec code
-
2851 STAmount spentXrp; // Amount removed from fundXrp
-
2852 STAmount finalUsd; // Final USD balance on acct
-
2853 int offers; // Offers on acct
-
2854 int owners; // Owners on acct
-
2855 STAmount takerGets; // Remainder of acct's offer
-
2856 STAmount takerPays; //
-
2857
-
2858 // Constructor with takerGets/takerPays
-
2859 TestData(
-
2860 std::string&& account_, // Account operated on
-
2861 STAmount const& fundXrp_, // XRP acct funded with
-
2862 STAmount const& fundUSD_, // USD acct funded with
-
2863 STAmount const& gwGets_, // gw's offer
-
2864 STAmount const& gwPays_, //
-
2865 STAmount const& acctGets_, // acct's offer
-
2866 STAmount const& acctPays_, //
-
2867 TER tec_, // Returned tec code
-
2868 STAmount const& spentXrp_, // Amount removed from fundXrp
-
2869 STAmount const& finalUsd_, // Final USD balance on acct
-
2870 int offers_, // Offers on acct
-
2871 int owners_, // Owners on acct
-
2872 STAmount const& takerGets_, // Remainder of acct's offer
-
2873 STAmount const& takerPays_) //
-
2874 : account(std::move(account_))
-
2875 , fundXrp(fundXrp_)
-
2876 , fundUSD(fundUSD_)
-
2877 , gwGets(gwGets_)
-
2878 , gwPays(gwPays_)
-
2879 , acctGets(acctGets_)
-
2880 , acctPays(acctPays_)
-
2881 , tec(tec_)
-
2882 , spentXrp(spentXrp_)
-
2883 , finalUsd(finalUsd_)
-
2884 , offers(offers_)
-
2885 , owners(owners_)
-
2886 , takerGets(takerGets_)
-
2887 , takerPays(takerPays_)
-
2888 {
-
2889 }
-
2890
-
2891 // Constructor without takerGets/takerPays
-
2892 TestData(
-
2893 std::string&& account_, // Account operated on
-
2894 STAmount const& fundXrp_, // XRP acct funded with
-
2895 STAmount const& fundUSD_, // USD acct funded with
-
2896 STAmount const& gwGets_, // gw's offer
-
2897 STAmount const& gwPays_, //
-
2898 STAmount const& acctGets_, // acct's offer
-
2899 STAmount const& acctPays_, //
-
2900 TER tec_, // Returned tec code
-
2901 STAmount const& spentXrp_, // Amount removed from fundXrp
-
2902 STAmount const& finalUsd_, // Final USD balance on acct
-
2903 int offers_, // Offers on acct
-
2904 int owners_) // Owners on acct
-
2905 : TestData(
-
2906 std::move(account_),
-
2907 fundXrp_,
-
2908 fundUSD_,
-
2909 gwGets_,
-
2910 gwPays_,
-
2911 acctGets_,
-
2912 acctPays_,
-
2913 tec_,
-
2914 spentXrp_,
-
2915 finalUsd_,
-
2916 offers_,
-
2917 owners_,
-
2918 STAmount{0},
-
2919 STAmount{0})
-
2920 {
-
2921 }
-
2922 };
-
2923
-
2924 // clang-format off
-
2925 TestData const tests[]{
-
2926 // acct pays XRP
-
2927 // acct fundXrp fundUSD gwGets gwPays acctGets acctPays tec spentXrp finalUSD offers owners takerGets takerPays
-
2928 {"ann", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD( 5), USD(10), XRP(10), tecINSUF_RESERVE_OFFER, XRP( 0) + (1 * f), USD( 0), 0, 0},
-
2929 {"bev", XRP(10) + reserve(env, 1) + 1 * f, USD( 0), XRP(10), USD( 5), USD(10), XRP(10), tesSUCCESS, XRP( 0) + (1 * f), USD( 0), 1, 1, XRP(10), USD(10)},
-
2930 {"cam", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(10), USD(10), XRP(10), tesSUCCESS, XRP( 10) + (1 * f), USD(10), 0, 1},
-
2931 {"deb", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD(10), XRP(10), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
-
2932 {"eve", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD( 5), XRP( 5), tesSUCCESS, XRP( 5) + (1 * f), USD(10), 0, 1},
-
2933 {"flo", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
-
2934 {"gay", XRP(20) + reserve(env, 1) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
-
2935 {"hye", XRP(20) + reserve(env, 2) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 1, 2, XRP(10), USD(10)},
-
2936 // acct pays USD
-
2937 {"meg", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP( 5), XRP(10), USD(10), tecINSUF_RESERVE_OFFER, XRP( 0) + (2 * f), USD(10), 0, 1},
-
2938 {"nia", reserve(env, 2) + 2 * f, USD(10), USD(10), XRP( 5), XRP(10), USD(10), tesSUCCESS, XRP( 0) + (2 * f), USD(10), 1, 2, USD(10), XRP(10)},
-
2939 {"ova", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP(10), XRP(10), USD(10), tesSUCCESS, XRP(-10) + (2 * f), USD( 0), 0, 1},
-
2940 {"pam", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP(20), XRP(10), USD(10), tesSUCCESS, XRP(-20) + (2 * f), USD( 0), 0, 1},
-
2941 {"qui", reserve(env, 1) + 2 * f, USD(10), USD(20), XRP(40), XRP(10), USD(10), tesSUCCESS, XRP(-20) + (2 * f), USD( 0), 0, 1},
-
2942 {"rae", reserve(env, 2) + 2 * f, USD(10), USD( 5), XRP( 5), XRP(10), USD(10), tesSUCCESS, XRP( -5) + (2 * f), USD( 5), 1, 2, USD( 5), XRP( 5)},
-
2943 {"sue", reserve(env, 2) + 2 * f, USD(10), USD( 5), XRP(10), XRP(10), USD(10), tesSUCCESS, XRP(-10) + (2 * f), USD( 5), 1, 2, USD( 5), XRP( 5)},
-
2944 };
-
2945 // clang-format on
-
2946
-
2947 auto const zeroUsd = USD(0);
-
2948 for (auto const& t : tests)
-
2949 {
-
2950 // Make sure gateway has no current offers.
-
2951 env.require(offers(gw, 0));
+
2812 auto const gw = Account("gateway");
+
2813 auto const USD = gw["USD"];
+
2814
+
2815 Env env{*this, features};
+
2816
+
2817 env.fund(XRP(10000000), gw);
+
2818 env.close();
+
2819
+
2820 // The fee that's charged for transactions
+
2821 auto const f = env.current()->fees().base;
+
2822
+
2823 // To keep things simple all offers are 1 : 1 for XRP : USD.
+
2824 enum preTrustType { noPreTrust, gwPreTrust, acctPreTrust };
+
2825 struct TestData
+
2826 {
+
2827 std::string account; // Account operated on
+
2828 STAmount fundXrp; // XRP acct funded with
+
2829 STAmount fundUSD; // USD acct funded with
+
2830 STAmount gwGets; // gw's offer
+
2831 STAmount gwPays; //
+
2832 STAmount acctGets; // acct's offer
+
2833 STAmount acctPays; //
+
2834 TER tec; // Returned tec code
+
2835 STAmount spentXrp; // Amount removed from fundXrp
+
2836 STAmount finalUsd; // Final USD balance on acct
+
2837 int offers; // Offers on acct
+
2838 int owners; // Owners on acct
+
2839 STAmount takerGets; // Remainder of acct's offer
+
2840 STAmount takerPays; //
+
2841
+
2842 // Constructor with takerGets/takerPays
+
2843 TestData(
+
2844 std::string&& account_, // Account operated on
+
2845 STAmount const& fundXrp_, // XRP acct funded with
+
2846 STAmount const& fundUSD_, // USD acct funded with
+
2847 STAmount const& gwGets_, // gw's offer
+
2848 STAmount const& gwPays_, //
+
2849 STAmount const& acctGets_, // acct's offer
+
2850 STAmount const& acctPays_, //
+
2851 TER tec_, // Returned tec code
+
2852 STAmount const& spentXrp_, // Amount removed from fundXrp
+
2853 STAmount const& finalUsd_, // Final USD balance on acct
+
2854 int offers_, // Offers on acct
+
2855 int owners_, // Owners on acct
+
2856 STAmount const& takerGets_, // Remainder of acct's offer
+
2857 STAmount const& takerPays_) //
+
2858 : account(std::move(account_))
+
2859 , fundXrp(fundXrp_)
+
2860 , fundUSD(fundUSD_)
+
2861 , gwGets(gwGets_)
+
2862 , gwPays(gwPays_)
+
2863 , acctGets(acctGets_)
+
2864 , acctPays(acctPays_)
+
2865 , tec(tec_)
+
2866 , spentXrp(spentXrp_)
+
2867 , finalUsd(finalUsd_)
+
2868 , offers(offers_)
+
2869 , owners(owners_)
+
2870 , takerGets(takerGets_)
+
2871 , takerPays(takerPays_)
+
2872 {
+
2873 }
+
2874
+
2875 // Constructor without takerGets/takerPays
+
2876 TestData(
+
2877 std::string&& account_, // Account operated on
+
2878 STAmount const& fundXrp_, // XRP acct funded with
+
2879 STAmount const& fundUSD_, // USD acct funded with
+
2880 STAmount const& gwGets_, // gw's offer
+
2881 STAmount const& gwPays_, //
+
2882 STAmount const& acctGets_, // acct's offer
+
2883 STAmount const& acctPays_, //
+
2884 TER tec_, // Returned tec code
+
2885 STAmount const& spentXrp_, // Amount removed from fundXrp
+
2886 STAmount const& finalUsd_, // Final USD balance on acct
+
2887 int offers_, // Offers on acct
+
2888 int owners_) // Owners on acct
+
2889 : TestData(
+
2890 std::move(account_),
+
2891 fundXrp_,
+
2892 fundUSD_,
+
2893 gwGets_,
+
2894 gwPays_,
+
2895 acctGets_,
+
2896 acctPays_,
+
2897 tec_,
+
2898 spentXrp_,
+
2899 finalUsd_,
+
2900 offers_,
+
2901 owners_,
+
2902 STAmount{0},
+
2903 STAmount{0})
+
2904 {
+
2905 }
+
2906 };
+
2907
+
2908 // clang-format off
+
2909 TestData const tests[]{
+
2910 // acct pays XRP
+
2911 // acct fundXrp fundUSD gwGets gwPays acctGets acctPays tec spentXrp finalUSD offers owners takerGets takerPays
+
2912 {"ann", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD( 5), USD(10), XRP(10), tecINSUF_RESERVE_OFFER, XRP( 0) + (1 * f), USD( 0), 0, 0},
+
2913 {"bev", XRP(10) + reserve(env, 1) + 1 * f, USD( 0), XRP(10), USD( 5), USD(10), XRP(10), tesSUCCESS, XRP( 0) + (1 * f), USD( 0), 1, 1, XRP(10), USD(10)},
+
2914 {"cam", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(10), USD(10), XRP(10), tesSUCCESS, XRP( 10) + (1 * f), USD(10), 0, 1},
+
2915 {"deb", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD(10), XRP(10), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
+
2916 {"eve", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD( 5), XRP( 5), tesSUCCESS, XRP( 5) + (1 * f), USD(10), 0, 1},
+
2917 {"flo", XRP(10) + reserve(env, 0) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
+
2918 {"gay", XRP(20) + reserve(env, 1) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 0, 1},
+
2919 {"hye", XRP(20) + reserve(env, 2) + 1 * f, USD( 0), XRP(10), USD(20), USD(20), XRP(20), tesSUCCESS, XRP( 10) + (1 * f), USD(20), 1, 2, XRP(10), USD(10)},
+
2920 // acct pays USD
+
2921 {"meg", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP( 5), XRP(10), USD(10), tecINSUF_RESERVE_OFFER, XRP( 0) + (2 * f), USD(10), 0, 1},
+
2922 {"nia", reserve(env, 2) + 2 * f, USD(10), USD(10), XRP( 5), XRP(10), USD(10), tesSUCCESS, XRP( 0) + (2 * f), USD(10), 1, 2, USD(10), XRP(10)},
+
2923 {"ova", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP(10), XRP(10), USD(10), tesSUCCESS, XRP(-10) + (2 * f), USD( 0), 0, 1},
+
2924 {"pam", reserve(env, 1) + 2 * f, USD(10), USD(10), XRP(20), XRP(10), USD(10), tesSUCCESS, XRP(-20) + (2 * f), USD( 0), 0, 1},
+
2925 {"qui", reserve(env, 1) + 2 * f, USD(10), USD(20), XRP(40), XRP(10), USD(10), tesSUCCESS, XRP(-20) + (2 * f), USD( 0), 0, 1},
+
2926 {"rae", reserve(env, 2) + 2 * f, USD(10), USD( 5), XRP( 5), XRP(10), USD(10), tesSUCCESS, XRP( -5) + (2 * f), USD( 5), 1, 2, USD( 5), XRP( 5)},
+
2927 {"sue", reserve(env, 2) + 2 * f, USD(10), USD( 5), XRP(10), XRP(10), USD(10), tesSUCCESS, XRP(-10) + (2 * f), USD( 5), 1, 2, USD( 5), XRP( 5)},
+
2928 };
+
2929 // clang-format on
+
2930
+
2931 auto const zeroUsd = USD(0);
+
2932 for (auto const& t : tests)
+
2933 {
+
2934 // Make sure gateway has no current offers.
+
2935 env.require(offers(gw, 0));
+
2936
+
2937 auto const acct = Account(t.account);
+
2938
+
2939 env.fund(t.fundXrp, acct);
+
2940 env.close();
+
2941
+
2942 // Optionally give acct some USD. This is not part of the test,
+
2943 // so we assume that acct has sufficient USD to cover the reserve
+
2944 // on the trust line.
+
2945 if (t.fundUSD != zeroUsd)
+
2946 {
+
2947 env(trust(acct, t.fundUSD));
+
2948 env.close();
+
2949 env(pay(gw, acct, t.fundUSD));
+
2950 env.close();
+
2951 }
2952
-
2953 auto const acct = Account(t.account);
-
2954
-
2955 env.fund(t.fundXrp, acct);
-
2956 env.close();
-
2957
-
2958 // Optionally give acct some USD. This is not part of the test,
-
2959 // so we assume that acct has sufficient USD to cover the reserve
-
2960 // on the trust line.
-
2961 if (t.fundUSD != zeroUsd)
-
2962 {
-
2963 env(trust(acct, t.fundUSD));
-
2964 env.close();
-
2965 env(pay(gw, acct, t.fundUSD));
-
2966 env.close();
-
2967 }
+
2953 env(offer(gw, t.gwGets, t.gwPays));
+
2954 env.close();
+
2955 std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
+
2956
+
2957 // Acct creates a tfSell offer. This is the heart of the test.
+
2958 env(offer(acct, t.acctGets, t.acctPays, tfSell), ter(t.tec));
+
2959 env.close();
+
2960 std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
+
2961
+
2962 // Check results
+
2963 BEAST_EXPECT(env.balance(acct, USD.issue()) == t.finalUsd);
+
2964 BEAST_EXPECT(
+
2965 env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
+
2966 env.require(offers(acct, t.offers));
+
2967 env.require(owners(acct, t.owners));
2968
-
2969 env(offer(gw, t.gwGets, t.gwPays));
-
2970 env.close();
-
2971 std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
-
2972
-
2973 // Acct creates a tfSell offer. This is the heart of the test.
-
2974 env(offer(acct, t.acctGets, t.acctPays, tfSell), ter(t.tec));
-
2975 env.close();
-
2976 std::uint32_t const acctOfferSeq = env.seq(acct) - 1;
-
2977
-
2978 // Check results
-
2979 BEAST_EXPECT(env.balance(acct, USD.issue()) == t.finalUsd);
-
2980 BEAST_EXPECT(
-
2981 env.balance(acct, xrpIssue()) == t.fundXrp - t.spentXrp);
-
2982 env.require(offers(acct, t.offers));
-
2983 env.require(owners(acct, t.owners));
-
2984
-
2985 if (t.offers)
-
2986 {
-
2987 auto const acctOffers = offersOnAccount(env, acct);
-
2988 if (acctOffers.size() > 0)
-
2989 {
-
2990 BEAST_EXPECT(acctOffers.size() == 1);
-
2991 auto const& acctOffer = *(acctOffers.front());
-
2992
-
2993 BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER);
-
2994 BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets);
-
2995 BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays);
-
2996 }
-
2997 }
-
2998
-
2999 // Give the next loop a clean slate by canceling any left-overs
-
3000 // in the offers.
-
3001 env(offer_cancel(acct, acctOfferSeq));
-
3002 env(offer_cancel(gw, gwOfferSeq));
-
3003 env.close();
-
3004 }
-
3005 }
+
2969 if (t.offers)
+
2970 {
+
2971 auto const acctOffers = offersOnAccount(env, acct);
+
2972 if (acctOffers.size() > 0)
+
2973 {
+
2974 BEAST_EXPECT(acctOffers.size() == 1);
+
2975 auto const& acctOffer = *(acctOffers.front());
+
2976
+
2977 BEAST_EXPECT(acctOffer[sfLedgerEntryType] == ltOFFER);
+
2978 BEAST_EXPECT(acctOffer[sfTakerGets] == t.takerGets);
+
2979 BEAST_EXPECT(acctOffer[sfTakerPays] == t.takerPays);
+
2980 }
+
2981 }
+
2982
+
2983 // Give the next loop a clean slate by canceling any left-overs
+
2984 // in the offers.
+
2985 env(offer_cancel(acct, acctOfferSeq));
+
2986 env(offer_cancel(gw, gwOfferSeq));
+
2987 env.close();
+
2988 }
+
2989 }
+
2990
+
2991 void
+
2992 testSellWithFillOrKill(FeatureBitset features)
+
2993 {
+
2994 // Test a number of different corner cases regarding offer crossing
+
2995 // when both the tfSell flag and tfFillOrKill flags are set.
+
2996 testcase("Combine tfSell with tfFillOrKill");
+
2997
+
2998 using namespace jtx;
+
2999
+
3000 auto const gw = Account("gateway");
+
3001 auto const alice = Account("alice");
+
3002 auto const bob = Account("bob");
+
3003 auto const USD = gw["USD"];
+
3004
+
3005 Env env{*this, features};
3006
-
3007 void
-
3008 testSellWithFillOrKill(FeatureBitset features)
-
3009 {
-
3010 // Test a number of different corner cases regarding offer crossing
-
3011 // when both the tfSell flag and tfFillOrKill flags are set.
-
3012 testcase("Combine tfSell with tfFillOrKill");
+
3007 env.fund(XRP(10000000), gw, alice, bob);
+
3008 env.close();
+
3009
+
3010 // Code returned if an offer is killed.
+
3011 TER const killedCode{
+
3012 features[fix1578] ? TER{tecKILLED} : TER{tesSUCCESS}};
3013
-
3014 using namespace jtx;
-
3015
-
3016 auto const gw = Account("gateway");
-
3017 auto const alice = Account("alice");
-
3018 auto const bob = Account("bob");
-
3019 auto const USD = gw["USD"];
-
3020
-
3021 Env env{*this, features};
-
3022
-
3023 env.fund(XRP(10000000), gw, alice, bob);
-
3024 env.close();
-
3025
-
3026 // Code returned if an offer is killed.
-
3027 TER const killedCode{
-
3028 features[fix1578] ? TER{tecKILLED} : TER{tesSUCCESS}};
-
3029
-
3030 // bob offers XRP for USD.
-
3031 env(trust(bob, USD(200)));
-
3032 env.close();
-
3033 env(pay(gw, bob, USD(100)));
-
3034 env.close();
-
3035 env(offer(bob, XRP(2000), USD(20)));
-
3036 env.close();
-
3037 {
-
3038 // alice submits a tfSell | tfFillOrKill offer that does not cross.
-
3039 env(offer(alice, USD(21), XRP(2100), tfSell | tfFillOrKill),
-
3040 ter(killedCode));
-
3041 env.close();
-
3042 env.require(balance(alice, USD(none)));
-
3043 env.require(offers(alice, 0));
-
3044 env.require(balance(bob, USD(100)));
-
3045 }
-
3046 {
-
3047 // alice submits a tfSell | tfFillOrKill offer that crosses.
-
3048 // Even though tfSell is present it doesn't matter this time.
-
3049 env(offer(alice, USD(20), XRP(2000), tfSell | tfFillOrKill));
-
3050 env.close();
-
3051 env.require(balance(alice, USD(20)));
-
3052 env.require(offers(alice, 0));
-
3053 env.require(balance(bob, USD(80)));
-
3054 }
-
3055 {
-
3056 // alice submits a tfSell | tfFillOrKill offer that crosses and
-
3057 // returns more than was asked for (because of the tfSell flag).
-
3058 env(offer(bob, XRP(2000), USD(20)));
+
3014 // bob offers XRP for USD.
+
3015 env(trust(bob, USD(200)));
+
3016 env.close();
+
3017 env(pay(gw, bob, USD(100)));
+
3018 env.close();
+
3019 env(offer(bob, XRP(2000), USD(20)));
+
3020 env.close();
+
3021 {
+
3022 // alice submits a tfSell | tfFillOrKill offer that does not cross.
+
3023 env(offer(alice, USD(21), XRP(2100), tfSell | tfFillOrKill),
+
3024 ter(killedCode));
+
3025 env.close();
+
3026 env.require(balance(alice, USD(none)));
+
3027 env.require(offers(alice, 0));
+
3028 env.require(balance(bob, USD(100)));
+
3029 }
+
3030 {
+
3031 // alice submits a tfSell | tfFillOrKill offer that crosses.
+
3032 // Even though tfSell is present it doesn't matter this time.
+
3033 env(offer(alice, USD(20), XRP(2000), tfSell | tfFillOrKill));
+
3034 env.close();
+
3035 env.require(balance(alice, USD(20)));
+
3036 env.require(offers(alice, 0));
+
3037 env.require(balance(bob, USD(80)));
+
3038 }
+
3039 {
+
3040 // alice submits a tfSell | tfFillOrKill offer that crosses and
+
3041 // returns more than was asked for (because of the tfSell flag).
+
3042 env(offer(bob, XRP(2000), USD(20)));
+
3043 env.close();
+
3044 env(offer(alice, USD(10), XRP(1500), tfSell | tfFillOrKill));
+
3045 env.close();
+
3046 env.require(balance(alice, USD(35)));
+
3047 env.require(offers(alice, 0));
+
3048 env.require(balance(bob, USD(65)));
+
3049 }
+
3050 {
+
3051 // alice submits a tfSell | tfFillOrKill offer that doesn't cross.
+
3052 // This would have succeeded with a regular tfSell, but the
+
3053 // fillOrKill prevents the transaction from crossing since not
+
3054 // all of the offer is consumed.
+
3055
+
3056 // We're using bob's left-over offer for XRP(500), USD(5)
+
3057 env(offer(alice, USD(1), XRP(501), tfSell | tfFillOrKill),
+
3058 ter(killedCode));
3059 env.close();
-
3060 env(offer(alice, USD(10), XRP(1500), tfSell | tfFillOrKill));
-
3061 env.close();
-
3062 env.require(balance(alice, USD(35)));
-
3063 env.require(offers(alice, 0));
-
3064 env.require(balance(bob, USD(65)));
-
3065 }
-
3066 {
-
3067 // alice submits a tfSell | tfFillOrKill offer that doesn't cross.
-
3068 // This would have succeeded with a regular tfSell, but the
-
3069 // fillOrKill prevents the transaction from crossing since not
-
3070 // all of the offer is consumed.
-
3071
-
3072 // We're using bob's left-over offer for XRP(500), USD(5)
-
3073 env(offer(alice, USD(1), XRP(501), tfSell | tfFillOrKill),
-
3074 ter(killedCode));
-
3075 env.close();
-
3076 env.require(balance(alice, USD(35)));
-
3077 env.require(offers(alice, 0));
-
3078 env.require(balance(bob, USD(65)));
-
3079 }
-
3080 {
-
3081 // Alice submits a tfSell | tfFillOrKill offer that finishes
-
3082 // off the remainder of bob's offer.
+
3060 env.require(balance(alice, USD(35)));
+
3061 env.require(offers(alice, 0));
+
3062 env.require(balance(bob, USD(65)));
+
3063 }
+
3064 {
+
3065 // Alice submits a tfSell | tfFillOrKill offer that finishes
+
3066 // off the remainder of bob's offer.
+
3067
+
3068 // We're using bob's left-over offer for XRP(500), USD(5)
+
3069 env(offer(alice, USD(1), XRP(500), tfSell | tfFillOrKill));
+
3070 env.close();
+
3071 env.require(balance(alice, USD(40)));
+
3072 env.require(offers(alice, 0));
+
3073 env.require(balance(bob, USD(60)));
+
3074 }
+
3075 }
+
3076
+
3077 void
+
3078 testTransferRateOffer(FeatureBitset features)
+
3079 {
+
3080 testcase("Transfer Rate Offer");
+
3081
+
3082 using namespace jtx;
3083
-
3084 // We're using bob's left-over offer for XRP(500), USD(5)
-
3085 env(offer(alice, USD(1), XRP(500), tfSell | tfFillOrKill));
-
3086 env.close();
-
3087 env.require(balance(alice, USD(40)));
-
3088 env.require(offers(alice, 0));
-
3089 env.require(balance(bob, USD(60)));
-
3090 }
-
3091 }
-
3092
-
3093 void
-
3094 testTransferRateOffer(FeatureBitset features)
-
3095 {
-
3096 testcase("Transfer Rate Offer");
-
3097
-
3098 using namespace jtx;
-
3099
-
3100 auto const gw1 = Account("gateway1");
-
3101 auto const USD = gw1["USD"];
-
3102
-
3103 Env env{*this, features};
-
3104
-
3105 // The fee that's charged for transactions.
-
3106 auto const fee = env.current()->fees().base;
-
3107
-
3108 env.fund(XRP(100000), gw1);
-
3109 env.close();
-
3110
-
3111 env(rate(gw1, 1.25));
-
3112 {
-
3113 auto const ann = Account("ann");
-
3114 auto const bob = Account("bob");
-
3115 env.fund(XRP(100) + reserve(env, 2) + (fee * 2), ann, bob);
+
3084 auto const gw1 = Account("gateway1");
+
3085 auto const USD = gw1["USD"];
+
3086
+
3087 Env env{*this, features};
+
3088
+
3089 // The fee that's charged for transactions.
+
3090 auto const fee = env.current()->fees().base;
+
3091
+
3092 env.fund(XRP(100000), gw1);
+
3093 env.close();
+
3094
+
3095 env(rate(gw1, 1.25));
+
3096 {
+
3097 auto const ann = Account("ann");
+
3098 auto const bob = Account("bob");
+
3099 env.fund(XRP(100) + reserve(env, 2) + (fee * 2), ann, bob);
+
3100 env.close();
+
3101
+
3102 env(trust(ann, USD(200)));
+
3103 env(trust(bob, USD(200)));
+
3104 env.close();
+
3105
+
3106 env(pay(gw1, bob, USD(125)));
+
3107 env.close();
+
3108
+
3109 // bob offers to sell USD(100) for XRP. alice takes bob's offer.
+
3110 // Notice that although bob only offered USD(100), USD(125) was
+
3111 // removed from his account due to the gateway fee.
+
3112 //
+
3113 // A comparable payment would look like this:
+
3114 // env (pay (bob, alice, USD(100)), sendmax(USD(125)))
+
3115 env(offer(bob, XRP(1), USD(100)));
3116 env.close();
3117
-
3118 env(trust(ann, USD(200)));
-
3119 env(trust(bob, USD(200)));
-
3120 env.close();
-
3121
-
3122 env(pay(gw1, bob, USD(125)));
-
3123 env.close();
+
3118 env(offer(ann, USD(100), XRP(1)));
+
3119 env.close();
+
3120
+
3121 env.require(balance(ann, USD(100)));
+
3122 env.require(balance(ann, XRP(99) + reserve(env, 2)));
+
3123 env.require(offers(ann, 0));
3124
-
3125 // bob offers to sell USD(100) for XRP. alice takes bob's offer.
-
3126 // Notice that although bob only offered USD(100), USD(125) was
-
3127 // removed from his account due to the gateway fee.
-
3128 //
-
3129 // A comparable payment would look like this:
-
3130 // env (pay (bob, alice, USD(100)), sendmax(USD(125)))
-
3131 env(offer(bob, XRP(1), USD(100)));
-
3132 env.close();
-
3133
-
3134 env(offer(ann, USD(100), XRP(1)));
+
3125 env.require(balance(bob, USD(0)));
+
3126 env.require(balance(bob, XRP(101) + reserve(env, 2)));
+
3127 env.require(offers(bob, 0));
+
3128 }
+
3129 {
+
3130 // Reverse the order, so the offer in the books is to sell XRP
+
3131 // in return for USD. Gateway rate should still apply identically.
+
3132 auto const che = Account("che");
+
3133 auto const deb = Account("deb");
+
3134 env.fund(XRP(100) + reserve(env, 2) + (fee * 2), che, deb);
3135 env.close();
3136
-
3137 env.require(balance(ann, USD(100)));
-
3138 env.require(balance(ann, XRP(99) + reserve(env, 2)));
-
3139 env.require(offers(ann, 0));
+
3137 env(trust(che, USD(200)));
+
3138 env(trust(deb, USD(200)));
+
3139 env.close();
3140
-
3141 env.require(balance(bob, USD(0)));
-
3142 env.require(balance(bob, XRP(101) + reserve(env, 2)));
-
3143 env.require(offers(bob, 0));
-
3144 }
-
3145 {
-
3146 // Reverse the order, so the offer in the books is to sell XRP
-
3147 // in return for USD. Gateway rate should still apply identically.
-
3148 auto const che = Account("che");
-
3149 auto const deb = Account("deb");
-
3150 env.fund(XRP(100) + reserve(env, 2) + (fee * 2), che, deb);
-
3151 env.close();
-
3152
-
3153 env(trust(che, USD(200)));
-
3154 env(trust(deb, USD(200)));
-
3155 env.close();
-
3156
-
3157 env(pay(gw1, deb, USD(125)));
-
3158 env.close();
-
3159
-
3160 env(offer(che, USD(100), XRP(1)));
-
3161 env.close();
-
3162
-
3163 env(offer(deb, XRP(1), USD(100)));
-
3164 env.close();
-
3165
-
3166 env.require(balance(che, USD(100)));
-
3167 env.require(balance(che, XRP(99) + reserve(env, 2)));
-
3168 env.require(offers(che, 0));
-
3169
-
3170 env.require(balance(deb, USD(0)));
-
3171 env.require(balance(deb, XRP(101) + reserve(env, 2)));
-
3172 env.require(offers(deb, 0));
-
3173 }
-
3174 {
-
3175 auto const eve = Account("eve");
-
3176 auto const fyn = Account("fyn");
-
3177
-
3178 env.fund(XRP(20000) + (fee * 2), eve, fyn);
-
3179 env.close();
-
3180
-
3181 env(trust(eve, USD(1000)));
-
3182 env(trust(fyn, USD(1000)));
-
3183 env.close();
-
3184
-
3185 env(pay(gw1, eve, USD(100)));
-
3186 env(pay(gw1, fyn, USD(100)));
-
3187 env.close();
-
3188
-
3189 // This test verifies that the amount removed from an offer
-
3190 // accounts for the transfer fee that is removed from the
-
3191 // account but not from the remaining offer.
-
3192 env(offer(eve, USD(10), XRP(4000)));
-
3193 env.close();
-
3194 std::uint32_t const eveOfferSeq = env.seq(eve) - 1;
+
3141 env(pay(gw1, deb, USD(125)));
+
3142 env.close();
+
3143
+
3144 env(offer(che, USD(100), XRP(1)));
+
3145 env.close();
+
3146
+
3147 env(offer(deb, XRP(1), USD(100)));
+
3148 env.close();
+
3149
+
3150 env.require(balance(che, USD(100)));
+
3151 env.require(balance(che, XRP(99) + reserve(env, 2)));
+
3152 env.require(offers(che, 0));
+
3153
+
3154 env.require(balance(deb, USD(0)));
+
3155 env.require(balance(deb, XRP(101) + reserve(env, 2)));
+
3156 env.require(offers(deb, 0));
+
3157 }
+
3158 {
+
3159 auto const eve = Account("eve");
+
3160 auto const fyn = Account("fyn");
+
3161
+
3162 env.fund(XRP(20000) + (fee * 2), eve, fyn);
+
3163 env.close();
+
3164
+
3165 env(trust(eve, USD(1000)));
+
3166 env(trust(fyn, USD(1000)));
+
3167 env.close();
+
3168
+
3169 env(pay(gw1, eve, USD(100)));
+
3170 env(pay(gw1, fyn, USD(100)));
+
3171 env.close();
+
3172
+
3173 // This test verifies that the amount removed from an offer
+
3174 // accounts for the transfer fee that is removed from the
+
3175 // account but not from the remaining offer.
+
3176 env(offer(eve, USD(10), XRP(4000)));
+
3177 env.close();
+
3178 std::uint32_t const eveOfferSeq = env.seq(eve) - 1;
+
3179
+
3180 env(offer(fyn, XRP(2000), USD(5)));
+
3181 env.close();
+
3182
+
3183 env.require(balance(eve, USD(105)));
+
3184 env.require(balance(eve, XRP(18000)));
+
3185 auto const evesOffers = offersOnAccount(env, eve);
+
3186 BEAST_EXPECT(evesOffers.size() == 1);
+
3187 if (evesOffers.size() != 0)
+
3188 {
+
3189 auto const& evesOffer = *(evesOffers.front());
+
3190 BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER);
+
3191 BEAST_EXPECT(evesOffer[sfTakerGets] == XRP(2000));
+
3192 BEAST_EXPECT(evesOffer[sfTakerPays] == USD(5));
+
3193 }
+
3194 env(offer_cancel(eve, eveOfferSeq)); // For later tests
3195
-
3196 env(offer(fyn, XRP(2000), USD(5)));
-
3197 env.close();
-
3198
-
3199 env.require(balance(eve, USD(105)));
-
3200 env.require(balance(eve, XRP(18000)));
-
3201 auto const evesOffers = offersOnAccount(env, eve);
-
3202 BEAST_EXPECT(evesOffers.size() == 1);
-
3203 if (evesOffers.size() != 0)
-
3204 {
-
3205 auto const& evesOffer = *(evesOffers.front());
-
3206 BEAST_EXPECT(evesOffer[sfLedgerEntryType] == ltOFFER);
-
3207 BEAST_EXPECT(evesOffer[sfTakerGets] == XRP(2000));
-
3208 BEAST_EXPECT(evesOffer[sfTakerPays] == USD(5));
-
3209 }
-
3210 env(offer_cancel(eve, eveOfferSeq)); // For later tests
-
3211
-
3212 env.require(balance(fyn, USD(93.75)));
-
3213 env.require(balance(fyn, XRP(22000)));
-
3214 env.require(offers(fyn, 0));
-
3215 }
-
3216 // Start messing with two non-native currencies.
-
3217 auto const gw2 = Account("gateway2");
-
3218 auto const EUR = gw2["EUR"];
-
3219
-
3220 env.fund(XRP(100000), gw2);
-
3221 env.close();
+
3196 env.require(balance(fyn, USD(93.75)));
+
3197 env.require(balance(fyn, XRP(22000)));
+
3198 env.require(offers(fyn, 0));
+
3199 }
+
3200 // Start messing with two non-native currencies.
+
3201 auto const gw2 = Account("gateway2");
+
3202 auto const EUR = gw2["EUR"];
+
3203
+
3204 env.fund(XRP(100000), gw2);
+
3205 env.close();
+
3206
+
3207 env(rate(gw2, 1.5));
+
3208 {
+
3209 // Remove XRP from the equation. Give the two currencies two
+
3210 // different transfer rates so we can see both transfer rates
+
3211 // apply in the same transaction.
+
3212 auto const gay = Account("gay");
+
3213 auto const hal = Account("hal");
+
3214 env.fund(reserve(env, 3) + (fee * 3), gay, hal);
+
3215 env.close();
+
3216
+
3217 env(trust(gay, USD(200)));
+
3218 env(trust(gay, EUR(200)));
+
3219 env(trust(hal, USD(200)));
+
3220 env(trust(hal, EUR(200)));
+
3221 env.close();
3222
-
3223 env(rate(gw2, 1.5));
-
3224 {
-
3225 // Remove XRP from the equation. Give the two currencies two
-
3226 // different transfer rates so we can see both transfer rates
-
3227 // apply in the same transaction.
-
3228 auto const gay = Account("gay");
-
3229 auto const hal = Account("hal");
-
3230 env.fund(reserve(env, 3) + (fee * 3), gay, hal);
+
3223 env(pay(gw1, gay, USD(125)));
+
3224 env(pay(gw2, hal, EUR(150)));
+
3225 env.close();
+
3226
+
3227 env(offer(gay, EUR(100), USD(100)));
+
3228 env.close();
+
3229
+
3230 env(offer(hal, USD(100), EUR(100)));
3231 env.close();
3232
-
3233 env(trust(gay, USD(200)));
-
3234 env(trust(gay, EUR(200)));
-
3235 env(trust(hal, USD(200)));
-
3236 env(trust(hal, EUR(200)));
-
3237 env.close();
-
3238
-
3239 env(pay(gw1, gay, USD(125)));
-
3240 env(pay(gw2, hal, EUR(150)));
-
3241 env.close();
-
3242
-
3243 env(offer(gay, EUR(100), USD(100)));
-
3244 env.close();
-
3245
-
3246 env(offer(hal, USD(100), EUR(100)));
-
3247 env.close();
-
3248
-
3249 env.require(balance(gay, USD(0)));
-
3250 env.require(balance(gay, EUR(100)));
-
3251 env.require(balance(gay, reserve(env, 3)));
-
3252 env.require(offers(gay, 0));
-
3253
-
3254 env.require(balance(hal, USD(100)));
-
3255 env.require(balance(hal, EUR(0)));
-
3256 env.require(balance(hal, reserve(env, 3)));
-
3257 env.require(offers(hal, 0));
-
3258 }
-
3259 {
-
3260 // A trust line's QualityIn should not affect offer crossing.
-
3261 auto const ivy = Account("ivy");
-
3262 auto const joe = Account("joe");
-
3263 env.fund(reserve(env, 3) + (fee * 3), ivy, joe);
-
3264 env.close();
-
3265
-
3266 env(trust(ivy, USD(400)), qualityInPercent(90));
-
3267 env(trust(ivy, EUR(400)), qualityInPercent(80));
-
3268 env(trust(joe, USD(400)), qualityInPercent(70));
-
3269 env(trust(joe, EUR(400)), qualityInPercent(60));
-
3270 env.close();
-
3271
-
3272 env(pay(gw1, ivy, USD(270)), sendmax(USD(500)));
-
3273 env(pay(gw2, joe, EUR(150)), sendmax(EUR(300)));
-
3274 env.close();
-
3275 env.require(balance(ivy, USD(300)));
-
3276 env.require(balance(joe, EUR(250)));
-
3277
-
3278 env(offer(ivy, EUR(100), USD(200)));
-
3279 env.close();
-
3280
-
3281 env(offer(joe, USD(200), EUR(100)));
-
3282 env.close();
-
3283
-
3284 env.require(balance(ivy, USD(50)));
-
3285 env.require(balance(ivy, EUR(100)));
-
3286 env.require(balance(ivy, reserve(env, 3)));
-
3287 env.require(offers(ivy, 0));
-
3288
-
3289 env.require(balance(joe, USD(200)));
-
3290 env.require(balance(joe, EUR(100)));
-
3291 env.require(balance(joe, reserve(env, 3)));
-
3292 env.require(offers(joe, 0));
-
3293 }
-
3294 {
-
3295 // A trust line's QualityOut should not affect offer crossing.
-
3296 auto const kim = Account("kim");
-
3297 auto const K_BUX = kim["BUX"];
-
3298 auto const lex = Account("lex");
-
3299 auto const meg = Account("meg");
-
3300 auto const ned = Account("ned");
-
3301 auto const N_BUX = ned["BUX"];
-
3302
-
3303 // Verify trust line QualityOut affects payments.
-
3304 env.fund(reserve(env, 4) + (fee * 4), kim, lex, meg, ned);
-
3305 env.close();
-
3306
-
3307 env(trust(lex, K_BUX(400)));
-
3308 env(trust(lex, N_BUX(200)), qualityOutPercent(120));
-
3309 env(trust(meg, N_BUX(100)));
-
3310 env.close();
-
3311 env(pay(ned, lex, N_BUX(100)));
-
3312 env.close();
-
3313 env.require(balance(lex, N_BUX(100)));
+
3233 env.require(balance(gay, USD(0)));
+
3234 env.require(balance(gay, EUR(100)));
+
3235 env.require(balance(gay, reserve(env, 3)));
+
3236 env.require(offers(gay, 0));
+
3237
+
3238 env.require(balance(hal, USD(100)));
+
3239 env.require(balance(hal, EUR(0)));
+
3240 env.require(balance(hal, reserve(env, 3)));
+
3241 env.require(offers(hal, 0));
+
3242 }
+
3243 {
+
3244 // A trust line's QualityIn should not affect offer crossing.
+
3245 auto const ivy = Account("ivy");
+
3246 auto const joe = Account("joe");
+
3247 env.fund(reserve(env, 3) + (fee * 3), ivy, joe);
+
3248 env.close();
+
3249
+
3250 env(trust(ivy, USD(400)), qualityInPercent(90));
+
3251 env(trust(ivy, EUR(400)), qualityInPercent(80));
+
3252 env(trust(joe, USD(400)), qualityInPercent(70));
+
3253 env(trust(joe, EUR(400)), qualityInPercent(60));
+
3254 env.close();
+
3255
+
3256 env(pay(gw1, ivy, USD(270)), sendmax(USD(500)));
+
3257 env(pay(gw2, joe, EUR(150)), sendmax(EUR(300)));
+
3258 env.close();
+
3259 env.require(balance(ivy, USD(300)));
+
3260 env.require(balance(joe, EUR(250)));
+
3261
+
3262 env(offer(ivy, EUR(100), USD(200)));
+
3263 env.close();
+
3264
+
3265 env(offer(joe, USD(200), EUR(100)));
+
3266 env.close();
+
3267
+
3268 env.require(balance(ivy, USD(50)));
+
3269 env.require(balance(ivy, EUR(100)));
+
3270 env.require(balance(ivy, reserve(env, 3)));
+
3271 env.require(offers(ivy, 0));
+
3272
+
3273 env.require(balance(joe, USD(200)));
+
3274 env.require(balance(joe, EUR(100)));
+
3275 env.require(balance(joe, reserve(env, 3)));
+
3276 env.require(offers(joe, 0));
+
3277 }
+
3278 {
+
3279 // A trust line's QualityOut should not affect offer crossing.
+
3280 auto const kim = Account("kim");
+
3281 auto const K_BUX = kim["BUX"];
+
3282 auto const lex = Account("lex");
+
3283 auto const meg = Account("meg");
+
3284 auto const ned = Account("ned");
+
3285 auto const N_BUX = ned["BUX"];
+
3286
+
3287 // Verify trust line QualityOut affects payments.
+
3288 env.fund(reserve(env, 4) + (fee * 4), kim, lex, meg, ned);
+
3289 env.close();
+
3290
+
3291 env(trust(lex, K_BUX(400)));
+
3292 env(trust(lex, N_BUX(200)), qualityOutPercent(120));
+
3293 env(trust(meg, N_BUX(100)));
+
3294 env.close();
+
3295 env(pay(ned, lex, N_BUX(100)));
+
3296 env.close();
+
3297 env.require(balance(lex, N_BUX(100)));
+
3298
+
3299 env(pay(kim, meg, N_BUX(60)), path(lex, ned), sendmax(K_BUX(200)));
+
3300 env.close();
+
3301
+
3302 env.require(balance(kim, K_BUX(none)));
+
3303 env.require(balance(kim, N_BUX(none)));
+
3304 env.require(balance(lex, K_BUX(72)));
+
3305 env.require(balance(lex, N_BUX(40)));
+
3306 env.require(balance(meg, K_BUX(none)));
+
3307 env.require(balance(meg, N_BUX(60)));
+
3308 env.require(balance(ned, K_BUX(none)));
+
3309 env.require(balance(ned, N_BUX(none)));
+
3310
+
3311 // Now verify that offer crossing is unaffected by QualityOut.
+
3312 env(offer(lex, K_BUX(30), N_BUX(30)));
+
3313 env.close();
3314
-
3315 env(pay(kim, meg, N_BUX(60)), path(lex, ned), sendmax(K_BUX(200)));
+
3315 env(offer(kim, N_BUX(30), K_BUX(30)));
3316 env.close();
3317
3318 env.require(balance(kim, K_BUX(none)));
-
3319 env.require(balance(kim, N_BUX(none)));
-
3320 env.require(balance(lex, K_BUX(72)));
-
3321 env.require(balance(lex, N_BUX(40)));
+
3319 env.require(balance(kim, N_BUX(30)));
+
3320 env.require(balance(lex, K_BUX(102)));
+
3321 env.require(balance(lex, N_BUX(10)));
3322 env.require(balance(meg, K_BUX(none)));
3323 env.require(balance(meg, N_BUX(60)));
-
3324 env.require(balance(ned, K_BUX(none)));
+
3324 env.require(balance(ned, K_BUX(-30)));
3325 env.require(balance(ned, N_BUX(none)));
-
3326
-
3327 // Now verify that offer crossing is unaffected by QualityOut.
-
3328 env(offer(lex, K_BUX(30), N_BUX(30)));
-
3329 env.close();
-
3330
-
3331 env(offer(kim, N_BUX(30), K_BUX(30)));
-
3332 env.close();
-
3333
-
3334 env.require(balance(kim, K_BUX(none)));
-
3335 env.require(balance(kim, N_BUX(30)));
-
3336 env.require(balance(lex, K_BUX(102)));
-
3337 env.require(balance(lex, N_BUX(10)));
-
3338 env.require(balance(meg, K_BUX(none)));
-
3339 env.require(balance(meg, N_BUX(60)));
-
3340 env.require(balance(ned, K_BUX(-30)));
-
3341 env.require(balance(ned, N_BUX(none)));
-
3342 }
-
3343 {
-
3344 // Make sure things work right when we're auto-bridging as well.
-
3345 auto const ova = Account("ova");
-
3346 auto const pat = Account("pat");
-
3347 auto const qae = Account("qae");
-
3348 env.fund(XRP(2) + reserve(env, 3) + (fee * 3), ova, pat, qae);
-
3349 env.close();
-
3350
-
3351 // o ova has USD but wants XRP.
-
3352 // o pat has XRP but wants EUR.
-
3353 // o qae has EUR but wants USD.
-
3354 env(trust(ova, USD(200)));
-
3355 env(trust(ova, EUR(200)));
-
3356 env(trust(pat, USD(200)));
-
3357 env(trust(pat, EUR(200)));
-
3358 env(trust(qae, USD(200)));
-
3359 env(trust(qae, EUR(200)));
-
3360 env.close();
-
3361
-
3362 env(pay(gw1, ova, USD(125)));
-
3363 env(pay(gw2, qae, EUR(150)));
-
3364 env.close();
-
3365
-
3366 env(offer(ova, XRP(2), USD(100)));
-
3367 env(offer(pat, EUR(100), XRP(2)));
-
3368 env.close();
-
3369
-
3370 env(offer(qae, USD(100), EUR(100)));
-
3371 env.close();
+
3326 }
+
3327 {
+
3328 // Make sure things work right when we're auto-bridging as well.
+
3329 auto const ova = Account("ova");
+
3330 auto const pat = Account("pat");
+
3331 auto const qae = Account("qae");
+
3332 env.fund(XRP(2) + reserve(env, 3) + (fee * 3), ova, pat, qae);
+
3333 env.close();
+
3334
+
3335 // o ova has USD but wants XRP.
+
3336 // o pat has XRP but wants EUR.
+
3337 // o qae has EUR but wants USD.
+
3338 env(trust(ova, USD(200)));
+
3339 env(trust(ova, EUR(200)));
+
3340 env(trust(pat, USD(200)));
+
3341 env(trust(pat, EUR(200)));
+
3342 env(trust(qae, USD(200)));
+
3343 env(trust(qae, EUR(200)));
+
3344 env.close();
+
3345
+
3346 env(pay(gw1, ova, USD(125)));
+
3347 env(pay(gw2, qae, EUR(150)));
+
3348 env.close();
+
3349
+
3350 env(offer(ova, XRP(2), USD(100)));
+
3351 env(offer(pat, EUR(100), XRP(2)));
+
3352 env.close();
+
3353
+
3354 env(offer(qae, USD(100), EUR(100)));
+
3355 env.close();
+
3356
+
3357 env.require(balance(ova, USD(0)));
+
3358 env.require(balance(ova, EUR(0)));
+
3359 env.require(balance(ova, XRP(4) + reserve(env, 3)));
+
3360
+
3361 // In pre-flow code ova's offer is left empty in the ledger.
+
3362 auto const ovasOffers = offersOnAccount(env, ova);
+
3363 if (ovasOffers.size() != 0)
+
3364 {
+
3365 BEAST_EXPECT(ovasOffers.size() == 1);
+
3366 auto const& ovasOffer = *(ovasOffers.front());
+
3367
+
3368 BEAST_EXPECT(ovasOffer[sfLedgerEntryType] == ltOFFER);
+
3369 BEAST_EXPECT(ovasOffer[sfTakerGets] == USD(0));
+
3370 BEAST_EXPECT(ovasOffer[sfTakerPays] == XRP(0));
+
3371 }
3372
-
3373 env.require(balance(ova, USD(0)));
-
3374 env.require(balance(ova, EUR(0)));
-
3375 env.require(balance(ova, XRP(4) + reserve(env, 3)));
-
3376
-
3377 // In pre-flow code ova's offer is left empty in the ledger.
-
3378 auto const ovasOffers = offersOnAccount(env, ova);
-
3379 if (ovasOffers.size() != 0)
-
3380 {
-
3381 BEAST_EXPECT(ovasOffers.size() == 1);
-
3382 auto const& ovasOffer = *(ovasOffers.front());
-
3383
-
3384 BEAST_EXPECT(ovasOffer[sfLedgerEntryType] == ltOFFER);
-
3385 BEAST_EXPECT(ovasOffer[sfTakerGets] == USD(0));
-
3386 BEAST_EXPECT(ovasOffer[sfTakerPays] == XRP(0));
-
3387 }
-
3388
-
3389 env.require(balance(pat, USD(0)));
-
3390 env.require(balance(pat, EUR(100)));
-
3391 env.require(balance(pat, XRP(0) + reserve(env, 3)));
-
3392 env.require(offers(pat, 0));
-
3393
-
3394 env.require(balance(qae, USD(100)));
-
3395 env.require(balance(qae, EUR(0)));
-
3396 env.require(balance(qae, XRP(2) + reserve(env, 3)));
-
3397 env.require(offers(qae, 0));
-
3398 }
-
3399 }
-
3400
-
3401 void
-
3402 testSelfCrossOffer1(FeatureBitset features)
-
3403 {
-
3404 // The following test verifies some correct but slightly surprising
-
3405 // behavior in offer crossing. The scenario:
-
3406 //
-
3407 // o An entity has created one or more offers.
-
3408 // o The entity creates another offer that can be directly crossed
-
3409 // (not autobridged) by the previously created offer(s).
-
3410 // o Rather than self crossing the offers, delete the old offer(s).
-
3411 //
-
3412 // See a more complete explanation in the comments for
-
3413 // BookOfferCrossingStep::limitSelfCrossQuality().
-
3414 //
-
3415 // Note that, in this particular example, one offer causes several
-
3416 // crossable offers (worth considerably more than the new offer)
-
3417 // to be removed from the book.
-
3418 using namespace jtx;
-
3419
-
3420 auto const gw = Account("gateway");
-
3421 auto const USD = gw["USD"];
+
3373 env.require(balance(pat, USD(0)));
+
3374 env.require(balance(pat, EUR(100)));
+
3375 env.require(balance(pat, XRP(0) + reserve(env, 3)));
+
3376 env.require(offers(pat, 0));
+
3377
+
3378 env.require(balance(qae, USD(100)));
+
3379 env.require(balance(qae, EUR(0)));
+
3380 env.require(balance(qae, XRP(2) + reserve(env, 3)));
+
3381 env.require(offers(qae, 0));
+
3382 }
+
3383 }
+
3384
+
3385 void
+
3386 testSelfCrossOffer1(FeatureBitset features)
+
3387 {
+
3388 // The following test verifies some correct but slightly surprising
+
3389 // behavior in offer crossing. The scenario:
+
3390 //
+
3391 // o An entity has created one or more offers.
+
3392 // o The entity creates another offer that can be directly crossed
+
3393 // (not autobridged) by the previously created offer(s).
+
3394 // o Rather than self crossing the offers, delete the old offer(s).
+
3395 //
+
3396 // See a more complete explanation in the comments for
+
3397 // BookOfferCrossingStep::limitSelfCrossQuality().
+
3398 //
+
3399 // Note that, in this particular example, one offer causes several
+
3400 // crossable offers (worth considerably more than the new offer)
+
3401 // to be removed from the book.
+
3402 using namespace jtx;
+
3403
+
3404 auto const gw = Account("gateway");
+
3405 auto const USD = gw["USD"];
+
3406
+
3407 Env env{*this, features};
+
3408
+
3409 // The fee that's charged for transactions.
+
3410 auto const fee = env.current()->fees().base;
+
3411 auto const startBalance = XRP(1000000);
+
3412
+
3413 env.fund(startBalance + (fee * 4), gw);
+
3414 env.close();
+
3415
+
3416 env(offer(gw, USD(60), XRP(600)));
+
3417 env.close();
+
3418 env(offer(gw, USD(60), XRP(600)));
+
3419 env.close();
+
3420 env(offer(gw, USD(60), XRP(600)));
+
3421 env.close();
3422
-
3423 Env env{*this, features};
-
3424
-
3425 // The fee that's charged for transactions.
-
3426 auto const fee = env.current()->fees().base;
-
3427 auto const startBalance = XRP(1000000);
-
3428
-
3429 env.fund(startBalance + (fee * 4), gw);
-
3430 env.close();
-
3431
-
3432 env(offer(gw, USD(60), XRP(600)));
-
3433 env.close();
-
3434 env(offer(gw, USD(60), XRP(600)));
-
3435 env.close();
-
3436 env(offer(gw, USD(60), XRP(600)));
-
3437 env.close();
-
3438
-
3439 env.require(owners(gw, 3));
-
3440 env.require(balance(gw, startBalance + fee));
-
3441
-
3442 auto gwOffers = offersOnAccount(env, gw);
-
3443 BEAST_EXPECT(gwOffers.size() == 3);
-
3444 for (auto const& offerPtr : gwOffers)
-
3445 {
-
3446 auto const& offer = *offerPtr;
-
3447 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3448 BEAST_EXPECT(offer[sfTakerGets] == XRP(600));
-
3449 BEAST_EXPECT(offer[sfTakerPays] == USD(60));
-
3450 }
-
3451
-
3452 // Since this offer crosses the first offers, the previous offers
-
3453 // will be deleted and this offer will be put on the order book.
-
3454 env(offer(gw, XRP(1000), USD(100)));
-
3455 env.close();
-
3456 env.require(owners(gw, 1));
-
3457 env.require(offers(gw, 1));
-
3458 env.require(balance(gw, startBalance));
+
3423 env.require(owners(gw, 3));
+
3424 env.require(balance(gw, startBalance + fee));
+
3425
+
3426 auto gwOffers = offersOnAccount(env, gw);
+
3427 BEAST_EXPECT(gwOffers.size() == 3);
+
3428 for (auto const& offerPtr : gwOffers)
+
3429 {
+
3430 auto const& offer = *offerPtr;
+
3431 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3432 BEAST_EXPECT(offer[sfTakerGets] == XRP(600));
+
3433 BEAST_EXPECT(offer[sfTakerPays] == USD(60));
+
3434 }
+
3435
+
3436 // Since this offer crosses the first offers, the previous offers
+
3437 // will be deleted and this offer will be put on the order book.
+
3438 env(offer(gw, XRP(1000), USD(100)));
+
3439 env.close();
+
3440 env.require(owners(gw, 1));
+
3441 env.require(offers(gw, 1));
+
3442 env.require(balance(gw, startBalance));
+
3443
+
3444 gwOffers = offersOnAccount(env, gw);
+
3445 BEAST_EXPECT(gwOffers.size() == 1);
+
3446 for (auto const& offerPtr : gwOffers)
+
3447 {
+
3448 auto const& offer = *offerPtr;
+
3449 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3450 BEAST_EXPECT(offer[sfTakerGets] == USD(100));
+
3451 BEAST_EXPECT(offer[sfTakerPays] == XRP(1000));
+
3452 }
+
3453 }
+
3454
+
3455 void
+
3456 testSelfCrossOffer2(FeatureBitset features)
+
3457 {
+
3458 using namespace jtx;
3459
-
3460 gwOffers = offersOnAccount(env, gw);
-
3461 BEAST_EXPECT(gwOffers.size() == 1);
-
3462 for (auto const& offerPtr : gwOffers)
-
3463 {
-
3464 auto const& offer = *offerPtr;
-
3465 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3466 BEAST_EXPECT(offer[sfTakerGets] == USD(100));
-
3467 BEAST_EXPECT(offer[sfTakerPays] == XRP(1000));
-
3468 }
-
3469 }
+
3460 auto const gw1 = Account("gateway1");
+
3461 auto const gw2 = Account("gateway2");
+
3462 auto const alice = Account("alice");
+
3463 auto const USD = gw1["USD"];
+
3464 auto const EUR = gw2["EUR"];
+
3465
+
3466 Env env{*this, features};
+
3467
+
3468 env.fund(XRP(1000000), gw1, gw2);
+
3469 env.close();
3470
-
3471 void
-
3472 testSelfCrossOffer2(FeatureBitset features)
-
3473 {
-
3474 using namespace jtx;
-
3475
-
3476 auto const gw1 = Account("gateway1");
-
3477 auto const gw2 = Account("gateway2");
-
3478 auto const alice = Account("alice");
-
3479 auto const USD = gw1["USD"];
-
3480 auto const EUR = gw2["EUR"];
-
3481
-
3482 Env env{*this, features};
-
3483
-
3484 env.fund(XRP(1000000), gw1, gw2);
-
3485 env.close();
-
3486
-
3487 // The fee that's charged for transactions.
-
3488 auto const f = env.current()->fees().base;
-
3489
-
3490 // Test cases
-
3491 struct TestData
-
3492 {
-
3493 std::string acct; // Account operated on
-
3494 STAmount fundXRP; // XRP acct funded with
-
3495 STAmount fundUSD; // USD acct funded with
-
3496 STAmount fundEUR; // EUR acct funded with
-
3497 TER firstOfferTec; // tec code on first offer
-
3498 TER secondOfferTec; // tec code on second offer
-
3499 };
-
3500
-
3501 // clang-format off
-
3502 TestData const tests[]{
-
3503 // acct fundXRP fundUSD fundEUR firstOfferTec secondOfferTec
-
3504 {"ann", reserve(env, 3) + f * 4, USD(1000), EUR(1000), tesSUCCESS, tesSUCCESS},
-
3505 {"bev", reserve(env, 3) + f * 4, USD( 1), EUR(1000), tesSUCCESS, tesSUCCESS},
-
3506 {"cam", reserve(env, 3) + f * 4, USD(1000), EUR( 1), tesSUCCESS, tesSUCCESS},
-
3507 {"deb", reserve(env, 3) + f * 4, USD( 0), EUR( 1), tesSUCCESS, tecUNFUNDED_OFFER},
-
3508 {"eve", reserve(env, 3) + f * 4, USD( 1), EUR( 0), tecUNFUNDED_OFFER, tesSUCCESS},
-
3509 {"flo", reserve(env, 3) + 0, USD(1000), EUR(1000), tecINSUF_RESERVE_OFFER, tecINSUF_RESERVE_OFFER},
-
3510 };
-
3511 //clang-format on
+
3471 // The fee that's charged for transactions.
+
3472 auto const f = env.current()->fees().base;
+
3473
+
3474 // Test cases
+
3475 struct TestData
+
3476 {
+
3477 std::string acct; // Account operated on
+
3478 STAmount fundXRP; // XRP acct funded with
+
3479 STAmount fundUSD; // USD acct funded with
+
3480 STAmount fundEUR; // EUR acct funded with
+
3481 TER firstOfferTec; // tec code on first offer
+
3482 TER secondOfferTec; // tec code on second offer
+
3483 };
+
3484
+
3485 // clang-format off
+
3486 TestData const tests[]{
+
3487 // acct fundXRP fundUSD fundEUR firstOfferTec secondOfferTec
+
3488 {"ann", reserve(env, 3) + f * 4, USD(1000), EUR(1000), tesSUCCESS, tesSUCCESS},
+
3489 {"bev", reserve(env, 3) + f * 4, USD( 1), EUR(1000), tesSUCCESS, tesSUCCESS},
+
3490 {"cam", reserve(env, 3) + f * 4, USD(1000), EUR( 1), tesSUCCESS, tesSUCCESS},
+
3491 {"deb", reserve(env, 3) + f * 4, USD( 0), EUR( 1), tesSUCCESS, tecUNFUNDED_OFFER},
+
3492 {"eve", reserve(env, 3) + f * 4, USD( 1), EUR( 0), tecUNFUNDED_OFFER, tesSUCCESS},
+
3493 {"flo", reserve(env, 3) + 0, USD(1000), EUR(1000), tecINSUF_RESERVE_OFFER, tecINSUF_RESERVE_OFFER},
+
3494 };
+
3495 //clang-format on
+
3496
+
3497 for (auto const& t : tests)
+
3498 {
+
3499 auto const acct = Account{t.acct};
+
3500 env.fund(t.fundXRP, acct);
+
3501 env.close();
+
3502
+
3503 env(trust(acct, USD(1000)));
+
3504 env(trust(acct, EUR(1000)));
+
3505 env.close();
+
3506
+
3507 if (t.fundUSD > USD(0))
+
3508 env(pay(gw1, acct, t.fundUSD));
+
3509 if (t.fundEUR > EUR(0))
+
3510 env(pay(gw2, acct, t.fundEUR));
+
3511 env.close();
3512
-
3513 for (auto const& t : tests)
-
3514 {
-
3515 auto const acct = Account{t.acct};
-
3516 env.fund(t.fundXRP, acct);
-
3517 env.close();
-
3518
-
3519 env(trust(acct, USD(1000)));
-
3520 env(trust(acct, EUR(1000)));
-
3521 env.close();
-
3522
-
3523 if (t.fundUSD > USD(0))
-
3524 env(pay(gw1, acct, t.fundUSD));
-
3525 if (t.fundEUR > EUR(0))
-
3526 env(pay(gw2, acct, t.fundEUR));
-
3527 env.close();
-
3528
-
3529 env(offer(acct, USD(500), EUR(600)), ter(t.firstOfferTec));
-
3530 env.close();
-
3531 std::uint32_t const firstOfferSeq = env.seq(acct) - 1;
-
3532
-
3533 int offerCount = t.firstOfferTec == tesSUCCESS ? 1 : 0;
-
3534 env.require(owners(acct, 2 + offerCount));
-
3535 env.require(balance(acct, t.fundUSD));
-
3536 env.require(balance(acct, t.fundEUR));
-
3537
-
3538 auto acctOffers = offersOnAccount(env, acct);
-
3539 BEAST_EXPECT(acctOffers.size() == offerCount);
-
3540 for (auto const& offerPtr : acctOffers)
-
3541 {
-
3542 auto const& offer = *offerPtr;
-
3543 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3544 BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
-
3545 BEAST_EXPECT(offer[sfTakerPays] == USD(500));
-
3546 }
-
3547
-
3548 env(offer(acct, EUR(600), USD(500)), ter(t.secondOfferTec));
-
3549 env.close();
-
3550 std::uint32_t const secondOfferSeq = env.seq(acct) - 1;
-
3551
-
3552 offerCount = t.secondOfferTec == tesSUCCESS ? 1 : offerCount;
-
3553 env.require(owners(acct, 2 + offerCount));
-
3554 env.require(balance(acct, t.fundUSD));
-
3555 env.require(balance(acct, t.fundEUR));
-
3556
-
3557 acctOffers = offersOnAccount(env, acct);
-
3558 BEAST_EXPECT(acctOffers.size() == offerCount);
-
3559 for (auto const& offerPtr : acctOffers)
-
3560 {
-
3561 auto const& offer = *offerPtr;
-
3562 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3563 if (offer[sfSequence] == firstOfferSeq)
-
3564 {
-
3565 BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
-
3566 BEAST_EXPECT(offer[sfTakerPays] == USD(500));
-
3567 }
-
3568 else
-
3569 {
-
3570 BEAST_EXPECT(offer[sfTakerGets] == USD(500));
-
3571 BEAST_EXPECT(offer[sfTakerPays] == EUR(600));
-
3572 }
-
3573 }
+
3513 env(offer(acct, USD(500), EUR(600)), ter(t.firstOfferTec));
+
3514 env.close();
+
3515 std::uint32_t const firstOfferSeq = env.seq(acct) - 1;
+
3516
+
3517 int offerCount = t.firstOfferTec == tesSUCCESS ? 1 : 0;
+
3518 env.require(owners(acct, 2 + offerCount));
+
3519 env.require(balance(acct, t.fundUSD));
+
3520 env.require(balance(acct, t.fundEUR));
+
3521
+
3522 auto acctOffers = offersOnAccount(env, acct);
+
3523 BEAST_EXPECT(acctOffers.size() == offerCount);
+
3524 for (auto const& offerPtr : acctOffers)
+
3525 {
+
3526 auto const& offer = *offerPtr;
+
3527 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3528 BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
+
3529 BEAST_EXPECT(offer[sfTakerPays] == USD(500));
+
3530 }
+
3531
+
3532 env(offer(acct, EUR(600), USD(500)), ter(t.secondOfferTec));
+
3533 env.close();
+
3534 std::uint32_t const secondOfferSeq = env.seq(acct) - 1;
+
3535
+
3536 offerCount = t.secondOfferTec == tesSUCCESS ? 1 : offerCount;
+
3537 env.require(owners(acct, 2 + offerCount));
+
3538 env.require(balance(acct, t.fundUSD));
+
3539 env.require(balance(acct, t.fundEUR));
+
3540
+
3541 acctOffers = offersOnAccount(env, acct);
+
3542 BEAST_EXPECT(acctOffers.size() == offerCount);
+
3543 for (auto const& offerPtr : acctOffers)
+
3544 {
+
3545 auto const& offer = *offerPtr;
+
3546 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3547 if (offer[sfSequence] == firstOfferSeq)
+
3548 {
+
3549 BEAST_EXPECT(offer[sfTakerGets] == EUR(600));
+
3550 BEAST_EXPECT(offer[sfTakerPays] == USD(500));
+
3551 }
+
3552 else
+
3553 {
+
3554 BEAST_EXPECT(offer[sfTakerGets] == USD(500));
+
3555 BEAST_EXPECT(offer[sfTakerPays] == EUR(600));
+
3556 }
+
3557 }
+
3558
+
3559 // Remove any offers from acct for the next pass.
+
3560 env(offer_cancel(acct, firstOfferSeq));
+
3561 env.close();
+
3562 env(offer_cancel(acct, secondOfferSeq));
+
3563 env.close();
+
3564 }
+
3565 }
+
3566
+
3567 void
+
3568 testSelfCrossOffer(FeatureBitset features)
+
3569 {
+
3570 testcase("Self Cross Offer");
+
3571 testSelfCrossOffer1(features);
+
3572 testSelfCrossOffer2(features);
+
3573 }
3574
-
3575 // Remove any offers from acct for the next pass.
-
3576 env(offer_cancel(acct, firstOfferSeq));
-
3577 env.close();
-
3578 env(offer_cancel(acct, secondOfferSeq));
-
3579 env.close();
-
3580 }
-
3581 }
-
3582
-
3583 void
-
3584 testSelfCrossOffer(FeatureBitset features)
-
3585 {
-
3586 testcase("Self Cross Offer");
-
3587 testSelfCrossOffer1(features);
-
3588 testSelfCrossOffer2(features);
-
3589 }
+
3575 void
+
3576 testSelfIssueOffer(FeatureBitset features)
+
3577 {
+
3578 // Folks who issue their own currency have, in effect, as many
+
3579 // funds as they are trusted for. This test used to fail because
+
3580 // self-issuing was not properly checked. Verify that it works
+
3581 // correctly now.
+
3582 using namespace jtx;
+
3583
+
3584 Env env{*this, features};
+
3585
+
3586 auto const alice = Account("alice");
+
3587 auto const bob = Account("bob");
+
3588 auto const USD = bob["USD"];
+
3589 auto const f = env.current()->fees().base;
3590
-
3591 void
-
3592 testSelfIssueOffer(FeatureBitset features)
-
3593 {
-
3594 // Folks who issue their own currency have, in effect, as many
-
3595 // funds as they are trusted for. This test used to fail because
-
3596 // self-issuing was not properly checked. Verify that it works
-
3597 // correctly now.
-
3598 using namespace jtx;
-
3599
-
3600 Env env{*this, features};
-
3601
-
3602 auto const alice = Account("alice");
-
3603 auto const bob = Account("bob");
-
3604 auto const USD = bob["USD"];
-
3605 auto const f = env.current()->fees().base;
+
3591 env.fund(XRP(50000) + f, alice, bob);
+
3592 env.close();
+
3593
+
3594 env(offer(alice, USD(5000), XRP(50000)));
+
3595 env.close();
+
3596
+
3597 // This offer should take alice's offer up to Alice's reserve.
+
3598 env(offer(bob, XRP(50000), USD(5000)));
+
3599 env.close();
+
3600
+
3601 // alice's offer should have been removed, since she's down to her
+
3602 // XRP reserve.
+
3603 env.require(balance(alice, XRP(250)));
+
3604 env.require(owners(alice, 1));
+
3605 env.require(lines(alice, 1));
3606
-
3607 env.fund(XRP(50000) + f, alice, bob);
-
3608 env.close();
-
3609
-
3610 env(offer(alice, USD(5000), XRP(50000)));
-
3611 env.close();
-
3612
-
3613 // This offer should take alice's offer up to Alice's reserve.
-
3614 env(offer(bob, XRP(50000), USD(5000)));
-
3615 env.close();
-
3616
-
3617 // alice's offer should have been removed, since she's down to her
-
3618 // XRP reserve.
-
3619 env.require(balance(alice, XRP(250)));
-
3620 env.require(owners(alice, 1));
-
3621 env.require(lines(alice, 1));
-
3622
-
3623 // However bob's offer should be in the ledger, since it was not
-
3624 // fully crossed.
-
3625 auto const bobOffers = offersOnAccount(env, bob);
-
3626 BEAST_EXPECT(bobOffers.size() == 1);
-
3627 for (auto const& offerPtr : bobOffers)
-
3628 {
-
3629 auto const& offer = *offerPtr;
-
3630 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3631 BEAST_EXPECT(offer[sfTakerGets] == USD(25));
-
3632 BEAST_EXPECT(offer[sfTakerPays] == XRP(250));
-
3633 }
-
3634 }
-
3635
-
3636 void
-
3637 testBadPathAssert(FeatureBitset features)
-
3638 {
-
3639 // At one point in the past this invalid path caused an assert. It
-
3640 // should not be possible for user-supplied data to cause an assert.
-
3641 // Make sure the assert is gone.
-
3642 testcase("Bad path assert");
-
3643
-
3644 using namespace jtx;
-
3645
-
3646 Env env{*this, features};
-
3647
-
3648 // The fee that's charged for transactions.
-
3649 auto const fee = env.current()->fees().base;
-
3650 {
-
3651 // A trust line's QualityOut should not affect offer crossing.
-
3652 auto const ann = Account("ann");
-
3653 auto const A_BUX = ann["BUX"];
-
3654 auto const bob = Account("bob");
-
3655 auto const cam = Account("cam");
-
3656 auto const dan = Account("dan");
-
3657 auto const D_BUX = dan["BUX"];
-
3658
-
3659 // Verify trust line QualityOut affects payments.
-
3660 env.fund(reserve(env, 4) + (fee * 4), ann, bob, cam, dan);
-
3661 env.close();
-
3662
-
3663 env(trust(bob, A_BUX(400)));
-
3664 env(trust(bob, D_BUX(200)), qualityOutPercent(120));
-
3665 env(trust(cam, D_BUX(100)));
-
3666 env.close();
-
3667 env(pay(dan, bob, D_BUX(100)));
+
3607 // However bob's offer should be in the ledger, since it was not
+
3608 // fully crossed.
+
3609 auto const bobOffers = offersOnAccount(env, bob);
+
3610 BEAST_EXPECT(bobOffers.size() == 1);
+
3611 for (auto const& offerPtr : bobOffers)
+
3612 {
+
3613 auto const& offer = *offerPtr;
+
3614 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3615 BEAST_EXPECT(offer[sfTakerGets] == USD(25));
+
3616 BEAST_EXPECT(offer[sfTakerPays] == XRP(250));
+
3617 }
+
3618 }
+
3619
+
3620 void
+
3621 testBadPathAssert(FeatureBitset features)
+
3622 {
+
3623 // At one point in the past this invalid path caused an assert. It
+
3624 // should not be possible for user-supplied data to cause an assert.
+
3625 // Make sure the assert is gone.
+
3626 testcase("Bad path assert");
+
3627
+
3628 using namespace jtx;
+
3629
+
3630 Env env{*this, features};
+
3631
+
3632 // The fee that's charged for transactions.
+
3633 auto const fee = env.current()->fees().base;
+
3634 {
+
3635 // A trust line's QualityOut should not affect offer crossing.
+
3636 auto const ann = Account("ann");
+
3637 auto const A_BUX = ann["BUX"];
+
3638 auto const bob = Account("bob");
+
3639 auto const cam = Account("cam");
+
3640 auto const dan = Account("dan");
+
3641 auto const D_BUX = dan["BUX"];
+
3642
+
3643 // Verify trust line QualityOut affects payments.
+
3644 env.fund(reserve(env, 4) + (fee * 4), ann, bob, cam, dan);
+
3645 env.close();
+
3646
+
3647 env(trust(bob, A_BUX(400)));
+
3648 env(trust(bob, D_BUX(200)), qualityOutPercent(120));
+
3649 env(trust(cam, D_BUX(100)));
+
3650 env.close();
+
3651 env(pay(dan, bob, D_BUX(100)));
+
3652 env.close();
+
3653 env.require(balance(bob, D_BUX(100)));
+
3654
+
3655 env(pay(ann, cam, D_BUX(60)), path(bob, dan), sendmax(A_BUX(200)));
+
3656 env.close();
+
3657
+
3658 env.require(balance(ann, A_BUX(none)));
+
3659 env.require(balance(ann, D_BUX(none)));
+
3660 env.require(balance(bob, A_BUX(72)));
+
3661 env.require(balance(bob, D_BUX(40)));
+
3662 env.require(balance(cam, A_BUX(none)));
+
3663 env.require(balance(cam, D_BUX(60)));
+
3664 env.require(balance(dan, A_BUX(none)));
+
3665 env.require(balance(dan, D_BUX(none)));
+
3666
+
3667 env(offer(bob, A_BUX(30), D_BUX(30)));
3668 env.close();
-
3669 env.require(balance(bob, D_BUX(100)));
-
3670
-
3671 env(pay(ann, cam, D_BUX(60)), path(bob, dan), sendmax(A_BUX(200)));
-
3672 env.close();
-
3673
-
3674 env.require(balance(ann, A_BUX(none)));
-
3675 env.require(balance(ann, D_BUX(none)));
-
3676 env.require(balance(bob, A_BUX(72)));
-
3677 env.require(balance(bob, D_BUX(40)));
-
3678 env.require(balance(cam, A_BUX(none)));
-
3679 env.require(balance(cam, D_BUX(60)));
-
3680 env.require(balance(dan, A_BUX(none)));
-
3681 env.require(balance(dan, D_BUX(none)));
-
3682
-
3683 env(offer(bob, A_BUX(30), D_BUX(30)));
-
3684 env.close();
-
3685
-
3686 env(trust(ann, D_BUX(100)));
-
3687 env.close();
-
3688
-
3689 // This payment caused the assert.
-
3690 env(pay(ann, ann, D_BUX(30)),
-
3691 path(A_BUX, D_BUX),
-
3692 sendmax(A_BUX(30)),
-
3693 ter(temBAD_PATH));
-
3694 env.close();
-
3695
-
3696 env.require(balance(ann, A_BUX(none)));
-
3697 env.require(balance(ann, D_BUX(0)));
-
3698 env.require(balance(bob, A_BUX(72)));
-
3699 env.require(balance(bob, D_BUX(40)));
-
3700 env.require(balance(cam, A_BUX(none)));
-
3701 env.require(balance(cam, D_BUX(60)));
-
3702 env.require(balance(dan, A_BUX(0)));
-
3703 env.require(balance(dan, D_BUX(none)));
-
3704 }
-
3705 }
-
3706
-
3707 void
-
3708 testDirectToDirectPath(FeatureBitset features)
-
3709 {
-
3710 // The offer crossing code expects that a DirectStep is always
-
3711 // preceded by a BookStep. In one instance the default path
-
3712 // was not matching that assumption. Here we recreate that case
-
3713 // so we can prove the bug stays fixed.
-
3714 testcase("Direct to Direct path");
-
3715
-
3716 using namespace jtx;
-
3717
-
3718 Env env{*this, features};
-
3719
-
3720 auto const ann = Account("ann");
-
3721 auto const bob = Account("bob");
-
3722 auto const cam = Account("cam");
-
3723 auto const A_BUX = ann["BUX"];
-
3724 auto const B_BUX = bob["BUX"];
-
3725
-
3726 auto const fee = env.current()->fees().base;
-
3727 env.fund(reserve(env, 4) + (fee * 5), ann, bob, cam);
-
3728 env.close();
-
3729
-
3730 env(trust(ann, B_BUX(40)));
-
3731 env(trust(cam, A_BUX(40)));
-
3732 env(trust(cam, B_BUX(40)));
-
3733 env.close();
-
3734
-
3735 env(pay(ann, cam, A_BUX(35)));
-
3736 env(pay(bob, cam, B_BUX(35)));
+
3669
+
3670 env(trust(ann, D_BUX(100)));
+
3671 env.close();
+
3672
+
3673 // This payment caused the assert.
+
3674 env(pay(ann, ann, D_BUX(30)),
+
3675 path(A_BUX, D_BUX),
+
3676 sendmax(A_BUX(30)),
+
3677 ter(temBAD_PATH));
+
3678 env.close();
+
3679
+
3680 env.require(balance(ann, A_BUX(none)));
+
3681 env.require(balance(ann, D_BUX(0)));
+
3682 env.require(balance(bob, A_BUX(72)));
+
3683 env.require(balance(bob, D_BUX(40)));
+
3684 env.require(balance(cam, A_BUX(none)));
+
3685 env.require(balance(cam, D_BUX(60)));
+
3686 env.require(balance(dan, A_BUX(0)));
+
3687 env.require(balance(dan, D_BUX(none)));
+
3688 }
+
3689 }
+
3690
+
3691 void
+
3692 testDirectToDirectPath(FeatureBitset features)
+
3693 {
+
3694 // The offer crossing code expects that a DirectStep is always
+
3695 // preceded by a BookStep. In one instance the default path
+
3696 // was not matching that assumption. Here we recreate that case
+
3697 // so we can prove the bug stays fixed.
+
3698 testcase("Direct to Direct path");
+
3699
+
3700 using namespace jtx;
+
3701
+
3702 Env env{*this, features};
+
3703
+
3704 auto const ann = Account("ann");
+
3705 auto const bob = Account("bob");
+
3706 auto const cam = Account("cam");
+
3707 auto const A_BUX = ann["BUX"];
+
3708 auto const B_BUX = bob["BUX"];
+
3709
+
3710 auto const fee = env.current()->fees().base;
+
3711 env.fund(reserve(env, 4) + (fee * 5), ann, bob, cam);
+
3712 env.close();
+
3713
+
3714 env(trust(ann, B_BUX(40)));
+
3715 env(trust(cam, A_BUX(40)));
+
3716 env(trust(cam, B_BUX(40)));
+
3717 env.close();
+
3718
+
3719 env(pay(ann, cam, A_BUX(35)));
+
3720 env(pay(bob, cam, B_BUX(35)));
+
3721
+
3722 env(offer(bob, A_BUX(30), B_BUX(30)));
+
3723 env.close();
+
3724
+
3725 // cam puts an offer on the books that her upcoming offer could cross.
+
3726 // But this offer should be deleted, not crossed, by her upcoming
+
3727 // offer.
+
3728 env(offer(cam, A_BUX(29), B_BUX(30), tfPassive));
+
3729 env.close();
+
3730 env.require(balance(cam, A_BUX(35)));
+
3731 env.require(balance(cam, B_BUX(35)));
+
3732 env.require(offers(cam, 1));
+
3733
+
3734 // This offer caused the assert.
+
3735 env(offer(cam, B_BUX(30), A_BUX(30)));
+
3736 env.close();
3737
-
3738 env(offer(bob, A_BUX(30), B_BUX(30)));
-
3739 env.close();
-
3740
-
3741 // cam puts an offer on the books that her upcoming offer could cross.
-
3742 // But this offer should be deleted, not crossed, by her upcoming
-
3743 // offer.
-
3744 env(offer(cam, A_BUX(29), B_BUX(30), tfPassive));
-
3745 env.close();
-
3746 env.require(balance(cam, A_BUX(35)));
-
3747 env.require(balance(cam, B_BUX(35)));
-
3748 env.require(offers(cam, 1));
-
3749
-
3750 // This offer caused the assert.
-
3751 env(offer(cam, B_BUX(30), A_BUX(30)));
-
3752 env.close();
+
3738 env.require(balance(bob, A_BUX(30)));
+
3739 env.require(balance(cam, A_BUX(5)));
+
3740 env.require(balance(cam, B_BUX(65)));
+
3741 env.require(offers(cam, 0));
+
3742 }
+
3743
+
3744 void
+
3745 testSelfCrossLowQualityOffer(FeatureBitset features)
+
3746 {
+
3747 // The Flow offer crossing code used to assert if an offer was made
+
3748 // for more XRP than the offering account held. This unit test
+
3749 // reproduces that failing case.
+
3750 testcase("Self crossing low quality offer");
+
3751
+
3752 using namespace jtx;
3753
-
3754 env.require(balance(bob, A_BUX(30)));
-
3755 env.require(balance(cam, A_BUX(5)));
-
3756 env.require(balance(cam, B_BUX(65)));
-
3757 env.require(offers(cam, 0));
-
3758 }
+
3754 Env env{*this, features};
+
3755
+
3756 auto const ann = Account("ann");
+
3757 auto const gw = Account("gateway");
+
3758 auto const BTC = gw["BTC"];
3759
-
3760 void
-
3761 testSelfCrossLowQualityOffer(FeatureBitset features)
-
3762 {
-
3763 // The Flow offer crossing code used to assert if an offer was made
-
3764 // for more XRP than the offering account held. This unit test
-
3765 // reproduces that failing case.
-
3766 testcase("Self crossing low quality offer");
-
3767
-
3768 using namespace jtx;
-
3769
-
3770 Env env{*this, features};
+
3760 auto const fee = env.current()->fees().base;
+
3761 env.fund(reserve(env, 2) + drops(9999640) + (fee), ann);
+
3762 env.fund(reserve(env, 2) + (fee * 4), gw);
+
3763 env.close();
+
3764
+
3765 env(rate(gw, 1.002));
+
3766 env(trust(ann, BTC(10)));
+
3767 env.close();
+
3768
+
3769 env(pay(gw, ann, BTC(2.856)));
+
3770 env.close();
3771
-
3772 auto const ann = Account("ann");
-
3773 auto const gw = Account("gateway");
-
3774 auto const BTC = gw["BTC"];
-
3775
-
3776 auto const fee = env.current()->fees().base;
-
3777 env.fund(reserve(env, 2) + drops(9999640) + (fee), ann);
-
3778 env.fund(reserve(env, 2) + (fee * 4), gw);
-
3779 env.close();
-
3780
-
3781 env(rate(gw, 1.002));
-
3782 env(trust(ann, BTC(10)));
-
3783 env.close();
-
3784
-
3785 env(pay(gw, ann, BTC(2.856)));
-
3786 env.close();
-
3787
-
3788 env(offer(ann, drops(365611702030), BTC(5.713)));
-
3789 env.close();
+
3772 env(offer(ann, drops(365611702030), BTC(5.713)));
+
3773 env.close();
+
3774
+
3775 // This offer caused the assert.
+
3776 env(offer(ann, BTC(0.687), drops(20000000000)),
+
3777 ter(tecINSUF_RESERVE_OFFER));
+
3778 }
+
3779
+
3780 void
+
3781 testOfferInScaling(FeatureBitset features)
+
3782 {
+
3783 // The Flow offer crossing code had a case where it was not rounding
+
3784 // the offer crossing correctly after a partial crossing. The
+
3785 // failing case was found on the network. Here we add the case to
+
3786 // the unit tests.
+
3787 testcase("Offer In Scaling");
+
3788
+
3789 using namespace jtx;
3790
-
3791 // This offer caused the assert.
-
3792 env(offer(ann, BTC(0.687), drops(20000000000)),
-
3793 ter(tecINSUF_RESERVE_OFFER));
-
3794 }
-
3795
-
3796 void
-
3797 testOfferInScaling(FeatureBitset features)
-
3798 {
-
3799 // The Flow offer crossing code had a case where it was not rounding
-
3800 // the offer crossing correctly after a partial crossing. The
-
3801 // failing case was found on the network. Here we add the case to
-
3802 // the unit tests.
-
3803 testcase("Offer In Scaling");
-
3804
-
3805 using namespace jtx;
-
3806
-
3807 Env env{*this, features};
+
3791 Env env{*this, features};
+
3792
+
3793 auto const gw = Account("gateway");
+
3794 auto const alice = Account("alice");
+
3795 auto const bob = Account("bob");
+
3796 auto const CNY = gw["CNY"];
+
3797
+
3798 auto const fee = env.current()->fees().base;
+
3799 env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
+
3800 env.fund(reserve(env, 2) + (fee * 4), gw);
+
3801 env.close();
+
3802
+
3803 env(trust(bob, CNY(500)));
+
3804 env.close();
+
3805
+
3806 env(pay(gw, bob, CNY(300)));
+
3807 env.close();
3808
-
3809 auto const gw = Account("gateway");
-
3810 auto const alice = Account("alice");
-
3811 auto const bob = Account("bob");
-
3812 auto const CNY = gw["CNY"];
-
3813
-
3814 auto const fee = env.current()->fees().base;
-
3815 env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
-
3816 env.fund(reserve(env, 2) + (fee * 4), gw);
-
3817 env.close();
-
3818
-
3819 env(trust(bob, CNY(500)));
-
3820 env.close();
-
3821
-
3822 env(pay(gw, bob, CNY(300)));
-
3823 env.close();
-
3824
-
3825 env(offer(bob, drops(5400000000), CNY(216.054)));
-
3826 env.close();
-
3827
-
3828 // This offer did not round result of partial crossing correctly.
-
3829 env(offer(alice, CNY(13562.0001), drops(339000000000)));
-
3830 env.close();
-
3831
-
3832 auto const aliceOffers = offersOnAccount(env, alice);
-
3833 BEAST_EXPECT(aliceOffers.size() == 1);
-
3834 for (auto const& offerPtr : aliceOffers)
-
3835 {
-
3836 auto const& offer = *offerPtr;
-
3837 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3838 BEAST_EXPECT(offer[sfTakerGets] == drops(333599446582));
-
3839 BEAST_EXPECT(offer[sfTakerPays] == CNY(13345.9461));
-
3840 }
-
3841 }
-
3842
-
3843 void
-
3844 testOfferInScalingWithXferRate(FeatureBitset features)
-
3845 {
-
3846 // After adding the previous case, there were still failing rounding
-
3847 // cases in Flow offer crossing. This one was because the gateway
-
3848 // transfer rate was not being correctly handled.
-
3849 testcase("Offer In Scaling With Xfer Rate");
-
3850
-
3851 using namespace jtx;
-
3852
-
3853 Env env{*this, features};
+
3809 env(offer(bob, drops(5400000000), CNY(216.054)));
+
3810 env.close();
+
3811
+
3812 // This offer did not round result of partial crossing correctly.
+
3813 env(offer(alice, CNY(13562.0001), drops(339000000000)));
+
3814 env.close();
+
3815
+
3816 auto const aliceOffers = offersOnAccount(env, alice);
+
3817 BEAST_EXPECT(aliceOffers.size() == 1);
+
3818 for (auto const& offerPtr : aliceOffers)
+
3819 {
+
3820 auto const& offer = *offerPtr;
+
3821 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3822 BEAST_EXPECT(offer[sfTakerGets] == drops(333599446582));
+
3823 BEAST_EXPECT(offer[sfTakerPays] == CNY(13345.9461));
+
3824 }
+
3825 }
+
3826
+
3827 void
+
3828 testOfferInScalingWithXferRate(FeatureBitset features)
+
3829 {
+
3830 // After adding the previous case, there were still failing rounding
+
3831 // cases in Flow offer crossing. This one was because the gateway
+
3832 // transfer rate was not being correctly handled.
+
3833 testcase("Offer In Scaling With Xfer Rate");
+
3834
+
3835 using namespace jtx;
+
3836
+
3837 Env env{*this, features};
+
3838
+
3839 auto const gw = Account("gateway");
+
3840 auto const alice = Account("alice");
+
3841 auto const bob = Account("bob");
+
3842 auto const BTC = gw["BTC"];
+
3843 auto const JPY = gw["JPY"];
+
3844
+
3845 auto const fee = env.current()->fees().base;
+
3846 env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
+
3847 env.fund(reserve(env, 2) + (fee * 4), gw);
+
3848 env.close();
+
3849
+
3850 env(rate(gw, 1.002));
+
3851 env(trust(alice, JPY(4000)));
+
3852 env(trust(bob, BTC(2)));
+
3853 env.close();
3854
-
3855 auto const gw = Account("gateway");
-
3856 auto const alice = Account("alice");
-
3857 auto const bob = Account("bob");
-
3858 auto const BTC = gw["BTC"];
-
3859 auto const JPY = gw["JPY"];
-
3860
-
3861 auto const fee = env.current()->fees().base;
-
3862 env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
-
3863 env.fund(reserve(env, 2) + (fee * 4), gw);
+
3855 env(pay(gw, alice, JPY(3699.034802280317)));
+
3856 env(pay(gw, bob, BTC(1.156722559140311)));
+
3857 env.close();
+
3858
+
3859 env(offer(bob, JPY(1241.913390770747), BTC(0.01969825690469254)));
+
3860 env.close();
+
3861
+
3862 // This offer did not round result of partial crossing correctly.
+
3863 env(offer(alice, BTC(0.05507568706427876), JPY(3472.696773391072)));
3864 env.close();
3865
-
3866 env(rate(gw, 1.002));
-
3867 env(trust(alice, JPY(4000)));
-
3868 env(trust(bob, BTC(2)));
-
3869 env.close();
-
3870
-
3871 env(pay(gw, alice, JPY(3699.034802280317)));
-
3872 env(pay(gw, bob, BTC(1.156722559140311)));
-
3873 env.close();
-
3874
-
3875 env(offer(bob, JPY(1241.913390770747), BTC(0.01969825690469254)));
-
3876 env.close();
-
3877
-
3878 // This offer did not round result of partial crossing correctly.
-
3879 env(offer(alice, BTC(0.05507568706427876), JPY(3472.696773391072)));
-
3880 env.close();
-
3881
-
3882 auto const aliceOffers = offersOnAccount(env, alice);
-
3883 BEAST_EXPECT(aliceOffers.size() == 1);
-
3884 for (auto const& offerPtr : aliceOffers)
-
3885 {
-
3886 auto const& offer = *offerPtr;
-
3887 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3888 BEAST_EXPECT(
-
3889 offer[sfTakerGets] ==
-
3890 STAmount(JPY.issue(), std::uint64_t(2230682446713524ul), -12));
-
3891 BEAST_EXPECT(offer[sfTakerPays] == BTC(0.035378));
-
3892 }
-
3893 }
-
3894
-
3895 void
-
3896 testOfferThresholdWithReducedFunds(FeatureBitset features)
-
3897 {
-
3898 // Another instance where Flow offer crossing was not always
-
3899 // working right was if the Taker had fewer funds than the Offer
-
3900 // was offering. The basis for this test came off the network.
-
3901 testcase("Offer Threshold With Reduced Funds");
+
3866 auto const aliceOffers = offersOnAccount(env, alice);
+
3867 BEAST_EXPECT(aliceOffers.size() == 1);
+
3868 for (auto const& offerPtr : aliceOffers)
+
3869 {
+
3870 auto const& offer = *offerPtr;
+
3871 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3872 BEAST_EXPECT(
+
3873 offer[sfTakerGets] ==
+
3874 STAmount(JPY.issue(), std::uint64_t(2230682446713524ul), -12));
+
3875 BEAST_EXPECT(offer[sfTakerPays] == BTC(0.035378));
+
3876 }
+
3877 }
+
3878
+
3879 void
+
3880 testOfferThresholdWithReducedFunds(FeatureBitset features)
+
3881 {
+
3882 // Another instance where Flow offer crossing was not always
+
3883 // working right was if the Taker had fewer funds than the Offer
+
3884 // was offering. The basis for this test came off the network.
+
3885 testcase("Offer Threshold With Reduced Funds");
+
3886
+
3887 using namespace jtx;
+
3888
+
3889 Env env{*this, features};
+
3890
+
3891 auto const gw1 = Account("gw1");
+
3892 auto const gw2 = Account("gw2");
+
3893 auto const alice = Account("alice");
+
3894 auto const bob = Account("bob");
+
3895 auto const USD = gw1["USD"];
+
3896 auto const JPY = gw2["JPY"];
+
3897
+
3898 auto const fee = env.current()->fees().base;
+
3899 env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
+
3900 env.fund(reserve(env, 2) + (fee * 4), gw1, gw2);
+
3901 env.close();
3902
-
3903 using namespace jtx;
-
3904
-
3905 Env env{*this, features};
-
3906
-
3907 auto const gw1 = Account("gw1");
-
3908 auto const gw2 = Account("gw2");
-
3909 auto const alice = Account("alice");
-
3910 auto const bob = Account("bob");
-
3911 auto const USD = gw1["USD"];
-
3912 auto const JPY = gw2["JPY"];
-
3913
-
3914 auto const fee = env.current()->fees().base;
-
3915 env.fund(reserve(env, 2) + drops(400000000000) + (fee), alice, bob);
-
3916 env.fund(reserve(env, 2) + (fee * 4), gw1, gw2);
-
3917 env.close();
-
3918
-
3919 env(rate(gw1, 1.002));
-
3920 env(trust(alice, USD(1000)));
-
3921 env(trust(bob, JPY(100000)));
+
3903 env(rate(gw1, 1.002));
+
3904 env(trust(alice, USD(1000)));
+
3905 env(trust(bob, JPY(100000)));
+
3906 env.close();
+
3907
+
3908 env(
+
3909 pay(gw1,
+
3910 alice,
+
3911 STAmount{USD.issue(), std::uint64_t(2185410179555600), -14}));
+
3912 env(
+
3913 pay(gw2,
+
3914 bob,
+
3915 STAmount{JPY.issue(), std::uint64_t(6351823459548956), -12}));
+
3916 env.close();
+
3917
+
3918 env(offer(
+
3919 bob,
+
3920 STAmount{USD.issue(), std::uint64_t(4371257532306000), -17},
+
3921 STAmount{JPY.issue(), std::uint64_t(4573216636606000), -15}));
3922 env.close();
3923
-
3924 env(
-
3925 pay(gw1,
-
3926 alice,
-
3927 STAmount{USD.issue(), std::uint64_t(2185410179555600), -14}));
-
3928 env(
-
3929 pay(gw2,
-
3930 bob,
-
3931 STAmount{JPY.issue(), std::uint64_t(6351823459548956), -12}));
-
3932 env.close();
-
3933
-
3934 env(offer(
-
3935 bob,
-
3936 STAmount{USD.issue(), std::uint64_t(4371257532306000), -17},
-
3937 STAmount{JPY.issue(), std::uint64_t(4573216636606000), -15}));
-
3938 env.close();
-
3939
-
3940 // This offer did not partially cross correctly.
-
3941 env(offer(
-
3942 alice,
-
3943 STAmount{JPY.issue(), std::uint64_t(2291181510070762), -12},
-
3944 STAmount{USD.issue(), std::uint64_t(2190218999914694), -14}));
-
3945 env.close();
-
3946
-
3947 auto const aliceOffers = offersOnAccount(env, alice);
-
3948 BEAST_EXPECT(aliceOffers.size() == 1);
-
3949 for (auto const& offerPtr : aliceOffers)
-
3950 {
-
3951 auto const& offer = *offerPtr;
-
3952 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
-
3953 BEAST_EXPECT(
-
3954 offer[sfTakerGets] ==
-
3955 STAmount(USD.issue(), std::uint64_t(2185847305256635), -14));
-
3956 BEAST_EXPECT(
-
3957 offer[sfTakerPays] ==
-
3958 STAmount(JPY.issue(), std::uint64_t(2286608293434156), -12));
-
3959 }
-
3960 }
+
3924 // This offer did not partially cross correctly.
+
3925 env(offer(
+
3926 alice,
+
3927 STAmount{JPY.issue(), std::uint64_t(2291181510070762), -12},
+
3928 STAmount{USD.issue(), std::uint64_t(2190218999914694), -14}));
+
3929 env.close();
+
3930
+
3931 auto const aliceOffers = offersOnAccount(env, alice);
+
3932 BEAST_EXPECT(aliceOffers.size() == 1);
+
3933 for (auto const& offerPtr : aliceOffers)
+
3934 {
+
3935 auto const& offer = *offerPtr;
+
3936 BEAST_EXPECT(offer[sfLedgerEntryType] == ltOFFER);
+
3937 BEAST_EXPECT(
+
3938 offer[sfTakerGets] ==
+
3939 STAmount(USD.issue(), std::uint64_t(2185847305256635), -14));
+
3940 BEAST_EXPECT(
+
3941 offer[sfTakerPays] ==
+
3942 STAmount(JPY.issue(), std::uint64_t(2286608293434156), -12));
+
3943 }
+
3944 }
+
3945
+
3946 void
+
3947 testTinyOffer(FeatureBitset features)
+
3948 {
+
3949 testcase("Tiny Offer");
+
3950
+
3951 using namespace jtx;
+
3952
+
3953 Env env{*this, features};
+
3954
+
3955 auto const gw = Account("gw");
+
3956 auto const alice = Account("alice");
+
3957 auto const bob = Account("bob");
+
3958 auto const CNY = gw["CNY"];
+
3959 auto const fee = env.current()->fees().base;
+
3960 auto const startXrpBalance = drops(400000000000) + (fee * 2);
3961
-
3962 void
-
3963 testTinyOffer(FeatureBitset features)
-
3964 {
-
3965 testcase("Tiny Offer");
-
3966
-
3967 using namespace jtx;
-
3968
-
3969 Env env{*this, features};
-
3970
-
3971 auto const gw = Account("gw");
-
3972 auto const alice = Account("alice");
-
3973 auto const bob = Account("bob");
-
3974 auto const CNY = gw["CNY"];
-
3975 auto const fee = env.current()->fees().base;
-
3976 auto const startXrpBalance = drops(400000000000) + (fee * 2);
-
3977
-
3978 env.fund(startXrpBalance, gw, alice, bob);
-
3979 env.close();
-
3980
-
3981 env(trust(bob, CNY(100000)));
-
3982 env.close();
-
3983
-
3984 // Place alice's tiny offer in the book first. Let's see what happens
-
3985 // when a reasonable offer crosses it.
-
3986 STAmount const alicesCnyOffer{
-
3987 CNY.issue(), std::uint64_t(4926000000000000), -23};
-
3988
-
3989 env(offer(alice, alicesCnyOffer, drops(1), tfPassive));
-
3990 env.close();
-
3991
-
3992 // bob places an ordinary offer
-
3993 STAmount const bobsCnyStartBalance{
-
3994 CNY.issue(), std::uint64_t(3767479960090235), -15};
-
3995 env(pay(gw, bob, bobsCnyStartBalance));
-
3996 env.close();
-
3997
-
3998 env(offer(
-
3999 bob,
-
4000 drops(203),
-
4001 STAmount{CNY.issue(), std::uint64_t(1000000000000000), -20}));
-
4002 env.close();
-
4003
-
4004 env.require(balance(alice, alicesCnyOffer));
-
4005 env.require(balance(alice, startXrpBalance - fee - drops(1)));
-
4006 env.require(balance(bob, bobsCnyStartBalance - alicesCnyOffer));
-
4007 env.require(balance(bob, startXrpBalance - (fee * 2) + drops(1)));
-
4008 }
-
4009
-
4010 void
-
4011 testSelfPayXferFeeOffer(FeatureBitset features)
-
4012 {
-
4013 testcase("Self Pay Xfer Fee");
-
4014 // The old offer crossing code does not charge a transfer fee
-
4015 // if alice pays alice. That's different from how payments work.
-
4016 // Payments always charge a transfer fee even if the money is staying
-
4017 // in the same hands.
+
3962 env.fund(startXrpBalance, gw, alice, bob);
+
3963 env.close();
+
3964
+
3965 env(trust(bob, CNY(100000)));
+
3966 env.close();
+
3967
+
3968 // Place alice's tiny offer in the book first. Let's see what happens
+
3969 // when a reasonable offer crosses it.
+
3970 STAmount const alicesCnyOffer{
+
3971 CNY.issue(), std::uint64_t(4926000000000000), -23};
+
3972
+
3973 env(offer(alice, alicesCnyOffer, drops(1), tfPassive));
+
3974 env.close();
+
3975
+
3976 // bob places an ordinary offer
+
3977 STAmount const bobsCnyStartBalance{
+
3978 CNY.issue(), std::uint64_t(3767479960090235), -15};
+
3979 env(pay(gw, bob, bobsCnyStartBalance));
+
3980 env.close();
+
3981
+
3982 env(offer(
+
3983 bob,
+
3984 drops(203),
+
3985 STAmount{CNY.issue(), std::uint64_t(1000000000000000), -20}));
+
3986 env.close();
+
3987
+
3988 env.require(balance(alice, alicesCnyOffer));
+
3989 env.require(balance(alice, startXrpBalance - fee - drops(1)));
+
3990 env.require(balance(bob, bobsCnyStartBalance - alicesCnyOffer));
+
3991 env.require(balance(bob, startXrpBalance - (fee * 2) + drops(1)));
+
3992 }
+
3993
+
3994 void
+
3995 testSelfPayXferFeeOffer(FeatureBitset features)
+
3996 {
+
3997 testcase("Self Pay Xfer Fee");
+
3998 // The old offer crossing code does not charge a transfer fee
+
3999 // if alice pays alice. That's different from how payments work.
+
4000 // Payments always charge a transfer fee even if the money is staying
+
4001 // in the same hands.
+
4002 //
+
4003 // What's an example where alice pays alice? There are three actors:
+
4004 // gw, alice, and bob.
+
4005 //
+
4006 // 1. gw issues BTC and USD. qw charges a 0.2% transfer fee.
+
4007 //
+
4008 // 2. alice makes an offer to buy XRP and sell USD.
+
4009 // 3. bob makes an offer to buy BTC and sell XRP.
+
4010 //
+
4011 // 4. alice now makes an offer to sell BTC and buy USD.
+
4012 //
+
4013 // This last offer crosses using auto-bridging.
+
4014 // o alice's last offer sells BTC to...
+
4015 // o bob' offer which takes alice's BTC and sells XRP to...
+
4016 // o alice's first offer which takes bob's XRP and sells USD to...
+
4017 // o alice's last offer.
4018 //
-
4019 // What's an example where alice pays alice? There are three actors:
-
4020 // gw, alice, and bob.
-
4021 //
-
4022 // 1. gw issues BTC and USD. qw charges a 0.2% transfer fee.
-
4023 //
-
4024 // 2. alice makes an offer to buy XRP and sell USD.
-
4025 // 3. bob makes an offer to buy BTC and sell XRP.
-
4026 //
-
4027 // 4. alice now makes an offer to sell BTC and buy USD.
+
4019 // So alice sells USD to herself.
+
4020 //
+
4021 // There are six cases that we need to test:
+
4022 // o alice crosses her own offer on the first leg (BTC).
+
4023 // o alice crosses her own offer on the second leg (USD).
+
4024 // o alice crosses her own offers on both legs.
+
4025 // All three cases need to be tested:
+
4026 // o In reverse (alice has enough BTC to cover her offer) and
+
4027 // o Forward (alice owns less BTC than is in her final offer.
4028 //
-
4029 // This last offer crosses using auto-bridging.
-
4030 // o alice's last offer sells BTC to...
-
4031 // o bob' offer which takes alice's BTC and sells XRP to...
-
4032 // o alice's first offer which takes bob's XRP and sells USD to...
-
4033 // o alice's last offer.
-
4034 //
-
4035 // So alice sells USD to herself.
-
4036 //
-
4037 // There are six cases that we need to test:
-
4038 // o alice crosses her own offer on the first leg (BTC).
-
4039 // o alice crosses her own offer on the second leg (USD).
-
4040 // o alice crosses her own offers on both legs.
-
4041 // All three cases need to be tested:
-
4042 // o In reverse (alice has enough BTC to cover her offer) and
-
4043 // o Forward (alice owns less BTC than is in her final offer.
-
4044 //
-
4045 // It turns out that two of the forward cases fail for a different
-
4046 // reason. They are therefore commented out here, But they are
-
4047 // revisited in the testSelfPayUnlimitedFunds() unit test.
+
4029 // It turns out that two of the forward cases fail for a different
+
4030 // reason. They are therefore commented out here, But they are
+
4031 // revisited in the testSelfPayUnlimitedFunds() unit test.
+
4032
+
4033 using namespace jtx;
+
4034
+
4035 Env env{*this, features};
+
4036 auto const baseFee = env.current()->fees().base.drops();
+
4037
+
4038 auto const gw = Account("gw");
+
4039 auto const BTC = gw["BTC"];
+
4040 auto const USD = gw["USD"];
+
4041 auto const startXrpBalance = XRP(4000000);
+
4042
+
4043 env.fund(startXrpBalance, gw);
+
4044 env.close();
+
4045
+
4046 env(rate(gw, 1.25));
+
4047 env.close();
4048
-
4049 using namespace jtx;
-
4050
-
4051 Env env{*this, features};
-
4052 auto const baseFee = env.current()->fees().base.drops();
-
4053
-
4054 auto const gw = Account("gw");
-
4055 auto const BTC = gw["BTC"];
-
4056 auto const USD = gw["USD"];
-
4057 auto const startXrpBalance = XRP(4000000);
-
4058
-
4059 env.fund(startXrpBalance, gw);
-
4060 env.close();
-
4061
-
4062 env(rate(gw, 1.25));
-
4063 env.close();
-
4064
-
4065 // Test cases
-
4066 struct Actor
-
4067 {
-
4068 Account acct;
-
4069 int offers; // offers on account after crossing
-
4070 PrettyAmount xrp; // final expected after crossing
-
4071 PrettyAmount btc; // final expected after crossing
-
4072 PrettyAmount usd; // final expected after crossing
-
4073 };
-
4074 struct TestData
-
4075 {
-
4076 // The first three three integers give the *index* in actors
-
4077 // to assign each of the three roles. By using indices it is
-
4078 // easy for alice to own the offer in the first leg, the second
-
4079 // leg, or both.
-
4080 std::size_t self;
-
4081 std::size_t leg0;
-
4082 std::size_t leg1;
-
4083 PrettyAmount btcStart;
-
4084 std::vector<Actor> actors;
-
4085 };
+
4049 // Test cases
+
4050 struct Actor
+
4051 {
+
4052 Account acct;
+
4053 int offers; // offers on account after crossing
+
4054 PrettyAmount xrp; // final expected after crossing
+
4055 PrettyAmount btc; // final expected after crossing
+
4056 PrettyAmount usd; // final expected after crossing
+
4057 };
+
4058 struct TestData
+
4059 {
+
4060 // The first three three integers give the *index* in actors
+
4061 // to assign each of the three roles. By using indices it is
+
4062 // easy for alice to own the offer in the first leg, the second
+
4063 // leg, or both.
+
4064 std::size_t self;
+
4065 std::size_t leg0;
+
4066 std::size_t leg1;
+
4067 PrettyAmount btcStart;
+
4068 std::vector<Actor> actors;
+
4069 };
+
4070
+
4071 // clang-format off
+
4072 TestData const tests[]{
+
4073 // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] -------------------
+
4074 {0, 0, 1, BTC(20), {{"ann", 0, drops(3900000'000000 - 4 * baseFee), BTC(20.0), USD(3000)}, {"abe", 0, drops(4100000'000000 - 3 * baseFee), BTC( 0), USD(750)}}}, // no BTC xfer fee
+
4075 {0, 1, 0, BTC(20), {{"bev", 0, drops(4100000'000000 - 4 * baseFee), BTC( 7.5), USD(2000)}, {"bob", 0, drops(3900000'000000 - 3 * baseFee), BTC(10), USD( 0)}}}, // no USD xfer fee
+
4076 {0, 0, 0, BTC(20), {{"cam", 0, drops(4000000'000000 - 5 * baseFee), BTC(20.0), USD(2000)} }}, // no xfer fee
+
4077 {0, 1, 0, BTC( 5), {{"deb", 1, drops(4040000'000000 - 4 * baseFee), BTC( 0.0), USD(2000)}, {"dan", 1, drops(3960000'000000 - 3 * baseFee), BTC( 4), USD( 0)}}}, // no USD xfer fee
+
4078 };
+
4079 // clang-format on
+
4080
+
4081 for (auto const& t : tests)
+
4082 {
+
4083 Account const& self = t.actors[t.self].acct;
+
4084 Account const& leg0 = t.actors[t.leg0].acct;
+
4085 Account const& leg1 = t.actors[t.leg1].acct;
4086
-
4087 // clang-format off
-
4088 TestData const tests[]{
-
4089 // btcStart --------------------- actor[0] --------------------- -------------------- actor[1] -------------------
-
4090 {0, 0, 1, BTC(20), {{"ann", 0, drops(3900000'000000 - 4 * baseFee), BTC(20.0), USD(3000)}, {"abe", 0, drops(4100000'000000 - 3 * baseFee), BTC( 0), USD(750)}}}, // no BTC xfer fee
-
4091 {0, 1, 0, BTC(20), {{"bev", 0, drops(4100000'000000 - 4 * baseFee), BTC( 7.5), USD(2000)}, {"bob", 0, drops(3900000'000000 - 3 * baseFee), BTC(10), USD( 0)}}}, // no USD xfer fee
-
4092 {0, 0, 0, BTC(20), {{"cam", 0, drops(4000000'000000 - 5 * baseFee), BTC(20.0), USD(2000)} }}, // no xfer fee
-
4093 {0, 1, 0, BTC( 5), {{"deb", 1, drops(4040000'000000 - 4 * baseFee), BTC( 0.0), USD(2000)}, {"dan", 1, drops(3960000'000000 - 3 * baseFee), BTC( 4), USD( 0)}}}, // no USD xfer fee
-
4094 };
-
4095 // clang-format on
+
4087 for (auto const& actor : t.actors)
+
4088 {
+
4089 env.fund(XRP(4000000), actor.acct);
+
4090 env.close();
+
4091
+
4092 env(trust(actor.acct, BTC(40)));
+
4093 env(trust(actor.acct, USD(8000)));
+
4094 env.close();
+
4095 }
4096
-
4097 for (auto const& t : tests)
-
4098 {
-
4099 Account const& self = t.actors[t.self].acct;
-
4100 Account const& leg0 = t.actors[t.leg0].acct;
-
4101 Account const& leg1 = t.actors[t.leg1].acct;
+
4097 env(pay(gw, self, t.btcStart));
+
4098 env(pay(gw, self, USD(2000)));
+
4099 if (self.id() != leg1.id())
+
4100 env(pay(gw, leg1, USD(2000)));
+
4101 env.close();
4102
-
4103 for (auto const& actor : t.actors)
-
4104 {
-
4105 env.fund(XRP(4000000), actor.acct);
-
4106 env.close();
-
4107
-
4108 env(trust(actor.acct, BTC(40)));
-
4109 env(trust(actor.acct, USD(8000)));
-
4110 env.close();
-
4111 }
+
4103 // Get the initial offers in place. Remember their sequences
+
4104 // so we can delete them later.
+
4105 env(offer(leg0, BTC(10), XRP(100000), tfPassive));
+
4106 env.close();
+
4107 std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
+
4108
+
4109 env(offer(leg1, XRP(100000), USD(1000), tfPassive));
+
4110 env.close();
+
4111 std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
4112
-
4113 env(pay(gw, self, t.btcStart));
-
4114 env(pay(gw, self, USD(2000)));
-
4115 if (self.id() != leg1.id())
-
4116 env(pay(gw, leg1, USD(2000)));
-
4117 env.close();
-
4118
-
4119 // Get the initial offers in place. Remember their sequences
-
4120 // so we can delete them later.
-
4121 env(offer(leg0, BTC(10), XRP(100000), tfPassive));
-
4122 env.close();
-
4123 std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
-
4124
-
4125 env(offer(leg1, XRP(100000), USD(1000), tfPassive));
-
4126 env.close();
-
4127 std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
-
4128
-
4129 // This is the offer that matters.
-
4130 env(offer(self, USD(1000), BTC(10)));
-
4131 env.close();
-
4132 std::uint32_t const selfOfferSeq = env.seq(self) - 1;
+
4113 // This is the offer that matters.
+
4114 env(offer(self, USD(1000), BTC(10)));
+
4115 env.close();
+
4116 std::uint32_t const selfOfferSeq = env.seq(self) - 1;
+
4117
+
4118 // Verify results.
+
4119 for (auto const& actor : t.actors)
+
4120 {
+
4121 // Sometimes Taker crossing gets lazy about deleting offers.
+
4122 // Treat an empty offer as though it is deleted.
+
4123 auto actorOffers = offersOnAccount(env, actor.acct);
+
4124 auto const offerCount = std::distance(
+
4125 actorOffers.begin(),
+
4126 std::remove_if(
+
4127 actorOffers.begin(),
+
4128 actorOffers.end(),
+
4129 [](std::shared_ptr<SLE const>& offer) {
+
4130 return (*offer)[sfTakerGets].signum() == 0;
+
4131 }));
+
4132 BEAST_EXPECT(offerCount == actor.offers);
4133
-
4134 // Verify results.
-
4135 for (auto const& actor : t.actors)
-
4136 {
-
4137 // Sometimes Taker crossing gets lazy about deleting offers.
-
4138 // Treat an empty offer as though it is deleted.
-
4139 auto actorOffers = offersOnAccount(env, actor.acct);
-
4140 auto const offerCount = std::distance(
-
4141 actorOffers.begin(),
-
4142 std::remove_if(
-
4143 actorOffers.begin(),
-
4144 actorOffers.end(),
-
4145 [](std::shared_ptr<SLE const>& offer) {
-
4146 return (*offer)[sfTakerGets].signum() == 0;
-
4147 }));
-
4148 BEAST_EXPECT(offerCount == actor.offers);
-
4149
-
4150 env.require(balance(actor.acct, actor.xrp));
-
4151 env.require(balance(actor.acct, actor.btc));
-
4152 env.require(balance(actor.acct, actor.usd));
-
4153 }
-
4154 // Remove any offers that might be left hanging around. They
-
4155 // could bollix up later loops.
-
4156 env(offer_cancel(leg0, leg0OfferSeq));
-
4157 env.close();
-
4158 env(offer_cancel(leg1, leg1OfferSeq));
-
4159 env.close();
-
4160 env(offer_cancel(self, selfOfferSeq));
-
4161 env.close();
-
4162 }
-
4163 }
-
4164
-
4165 void
-
4166 testSelfPayUnlimitedFunds(FeatureBitset features)
-
4167 {
-
4168 testcase("Self Pay Unlimited Funds");
-
4169 // The Taker offer crossing code recognized when Alice was paying
-
4170 // Alice the same denomination. In this case, as long as Alice
-
4171 // has a little bit of that denomination, it treats Alice as though
-
4172 // she has unlimited funds in that denomination.
-
4173 //
-
4174 // Huh? What kind of sense does that make?
-
4175 //
-
4176 // One way to think about it is to break a single payment into a
-
4177 // series of very small payments executed sequentially but very
-
4178 // quickly. Alice needs to pay herself 1 USD, but she only has
-
4179 // 0.01 USD. Alice says, "Hey Alice, let me pay you a penny."
-
4180 // Alice does this, taking the penny out of her pocket and then
-
4181 // putting it back in her pocket. Then she says, "Hey Alice,
-
4182 // I found another penny. I can pay you another penny." Repeat
-
4183 // these steps 100 times and Alice has paid herself 1 USD even though
-
4184 // she only owns 0.01 USD.
-
4185 //
-
4186 // That's all very nice, but the payment code does not support this
-
4187 // optimization. In part that's because the payment code can
-
4188 // operate on a whole batch of offers. As a matter of fact, it can
-
4189 // deal in two consecutive batches of offers. It would take a great
-
4190 // deal of sorting out to figure out which offers in the two batches
-
4191 // had the same owner and give them special processing. And,
-
4192 // honestly, it's a weird little corner case.
-
4193 //
-
4194 // So, since Flow offer crossing uses the payments engine, Flow
-
4195 // offer crossing no longer supports this optimization.
-
4196 //
-
4197 // The following test shows the difference in the behaviors between
-
4198 // Taker offer crossing and Flow offer crossing.
+
4134 env.require(balance(actor.acct, actor.xrp));
+
4135 env.require(balance(actor.acct, actor.btc));
+
4136 env.require(balance(actor.acct, actor.usd));
+
4137 }
+
4138 // Remove any offers that might be left hanging around. They
+
4139 // could bollix up later loops.
+
4140 env(offer_cancel(leg0, leg0OfferSeq));
+
4141 env.close();
+
4142 env(offer_cancel(leg1, leg1OfferSeq));
+
4143 env.close();
+
4144 env(offer_cancel(self, selfOfferSeq));
+
4145 env.close();
+
4146 }
+
4147 }
+
4148
+
4149 void
+
4150 testSelfPayUnlimitedFunds(FeatureBitset features)
+
4151 {
+
4152 testcase("Self Pay Unlimited Funds");
+
4153 // The Taker offer crossing code recognized when Alice was paying
+
4154 // Alice the same denomination. In this case, as long as Alice
+
4155 // has a little bit of that denomination, it treats Alice as though
+
4156 // she has unlimited funds in that denomination.
+
4157 //
+
4158 // Huh? What kind of sense does that make?
+
4159 //
+
4160 // One way to think about it is to break a single payment into a
+
4161 // series of very small payments executed sequentially but very
+
4162 // quickly. Alice needs to pay herself 1 USD, but she only has
+
4163 // 0.01 USD. Alice says, "Hey Alice, let me pay you a penny."
+
4164 // Alice does this, taking the penny out of her pocket and then
+
4165 // putting it back in her pocket. Then she says, "Hey Alice,
+
4166 // I found another penny. I can pay you another penny." Repeat
+
4167 // these steps 100 times and Alice has paid herself 1 USD even though
+
4168 // she only owns 0.01 USD.
+
4169 //
+
4170 // That's all very nice, but the payment code does not support this
+
4171 // optimization. In part that's because the payment code can
+
4172 // operate on a whole batch of offers. As a matter of fact, it can
+
4173 // deal in two consecutive batches of offers. It would take a great
+
4174 // deal of sorting out to figure out which offers in the two batches
+
4175 // had the same owner and give them special processing. And,
+
4176 // honestly, it's a weird little corner case.
+
4177 //
+
4178 // So, since Flow offer crossing uses the payments engine, Flow
+
4179 // offer crossing no longer supports this optimization.
+
4180 //
+
4181 // The following test shows the difference in the behaviors between
+
4182 // Taker offer crossing and Flow offer crossing.
+
4183
+
4184 using namespace jtx;
+
4185
+
4186 Env env{*this, features};
+
4187 auto const baseFee = env.current()->fees().base.drops();
+
4188
+
4189 auto const gw = Account("gw");
+
4190 auto const BTC = gw["BTC"];
+
4191 auto const USD = gw["USD"];
+
4192 auto const startXrpBalance = XRP(4000000);
+
4193
+
4194 env.fund(startXrpBalance, gw);
+
4195 env.close();
+
4196
+
4197 env(rate(gw, 1.25));
+
4198 env.close();
4199
-
4200 using namespace jtx;
-
4201
-
4202 Env env{*this, features};
-
4203 auto const baseFee = env.current()->fees().base.drops();
-
4204
-
4205 auto const gw = Account("gw");
-
4206 auto const BTC = gw["BTC"];
-
4207 auto const USD = gw["USD"];
-
4208 auto const startXrpBalance = XRP(4000000);
-
4209
-
4210 env.fund(startXrpBalance, gw);
-
4211 env.close();
-
4212
-
4213 env(rate(gw, 1.25));
-
4214 env.close();
-
4215
-
4216 // Test cases
-
4217 struct Actor
-
4218 {
-
4219 Account acct;
-
4220 int offers; // offers on account after crossing
-
4221 PrettyAmount xrp; // final expected after crossing
-
4222 PrettyAmount btc; // final expected after crossing
-
4223 PrettyAmount usd; // final expected after crossing
-
4224 };
-
4225 struct TestData
-
4226 {
-
4227 // The first three three integers give the *index* in actors
-
4228 // to assign each of the three roles. By using indices it is
-
4229 // easy for alice to own the offer in the first leg, the second
-
4230 // leg, or both.
-
4231 std::size_t self;
-
4232 std::size_t leg0;
-
4233 std::size_t leg1;
-
4234 PrettyAmount btcStart;
-
4235 std::vector<Actor> actors;
-
4236 };
-
4237
-
4238 // clang-format off
-
4239 TestData const takerTests[]{
-
4240 // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
-
4241 {0, 0, 1, BTC(5), {{"deb", 0, drops(3900000'000000 - 4 * baseFee), BTC(5), USD(3000)}, {"dan", 0, drops(4100000'000000 - 3 * baseFee), BTC(0), USD(750)}}}, // no BTC xfer fee
-
4242 {0, 0, 0, BTC(5), {{"flo", 0, drops(4000000'000000 - 5 * baseFee), BTC(5), USD(2000)} }} // no xfer fee
-
4243 };
-
4244
-
4245 TestData const flowTests[]{
-
4246 // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
-
4247 {0, 0, 1, BTC(5), {{"gay", 1, drops(3950000'000000 - 4 * baseFee), BTC(5), USD(2500)}, {"gar", 1, drops(4050000'000000 - 3 * baseFee), BTC(0), USD(1375)}}}, // no BTC xfer fee
-
4248 {0, 0, 0, BTC(5), {{"hye", 2, drops(4000000'000000 - 5 * baseFee), BTC(5), USD(2000)} }} // no xfer fee
-
4249 };
-
4250 // clang-format on
+
4200 // Test cases
+
4201 struct Actor
+
4202 {
+
4203 Account acct;
+
4204 int offers; // offers on account after crossing
+
4205 PrettyAmount xrp; // final expected after crossing
+
4206 PrettyAmount btc; // final expected after crossing
+
4207 PrettyAmount usd; // final expected after crossing
+
4208 };
+
4209 struct TestData
+
4210 {
+
4211 // The first three three integers give the *index* in actors
+
4212 // to assign each of the three roles. By using indices it is
+
4213 // easy for alice to own the offer in the first leg, the second
+
4214 // leg, or both.
+
4215 std::size_t self;
+
4216 std::size_t leg0;
+
4217 std::size_t leg1;
+
4218 PrettyAmount btcStart;
+
4219 std::vector<Actor> actors;
+
4220 };
+
4221
+
4222 // clang-format off
+
4223 TestData const tests[]{
+
4224 // btcStart ------------------- actor[0] -------------------- ------------------- actor[1] --------------------
+
4225 {0, 0, 1, BTC(5), {{"gay", 1, drops(3950000'000000 - 4 * baseFee), BTC(5), USD(2500)}, {"gar", 1, drops(4050000'000000 - 3 * baseFee), BTC(0), USD(1375)}}}, // no BTC xfer fee
+
4226 {0, 0, 0, BTC(5), {{"hye", 2, drops(4000000'000000 - 5 * baseFee), BTC(5), USD(2000)} }} // no xfer fee
+
4227 };
+
4228 // clang-format on
+
4229
+
4230 for (auto const& t : tests)
+
4231 {
+
4232 Account const& self = t.actors[t.self].acct;
+
4233 Account const& leg0 = t.actors[t.leg0].acct;
+
4234 Account const& leg1 = t.actors[t.leg1].acct;
+
4235
+
4236 for (auto const& actor : t.actors)
+
4237 {
+
4238 env.fund(XRP(4000000), actor.acct);
+
4239 env.close();
+
4240
+
4241 env(trust(actor.acct, BTC(40)));
+
4242 env(trust(actor.acct, USD(8000)));
+
4243 env.close();
+
4244 }
+
4245
+
4246 env(pay(gw, self, t.btcStart));
+
4247 env(pay(gw, self, USD(2000)));
+
4248 if (self.id() != leg1.id())
+
4249 env(pay(gw, leg1, USD(2000)));
+
4250 env.close();
4251
-
4252 // Pick the right tests.
-
4253 auto const& tests = features[featureFlowCross] ? flowTests : takerTests;
-
4254
-
4255 for (auto const& t : tests)
-
4256 {
-
4257 Account const& self = t.actors[t.self].acct;
-
4258 Account const& leg0 = t.actors[t.leg0].acct;
-
4259 Account const& leg1 = t.actors[t.leg1].acct;
-
4260
-
4261 for (auto const& actor : t.actors)
-
4262 {
-
4263 env.fund(XRP(4000000), actor.acct);
-
4264 env.close();
-
4265
-
4266 env(trust(actor.acct, BTC(40)));
-
4267 env(trust(actor.acct, USD(8000)));
-
4268 env.close();
-
4269 }
-
4270
-
4271 env(pay(gw, self, t.btcStart));
-
4272 env(pay(gw, self, USD(2000)));
-
4273 if (self.id() != leg1.id())
-
4274 env(pay(gw, leg1, USD(2000)));
-
4275 env.close();
-
4276
-
4277 // Get the initial offers in place. Remember their sequences
-
4278 // so we can delete them later.
-
4279 env(offer(leg0, BTC(10), XRP(100000), tfPassive));
-
4280 env.close();
-
4281 std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
+
4252 // Get the initial offers in place. Remember their sequences
+
4253 // so we can delete them later.
+
4254 env(offer(leg0, BTC(10), XRP(100000), tfPassive));
+
4255 env.close();
+
4256 std::uint32_t const leg0OfferSeq = env.seq(leg0) - 1;
+
4257
+
4258 env(offer(leg1, XRP(100000), USD(1000), tfPassive));
+
4259 env.close();
+
4260 std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
+
4261
+
4262 // This is the offer that matters.
+
4263 env(offer(self, USD(1000), BTC(10)));
+
4264 env.close();
+
4265 std::uint32_t const selfOfferSeq = env.seq(self) - 1;
+
4266
+
4267 // Verify results.
+
4268 for (auto const& actor : t.actors)
+
4269 {
+
4270 // Sometimes Taker offer crossing gets lazy about deleting
+
4271 // offers. Treat an empty offer as though it is deleted.
+
4272 auto actorOffers = offersOnAccount(env, actor.acct);
+
4273 auto const offerCount = std::distance(
+
4274 actorOffers.begin(),
+
4275 std::remove_if(
+
4276 actorOffers.begin(),
+
4277 actorOffers.end(),
+
4278 [](std::shared_ptr<SLE const>& offer) {
+
4279 return (*offer)[sfTakerGets].signum() == 0;
+
4280 }));
+
4281 BEAST_EXPECT(offerCount == actor.offers);
4282
-
4283 env(offer(leg1, XRP(100000), USD(1000), tfPassive));
-
4284 env.close();
-
4285 std::uint32_t const leg1OfferSeq = env.seq(leg1) - 1;
-
4286
-
4287 // This is the offer that matters.
-
4288 env(offer(self, USD(1000), BTC(10)));
-
4289 env.close();
-
4290 std::uint32_t const selfOfferSeq = env.seq(self) - 1;
-
4291
-
4292 // Verify results.
-
4293 for (auto const& actor : t.actors)
-
4294 {
-
4295 // Sometimes Taker offer crossing gets lazy about deleting
-
4296 // offers. Treat an empty offer as though it is deleted.
-
4297 auto actorOffers = offersOnAccount(env, actor.acct);
-
4298 auto const offerCount = std::distance(
-
4299 actorOffers.begin(),
-
4300 std::remove_if(
-
4301 actorOffers.begin(),
-
4302 actorOffers.end(),
-
4303 [](std::shared_ptr<SLE const>& offer) {
-
4304 return (*offer)[sfTakerGets].signum() == 0;
-
4305 }));
-
4306 BEAST_EXPECT(offerCount == actor.offers);
-
4307
-
4308 env.require(balance(actor.acct, actor.xrp));
-
4309 env.require(balance(actor.acct, actor.btc));
-
4310 env.require(balance(actor.acct, actor.usd));
-
4311 }
-
4312 // Remove any offers that might be left hanging around. They
-
4313 // could bollix up later loops.
-
4314 env(offer_cancel(leg0, leg0OfferSeq));
-
4315 env.close();
-
4316 env(offer_cancel(leg1, leg1OfferSeq));
-
4317 env.close();
-
4318 env(offer_cancel(self, selfOfferSeq));
-
4319 env.close();
-
4320 }
-
4321 }
-
4322
-
4323 void
-
4324 testRequireAuth(FeatureBitset features)
-
4325 {
-
4326 testcase("lsfRequireAuth");
-
4327
-
4328 using namespace jtx;
+
4283 env.require(balance(actor.acct, actor.xrp));
+
4284 env.require(balance(actor.acct, actor.btc));
+
4285 env.require(balance(actor.acct, actor.usd));
+
4286 }
+
4287 // Remove any offers that might be left hanging around. They
+
4288 // could bollix up later loops.
+
4289 env(offer_cancel(leg0, leg0OfferSeq));
+
4290 env.close();
+
4291 env(offer_cancel(leg1, leg1OfferSeq));
+
4292 env.close();
+
4293 env(offer_cancel(self, selfOfferSeq));
+
4294 env.close();
+
4295 }
+
4296 }
+
4297
+
4298 void
+
4299 testRequireAuth(FeatureBitset features)
+
4300 {
+
4301 testcase("lsfRequireAuth");
+
4302
+
4303 using namespace jtx;
+
4304
+
4305 Env env{*this, features};
+
4306
+
4307 auto const gw = Account("gw");
+
4308 auto const alice = Account("alice");
+
4309 auto const bob = Account("bob");
+
4310 auto const gwUSD = gw["USD"];
+
4311 auto const aliceUSD = alice["USD"];
+
4312 auto const bobUSD = bob["USD"];
+
4313
+
4314 env.fund(XRP(400000), gw, alice, bob);
+
4315 env.close();
+
4316
+
4317 // GW requires authorization for holders of its IOUs
+
4318 env(fset(gw, asfRequireAuth));
+
4319 env.close();
+
4320
+
4321 // Properly set trust and have gw authorize bob and alice
+
4322 env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
+
4323 env(trust(bob, gwUSD(100)));
+
4324 env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
+
4325 env(trust(alice, gwUSD(100)));
+
4326 // Alice is able to place the offer since the GW has authorized her
+
4327 env(offer(alice, gwUSD(40), XRP(4000)));
+
4328 env.close();
4329
-
4330 Env env{*this, features};
-
4331
-
4332 auto const gw = Account("gw");
-
4333 auto const alice = Account("alice");
-
4334 auto const bob = Account("bob");
-
4335 auto const gwUSD = gw["USD"];
-
4336 auto const aliceUSD = alice["USD"];
-
4337 auto const bobUSD = bob["USD"];
-
4338
-
4339 env.fund(XRP(400000), gw, alice, bob);
+
4330 env.require(offers(alice, 1));
+
4331 env.require(balance(alice, gwUSD(0)));
+
4332
+
4333 env(pay(gw, bob, gwUSD(50)));
+
4334 env.close();
+
4335
+
4336 env.require(balance(bob, gwUSD(50)));
+
4337
+
4338 // Bob's offer should cross Alice's
+
4339 env(offer(bob, XRP(4000), gwUSD(40)));
4340 env.close();
4341
-
4342 // GW requires authorization for holders of its IOUs
-
4343 env(fset(gw, asfRequireAuth));
-
4344 env.close();
-
4345
-
4346 // Properly set trust and have gw authorize bob and alice
-
4347 env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
-
4348 env(trust(bob, gwUSD(100)));
-
4349 env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
-
4350 env(trust(alice, gwUSD(100)));
-
4351 // Alice is able to place the offer since the GW has authorized her
-
4352 env(offer(alice, gwUSD(40), XRP(4000)));
-
4353 env.close();
-
4354
-
4355 env.require(offers(alice, 1));
-
4356 env.require(balance(alice, gwUSD(0)));
-
4357
-
4358 env(pay(gw, bob, gwUSD(50)));
-
4359 env.close();
-
4360
-
4361 env.require(balance(bob, gwUSD(50)));
-
4362
-
4363 // Bob's offer should cross Alice's
-
4364 env(offer(bob, XRP(4000), gwUSD(40)));
-
4365 env.close();
-
4366
-
4367 env.require(offers(alice, 0));
-
4368 env.require(balance(alice, gwUSD(40)));
-
4369
-
4370 env.require(offers(bob, 0));
-
4371 env.require(balance(bob, gwUSD(10)));
-
4372 }
-
4373
-
4374 void
-
4375 testMissingAuth(FeatureBitset features)
-
4376 {
-
4377 testcase("Missing Auth");
-
4378 // 1. alice creates an offer to acquire USD/gw, an asset for which
-
4379 // she does not have a trust line. At some point in the future,
-
4380 // gw adds lsfRequireAuth. Then, later, alice's offer is crossed.
-
4381 // a. With Taker alice's unauthorized offer is consumed.
-
4382 // b. With FlowCross alice's offer is deleted, not consumed,
-
4383 // since alice is not authorized to hold USD/gw.
-
4384 //
-
4385 // 2. alice tries to create an offer for USD/gw, now that gw has
-
4386 // lsfRequireAuth set. This time the offer create fails because
-
4387 // alice is not authorized to hold USD/gw.
-
4388 //
-
4389 // 3. Next, gw creates a trust line to alice, but does not set
-
4390 // tfSetfAuth on that trust line. alice attempts to create an
-
4391 // offer and again fails.
-
4392 //
-
4393 // 4. Finally, gw sets tsfSetAuth on the trust line authorizing
-
4394 // alice to own USD/gw. At this point alice successfully
-
4395 // creates and crosses an offer for USD/gw.
-
4396
-
4397 using namespace jtx;
-
4398
-
4399 Env env{*this, features};
-
4400
-
4401 auto const gw = Account("gw");
-
4402 auto const alice = Account("alice");
-
4403 auto const bob = Account("bob");
-
4404 auto const gwUSD = gw["USD"];
-
4405 auto const aliceUSD = alice["USD"];
-
4406 auto const bobUSD = bob["USD"];
-
4407
-
4408 env.fund(XRP(400000), gw, alice, bob);
-
4409 env.close();
+
4342 env.require(offers(alice, 0));
+
4343 env.require(balance(alice, gwUSD(40)));
+
4344
+
4345 env.require(offers(bob, 0));
+
4346 env.require(balance(bob, gwUSD(10)));
+
4347 }
+
4348
+
4349 void
+
4350 testMissingAuth(FeatureBitset features)
+
4351 {
+
4352 testcase("Missing Auth");
+
4353 // 1. alice creates an offer to acquire USD/gw, an asset for which
+
4354 // she does not have a trust line. At some point in the future,
+
4355 // gw adds lsfRequireAuth. Then, later, alice's offer is crossed.
+
4356 // Alice's offer is deleted, not consumed, since alice is not
+
4357 // authorized to hold USD/gw.
+
4358 //
+
4359 // 2. alice tries to create an offer for USD/gw, now that gw has
+
4360 // lsfRequireAuth set. This time the offer create fails because
+
4361 // alice is not authorized to hold USD/gw.
+
4362 //
+
4363 // 3. Next, gw creates a trust line to alice, but does not set
+
4364 // tfSetfAuth on that trust line. alice attempts to create an
+
4365 // offer and again fails.
+
4366 //
+
4367 // 4. Finally, gw sets tsfSetAuth on the trust line authorizing
+
4368 // alice to own USD/gw. At this point alice successfully
+
4369 // creates and crosses an offer for USD/gw.
+
4370
+
4371 using namespace jtx;
+
4372
+
4373 Env env{*this, features};
+
4374
+
4375 auto const gw = Account("gw");
+
4376 auto const alice = Account("alice");
+
4377 auto const bob = Account("bob");
+
4378 auto const gwUSD = gw["USD"];
+
4379 auto const aliceUSD = alice["USD"];
+
4380 auto const bobUSD = bob["USD"];
+
4381
+
4382 env.fund(XRP(400000), gw, alice, bob);
+
4383 env.close();
+
4384
+
4385 env(offer(alice, gwUSD(40), XRP(4000)));
+
4386 env.close();
+
4387
+
4388 env.require(offers(alice, 1));
+
4389 env.require(balance(alice, gwUSD(none)));
+
4390 env(fset(gw, asfRequireAuth));
+
4391 env.close();
+
4392
+
4393 env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
+
4394 env.close();
+
4395 env(trust(bob, gwUSD(100)));
+
4396 env.close();
+
4397
+
4398 env(pay(gw, bob, gwUSD(50)));
+
4399 env.close();
+
4400 env.require(balance(bob, gwUSD(50)));
+
4401
+
4402 // gw now requires authorization and bob has gwUSD(50). Let's see if
+
4403 // bob can cross alice's offer.
+
4404 //
+
4405 // Bob's offer shouldn't cross and alice's unauthorized offer should be
+
4406 // deleted.
+
4407 env(offer(bob, XRP(4000), gwUSD(40)));
+
4408 env.close();
+
4409 std::uint32_t const bobOfferSeq = env.seq(bob) - 1;
4410
-
4411 env(offer(alice, gwUSD(40), XRP(4000)));
-
4412 env.close();
-
4413
-
4414 env.require(offers(alice, 1));
-
4415 env.require(balance(alice, gwUSD(none)));
-
4416 env(fset(gw, asfRequireAuth));
-
4417 env.close();
-
4418
-
4419 env(trust(gw, bobUSD(100)), txflags(tfSetfAuth));
-
4420 env.close();
-
4421 env(trust(bob, gwUSD(100)));
-
4422 env.close();
-
4423
-
4424 env(pay(gw, bob, gwUSD(50)));
-
4425 env.close();
-
4426 env.require(balance(bob, gwUSD(50)));
-
4427
-
4428 // gw now requires authorization and bob has gwUSD(50). Let's see if
-
4429 // bob can cross alice's offer.
-
4430 //
-
4431 // o With Taker bob's offer should cross alice's.
-
4432 // o With FlowCross bob's offer shouldn't cross and alice's
-
4433 // unauthorized offer should be deleted.
-
4434 env(offer(bob, XRP(4000), gwUSD(40)));
+
4411 env.require(offers(alice, 0));
+
4412 // alice's unauthorized offer is deleted & bob's offer not crossed.
+
4413 env.require(balance(alice, gwUSD(none)));
+
4414 env.require(offers(bob, 1));
+
4415 env.require(balance(bob, gwUSD(50)));
+
4416
+
4417 // See if alice can create an offer without authorization. alice
+
4418 // should not be able to create the offer and bob's offer should be
+
4419 // untouched.
+
4420 env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_LINE));
+
4421 env.close();
+
4422
+
4423 env.require(offers(alice, 0));
+
4424 env.require(balance(alice, gwUSD(none)));
+
4425
+
4426 env.require(offers(bob, 1));
+
4427 env.require(balance(bob, gwUSD(50)));
+
4428
+
4429 // Set up a trust line for alice, but don't authorize it. alice
+
4430 // should still not be able to create an offer for USD/gw.
+
4431 env(trust(gw, aliceUSD(100)));
+
4432 env.close();
+
4433
+
4434 env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_AUTH));
4435 env.close();
-
4436 std::uint32_t const bobOfferSeq = env.seq(bob) - 1;
-
4437
-
4438 bool const flowCross = features[featureFlowCross];
+
4436
+
4437 env.require(offers(alice, 0));
+
4438 env.require(balance(alice, gwUSD(0)));
4439
-
4440 env.require(offers(alice, 0));
-
4441 if (flowCross)
-
4442 {
-
4443 // alice's unauthorized offer is deleted & bob's offer not crossed.
-
4444 env.require(balance(alice, gwUSD(none)));
-
4445 env.require(offers(bob, 1));
-
4446 env.require(balance(bob, gwUSD(50)));
-
4447 }
-
4448 else
-
4449 {
-
4450 // alice's offer crosses bob's
-
4451 env.require(balance(alice, gwUSD(40)));
-
4452 env.require(offers(bob, 0));
-
4453 env.require(balance(bob, gwUSD(10)));
-
4454
-
4455 // The rest of the test verifies FlowCross behavior.
-
4456 return;
-
4457 }
+
4440 env.require(offers(bob, 1));
+
4441 env.require(balance(bob, gwUSD(50)));
+
4442
+
4443 // Delete bob's offer so alice can create an offer without crossing.
+
4444 env(offer_cancel(bob, bobOfferSeq));
+
4445 env.close();
+
4446 env.require(offers(bob, 0));
+
4447
+
4448 // Finally, set up an authorized trust line for alice. Now alice's
+
4449 // offer should succeed. Note that, since this is an offer rather
+
4450 // than a payment, alice does not need to set a trust line limit.
+
4451 env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
+
4452 env.close();
+
4453
+
4454 env(offer(alice, gwUSD(40), XRP(4000)));
+
4455 env.close();
+
4456
+
4457 env.require(offers(alice, 1));
4458
-
4459 // See if alice can create an offer without authorization. alice
-
4460 // should not be able to create the offer and bob's offer should be
-
4461 // untouched.
-
4462 env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_LINE));
-
4463 env.close();
-
4464
-
4465 env.require(offers(alice, 0));
-
4466 env.require(balance(alice, gwUSD(none)));
-
4467
-
4468 env.require(offers(bob, 1));
-
4469 env.require(balance(bob, gwUSD(50)));
-
4470
-
4471 // Set up a trust line for alice, but don't authorize it. alice
-
4472 // should still not be able to create an offer for USD/gw.
-
4473 env(trust(gw, aliceUSD(100)));
-
4474 env.close();
+
4459 // Now bob creates his offer again. alice's offer should cross.
+
4460 env(offer(bob, XRP(4000), gwUSD(40)));
+
4461 env.close();
+
4462
+
4463 env.require(offers(alice, 0));
+
4464 env.require(balance(alice, gwUSD(40)));
+
4465
+
4466 env.require(offers(bob, 0));
+
4467 env.require(balance(bob, gwUSD(10)));
+
4468 }
+
4469
+
4470 void
+
4471 testRCSmoketest(FeatureBitset features)
+
4472 {
+
4473 testcase("RippleConnect Smoketest payment flow");
+
4474 using namespace jtx;
4475
-
4476 env(offer(alice, gwUSD(40), XRP(4000)), ter(tecNO_AUTH));
-
4477 env.close();
-
4478
-
4479 env.require(offers(alice, 0));
-
4480 env.require(balance(alice, gwUSD(0)));
-
4481
-
4482 env.require(offers(bob, 1));
-
4483 env.require(balance(bob, gwUSD(50)));
-
4484
-
4485 // Delete bob's offer so alice can create an offer without crossing.
-
4486 env(offer_cancel(bob, bobOfferSeq));
-
4487 env.close();
-
4488 env.require(offers(bob, 0));
-
4489
-
4490 // Finally, set up an authorized trust line for alice. Now alice's
-
4491 // offer should succeed. Note that, since this is an offer rather
-
4492 // than a payment, alice does not need to set a trust line limit.
-
4493 env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
-
4494 env.close();
-
4495
-
4496 env(offer(alice, gwUSD(40), XRP(4000)));
-
4497 env.close();
-
4498
-
4499 env.require(offers(alice, 1));
-
4500
-
4501 // Now bob creates his offer again. alice's offer should cross.
-
4502 env(offer(bob, XRP(4000), gwUSD(40)));
-
4503 env.close();
-
4504
-
4505 env.require(offers(alice, 0));
-
4506 env.require(balance(alice, gwUSD(40)));
-
4507
-
4508 env.require(offers(bob, 0));
-
4509 env.require(balance(bob, gwUSD(10)));
-
4510 }
-
4511
-
4512 void
-
4513 testRCSmoketest(FeatureBitset features)
-
4514 {
-
4515 testcase("RippleConnect Smoketest payment flow");
-
4516 using namespace jtx;
-
4517
-
4518 Env env{*this, features};
-
4519
-
4520 // This test mimics the payment flow used in the Ripple Connect
-
4521 // smoke test. The players:
-
4522 // A USD gateway with hot and cold wallets
-
4523 // A EUR gateway with hot and cold walllets
-
4524 // A MM gateway that will provide offers from USD->EUR and EUR->USD
-
4525 // A path from hot US to cold EUR is found and then used to send
-
4526 // USD for EUR that goes through the market maker
+
4476 Env env{*this, features};
+
4477
+
4478 // This test mimics the payment flow used in the Ripple Connect
+
4479 // smoke test. The players:
+
4480 // A USD gateway with hot and cold wallets
+
4481 // A EUR gateway with hot and cold walllets
+
4482 // A MM gateway that will provide offers from USD->EUR and EUR->USD
+
4483 // A path from hot US to cold EUR is found and then used to send
+
4484 // USD for EUR that goes through the market maker
+
4485
+
4486 auto const hotUS = Account("hotUS");
+
4487 auto const coldUS = Account("coldUS");
+
4488 auto const hotEU = Account("hotEU");
+
4489 auto const coldEU = Account("coldEU");
+
4490 auto const mm = Account("mm");
+
4491
+
4492 auto const USD = coldUS["USD"];
+
4493 auto const EUR = coldEU["EUR"];
+
4494
+
4495 env.fund(XRP(100000), hotUS, coldUS, hotEU, coldEU, mm);
+
4496 env.close();
+
4497
+
4498 // Cold wallets require trust but will ripple by default
+
4499 for (auto const& cold : {coldUS, coldEU})
+
4500 {
+
4501 env(fset(cold, asfRequireAuth));
+
4502 env(fset(cold, asfDefaultRipple));
+
4503 }
+
4504 env.close();
+
4505
+
4506 // Each hot wallet trusts the related cold wallet for a large amount
+
4507 env(trust(hotUS, USD(10000000)), txflags(tfSetNoRipple));
+
4508 env(trust(hotEU, EUR(10000000)), txflags(tfSetNoRipple));
+
4509 // Market maker trusts both cold wallets for a large amount
+
4510 env(trust(mm, USD(10000000)), txflags(tfSetNoRipple));
+
4511 env(trust(mm, EUR(10000000)), txflags(tfSetNoRipple));
+
4512 env.close();
+
4513
+
4514 // Gateways authorize the trustlines of hot and market maker
+
4515 env(trust(coldUS, USD(0), hotUS, tfSetfAuth));
+
4516 env(trust(coldEU, EUR(0), hotEU, tfSetfAuth));
+
4517 env(trust(coldUS, USD(0), mm, tfSetfAuth));
+
4518 env(trust(coldEU, EUR(0), mm, tfSetfAuth));
+
4519 env.close();
+
4520
+
4521 // Issue currency from cold wallets to hot and market maker
+
4522 env(pay(coldUS, hotUS, USD(5000000)));
+
4523 env(pay(coldEU, hotEU, EUR(5000000)));
+
4524 env(pay(coldUS, mm, USD(5000000)));
+
4525 env(pay(coldEU, mm, EUR(5000000)));
+
4526 env.close();
4527
-
4528 auto const hotUS = Account("hotUS");
-
4529 auto const coldUS = Account("coldUS");
-
4530 auto const hotEU = Account("hotEU");
-
4531 auto const coldEU = Account("coldEU");
-
4532 auto const mm = Account("mm");
-
4533
-
4534 auto const USD = coldUS["USD"];
-
4535 auto const EUR = coldEU["EUR"];
-
4536
-
4537 env.fund(XRP(100000), hotUS, coldUS, hotEU, coldEU, mm);
-
4538 env.close();
-
4539
-
4540 // Cold wallets require trust but will ripple by default
-
4541 for (auto const& cold : {coldUS, coldEU})
-
4542 {
-
4543 env(fset(cold, asfRequireAuth));
-
4544 env(fset(cold, asfDefaultRipple));
-
4545 }
-
4546 env.close();
-
4547
-
4548 // Each hot wallet trusts the related cold wallet for a large amount
-
4549 env(trust(hotUS, USD(10000000)), txflags(tfSetNoRipple));
-
4550 env(trust(hotEU, EUR(10000000)), txflags(tfSetNoRipple));
-
4551 // Market maker trusts both cold wallets for a large amount
-
4552 env(trust(mm, USD(10000000)), txflags(tfSetNoRipple));
-
4553 env(trust(mm, EUR(10000000)), txflags(tfSetNoRipple));
-
4554 env.close();
-
4555
-
4556 // Gateways authorize the trustlines of hot and market maker
-
4557 env(trust(coldUS, USD(0), hotUS, tfSetfAuth));
-
4558 env(trust(coldEU, EUR(0), hotEU, tfSetfAuth));
-
4559 env(trust(coldUS, USD(0), mm, tfSetfAuth));
-
4560 env(trust(coldEU, EUR(0), mm, tfSetfAuth));
-
4561 env.close();
-
4562
-
4563 // Issue currency from cold wallets to hot and market maker
-
4564 env(pay(coldUS, hotUS, USD(5000000)));
-
4565 env(pay(coldEU, hotEU, EUR(5000000)));
-
4566 env(pay(coldUS, mm, USD(5000000)));
-
4567 env(pay(coldEU, mm, EUR(5000000)));
-
4568 env.close();
-
4569
-
4570 // MM places offers
-
4571 float const rate = 0.9f; // 0.9 USD = 1 EUR
-
4572 env(offer(mm, EUR(4000000 * rate), USD(4000000)),
-
4573 json(jss::Flags, tfSell));
-
4574
-
4575 float const reverseRate = 1.0f / rate * 1.00101f;
-
4576 env(offer(mm, USD(4000000 * reverseRate), EUR(4000000)),
-
4577 json(jss::Flags, tfSell));
+
4528 // MM places offers
+
4529 float const rate = 0.9f; // 0.9 USD = 1 EUR
+
4530 env(offer(mm, EUR(4000000 * rate), USD(4000000)),
+
4531 json(jss::Flags, tfSell));
+
4532
+
4533 float const reverseRate = 1.0f / rate * 1.00101f;
+
4534 env(offer(mm, USD(4000000 * reverseRate), EUR(4000000)),
+
4535 json(jss::Flags, tfSell));
+
4536 env.close();
+
4537
+
4538 // There should be a path available from hot US to cold EUR
+
4539 {
+
4540 Json::Value jvParams;
+
4541 jvParams[jss::destination_account] = coldEU.human();
+
4542 jvParams[jss::destination_amount][jss::issuer] = coldEU.human();
+
4543 jvParams[jss::destination_amount][jss::currency] = "EUR";
+
4544 jvParams[jss::destination_amount][jss::value] = 10;
+
4545 jvParams[jss::source_account] = hotUS.human();
+
4546
+
4547 Json::Value const jrr{env.rpc(
+
4548 "json", "ripple_path_find", to_string(jvParams))[jss::result]};
+
4549
+
4550 BEAST_EXPECT(jrr[jss::status] == "success");
+
4551 BEAST_EXPECT(
+
4552 jrr[jss::alternatives].isArray() &&
+
4553 jrr[jss::alternatives].size() > 0);
+
4554 }
+
4555 // Send the payment using the found path.
+
4556 env(pay(hotUS, coldEU, EUR(10)), sendmax(USD(11.1223326)));
+
4557 }
+
4558
+
4559 void
+
4560 testSelfAuth(FeatureBitset features)
+
4561 {
+
4562 testcase("Self Auth");
+
4563
+
4564 using namespace jtx;
+
4565
+
4566 Env env{*this, features};
+
4567
+
4568 auto const gw = Account("gw");
+
4569 auto const alice = Account("alice");
+
4570 auto const gwUSD = gw["USD"];
+
4571 auto const aliceUSD = alice["USD"];
+
4572
+
4573 env.fund(XRP(400000), gw, alice);
+
4574 env.close();
+
4575
+
4576 // Test that gw can create an offer to buy gw's currency.
+
4577 env(offer(gw, gwUSD(40), XRP(4000)));
4578 env.close();
-
4579
-
4580 // There should be a path available from hot US to cold EUR
-
4581 {
-
4582 Json::Value jvParams;
-
4583 jvParams[jss::destination_account] = coldEU.human();
-
4584 jvParams[jss::destination_amount][jss::issuer] = coldEU.human();
-
4585 jvParams[jss::destination_amount][jss::currency] = "EUR";
-
4586 jvParams[jss::destination_amount][jss::value] = 10;
-
4587 jvParams[jss::source_account] = hotUS.human();
-
4588
-
4589 Json::Value const jrr{env.rpc(
-
4590 "json", "ripple_path_find", to_string(jvParams))[jss::result]};
-
4591
-
4592 BEAST_EXPECT(jrr[jss::status] == "success");
-
4593 BEAST_EXPECT(
-
4594 jrr[jss::alternatives].isArray() &&
-
4595 jrr[jss::alternatives].size() > 0);
-
4596 }
-
4597 // Send the payment using the found path.
-
4598 env(pay(hotUS, coldEU, EUR(10)), sendmax(USD(11.1223326)));
-
4599 }
-
4600
-
4601 void
-
4602 testSelfAuth(FeatureBitset features)
-
4603 {
-
4604 testcase("Self Auth");
-
4605
-
4606 using namespace jtx;
-
4607
-
4608 Env env{*this, features};
-
4609
-
4610 auto const gw = Account("gw");
-
4611 auto const alice = Account("alice");
-
4612 auto const gwUSD = gw["USD"];
-
4613 auto const aliceUSD = alice["USD"];
-
4614
-
4615 env.fund(XRP(400000), gw, alice);
-
4616 env.close();
-
4617
-
4618 // Test that gw can create an offer to buy gw's currency.
-
4619 env(offer(gw, gwUSD(40), XRP(4000)));
-
4620 env.close();
-
4621 std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
-
4622 env.require(offers(gw, 1));
-
4623
-
4624 // Since gw has an offer out, gw should not be able to set RequireAuth.
-
4625 env(fset(gw, asfRequireAuth), ter(tecOWNERS));
-
4626 env.close();
+
4579 std::uint32_t const gwOfferSeq = env.seq(gw) - 1;
+
4580 env.require(offers(gw, 1));
+
4581
+
4582 // Since gw has an offer out, gw should not be able to set RequireAuth.
+
4583 env(fset(gw, asfRequireAuth), ter(tecOWNERS));
+
4584 env.close();
+
4585
+
4586 // Cancel gw's offer so we can set RequireAuth.
+
4587 env(offer_cancel(gw, gwOfferSeq));
+
4588 env.close();
+
4589 env.require(offers(gw, 0));
+
4590
+
4591 // gw now requires authorization for holders of its IOUs
+
4592 env(fset(gw, asfRequireAuth));
+
4593 env.close();
+
4594
+
4595 // The test behaves differently with or without DepositPreauth.
+
4596 bool const preauth = features[featureDepositPreauth];
+
4597
+
4598 // Before DepositPreauth an account with lsfRequireAuth set could not
+
4599 // create an offer to buy their own currency. After DepositPreauth
+
4600 // they can.
+
4601 env(offer(gw, gwUSD(40), XRP(4000)),
+
4602 ter(preauth ? TER{tesSUCCESS} : TER{tecNO_LINE}));
+
4603 env.close();
+
4604
+
4605 env.require(offers(gw, preauth ? 1 : 0));
+
4606
+
4607 if (!preauth)
+
4608 // The rest of the test verifies DepositPreauth behavior.
+
4609 return;
+
4610
+
4611 // Set up an authorized trust line and pay alice gwUSD 50.
+
4612 env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
+
4613 env(trust(alice, gwUSD(100)));
+
4614 env.close();
+
4615
+
4616 env(pay(gw, alice, gwUSD(50)));
+
4617 env.close();
+
4618
+
4619 env.require(balance(alice, gwUSD(50)));
+
4620
+
4621 // alice's offer should cross gw's
+
4622 env(offer(alice, XRP(4000), gwUSD(40)));
+
4623 env.close();
+
4624
+
4625 env.require(offers(alice, 0));
+
4626 env.require(balance(alice, gwUSD(10)));
4627
-
4628 // Cancel gw's offer so we can set RequireAuth.
-
4629 env(offer_cancel(gw, gwOfferSeq));
-
4630 env.close();
-
4631 env.require(offers(gw, 0));
-
4632
-
4633 // gw now requires authorization for holders of its IOUs
-
4634 env(fset(gw, asfRequireAuth));
-
4635 env.close();
+
4628 env.require(offers(gw, 0));
+
4629 }
+
4630
+
4631 void
+
4632 testDeletedOfferIssuer(FeatureBitset features)
+
4633 {
+
4634 // Show that an offer who's issuer has been deleted cannot be crossed.
+
4635 using namespace jtx;
4636
-
4637 // The test behaves differently with or without DepositPreauth.
-
4638 bool const preauth = features[featureDepositPreauth];
-
4639
-
4640 // Before DepositPreauth an account with lsfRequireAuth set could not
-
4641 // create an offer to buy their own currency. After DepositPreauth
-
4642 // they can.
-
4643 env(offer(gw, gwUSD(40), XRP(4000)),
-
4644 ter(preauth ? TER{tesSUCCESS} : TER{tecNO_LINE}));
-
4645 env.close();
-
4646
-
4647 env.require(offers(gw, preauth ? 1 : 0));
-
4648
-
4649 if (!preauth)
-
4650 // The rest of the test verifies DepositPreauth behavior.
-
4651 return;
+
4637 testcase("Deleted offer issuer");
+
4638
+
4639 auto trustLineExists = [](jtx::Env const& env,
+
4640 jtx::Account const& src,
+
4641 jtx::Account const& dst,
+
4642 Currency const& cur) -> bool {
+
4643 return bool(env.le(keylet::line(src, dst, cur)));
+
4644 };
+
4645
+
4646 Account const alice("alice");
+
4647 Account const becky("becky");
+
4648 Account const carol("carol");
+
4649 Account const gw("gateway");
+
4650 auto const USD = gw["USD"];
+
4651 auto const BUX = alice["BUX"];
4652
-
4653 // Set up an authorized trust line and pay alice gwUSD 50.
-
4654 env(trust(gw, aliceUSD(100)), txflags(tfSetfAuth));
-
4655 env(trust(alice, gwUSD(100)));
-
4656 env.close();
-
4657
-
4658 env(pay(gw, alice, gwUSD(50)));
-
4659 env.close();
-
4660
-
4661 env.require(balance(alice, gwUSD(50)));
-
4662
-
4663 // alice's offer should cross gw's
-
4664 env(offer(alice, XRP(4000), gwUSD(40)));
-
4665 env.close();
-
4666
-
4667 env.require(offers(alice, 0));
-
4668 env.require(balance(alice, gwUSD(10)));
+
4653 Env env{*this, features};
+
4654
+
4655 env.fund(XRP(10000), alice, becky, carol, noripple(gw));
+
4656 env.close();
+
4657 env.trust(USD(1000), becky);
+
4658 env(pay(gw, becky, USD(5)));
+
4659 env.close();
+
4660 BEAST_EXPECT(trustLineExists(env, gw, becky, USD.currency));
+
4661
+
4662 // Make offers that produce USD and can be crossed two ways:
+
4663 // direct XRP -> USD
+
4664 // direct BUX -> USD
+
4665 env(offer(becky, XRP(2), USD(2)), txflags(tfPassive));
+
4666 std::uint32_t const beckyBuxUsdSeq{env.seq(becky)};
+
4667 env(offer(becky, BUX(3), USD(3)), txflags(tfPassive));
+
4668 env.close();
4669
-
4670 env.require(offers(gw, 0));
-
4671 }
-
4672
-
4673 void
-
4674 testDeletedOfferIssuer(FeatureBitset features)
-
4675 {
-
4676 // Show that an offer who's issuer has been deleted cannot be crossed.
-
4677 using namespace jtx;
-
4678
-
4679 testcase("Deleted offer issuer");
-
4680
-
4681 auto trustLineExists = [](jtx::Env const& env,
-
4682 jtx::Account const& src,
-
4683 jtx::Account const& dst,
-
4684 Currency const& cur) -> bool {
-
4685 return bool(env.le(keylet::line(src, dst, cur)));
-
4686 };
-
4687
-
4688 Account const alice("alice");
-
4689 Account const becky("becky");
-
4690 Account const carol("carol");
-
4691 Account const gw("gateway");
-
4692 auto const USD = gw["USD"];
-
4693 auto const BUX = alice["BUX"];
-
4694
-
4695 Env env{*this, features};
-
4696
-
4697 env.fund(XRP(10000), alice, becky, carol, noripple(gw));
-
4698 env.close();
-
4699 env.trust(USD(1000), becky);
-
4700 env(pay(gw, becky, USD(5)));
-
4701 env.close();
-
4702 BEAST_EXPECT(trustLineExists(env, gw, becky, USD.currency));
-
4703
-
4704 // Make offers that produce USD and can be crossed two ways:
-
4705 // direct XRP -> USD
-
4706 // direct BUX -> USD
-
4707 env(offer(becky, XRP(2), USD(2)), txflags(tfPassive));
-
4708 std::uint32_t const beckyBuxUsdSeq{env.seq(becky)};
-
4709 env(offer(becky, BUX(3), USD(3)), txflags(tfPassive));
-
4710 env.close();
-
4711
-
4712 // becky keeps the offers, but removes the trustline.
-
4713 env(pay(becky, gw, USD(5)));
-
4714 env.trust(USD(0), becky);
-
4715 env.close();
-
4716 BEAST_EXPECT(!trustLineExists(env, gw, becky, USD.currency));
-
4717 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
-
4718 BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
-
4719
-
4720 // Delete gw's account.
-
4721 {
-
4722 // The ledger sequence needs to far enough ahead of the account
-
4723 // sequence before the account can be deleted.
-
4724 int const delta =
-
4725 [&env, &gw, openLedgerSeq = env.current()->seq()]() -> int {
-
4726 std::uint32_t const gwSeq{env.seq(gw)};
-
4727 if (gwSeq + 255 > openLedgerSeq)
-
4728 return gwSeq - openLedgerSeq + 255;
-
4729 return 0;
-
4730 }();
-
4731
-
4732 for (int i = 0; i < delta; ++i)
-
4733 env.close();
-
4734
-
4735 // Account deletion has a high fee. Account for that.
-
4736 env(acctdelete(gw, alice),
-
4737 fee(drops(env.current()->fees().increment)));
-
4738 env.close();
-
4739
-
4740 // Verify that gw's account root is gone from the ledger.
-
4741 BEAST_EXPECT(!env.closed()->exists(keylet::account(gw.id())));
-
4742 }
-
4743
-
4744 // alice crosses becky's first offer. The offer create fails because
-
4745 // the USD issuer is not in the ledger.
-
4746 env(offer(alice, USD(2), XRP(2)), ter(tecNO_ISSUER));
-
4747 env.close();
-
4748 env.require(offers(alice, 0));
-
4749 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
-
4750 BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
+
4670 // becky keeps the offers, but removes the trustline.
+
4671 env(pay(becky, gw, USD(5)));
+
4672 env.trust(USD(0), becky);
+
4673 env.close();
+
4674 BEAST_EXPECT(!trustLineExists(env, gw, becky, USD.currency));
+
4675 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
+
4676 BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
+
4677
+
4678 // Delete gw's account.
+
4679 {
+
4680 // The ledger sequence needs to far enough ahead of the account
+
4681 // sequence before the account can be deleted.
+
4682 int const delta =
+
4683 [&env, &gw, openLedgerSeq = env.current()->seq()]() -> int {
+
4684 std::uint32_t const gwSeq{env.seq(gw)};
+
4685 if (gwSeq + 255 > openLedgerSeq)
+
4686 return gwSeq - openLedgerSeq + 255;
+
4687 return 0;
+
4688 }();
+
4689
+
4690 for (int i = 0; i < delta; ++i)
+
4691 env.close();
+
4692
+
4693 // Account deletion has a high fee. Account for that.
+
4694 env(acctdelete(gw, alice),
+
4695 fee(drops(env.current()->fees().increment)));
+
4696 env.close();
+
4697
+
4698 // Verify that gw's account root is gone from the ledger.
+
4699 BEAST_EXPECT(!env.closed()->exists(keylet::account(gw.id())));
+
4700 }
+
4701
+
4702 // alice crosses becky's first offer. The offer create fails because
+
4703 // the USD issuer is not in the ledger.
+
4704 env(offer(alice, USD(2), XRP(2)), ter(tecNO_ISSUER));
+
4705 env.close();
+
4706 env.require(offers(alice, 0));
+
4707 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
+
4708 BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
+
4709
+
4710 // alice crosses becky's second offer. Again, the offer create fails
+
4711 // because the USD issuer is not in the ledger.
+
4712 env(offer(alice, USD(3), BUX(3)), ter(tecNO_ISSUER));
+
4713 env.require(offers(alice, 0));
+
4714 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
+
4715 BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
+
4716
+
4717 // Cancel becky's BUX -> USD offer so we can try auto-bridging.
+
4718 env(offer_cancel(becky, beckyBuxUsdSeq));
+
4719 env.close();
+
4720 BEAST_EXPECT(!isOffer(env, becky, BUX(3), USD(3)));
+
4721
+
4722 // alice creates an offer that can be auto-bridged with becky's
+
4723 // remaining offer.
+
4724 env.trust(BUX(1000), carol);
+
4725 env(pay(alice, carol, BUX(2)));
+
4726
+
4727 env(offer(alice, BUX(2), XRP(2)));
+
4728 env.close();
+
4729
+
4730 // carol attempts the auto-bridge. Again, the offer create fails
+
4731 // because the USD issuer is not in the ledger.
+
4732 env(offer(carol, USD(2), BUX(2)), ter(tecNO_ISSUER));
+
4733 env.close();
+
4734 BEAST_EXPECT(isOffer(env, alice, BUX(2), XRP(2)));
+
4735 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
+
4736 }
+
4737
+
4738 void
+
4739 testTickSize(FeatureBitset features)
+
4740 {
+
4741 testcase("Tick Size");
+
4742
+
4743 using namespace jtx;
+
4744
+
4745 // Try to set tick size out of range
+
4746 {
+
4747 Env env{*this, features};
+
4748 auto const gw = Account{"gateway"};
+
4749 env.fund(XRP(10000), gw);
+
4750 env.close();
4751
-
4752 // alice crosses becky's second offer. Again, the offer create fails
-
4753 // because the USD issuer is not in the ledger.
-
4754 env(offer(alice, USD(3), BUX(3)), ter(tecNO_ISSUER));
-
4755 env.require(offers(alice, 0));
-
4756 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
-
4757 BEAST_EXPECT(isOffer(env, becky, BUX(3), USD(3)));
-
4758
-
4759 // Cancel becky's BUX -> USD offer so we can try auto-bridging.
-
4760 env(offer_cancel(becky, beckyBuxUsdSeq));
-
4761 env.close();
-
4762 BEAST_EXPECT(!isOffer(env, becky, BUX(3), USD(3)));
-
4763
-
4764 // alice creates an offer that can be auto-bridged with becky's
-
4765 // remaining offer.
-
4766 env.trust(BUX(1000), carol);
-
4767 env(pay(alice, carol, BUX(2)));
-
4768
-
4769 env(offer(alice, BUX(2), XRP(2)));
-
4770 env.close();
-
4771
-
4772 // carol attempts the auto-bridge. Again, the offer create fails
-
4773 // because the USD issuer is not in the ledger.
-
4774 env(offer(carol, USD(2), BUX(2)), ter(tecNO_ISSUER));
-
4775 env.close();
-
4776 BEAST_EXPECT(isOffer(env, alice, BUX(2), XRP(2)));
-
4777 BEAST_EXPECT(isOffer(env, becky, XRP(2), USD(2)));
-
4778 }
-
4779
-
4780 void
-
4781 testTickSize(FeatureBitset features)
-
4782 {
-
4783 testcase("Tick Size");
+
4752 auto txn = noop(gw);
+
4753 txn[sfTickSize.fieldName] = Quality::minTickSize - 1;
+
4754 env(txn, ter(temBAD_TICK_SIZE));
+
4755
+
4756 txn[sfTickSize.fieldName] = Quality::minTickSize;
+
4757 env(txn);
+
4758 BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::minTickSize);
+
4759
+
4760 txn = noop(gw);
+
4761 txn[sfTickSize.fieldName] = Quality::maxTickSize;
+
4762 env(txn);
+
4763 BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
+
4764
+
4765 txn = noop(gw);
+
4766 txn[sfTickSize.fieldName] = Quality::maxTickSize - 1;
+
4767 env(txn);
+
4768 BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::maxTickSize - 1);
+
4769
+
4770 txn = noop(gw);
+
4771 txn[sfTickSize.fieldName] = Quality::maxTickSize + 1;
+
4772 env(txn, ter(temBAD_TICK_SIZE));
+
4773
+
4774 txn[sfTickSize.fieldName] = 0;
+
4775 env(txn);
+
4776 BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
+
4777 }
+
4778
+
4779 Env env{*this, features};
+
4780 auto const gw = Account{"gateway"};
+
4781 auto const alice = Account{"alice"};
+
4782 auto const XTS = gw["XTS"];
+
4783 auto const XXX = gw["XXX"];
4784
-
4785 using namespace jtx;
-
4786
-
4787 // Try to set tick size out of range
+
4785 env.fund(XRP(10000), gw, alice);
+
4786 env.close();
+
4787
4788 {
-
4789 Env env{*this, features};
-
4790 auto const gw = Account{"gateway"};
-
4791 env.fund(XRP(10000), gw);
-
4792 env.close();
-
4793
-
4794 auto txn = noop(gw);
-
4795 txn[sfTickSize.fieldName] = Quality::minTickSize - 1;
-
4796 env(txn, ter(temBAD_TICK_SIZE));
-
4797
-
4798 txn[sfTickSize.fieldName] = Quality::minTickSize;
-
4799 env(txn);
-
4800 BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::minTickSize);
+
4789 // Gateway sets its tick size to 5
+
4790 auto txn = noop(gw);
+
4791 txn[sfTickSize.fieldName] = 5;
+
4792 env(txn);
+
4793 BEAST_EXPECT((*env.le(gw))[sfTickSize] == 5);
+
4794 }
+
4795
+
4796 env(trust(alice, XTS(1000)));
+
4797 env(trust(alice, XXX(1000)));
+
4798
+
4799 env(pay(gw, alice, alice["XTS"](100)));
+
4800 env(pay(gw, alice, alice["XXX"](100)));
4801
-
4802 txn = noop(gw);
-
4803 txn[sfTickSize.fieldName] = Quality::maxTickSize;
-
4804 env(txn);
-
4805 BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
+
4802 env(offer(alice, XTS(10), XXX(30)));
+
4803 env(offer(alice, XTS(30), XXX(10)));
+
4804 env(offer(alice, XTS(10), XXX(30)), json(jss::Flags, tfSell));
+
4805 env(offer(alice, XTS(30), XXX(10)), json(jss::Flags, tfSell));
4806
-
4807 txn = noop(gw);
-
4808 txn[sfTickSize.fieldName] = Quality::maxTickSize - 1;
-
4809 env(txn);
-
4810 BEAST_EXPECT((*env.le(gw))[sfTickSize] == Quality::maxTickSize - 1);
-
4811
-
4812 txn = noop(gw);
-
4813 txn[sfTickSize.fieldName] = Quality::maxTickSize + 1;
-
4814 env(txn, ter(temBAD_TICK_SIZE));
-
4815
-
4816 txn[sfTickSize.fieldName] = 0;
-
4817 env(txn);
-
4818 BEAST_EXPECT(!env.le(gw)->isFieldPresent(sfTickSize));
-
4819 }
-
4820
-
4821 Env env{*this, features};
-
4822 auto const gw = Account{"gateway"};
-
4823 auto const alice = Account{"alice"};
-
4824 auto const XTS = gw["XTS"];
-
4825 auto const XXX = gw["XXX"];
-
4826
-
4827 env.fund(XRP(10000), gw, alice);
-
4828 env.close();
+
4807 std::map<std::uint32_t, std::pair<STAmount, STAmount>> offers;
+
4808 forEachItem(
+
4809 *env.current(), alice, [&](std::shared_ptr<SLE const> const& sle) {
+
4810 if (sle->getType() == ltOFFER)
+
4811 offers.emplace(
+
4812 (*sle)[sfSequence],
+
4813 std::make_pair(
+
4814 (*sle)[sfTakerPays], (*sle)[sfTakerGets]));
+
4815 });
+
4816
+
4817 // first offer
+
4818 auto it = offers.begin();
+
4819 BEAST_EXPECT(it != offers.end());
+
4820 BEAST_EXPECT(
+
4821 it->second.first == XTS(10) && it->second.second < XXX(30) &&
+
4822 it->second.second > XXX(29.9994));
+
4823
+
4824 // second offer
+
4825 ++it;
+
4826 BEAST_EXPECT(it != offers.end());
+
4827 BEAST_EXPECT(
+
4828 it->second.first == XTS(30) && it->second.second == XXX(10));
4829
-
4830 {
-
4831 // Gateway sets its tick size to 5
-
4832 auto txn = noop(gw);
-
4833 txn[sfTickSize.fieldName] = 5;
-
4834 env(txn);
-
4835 BEAST_EXPECT((*env.le(gw))[sfTickSize] == 5);
-
4836 }
-
4837
-
4838 env(trust(alice, XTS(1000)));
-
4839 env(trust(alice, XXX(1000)));
-
4840
-
4841 env(pay(gw, alice, alice["XTS"](100)));
-
4842 env(pay(gw, alice, alice["XXX"](100)));
-
4843
-
4844 env(offer(alice, XTS(10), XXX(30)));
-
4845 env(offer(alice, XTS(30), XXX(10)));
-
4846 env(offer(alice, XTS(10), XXX(30)), json(jss::Flags, tfSell));
-
4847 env(offer(alice, XTS(30), XXX(10)), json(jss::Flags, tfSell));
-
4848
-
4849 std::map<std::uint32_t, std::pair<STAmount, STAmount>> offers;
-
4850 forEachItem(
-
4851 *env.current(), alice, [&](std::shared_ptr<SLE const> const& sle) {
-
4852 if (sle->getType() == ltOFFER)
-
4853 offers.emplace(
-
4854 (*sle)[sfSequence],
-
4855 std::make_pair(
-
4856 (*sle)[sfTakerPays], (*sle)[sfTakerGets]));
-
4857 });
-
4858
-
4859 // first offer
-
4860 auto it = offers.begin();
-
4861 BEAST_EXPECT(it != offers.end());
-
4862 BEAST_EXPECT(
-
4863 it->second.first == XTS(10) && it->second.second < XXX(30) &&
-
4864 it->second.second > XXX(29.9994));
-
4865
-
4866 // second offer
-
4867 ++it;
-
4868 BEAST_EXPECT(it != offers.end());
-
4869 BEAST_EXPECT(
-
4870 it->second.first == XTS(30) && it->second.second == XXX(10));
-
4871
-
4872 // third offer
-
4873 ++it;
-
4874 BEAST_EXPECT(it != offers.end());
-
4875 BEAST_EXPECT(
-
4876 it->second.first == XTS(10.0002) && it->second.second == XXX(30));
-
4877
-
4878 // fourth offer
-
4879 // exact TakerPays is XTS(1/.033333)
-
4880 ++it;
-
4881 BEAST_EXPECT(it != offers.end());
-
4882 BEAST_EXPECT(
-
4883 it->second.first == XTS(30) && it->second.second == XXX(10));
-
4884
-
4885 BEAST_EXPECT(++it == offers.end());
-
4886 }
+
4830 // third offer
+
4831 ++it;
+
4832 BEAST_EXPECT(it != offers.end());
+
4833 BEAST_EXPECT(
+
4834 it->second.first == XTS(10.0002) && it->second.second == XXX(30));
+
4835
+
4836 // fourth offer
+
4837 // exact TakerPays is XTS(1/.033333)
+
4838 ++it;
+
4839 BEAST_EXPECT(it != offers.end());
+
4840 BEAST_EXPECT(
+
4841 it->second.first == XTS(30) && it->second.second == XXX(10));
+
4842
+
4843 BEAST_EXPECT(++it == offers.end());
+
4844 }
+
4845
+
4846 // Helper function that returns offers on an account sorted by sequence.
+
4847 static std::vector<std::shared_ptr<SLE const>>
+
4848 sortedOffersOnAccount(jtx::Env& env, jtx::Account const& acct)
+
4849 {
+
4850 std::vector<std::shared_ptr<SLE const>> offers{
+
4851 offersOnAccount(env, acct)};
+
4852 std::sort(
+
4853 offers.begin(),
+
4854 offers.end(),
+
4855 [](std::shared_ptr<SLE const> const& rhs,
+
4856 std::shared_ptr<SLE const> const& lhs) {
+
4857 return (*rhs)[sfSequence] < (*lhs)[sfSequence];
+
4858 });
+
4859 return offers;
+
4860 }
+
4861
+
4862 void
+
4863 testTicketOffer(FeatureBitset features)
+
4864 {
+
4865 testcase("Ticket Offers");
+
4866
+
4867 using namespace jtx;
+
4868
+
4869 // Two goals for this test.
+
4870 //
+
4871 // o Verify that offers can be created using tickets.
+
4872 //
+
4873 // o Show that offers in the _same_ order book remain in
+
4874 // chronological order regardless of sequence/ticket numbers.
+
4875 Env env{*this, features};
+
4876 auto const gw = Account{"gateway"};
+
4877 auto const alice = Account{"alice"};
+
4878 auto const bob = Account{"bob"};
+
4879 auto const USD = gw["USD"];
+
4880
+
4881 env.fund(XRP(10000), gw, alice, bob);
+
4882 env.close();
+
4883
+
4884 env(trust(alice, USD(1000)));
+
4885 env(trust(bob, USD(1000)));
+
4886 env.close();
4887
-
4888 // Helper function that returns offers on an account sorted by sequence.
-
4889 static std::vector<std::shared_ptr<SLE const>>
-
4890 sortedOffersOnAccount(jtx::Env& env, jtx::Account const& acct)
-
4891 {
-
4892 std::vector<std::shared_ptr<SLE const>> offers{
-
4893 offersOnAccount(env, acct)};
-
4894 std::sort(
-
4895 offers.begin(),
-
4896 offers.end(),
-
4897 [](std::shared_ptr<SLE const> const& rhs,
-
4898 std::shared_ptr<SLE const> const& lhs) {
-
4899 return (*rhs)[sfSequence] < (*lhs)[sfSequence];
-
4900 });
-
4901 return offers;
-
4902 }
-
4903
-
4904 void
-
4905 testTicketOffer(FeatureBitset features)
-
4906 {
-
4907 testcase("Ticket Offers");
+
4888 env(pay(gw, alice, USD(200)));
+
4889 env.close();
+
4890
+
4891 // Create four offers from the same account with identical quality
+
4892 // so they go in the same order book. Each offer goes in a different
+
4893 // ledger so the chronology is clear.
+
4894 std::uint32_t const offerId_0{env.seq(alice)};
+
4895 env(offer(alice, XRP(50), USD(50)));
+
4896 env.close();
+
4897
+
4898 // Create two tickets.
+
4899 std::uint32_t const ticketSeq{env.seq(alice) + 1};
+
4900 env(ticket::create(alice, 2));
+
4901 env.close();
+
4902
+
4903 // Create another sequence-based offer.
+
4904 std::uint32_t const offerId_1{env.seq(alice)};
+
4905 BEAST_EXPECT(offerId_1 == offerId_0 + 4);
+
4906 env(offer(alice, XRP(50), USD(50)));
+
4907 env.close();
4908
-
4909 using namespace jtx;
-
4910
-
4911 // Two goals for this test.
-
4912 //
-
4913 // o Verify that offers can be created using tickets.
-
4914 //
-
4915 // o Show that offers in the _same_ order book remain in
-
4916 // chronological order regardless of sequence/ticket numbers.
-
4917 Env env{*this, features};
-
4918 auto const gw = Account{"gateway"};
-
4919 auto const alice = Account{"alice"};
-
4920 auto const bob = Account{"bob"};
-
4921 auto const USD = gw["USD"];
-
4922
-
4923 env.fund(XRP(10000), gw, alice, bob);
-
4924 env.close();
-
4925
-
4926 env(trust(alice, USD(1000)));
-
4927 env(trust(bob, USD(1000)));
-
4928 env.close();
-
4929
-
4930 env(pay(gw, alice, USD(200)));
-
4931 env.close();
-
4932
-
4933 // Create four offers from the same account with identical quality
-
4934 // so they go in the same order book. Each offer goes in a different
-
4935 // ledger so the chronology is clear.
-
4936 std::uint32_t const offerId_0{env.seq(alice)};
-
4937 env(offer(alice, XRP(50), USD(50)));
-
4938 env.close();
-
4939
-
4940 // Create two tickets.
-
4941 std::uint32_t const ticketSeq{env.seq(alice) + 1};
-
4942 env(ticket::create(alice, 2));
-
4943 env.close();
-
4944
-
4945 // Create another sequence-based offer.
-
4946 std::uint32_t const offerId_1{env.seq(alice)};
-
4947 BEAST_EXPECT(offerId_1 == offerId_0 + 4);
-
4948 env(offer(alice, XRP(50), USD(50)));
-
4949 env.close();
-
4950
-
4951 // Create two ticket based offers in reverse order.
-
4952 std::uint32_t const offerId_2{ticketSeq + 1};
-
4953 env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_2));
-
4954 env.close();
+
4909 // Create two ticket based offers in reverse order.
+
4910 std::uint32_t const offerId_2{ticketSeq + 1};
+
4911 env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_2));
+
4912 env.close();
+
4913
+
4914 // Create the last offer.
+
4915 std::uint32_t const offerId_3{ticketSeq};
+
4916 env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_3));
+
4917 env.close();
+
4918
+
4919 // Verify that all of alice's offers are present.
+
4920 {
+
4921 auto offers = sortedOffersOnAccount(env, alice);
+
4922 BEAST_EXPECT(offers.size() == 4);
+
4923 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_0);
+
4924 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_3);
+
4925 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_2);
+
4926 BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerId_1);
+
4927 env.require(balance(alice, USD(200)));
+
4928 env.require(owners(alice, 5));
+
4929 }
+
4930
+
4931 // Cross alice's first offer.
+
4932 env(offer(bob, USD(50), XRP(50)));
+
4933 env.close();
+
4934
+
4935 // Verify that the first offer alice created was consumed.
+
4936 {
+
4937 auto offers = sortedOffersOnAccount(env, alice);
+
4938 BEAST_EXPECT(offers.size() == 3);
+
4939 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
+
4940 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
+
4941 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_1);
+
4942 }
+
4943
+
4944 // Cross alice's second offer.
+
4945 env(offer(bob, USD(50), XRP(50)));
+
4946 env.close();
+
4947
+
4948 // Verify that the second offer alice created was consumed.
+
4949 {
+
4950 auto offers = sortedOffersOnAccount(env, alice);
+
4951 BEAST_EXPECT(offers.size() == 2);
+
4952 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
+
4953 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
+
4954 }
4955
-
4956 // Create the last offer.
-
4957 std::uint32_t const offerId_3{ticketSeq};
-
4958 env(offer(alice, XRP(50), USD(50)), ticket::use(offerId_3));
-
4959 env.close();
-
4960
-
4961 // Verify that all of alice's offers are present.
-
4962 {
-
4963 auto offers = sortedOffersOnAccount(env, alice);
-
4964 BEAST_EXPECT(offers.size() == 4);
-
4965 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_0);
-
4966 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_3);
-
4967 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_2);
-
4968 BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerId_1);
-
4969 env.require(balance(alice, USD(200)));
-
4970 env.require(owners(alice, 5));
-
4971 }
-
4972
-
4973 // Cross alice's first offer.
-
4974 env(offer(bob, USD(50), XRP(50)));
-
4975 env.close();
-
4976
-
4977 // Verify that the first offer alice created was consumed.
-
4978 {
-
4979 auto offers = sortedOffersOnAccount(env, alice);
-
4980 BEAST_EXPECT(offers.size() == 3);
-
4981 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
-
4982 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
-
4983 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerId_1);
-
4984 }
-
4985
-
4986 // Cross alice's second offer.
-
4987 env(offer(bob, USD(50), XRP(50)));
-
4988 env.close();
-
4989
-
4990 // Verify that the second offer alice created was consumed.
-
4991 {
-
4992 auto offers = sortedOffersOnAccount(env, alice);
-
4993 BEAST_EXPECT(offers.size() == 2);
-
4994 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
-
4995 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerId_2);
-
4996 }
-
4997
-
4998 // Cross alice's third offer.
-
4999 env(offer(bob, USD(50), XRP(50)));
+
4956 // Cross alice's third offer.
+
4957 env(offer(bob, USD(50), XRP(50)));
+
4958 env.close();
+
4959
+
4960 // Verify that the third offer alice created was consumed.
+
4961 {
+
4962 auto offers = sortedOffersOnAccount(env, alice);
+
4963 BEAST_EXPECT(offers.size() == 1);
+
4964 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
+
4965 }
+
4966
+
4967 // Cross alice's last offer.
+
4968 env(offer(bob, USD(50), XRP(50)));
+
4969 env.close();
+
4970
+
4971 // Verify that the third offer alice created was consumed.
+
4972 {
+
4973 auto offers = sortedOffersOnAccount(env, alice);
+
4974 BEAST_EXPECT(offers.size() == 0);
+
4975 }
+
4976 env.require(balance(alice, USD(0)));
+
4977 env.require(owners(alice, 1));
+
4978 env.require(balance(bob, USD(200)));
+
4979 env.require(owners(bob, 1));
+
4980 }
+
4981
+
4982 void
+
4983 testTicketCancelOffer(FeatureBitset features)
+
4984 {
+
4985 testcase("Ticket Cancel Offers");
+
4986
+
4987 using namespace jtx;
+
4988
+
4989 // Verify that offers created with or without tickets can be canceled
+
4990 // by transactions with or without tickets.
+
4991 Env env{*this, features};
+
4992 auto const gw = Account{"gateway"};
+
4993 auto const alice = Account{"alice"};
+
4994 auto const USD = gw["USD"];
+
4995
+
4996 env.fund(XRP(10000), gw, alice);
+
4997 env.close();
+
4998
+
4999 env(trust(alice, USD(1000)));
5000 env.close();
-
5001
-
5002 // Verify that the third offer alice created was consumed.
-
5003 {
-
5004 auto offers = sortedOffersOnAccount(env, alice);
-
5005 BEAST_EXPECT(offers.size() == 1);
-
5006 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerId_3);
-
5007 }
-
5008
-
5009 // Cross alice's last offer.
-
5010 env(offer(bob, USD(50), XRP(50)));
-
5011 env.close();
-
5012
-
5013 // Verify that the third offer alice created was consumed.
-
5014 {
-
5015 auto offers = sortedOffersOnAccount(env, alice);
-
5016 BEAST_EXPECT(offers.size() == 0);
-
5017 }
-
5018 env.require(balance(alice, USD(0)));
-
5019 env.require(owners(alice, 1));
-
5020 env.require(balance(bob, USD(200)));
-
5021 env.require(owners(bob, 1));
-
5022 }
+
5001 env.require(owners(alice, 1), tickets(alice, 0));
+
5002
+
5003 env(pay(gw, alice, USD(200)));
+
5004 env.close();
+
5005
+
5006 // Create the first of four offers using a sequence.
+
5007 std::uint32_t const offerSeqId_0{env.seq(alice)};
+
5008 env(offer(alice, XRP(50), USD(50)));
+
5009 env.close();
+
5010 env.require(owners(alice, 2), tickets(alice, 0));
+
5011
+
5012 // Create four tickets.
+
5013 std::uint32_t const ticketSeq{env.seq(alice) + 1};
+
5014 env(ticket::create(alice, 4));
+
5015 env.close();
+
5016 env.require(owners(alice, 6), tickets(alice, 4));
+
5017
+
5018 // Create the second (also sequence-based) offer.
+
5019 std::uint32_t const offerSeqId_1{env.seq(alice)};
+
5020 BEAST_EXPECT(offerSeqId_1 == offerSeqId_0 + 6);
+
5021 env(offer(alice, XRP(50), USD(50)));
+
5022 env.close();
5023
-
5024 void
-
5025 testTicketCancelOffer(FeatureBitset features)
-
5026 {
-
5027 testcase("Ticket Cancel Offers");
+
5024 // Create the third (ticket-based) offer.
+
5025 std::uint32_t const offerTixId_0{ticketSeq + 1};
+
5026 env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_0));
+
5027 env.close();
5028
-
5029 using namespace jtx;
-
5030
-
5031 // Verify that offers created with or without tickets can be canceled
-
5032 // by transactions with or without tickets.
-
5033 Env env{*this, features};
-
5034 auto const gw = Account{"gateway"};
-
5035 auto const alice = Account{"alice"};
-
5036 auto const USD = gw["USD"];
-
5037
-
5038 env.fund(XRP(10000), gw, alice);
-
5039 env.close();
-
5040
-
5041 env(trust(alice, USD(1000)));
-
5042 env.close();
-
5043 env.require(owners(alice, 1), tickets(alice, 0));
-
5044
-
5045 env(pay(gw, alice, USD(200)));
-
5046 env.close();
-
5047
-
5048 // Create the first of four offers using a sequence.
-
5049 std::uint32_t const offerSeqId_0{env.seq(alice)};
-
5050 env(offer(alice, XRP(50), USD(50)));
-
5051 env.close();
-
5052 env.require(owners(alice, 2), tickets(alice, 0));
-
5053
-
5054 // Create four tickets.
-
5055 std::uint32_t const ticketSeq{env.seq(alice) + 1};
-
5056 env(ticket::create(alice, 4));
-
5057 env.close();
-
5058 env.require(owners(alice, 6), tickets(alice, 4));
-
5059
-
5060 // Create the second (also sequence-based) offer.
-
5061 std::uint32_t const offerSeqId_1{env.seq(alice)};
-
5062 BEAST_EXPECT(offerSeqId_1 == offerSeqId_0 + 6);
-
5063 env(offer(alice, XRP(50), USD(50)));
-
5064 env.close();
-
5065
-
5066 // Create the third (ticket-based) offer.
-
5067 std::uint32_t const offerTixId_0{ticketSeq + 1};
-
5068 env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_0));
-
5069 env.close();
+
5029 // Create the last offer.
+
5030 std::uint32_t const offerTixId_1{ticketSeq};
+
5031 env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_1));
+
5032 env.close();
+
5033
+
5034 // Verify that all of alice's offers are present.
+
5035 {
+
5036 auto offers = sortedOffersOnAccount(env, alice);
+
5037 BEAST_EXPECT(offers.size() == 4);
+
5038 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_0);
+
5039 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_1);
+
5040 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerTixId_0);
+
5041 BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerSeqId_1);
+
5042 env.require(balance(alice, USD(200)));
+
5043 env.require(owners(alice, 7));
+
5044 }
+
5045
+
5046 // Use a ticket to cancel an offer created with a sequence.
+
5047 env(offer_cancel(alice, offerSeqId_0), ticket::use(ticketSeq + 2));
+
5048 env.close();
+
5049
+
5050 // Verify that offerSeqId_0 was canceled.
+
5051 {
+
5052 auto offers = sortedOffersOnAccount(env, alice);
+
5053 BEAST_EXPECT(offers.size() == 3);
+
5054 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
+
5055 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_0);
+
5056 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerSeqId_1);
+
5057 }
+
5058
+
5059 // Use a ticket to cancel an offer created with a ticket.
+
5060 env(offer_cancel(alice, offerTixId_0), ticket::use(ticketSeq + 3));
+
5061 env.close();
+
5062
+
5063 // Verify that offerTixId_0 was canceled.
+
5064 {
+
5065 auto offers = sortedOffersOnAccount(env, alice);
+
5066 BEAST_EXPECT(offers.size() == 2);
+
5067 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
+
5068 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerSeqId_1);
+
5069 }
5070
-
5071 // Create the last offer.
-
5072 std::uint32_t const offerTixId_1{ticketSeq};
-
5073 env(offer(alice, XRP(50), USD(50)), ticket::use(offerTixId_1));
-
5074 env.close();
-
5075
-
5076 // Verify that all of alice's offers are present.
-
5077 {
-
5078 auto offers = sortedOffersOnAccount(env, alice);
-
5079 BEAST_EXPECT(offers.size() == 4);
-
5080 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_0);
-
5081 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_1);
-
5082 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerTixId_0);
-
5083 BEAST_EXPECT(offers[3]->getFieldU32(sfSequence) == offerSeqId_1);
-
5084 env.require(balance(alice, USD(200)));
-
5085 env.require(owners(alice, 7));
-
5086 }
-
5087
-
5088 // Use a ticket to cancel an offer created with a sequence.
-
5089 env(offer_cancel(alice, offerSeqId_0), ticket::use(ticketSeq + 2));
-
5090 env.close();
-
5091
-
5092 // Verify that offerSeqId_0 was canceled.
-
5093 {
-
5094 auto offers = sortedOffersOnAccount(env, alice);
-
5095 BEAST_EXPECT(offers.size() == 3);
-
5096 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
-
5097 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerTixId_0);
-
5098 BEAST_EXPECT(offers[2]->getFieldU32(sfSequence) == offerSeqId_1);
-
5099 }
-
5100
-
5101 // Use a ticket to cancel an offer created with a ticket.
-
5102 env(offer_cancel(alice, offerTixId_0), ticket::use(ticketSeq + 3));
-
5103 env.close();
-
5104
-
5105 // Verify that offerTixId_0 was canceled.
-
5106 {
-
5107 auto offers = sortedOffersOnAccount(env, alice);
-
5108 BEAST_EXPECT(offers.size() == 2);
-
5109 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerTixId_1);
-
5110 BEAST_EXPECT(offers[1]->getFieldU32(sfSequence) == offerSeqId_1);
-
5111 }
-
5112
-
5113 // All of alice's tickets should now be used up.
-
5114 env.require(owners(alice, 3), tickets(alice, 0));
-
5115
-
5116 // Use a sequence to cancel an offer created with a ticket.
-
5117 env(offer_cancel(alice, offerTixId_1));
-
5118 env.close();
-
5119
-
5120 // Verify that offerTixId_1 was canceled.
-
5121 {
-
5122 auto offers = sortedOffersOnAccount(env, alice);
-
5123 BEAST_EXPECT(offers.size() == 1);
-
5124 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_1);
-
5125 }
-
5126
-
5127 // Use a sequence to cancel an offer created with a sequence.
-
5128 env(offer_cancel(alice, offerSeqId_1));
-
5129 env.close();
-
5130
-
5131 // Verify that offerSeqId_1 was canceled.
-
5132 // All of alice's tickets should now be used up.
-
5133 env.require(owners(alice, 1), tickets(alice, 0), offers(alice, 0));
-
5134 }
-
5135
-
5136 void
-
5137 testFalseAssert()
-
5138 {
-
5139 // An assert was falsely triggering when computing rates for offers.
-
5140 // This unit test would trigger that assert (which has been removed).
-
5141 testcase("incorrect assert fixed");
-
5142 using namespace jtx;
+
5071 // All of alice's tickets should now be used up.
+
5072 env.require(owners(alice, 3), tickets(alice, 0));
+
5073
+
5074 // Use a sequence to cancel an offer created with a ticket.
+
5075 env(offer_cancel(alice, offerTixId_1));
+
5076 env.close();
+
5077
+
5078 // Verify that offerTixId_1 was canceled.
+
5079 {
+
5080 auto offers = sortedOffersOnAccount(env, alice);
+
5081 BEAST_EXPECT(offers.size() == 1);
+
5082 BEAST_EXPECT(offers[0]->getFieldU32(sfSequence) == offerSeqId_1);
+
5083 }
+
5084
+
5085 // Use a sequence to cancel an offer created with a sequence.
+
5086 env(offer_cancel(alice, offerSeqId_1));
+
5087 env.close();
+
5088
+
5089 // Verify that offerSeqId_1 was canceled.
+
5090 // All of alice's tickets should now be used up.
+
5091 env.require(owners(alice, 1), tickets(alice, 0), offers(alice, 0));
+
5092 }
+
5093
+
5094 void
+
5095 testFalseAssert()
+
5096 {
+
5097 // An assert was falsely triggering when computing rates for offers.
+
5098 // This unit test would trigger that assert (which has been removed).
+
5099 testcase("incorrect assert fixed");
+
5100 using namespace jtx;
+
5101
+
5102 Env env{*this};
+
5103 auto const alice = Account("alice");
+
5104 auto const USD = alice["USD"];
+
5105
+
5106 env.fund(XRP(10000), alice);
+
5107 env.close();
+
5108 env(offer(alice, XRP(100000000000), USD(100000000)));
+
5109 pass();
+
5110 }
+
5111
+
5112 void
+
5113 testFillOrKill(FeatureBitset features)
+
5114 {
+
5115 testcase("fixFillOrKill");
+
5116 using namespace jtx;
+
5117 Env env(*this, features);
+
5118 Account const issuer("issuer");
+
5119 Account const maker("maker");
+
5120 Account const taker("taker");
+
5121 auto const USD = issuer["USD"];
+
5122 auto const EUR = issuer["EUR"];
+
5123
+
5124 env.fund(XRP(1'000), issuer);
+
5125 env.fund(XRP(1'000), maker, taker);
+
5126 env.close();
+
5127
+
5128 env.trust(USD(1'000), maker, taker);
+
5129 env.trust(EUR(1'000), maker, taker);
+
5130 env.close();
+
5131
+
5132 env(pay(issuer, maker, USD(1'000)));
+
5133 env(pay(issuer, taker, USD(1'000)));
+
5134 env(pay(issuer, maker, EUR(1'000)));
+
5135 env.close();
+
5136
+
5137 auto makerUSDBalance = env.balance(maker, USD).value();
+
5138 auto takerUSDBalance = env.balance(taker, USD).value();
+
5139 auto makerEURBalance = env.balance(maker, EUR).value();
+
5140 auto takerEURBalance = env.balance(taker, EUR).value();
+
5141 auto makerXRPBalance = env.balance(maker, XRP).value();
+
5142 auto takerXRPBalance = env.balance(taker, XRP).value();
5143
-
5144 Env env{*this};
-
5145 auto const alice = Account("alice");
-
5146 auto const USD = alice["USD"];
-
5147
-
5148 env.fund(XRP(10000), alice);
-
5149 env.close();
-
5150 env(offer(alice, XRP(100000000000), USD(100000000)));
-
5151 pass();
-
5152 }
-
5153
-
5154 void
-
5155 testFillOrKill(FeatureBitset features)
-
5156 {
-
5157 testcase("fixFillOrKill");
-
5158 using namespace jtx;
-
5159 Env env(*this, features);
-
5160 Account const issuer("issuer");
-
5161 Account const maker("maker");
-
5162 Account const taker("taker");
-
5163 auto const USD = issuer["USD"];
-
5164 auto const EUR = issuer["EUR"];
-
5165
-
5166 env.fund(XRP(1'000), issuer);
-
5167 env.fund(XRP(1'000), maker, taker);
-
5168 env.close();
-
5169
-
5170 env.trust(USD(1'000), maker, taker);
-
5171 env.trust(EUR(1'000), maker, taker);
-
5172 env.close();
-
5173
-
5174 env(pay(issuer, maker, USD(1'000)));
-
5175 env(pay(issuer, taker, USD(1'000)));
-
5176 env(pay(issuer, maker, EUR(1'000)));
-
5177 env.close();
-
5178
-
5179 auto makerUSDBalance = env.balance(maker, USD).value();
-
5180 auto takerUSDBalance = env.balance(taker, USD).value();
-
5181 auto makerEURBalance = env.balance(maker, EUR).value();
-
5182 auto takerEURBalance = env.balance(taker, EUR).value();
-
5183 auto makerXRPBalance = env.balance(maker, XRP).value();
-
5184 auto takerXRPBalance = env.balance(taker, XRP).value();
-
5185
-
5186 // tfFillOrKill, TakerPays must be filled
-
5187 {
-
5188 TER const err =
-
5189 features[fixFillOrKill] || !features[featureFlowCross]
-
5190 ? TER(tesSUCCESS)
-
5191 : tecKILLED;
-
5192
-
5193 env(offer(maker, XRP(100), USD(100)));
-
5194 env.close();
-
5195
-
5196 env(offer(taker, USD(100), XRP(101)),
-
5197 txflags(tfFillOrKill),
-
5198 ter(err));
-
5199 env.close();
-
5200
-
5201 makerXRPBalance -= txfee(env, 1);
-
5202 takerXRPBalance -= txfee(env, 1);
-
5203 if (err == tesSUCCESS)
-
5204 {
-
5205 makerUSDBalance -= USD(100);
-
5206 takerUSDBalance += USD(100);
-
5207 makerXRPBalance += XRP(100).value();
-
5208 takerXRPBalance -= XRP(100).value();
-
5209 }
-
5210 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5144 // tfFillOrKill, TakerPays must be filled
+
5145 {
+
5146 TER const err =
+
5147 features[fixFillOrKill] ? TER(tesSUCCESS) : tecKILLED;
+
5148
+
5149 env(offer(maker, XRP(100), USD(100)));
+
5150 env.close();
+
5151
+
5152 env(offer(taker, USD(100), XRP(101)),
+
5153 txflags(tfFillOrKill),
+
5154 ter(err));
+
5155 env.close();
+
5156
+
5157 makerXRPBalance -= txfee(env, 1);
+
5158 takerXRPBalance -= txfee(env, 1);
+
5159 if (err == tesSUCCESS)
+
5160 {
+
5161 makerUSDBalance -= USD(100);
+
5162 takerUSDBalance += USD(100);
+
5163 makerXRPBalance += XRP(100).value();
+
5164 takerXRPBalance -= XRP(100).value();
+
5165 }
+
5166 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5167
+
5168 env(offer(maker, USD(100), XRP(100)));
+
5169 env.close();
+
5170
+
5171 env(offer(taker, XRP(100), USD(101)),
+
5172 txflags(tfFillOrKill),
+
5173 ter(err));
+
5174 env.close();
+
5175
+
5176 makerXRPBalance -= txfee(env, 1);
+
5177 takerXRPBalance -= txfee(env, 1);
+
5178 if (err == tesSUCCESS)
+
5179 {
+
5180 makerUSDBalance += USD(100);
+
5181 takerUSDBalance -= USD(100);
+
5182 makerXRPBalance -= XRP(100).value();
+
5183 takerXRPBalance += XRP(100).value();
+
5184 }
+
5185 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5186
+
5187 env(offer(maker, USD(100), EUR(100)));
+
5188 env.close();
+
5189
+
5190 env(offer(taker, EUR(100), USD(101)),
+
5191 txflags(tfFillOrKill),
+
5192 ter(err));
+
5193 env.close();
+
5194
+
5195 makerXRPBalance -= txfee(env, 1);
+
5196 takerXRPBalance -= txfee(env, 1);
+
5197 if (err == tesSUCCESS)
+
5198 {
+
5199 makerUSDBalance += USD(100);
+
5200 takerUSDBalance -= USD(100);
+
5201 makerEURBalance -= EUR(100);
+
5202 takerEURBalance += EUR(100);
+
5203 }
+
5204 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5205 }
+
5206
+
5207 // tfFillOrKill + tfSell, TakerGets must be filled
+
5208 {
+
5209 env(offer(maker, XRP(101), USD(101)));
+
5210 env.close();
5211
-
5212 env(offer(maker, USD(100), XRP(100)));
-
5213 env.close();
-
5214
-
5215 env(offer(taker, XRP(100), USD(101)),
-
5216 txflags(tfFillOrKill),
-
5217 ter(err));
-
5218 env.close();
-
5219
-
5220 makerXRPBalance -= txfee(env, 1);
-
5221 takerXRPBalance -= txfee(env, 1);
-
5222 if (err == tesSUCCESS)
-
5223 {
-
5224 makerUSDBalance += USD(100);
-
5225 takerUSDBalance -= USD(100);
-
5226 makerXRPBalance -= XRP(100).value();
-
5227 takerXRPBalance += XRP(100).value();
-
5228 }
-
5229 BEAST_EXPECT(expectOffers(env, taker, 0));
-
5230
-
5231 env(offer(maker, USD(100), EUR(100)));
-
5232 env.close();
-
5233
-
5234 env(offer(taker, EUR(100), USD(101)),
-
5235 txflags(tfFillOrKill),
-
5236 ter(err));
-
5237 env.close();
-
5238
-
5239 makerXRPBalance -= txfee(env, 1);
-
5240 takerXRPBalance -= txfee(env, 1);
-
5241 if (err == tesSUCCESS)
-
5242 {
-
5243 makerUSDBalance += USD(100);
-
5244 takerUSDBalance -= USD(100);
-
5245 makerEURBalance -= EUR(100);
-
5246 takerEURBalance += EUR(100);
-
5247 }
+
5212 env(offer(taker, USD(100), XRP(101)),
+
5213 txflags(tfFillOrKill | tfSell));
+
5214 env.close();
+
5215
+
5216 makerUSDBalance -= USD(101);
+
5217 takerUSDBalance += USD(101);
+
5218 makerXRPBalance += XRP(101).value() - txfee(env, 1);
+
5219 takerXRPBalance -= XRP(101).value() + txfee(env, 1);
+
5220 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5221
+
5222 env(offer(maker, USD(101), XRP(101)));
+
5223 env.close();
+
5224
+
5225 env(offer(taker, XRP(100), USD(101)),
+
5226 txflags(tfFillOrKill | tfSell));
+
5227 env.close();
+
5228
+
5229 makerUSDBalance += USD(101);
+
5230 takerUSDBalance -= USD(101);
+
5231 makerXRPBalance -= XRP(101).value() + txfee(env, 1);
+
5232 takerXRPBalance += XRP(101).value() - txfee(env, 1);
+
5233 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5234
+
5235 env(offer(maker, USD(101), EUR(101)));
+
5236 env.close();
+
5237
+
5238 env(offer(taker, EUR(100), USD(101)),
+
5239 txflags(tfFillOrKill | tfSell));
+
5240 env.close();
+
5241
+
5242 makerUSDBalance += USD(101);
+
5243 takerUSDBalance -= USD(101);
+
5244 makerEURBalance -= EUR(101);
+
5245 takerEURBalance += EUR(101);
+
5246 makerXRPBalance -= txfee(env, 1);
+
5247 takerXRPBalance -= txfee(env, 1);
5248 BEAST_EXPECT(expectOffers(env, taker, 0));
5249 }
5250
-
5251 // tfFillOrKill + tfSell, TakerGets must be filled
-
5252 {
-
5253 env(offer(maker, XRP(101), USD(101)));
-
5254 env.close();
-
5255
-
5256 env(offer(taker, USD(100), XRP(101)),
-
5257 txflags(tfFillOrKill | tfSell));
-
5258 env.close();
-
5259
-
5260 makerUSDBalance -= USD(101);
-
5261 takerUSDBalance += USD(101);
-
5262 makerXRPBalance += XRP(101).value() - txfee(env, 1);
-
5263 takerXRPBalance -= XRP(101).value() + txfee(env, 1);
+
5251 // Fail regardless of fixFillOrKill amendment
+
5252 for (auto const flags : {tfFillOrKill, tfFillOrKill + tfSell})
+
5253 {
+
5254 env(offer(maker, XRP(100), USD(100)));
+
5255 env.close();
+
5256
+
5257 env(offer(taker, USD(100), XRP(99)),
+
5258 txflags(flags),
+
5259 ter(tecKILLED));
+
5260 env.close();
+
5261
+
5262 makerXRPBalance -= txfee(env, 1);
+
5263 takerXRPBalance -= txfee(env, 1);
5264 BEAST_EXPECT(expectOffers(env, taker, 0));
5265
-
5266 env(offer(maker, USD(101), XRP(101)));
+
5266 env(offer(maker, USD(100), XRP(100)));
5267 env.close();
5268
-
5269 env(offer(taker, XRP(100), USD(101)),
-
5270 txflags(tfFillOrKill | tfSell));
-
5271 env.close();
-
5272
-
5273 makerUSDBalance += USD(101);
-
5274 takerUSDBalance -= USD(101);
-
5275 makerXRPBalance -= XRP(101).value() + txfee(env, 1);
-
5276 takerXRPBalance += XRP(101).value() - txfee(env, 1);
-
5277 BEAST_EXPECT(expectOffers(env, taker, 0));
-
5278
-
5279 env(offer(maker, USD(101), EUR(101)));
-
5280 env.close();
-
5281
-
5282 env(offer(taker, EUR(100), USD(101)),
-
5283 txflags(tfFillOrKill | tfSell));
+
5269 env(offer(taker, XRP(100), USD(99)),
+
5270 txflags(flags),
+
5271 ter(tecKILLED));
+
5272 env.close();
+
5273
+
5274 makerXRPBalance -= txfee(env, 1);
+
5275 takerXRPBalance -= txfee(env, 1);
+
5276 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5277
+
5278 env(offer(maker, USD(100), EUR(100)));
+
5279 env.close();
+
5280
+
5281 env(offer(taker, EUR(100), USD(99)),
+
5282 txflags(flags),
+
5283 ter(tecKILLED));
5284 env.close();
5285
-
5286 makerUSDBalance += USD(101);
-
5287 takerUSDBalance -= USD(101);
-
5288 makerEURBalance -= EUR(101);
-
5289 takerEURBalance += EUR(101);
-
5290 makerXRPBalance -= txfee(env, 1);
-
5291 takerXRPBalance -= txfee(env, 1);
-
5292 BEAST_EXPECT(expectOffers(env, taker, 0));
-
5293 }
-
5294
-
5295 // Fail regardless of fixFillOrKill amendment
-
5296 for (auto const flags : {tfFillOrKill, tfFillOrKill + tfSell})
-
5297 {
-
5298 env(offer(maker, XRP(100), USD(100)));
-
5299 env.close();
-
5300
-
5301 env(offer(taker, USD(100), XRP(99)),
-
5302 txflags(flags),
-
5303 ter(tecKILLED));
-
5304 env.close();
-
5305
-
5306 makerXRPBalance -= txfee(env, 1);
-
5307 takerXRPBalance -= txfee(env, 1);
-
5308 BEAST_EXPECT(expectOffers(env, taker, 0));
-
5309
-
5310 env(offer(maker, USD(100), XRP(100)));
-
5311 env.close();
-
5312
-
5313 env(offer(taker, XRP(100), USD(99)),
-
5314 txflags(flags),
-
5315 ter(tecKILLED));
-
5316 env.close();
-
5317
-
5318 makerXRPBalance -= txfee(env, 1);
-
5319 takerXRPBalance -= txfee(env, 1);
-
5320 BEAST_EXPECT(expectOffers(env, taker, 0));
-
5321
-
5322 env(offer(maker, USD(100), EUR(100)));
-
5323 env.close();
-
5324
-
5325 env(offer(taker, EUR(100), USD(99)),
-
5326 txflags(flags),
-
5327 ter(tecKILLED));
-
5328 env.close();
-
5329
-
5330 makerXRPBalance -= txfee(env, 1);
-
5331 takerXRPBalance -= txfee(env, 1);
-
5332 BEAST_EXPECT(expectOffers(env, taker, 0));
-
5333 }
-
5334
-
5335 BEAST_EXPECT(
-
5336 env.balance(maker, USD) == makerUSDBalance &&
-
5337 env.balance(taker, USD) == takerUSDBalance &&
-
5338 env.balance(maker, EUR) == makerEURBalance &&
-
5339 env.balance(taker, EUR) == takerEURBalance &&
-
5340 env.balance(maker, XRP) == makerXRPBalance &&
-
5341 env.balance(taker, XRP) == takerXRPBalance);
-
5342 }
-
5343
-
5344 void
-
5345 testAll(FeatureBitset features)
-
5346 {
-
5347 testCanceledOffer(features);
-
5348 testRmFundedOffer(features);
-
5349 testTinyPayment(features);
-
5350 testXRPTinyPayment(features);
-
5351 testEnforceNoRipple(features);
-
5352 testInsufficientReserve(features);
-
5353 testFillModes(features);
-
5354 testMalformed(features);
-
5355 testExpiration(features);
-
5356 testUnfundedCross(features);
-
5357 testSelfCross(false, features);
-
5358 testSelfCross(true, features);
-
5359 testNegativeBalance(features);
-
5360 testOfferCrossWithXRP(true, features);
-
5361 testOfferCrossWithXRP(false, features);
-
5362 testOfferCrossWithLimitOverride(features);
-
5363 testOfferAcceptThenCancel(features);
-
5364 testOfferCancelPastAndFuture(features);
-
5365 testCurrencyConversionEntire(features);
-
5366 testCurrencyConversionIntoDebt(features);
-
5367 testCurrencyConversionInParts(features);
-
5368 testCrossCurrencyStartXRP(features);
-
5369 testCrossCurrencyEndXRP(features);
-
5370 testCrossCurrencyBridged(features);
-
5371 testBridgedSecondLegDry(features);
-
5372 testOfferFeesConsumeFunds(features);
-
5373 testOfferCreateThenCross(features);
-
5374 testSellFlagBasic(features);
-
5375 testSellFlagExceedLimit(features);
-
5376 testGatewayCrossCurrency(features);
-
5377 testPartialCross(features);
-
5378 testXRPDirectCross(features);
-
5379 testDirectCross(features);
-
5380 testBridgedCross(features);
-
5381 testSellOffer(features);
-
5382 testSellWithFillOrKill(features);
-
5383 testTransferRateOffer(features);
-
5384 testSelfCrossOffer(features);
-
5385 testSelfIssueOffer(features);
-
5386 testBadPathAssert(features);
-
5387 testDirectToDirectPath(features);
-
5388 testSelfCrossLowQualityOffer(features);
-
5389 testOfferInScaling(features);
-
5390 testOfferInScalingWithXferRate(features);
-
5391 testOfferThresholdWithReducedFunds(features);
-
5392 testTinyOffer(features);
-
5393 testSelfPayXferFeeOffer(features);
-
5394 testSelfPayUnlimitedFunds(features);
-
5395 testRequireAuth(features);
-
5396 testMissingAuth(features);
-
5397 testRCSmoketest(features);
-
5398 testSelfAuth(features);
-
5399 testDeletedOfferIssuer(features);
-
5400 testTickSize(features);
-
5401 testTicketOffer(features);
-
5402 testTicketCancelOffer(features);
-
5403 testRmSmallIncreasedQOffersXRP(features);
-
5404 testRmSmallIncreasedQOffersIOU(features);
-
5405 testFillOrKill(features);
-
5406 }
-
5407
-
5408 void
-
5409 run(std::uint32_t instance, bool last = false)
-
5410 {
-
5411 using namespace jtx;
-
5412 static FeatureBitset const all{supported_amendments()};
-
5413 static FeatureBitset const flowCross{featureFlowCross};
-
5414 static FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
-
5415 static FeatureBitset const rmSmallIncreasedQOffers{
-
5416 fixRmSmallIncreasedQOffers};
-
5417 static FeatureBitset const immediateOfferKilled{
-
5418 featureImmediateOfferKilled};
-
5419 FeatureBitset const fillOrKill{fixFillOrKill};
-
5420 FeatureBitset const permDEX{featurePermissionedDEX};
-
5421
-
5422 static std::array<FeatureBitset, 7> const feats{
-
5423 all - takerDryOffer - immediateOfferKilled - permDEX,
-
5424 all - flowCross - takerDryOffer - immediateOfferKilled - permDEX,
-
5425 all - flowCross - immediateOfferKilled - permDEX,
-
5426 all - rmSmallIncreasedQOffers - immediateOfferKilled - fillOrKill -
-
5427 permDEX,
-
5428 all - fillOrKill - permDEX,
-
5429 all - permDEX,
-
5430 all};
-
5431
-
5432 if (BEAST_EXPECT(instance < feats.size()))
-
5433 {
-
5434 testAll(feats[instance]);
-
5435 }
-
5436 BEAST_EXPECT(!last || instance == feats.size() - 1);
-
5437 }
-
5438
+
5286 makerXRPBalance -= txfee(env, 1);
+
5287 takerXRPBalance -= txfee(env, 1);
+
5288 BEAST_EXPECT(expectOffers(env, taker, 0));
+
5289 }
+
5290
+
5291 BEAST_EXPECT(
+
5292 env.balance(maker, USD) == makerUSDBalance &&
+
5293 env.balance(taker, USD) == takerUSDBalance &&
+
5294 env.balance(maker, EUR) == makerEURBalance &&
+
5295 env.balance(taker, EUR) == takerEURBalance &&
+
5296 env.balance(maker, XRP) == makerXRPBalance &&
+
5297 env.balance(taker, XRP) == takerXRPBalance);
+
5298 }
+
5299
+
5300 void
+
5301 testAll(FeatureBitset features)
+
5302 {
+
5303 testCanceledOffer(features);
+
5304 testRmFundedOffer(features);
+
5305 testTinyPayment(features);
+
5306 testXRPTinyPayment(features);
+
5307 testEnforceNoRipple(features);
+
5308 testInsufficientReserve(features);
+
5309 testFillModes(features);
+
5310 testMalformed(features);
+
5311 testExpiration(features);
+
5312 testUnfundedCross(features);
+
5313 testSelfCross(false, features);
+
5314 testSelfCross(true, features);
+
5315 testNegativeBalance(features);
+
5316 testOfferCrossWithXRP(true, features);
+
5317 testOfferCrossWithXRP(false, features);
+
5318 testOfferCrossWithLimitOverride(features);
+
5319 testOfferAcceptThenCancel(features);
+
5320 testOfferCancelPastAndFuture(features);
+
5321 testCurrencyConversionEntire(features);
+
5322 testCurrencyConversionIntoDebt(features);
+
5323 testCurrencyConversionInParts(features);
+
5324 testCrossCurrencyStartXRP(features);
+
5325 testCrossCurrencyEndXRP(features);
+
5326 testCrossCurrencyBridged(features);
+
5327 testBridgedSecondLegDry(features);
+
5328 testOfferFeesConsumeFunds(features);
+
5329 testOfferCreateThenCross(features);
+
5330 testSellFlagBasic(features);
+
5331 testSellFlagExceedLimit(features);
+
5332 testGatewayCrossCurrency(features);
+
5333 testPartialCross(features);
+
5334 testXRPDirectCross(features);
+
5335 testDirectCross(features);
+
5336 testBridgedCross(features);
+
5337 testSellOffer(features);
+
5338 testSellWithFillOrKill(features);
+
5339 testTransferRateOffer(features);
+
5340 testSelfCrossOffer(features);
+
5341 testSelfIssueOffer(features);
+
5342 testBadPathAssert(features);
+
5343 testDirectToDirectPath(features);
+
5344 testSelfCrossLowQualityOffer(features);
+
5345 testOfferInScaling(features);
+
5346 testOfferInScalingWithXferRate(features);
+
5347 testOfferThresholdWithReducedFunds(features);
+
5348 testTinyOffer(features);
+
5349 testSelfPayXferFeeOffer(features);
+
5350 testSelfPayUnlimitedFunds(features);
+
5351 testRequireAuth(features);
+
5352 testMissingAuth(features);
+
5353 testRCSmoketest(features);
+
5354 testSelfAuth(features);
+
5355 testDeletedOfferIssuer(features);
+
5356 testTickSize(features);
+
5357 testTicketOffer(features);
+
5358 testTicketCancelOffer(features);
+
5359 testRmSmallIncreasedQOffersXRP(features);
+
5360 testRmSmallIncreasedQOffersIOU(features);
+
5361 testFillOrKill(features);
+
5362 }
+
5363
+
5364 void
+
5365 run(std::uint32_t instance, bool last = false)
+
5366 {
+
5367 using namespace jtx;
+
5368 static FeatureBitset const all{supported_amendments()};
+
5369 static FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
+
5370 static FeatureBitset const rmSmallIncreasedQOffers{
+
5371 fixRmSmallIncreasedQOffers};
+
5372 static FeatureBitset const immediateOfferKilled{
+
5373 featureImmediateOfferKilled};
+
5374 FeatureBitset const fillOrKill{fixFillOrKill};
+
5375 FeatureBitset const permDEX{featurePermissionedDEX};
+
5376
+
5377 static std::array<FeatureBitset, 6> const feats{
+
5378 all - takerDryOffer - immediateOfferKilled - permDEX,
+
5379 all - immediateOfferKilled - permDEX,
+
5380 all - rmSmallIncreasedQOffers - immediateOfferKilled - fillOrKill -
+
5381 permDEX,
+
5382 all - fillOrKill - permDEX,
+
5383 all - permDEX,
+
5384 all};
+
5385
+
5386 if (BEAST_EXPECT(instance < feats.size()))
+
5387 {
+
5388 testAll(feats[instance]);
+
5389 }
+
5390 BEAST_EXPECT(!last || instance == feats.size() - 1);
+
5391 }
+
5392
+
5393 void
+
5394 run() override
+
5395 {
+
5396 run(0);
+
5397 testFalseAssert();
+
5398 }
+
5399};
+
5400
+
5401class OfferWTakerDryOffer_test : public OfferBaseUtil_test
+
5402{
+
5403 void
+
5404 run() override
+
5405 {
+
5406 OfferBaseUtil_test::run(1);
+
5407 }
+
5408};
+
5409
+
5410class OfferWOSmallQOffers_test : public OfferBaseUtil_test
+
5411{
+
5412 void
+
5413 run() override
+
5414 {
+
5415 OfferBaseUtil_test::run(2);
+
5416 }
+
5417};
+
5418
+
5419class OfferWOFillOrKill_test : public OfferBaseUtil_test
+
5420{
+
5421 void
+
5422 run() override
+
5423 {
+
5424 OfferBaseUtil_test::run(3);
+
5425 }
+
5426};
+
5427
+
5428class OfferWOPermDEX_test : public OfferBaseUtil_test
+
5429{
+
5430 void
+
5431 run() override
+
5432 {
+
5433 OfferBaseUtil_test::run(4);
+
5434 }
+
5435};
+
5436
+
5437class OfferAllFeatures_test : public OfferBaseUtil_test
+
5438{
5439 void
-
5440 run() override
+
5440 run() override
5441 {
-
5442 run(0);
-
5443 testFalseAssert();
-
5444 }
-
5445};
-
5446
-
5447class OfferWOFlowCross_test : public OfferBaseUtil_test
-
5448{
-
5449 void
-
5450 run() override
-
5451 {
-
5452 OfferBaseUtil_test::run(1);
-
5453 }
-
5454};
-
5455
-
5456class OfferWTakerDryOffer_test : public OfferBaseUtil_test
-
5457{
-
5458 void
-
5459 run() override
-
5460 {
-
5461 OfferBaseUtil_test::run(2);
-
5462 }
-
5463};
+
5442 OfferBaseUtil_test::run(5, true);
+
5443 }
+
5444};
+
5445
+
5446class Offer_manual_test : public OfferBaseUtil_test
+
5447{
+
5448 void
+
5449 run() override
+
5450 {
+
5451 using namespace jtx;
+
5452 FeatureBitset const all{supported_amendments()};
+
5453 FeatureBitset const f1513{fix1513};
+
5454 FeatureBitset const immediateOfferKilled{featureImmediateOfferKilled};
+
5455 FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
+
5456 FeatureBitset const fillOrKill{fixFillOrKill};
+
5457 FeatureBitset const permDEX{featurePermissionedDEX};
+
5458
+
5459 testAll(all - f1513 - immediateOfferKilled - permDEX);
+
5460 testAll(all - immediateOfferKilled - fillOrKill - permDEX);
+
5461 testAll(all - fillOrKill - permDEX);
+
5462 testAll(all - permDEX);
+
5463 testAll(all);
5464
-
5465class OfferWOSmallQOffers_test : public OfferBaseUtil_test
-
5466{
-
5467 void
-
5468 run() override
-
5469 {
-
5470 OfferBaseUtil_test::run(3);
-
5471 }
-
5472};
-
5473
-
5474class OfferWOFillOrKill_test : public OfferBaseUtil_test
-
5475{
-
5476 void
-
5477 run() override
-
5478 {
-
5479 OfferBaseUtil_test::run(4);
-
5480 }
-
5481};
-
5482
-
5483class OfferWOPermDEX_test : public OfferBaseUtil_test
-
5484{
-
5485 void
-
5486 run() override
-
5487 {
-
5488 OfferBaseUtil_test::run(5);
-
5489 }
-
5490};
-
5491
-
5492class OfferAllFeatures_test : public OfferBaseUtil_test
-
5493{
-
5494 void
-
5495 run() override
-
5496 {
-
5497 OfferBaseUtil_test::run(6, true);
-
5498 }
-
5499};
-
5500
-
5501class Offer_manual_test : public OfferBaseUtil_test
-
5502{
-
5503 void
-
5504 run() override
-
5505 {
-
5506 using namespace jtx;
-
5507 FeatureBitset const all{supported_amendments()};
-
5508 FeatureBitset const flowCross{featureFlowCross};
-
5509 FeatureBitset const f1513{fix1513};
-
5510 FeatureBitset const immediateOfferKilled{featureImmediateOfferKilled};
-
5511 FeatureBitset const takerDryOffer{fixTakerDryOfferRemoval};
-
5512 FeatureBitset const fillOrKill{fixFillOrKill};
-
5513 FeatureBitset const permDEX{featurePermissionedDEX};
-
5514
-
5515 testAll(all - flowCross - f1513 - immediateOfferKilled - permDEX);
-
5516 testAll(all - flowCross - immediateOfferKilled - permDEX);
-
5517 testAll(all - immediateOfferKilled - fillOrKill - permDEX);
-
5518 testAll(all - fillOrKill - permDEX);
-
5519 testAll(all - permDEX);
-
5520 testAll(all);
-
5521
-
5522 testAll(all - flowCross - takerDryOffer - permDEX);
-
5523 }
-
5524};
-
5525
-
5526BEAST_DEFINE_TESTSUITE_PRIO(OfferBaseUtil, tx, ripple, 2);
-
5527BEAST_DEFINE_TESTSUITE_PRIO(OfferWOFlowCross, tx, ripple, 2);
-
5528BEAST_DEFINE_TESTSUITE_PRIO(OfferWTakerDryOffer, tx, ripple, 2);
-
5529BEAST_DEFINE_TESTSUITE_PRIO(OfferWOSmallQOffers, tx, ripple, 2);
-
5530BEAST_DEFINE_TESTSUITE_PRIO(OfferWOFillOrKill, tx, ripple, 2);
-
5531BEAST_DEFINE_TESTSUITE_PRIO(OfferWOPermDEX, tx, ripple, 2);
-
5532BEAST_DEFINE_TESTSUITE_PRIO(OfferAllFeatures, tx, ripple, 2);
-
5533BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Offer_manual, tx, ripple, 20);
-
5534
-
5535} // namespace test
-
5536} // namespace ripple
+
5465 testAll(all - takerDryOffer - permDEX);
+
5466 }
+
5467};
+
5468
+
5469BEAST_DEFINE_TESTSUITE_PRIO(OfferBaseUtil, tx, ripple, 2);
+
5470BEAST_DEFINE_TESTSUITE_PRIO(OfferWTakerDryOffer, tx, ripple, 2);
+
5471BEAST_DEFINE_TESTSUITE_PRIO(OfferWOSmallQOffers, tx, ripple, 2);
+
5472BEAST_DEFINE_TESTSUITE_PRIO(OfferWOFillOrKill, tx, ripple, 2);
+
5473BEAST_DEFINE_TESTSUITE_PRIO(OfferWOPermDEX, tx, ripple, 2);
+
5474BEAST_DEFINE_TESTSUITE_PRIO(OfferAllFeatures, tx, ripple, 2);
+
5475BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Offer_manual, tx, ripple, 20);
+
5476
+
5477} // namespace test
+
5478} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
@@ -5629,91 +5571,89 @@ $(function() {
Definition: XRPAmount.h:43
-
-
void run() override
Runs the suite.
+
+
void run() override
Runs the suite.
Definition: Offer_test.cpp:32
-
void testSelfCrossOffer2(FeatureBitset features)
-
void run() override
Runs the suite.
-
void testMissingAuth(FeatureBitset features)
-
void testSelfAuth(FeatureBitset features)
+
void testSelfCrossOffer2(FeatureBitset features)
+
void run() override
Runs the suite.
+
void testMissingAuth(FeatureBitset features)
+
void testSelfAuth(FeatureBitset features)
void testSelfCross(bool use_partner, FeatureBitset features)
-
void testCrossCurrencyBridged(FeatureBitset features)
-
void testAll(FeatureBitset features)
-
void testSelfIssueOffer(FeatureBitset features)
-
void testRCSmoketest(FeatureBitset features)
+
void testCrossCurrencyBridged(FeatureBitset features)
+
void testAll(FeatureBitset features)
+
void testSelfIssueOffer(FeatureBitset features)
+
void testRCSmoketest(FeatureBitset features)
void testExpiration(FeatureBitset features)
void testUnfundedCross(FeatureBitset features)
-
void testCrossCurrencyStartXRP(FeatureBitset features)
+
void testCrossCurrencyStartXRP(FeatureBitset features)
static std::vector< std::shared_ptr< SLE const > > offersOnAccount(jtx::Env &env, jtx::Account account)
Definition: Offer_test.cpp:820
void testRmSmallIncreasedQOffersIOU(FeatureBitset features)
Definition: Offer_test.cpp:472
-
void testSellWithFillOrKill(FeatureBitset features)
-
void testTinyOffer(FeatureBitset features)
+
void testSellWithFillOrKill(FeatureBitset features)
+
void testTinyOffer(FeatureBitset features)
void testInsufficientReserve(FeatureBitset features)
Definition: Offer_test.cpp:711
-
void testDirectCross(FeatureBitset features)
-
void testOfferThresholdWithReducedFunds(FeatureBitset features)
-
void testRequireAuth(FeatureBitset features)
-
void verifyDefaultTrustline(jtx::Env &env, jtx::Account const &account, jtx::PrettyAmount const &expectBalance)
+
void testDirectCross(FeatureBitset features)
+
void testOfferThresholdWithReducedFunds(FeatureBitset features)
+
void testRequireAuth(FeatureBitset features)
+
void verifyDefaultTrustline(jtx::Env &env, jtx::Account const &account, jtx::PrettyAmount const &expectBalance)
void testRmSmallIncreasedQOffersXRP(FeatureBitset features)
Definition: Offer_test.cpp:324
-
void testDirectToDirectPath(FeatureBitset features)
+
void testDirectToDirectPath(FeatureBitset features)
void testRmFundedOffer(FeatureBitset features)
Definition: Offer_test.cpp:75
-
void testOfferFeesConsumeFunds(FeatureBitset features)
-
void testTickSize(FeatureBitset features)
-
void testTicketOffer(FeatureBitset features)
-
void testOfferCreateThenCross(FeatureBitset features)
-
void testFillOrKill(FeatureBitset features)
-
void testSelfPayUnlimitedFunds(FeatureBitset features)
-
void testOfferCancelPastAndFuture(FeatureBitset features)
-
void testSellFlagBasic(FeatureBitset features)
-
void testBridgedCross(FeatureBitset features)
-
void testXRPDirectCross(FeatureBitset features)
-
void testDeletedOfferIssuer(FeatureBitset features)
+
void testOfferFeesConsumeFunds(FeatureBitset features)
+
void testTickSize(FeatureBitset features)
+
void testTicketOffer(FeatureBitset features)
+
void testOfferCreateThenCross(FeatureBitset features)
+
void testFillOrKill(FeatureBitset features)
+
void testSelfPayUnlimitedFunds(FeatureBitset features)
+
void testOfferCancelPastAndFuture(FeatureBitset features)
+
void testSellFlagBasic(FeatureBitset features)
+
void testBridgedCross(FeatureBitset features)
+
void testXRPDirectCross(FeatureBitset features)
+
void testDeletedOfferIssuer(FeatureBitset features)
void testXRPTinyPayment(FeatureBitset features)
Definition: Offer_test.cpp:252
-
void testFalseAssert()
-
void testTransferRateOffer(FeatureBitset features)
-
void testPartialCross(FeatureBitset features)
-
static std::vector< std::shared_ptr< SLE const > > sortedOffersOnAccount(jtx::Env &env, jtx::Account const &acct)
-
void testCurrencyConversionIntoDebt(FeatureBitset features)
+
void testFalseAssert()
+
void testTransferRateOffer(FeatureBitset features)
+
void testPartialCross(FeatureBitset features)
+
static std::vector< std::shared_ptr< SLE const > > sortedOffersOnAccount(jtx::Env &env, jtx::Account const &acct)
+
void testCurrencyConversionIntoDebt(FeatureBitset features)
void testMalformed(FeatureBitset features)
-
void run(std::uint32_t instance, bool last=false)
-
void testOfferCrossWithXRP(bool reverse_order, FeatureBitset features)
+
void run(std::uint32_t instance, bool last=false)
+
void testOfferCrossWithXRP(bool reverse_order, FeatureBitset features)
void testFillModes(FeatureBitset features)
Definition: Offer_test.cpp:834
-
void testOfferInScaling(FeatureBitset features)
-
void testOfferInScalingWithXferRate(FeatureBitset features)
+
void testOfferInScaling(FeatureBitset features)
+
void testOfferInScalingWithXferRate(FeatureBitset features)
static auto ledgerEntryOffer(jtx::Env &env, jtx::Account const &acct, std::uint32_t offer_seq)
Definition: Offer_test.cpp:46
-
void testCurrencyConversionEntire(FeatureBitset features)
-
void testSelfPayXferFeeOffer(FeatureBitset features)
-
void testCurrencyConversionInParts(FeatureBitset features)
-
void testBridgedSecondLegDry(FeatureBitset features)
-
void testSellOffer(FeatureBitset features)
-
void testCrossCurrencyEndXRP(FeatureBitset features)
-
void testGatewayCrossCurrency(FeatureBitset features)
-
void testNegativeBalance(FeatureBitset features)
+
void testCurrencyConversionEntire(FeatureBitset features)
+
void testSelfPayXferFeeOffer(FeatureBitset features)
+
void testCurrencyConversionInParts(FeatureBitset features)
+
void testBridgedSecondLegDry(FeatureBitset features)
+
void testSellOffer(FeatureBitset features)
+
void testCrossCurrencyEndXRP(FeatureBitset features)
+
void testGatewayCrossCurrency(FeatureBitset features)
+
void testNegativeBalance(FeatureBitset features)
static auto getBookOffers(jtx::Env &env, Issue const &taker_pays, Issue const &taker_gets)
Definition: Offer_test.cpp:59
-
void testSelfCrossOffer(FeatureBitset features)
+
void testSelfCrossOffer(FeatureBitset features)
std::uint32_t lastClose(jtx::Env &env)
Definition: Offer_test.cpp:40
-
void testSelfCrossOffer1(FeatureBitset features)
+
void testSelfCrossOffer1(FeatureBitset features)
XRPAmount reserve(jtx::Env &env, std::uint32_t count)
Definition: Offer_test.cpp:34
-
void testOfferCrossWithLimitOverride(FeatureBitset features)
-
void testTicketCancelOffer(FeatureBitset features)
-
void testSelfCrossLowQualityOffer(FeatureBitset features)
-
void testOfferAcceptThenCancel(FeatureBitset features)
-
void testSellFlagExceedLimit(FeatureBitset features)
+
void testOfferCrossWithLimitOverride(FeatureBitset features)
+
void testTicketCancelOffer(FeatureBitset features)
+
void testSelfCrossLowQualityOffer(FeatureBitset features)
+
void testOfferAcceptThenCancel(FeatureBitset features)
+
void testSellFlagExceedLimit(FeatureBitset features)
void testCanceledOffer(FeatureBitset features)
Definition: Offer_test.cpp:136
-
void testBadPathAssert(FeatureBitset features)
+
void testBadPathAssert(FeatureBitset features)
void testTinyPayment(FeatureBitset features)
Definition: Offer_test.cpp:217
void testEnforceNoRipple(FeatureBitset features)
Definition: Offer_test.cpp:635
-
-
void run() override
Runs the suite.
-
-
void run() override
Runs the suite.
-
-
void run() override
Runs the suite.
-
-
void run() override
Runs the suite.
-
-
void run() override
Runs the suite.
-
-
void run() override
Runs the suite.
+
+
void run() override
Runs the suite.
+
+
void run() override
Runs the suite.
+
+
void run() override
Runs the suite.
+
+
void run() override
Runs the suite.
+
+
void run() override
Runs the suite.
Definition: PathSet.h:167
Definition: PathSet.h:94
Immutable cryptographic account descriptor.
Definition: Account.h:39
diff --git a/PaySteps_8cpp_source.html b/PaySteps_8cpp_source.html index a98a6ad91a..55ec10e256 100644 --- a/PaySteps_8cpp_source.html +++ b/PaySteps_8cpp_source.html @@ -775,13 +775,13 @@ $(function() {
@ tefEXCEPTION
Definition: TER.h:172
template bool isDirectXrpToXrp< IOUAmount, XRPAmount >(Strand const &strand)
OfferCrossing
Definition: Steps.h:45
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1488
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1482
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1487
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1481
template bool isDirectXrpToXrp< XRPAmount, IOUAmount >(Strand const &strand)
Currency const & xrpCurrency()
XRP currency.
Definition: UintTypes.cpp:119
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:34
@ tesSUCCESS
Definition: TER.h:244
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1476
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1475
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:985
bool isDirectXrpToXrp(Strand const &strand)
Definition: PaySteps.cpp:624
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:355
diff --git a/PayStrand__test_8cpp_source.html b/PayStrand__test_8cpp_source.html index 8ad839f387..8b0e1eb6b6 100644 --- a/PayStrand__test_8cpp_source.html +++ b/PayStrand__test_8cpp_source.html @@ -1346,26 +1346,23 @@ $(function() {
1268 {
1269 using namespace jtx;
1270 auto const sa = supported_amendments();
-
1271 testToStrand(sa - featureFlowCross - featurePermissionedDEX);
-
1272 testToStrand(sa - featurePermissionedDEX);
-
1273 testToStrand(sa);
-
1274
-
1275 testRIPD1373(sa - featureFlowCross - featurePermissionedDEX);
-
1276 testRIPD1373(sa - featurePermissionedDEX);
-
1277 testRIPD1373(sa);
-
1278
-
1279 testLoop(sa - featureFlowCross - featurePermissionedDEX);
-
1280 testLoop(sa - featurePermissionedDEX);
-
1281 testLoop(sa);
-
1282
-
1283 testNoAccount(sa);
-
1284 }
-
1285};
-
1286
-
1287BEAST_DEFINE_TESTSUITE(PayStrand, app, ripple);
-
1288
-
1289} // namespace test
-
1290} // namespace ripple
+
1271 testToStrand(sa - featurePermissionedDEX);
+
1272 testToStrand(sa);
+
1273
+
1274 testRIPD1373(sa - featurePermissionedDEX);
+
1275 testRIPD1373(sa);
+
1276
+
1277 testLoop(sa - featurePermissionedDEX);
+
1278 testLoop(sa);
+
1279
+
1280 testNoAccount(sa);
+
1281 }
+
1282};
+
1283
+
1284BEAST_DEFINE_TESTSUITE(PayStrand, app, ripple);
+
1285
+
1286} // namespace test
+
1287} // namespace ripple
T back(T... args)
T begin(T... args)
A testsuite class.
Definition: suite.h:55
@@ -1467,7 +1464,7 @@ $(function() {
STPathElement iape(AccountID const &account)
STPathElement ape(AccountID const &a)
std::uint32_t trustFlag(TrustFlag f, bool useHigh)
-
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1419
+
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1418
bool strandEqualHelper(Iter i)
bool equal(std::unique_ptr< Step > const &s1, DirectStepInfo const &dsi)
STPathElement ipe(Issue const &iss)
diff --git a/PaymentSandbox__test_8cpp_source.html b/PaymentSandbox__test_8cpp_source.html index 2f44de21ae..2af4661571 100644 --- a/PaymentSandbox__test_8cpp_source.html +++ b/PaymentSandbox__test_8cpp_source.html @@ -499,16 +499,15 @@ $(function() {
421 };
422 using namespace jtx;
423 auto const sa = supported_amendments();
-
424 testAll(sa - featureFlowCross - featurePermissionedDEX);
-
425 testAll(sa - featurePermissionedDEX);
-
426 testAll(sa);
-
427 }
-
428};
-
429
-
430BEAST_DEFINE_TESTSUITE(PaymentSandbox, ledger, ripple);
-
431
-
432} // namespace test
-
433} // namespace ripple
+
424 testAll(sa - featurePermissionedDEX);
+
425 testAll(sa);
+
426 }
+
427};
+
428
+
429BEAST_DEFINE_TESTSUITE(PaymentSandbox, ledger, ripple);
+
430
+
431} // namespace test
+
432} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
diff --git a/PermissionedDEX__test_8cpp_source.html b/PermissionedDEX__test_8cpp_source.html index 4d9406c50f..66c3429d75 100644 --- a/PermissionedDEX__test_8cpp_source.html +++ b/PermissionedDEX__test_8cpp_source.html @@ -285,1392 +285,1374 @@ $(function() {
207 env.close();
208 }
209
-
210 // test preflight: permissioned dex cannot be used without enable
-
211 // flowcross
-
212 {
-
213 Env env(*this, features - featureFlowCross);
-
214 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
215 PermissionedDEX(env);
-
216
-
217 env(offer(bob, XRP(10), USD(10)),
-
218 domain(domainID),
-
219 ter(temDISABLED));
-
220 env.close();
-
221
-
222 env.enableFeature(featureFlowCross);
+
210 // preclaim - someone outside of the domain cannot create domain offer
+
211 {
+
212 Env env(*this, features);
+
213 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
214 PermissionedDEX(env);
+
215
+
216 // create devin account who is not part of the domain
+
217 Account devin("devin");
+
218 env.fund(XRP(1000), devin);
+
219 env.close();
+
220 env.trust(USD(1000), devin);
+
221 env.close();
+
222 env(pay(gw, devin, USD(100)));
223 env.close();
-
224 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
225 env.close();
-
226 }
-
227
-
228 // preclaim - someone outside of the domain cannot create domain offer
-
229 {
-
230 Env env(*this, features);
-
231 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
232 PermissionedDEX(env);
+
224
+
225 env(offer(devin, XRP(10), USD(10)),
+
226 domain(domainID),
+
227 ter(tecNO_PERMISSION));
+
228 env.close();
+
229
+
230 // domain owner also issues a credential for devin
+
231 env(credentials::create(devin, domainOwner, credType));
+
232 env.close();
233
-
234 // create devin account who is not part of the domain
-
235 Account devin("devin");
-
236 env.fund(XRP(1000), devin);
-
237 env.close();
-
238 env.trust(USD(1000), devin);
-
239 env.close();
-
240 env(pay(gw, devin, USD(100)));
+
234 // devin still cannot create offer since he didn't accept credential
+
235 env(offer(devin, XRP(10), USD(10)),
+
236 domain(domainID),
+
237 ter(tecNO_PERMISSION));
+
238 env.close();
+
239
+
240 env(credentials::accept(devin, domainOwner, credType));
241 env.close();
242
-
243 env(offer(devin, XRP(10), USD(10)),
-
244 domain(domainID),
-
245 ter(tecNO_PERMISSION));
-
246 env.close();
-
247
-
248 // domain owner also issues a credential for devin
-
249 env(credentials::create(devin, domainOwner, credType));
-
250 env.close();
-
251
-
252 // devin still cannot create offer since he didn't accept credential
-
253 env(offer(devin, XRP(10), USD(10)),
-
254 domain(domainID),
-
255 ter(tecNO_PERMISSION));
+
243 env(offer(devin, XRP(10), USD(10)), domain(domainID));
+
244 env.close();
+
245 }
+
246
+
247 // preclaim - someone with expired cred cannot create domain offer
+
248 {
+
249 Env env(*this, features);
+
250 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
251 PermissionedDEX(env);
+
252
+
253 // create devin account who is not part of the domain
+
254 Account devin("devin");
+
255 env.fund(XRP(1000), devin);
256 env.close();
-
257
-
258 env(credentials::accept(devin, domainOwner, credType));
-
259 env.close();
-
260
-
261 env(offer(devin, XRP(10), USD(10)), domain(domainID));
-
262 env.close();
-
263 }
-
264
-
265 // preclaim - someone with expired cred cannot create domain offer
-
266 {
-
267 Env env(*this, features);
-
268 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
269 PermissionedDEX(env);
-
270
-
271 // create devin account who is not part of the domain
-
272 Account devin("devin");
-
273 env.fund(XRP(1000), devin);
-
274 env.close();
-
275 env.trust(USD(1000), devin);
-
276 env.close();
-
277 env(pay(gw, devin, USD(100)));
-
278 env.close();
+
257 env.trust(USD(1000), devin);
+
258 env.close();
+
259 env(pay(gw, devin, USD(100)));
+
260 env.close();
+
261
+
262 auto jv = credentials::create(devin, domainOwner, credType);
+
263 uint32_t const t = env.current()
+
264 ->info()
+
265 .parentCloseTime.time_since_epoch()
+
266 .count();
+
267 jv[sfExpiration.jsonName] = t + 20;
+
268 env(jv);
+
269
+
270 env(credentials::accept(devin, domainOwner, credType));
+
271 env.close();
+
272
+
273 // devin can still create offer while his cred is not expired
+
274 env(offer(devin, XRP(10), USD(10)), domain(domainID));
+
275 env.close();
+
276
+
277 // time advance
+
278 env.close(std::chrono::seconds(20));
279
-
280 auto jv = credentials::create(devin, domainOwner, credType);
-
281 uint32_t const t = env.current()
-
282 ->info()
-
283 .parentCloseTime.time_since_epoch()
-
284 .count();
-
285 jv[sfExpiration.jsonName] = t + 20;
-
286 env(jv);
-
287
-
288 env(credentials::accept(devin, domainOwner, credType));
-
289 env.close();
-
290
-
291 // devin can still create offer while his cred is not expired
-
292 env(offer(devin, XRP(10), USD(10)), domain(domainID));
-
293 env.close();
-
294
-
295 // time advance
-
296 env.close(std::chrono::seconds(20));
-
297
-
298 // devin cannot create offer with expired cred
-
299 env(offer(devin, XRP(10), USD(10)),
-
300 domain(domainID),
-
301 ter(tecNO_PERMISSION));
-
302 env.close();
-
303 }
-
304
-
305 // preclaim - cannot create an offer in a non existent domain
-
306 {
-
307 Env env(*this, features);
-
308 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
309 PermissionedDEX(env);
-
310 uint256 const badDomain{
-
311 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
-
312 "E5"};
-
313
-
314 env(offer(bob, XRP(10), USD(10)),
-
315 domain(badDomain),
-
316 ter(tecNO_PERMISSION));
-
317 env.close();
-
318 }
-
319
-
320 // apply - offer can be created even if takergets issuer is not in
-
321 // domain
-
322 {
-
323 Env env(*this, features);
-
324 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
325 PermissionedDEX(env);
-
326
-
327 env(credentials::deleteCred(
-
328 domainOwner, gw, domainOwner, credType));
-
329 env.close();
-
330
-
331 auto const bobOfferSeq{env.seq(bob)};
-
332 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
333 env.close();
-
334
-
335 BEAST_EXPECT(
-
336 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
-
337 }
-
338
-
339 // apply - offer can be created even if takerpays issuer is not in
-
340 // domain
+
280 // devin cannot create offer with expired cred
+
281 env(offer(devin, XRP(10), USD(10)),
+
282 domain(domainID),
+
283 ter(tecNO_PERMISSION));
+
284 env.close();
+
285 }
+
286
+
287 // preclaim - cannot create an offer in a non existent domain
+
288 {
+
289 Env env(*this, features);
+
290 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
291 PermissionedDEX(env);
+
292 uint256 const badDomain{
+
293 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
+
294 "E5"};
+
295
+
296 env(offer(bob, XRP(10), USD(10)),
+
297 domain(badDomain),
+
298 ter(tecNO_PERMISSION));
+
299 env.close();
+
300 }
+
301
+
302 // apply - offer can be created even if takergets issuer is not in
+
303 // domain
+
304 {
+
305 Env env(*this, features);
+
306 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
307 PermissionedDEX(env);
+
308
+
309 env(credentials::deleteCred(
+
310 domainOwner, gw, domainOwner, credType));
+
311 env.close();
+
312
+
313 auto const bobOfferSeq{env.seq(bob)};
+
314 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
315 env.close();
+
316
+
317 BEAST_EXPECT(
+
318 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
319 }
+
320
+
321 // apply - offer can be created even if takerpays issuer is not in
+
322 // domain
+
323 {
+
324 Env env(*this, features);
+
325 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
326 PermissionedDEX(env);
+
327
+
328 env(credentials::deleteCred(
+
329 domainOwner, gw, domainOwner, credType));
+
330 env.close();
+
331
+
332 auto const bobOfferSeq{env.seq(bob)};
+
333 env(offer(bob, USD(10), XRP(10)), domain(domainID));
+
334 env.close();
+
335
+
336 BEAST_EXPECT(
+
337 checkOffer(env, bob, bobOfferSeq, USD(10), XRP(10), 0, true));
+
338 }
+
339
+
340 // apply - two domain offers cross with each other
341 {
342 Env env(*this, features);
343 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
344 PermissionedDEX(env);
345
-
346 env(credentials::deleteCred(
-
347 domainOwner, gw, domainOwner, credType));
+
346 auto const bobOfferSeq{env.seq(bob)};
+
347 env(offer(bob, XRP(10), USD(10)), domain(domainID));
348 env.close();
349
-
350 auto const bobOfferSeq{env.seq(bob)};
-
351 env(offer(bob, USD(10), XRP(10)), domain(domainID));
-
352 env.close();
+
350 BEAST_EXPECT(
+
351 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
352 BEAST_EXPECT(ownerCount(env, bob) == 3);
353
-
354 BEAST_EXPECT(
-
355 checkOffer(env, bob, bobOfferSeq, USD(10), XRP(10), 0, true));
-
356 }
+
354 // a non domain offer cannot cross with domain offer
+
355 env(offer(carol, USD(10), XRP(10)));
+
356 env.close();
357
-
358 // apply - two domain offers cross with each other
-
359 {
-
360 Env env(*this, features);
-
361 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
362 PermissionedDEX(env);
-
363
-
364 auto const bobOfferSeq{env.seq(bob)};
-
365 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
366 env.close();
-
367
-
368 BEAST_EXPECT(
-
369 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
-
370 BEAST_EXPECT(ownerCount(env, bob) == 3);
-
371
-
372 // a non domain offer cannot cross with domain offer
-
373 env(offer(carol, USD(10), XRP(10)));
-
374 env.close();
+
358 BEAST_EXPECT(
+
359 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
360
+
361 auto const aliceOfferSeq{env.seq(alice)};
+
362 env(offer(alice, USD(10), XRP(10)), domain(domainID));
+
363 env.close();
+
364
+
365 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
+
366 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
+
367 BEAST_EXPECT(ownerCount(env, alice) == 2);
+
368 }
+
369
+
370 // apply - create lots of domain offers
+
371 {
+
372 Env env(*this, features);
+
373 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
374 PermissionedDEX(env);
375
-
376 BEAST_EXPECT(
-
377 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
376 std::vector<std::uint32_t> offerSeqs;
+
377 offerSeqs.reserve(100);
378
-
379 auto const aliceOfferSeq{env.seq(alice)};
-
380 env(offer(alice, USD(10), XRP(10)), domain(domainID));
-
381 env.close();
-
382
-
383 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
-
384 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
-
385 BEAST_EXPECT(ownerCount(env, alice) == 2);
-
386 }
-
387
-
388 // apply - create lots of domain offers
-
389 {
-
390 Env env(*this, features);
-
391 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
392 PermissionedDEX(env);
-
393
-
394 std::vector<std::uint32_t> offerSeqs;
-
395 offerSeqs.reserve(100);
-
396
-
397 for (size_t i = 0; i <= 100; i++)
-
398 {
-
399 auto const bobOfferSeq{env.seq(bob)};
-
400 offerSeqs.emplace_back(bobOfferSeq);
-
401
-
402 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
403 env.close();
-
404 BEAST_EXPECT(checkOffer(
-
405 env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
-
406 }
-
407
-
408 for (auto const offerSeq : offerSeqs)
-
409 {
-
410 env(offer_cancel(bob, offerSeq));
-
411 env.close();
-
412 BEAST_EXPECT(!offerExists(env, bob, offerSeq));
-
413 }
-
414 }
-
415 }
+
379 for (size_t i = 0; i <= 100; i++)
+
380 {
+
381 auto const bobOfferSeq{env.seq(bob)};
+
382 offerSeqs.emplace_back(bobOfferSeq);
+
383
+
384 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
385 env.close();
+
386 BEAST_EXPECT(checkOffer(
+
387 env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
388 }
+
389
+
390 for (auto const offerSeq : offerSeqs)
+
391 {
+
392 env(offer_cancel(bob, offerSeq));
+
393 env.close();
+
394 BEAST_EXPECT(!offerExists(env, bob, offerSeq));
+
395 }
+
396 }
+
397 }
+
398
+
399 void
+
400 testPayment(FeatureBitset features)
+
401 {
+
402 testcase("Payment");
+
403
+
404 // test preflight - without enabling featurePermissionedDEX amendment
+
405 {
+
406 Env env(*this, features - featurePermissionedDEX);
+
407 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
408 PermissionedDEX(env);
+
409
+
410 env(pay(bob, alice, USD(10)),
+
411 path(~USD),
+
412 sendmax(XRP(10)),
+
413 domain(domainID),
+
414 ter(temDISABLED));
+
415 env.close();
416
-
417 void
-
418 testPayment(FeatureBitset features)
-
419 {
-
420 testcase("Payment");
-
421
-
422 // test preflight - without enabling featurePermissionedDEX amendment
-
423 {
-
424 Env env(*this, features - featurePermissionedDEX);
-
425 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
426 PermissionedDEX(env);
-
427
-
428 env(pay(bob, alice, USD(10)),
-
429 path(~USD),
-
430 sendmax(XRP(10)),
-
431 domain(domainID),
-
432 ter(temDISABLED));
-
433 env.close();
-
434
-
435 env.enableFeature(featurePermissionedDEX);
-
436 env.close();
-
437
-
438 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
439 env.close();
-
440
-
441 env(pay(bob, alice, USD(10)),
-
442 path(~USD),
-
443 sendmax(XRP(10)),
-
444 domain(domainID));
-
445 env.close();
-
446 }
-
447
-
448 // preclaim - cannot send payment with non existent domain
-
449 {
-
450 Env env(*this, features);
-
451 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
452 PermissionedDEX(env);
-
453 uint256 const badDomain{
-
454 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
-
455 "E5"};
-
456
-
457 env(pay(bob, alice, USD(10)),
-
458 path(~USD),
-
459 sendmax(XRP(10)),
-
460 domain(badDomain),
-
461 ter(tecNO_PERMISSION));
-
462 env.close();
-
463 }
+
417 env.enableFeature(featurePermissionedDEX);
+
418 env.close();
+
419
+
420 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
421 env.close();
+
422
+
423 env(pay(bob, alice, USD(10)),
+
424 path(~USD),
+
425 sendmax(XRP(10)),
+
426 domain(domainID));
+
427 env.close();
+
428 }
+
429
+
430 // preclaim - cannot send payment with non existent domain
+
431 {
+
432 Env env(*this, features);
+
433 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
434 PermissionedDEX(env);
+
435 uint256 const badDomain{
+
436 "F10D0CC9A0F9A3CBF585B80BE09A186483668FDBDD39AA7E3370F3649CE134"
+
437 "E5"};
+
438
+
439 env(pay(bob, alice, USD(10)),
+
440 path(~USD),
+
441 sendmax(XRP(10)),
+
442 domain(badDomain),
+
443 ter(tecNO_PERMISSION));
+
444 env.close();
+
445 }
+
446
+
447 // preclaim - payment with non-domain destination fails
+
448 {
+
449 Env env(*this, features);
+
450 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
451 PermissionedDEX(env);
+
452
+
453 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
454 env.close();
+
455
+
456 // create devin account who is not part of the domain
+
457 Account devin("devin");
+
458 env.fund(XRP(1000), devin);
+
459 env.close();
+
460 env.trust(USD(1000), devin);
+
461 env.close();
+
462 env(pay(gw, devin, USD(100)));
+
463 env.close();
464
-
465 // preclaim - payment with non-domain destination fails
-
466 {
-
467 Env env(*this, features);
-
468 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
469 PermissionedDEX(env);
-
470
-
471 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
472 env.close();
-
473
-
474 // create devin account who is not part of the domain
-
475 Account devin("devin");
-
476 env.fund(XRP(1000), devin);
-
477 env.close();
-
478 env.trust(USD(1000), devin);
-
479 env.close();
-
480 env(pay(gw, devin, USD(100)));
-
481 env.close();
-
482
-
483 // devin is not part of domain
-
484 env(pay(alice, devin, USD(10)),
-
485 path(~USD),
-
486 sendmax(XRP(10)),
-
487 domain(domainID),
-
488 ter(tecNO_PERMISSION));
-
489 env.close();
-
490
-
491 // domain owner also issues a credential for devin
-
492 env(credentials::create(devin, domainOwner, credType));
+
465 // devin is not part of domain
+
466 env(pay(alice, devin, USD(10)),
+
467 path(~USD),
+
468 sendmax(XRP(10)),
+
469 domain(domainID),
+
470 ter(tecNO_PERMISSION));
+
471 env.close();
+
472
+
473 // domain owner also issues a credential for devin
+
474 env(credentials::create(devin, domainOwner, credType));
+
475 env.close();
+
476
+
477 // devin has not yet accepted cred
+
478 env(pay(alice, devin, USD(10)),
+
479 path(~USD),
+
480 sendmax(XRP(10)),
+
481 domain(domainID),
+
482 ter(tecNO_PERMISSION));
+
483 env.close();
+
484
+
485 env(credentials::accept(devin, domainOwner, credType));
+
486 env.close();
+
487
+
488 // devin can now receive payment after he is in domain
+
489 env(pay(alice, devin, USD(10)),
+
490 path(~USD),
+
491 sendmax(XRP(10)),
+
492 domain(domainID));
493 env.close();
-
494
-
495 // devin has not yet accepted cred
-
496 env(pay(alice, devin, USD(10)),
-
497 path(~USD),
-
498 sendmax(XRP(10)),
-
499 domain(domainID),
-
500 ter(tecNO_PERMISSION));
-
501 env.close();
-
502
-
503 env(credentials::accept(devin, domainOwner, credType));
-
504 env.close();
-
505
-
506 // devin can now receive payment after he is in domain
-
507 env(pay(alice, devin, USD(10)),
-
508 path(~USD),
-
509 sendmax(XRP(10)),
-
510 domain(domainID));
-
511 env.close();
-
512 }
+
494 }
+
495
+
496 // preclaim - non-domain sender cannot send payment
+
497 {
+
498 Env env(*this, features);
+
499 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
500 PermissionedDEX(env);
+
501
+
502 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
503 env.close();
+
504
+
505 // create devin account who is not part of the domain
+
506 Account devin("devin");
+
507 env.fund(XRP(1000), devin);
+
508 env.close();
+
509 env.trust(USD(1000), devin);
+
510 env.close();
+
511 env(pay(gw, devin, USD(100)));
+
512 env.close();
513
-
514 // preclaim - non-domain sender cannot send payment
-
515 {
-
516 Env env(*this, features);
-
517 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
518 PermissionedDEX(env);
-
519
-
520 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
521 env.close();
-
522
-
523 // create devin account who is not part of the domain
-
524 Account devin("devin");
-
525 env.fund(XRP(1000), devin);
-
526 env.close();
-
527 env.trust(USD(1000), devin);
-
528 env.close();
-
529 env(pay(gw, devin, USD(100)));
-
530 env.close();
-
531
-
532 // devin tries to send domain payment
-
533 env(pay(devin, alice, USD(10)),
-
534 path(~USD),
-
535 sendmax(XRP(10)),
-
536 domain(domainID),
-
537 ter(tecNO_PERMISSION));
-
538 env.close();
-
539
-
540 // domain owner also issues a credential for devin
-
541 env(credentials::create(devin, domainOwner, credType));
+
514 // devin tries to send domain payment
+
515 env(pay(devin, alice, USD(10)),
+
516 path(~USD),
+
517 sendmax(XRP(10)),
+
518 domain(domainID),
+
519 ter(tecNO_PERMISSION));
+
520 env.close();
+
521
+
522 // domain owner also issues a credential for devin
+
523 env(credentials::create(devin, domainOwner, credType));
+
524 env.close();
+
525
+
526 // devin has not yet accepted cred
+
527 env(pay(devin, alice, USD(10)),
+
528 path(~USD),
+
529 sendmax(XRP(10)),
+
530 domain(domainID),
+
531 ter(tecNO_PERMISSION));
+
532 env.close();
+
533
+
534 env(credentials::accept(devin, domainOwner, credType));
+
535 env.close();
+
536
+
537 // devin can now send payment after he is in domain
+
538 env(pay(devin, alice, USD(10)),
+
539 path(~USD),
+
540 sendmax(XRP(10)),
+
541 domain(domainID));
542 env.close();
-
543
-
544 // devin has not yet accepted cred
-
545 env(pay(devin, alice, USD(10)),
-
546 path(~USD),
-
547 sendmax(XRP(10)),
-
548 domain(domainID),
-
549 ter(tecNO_PERMISSION));
-
550 env.close();
-
551
-
552 env(credentials::accept(devin, domainOwner, credType));
-
553 env.close();
-
554
-
555 // devin can now send payment after he is in domain
-
556 env(pay(devin, alice, USD(10)),
-
557 path(~USD),
-
558 sendmax(XRP(10)),
-
559 domain(domainID));
-
560 env.close();
-
561 }
-
562
-
563 // apply - domain owner can always send and receive domain payment
-
564 {
-
565 Env env(*this, features);
-
566 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
567 PermissionedDEX(env);
-
568
-
569 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
570 env.close();
-
571
-
572 // domain owner can always be destination
-
573 env(pay(alice, domainOwner, USD(10)),
-
574 path(~USD),
-
575 sendmax(XRP(10)),
-
576 domain(domainID));
-
577 env.close();
-
578
-
579 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
580 env.close();
-
581
-
582 // domain owner can send
-
583 env(pay(domainOwner, alice, USD(10)),
-
584 path(~USD),
-
585 sendmax(XRP(10)),
-
586 domain(domainID));
+
543 }
+
544
+
545 // apply - domain owner can always send and receive domain payment
+
546 {
+
547 Env env(*this, features);
+
548 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
549 PermissionedDEX(env);
+
550
+
551 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
552 env.close();
+
553
+
554 // domain owner can always be destination
+
555 env(pay(alice, domainOwner, USD(10)),
+
556 path(~USD),
+
557 sendmax(XRP(10)),
+
558 domain(domainID));
+
559 env.close();
+
560
+
561 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
562 env.close();
+
563
+
564 // domain owner can send
+
565 env(pay(domainOwner, alice, USD(10)),
+
566 path(~USD),
+
567 sendmax(XRP(10)),
+
568 domain(domainID));
+
569 env.close();
+
570 }
+
571 }
+
572
+
573 void
+
574 testBookStep(FeatureBitset features)
+
575 {
+
576 testcase("Book step");
+
577
+
578 // test domain cross currency payment consuming one offer
+
579 {
+
580 Env env(*this, features);
+
581 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
582 PermissionedDEX(env);
+
583
+
584 // create a regular offer without domain
+
585 auto const regularOfferSeq{env.seq(bob)};
+
586 env(offer(bob, XRP(10), USD(10)));
587 env.close();
-
588 }
-
589 }
+
588 BEAST_EXPECT(
+
589 checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10)));
590
-
591 void
-
592 testBookStep(FeatureBitset features)
-
593 {
-
594 testcase("Book step");
+
591 auto const regularDirKey =
+
592 getDefaultOfferDirKey(env, bob, regularOfferSeq);
+
593 BEAST_EXPECT(regularDirKey);
+
594 BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1));
595
-
596 // test domain cross currency payment consuming one offer
-
597 {
-
598 Env env(*this, features);
-
599 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
600 PermissionedDEX(env);
-
601
-
602 // create a regular offer without domain
-
603 auto const regularOfferSeq{env.seq(bob)};
-
604 env(offer(bob, XRP(10), USD(10)));
-
605 env.close();
-
606 BEAST_EXPECT(
-
607 checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10)));
+
596 // a domain payment cannot consume regular offers
+
597 env(pay(alice, carol, USD(10)),
+
598 path(~USD),
+
599 sendmax(XRP(10)),
+
600 domain(domainID),
+
601 ter(tecPATH_PARTIAL));
+
602 env.close();
+
603
+
604 // create a domain offer
+
605 auto const domainOfferSeq{env.seq(bob)};
+
606 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
607 env.close();
608
-
609 auto const regularDirKey =
-
610 getDefaultOfferDirKey(env, bob, regularOfferSeq);
-
611 BEAST_EXPECT(regularDirKey);
-
612 BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1));
-
613
-
614 // a domain payment cannot consume regular offers
-
615 env(pay(alice, carol, USD(10)),
-
616 path(~USD),
-
617 sendmax(XRP(10)),
-
618 domain(domainID),
-
619 ter(tecPATH_PARTIAL));
-
620 env.close();
-
621
-
622 // create a domain offer
-
623 auto const domainOfferSeq{env.seq(bob)};
-
624 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
625 env.close();
-
626
-
627 BEAST_EXPECT(checkOffer(
-
628 env, bob, domainOfferSeq, XRP(10), USD(10), 0, true));
-
629
-
630 auto const domainDirKey =
-
631 getDefaultOfferDirKey(env, bob, domainOfferSeq);
-
632 BEAST_EXPECT(domainDirKey);
-
633 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1));
-
634
-
635 // cross-currency permissioned payment consumed
-
636 // domain offer instead of regular offer
-
637 env(pay(alice, carol, USD(10)),
-
638 path(~USD),
-
639 sendmax(XRP(10)),
-
640 domain(domainID));
+
609 BEAST_EXPECT(checkOffer(
+
610 env, bob, domainOfferSeq, XRP(10), USD(10), 0, true));
+
611
+
612 auto const domainDirKey =
+
613 getDefaultOfferDirKey(env, bob, domainOfferSeq);
+
614 BEAST_EXPECT(domainDirKey);
+
615 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1));
+
616
+
617 // cross-currency permissioned payment consumed
+
618 // domain offer instead of regular offer
+
619 env(pay(alice, carol, USD(10)),
+
620 path(~USD),
+
621 sendmax(XRP(10)),
+
622 domain(domainID));
+
623 env.close();
+
624 BEAST_EXPECT(!offerExists(env, bob, domainOfferSeq));
+
625 BEAST_EXPECT(
+
626 checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10)));
+
627
+
628 // domain directory is empty
+
629 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 0));
+
630 BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1));
+
631 }
+
632
+
633 // test domain payment consuming two offers in the path
+
634 {
+
635 Env env(*this, features);
+
636 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
637 PermissionedDEX(env);
+
638
+
639 auto const EUR = gw["EUR"];
+
640 env.trust(EUR(1000), alice);
641 env.close();
-
642 BEAST_EXPECT(!offerExists(env, bob, domainOfferSeq));
-
643 BEAST_EXPECT(
-
644 checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10)));
-
645
-
646 // domain directory is empty
-
647 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 0));
-
648 BEAST_EXPECT(checkDirectorySize(env, *regularDirKey, 1));
-
649 }
-
650
-
651 // test domain payment consuming two offers in the path
-
652 {
-
653 Env env(*this, features);
-
654 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
655 PermissionedDEX(env);
+
642 env.trust(EUR(1000), bob);
+
643 env.close();
+
644 env.trust(EUR(1000), carol);
+
645 env.close();
+
646 env(pay(gw, bob, EUR(100)));
+
647 env.close();
+
648
+
649 // create XRP/USD domain offer
+
650 auto const usdOfferSeq{env.seq(bob)};
+
651 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
652 env.close();
+
653
+
654 BEAST_EXPECT(
+
655 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
656
-
657 auto const EUR = gw["EUR"];
-
658 env.trust(EUR(1000), alice);
-
659 env.close();
-
660 env.trust(EUR(1000), bob);
-
661 env.close();
-
662 env.trust(EUR(1000), carol);
+
657 // payment fail because there isn't eur offer
+
658 env(pay(alice, carol, EUR(10)),
+
659 path(~USD, ~EUR),
+
660 sendmax(XRP(10)),
+
661 domain(domainID),
+
662 ter(tecPATH_PARTIAL));
663 env.close();
-
664 env(pay(gw, bob, EUR(100)));
-
665 env.close();
+
664 BEAST_EXPECT(
+
665 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
666
-
667 // create XRP/USD domain offer
-
668 auto const usdOfferSeq{env.seq(bob)};
-
669 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
667 // bob creates a regular USD/EUR offer
+
668 auto const regularOfferSeq{env.seq(bob)};
+
669 env(offer(bob, USD(10), EUR(10)));
670 env.close();
-
671
-
672 BEAST_EXPECT(
-
673 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
-
674
-
675 // payment fail because there isn't eur offer
+
671 BEAST_EXPECT(
+
672 checkOffer(env, bob, regularOfferSeq, USD(10), EUR(10)));
+
673
+
674 // alice tries to pay again, but still fails because the regular
+
675 // offer cannot be consumed
676 env(pay(alice, carol, EUR(10)),
677 path(~USD, ~EUR),
678 sendmax(XRP(10)),
679 domain(domainID),
680 ter(tecPATH_PARTIAL));
681 env.close();
-
682 BEAST_EXPECT(
-
683 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
-
684
-
685 // bob creates a regular USD/EUR offer
-
686 auto const regularOfferSeq{env.seq(bob)};
-
687 env(offer(bob, USD(10), EUR(10)));
-
688 env.close();
-
689 BEAST_EXPECT(
-
690 checkOffer(env, bob, regularOfferSeq, USD(10), EUR(10)));
-
691
-
692 // alice tries to pay again, but still fails because the regular
-
693 // offer cannot be consumed
-
694 env(pay(alice, carol, EUR(10)),
-
695 path(~USD, ~EUR),
-
696 sendmax(XRP(10)),
-
697 domain(domainID),
-
698 ter(tecPATH_PARTIAL));
-
699 env.close();
-
700
-
701 // bob creates a domain USD/EUR offer
-
702 auto const eurOfferSeq{env.seq(bob)};
-
703 env(offer(bob, USD(10), EUR(10)), domain(domainID));
-
704 env.close();
-
705 BEAST_EXPECT(
-
706 checkOffer(env, bob, eurOfferSeq, USD(10), EUR(10), 0, true));
-
707
-
708 // alice successfully consume two domain offers: xrp/usd and usd/eur
-
709 env(pay(alice, carol, EUR(5)),
-
710 sendmax(XRP(5)),
-
711 domain(domainID),
-
712 path(~USD, ~EUR));
-
713 env.close();
-
714
-
715 BEAST_EXPECT(
-
716 checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, true));
-
717 BEAST_EXPECT(
-
718 checkOffer(env, bob, eurOfferSeq, USD(5), EUR(5), 0, true));
-
719
-
720 // alice successfully consume two domain offers and deletes them
-
721 // we compute path this time using `paths`
-
722 env(pay(alice, carol, EUR(5)),
-
723 sendmax(XRP(5)),
-
724 domain(domainID),
-
725 paths(XRP));
-
726 env.close();
-
727
-
728 BEAST_EXPECT(!offerExists(env, bob, usdOfferSeq));
-
729 BEAST_EXPECT(!offerExists(env, bob, eurOfferSeq));
-
730
-
731 // regular offer is not consumed
-
732 BEAST_EXPECT(
-
733 checkOffer(env, bob, regularOfferSeq, USD(10), EUR(10)));
-
734 }
-
735
-
736 // domain payment cannot consume offer from another domain
-
737 {
-
738 Env env(*this, features);
-
739 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
740 PermissionedDEX(env);
-
741
-
742 // Fund devin and create USD trustline
-
743 Account badDomainOwner("badDomainOwner");
-
744 Account devin("devin");
-
745 env.fund(XRP(1000), badDomainOwner, devin);
-
746 env.close();
-
747 env.trust(USD(1000), devin);
-
748 env.close();
-
749 env(pay(gw, devin, USD(100)));
-
750 env.close();
-
751
-
752 auto const badCredType = "badCred";
-
753 pdomain::Credentials credentials{{badDomainOwner, badCredType}};
-
754 env(pdomain::setTx(badDomainOwner, credentials));
-
755
-
756 auto objects = pdomain::getObjects(badDomainOwner, env);
-
757 auto const badDomainID = objects.begin()->first;
-
758
-
759 env(credentials::create(devin, badDomainOwner, badCredType));
+
682
+
683 // bob creates a domain USD/EUR offer
+
684 auto const eurOfferSeq{env.seq(bob)};
+
685 env(offer(bob, USD(10), EUR(10)), domain(domainID));
+
686 env.close();
+
687 BEAST_EXPECT(
+
688 checkOffer(env, bob, eurOfferSeq, USD(10), EUR(10), 0, true));
+
689
+
690 // alice successfully consume two domain offers: xrp/usd and usd/eur
+
691 env(pay(alice, carol, EUR(5)),
+
692 sendmax(XRP(5)),
+
693 domain(domainID),
+
694 path(~USD, ~EUR));
+
695 env.close();
+
696
+
697 BEAST_EXPECT(
+
698 checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, true));
+
699 BEAST_EXPECT(
+
700 checkOffer(env, bob, eurOfferSeq, USD(5), EUR(5), 0, true));
+
701
+
702 // alice successfully consume two domain offers and deletes them
+
703 // we compute path this time using `paths`
+
704 env(pay(alice, carol, EUR(5)),
+
705 sendmax(XRP(5)),
+
706 domain(domainID),
+
707 paths(XRP));
+
708 env.close();
+
709
+
710 BEAST_EXPECT(!offerExists(env, bob, usdOfferSeq));
+
711 BEAST_EXPECT(!offerExists(env, bob, eurOfferSeq));
+
712
+
713 // regular offer is not consumed
+
714 BEAST_EXPECT(
+
715 checkOffer(env, bob, regularOfferSeq, USD(10), EUR(10)));
+
716 }
+
717
+
718 // domain payment cannot consume offer from another domain
+
719 {
+
720 Env env(*this, features);
+
721 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
722 PermissionedDEX(env);
+
723
+
724 // Fund devin and create USD trustline
+
725 Account badDomainOwner("badDomainOwner");
+
726 Account devin("devin");
+
727 env.fund(XRP(1000), badDomainOwner, devin);
+
728 env.close();
+
729 env.trust(USD(1000), devin);
+
730 env.close();
+
731 env(pay(gw, devin, USD(100)));
+
732 env.close();
+
733
+
734 auto const badCredType = "badCred";
+
735 pdomain::Credentials credentials{{badDomainOwner, badCredType}};
+
736 env(pdomain::setTx(badDomainOwner, credentials));
+
737
+
738 auto objects = pdomain::getObjects(badDomainOwner, env);
+
739 auto const badDomainID = objects.begin()->first;
+
740
+
741 env(credentials::create(devin, badDomainOwner, badCredType));
+
742 env.close();
+
743 env(credentials::accept(devin, badDomainOwner, badCredType));
+
744
+
745 // devin creates a domain offer in another domain
+
746 env(offer(devin, XRP(10), USD(10)), domain(badDomainID));
+
747 env.close();
+
748
+
749 // domain payment can't consume an offer from another domain
+
750 env(pay(alice, carol, USD(10)),
+
751 path(~USD),
+
752 sendmax(XRP(10)),
+
753 domain(domainID),
+
754 ter(tecPATH_PARTIAL));
+
755 env.close();
+
756
+
757 // bob creates an offer under the right domain
+
758 auto const bobOfferSeq{env.seq(bob)};
+
759 env(offer(bob, XRP(10), USD(10)), domain(domainID));
760 env.close();
-
761 env(credentials::accept(devin, badDomainOwner, badCredType));
-
762
-
763 // devin creates a domain offer in another domain
-
764 env(offer(devin, XRP(10), USD(10)), domain(badDomainID));
-
765 env.close();
-
766
-
767 // domain payment can't consume an offer from another domain
-
768 env(pay(alice, carol, USD(10)),
-
769 path(~USD),
-
770 sendmax(XRP(10)),
-
771 domain(domainID),
-
772 ter(tecPATH_PARTIAL));
-
773 env.close();
-
774
-
775 // bob creates an offer under the right domain
-
776 auto const bobOfferSeq{env.seq(bob)};
-
777 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
778 env.close();
-
779 BEAST_EXPECT(
-
780 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
761 BEAST_EXPECT(
+
762 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
763
+
764 // domain payment now consumes from the right domain
+
765 env(pay(alice, carol, USD(10)),
+
766 path(~USD),
+
767 sendmax(XRP(10)),
+
768 domain(domainID));
+
769 env.close();
+
770
+
771 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
+
772 }
+
773
+
774 // sanity check: devin, who is part of the domain but doesn't have a
+
775 // trustline with USD issuer, can successfully make a payment using
+
776 // offer
+
777 {
+
778 Env env(*this, features);
+
779 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
780 PermissionedDEX(env);
781
-
782 // domain payment now consumes from the right domain
-
783 env(pay(alice, carol, USD(10)),
-
784 path(~USD),
-
785 sendmax(XRP(10)),
-
786 domain(domainID));
-
787 env.close();
-
788
-
789 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
-
790 }
-
791
-
792 // sanity check: devin, who is part of the domain but doesn't have a
-
793 // trustline with USD issuer, can successfully make a payment using
-
794 // offer
-
795 {
-
796 Env env(*this, features);
-
797 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
798 PermissionedDEX(env);
-
799
-
800 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
801 env.close();
-
802
-
803 // fund devin but don't create a USD trustline with gateway
-
804 Account devin("devin");
-
805 env.fund(XRP(1000), devin);
-
806 env.close();
+
782 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
783 env.close();
+
784
+
785 // fund devin but don't create a USD trustline with gateway
+
786 Account devin("devin");
+
787 env.fund(XRP(1000), devin);
+
788 env.close();
+
789
+
790 // domain owner also issues a credential for devin
+
791 env(credentials::create(devin, domainOwner, credType));
+
792 env.close();
+
793
+
794 env(credentials::accept(devin, domainOwner, credType));
+
795 env.close();
+
796
+
797 // successful payment because offer is consumed
+
798 env(pay(devin, alice, USD(10)), sendmax(XRP(10)), domain(domainID));
+
799 env.close();
+
800 }
+
801
+
802 // offer becomes unfunded when offer owner's cred expires
+
803 {
+
804 Env env(*this, features);
+
805 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
806 PermissionedDEX(env);
807
-
808 // domain owner also issues a credential for devin
-
809 env(credentials::create(devin, domainOwner, credType));
-
810 env.close();
-
811
-
812 env(credentials::accept(devin, domainOwner, credType));
+
808 // create devin account who is not part of the domain
+
809 Account devin("devin");
+
810 env.fund(XRP(1000), devin);
+
811 env.close();
+
812 env.trust(USD(1000), devin);
813 env.close();
-
814
-
815 // successful payment because offer is consumed
-
816 env(pay(devin, alice, USD(10)), sendmax(XRP(10)), domain(domainID));
-
817 env.close();
-
818 }
-
819
-
820 // offer becomes unfunded when offer owner's cred expires
-
821 {
-
822 Env env(*this, features);
-
823 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
824 PermissionedDEX(env);
-
825
-
826 // create devin account who is not part of the domain
-
827 Account devin("devin");
-
828 env.fund(XRP(1000), devin);
-
829 env.close();
-
830 env.trust(USD(1000), devin);
+
814 env(pay(gw, devin, USD(100)));
+
815 env.close();
+
816
+
817 auto jv = credentials::create(devin, domainOwner, credType);
+
818 uint32_t const t = env.current()
+
819 ->info()
+
820 .parentCloseTime.time_since_epoch()
+
821 .count();
+
822 jv[sfExpiration.jsonName] = t + 20;
+
823 env(jv);
+
824
+
825 env(credentials::accept(devin, domainOwner, credType));
+
826 env.close();
+
827
+
828 // devin can still create offer while his cred is not expired
+
829 auto const offerSeq{env.seq(devin)};
+
830 env(offer(devin, XRP(10), USD(10)), domain(domainID));
831 env.close();
-
832 env(pay(gw, devin, USD(100)));
-
833 env.close();
-
834
-
835 auto jv = credentials::create(devin, domainOwner, credType);
-
836 uint32_t const t = env.current()
-
837 ->info()
-
838 .parentCloseTime.time_since_epoch()
-
839 .count();
-
840 jv[sfExpiration.jsonName] = t + 20;
-
841 env(jv);
-
842
-
843 env(credentials::accept(devin, domainOwner, credType));
-
844 env.close();
-
845
-
846 // devin can still create offer while his cred is not expired
-
847 auto const offerSeq{env.seq(devin)};
-
848 env(offer(devin, XRP(10), USD(10)), domain(domainID));
-
849 env.close();
-
850
-
851 // devin's offer can still be consumed while his cred isn't expired
-
852 env(pay(alice, carol, USD(5)),
-
853 path(~USD),
-
854 sendmax(XRP(5)),
-
855 domain(domainID));
-
856 env.close();
-
857 BEAST_EXPECT(
-
858 checkOffer(env, devin, offerSeq, XRP(5), USD(5), 0, true));
-
859
-
860 // advance time
-
861 env.close(std::chrono::seconds(20));
-
862
-
863 // devin's offer is unfunded now due to expired cred
-
864 env(pay(alice, carol, USD(5)),
-
865 path(~USD),
-
866 sendmax(XRP(5)),
-
867 domain(domainID),
-
868 ter(tecPATH_PARTIAL));
-
869 env.close();
-
870 BEAST_EXPECT(
-
871 checkOffer(env, devin, offerSeq, XRP(5), USD(5), 0, true));
-
872 }
-
873
-
874 // offer becomes unfunded when offer owner's cred is removed
-
875 {
-
876 Env env(*this, features);
-
877 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
878 PermissionedDEX(env);
+
832
+
833 // devin's offer can still be consumed while his cred isn't expired
+
834 env(pay(alice, carol, USD(5)),
+
835 path(~USD),
+
836 sendmax(XRP(5)),
+
837 domain(domainID));
+
838 env.close();
+
839 BEAST_EXPECT(
+
840 checkOffer(env, devin, offerSeq, XRP(5), USD(5), 0, true));
+
841
+
842 // advance time
+
843 env.close(std::chrono::seconds(20));
+
844
+
845 // devin's offer is unfunded now due to expired cred
+
846 env(pay(alice, carol, USD(5)),
+
847 path(~USD),
+
848 sendmax(XRP(5)),
+
849 domain(domainID),
+
850 ter(tecPATH_PARTIAL));
+
851 env.close();
+
852 BEAST_EXPECT(
+
853 checkOffer(env, devin, offerSeq, XRP(5), USD(5), 0, true));
+
854 }
+
855
+
856 // offer becomes unfunded when offer owner's cred is removed
+
857 {
+
858 Env env(*this, features);
+
859 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
860 PermissionedDEX(env);
+
861
+
862 auto const offerSeq{env.seq(bob)};
+
863 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
864 env.close();
+
865
+
866 // bob's offer can still be consumed while his cred exists
+
867 env(pay(alice, carol, USD(5)),
+
868 path(~USD),
+
869 sendmax(XRP(5)),
+
870 domain(domainID));
+
871 env.close();
+
872 BEAST_EXPECT(
+
873 checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true));
+
874
+
875 // remove bob's cred
+
876 env(credentials::deleteCred(
+
877 domainOwner, bob, domainOwner, credType));
+
878 env.close();
879
-
880 auto const offerSeq{env.seq(bob)};
-
881 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
882 env.close();
-
883
-
884 // bob's offer can still be consumed while his cred exists
-
885 env(pay(alice, carol, USD(5)),
-
886 path(~USD),
-
887 sendmax(XRP(5)),
-
888 domain(domainID));
-
889 env.close();
-
890 BEAST_EXPECT(
-
891 checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true));
-
892
-
893 // remove bob's cred
-
894 env(credentials::deleteCred(
-
895 domainOwner, bob, domainOwner, credType));
-
896 env.close();
-
897
-
898 // bob's offer is unfunded now due to expired cred
-
899 env(pay(alice, carol, USD(5)),
-
900 path(~USD),
-
901 sendmax(XRP(5)),
-
902 domain(domainID),
-
903 ter(tecPATH_PARTIAL));
-
904 env.close();
-
905 BEAST_EXPECT(
-
906 checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true));
-
907 }
-
908 }
-
909
-
910 void
-
911 testRippling(FeatureBitset features)
-
912 {
-
913 testcase("Rippling");
+
880 // bob's offer is unfunded now due to expired cred
+
881 env(pay(alice, carol, USD(5)),
+
882 path(~USD),
+
883 sendmax(XRP(5)),
+
884 domain(domainID),
+
885 ter(tecPATH_PARTIAL));
+
886 env.close();
+
887 BEAST_EXPECT(
+
888 checkOffer(env, bob, offerSeq, XRP(5), USD(5), 0, true));
+
889 }
+
890 }
+
891
+
892 void
+
893 testRippling(FeatureBitset features)
+
894 {
+
895 testcase("Rippling");
+
896
+
897 // test a non-domain account can still be part of rippling in a domain
+
898 // payment. If the domain wishes to control who is allowed to ripple
+
899 // through, they should set the rippling individually
+
900 Env env(*this, features);
+
901 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
902 PermissionedDEX(env);
+
903
+
904 auto const EURA = alice["EUR"];
+
905 auto const EURB = bob["EUR"];
+
906
+
907 env.trust(EURA(100), bob);
+
908 env.trust(EURB(100), carol);
+
909 env.close();
+
910
+
911 // remove bob from domain
+
912 env(credentials::deleteCred(domainOwner, bob, domainOwner, credType));
+
913 env.close();
914
-
915 // test a non-domain account can still be part of rippling in a domain
-
916 // payment. If the domain wishes to control who is allowed to ripple
-
917 // through, they should set the rippling individually
-
918 Env env(*this, features);
-
919 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
920 PermissionedDEX(env);
-
921
-
922 auto const EURA = alice["EUR"];
-
923 auto const EURB = bob["EUR"];
+
915 // alice can still ripple through bob even though he's not part
+
916 // of the domain, this is intentional
+
917 env(pay(alice, carol, EURB(10)), paths(EURA), domain(domainID));
+
918 env.close();
+
919 env.require(balance(bob, EURA(10)), balance(carol, EURB(10)));
+
920
+
921 // carol sets no ripple on bob
+
922 env(trust(carol, bob["EUR"](0), bob, tfSetNoRipple));
+
923 env.close();
924
-
925 env.trust(EURA(100), bob);
-
926 env.trust(EURB(100), carol);
-
927 env.close();
-
928
-
929 // remove bob from domain
-
930 env(credentials::deleteCred(domainOwner, bob, domainOwner, credType));
-
931 env.close();
-
932
-
933 // alice can still ripple through bob even though he's not part
-
934 // of the domain, this is intentional
-
935 env(pay(alice, carol, EURB(10)), paths(EURA), domain(domainID));
-
936 env.close();
-
937 env.require(balance(bob, EURA(10)), balance(carol, EURB(10)));
+
925 // payment no longer works because carol has no ripple on bob
+
926 env(pay(alice, carol, EURB(5)),
+
927 paths(EURA),
+
928 domain(domainID),
+
929 ter(tecPATH_DRY));
+
930 env.close();
+
931 env.require(balance(bob, EURA(10)), balance(carol, EURB(10)));
+
932 }
+
933
+
934 void
+
935 testOfferTokenIssuerInDomain(FeatureBitset features)
+
936 {
+
937 testcase("Offer token issuer in domain");
938
-
939 // carol sets no ripple on bob
-
940 env(trust(carol, bob["EUR"](0), bob, tfSetNoRipple));
-
941 env.close();
-
942
-
943 // payment no longer works because carol has no ripple on bob
-
944 env(pay(alice, carol, EURB(5)),
-
945 paths(EURA),
-
946 domain(domainID),
-
947 ter(tecPATH_DRY));
+
939 // whether the issuer is in the domain should NOT affect whether an
+
940 // offer can be consumed in domain payment
+
941 Env env(*this, features);
+
942 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
943 PermissionedDEX(env);
+
944
+
945 // create an xrp/usd offer with usd as takergets
+
946 auto const bobOffer1Seq{env.seq(bob)};
+
947 env(offer(bob, XRP(10), USD(10)), domain(domainID));
948 env.close();
-
949 env.require(balance(bob, EURA(10)), balance(carol, EURB(10)));
-
950 }
-
951
-
952 void
-
953 testOfferTokenIssuerInDomain(FeatureBitset features)
-
954 {
-
955 testcase("Offer token issuer in domain");
-
956
-
957 // whether the issuer is in the domain should NOT affect whether an
-
958 // offer can be consumed in domain payment
-
959 Env env(*this, features);
-
960 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
961 PermissionedDEX(env);
-
962
-
963 // create an xrp/usd offer with usd as takergets
-
964 auto const bobOffer1Seq{env.seq(bob)};
-
965 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
966 env.close();
-
967
-
968 // create an usd/xrp offer with usd as takerpays
-
969 auto const bobOffer2Seq{env.seq(bob)};
-
970 env(offer(bob, USD(10), XRP(10)), domain(domainID), txflags(tfPassive));
-
971 env.close();
+
949
+
950 // create an usd/xrp offer with usd as takerpays
+
951 auto const bobOffer2Seq{env.seq(bob)};
+
952 env(offer(bob, USD(10), XRP(10)), domain(domainID), txflags(tfPassive));
+
953 env.close();
+
954
+
955 BEAST_EXPECT(
+
956 checkOffer(env, bob, bobOffer1Seq, XRP(10), USD(10), 0, true));
+
957 BEAST_EXPECT(checkOffer(
+
958 env, bob, bobOffer2Seq, USD(10), XRP(10), lsfPassive, true));
+
959
+
960 // remove gateway from domain
+
961 env(credentials::deleteCred(domainOwner, gw, domainOwner, credType));
+
962 env.close();
+
963
+
964 // payment succeeds even if issuer is not in domain
+
965 // xrp/usd offer is consumed
+
966 env(pay(alice, carol, USD(10)),
+
967 path(~USD),
+
968 sendmax(XRP(10)),
+
969 domain(domainID));
+
970 env.close();
+
971 BEAST_EXPECT(!offerExists(env, bob, bobOffer1Seq));
972
-
973 BEAST_EXPECT(
-
974 checkOffer(env, bob, bobOffer1Seq, XRP(10), USD(10), 0, true));
-
975 BEAST_EXPECT(checkOffer(
-
976 env, bob, bobOffer2Seq, USD(10), XRP(10), lsfPassive, true));
-
977
-
978 // remove gateway from domain
-
979 env(credentials::deleteCred(domainOwner, gw, domainOwner, credType));
-
980 env.close();
-
981
-
982 // payment succeeds even if issuer is not in domain
-
983 // xrp/usd offer is consumed
-
984 env(pay(alice, carol, USD(10)),
-
985 path(~USD),
-
986 sendmax(XRP(10)),
-
987 domain(domainID));
-
988 env.close();
-
989 BEAST_EXPECT(!offerExists(env, bob, bobOffer1Seq));
-
990
-
991 // payment succeeds even if issuer is not in domain
-
992 // usd/xrp offer is consumed
-
993 env(pay(alice, carol, XRP(10)),
-
994 path(~XRP),
-
995 sendmax(USD(10)),
-
996 domain(domainID));
-
997 env.close();
-
998 BEAST_EXPECT(!offerExists(env, bob, bobOffer2Seq));
-
999 }
-
1000
-
1001 void
-
1002 testRemoveUnfundedOffer(FeatureBitset features)
-
1003 {
-
1004 testcase("Remove unfunded offer");
-
1005
-
1006 // checking that an unfunded offer will be implictly removed by a
-
1007 // successfuly payment tx
-
1008 Env env(*this, features);
-
1009 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1010 PermissionedDEX(env);
-
1011
-
1012 auto const aliceOfferSeq{env.seq(alice)};
-
1013 env(offer(alice, XRP(100), USD(100)), domain(domainID));
-
1014 env.close();
-
1015
-
1016 auto const bobOfferSeq{env.seq(bob)};
-
1017 env(offer(bob, XRP(20), USD(20)), domain(domainID));
-
1018 env.close();
-
1019
-
1020 BEAST_EXPECT(
-
1021 checkOffer(env, bob, bobOfferSeq, XRP(20), USD(20), 0, true));
-
1022 BEAST_EXPECT(
-
1023 checkOffer(env, alice, aliceOfferSeq, XRP(100), USD(100), 0, true));
-
1024
-
1025 auto const domainDirKey = getDefaultOfferDirKey(env, bob, bobOfferSeq);
-
1026 BEAST_EXPECT(domainDirKey);
-
1027 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 2));
+
973 // payment succeeds even if issuer is not in domain
+
974 // usd/xrp offer is consumed
+
975 env(pay(alice, carol, XRP(10)),
+
976 path(~XRP),
+
977 sendmax(USD(10)),
+
978 domain(domainID));
+
979 env.close();
+
980 BEAST_EXPECT(!offerExists(env, bob, bobOffer2Seq));
+
981 }
+
982
+
983 void
+
984 testRemoveUnfundedOffer(FeatureBitset features)
+
985 {
+
986 testcase("Remove unfunded offer");
+
987
+
988 // checking that an unfunded offer will be implictly removed by a
+
989 // successfuly payment tx
+
990 Env env(*this, features);
+
991 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
992 PermissionedDEX(env);
+
993
+
994 auto const aliceOfferSeq{env.seq(alice)};
+
995 env(offer(alice, XRP(100), USD(100)), domain(domainID));
+
996 env.close();
+
997
+
998 auto const bobOfferSeq{env.seq(bob)};
+
999 env(offer(bob, XRP(20), USD(20)), domain(domainID));
+
1000 env.close();
+
1001
+
1002 BEAST_EXPECT(
+
1003 checkOffer(env, bob, bobOfferSeq, XRP(20), USD(20), 0, true));
+
1004 BEAST_EXPECT(
+
1005 checkOffer(env, alice, aliceOfferSeq, XRP(100), USD(100), 0, true));
+
1006
+
1007 auto const domainDirKey = getDefaultOfferDirKey(env, bob, bobOfferSeq);
+
1008 BEAST_EXPECT(domainDirKey);
+
1009 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 2));
+
1010
+
1011 // remove alice from domain and thus alice's offer becomes unfunded
+
1012 env(credentials::deleteCred(domainOwner, alice, domainOwner, credType));
+
1013 env.close();
+
1014
+
1015 env(pay(gw, carol, USD(10)),
+
1016 path(~USD),
+
1017 sendmax(XRP(10)),
+
1018 domain(domainID));
+
1019 env.close();
+
1020
+
1021 BEAST_EXPECT(
+
1022 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
1023
+
1024 // alice's unfunded offer is removed implicitly
+
1025 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
+
1026 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1));
+
1027 }
1028
-
1029 // remove alice from domain and thus alice's offer becomes unfunded
-
1030 env(credentials::deleteCred(domainOwner, alice, domainOwner, credType));
-
1031 env.close();
-
1032
-
1033 env(pay(gw, carol, USD(10)),
-
1034 path(~USD),
-
1035 sendmax(XRP(10)),
-
1036 domain(domainID));
-
1037 env.close();
+
1029 void
+
1030 testAmmNotUsed(FeatureBitset features)
+
1031 {
+
1032 testcase("AMM not used");
+
1033
+
1034 Env env(*this, features);
+
1035 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1036 PermissionedDEX(env);
+
1037 AMM amm(env, alice, XRP(10), USD(50));
1038
-
1039 BEAST_EXPECT(
-
1040 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
-
1041
-
1042 // alice's unfunded offer is removed implicitly
-
1043 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
-
1044 BEAST_EXPECT(checkDirectorySize(env, *domainDirKey, 1));
-
1045 }
+
1039 // a domain payment isn't able to consume AMM
+
1040 env(pay(bob, carol, USD(5)),
+
1041 path(~USD),
+
1042 sendmax(XRP(5)),
+
1043 domain(domainID),
+
1044 ter(tecPATH_PARTIAL));
+
1045 env.close();
1046
-
1047 void
-
1048 testAmmNotUsed(FeatureBitset features)
-
1049 {
-
1050 testcase("AMM not used");
-
1051
-
1052 Env env(*this, features);
-
1053 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1054 PermissionedDEX(env);
-
1055 AMM amm(env, alice, XRP(10), USD(50));
-
1056
-
1057 // a domain payment isn't able to consume AMM
-
1058 env(pay(bob, carol, USD(5)),
-
1059 path(~USD),
-
1060 sendmax(XRP(5)),
-
1061 domain(domainID),
-
1062 ter(tecPATH_PARTIAL));
-
1063 env.close();
-
1064
-
1065 // a non domain payment can use AMM
-
1066 env(pay(bob, carol, USD(5)), path(~USD), sendmax(XRP(5)));
-
1067 env.close();
-
1068
-
1069 // USD amount in AMM is changed
-
1070 auto [xrp, usd, lpt] = amm.balances(XRP, USD);
-
1071 BEAST_EXPECT(usd == USD(45));
-
1072 }
-
1073
-
1074 void
-
1075 testHybridOfferCreate(FeatureBitset features)
-
1076 {
-
1077 testcase("Hybrid offer create");
-
1078
-
1079 // test preflight - invalid hybrid flag
-
1080 {
-
1081 Env env(*this, features - featurePermissionedDEX);
-
1082 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1083 PermissionedDEX(env);
-
1084
-
1085 env(offer(bob, XRP(10), USD(10)),
-
1086 domain(domainID),
-
1087 txflags(tfHybrid),
-
1088 ter(temDISABLED));
-
1089 env.close();
-
1090
-
1091 env(offer(bob, XRP(10), USD(10)),
-
1092 txflags(tfHybrid),
-
1093 ter(temINVALID_FLAG));
-
1094 env.close();
-
1095
-
1096 env.enableFeature(featurePermissionedDEX);
-
1097 env.close();
-
1098
-
1099 // hybrid offer must have domainID
-
1100 env(offer(bob, XRP(10), USD(10)),
-
1101 txflags(tfHybrid),
-
1102 ter(temINVALID_FLAG));
-
1103 env.close();
-
1104
-
1105 // hybrid offer must have domainID
-
1106 auto const offerSeq{env.seq(bob)};
-
1107 env(offer(bob, XRP(10), USD(10)),
-
1108 txflags(tfHybrid),
-
1109 domain(domainID));
-
1110 env.close();
-
1111 BEAST_EXPECT(checkOffer(
-
1112 env, bob, offerSeq, XRP(10), USD(10), lsfHybrid, true));
-
1113 }
-
1114
-
1115 // apply - domain offer can cross with hybrid
-
1116 {
-
1117 Env env(*this, features);
-
1118 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1119 PermissionedDEX(env);
-
1120
-
1121 auto const bobOfferSeq{env.seq(bob)};
-
1122 env(offer(bob, XRP(10), USD(10)),
-
1123 txflags(tfHybrid),
-
1124 domain(domainID));
-
1125 env.close();
-
1126
-
1127 BEAST_EXPECT(checkOffer(
-
1128 env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true));
-
1129 BEAST_EXPECT(offerExists(env, bob, bobOfferSeq));
-
1130 BEAST_EXPECT(ownerCount(env, bob) == 3);
-
1131
-
1132 auto const aliceOfferSeq{env.seq(alice)};
-
1133 env(offer(alice, USD(10), XRP(10)), domain(domainID));
-
1134 env.close();
-
1135
-
1136 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
-
1137 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
-
1138 BEAST_EXPECT(ownerCount(env, alice) == 2);
-
1139 }
-
1140
-
1141 // apply - open offer can cross with hybrid
-
1142 {
-
1143 Env env(*this, features);
-
1144 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1145 PermissionedDEX(env);
-
1146
-
1147 auto const bobOfferSeq{env.seq(bob)};
-
1148 env(offer(bob, XRP(10), USD(10)),
-
1149 txflags(tfHybrid),
-
1150 domain(domainID));
-
1151 env.close();
-
1152
-
1153 BEAST_EXPECT(offerExists(env, bob, bobOfferSeq));
-
1154 BEAST_EXPECT(ownerCount(env, bob) == 3);
-
1155 BEAST_EXPECT(checkOffer(
-
1156 env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true));
-
1157
-
1158 auto const aliceOfferSeq{env.seq(alice)};
-
1159 env(offer(alice, USD(10), XRP(10)));
-
1160 env.close();
-
1161
-
1162 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
-
1163 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
-
1164 BEAST_EXPECT(ownerCount(env, alice) == 2);
-
1165 }
-
1166
-
1167 // apply - by default, hybrid offer tries to cross with offers in the
-
1168 // domain book
-
1169 {
-
1170 Env env(*this, features);
-
1171 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1172 PermissionedDEX(env);
-
1173
-
1174 auto const bobOfferSeq{env.seq(bob)};
-
1175 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
1176 env.close();
-
1177
-
1178 BEAST_EXPECT(
-
1179 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
-
1180 BEAST_EXPECT(ownerCount(env, bob) == 3);
-
1181
-
1182 // hybrid offer auto crosses with domain offer
-
1183 auto const aliceOfferSeq{env.seq(alice)};
-
1184 env(offer(alice, USD(10), XRP(10)),
-
1185 domain(domainID),
-
1186 txflags(tfHybrid));
-
1187 env.close();
-
1188
-
1189 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
-
1190 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
-
1191 BEAST_EXPECT(ownerCount(env, alice) == 2);
-
1192 }
-
1193
-
1194 // apply - hybrid offer does not automatically cross with open offers
-
1195 // because by default, it only tries to cross domain offers
-
1196 {
-
1197 Env env(*this, features);
-
1198 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1199 PermissionedDEX(env);
-
1200
-
1201 auto const bobOfferSeq{env.seq(bob)};
-
1202 env(offer(bob, XRP(10), USD(10)));
-
1203 env.close();
-
1204
-
1205 BEAST_EXPECT(
-
1206 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, false));
-
1207 BEAST_EXPECT(ownerCount(env, bob) == 3);
-
1208
-
1209 // hybrid offer auto crosses with domain offer
-
1210 auto const aliceOfferSeq{env.seq(alice)};
-
1211 env(offer(alice, USD(10), XRP(10)),
-
1212 domain(domainID),
-
1213 txflags(tfHybrid));
-
1214 env.close();
-
1215
-
1216 BEAST_EXPECT(offerExists(env, alice, aliceOfferSeq));
-
1217 BEAST_EXPECT(offerExists(env, bob, bobOfferSeq));
-
1218 BEAST_EXPECT(
-
1219 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, false));
-
1220 BEAST_EXPECT(checkOffer(
-
1221 env, alice, aliceOfferSeq, USD(10), XRP(10), lsfHybrid, true));
-
1222 BEAST_EXPECT(ownerCount(env, alice) == 3);
-
1223 }
-
1224 }
-
1225
-
1226 void
-
1227 testHybridInvalidOffer(FeatureBitset features)
-
1228 {
-
1229 testcase("Hybrid invalid offer");
-
1230
-
1231 // bob has a hybrid offer and then he is removed from domain.
-
1232 // in this case, the hybrid offer will be considered as unfunded even in
-
1233 // a regular payment
-
1234 Env env(*this, features);
-
1235 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1236 PermissionedDEX(env);
-
1237
-
1238 auto const hybridOfferSeq{env.seq(bob)};
-
1239 env(offer(bob, XRP(50), USD(50)), txflags(tfHybrid), domain(domainID));
-
1240 env.close();
-
1241
-
1242 // remove bob from domain
-
1243 env(credentials::deleteCred(domainOwner, bob, domainOwner, credType));
-
1244 env.close();
-
1245
-
1246 // bob's hybrid offer is unfunded and can not be consumed in a domain
-
1247 // payment
-
1248 env(pay(alice, carol, USD(5)),
-
1249 path(~USD),
-
1250 sendmax(XRP(5)),
-
1251 domain(domainID),
-
1252 ter(tecPATH_PARTIAL));
-
1253 env.close();
-
1254 BEAST_EXPECT(checkOffer(
-
1255 env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true));
-
1256
-
1257 // bob's unfunded hybrid offer can't be consumed even with a regular
-
1258 // payment
-
1259 env(pay(alice, carol, USD(5)),
-
1260 path(~USD),
-
1261 sendmax(XRP(5)),
-
1262 ter(tecPATH_PARTIAL));
-
1263 env.close();
-
1264 BEAST_EXPECT(checkOffer(
-
1265 env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true));
-
1266
-
1267 // create a regular offer
-
1268 auto const regularOfferSeq{env.seq(bob)};
-
1269 env(offer(bob, XRP(10), USD(10)));
-
1270 env.close();
-
1271 BEAST_EXPECT(offerExists(env, bob, regularOfferSeq));
-
1272 BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10)));
+
1047 // a non domain payment can use AMM
+
1048 env(pay(bob, carol, USD(5)), path(~USD), sendmax(XRP(5)));
+
1049 env.close();
+
1050
+
1051 // USD amount in AMM is changed
+
1052 auto [xrp, usd, lpt] = amm.balances(XRP, USD);
+
1053 BEAST_EXPECT(usd == USD(45));
+
1054 }
+
1055
+
1056 void
+
1057 testHybridOfferCreate(FeatureBitset features)
+
1058 {
+
1059 testcase("Hybrid offer create");
+
1060
+
1061 // test preflight - invalid hybrid flag
+
1062 {
+
1063 Env env(*this, features - featurePermissionedDEX);
+
1064 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1065 PermissionedDEX(env);
+
1066
+
1067 env(offer(bob, XRP(10), USD(10)),
+
1068 domain(domainID),
+
1069 txflags(tfHybrid),
+
1070 ter(temDISABLED));
+
1071 env.close();
+
1072
+
1073 env(offer(bob, XRP(10), USD(10)),
+
1074 txflags(tfHybrid),
+
1075 ter(temINVALID_FLAG));
+
1076 env.close();
+
1077
+
1078 env.enableFeature(featurePermissionedDEX);
+
1079 env.close();
+
1080
+
1081 // hybrid offer must have domainID
+
1082 env(offer(bob, XRP(10), USD(10)),
+
1083 txflags(tfHybrid),
+
1084 ter(temINVALID_FLAG));
+
1085 env.close();
+
1086
+
1087 // hybrid offer must have domainID
+
1088 auto const offerSeq{env.seq(bob)};
+
1089 env(offer(bob, XRP(10), USD(10)),
+
1090 txflags(tfHybrid),
+
1091 domain(domainID));
+
1092 env.close();
+
1093 BEAST_EXPECT(checkOffer(
+
1094 env, bob, offerSeq, XRP(10), USD(10), lsfHybrid, true));
+
1095 }
+
1096
+
1097 // apply - domain offer can cross with hybrid
+
1098 {
+
1099 Env env(*this, features);
+
1100 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1101 PermissionedDEX(env);
+
1102
+
1103 auto const bobOfferSeq{env.seq(bob)};
+
1104 env(offer(bob, XRP(10), USD(10)),
+
1105 txflags(tfHybrid),
+
1106 domain(domainID));
+
1107 env.close();
+
1108
+
1109 BEAST_EXPECT(checkOffer(
+
1110 env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true));
+
1111 BEAST_EXPECT(offerExists(env, bob, bobOfferSeq));
+
1112 BEAST_EXPECT(ownerCount(env, bob) == 3);
+
1113
+
1114 auto const aliceOfferSeq{env.seq(alice)};
+
1115 env(offer(alice, USD(10), XRP(10)), domain(domainID));
+
1116 env.close();
+
1117
+
1118 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
+
1119 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
+
1120 BEAST_EXPECT(ownerCount(env, alice) == 2);
+
1121 }
+
1122
+
1123 // apply - open offer can cross with hybrid
+
1124 {
+
1125 Env env(*this, features);
+
1126 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1127 PermissionedDEX(env);
+
1128
+
1129 auto const bobOfferSeq{env.seq(bob)};
+
1130 env(offer(bob, XRP(10), USD(10)),
+
1131 txflags(tfHybrid),
+
1132 domain(domainID));
+
1133 env.close();
+
1134
+
1135 BEAST_EXPECT(offerExists(env, bob, bobOfferSeq));
+
1136 BEAST_EXPECT(ownerCount(env, bob) == 3);
+
1137 BEAST_EXPECT(checkOffer(
+
1138 env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true));
+
1139
+
1140 auto const aliceOfferSeq{env.seq(alice)};
+
1141 env(offer(alice, USD(10), XRP(10)));
+
1142 env.close();
+
1143
+
1144 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
+
1145 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
+
1146 BEAST_EXPECT(ownerCount(env, alice) == 2);
+
1147 }
+
1148
+
1149 // apply - by default, hybrid offer tries to cross with offers in the
+
1150 // domain book
+
1151 {
+
1152 Env env(*this, features);
+
1153 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1154 PermissionedDEX(env);
+
1155
+
1156 auto const bobOfferSeq{env.seq(bob)};
+
1157 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
1158 env.close();
+
1159
+
1160 BEAST_EXPECT(
+
1161 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, true));
+
1162 BEAST_EXPECT(ownerCount(env, bob) == 3);
+
1163
+
1164 // hybrid offer auto crosses with domain offer
+
1165 auto const aliceOfferSeq{env.seq(alice)};
+
1166 env(offer(alice, USD(10), XRP(10)),
+
1167 domain(domainID),
+
1168 txflags(tfHybrid));
+
1169 env.close();
+
1170
+
1171 BEAST_EXPECT(!offerExists(env, alice, aliceOfferSeq));
+
1172 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
+
1173 BEAST_EXPECT(ownerCount(env, alice) == 2);
+
1174 }
+
1175
+
1176 // apply - hybrid offer does not automatically cross with open offers
+
1177 // because by default, it only tries to cross domain offers
+
1178 {
+
1179 Env env(*this, features);
+
1180 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1181 PermissionedDEX(env);
+
1182
+
1183 auto const bobOfferSeq{env.seq(bob)};
+
1184 env(offer(bob, XRP(10), USD(10)));
+
1185 env.close();
+
1186
+
1187 BEAST_EXPECT(
+
1188 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, false));
+
1189 BEAST_EXPECT(ownerCount(env, bob) == 3);
+
1190
+
1191 // hybrid offer auto crosses with domain offer
+
1192 auto const aliceOfferSeq{env.seq(alice)};
+
1193 env(offer(alice, USD(10), XRP(10)),
+
1194 domain(domainID),
+
1195 txflags(tfHybrid));
+
1196 env.close();
+
1197
+
1198 BEAST_EXPECT(offerExists(env, alice, aliceOfferSeq));
+
1199 BEAST_EXPECT(offerExists(env, bob, bobOfferSeq));
+
1200 BEAST_EXPECT(
+
1201 checkOffer(env, bob, bobOfferSeq, XRP(10), USD(10), 0, false));
+
1202 BEAST_EXPECT(checkOffer(
+
1203 env, alice, aliceOfferSeq, USD(10), XRP(10), lsfHybrid, true));
+
1204 BEAST_EXPECT(ownerCount(env, alice) == 3);
+
1205 }
+
1206 }
+
1207
+
1208 void
+
1209 testHybridInvalidOffer(FeatureBitset features)
+
1210 {
+
1211 testcase("Hybrid invalid offer");
+
1212
+
1213 // bob has a hybrid offer and then he is removed from domain.
+
1214 // in this case, the hybrid offer will be considered as unfunded even in
+
1215 // a regular payment
+
1216 Env env(*this, features);
+
1217 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1218 PermissionedDEX(env);
+
1219
+
1220 auto const hybridOfferSeq{env.seq(bob)};
+
1221 env(offer(bob, XRP(50), USD(50)), txflags(tfHybrid), domain(domainID));
+
1222 env.close();
+
1223
+
1224 // remove bob from domain
+
1225 env(credentials::deleteCred(domainOwner, bob, domainOwner, credType));
+
1226 env.close();
+
1227
+
1228 // bob's hybrid offer is unfunded and can not be consumed in a domain
+
1229 // payment
+
1230 env(pay(alice, carol, USD(5)),
+
1231 path(~USD),
+
1232 sendmax(XRP(5)),
+
1233 domain(domainID),
+
1234 ter(tecPATH_PARTIAL));
+
1235 env.close();
+
1236 BEAST_EXPECT(checkOffer(
+
1237 env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true));
+
1238
+
1239 // bob's unfunded hybrid offer can't be consumed even with a regular
+
1240 // payment
+
1241 env(pay(alice, carol, USD(5)),
+
1242 path(~USD),
+
1243 sendmax(XRP(5)),
+
1244 ter(tecPATH_PARTIAL));
+
1245 env.close();
+
1246 BEAST_EXPECT(checkOffer(
+
1247 env, bob, hybridOfferSeq, XRP(50), USD(50), lsfHybrid, true));
+
1248
+
1249 // create a regular offer
+
1250 auto const regularOfferSeq{env.seq(bob)};
+
1251 env(offer(bob, XRP(10), USD(10)));
+
1252 env.close();
+
1253 BEAST_EXPECT(offerExists(env, bob, regularOfferSeq));
+
1254 BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(10), USD(10)));
+
1255
+
1256 auto const sleHybridOffer =
+
1257 env.le(keylet::offer(bob.id(), hybridOfferSeq));
+
1258 BEAST_EXPECT(sleHybridOffer);
+
1259 auto const openDir =
+
1260 sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(
+
1261 sfBookDirectory);
+
1262 BEAST_EXPECT(checkDirectorySize(env, openDir, 2));
+
1263
+
1264 // this normal payment should consume the regular offer and remove the
+
1265 // unfunded hybrid offer
+
1266 env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)));
+
1267 env.close();
+
1268
+
1269 BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq));
+
1270 BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(5), USD(5)));
+
1271 BEAST_EXPECT(checkDirectorySize(env, openDir, 1));
+
1272 }
1273
-
1274 auto const sleHybridOffer =
-
1275 env.le(keylet::offer(bob.id(), hybridOfferSeq));
-
1276 BEAST_EXPECT(sleHybridOffer);
-
1277 auto const openDir =
-
1278 sleHybridOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(
-
1279 sfBookDirectory);
-
1280 BEAST_EXPECT(checkDirectorySize(env, openDir, 2));
-
1281
-
1282 // this normal payment should consume the regular offer and remove the
-
1283 // unfunded hybrid offer
-
1284 env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)));
-
1285 env.close();
-
1286
-
1287 BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq));
-
1288 BEAST_EXPECT(checkOffer(env, bob, regularOfferSeq, XRP(5), USD(5)));
-
1289 BEAST_EXPECT(checkDirectorySize(env, openDir, 1));
-
1290 }
-
1291
-
1292 void
-
1293 testHybridBookStep(FeatureBitset features)
-
1294 {
-
1295 testcase("Hybrid book step");
-
1296
-
1297 // both non domain and domain payments can consume hybrid offer
-
1298 {
-
1299 Env env(*this, features);
-
1300 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1301 PermissionedDEX(env);
+
1274 void
+
1275 testHybridBookStep(FeatureBitset features)
+
1276 {
+
1277 testcase("Hybrid book step");
+
1278
+
1279 // both non domain and domain payments can consume hybrid offer
+
1280 {
+
1281 Env env(*this, features);
+
1282 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1283 PermissionedDEX(env);
+
1284
+
1285 auto const hybridOfferSeq{env.seq(bob)};
+
1286 env(offer(bob, XRP(10), USD(10)),
+
1287 txflags(tfHybrid),
+
1288 domain(domainID));
+
1289 env.close();
+
1290
+
1291 env(pay(alice, carol, USD(5)),
+
1292 path(~USD),
+
1293 sendmax(XRP(5)),
+
1294 domain(domainID));
+
1295 env.close();
+
1296 BEAST_EXPECT(checkOffer(
+
1297 env, bob, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true));
+
1298
+
1299 // hybrid offer can't be consumed since bob is not in domain anymore
+
1300 env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)));
+
1301 env.close();
1302
-
1303 auto const hybridOfferSeq{env.seq(bob)};
-
1304 env(offer(bob, XRP(10), USD(10)),
-
1305 txflags(tfHybrid),
-
1306 domain(domainID));
-
1307 env.close();
-
1308
-
1309 env(pay(alice, carol, USD(5)),
-
1310 path(~USD),
-
1311 sendmax(XRP(5)),
-
1312 domain(domainID));
-
1313 env.close();
-
1314 BEAST_EXPECT(checkOffer(
-
1315 env, bob, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true));
-
1316
-
1317 // hybrid offer can't be consumed since bob is not in domain anymore
-
1318 env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)));
-
1319 env.close();
-
1320
-
1321 BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq));
-
1322 }
-
1323
-
1324 // someone from another domain can't cross hybrid if they specified
-
1325 // wrong domainID
-
1326 {
-
1327 Env env(*this, features);
-
1328 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1329 PermissionedDEX(env);
+
1303 BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq));
+
1304 }
+
1305
+
1306 // someone from another domain can't cross hybrid if they specified
+
1307 // wrong domainID
+
1308 {
+
1309 Env env(*this, features);
+
1310 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1311 PermissionedDEX(env);
+
1312
+
1313 // Fund accounts
+
1314 Account badDomainOwner("badDomainOwner");
+
1315 Account devin("devin");
+
1316 env.fund(XRP(1000), badDomainOwner, devin);
+
1317 env.close();
+
1318
+
1319 auto const badCredType = "badCred";
+
1320 pdomain::Credentials credentials{{badDomainOwner, badCredType}};
+
1321 env(pdomain::setTx(badDomainOwner, credentials));
+
1322
+
1323 auto objects = pdomain::getObjects(badDomainOwner, env);
+
1324 auto const badDomainID = objects.begin()->first;
+
1325
+
1326 env(credentials::create(devin, badDomainOwner, badCredType));
+
1327 env.close();
+
1328 env(credentials::accept(devin, badDomainOwner, badCredType));
+
1329 env.close();
1330
-
1331 // Fund accounts
-
1332 Account badDomainOwner("badDomainOwner");
-
1333 Account devin("devin");
-
1334 env.fund(XRP(1000), badDomainOwner, devin);
+
1331 auto const hybridOfferSeq{env.seq(bob)};
+
1332 env(offer(bob, XRP(10), USD(10)),
+
1333 txflags(tfHybrid),
+
1334 domain(domainID));
1335 env.close();
1336
-
1337 auto const badCredType = "badCred";
-
1338 pdomain::Credentials credentials{{badDomainOwner, badCredType}};
-
1339 env(pdomain::setTx(badDomainOwner, credentials));
-
1340
-
1341 auto objects = pdomain::getObjects(badDomainOwner, env);
-
1342 auto const badDomainID = objects.begin()->first;
-
1343
-
1344 env(credentials::create(devin, badDomainOwner, badCredType));
-
1345 env.close();
-
1346 env(credentials::accept(devin, badDomainOwner, badCredType));
-
1347 env.close();
-
1348
-
1349 auto const hybridOfferSeq{env.seq(bob)};
-
1350 env(offer(bob, XRP(10), USD(10)),
-
1351 txflags(tfHybrid),
-
1352 domain(domainID));
-
1353 env.close();
+
1337 // other domains can't consume the offer
+
1338 env(pay(devin, badDomainOwner, USD(5)),
+
1339 path(~USD),
+
1340 sendmax(XRP(5)),
+
1341 domain(badDomainID),
+
1342 ter(tecPATH_DRY));
+
1343 env.close();
+
1344 BEAST_EXPECT(checkOffer(
+
1345 env, bob, hybridOfferSeq, XRP(10), USD(10), lsfHybrid, true));
+
1346
+
1347 env(pay(alice, carol, USD(5)),
+
1348 path(~USD),
+
1349 sendmax(XRP(5)),
+
1350 domain(domainID));
+
1351 env.close();
+
1352 BEAST_EXPECT(checkOffer(
+
1353 env, bob, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true));
1354
-
1355 // other domains can't consume the offer
-
1356 env(pay(devin, badDomainOwner, USD(5)),
-
1357 path(~USD),
-
1358 sendmax(XRP(5)),
-
1359 domain(badDomainID),
-
1360 ter(tecPATH_DRY));
-
1361 env.close();
-
1362 BEAST_EXPECT(checkOffer(
-
1363 env, bob, hybridOfferSeq, XRP(10), USD(10), lsfHybrid, true));
-
1364
-
1365 env(pay(alice, carol, USD(5)),
-
1366 path(~USD),
-
1367 sendmax(XRP(5)),
-
1368 domain(domainID));
-
1369 env.close();
-
1370 BEAST_EXPECT(checkOffer(
-
1371 env, bob, hybridOfferSeq, XRP(5), USD(5), lsfHybrid, true));
-
1372
-
1373 // hybrid offer can't be consumed since bob is not in domain anymore
-
1374 env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)));
-
1375 env.close();
-
1376
-
1377 BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq));
-
1378 }
-
1379
-
1380 // test domain payment consuming two offers w/ hybrid offer
-
1381 {
-
1382 Env env(*this, features);
-
1383 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1384 PermissionedDEX(env);
-
1385
-
1386 auto const EUR = gw["EUR"];
-
1387 env.trust(EUR(1000), alice);
-
1388 env.close();
-
1389 env.trust(EUR(1000), bob);
-
1390 env.close();
-
1391 env.trust(EUR(1000), carol);
-
1392 env.close();
-
1393 env(pay(gw, bob, EUR(100)));
-
1394 env.close();
-
1395
-
1396 auto const usdOfferSeq{env.seq(bob)};
-
1397 env(offer(bob, XRP(10), USD(10)), domain(domainID));
-
1398 env.close();
-
1399
-
1400 BEAST_EXPECT(
-
1401 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
-
1402
-
1403 // payment fail because there isn't eur offer
-
1404 env(pay(alice, carol, EUR(5)),
-
1405 path(~USD, ~EUR),
-
1406 sendmax(XRP(5)),
-
1407 domain(domainID),
-
1408 ter(tecPATH_PARTIAL));
+
1355 // hybrid offer can't be consumed since bob is not in domain anymore
+
1356 env(pay(alice, carol, USD(5)), path(~USD), sendmax(XRP(5)));
+
1357 env.close();
+
1358
+
1359 BEAST_EXPECT(!offerExists(env, bob, hybridOfferSeq));
+
1360 }
+
1361
+
1362 // test domain payment consuming two offers w/ hybrid offer
+
1363 {
+
1364 Env env(*this, features);
+
1365 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1366 PermissionedDEX(env);
+
1367
+
1368 auto const EUR = gw["EUR"];
+
1369 env.trust(EUR(1000), alice);
+
1370 env.close();
+
1371 env.trust(EUR(1000), bob);
+
1372 env.close();
+
1373 env.trust(EUR(1000), carol);
+
1374 env.close();
+
1375 env(pay(gw, bob, EUR(100)));
+
1376 env.close();
+
1377
+
1378 auto const usdOfferSeq{env.seq(bob)};
+
1379 env(offer(bob, XRP(10), USD(10)), domain(domainID));
+
1380 env.close();
+
1381
+
1382 BEAST_EXPECT(
+
1383 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
+
1384
+
1385 // payment fail because there isn't eur offer
+
1386 env(pay(alice, carol, EUR(5)),
+
1387 path(~USD, ~EUR),
+
1388 sendmax(XRP(5)),
+
1389 domain(domainID),
+
1390 ter(tecPATH_PARTIAL));
+
1391 env.close();
+
1392 BEAST_EXPECT(
+
1393 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
+
1394
+
1395 // bob creates a hybrid eur offer
+
1396 auto const eurOfferSeq{env.seq(bob)};
+
1397 env(offer(bob, USD(10), EUR(10)),
+
1398 domain(domainID),
+
1399 txflags(tfHybrid));
+
1400 env.close();
+
1401 BEAST_EXPECT(checkOffer(
+
1402 env, bob, eurOfferSeq, USD(10), EUR(10), lsfHybrid, true));
+
1403
+
1404 // alice successfully consume two domain offers: xrp/usd and usd/eur
+
1405 env(pay(alice, carol, EUR(5)),
+
1406 path(~USD, ~EUR),
+
1407 sendmax(XRP(5)),
+
1408 domain(domainID));
1409 env.close();
-
1410 BEAST_EXPECT(
-
1411 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, true));
-
1412
-
1413 // bob creates a hybrid eur offer
-
1414 auto const eurOfferSeq{env.seq(bob)};
-
1415 env(offer(bob, USD(10), EUR(10)),
-
1416 domain(domainID),
-
1417 txflags(tfHybrid));
-
1418 env.close();
-
1419 BEAST_EXPECT(checkOffer(
-
1420 env, bob, eurOfferSeq, USD(10), EUR(10), lsfHybrid, true));
-
1421
-
1422 // alice successfully consume two domain offers: xrp/usd and usd/eur
-
1423 env(pay(alice, carol, EUR(5)),
-
1424 path(~USD, ~EUR),
-
1425 sendmax(XRP(5)),
-
1426 domain(domainID));
+
1410
+
1411 BEAST_EXPECT(
+
1412 checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, true));
+
1413 BEAST_EXPECT(checkOffer(
+
1414 env, bob, eurOfferSeq, USD(5), EUR(5), lsfHybrid, true));
+
1415 }
+
1416
+
1417 // test regular payment using a regular offer and a hybrid offer
+
1418 {
+
1419 Env env(*this, features);
+
1420 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1421 PermissionedDEX(env);
+
1422
+
1423 auto const EUR = gw["EUR"];
+
1424 env.trust(EUR(1000), alice);
+
1425 env.close();
+
1426 env.trust(EUR(1000), bob);
1427 env.close();
-
1428
-
1429 BEAST_EXPECT(
-
1430 checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, true));
-
1431 BEAST_EXPECT(checkOffer(
-
1432 env, bob, eurOfferSeq, USD(5), EUR(5), lsfHybrid, true));
-
1433 }
-
1434
-
1435 // test regular payment using a regular offer and a hybrid offer
-
1436 {
-
1437 Env env(*this, features);
-
1438 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1439 PermissionedDEX(env);
+
1428 env.trust(EUR(1000), carol);
+
1429 env.close();
+
1430 env(pay(gw, bob, EUR(100)));
+
1431 env.close();
+
1432
+
1433 // bob creates a regular usd offer
+
1434 auto const usdOfferSeq{env.seq(bob)};
+
1435 env(offer(bob, XRP(10), USD(10)));
+
1436 env.close();
+
1437
+
1438 BEAST_EXPECT(
+
1439 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, false));
1440
-
1441 auto const EUR = gw["EUR"];
-
1442 env.trust(EUR(1000), alice);
-
1443 env.close();
-
1444 env.trust(EUR(1000), bob);
-
1445 env.close();
-
1446 env.trust(EUR(1000), carol);
-
1447 env.close();
-
1448 env(pay(gw, bob, EUR(100)));
-
1449 env.close();
-
1450
-
1451 // bob creates a regular usd offer
-
1452 auto const usdOfferSeq{env.seq(bob)};
-
1453 env(offer(bob, XRP(10), USD(10)));
-
1454 env.close();
-
1455
-
1456 BEAST_EXPECT(
-
1457 checkOffer(env, bob, usdOfferSeq, XRP(10), USD(10), 0, false));
-
1458
-
1459 // bob creates a hybrid eur offer
-
1460 auto const eurOfferSeq{env.seq(bob)};
-
1461 env(offer(bob, USD(10), EUR(10)),
-
1462 domain(domainID),
-
1463 txflags(tfHybrid));
-
1464 env.close();
-
1465 BEAST_EXPECT(checkOffer(
-
1466 env, bob, eurOfferSeq, USD(10), EUR(10), lsfHybrid, true));
+
1441 // bob creates a hybrid eur offer
+
1442 auto const eurOfferSeq{env.seq(bob)};
+
1443 env(offer(bob, USD(10), EUR(10)),
+
1444 domain(domainID),
+
1445 txflags(tfHybrid));
+
1446 env.close();
+
1447 BEAST_EXPECT(checkOffer(
+
1448 env, bob, eurOfferSeq, USD(10), EUR(10), lsfHybrid, true));
+
1449
+
1450 // alice successfully consume two offers: xrp/usd and usd/eur
+
1451 env(pay(alice, carol, EUR(5)), path(~USD, ~EUR), sendmax(XRP(5)));
+
1452 env.close();
+
1453
+
1454 BEAST_EXPECT(
+
1455 checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, false));
+
1456 BEAST_EXPECT(checkOffer(
+
1457 env, bob, eurOfferSeq, USD(5), EUR(5), lsfHybrid, true));
+
1458 }
+
1459 }
+
1460
+
1461 void
+
1462 testHybridOfferDirectories(FeatureBitset features)
+
1463 {
+
1464 Env env(*this, features);
+
1465 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1466 PermissionedDEX(env);
1467
-
1468 // alice successfully consume two offers: xrp/usd and usd/eur
-
1469 env(pay(alice, carol, EUR(5)), path(~USD, ~EUR), sendmax(XRP(5)));
-
1470 env.close();
-
1471
-
1472 BEAST_EXPECT(
-
1473 checkOffer(env, bob, usdOfferSeq, XRP(5), USD(5), 0, false));
-
1474 BEAST_EXPECT(checkOffer(
-
1475 env, bob, eurOfferSeq, USD(5), EUR(5), lsfHybrid, true));
-
1476 }
-
1477 }
+
1468 std::vector<std::uint32_t> offerSeqs;
+
1469 offerSeqs.reserve(100);
+
1470
+
1471 Book domainBook{Issue(XRP), Issue(USD), domainID};
+
1472 Book openBook{Issue(XRP), Issue(USD), std::nullopt};
+
1473
+
1474 auto const domainDir = getBookDirKey(domainBook, XRP(10), USD(10));
+
1475 auto const openDir = getBookDirKey(openBook, XRP(10), USD(10));
+
1476
+
1477 size_t dirCnt = 100;
1478
-
1479 void
-
1480 testHybridOfferDirectories(FeatureBitset features)
-
1481 {
-
1482 Env env(*this, features);
-
1483 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1484 PermissionedDEX(env);
-
1485
-
1486 std::vector<std::uint32_t> offerSeqs;
-
1487 offerSeqs.reserve(100);
-
1488
-
1489 Book domainBook{Issue(XRP), Issue(USD), domainID};
-
1490 Book openBook{Issue(XRP), Issue(USD), std::nullopt};
-
1491
-
1492 auto const domainDir = getBookDirKey(domainBook, XRP(10), USD(10));
-
1493 auto const openDir = getBookDirKey(openBook, XRP(10), USD(10));
-
1494
-
1495 size_t dirCnt = 100;
+
1479 for (size_t i = 1; i <= dirCnt; i++)
+
1480 {
+
1481 auto const bobOfferSeq{env.seq(bob)};
+
1482 offerSeqs.emplace_back(bobOfferSeq);
+
1483 env(offer(bob, XRP(10), USD(10)),
+
1484 txflags(tfHybrid),
+
1485 domain(domainID));
+
1486 env.close();
+
1487
+
1488 auto const sleOffer = env.le(keylet::offer(bob.id(), bobOfferSeq));
+
1489 BEAST_EXPECT(sleOffer);
+
1490 BEAST_EXPECT(sleOffer->getFieldH256(sfBookDirectory) == domainDir);
+
1491 BEAST_EXPECT(
+
1492 sleOffer->getFieldArray(sfAdditionalBooks).size() == 1);
+
1493 BEAST_EXPECT(
+
1494 sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(
+
1495 sfBookDirectory) == openDir);
1496
-
1497 for (size_t i = 1; i <= dirCnt; i++)
-
1498 {
-
1499 auto const bobOfferSeq{env.seq(bob)};
-
1500 offerSeqs.emplace_back(bobOfferSeq);
-
1501 env(offer(bob, XRP(10), USD(10)),
-
1502 txflags(tfHybrid),
-
1503 domain(domainID));
-
1504 env.close();
-
1505
-
1506 auto const sleOffer = env.le(keylet::offer(bob.id(), bobOfferSeq));
-
1507 BEAST_EXPECT(sleOffer);
-
1508 BEAST_EXPECT(sleOffer->getFieldH256(sfBookDirectory) == domainDir);
-
1509 BEAST_EXPECT(
-
1510 sleOffer->getFieldArray(sfAdditionalBooks).size() == 1);
-
1511 BEAST_EXPECT(
-
1512 sleOffer->getFieldArray(sfAdditionalBooks)[0].getFieldH256(
-
1513 sfBookDirectory) == openDir);
-
1514
-
1515 BEAST_EXPECT(checkOffer(
-
1516 env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true));
-
1517 BEAST_EXPECT(checkDirectorySize(env, domainDir, i));
-
1518 BEAST_EXPECT(checkDirectorySize(env, openDir, i));
-
1519 }
-
1520
-
1521 for (auto const offerSeq : offerSeqs)
-
1522 {
-
1523 env(offer_cancel(bob, offerSeq));
-
1524 env.close();
-
1525 dirCnt--;
-
1526 BEAST_EXPECT(!offerExists(env, bob, offerSeq));
-
1527 BEAST_EXPECT(checkDirectorySize(env, domainDir, dirCnt));
-
1528 BEAST_EXPECT(checkDirectorySize(env, openDir, dirCnt));
-
1529 }
-
1530 }
-
1531
-
1532 void
-
1533 testAutoBridge(FeatureBitset features)
-
1534 {
-
1535 testcase("Auto bridge");
-
1536
-
1537 Env env(*this, features);
-
1538 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
-
1539 PermissionedDEX(env);
-
1540 auto const EUR = gw["EUR"];
-
1541
-
1542 for (auto const& account : {alice, bob, carol})
-
1543 {
-
1544 env(trust(account, EUR(10000)));
-
1545 env.close();
-
1546 }
-
1547
-
1548 env(pay(gw, carol, EUR(1)));
-
1549 env.close();
-
1550
-
1551 auto const aliceOfferSeq{env.seq(alice)};
-
1552 auto const bobOfferSeq{env.seq(bob)};
-
1553 env(offer(alice, XRP(100), USD(1)), domain(domainID));
-
1554 env(offer(bob, EUR(1), XRP(100)), domain(domainID));
-
1555 env.close();
-
1556
-
1557 // carol's offer should cross bob and alice's offers due to auto
-
1558 // bridging
-
1559 auto const carolOfferSeq{env.seq(carol)};
-
1560 env(offer(carol, USD(1), EUR(1)), domain(domainID));
-
1561 env.close();
-
1562
-
1563 BEAST_EXPECT(!offerExists(env, bob, aliceOfferSeq));
-
1564 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
-
1565 BEAST_EXPECT(!offerExists(env, bob, carolOfferSeq));
-
1566 }
-
1567
-
1568public:
-
1569 void
-
1570 run() override
-
1571 {
-
1572 FeatureBitset const all{jtx::supported_amendments()};
+
1497 BEAST_EXPECT(checkOffer(
+
1498 env, bob, bobOfferSeq, XRP(10), USD(10), lsfHybrid, true));
+
1499 BEAST_EXPECT(checkDirectorySize(env, domainDir, i));
+
1500 BEAST_EXPECT(checkDirectorySize(env, openDir, i));
+
1501 }
+
1502
+
1503 for (auto const offerSeq : offerSeqs)
+
1504 {
+
1505 env(offer_cancel(bob, offerSeq));
+
1506 env.close();
+
1507 dirCnt--;
+
1508 BEAST_EXPECT(!offerExists(env, bob, offerSeq));
+
1509 BEAST_EXPECT(checkDirectorySize(env, domainDir, dirCnt));
+
1510 BEAST_EXPECT(checkDirectorySize(env, openDir, dirCnt));
+
1511 }
+
1512 }
+
1513
+
1514 void
+
1515 testAutoBridge(FeatureBitset features)
+
1516 {
+
1517 testcase("Auto bridge");
+
1518
+
1519 Env env(*this, features);
+
1520 auto const& [gw, domainOwner, alice, bob, carol, USD, domainID, credType] =
+
1521 PermissionedDEX(env);
+
1522 auto const EUR = gw["EUR"];
+
1523
+
1524 for (auto const& account : {alice, bob, carol})
+
1525 {
+
1526 env(trust(account, EUR(10000)));
+
1527 env.close();
+
1528 }
+
1529
+
1530 env(pay(gw, carol, EUR(1)));
+
1531 env.close();
+
1532
+
1533 auto const aliceOfferSeq{env.seq(alice)};
+
1534 auto const bobOfferSeq{env.seq(bob)};
+
1535 env(offer(alice, XRP(100), USD(1)), domain(domainID));
+
1536 env(offer(bob, EUR(1), XRP(100)), domain(domainID));
+
1537 env.close();
+
1538
+
1539 // carol's offer should cross bob and alice's offers due to auto
+
1540 // bridging
+
1541 auto const carolOfferSeq{env.seq(carol)};
+
1542 env(offer(carol, USD(1), EUR(1)), domain(domainID));
+
1543 env.close();
+
1544
+
1545 BEAST_EXPECT(!offerExists(env, bob, aliceOfferSeq));
+
1546 BEAST_EXPECT(!offerExists(env, bob, bobOfferSeq));
+
1547 BEAST_EXPECT(!offerExists(env, bob, carolOfferSeq));
+
1548 }
+
1549
+
1550public:
+
1551 void
+
1552 run() override
+
1553 {
+
1554 FeatureBitset const all{jtx::supported_amendments()};
+
1555
+
1556 // Test domain offer (w/o hyrbid)
+
1557 testOfferCreate(all);
+
1558 testPayment(all);
+
1559 testBookStep(all);
+
1560 testRippling(all);
+
1561 testOfferTokenIssuerInDomain(all);
+
1562 testRemoveUnfundedOffer(all);
+
1563 testAmmNotUsed(all);
+
1564 testAutoBridge(all);
+
1565
+
1566 // Test hybrid offers
+
1567 testHybridOfferCreate(all);
+
1568 testHybridBookStep(all);
+
1569 testHybridInvalidOffer(all);
+
1570 testHybridOfferDirectories(all);
+
1571 }
+
1572};
1573
-
1574 // Test domain offer (w/o hyrbid)
-
1575 testOfferCreate(all);
-
1576 testPayment(all);
-
1577 testBookStep(all);
-
1578 testRippling(all);
-
1579 testOfferTokenIssuerInDomain(all);
-
1580 testRemoveUnfundedOffer(all);
-
1581 testAmmNotUsed(all);
-
1582 testAutoBridge(all);
-
1583
-
1584 // Test hybrid offers
-
1585 testHybridOfferCreate(all);
-
1586 testHybridBookStep(all);
-
1587 testHybridInvalidOffer(all);
-
1588 testHybridOfferDirectories(all);
-
1589 }
-
1590};
-
1591
-
1592BEAST_DEFINE_TESTSUITE(PermissionedDEX, app, ripple);
-
1593
-
1594} // namespace test
-
1595} // namespace ripple
+
1574BEAST_DEFINE_TESTSUITE(PermissionedDEX, app, ripple);
+
1575
+
1576} // namespace test
+
1577} // namespace ripple
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
@@ -1680,24 +1662,24 @@ $(function() {
Definition: STAmount.h:50
-
void testOfferTokenIssuerInDomain(FeatureBitset features)
-
void testPayment(FeatureBitset features)
-
void testRippling(FeatureBitset features)
+
void testOfferTokenIssuerInDomain(FeatureBitset features)
+
void testPayment(FeatureBitset features)
+
void testRippling(FeatureBitset features)
bool checkOffer(Env const &env, Account const &account, std::uint32_t offerSeq, STAmount const &takerPays, STAmount const &takerGets, uint32_t const flags=0, bool const domainOffer=false)
void testOfferCreate(FeatureBitset features)
-
void testHybridBookStep(FeatureBitset features)
+
void testHybridBookStep(FeatureBitset features)
bool checkDirectorySize(Env const &env, uint256 directory, std::uint32_t dirSize)
-
void run() override
Runs the suite.
-
void testHybridOfferCreate(FeatureBitset features)
-
void testAutoBridge(FeatureBitset features)
+
void run() override
Runs the suite.
+
void testHybridOfferCreate(FeatureBitset features)
+
void testAutoBridge(FeatureBitset features)
std::optional< uint256 > getDefaultOfferDirKey(Env const &env, Account const &account, std::uint32_t offerSeq)
-
void testBookStep(FeatureBitset features)
-
void testHybridOfferDirectories(FeatureBitset features)
+
void testBookStep(FeatureBitset features)
+
void testHybridOfferDirectories(FeatureBitset features)
bool offerExists(Env const &env, Account const &account, std::uint32_t offerSeq)
uint256 getBookDirKey(Book const &book, STAmount const &takerPays, STAmount const &takerGets)
-
void testHybridInvalidOffer(FeatureBitset features)
-
void testRemoveUnfundedOffer(FeatureBitset features)
-
void testAmmNotUsed(FeatureBitset features)
+
void testHybridInvalidOffer(FeatureBitset features)
+
void testRemoveUnfundedOffer(FeatureBitset features)
+
void testAmmNotUsed(FeatureBitset features)
Convenience class to test AMM functionality.
Definition: AMM.h:124
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:121
diff --git a/SetAuth__test_8cpp_source.html b/SetAuth__test_8cpp_source.html index 3ed6bb291e..89d7bea1e3 100644 --- a/SetAuth__test_8cpp_source.html +++ b/SetAuth__test_8cpp_source.html @@ -153,16 +153,15 @@ $(function() {
75 {
76 using namespace jtx;
77 auto const sa = supported_amendments();
-
78 testAuth(sa - featureFlowCross - featurePermissionedDEX);
-
79 testAuth(sa - featurePermissionedDEX);
-
80 testAuth(sa);
-
81 }
-
82};
-
83
-
84BEAST_DEFINE_TESTSUITE(SetAuth, test, ripple);
-
85
-
86} // namespace test
-
87} // namespace ripple
+
78 testAuth(sa - featurePermissionedDEX);
+
79 testAuth(sa);
+
80 }
+
81};
+
82
+
83BEAST_DEFINE_TESTSUITE(SetAuth, test, ripple);
+
84
+
85} // namespace test
+
86} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
A testsuite class.
Definition: suite.h:55
diff --git a/Steps_8h_source.html b/Steps_8h_source.html index 314af13878..a1fd377291 100644 --- a/Steps_8h_source.html +++ b/Steps_8h_source.html @@ -528,7 +528,7 @@ $(function() {
bool xrpEndpointStepEqual(Step const &step, AccountID const &acc)
-
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1419
+
bool bookStepEqual(Step const &step, ripple::Book const &book)
Definition: BookStep.cpp:1418
bool directStepEqual(Step const &step, AccountID const &src, AccountID const &dst, Currency const &currency)
Definition: DirectStep.cpp:967
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition: AccountID.h:48
@@ -544,14 +544,14 @@ $(function() {
QualityDirection
Definition: Steps.h:43
@ in
@ out
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1488
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepXI(StrandContext const &ctx, Issue const &out)
Definition: BookStep.cpp:1487
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition: UintTypes.h:56
DebtDirection
Definition: Steps.h:42
@ redeems
@ issues
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1482
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepIX(StrandContext const &ctx, Issue const &in)
Definition: BookStep.cpp:1481
bool checkNear(IOUAmount const &expected, IOUAmount const &actual)
Definition: PaySteps.cpp:34
-
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1476
+
std::pair< TER, std::unique_ptr< Step > > make_BookStepII(StrandContext const &ctx, Issue const &in, Issue const &out)
Definition: BookStep.cpp:1475
std::pair< TER, STPath > normalizePath(AccountID const &src, AccountID const &dst, Issue const &deliver, std::optional< Issue > const &sendMaxIssue, STPath const &path)
std::pair< TER, std::unique_ptr< Step > > make_DirectStepI(StrandContext const &ctx, AccountID const &src, AccountID const &dst, Currency const &c)
Definition: DirectStep.cpp:985
bool isDirectXrpToXrp(Strand const &strand)
Definition: PaySteps.cpp:624
diff --git a/StrandFlow_8h_source.html b/StrandFlow_8h_source.html index ab4d6f6739..ab961f7c3f 100644 --- a/StrandFlow_8h_source.html +++ b/StrandFlow_8h_source.html @@ -958,7 +958,7 @@ $(function() {
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition: Indexes.cpp:274
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:25
-
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1005
+
static auto sum(TCollection const &col)
Definition: BookStep.cpp:1004
@ telFAILED_PROCESSING
Definition: TER.h:56
boost::outcome_v2::result< T, std::error_code > Result
Definition: b58_utils.h:37
StrandResult< TInAmt, TOutAmt > flow(PaymentSandbox const &baseView, Strand const &strand, std::optional< TInAmt > const &maxIn, TOutAmt const &out, beast::Journal j)
Request out amount from a strand.
Definition: StrandFlow.h:105
diff --git a/TrustAndBalance__test_8cpp_source.html b/TrustAndBalance__test_8cpp_source.html index 457e400f4c..0aef4d241c 100644 --- a/TrustAndBalance__test_8cpp_source.html +++ b/TrustAndBalance__test_8cpp_source.html @@ -559,15 +559,14 @@ $(function() {
481
482 using namespace test::jtx;
483 auto const sa = supported_amendments();
-
484 testWithFeatures(sa - featureFlowCross - featurePermissionedDEX);
-
485 testWithFeatures(sa - featurePermissionedDEX);
-
486 testWithFeatures(sa);
-
487 }
-
488};
-
489
-
490BEAST_DEFINE_TESTSUITE(TrustAndBalance, app, ripple);
-
491
-
492} // namespace ripple
+
484 testWithFeatures(sa - featurePermissionedDEX);
+
485 testWithFeatures(sa);
+
486 }
+
487};
+
488
+
489BEAST_DEFINE_TESTSUITE(TrustAndBalance, app, ripple);
+
490
+
491} // namespace ripple
Represents a JSON value.
Definition: json_value.h:149
Value & append(Value const &value)
Append value to array at the end.
Definition: json_value.cpp:910
diff --git a/annotated.html b/annotated.html index cd2acb49a1..9e0658106c 100644 --- a/annotated.html +++ b/annotated.html @@ -889,107 +889,106 @@ $(function() {  COfferAllFeatures_test  COfferBaseUtil_test  COfferWOFillOrKill_test - COfferWOFlowCross_test - COfferWOPermDEX_test - COfferWOSmallQOffers_test - COfferWTakerDryOffer_test - COverlaySimulate server's OverlayImpl - COverlaySim - COversizeMeta_test - CPath - CPath_test - Cgate - CPathSet - CPayChan_test - CPaymentSandbox_test - CPayStrand_test - CPeerPartialSimulate two entities - peer directly connected to the server (via squelch in PeerSim) and PeerImp (via Overlay) - CPeerSim - CPendingSaves_test - CPermissionedDEX_test - CPermissionedDomains_test - CPlumpBook_test - CPseudoTx_test - CRandomAccountParams - CRCLCensorshipDetector_test - CRCLValidations_test - Creduce_relay_simulate_test - Creduce_relay_test - CEvent - CHandler - CReducedOffer_test - CRegression_test - CRippleCalcTestParams - CRobustTransaction_test - CRoles_test - CRPCCall_test - CRPCCallTestData - CRPCHelpers_test - CRPCOverload_test - CScaleFreeSim_test - CScheduler_test - CSEnv - CServer_test - CTestHandler - CTestSink - CTestThread - CServerInfo_test - CServerStatus_test - CmyFields - CSetAuth_test - CSetTrust_test - CSHAMapStore_test - CSimulate_test - CSkipList_test - CSTIssue_test - CStreamSink - CSubscribe_test - CSuiteJournal - CSuiteJournalSink - CTestOutputSuite - CTestPeerSimulate a network peer - CTestPeerSetSimulate a peerSet that supplies peers to ledger replay subtasks - CTestPeerSetBuilderBuild the TestPeerSet - CTheoreticalQuality_test - CThinBook_test - Ctraffic_count_test - CTestCase - CTransaction_ordering_test - CTrustedPublisherServer - CBlobInfo - Clambda - CValidator - Ctx_reduce_relay_test - CPeerTest - CTxQMetaInfo_test - CTxQPosNegFlows_test - CValidatorSimulate Validator - CValidatorInfo_test - CValidatorKeys_test - CValidatorList_test - CValidator - CValidatorRPC_test - CValidatorSite_test - CFetchListConfig - CView_test - CWSClient - CWSClient_test - CWSClientImpl - Cmsg - CXChain_test - CXChainSim_test - CAccountCreate - CAccountStateTrack - CChainStateTrack - CBridgeCounters - CClaims - CChainStateTracker - CSmBase - CSmCreateAccount - CSmTransfer - CTransfer - CXEnv - CXRPEndpointStepInfo + COfferWOPermDEX_test + COfferWOSmallQOffers_test + COfferWTakerDryOffer_test + COverlaySimulate server's OverlayImpl + COverlaySim + COversizeMeta_test + CPath + CPath_test + Cgate + CPathSet + CPayChan_test + CPaymentSandbox_test + CPayStrand_test + CPeerPartialSimulate two entities - peer directly connected to the server (via squelch in PeerSim) and PeerImp (via Overlay) + CPeerSim + CPendingSaves_test + CPermissionedDEX_test + CPermissionedDomains_test + CPlumpBook_test + CPseudoTx_test + CRandomAccountParams + CRCLCensorshipDetector_test + CRCLValidations_test + Creduce_relay_simulate_test + Creduce_relay_test + CEvent + CHandler + CReducedOffer_test + CRegression_test + CRippleCalcTestParams + CRobustTransaction_test + CRoles_test + CRPCCall_test + CRPCCallTestData + CRPCHelpers_test + CRPCOverload_test + CScaleFreeSim_test + CScheduler_test + CSEnv + CServer_test + CTestHandler + CTestSink + CTestThread + CServerInfo_test + CServerStatus_test + CmyFields + CSetAuth_test + CSetTrust_test + CSHAMapStore_test + CSimulate_test + CSkipList_test + CSTIssue_test + CStreamSink + CSubscribe_test + CSuiteJournal + CSuiteJournalSink + CTestOutputSuite + CTestPeerSimulate a network peer + CTestPeerSetSimulate a peerSet that supplies peers to ledger replay subtasks + CTestPeerSetBuilderBuild the TestPeerSet + CTheoreticalQuality_test + CThinBook_test + Ctraffic_count_test + CTestCase + CTransaction_ordering_test + CTrustedPublisherServer + CBlobInfo + Clambda + CValidator + Ctx_reduce_relay_test + CPeerTest + CTxQMetaInfo_test + CTxQPosNegFlows_test + CValidatorSimulate Validator + CValidatorInfo_test + CValidatorKeys_test + CValidatorList_test + CValidator + CValidatorRPC_test + CValidatorSite_test + CFetchListConfig + CView_test + CWSClient + CWSClient_test + CWSClientImpl + Cmsg + CXChain_test + CXChainSim_test + CAccountCreate + CAccountStateTrack + CChainStateTrack + CBridgeCounters + CClaims + CChainStateTracker + CSmBase + CSmCreateAccount + CSmTransfer + CTransfer + CXEnv + CXRPEndpointStepInfo  Ntests  CBarrierExperimentally, we discovered that using std::barrier performs extremely poorly (~1 hour vs ~1 minute to run the test suite) in certain macOS environments  Ccluster_test diff --git a/classbeast_1_1aged__associative__container__test__base.html b/classbeast_1_1aged__associative__container__test__base.html index 0a416ddebd..cf0733743e 100644 --- a/classbeast_1_1aged__associative__container__test__base.html +++ b/classbeast_1_1aged__associative__container__test__base.html @@ -2141,7 +2141,7 @@ template<class Condition >

Runs the suite.

-

Implemented in ripple::test::AccountDelete_test, ripple::AccountTxPaging_test, ripple::AmendmentTable_test, ripple::test::AMM_test, ripple::test::AMMCalc_test, ripple::test::AMMClawback_test, ripple::test::AMMExtended_test, ripple::test::Batch_test, ripple::Check_test, ripple::Clawback_test, ripple::test::Credentials_test, ripple::test::CrossingLimits_test, ripple::test::Delegate_test, ripple::test::DeliverMin_test, ripple::test::DepositAuth_test, ripple::test::DepositPreauth_test, ripple::test::DID_test, ripple::Discrepancy_test, ripple::test::DNS_test, ripple::test::Escrow_test, ripple::test::EscrowToken_test, ripple::test::FeeVote_test, ripple::FixNFTokenPageLinks_test, ripple::test::Flow_test, ripple::test::Flow_manual_test, ripple::Freeze_test, ripple::test::HashRouter_test, ripple::test::LedgerHistory_test, ripple::LedgerLoad_test, ripple::test::LedgerMaster_test, ripple::test::LedgerReplay_test, ripple::test::LedgerReplayer_test, ripple::test::LedgerReplayerTimeout_test, ripple::test::LedgerReplayerLong_test, ripple::LoadFeeTrack_test, ripple::test::LPTokenTransfer_test, ripple::test::Manifest_test, ripple::test::MPToken_test, ripple::test::MultiSign_test, ripple::test::NetworkID_test, ripple::NFTokenBaseUtil_test, ripple::NFTokenDisallowIncoming_test, ripple::NFTokenWOfixV1_test, ripple::NFTokenWOTokenRemint_test, ripple::NFTokenWOTokenReserve_test, ripple::NFTokenWOMintOffer_test, ripple::NFTokenWOModify_test, ripple::NFTokenAllFeatures_test, ripple::NFTokenAuth_test, ripple::NFTokenBurnBaseUtil_test, ripple::NFTokenBurnWOfixFungTokens_test, ripple::NFTokenBurnWOFixTokenRemint_test, ripple::NFTokenBurnWOFixNFTPageLinks_test, ripple::NFTokenBurnAllFeatures_test, ripple::NFTokenDir_test, ripple::test::OfferBaseUtil_test, ripple::test::OfferWOFlowCross_test, ripple::test::OfferWTakerDryOffer_test, ripple::test::OfferWOSmallQOffers_test, ripple::test::OfferWOFillOrKill_test, ripple::test::OfferWOPermDEX_test, ripple::test::OfferAllFeatures_test, ripple::test::Offer_manual_test, ripple::OfferStream_test, ripple::test::jtx::oracle::Oracle_test, ripple::test::PlumpBook_test, ripple::test::ThinBook_test, ripple::test::OversizeMeta_test, ripple::test::FindOversizeCross_test, ripple::test::Path_test, ripple::test::PayChan_test, ripple::test::PayStrand_test, ripple::test::PermissionedDEX_test, ripple::test::PermissionedDomains_test, ripple::test::PseudoTx_test, ripple::test::RCLCensorshipDetector_test, ripple::test::RCLValidations_test, ripple::test::ReducedOffer_test, ripple::test::Regression_test, ripple::test::SetAuth_test, ripple::SetRegularKey_test, ripple::test::SetTrust_test, ripple::test::SHAMapStore_test, ripple::Taker_test, ripple::test::TheoreticalQuality_test, ripple::Ticket_test, ripple::test::Transaction_ordering_test, ripple::TrustAndBalance_test, ripple::Apply_test, ripple::test::TxQPosNegFlows_test, ripple::test::TxQMetaInfo_test, ripple::test::ValidatorKeys_test, ripple::test::ValidatorList_test, ripple::test::ValidatorSite_test, ripple::Vault_test, ripple::test::XChain_test, ripple::test::XChainSim_test, ripple::test::base_uint_test, ripple::test::Buffer_test, ripple::test::DetectCrash_test, ripple::test::Expected_test, ripple::test::feeunits_test, ripple::FileUtilities_test, ripple::hardened_hash_test, ripple::tests::IntrusiveShared_test, ripple::IOUAmount_test, ripple::test::join_test, ripple::KeyCache_test, ripple::Number_test, ripple::PerfLog_test, ripple::StringUtilities_test, ripple::TaggedCache_test, ripple::XRPAmount_test, beast::aged_set_test, beast::aged_map_test, beast::aged_multiset_test, beast::aged_multimap_test, beast::aged_unordered_set_test, beast::aged_unordered_map_test, beast::aged_unordered_multiset_test, beast::aged_unordered_multimap_test, beast::abstract_clock_test, beast::basic_seconds_clock_test, ripple::test::CurrentThreadName_test, io_latency_probe_test, beast::Journal_test, beast::PropertyStream_test, beast::Zero_test, beast::unit_test::print_test, beast::IP::IPEndpoint_test, beast::LexicalCast_test, beast::SemanticVersion_test, ripple::cryptoconditions::PreimageSha256_test, ripple::test::ByzantineFailureSim_test, ripple::test::Consensus_test, ripple::test::DistributedValidators_test, ripple::test::LedgerTiming_test, ripple::test::LedgerTrie_test, ripple::test::NegativeUNL_test, ripple::test::NegativeUNLNoAmendment_test, ripple::test::NegativeUNLVoteInternal_test, ripple::test::NegativeUNLVoteScoreTable_test, ripple::test::NegativeUNLVoteGoodScore_test, ripple::test::NegativeUNLVoteOffline_test, ripple::test::NegativeUNLVoteMaxListed_test, ripple::test::NegativeUNLVoteRetiredValidator_test, ripple::test::NegativeUNLVoteNewValidator_test, ripple::test::NegativeUNLVoteFilterValidations_test, ripple::test::ScaleFreeSim_test, ripple::test::csf::Validations_test, ripple::test::ClosureCounter_test, ripple::Config_test, ripple::test::Coroutine_test, ripple::test::JobQueue_test, ripple::SociDB_test, ripple::Workers_test, ripple::test::BasicNetwork_test, ripple::test::Digraph_test, ripple::test::Histogram_test, ripple::test::Scheduler_test, ripple::json_value_test, Json::JsonObject_test, Json::Output_test, Json::JsonWriter_test, ripple::test::Env_test, ripple::test::WSClient_test, ripple::test::BookDirs_test, ripple::test::Directory_test, ripple::Invariants_test, ripple::test::PaymentSandbox_test, ripple::test::PendingSaves_test, ripple::test::SkipList_test, ripple::test::View_test, ripple::test::GetAmendments_test, ripple::NodeStore::Backend_test, ripple::NodeStore::NodeStoreBasic_test, ripple::NodeStore::Database_test, ripple::NodeStore::Timing_test, ripple::NodeStore::tests::varint_test, ripple::tests::cluster_test, ripple::test::compression_test, ripple::test::handshake_test, ripple::ProtocolVersion_test, ripple::test::reduce_relay_test, ripple::test::reduce_relay_simulate_test, ripple::short_read_test, ripple::test::traffic_count_test, ripple::test::tx_reduce_relay_test, ripple::PeerFinder::Livecache_test, ripple::PeerFinder::PeerFinder_test, ripple::test::ApiVersion_test, ripple::BuildInfo_test, ripple::Hooks_test, ripple::InnerObjectFormatsParsedJSON_test, ripple::Issue_test, ripple::Memo_test, ripple::test::MultiApiJson_test, ripple::PublicKey_test, ripple::Quality_test, ripple::SecretKey_test, ripple::Seed_test, ripple::SeqProxy_test, ripple::Serializer_test, ripple::STAccount_test, ripple::STAmount_test, ripple::test::STIssue_test, ripple::STNumber_test, ripple::STObject_test, ripple::STTx_test, ripple::InnerObjectFormatsSerializer_test, ripple::STValidation_test, ripple::TER_test, ripple::types_test, ripple::Resource::ResourceManager_test, ripple::AccountCurrencies_test, ripple::RPC::AccountLines_test, ripple::test::AccountObjects_test, ripple::test::AccountOffers_test, ripple::AccountSet_test, ripple::test::AccountTx_test, ripple::AmendmentBlocked_test, ripple::test::AMMInfo_test, ripple::test::Book_test, ripple::test::BookChanges_test, ripple::Connect_test, ripple::test::DeliveredAmount_test, ripple::test::DepositAuthorized_test, ripple::Feature_test, ripple::test::GatewayBalances_test, ripple::test::jtx::oracle::GetAggregatePrice_test, ripple::GetCounts_test, ripple::test::Handler_test, ripple::RPC::JSONRPC_test, ripple::RPC::WalletPropose_test, ripple::LedgerClosed_test, ripple::LedgerData_test, ripple::test::LedgerEntry_test, ripple::test::LedgerEntry_XChain_test, ripple::LedgerHeader_test, ripple::RPC::LedgerRequestRPC_test, ripple::test::LedgerRPC_test, ripple::test::ManifestRPC_test, ripple::test::NoRipple_test, ripple::NoRippleCheck_test, ripple::NoRippleCheckLimits_test, ripple::OwnerInfo_test, ripple::Peers_test, ripple::test::RobustTransaction_test, ripple::test::Roles_test, ripple::test::RPCCall_test, ripple::test::RPCHelpers_test, ripple::test::RPCOverload_test, ripple::test::ServerInfo_test, ripple::test::Simulate_test, ripple::RPC::codeString_test, ripple::RPC::fillJson_test, ripple::test::Subscribe_test, ripple::Transaction_test, ripple::TransactionEntry_test, ripple::TransactionHistory_test, ripple::test::ValidatorInfo_test, ripple::test::ValidatorRPC_test, ripple::Version_test, ripple::test::Server_test, ripple::test::ServerStatus_test, ripple::tests::FetchPack_test, ripple::tests::SHAMap_test, ripple::tests::SHAMapPathProof_test, and ripple::tests::SHAMapSync_test.

+

Implemented in ripple::test::AccountDelete_test, ripple::AccountTxPaging_test, ripple::AmendmentTable_test, ripple::test::AMM_test, ripple::test::AMMCalc_test, ripple::test::AMMClawback_test, ripple::test::AMMExtended_test, ripple::test::Batch_test, ripple::Check_test, ripple::Clawback_test, ripple::test::Credentials_test, ripple::test::CrossingLimits_test, ripple::test::Delegate_test, ripple::test::DeliverMin_test, ripple::test::DepositAuth_test, ripple::test::DepositPreauth_test, ripple::test::DID_test, ripple::Discrepancy_test, ripple::test::DNS_test, ripple::test::Escrow_test, ripple::test::EscrowToken_test, ripple::test::FeeVote_test, ripple::FixNFTokenPageLinks_test, ripple::test::Flow_test, ripple::test::Flow_manual_test, ripple::Freeze_test, ripple::test::HashRouter_test, ripple::test::LedgerHistory_test, ripple::LedgerLoad_test, ripple::test::LedgerMaster_test, ripple::test::LedgerReplay_test, ripple::test::LedgerReplayer_test, ripple::test::LedgerReplayerTimeout_test, ripple::test::LedgerReplayerLong_test, ripple::LoadFeeTrack_test, ripple::test::LPTokenTransfer_test, ripple::test::Manifest_test, ripple::test::MPToken_test, ripple::test::MultiSign_test, ripple::test::NetworkID_test, ripple::NFTokenBaseUtil_test, ripple::NFTokenDisallowIncoming_test, ripple::NFTokenWOfixV1_test, ripple::NFTokenWOTokenRemint_test, ripple::NFTokenWOTokenReserve_test, ripple::NFTokenWOMintOffer_test, ripple::NFTokenWOModify_test, ripple::NFTokenAllFeatures_test, ripple::NFTokenAuth_test, ripple::NFTokenBurnBaseUtil_test, ripple::NFTokenBurnWOfixFungTokens_test, ripple::NFTokenBurnWOFixTokenRemint_test, ripple::NFTokenBurnWOFixNFTPageLinks_test, ripple::NFTokenBurnAllFeatures_test, ripple::NFTokenDir_test, ripple::test::OfferBaseUtil_test, ripple::test::OfferWTakerDryOffer_test, ripple::test::OfferWOSmallQOffers_test, ripple::test::OfferWOFillOrKill_test, ripple::test::OfferWOPermDEX_test, ripple::test::OfferAllFeatures_test, ripple::test::Offer_manual_test, ripple::OfferStream_test, ripple::test::jtx::oracle::Oracle_test, ripple::test::PlumpBook_test, ripple::test::ThinBook_test, ripple::test::OversizeMeta_test, ripple::test::FindOversizeCross_test, ripple::test::Path_test, ripple::test::PayChan_test, ripple::test::PayStrand_test, ripple::test::PermissionedDEX_test, ripple::test::PermissionedDomains_test, ripple::test::PseudoTx_test, ripple::test::RCLCensorshipDetector_test, ripple::test::RCLValidations_test, ripple::test::ReducedOffer_test, ripple::test::Regression_test, ripple::test::SetAuth_test, ripple::SetRegularKey_test, ripple::test::SetTrust_test, ripple::test::SHAMapStore_test, ripple::Taker_test, ripple::test::TheoreticalQuality_test, ripple::Ticket_test, ripple::test::Transaction_ordering_test, ripple::TrustAndBalance_test, ripple::Apply_test, ripple::test::TxQPosNegFlows_test, ripple::test::TxQMetaInfo_test, ripple::test::ValidatorKeys_test, ripple::test::ValidatorList_test, ripple::test::ValidatorSite_test, ripple::Vault_test, ripple::test::XChain_test, ripple::test::XChainSim_test, ripple::test::base_uint_test, ripple::test::Buffer_test, ripple::test::DetectCrash_test, ripple::test::Expected_test, ripple::test::feeunits_test, ripple::FileUtilities_test, ripple::hardened_hash_test, ripple::tests::IntrusiveShared_test, ripple::IOUAmount_test, ripple::test::join_test, ripple::KeyCache_test, ripple::Number_test, ripple::PerfLog_test, ripple::StringUtilities_test, ripple::TaggedCache_test, ripple::XRPAmount_test, beast::aged_set_test, beast::aged_map_test, beast::aged_multiset_test, beast::aged_multimap_test, beast::aged_unordered_set_test, beast::aged_unordered_map_test, beast::aged_unordered_multiset_test, beast::aged_unordered_multimap_test, beast::abstract_clock_test, beast::basic_seconds_clock_test, ripple::test::CurrentThreadName_test, io_latency_probe_test, beast::Journal_test, beast::PropertyStream_test, beast::Zero_test, beast::unit_test::print_test, beast::IP::IPEndpoint_test, beast::LexicalCast_test, beast::SemanticVersion_test, ripple::cryptoconditions::PreimageSha256_test, ripple::test::ByzantineFailureSim_test, ripple::test::Consensus_test, ripple::test::DistributedValidators_test, ripple::test::LedgerTiming_test, ripple::test::LedgerTrie_test, ripple::test::NegativeUNL_test, ripple::test::NegativeUNLNoAmendment_test, ripple::test::NegativeUNLVoteInternal_test, ripple::test::NegativeUNLVoteScoreTable_test, ripple::test::NegativeUNLVoteGoodScore_test, ripple::test::NegativeUNLVoteOffline_test, ripple::test::NegativeUNLVoteMaxListed_test, ripple::test::NegativeUNLVoteRetiredValidator_test, ripple::test::NegativeUNLVoteNewValidator_test, ripple::test::NegativeUNLVoteFilterValidations_test, ripple::test::ScaleFreeSim_test, ripple::test::csf::Validations_test, ripple::test::ClosureCounter_test, ripple::Config_test, ripple::test::Coroutine_test, ripple::test::JobQueue_test, ripple::SociDB_test, ripple::Workers_test, ripple::test::BasicNetwork_test, ripple::test::Digraph_test, ripple::test::Histogram_test, ripple::test::Scheduler_test, ripple::json_value_test, Json::JsonObject_test, Json::Output_test, Json::JsonWriter_test, ripple::test::Env_test, ripple::test::WSClient_test, ripple::test::BookDirs_test, ripple::test::Directory_test, ripple::Invariants_test, ripple::test::PaymentSandbox_test, ripple::test::PendingSaves_test, ripple::test::SkipList_test, ripple::test::View_test, ripple::test::GetAmendments_test, ripple::NodeStore::Backend_test, ripple::NodeStore::NodeStoreBasic_test, ripple::NodeStore::Database_test, ripple::NodeStore::Timing_test, ripple::NodeStore::tests::varint_test, ripple::tests::cluster_test, ripple::test::compression_test, ripple::test::handshake_test, ripple::ProtocolVersion_test, ripple::test::reduce_relay_test, ripple::test::reduce_relay_simulate_test, ripple::short_read_test, ripple::test::traffic_count_test, ripple::test::tx_reduce_relay_test, ripple::PeerFinder::Livecache_test, ripple::PeerFinder::PeerFinder_test, ripple::test::ApiVersion_test, ripple::BuildInfo_test, ripple::Hooks_test, ripple::InnerObjectFormatsParsedJSON_test, ripple::Issue_test, ripple::Memo_test, ripple::test::MultiApiJson_test, ripple::PublicKey_test, ripple::Quality_test, ripple::SecretKey_test, ripple::Seed_test, ripple::SeqProxy_test, ripple::Serializer_test, ripple::STAccount_test, ripple::STAmount_test, ripple::test::STIssue_test, ripple::STNumber_test, ripple::STObject_test, ripple::STTx_test, ripple::InnerObjectFormatsSerializer_test, ripple::STValidation_test, ripple::TER_test, ripple::types_test, ripple::Resource::ResourceManager_test, ripple::AccountCurrencies_test, ripple::RPC::AccountLines_test, ripple::test::AccountObjects_test, ripple::test::AccountOffers_test, ripple::AccountSet_test, ripple::test::AccountTx_test, ripple::AmendmentBlocked_test, ripple::test::AMMInfo_test, ripple::test::Book_test, ripple::test::BookChanges_test, ripple::Connect_test, ripple::test::DeliveredAmount_test, ripple::test::DepositAuthorized_test, ripple::Feature_test, ripple::test::GatewayBalances_test, ripple::test::jtx::oracle::GetAggregatePrice_test, ripple::GetCounts_test, ripple::test::Handler_test, ripple::RPC::JSONRPC_test, ripple::RPC::WalletPropose_test, ripple::LedgerClosed_test, ripple::LedgerData_test, ripple::test::LedgerEntry_test, ripple::test::LedgerEntry_XChain_test, ripple::LedgerHeader_test, ripple::RPC::LedgerRequestRPC_test, ripple::test::LedgerRPC_test, ripple::test::ManifestRPC_test, ripple::test::NoRipple_test, ripple::NoRippleCheck_test, ripple::NoRippleCheckLimits_test, ripple::OwnerInfo_test, ripple::Peers_test, ripple::test::RobustTransaction_test, ripple::test::Roles_test, ripple::test::RPCCall_test, ripple::test::RPCHelpers_test, ripple::test::RPCOverload_test, ripple::test::ServerInfo_test, ripple::test::Simulate_test, ripple::RPC::codeString_test, ripple::RPC::fillJson_test, ripple::test::Subscribe_test, ripple::Transaction_test, ripple::TransactionEntry_test, ripple::TransactionHistory_test, ripple::test::ValidatorInfo_test, ripple::test::ValidatorRPC_test, ripple::Version_test, ripple::test::Server_test, ripple::test::ServerStatus_test, ripple::tests::FetchPack_test, ripple::tests::SHAMap_test, ripple::tests::SHAMapPathProof_test, and ripple::tests::SHAMapSync_test.

diff --git a/classbeast_1_1unit__test_1_1suite.html b/classbeast_1_1unit__test_1_1suite.html index 3bda223cf5..d64fe9b9ae 100644 --- a/classbeast_1_1unit__test_1_1suite.html +++ b/classbeast_1_1unit__test_1_1suite.html @@ -930,7 +930,7 @@ template<class Condition >

Runs the suite.

-

Implemented in ripple::test::AccountDelete_test, ripple::AccountTxPaging_test, ripple::AmendmentTable_test, ripple::test::AMM_test, ripple::test::AMMCalc_test, ripple::test::AMMClawback_test, ripple::test::AMMExtended_test, ripple::test::Batch_test, ripple::Check_test, ripple::Clawback_test, ripple::test::Credentials_test, ripple::test::CrossingLimits_test, ripple::test::Delegate_test, ripple::test::DeliverMin_test, ripple::test::DepositAuth_test, ripple::test::DepositPreauth_test, ripple::test::DID_test, ripple::Discrepancy_test, ripple::test::DNS_test, ripple::test::Escrow_test, ripple::test::EscrowToken_test, ripple::test::FeeVote_test, ripple::FixNFTokenPageLinks_test, ripple::test::Flow_test, ripple::test::Flow_manual_test, ripple::Freeze_test, ripple::test::HashRouter_test, ripple::test::LedgerHistory_test, ripple::LedgerLoad_test, ripple::test::LedgerMaster_test, ripple::test::LedgerReplay_test, ripple::test::LedgerReplayer_test, ripple::test::LedgerReplayerTimeout_test, ripple::test::LedgerReplayerLong_test, ripple::LoadFeeTrack_test, ripple::test::LPTokenTransfer_test, ripple::test::Manifest_test, ripple::test::MPToken_test, ripple::test::MultiSign_test, ripple::test::NetworkID_test, ripple::NFTokenBaseUtil_test, ripple::NFTokenDisallowIncoming_test, ripple::NFTokenWOfixV1_test, ripple::NFTokenWOTokenRemint_test, ripple::NFTokenWOTokenReserve_test, ripple::NFTokenWOMintOffer_test, ripple::NFTokenWOModify_test, ripple::NFTokenAllFeatures_test, ripple::NFTokenAuth_test, ripple::NFTokenBurnBaseUtil_test, ripple::NFTokenBurnWOfixFungTokens_test, ripple::NFTokenBurnWOFixTokenRemint_test, ripple::NFTokenBurnWOFixNFTPageLinks_test, ripple::NFTokenBurnAllFeatures_test, ripple::NFTokenDir_test, ripple::test::OfferBaseUtil_test, ripple::test::OfferWOFlowCross_test, ripple::test::OfferWTakerDryOffer_test, ripple::test::OfferWOSmallQOffers_test, ripple::test::OfferWOFillOrKill_test, ripple::test::OfferWOPermDEX_test, ripple::test::OfferAllFeatures_test, ripple::test::Offer_manual_test, ripple::OfferStream_test, ripple::test::jtx::oracle::Oracle_test, ripple::test::PlumpBook_test, ripple::test::ThinBook_test, ripple::test::OversizeMeta_test, ripple::test::FindOversizeCross_test, ripple::test::Path_test, ripple::test::PayChan_test, ripple::test::PayStrand_test, ripple::test::PermissionedDEX_test, ripple::test::PermissionedDomains_test, ripple::test::PseudoTx_test, ripple::test::RCLCensorshipDetector_test, ripple::test::RCLValidations_test, ripple::test::ReducedOffer_test, ripple::test::Regression_test, ripple::test::SetAuth_test, ripple::SetRegularKey_test, ripple::test::SetTrust_test, ripple::test::SHAMapStore_test, ripple::Taker_test, ripple::test::TheoreticalQuality_test, ripple::Ticket_test, ripple::test::Transaction_ordering_test, ripple::TrustAndBalance_test, ripple::Apply_test, ripple::test::TxQPosNegFlows_test, ripple::test::TxQMetaInfo_test, ripple::test::ValidatorKeys_test, ripple::test::ValidatorList_test, ripple::test::ValidatorSite_test, ripple::Vault_test, ripple::test::XChain_test, ripple::test::XChainSim_test, ripple::test::base_uint_test, ripple::test::Buffer_test, ripple::test::DetectCrash_test, ripple::test::Expected_test, ripple::test::feeunits_test, ripple::FileUtilities_test, ripple::hardened_hash_test, ripple::tests::IntrusiveShared_test, ripple::IOUAmount_test, ripple::test::join_test, ripple::KeyCache_test, ripple::Number_test, ripple::PerfLog_test, ripple::StringUtilities_test, ripple::TaggedCache_test, ripple::XRPAmount_test, beast::aged_set_test, beast::aged_map_test, beast::aged_multiset_test, beast::aged_multimap_test, beast::aged_unordered_set_test, beast::aged_unordered_map_test, beast::aged_unordered_multiset_test, beast::aged_unordered_multimap_test, beast::abstract_clock_test, beast::basic_seconds_clock_test, ripple::test::CurrentThreadName_test, io_latency_probe_test, beast::Journal_test, beast::PropertyStream_test, beast::Zero_test, beast::unit_test::print_test, beast::IP::IPEndpoint_test, beast::LexicalCast_test, beast::SemanticVersion_test, ripple::cryptoconditions::PreimageSha256_test, ripple::test::ByzantineFailureSim_test, ripple::test::Consensus_test, ripple::test::DistributedValidators_test, ripple::test::LedgerTiming_test, ripple::test::LedgerTrie_test, ripple::test::NegativeUNL_test, ripple::test::NegativeUNLNoAmendment_test, ripple::test::NegativeUNLVoteInternal_test, ripple::test::NegativeUNLVoteScoreTable_test, ripple::test::NegativeUNLVoteGoodScore_test, ripple::test::NegativeUNLVoteOffline_test, ripple::test::NegativeUNLVoteMaxListed_test, ripple::test::NegativeUNLVoteRetiredValidator_test, ripple::test::NegativeUNLVoteNewValidator_test, ripple::test::NegativeUNLVoteFilterValidations_test, ripple::test::ScaleFreeSim_test, ripple::test::csf::Validations_test, ripple::test::ClosureCounter_test, ripple::Config_test, ripple::test::Coroutine_test, ripple::test::JobQueue_test, ripple::SociDB_test, ripple::Workers_test, ripple::test::BasicNetwork_test, ripple::test::Digraph_test, ripple::test::Histogram_test, ripple::test::Scheduler_test, ripple::json_value_test, Json::JsonObject_test, Json::Output_test, Json::JsonWriter_test, ripple::test::Env_test, ripple::test::WSClient_test, ripple::test::BookDirs_test, ripple::test::Directory_test, ripple::Invariants_test, ripple::test::PaymentSandbox_test, ripple::test::PendingSaves_test, ripple::test::SkipList_test, ripple::test::View_test, ripple::test::GetAmendments_test, ripple::NodeStore::Backend_test, ripple::NodeStore::NodeStoreBasic_test, ripple::NodeStore::Database_test, ripple::NodeStore::Timing_test, ripple::NodeStore::tests::varint_test, ripple::tests::cluster_test, ripple::test::compression_test, ripple::test::handshake_test, ripple::ProtocolVersion_test, ripple::test::reduce_relay_test, ripple::test::reduce_relay_simulate_test, ripple::short_read_test, ripple::test::traffic_count_test, ripple::test::tx_reduce_relay_test, ripple::PeerFinder::Livecache_test, ripple::PeerFinder::PeerFinder_test, ripple::test::ApiVersion_test, ripple::BuildInfo_test, ripple::Hooks_test, ripple::InnerObjectFormatsParsedJSON_test, ripple::Issue_test, ripple::Memo_test, ripple::test::MultiApiJson_test, ripple::PublicKey_test, ripple::Quality_test, ripple::SecretKey_test, ripple::Seed_test, ripple::SeqProxy_test, ripple::Serializer_test, ripple::STAccount_test, ripple::STAmount_test, ripple::test::STIssue_test, ripple::STNumber_test, ripple::STObject_test, ripple::STTx_test, ripple::InnerObjectFormatsSerializer_test, ripple::STValidation_test, ripple::TER_test, ripple::types_test, ripple::Resource::ResourceManager_test, ripple::AccountCurrencies_test, ripple::RPC::AccountLines_test, ripple::test::AccountObjects_test, ripple::test::AccountOffers_test, ripple::AccountSet_test, ripple::test::AccountTx_test, ripple::AmendmentBlocked_test, ripple::test::AMMInfo_test, ripple::test::Book_test, ripple::test::BookChanges_test, ripple::Connect_test, ripple::test::DeliveredAmount_test, ripple::test::DepositAuthorized_test, ripple::Feature_test, ripple::test::GatewayBalances_test, ripple::test::jtx::oracle::GetAggregatePrice_test, ripple::GetCounts_test, ripple::test::Handler_test, ripple::RPC::JSONRPC_test, ripple::RPC::WalletPropose_test, ripple::LedgerClosed_test, ripple::LedgerData_test, ripple::test::LedgerEntry_test, ripple::test::LedgerEntry_XChain_test, ripple::LedgerHeader_test, ripple::RPC::LedgerRequestRPC_test, ripple::test::LedgerRPC_test, ripple::test::ManifestRPC_test, ripple::test::NoRipple_test, ripple::NoRippleCheck_test, ripple::NoRippleCheckLimits_test, ripple::OwnerInfo_test, ripple::Peers_test, ripple::test::RobustTransaction_test, ripple::test::Roles_test, ripple::test::RPCCall_test, ripple::test::RPCHelpers_test, ripple::test::RPCOverload_test, ripple::test::ServerInfo_test, ripple::test::Simulate_test, ripple::RPC::codeString_test, ripple::RPC::fillJson_test, ripple::test::Subscribe_test, ripple::Transaction_test, ripple::TransactionEntry_test, ripple::TransactionHistory_test, ripple::test::ValidatorInfo_test, ripple::test::ValidatorRPC_test, ripple::Version_test, ripple::test::Server_test, ripple::test::ServerStatus_test, ripple::tests::FetchPack_test, ripple::tests::SHAMap_test, ripple::tests::SHAMapPathProof_test, and ripple::tests::SHAMapSync_test.

+

Implemented in ripple::test::AccountDelete_test, ripple::AccountTxPaging_test, ripple::AmendmentTable_test, ripple::test::AMM_test, ripple::test::AMMCalc_test, ripple::test::AMMClawback_test, ripple::test::AMMExtended_test, ripple::test::Batch_test, ripple::Check_test, ripple::Clawback_test, ripple::test::Credentials_test, ripple::test::CrossingLimits_test, ripple::test::Delegate_test, ripple::test::DeliverMin_test, ripple::test::DepositAuth_test, ripple::test::DepositPreauth_test, ripple::test::DID_test, ripple::Discrepancy_test, ripple::test::DNS_test, ripple::test::Escrow_test, ripple::test::EscrowToken_test, ripple::test::FeeVote_test, ripple::FixNFTokenPageLinks_test, ripple::test::Flow_test, ripple::test::Flow_manual_test, ripple::Freeze_test, ripple::test::HashRouter_test, ripple::test::LedgerHistory_test, ripple::LedgerLoad_test, ripple::test::LedgerMaster_test, ripple::test::LedgerReplay_test, ripple::test::LedgerReplayer_test, ripple::test::LedgerReplayerTimeout_test, ripple::test::LedgerReplayerLong_test, ripple::LoadFeeTrack_test, ripple::test::LPTokenTransfer_test, ripple::test::Manifest_test, ripple::test::MPToken_test, ripple::test::MultiSign_test, ripple::test::NetworkID_test, ripple::NFTokenBaseUtil_test, ripple::NFTokenDisallowIncoming_test, ripple::NFTokenWOfixV1_test, ripple::NFTokenWOTokenRemint_test, ripple::NFTokenWOTokenReserve_test, ripple::NFTokenWOMintOffer_test, ripple::NFTokenWOModify_test, ripple::NFTokenAllFeatures_test, ripple::NFTokenAuth_test, ripple::NFTokenBurnBaseUtil_test, ripple::NFTokenBurnWOfixFungTokens_test, ripple::NFTokenBurnWOFixTokenRemint_test, ripple::NFTokenBurnWOFixNFTPageLinks_test, ripple::NFTokenBurnAllFeatures_test, ripple::NFTokenDir_test, ripple::test::OfferBaseUtil_test, ripple::test::OfferWTakerDryOffer_test, ripple::test::OfferWOSmallQOffers_test, ripple::test::OfferWOFillOrKill_test, ripple::test::OfferWOPermDEX_test, ripple::test::OfferAllFeatures_test, ripple::test::Offer_manual_test, ripple::OfferStream_test, ripple::test::jtx::oracle::Oracle_test, ripple::test::PlumpBook_test, ripple::test::ThinBook_test, ripple::test::OversizeMeta_test, ripple::test::FindOversizeCross_test, ripple::test::Path_test, ripple::test::PayChan_test, ripple::test::PayStrand_test, ripple::test::PermissionedDEX_test, ripple::test::PermissionedDomains_test, ripple::test::PseudoTx_test, ripple::test::RCLCensorshipDetector_test, ripple::test::RCLValidations_test, ripple::test::ReducedOffer_test, ripple::test::Regression_test, ripple::test::SetAuth_test, ripple::SetRegularKey_test, ripple::test::SetTrust_test, ripple::test::SHAMapStore_test, ripple::Taker_test, ripple::test::TheoreticalQuality_test, ripple::Ticket_test, ripple::test::Transaction_ordering_test, ripple::TrustAndBalance_test, ripple::Apply_test, ripple::test::TxQPosNegFlows_test, ripple::test::TxQMetaInfo_test, ripple::test::ValidatorKeys_test, ripple::test::ValidatorList_test, ripple::test::ValidatorSite_test, ripple::Vault_test, ripple::test::XChain_test, ripple::test::XChainSim_test, ripple::test::base_uint_test, ripple::test::Buffer_test, ripple::test::DetectCrash_test, ripple::test::Expected_test, ripple::test::feeunits_test, ripple::FileUtilities_test, ripple::hardened_hash_test, ripple::tests::IntrusiveShared_test, ripple::IOUAmount_test, ripple::test::join_test, ripple::KeyCache_test, ripple::Number_test, ripple::PerfLog_test, ripple::StringUtilities_test, ripple::TaggedCache_test, ripple::XRPAmount_test, beast::aged_set_test, beast::aged_map_test, beast::aged_multiset_test, beast::aged_multimap_test, beast::aged_unordered_set_test, beast::aged_unordered_map_test, beast::aged_unordered_multiset_test, beast::aged_unordered_multimap_test, beast::abstract_clock_test, beast::basic_seconds_clock_test, ripple::test::CurrentThreadName_test, io_latency_probe_test, beast::Journal_test, beast::PropertyStream_test, beast::Zero_test, beast::unit_test::print_test, beast::IP::IPEndpoint_test, beast::LexicalCast_test, beast::SemanticVersion_test, ripple::cryptoconditions::PreimageSha256_test, ripple::test::ByzantineFailureSim_test, ripple::test::Consensus_test, ripple::test::DistributedValidators_test, ripple::test::LedgerTiming_test, ripple::test::LedgerTrie_test, ripple::test::NegativeUNL_test, ripple::test::NegativeUNLNoAmendment_test, ripple::test::NegativeUNLVoteInternal_test, ripple::test::NegativeUNLVoteScoreTable_test, ripple::test::NegativeUNLVoteGoodScore_test, ripple::test::NegativeUNLVoteOffline_test, ripple::test::NegativeUNLVoteMaxListed_test, ripple::test::NegativeUNLVoteRetiredValidator_test, ripple::test::NegativeUNLVoteNewValidator_test, ripple::test::NegativeUNLVoteFilterValidations_test, ripple::test::ScaleFreeSim_test, ripple::test::csf::Validations_test, ripple::test::ClosureCounter_test, ripple::Config_test, ripple::test::Coroutine_test, ripple::test::JobQueue_test, ripple::SociDB_test, ripple::Workers_test, ripple::test::BasicNetwork_test, ripple::test::Digraph_test, ripple::test::Histogram_test, ripple::test::Scheduler_test, ripple::json_value_test, Json::JsonObject_test, Json::Output_test, Json::JsonWriter_test, ripple::test::Env_test, ripple::test::WSClient_test, ripple::test::BookDirs_test, ripple::test::Directory_test, ripple::Invariants_test, ripple::test::PaymentSandbox_test, ripple::test::PendingSaves_test, ripple::test::SkipList_test, ripple::test::View_test, ripple::test::GetAmendments_test, ripple::NodeStore::Backend_test, ripple::NodeStore::NodeStoreBasic_test, ripple::NodeStore::Database_test, ripple::NodeStore::Timing_test, ripple::NodeStore::tests::varint_test, ripple::tests::cluster_test, ripple::test::compression_test, ripple::test::handshake_test, ripple::ProtocolVersion_test, ripple::test::reduce_relay_test, ripple::test::reduce_relay_simulate_test, ripple::short_read_test, ripple::test::traffic_count_test, ripple::test::tx_reduce_relay_test, ripple::PeerFinder::Livecache_test, ripple::PeerFinder::PeerFinder_test, ripple::test::ApiVersion_test, ripple::BuildInfo_test, ripple::Hooks_test, ripple::InnerObjectFormatsParsedJSON_test, ripple::Issue_test, ripple::Memo_test, ripple::test::MultiApiJson_test, ripple::PublicKey_test, ripple::Quality_test, ripple::SecretKey_test, ripple::Seed_test, ripple::SeqProxy_test, ripple::Serializer_test, ripple::STAccount_test, ripple::STAmount_test, ripple::test::STIssue_test, ripple::STNumber_test, ripple::STObject_test, ripple::STTx_test, ripple::InnerObjectFormatsSerializer_test, ripple::STValidation_test, ripple::TER_test, ripple::types_test, ripple::Resource::ResourceManager_test, ripple::AccountCurrencies_test, ripple::RPC::AccountLines_test, ripple::test::AccountObjects_test, ripple::test::AccountOffers_test, ripple::AccountSet_test, ripple::test::AccountTx_test, ripple::AmendmentBlocked_test, ripple::test::AMMInfo_test, ripple::test::Book_test, ripple::test::BookChanges_test, ripple::Connect_test, ripple::test::DeliveredAmount_test, ripple::test::DepositAuthorized_test, ripple::Feature_test, ripple::test::GatewayBalances_test, ripple::test::jtx::oracle::GetAggregatePrice_test, ripple::GetCounts_test, ripple::test::Handler_test, ripple::RPC::JSONRPC_test, ripple::RPC::WalletPropose_test, ripple::LedgerClosed_test, ripple::LedgerData_test, ripple::test::LedgerEntry_test, ripple::test::LedgerEntry_XChain_test, ripple::LedgerHeader_test, ripple::RPC::LedgerRequestRPC_test, ripple::test::LedgerRPC_test, ripple::test::ManifestRPC_test, ripple::test::NoRipple_test, ripple::NoRippleCheck_test, ripple::NoRippleCheckLimits_test, ripple::OwnerInfo_test, ripple::Peers_test, ripple::test::RobustTransaction_test, ripple::test::Roles_test, ripple::test::RPCCall_test, ripple::test::RPCHelpers_test, ripple::test::RPCOverload_test, ripple::test::ServerInfo_test, ripple::test::Simulate_test, ripple::RPC::codeString_test, ripple::RPC::fillJson_test, ripple::test::Subscribe_test, ripple::Transaction_test, ripple::TransactionEntry_test, ripple::TransactionHistory_test, ripple::test::ValidatorInfo_test, ripple::test::ValidatorRPC_test, ripple::Version_test, ripple::test::Server_test, ripple::test::ServerStatus_test, ripple::tests::FetchPack_test, ripple::tests::SHAMap_test, ripple::tests::SHAMapPathProof_test, and ripple::tests::SHAMapSync_test.

diff --git a/classes.html b/classes.html index e5a067ac61..23ee21e5c4 100644 --- a/classes.html +++ b/classes.html @@ -118,7 +118,7 @@ $(function() {
NegativeUNL_test (ripple::test)
NegativeUNLNoAmendment_test (ripple::test)
NegativeUNLVote (ripple)
NegativeUNLVoteFilterValidations_test (ripple::test)
NegativeUNLVoteGoodScore_test (ripple::test)
NegativeUNLVoteInternal_test (ripple::test)
NegativeUNLVoteMaxListed_test (ripple::test)
NegativeUNLVoteNewValidator_test (ripple::test)
NegativeUNLVoteOffline_test (ripple::test)
NegativeUNLVoteRetiredValidator_test (ripple::test)
NegativeUNLVoteScoreTable_test (ripple::test)
NetClock (ripple)
Network (ripple::test)
NetworkHistory (ripple::test)
NetworkID_test (ripple::test)
NetworkOfTwo (ripple::test)
NetworkOPs (ripple)
NetworkOPsImp (ripple)
next_t (ripple::keylet)
nflags (ripple::test::jtx)
NFTokenAcceptOffer (ripple)
NFTokenAllFeatures_test (ripple)
NFTokenAuth_test (ripple)
NFTokenBaseUtil_test (ripple)
NFTokenBurn (ripple)
NFTokenBurnAllFeatures_test (ripple)
NFTokenBurnBaseUtil_test (ripple)
NFTokenBurnWOfixFungTokens_test (ripple)
NFTokenBurnWOFixNFTPageLinks_test (ripple)
NFTokenBurnWOFixTokenRemint_test (ripple)
NFTokenCancelOffer (ripple)
NFTokenCountTracking (ripple)
NFTokenCreateOffer (ripple)
NFTokenDir_test (ripple)
NFTokenDisallowIncoming_test (ripple)
NFTokenMint (ripple)
NFTokenModify (ripple)
NFTokenWOfixV1_test (ripple)
NFTokenWOMintOffer_test (ripple)
NFTokenWOModify_test (ripple)
NFTokenWOTokenRemint_test (ripple)
NFTokenWOTokenReserve_test (ripple)
NoBadOffers (ripple)
LockFreeStack::Node (beast)
Validations_test::Node (ripple::test::csf)
Node (ripple::ledger_trie_detail)
NoDeepFreezeTrustLinesWithoutFreeze (ripple)
NodeFamily (ripple)
NodeIDTag (ripple::detail)
NodeObject (ripple)
AccountTx_test::NodeSanity (ripple::test)
NodeStoreBasic_test (ripple::NodeStore)
NodeStoreScheduler (ripple)
NoEdgeData (ripple::test::csf::detail)
None (ripple::test::jtx)
none_t (ripple::test::jtx)
nonhash (ripple::test)
nonPresentObject_t (ripple::detail)
NoRipple_test (ripple::test)
NoRippleCheck_test (ripple)
NoRippleCheckLimits_test (ripple)
TER_test::NotConvertible (ripple)
NoXRPTrustLines (ripple)
NoZeroEscrow (ripple)
NuDBBackend (ripple::NodeStore)
NuDBFactory (ripple::NodeStore)
NullBackend (ripple::NodeStore)
NullCollector (beast::insight)
NullCollector (ripple::test::csf)
NullCollectorImp (beast::insight::detail)
NullCounterImpl (beast::insight::detail)
NullEventImpl (beast::insight::detail)
NullFactory (ripple::NodeStore)
NullGaugeImpl (beast::insight::detail)
NullHookImpl (beast::insight::detail)
NullJournalSink (beast)
NullMeterImpl (beast::insight::detail)
Number (ripple)
Number_test (ripple)
NumberParts (ripple)
NumberRoundModeGuard (ripple)
NumberSO (ripple)
O
-
Object (Json)
Offer_manual_test (ripple::test)
OfferAllFeatures_test (ripple::test)
OfferBaseUtil_test (ripple::test)
OfferStream (ripple)
OfferStream_test (ripple)
OfferWOFillOrKill_test (ripple::test)
OfferWOFlowCross_test (ripple::test)
OfferWOPermDEX_test (ripple::test)
OfferWOSmallQOffers_test (ripple::test)
OfferWTakerDryOffer_test (ripple::test)
open_ledger_t (ripple)
OpenLedger (ripple)
openssl_ripemd160_hasher (ripple)
openssl_sha256_hasher (ripple)
openssl_sha512_hasher (ripple)
OpenView (ripple)
OptionaledField (ripple)
STObject::OptionalProxy (ripple)
Oracle (ripple::test::jtx::oracle)
Oracle_test (ripple::test::jtx::oracle)
OrderBookDB (ripple)
TxQ::OrderCandidates (ripple)
RippleCalc::Output (ripple::path)
Output_test (Json)
Overlay (ripple)
Overlay (ripple::test)
OverlayImpl (ripple)
OverlaySim (ripple::test)
OversizeMeta_test (ripple::test)
owner (ripple::test::jtx::token)
owner_count (ripple::test::jtx)
OwnerInfo_test (ripple)
owners (ripple::test::jtx)
+
Object (Json)
Offer_manual_test (ripple::test)
OfferAllFeatures_test (ripple::test)
OfferBaseUtil_test (ripple::test)
OfferStream (ripple)
OfferStream_test (ripple)
OfferWOFillOrKill_test (ripple::test)
OfferWOPermDEX_test (ripple::test)
OfferWOSmallQOffers_test (ripple::test)
OfferWTakerDryOffer_test (ripple::test)
open_ledger_t (ripple)
OpenLedger (ripple)
openssl_ripemd160_hasher (ripple)
openssl_sha256_hasher (ripple)
openssl_sha512_hasher (ripple)
OpenView (ripple)
OptionaledField (ripple)
STObject::OptionalProxy (ripple)
Oracle (ripple::test::jtx::oracle)
Oracle_test (ripple::test::jtx::oracle)
OrderBookDB (ripple)
TxQ::OrderCandidates (ripple)
RippleCalc::Output (ripple::path)
Output_test (Json)
Overlay (ripple)
Overlay (ripple::test)
OverlayImpl (ripple)
OverlaySim (ripple::test)
OversizeMeta_test (ripple::test)
owner (ripple::test::jtx::token)
owner_count (ripple::test::jtx)
OwnerInfo_test (ripple)
owners (ripple::test::jtx)
P
packed_spinlock (ripple)
aged_ordered_container::pair_value_compare (beast::detail)
Timing_test::parallel_for_lambda (ripple::NodeStore)
LedgerServer::Parameter (ripple::test)
NetworkHistory::Parameter (ripple::test)
Timing_test::Params (ripple::NodeStore)
parse_error (ripple::test::jtx)
ParsedPort (ripple)
Env::ParsedResult (ripple::test::jtx)
parsedURL (ripple)
partitioned_unordered_map (ripple)
FlowDebugInfo::PassInfo (ripple::path::detail)
path (ripple::test::jtx)
Path (ripple::test)
Path_test (ripple::test)
Pathfinder (ripple)
PathFindTrustLine (ripple)
Pathfinder::PathRank (ripple)
PathRequest (ripple)
PathRequests (ripple)
paths (ripple::test::jtx)
PathSet (ripple::test)
Workers::PausedTag (ripple)
PayChan_test (ripple::test)
PayChanClaim (ripple)
PayChanCreate (ripple)
PayChanFund (ripple)
Payment (ripple)
PaymentSandbox (ripple)
PaymentSandbox_test (ripple::test)
PayStrand_test (ripple::test)
Peer (ripple)
BasicNetwork_test::Peer (ripple::test)
Peer (ripple::test::csf)
peer_in_cluster (ripple)
peer_in_set (ripple)
PeerDataCounts (ripple::detail)
PeerFinder_test (ripple::PeerFinder)
PeerGroup (ripple::test::csf)
PeerImp (ripple)
Slot::PeerInfo (ripple::reduce_relay)
PeerPartial (ripple::test)
PeerReservation (ripple)
PeerReservationTable (ripple)
Peers_test (ripple)
PeerSet (ripple)
PeerSetBuilder (ripple)
PeerSetBuilderImpl (ripple)
PeerSetImpl (ripple)
PeerSim (ripple::test)
tx_reduce_relay_test::PeerTest (ripple::test)
PendingSaves (ripple)
PendingSaves_test (ripple::test)
PerfLog (ripple::perf)
PerfLog_test (ripple)
PerfLogImp (ripple::perf)
PerfLogTest (ripple::perf)
Permission (ripple)
PermissionedDEX (ripple::test::jtx)
PermissionedDEX_test (ripple::test)
PermissionedDomainDelete (ripple)
PermissionedDomains_test (ripple::test)
PermissionedDomainSet (ripple)
PlainHTTPPeer (ripple)
PlainWSPeer (ripple)
PlumpBook_test (ripple::test)
Port (ripple)
Peer::Position (ripple::test::csf)
PowerLawDistribution (ripple::test::csf)
Preamble (ripple::cryptoconditions::der)
PreclaimContext (ripple)
PreclaimResult (ripple)
PreflightContext (ripple)
PreflightResult (ripple)
PreimageSha256 (ripple::cryptoconditions)
PreimageSha256_test (ripple::cryptoconditions)
PrettyAmount (ripple::test::jtx)
PrettyAsset (ripple::test::jtx)
print_test (beast::unit_test)
SField::private_access_tag_t (ripple)
NodeObject::PrivateAccess (ripple)
Account::privateCtorTag (ripple::test::jtx)
Peer::ProcessingDelays (ripple::test::csf)
Processor (ripple)
progress (ripple::NodeStore)
prop (ripple::test::jtx)
JTx::prop_list (ripple::test::jtx)
prop_type (ripple::test::jtx)
PropertyStream (beast)
PropertyStream_test (beast)
ProtocolVersion_test (ripple)
PropertyStream::Proxy (beast)
Object::Proxy (Json)
STObject::Proxy (ripple)
PseudoTx_test (ripple::test)
PublicKey (ripple)
PublicKey_test (ripple)
ValidatorList::PublisherList (ripple)
ValidatorList::PublisherListCollection (ripple)
ValidatorList::PublisherListStats (ripple)
diff --git a/classripple_1_1BookStep.html b/classripple_1_1BookStep.html index e8b1b3e293..cf46ab0f03 100644 --- a/classripple_1_1BookStep.html +++ b/classripple_1_1BookStep.html @@ -623,7 +623,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 1015 of file BookStep.cpp.

+

Definition at line 1014 of file BookStep.cpp.

@@ -667,7 +667,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 1136 of file BookStep.cpp.

+

Definition at line 1135 of file BookStep.cpp.

@@ -713,7 +713,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 1307 of file BookStep.cpp.

+

Definition at line 1306 of file BookStep.cpp.

@@ -735,7 +735,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 1345 of file BookStep.cpp.

+

Definition at line 1344 of file BookStep.cpp.

@@ -938,7 +938,7 @@ template<template< typename, typename > typename Offer>
-

Definition at line 879 of file BookStep.cpp.

+

Definition at line 878 of file BookStep.cpp.

@@ -978,7 +978,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 928 of file BookStep.cpp.

+

Definition at line 927 of file BookStep.cpp.

@@ -1008,7 +1008,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 939 of file BookStep.cpp.

+

Definition at line 938 of file BookStep.cpp.

@@ -1038,7 +1038,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 979 of file BookStep.cpp.

+

Definition at line 978 of file BookStep.cpp.

@@ -1068,7 +1068,7 @@ template<class TIn , class TOut , class TDerived >
-

Definition at line 993 of file BookStep.cpp.

+

Definition at line 992 of file BookStep.cpp.

diff --git a/classripple_1_1CreateOffer-members.html b/classripple_1_1CreateOffer-members.html index 8727c77c06..0050ca5c46 100644 --- a/classripple_1_1CreateOffer-members.html +++ b/classripple_1_1CreateOffer-members.html @@ -125,14 +125,13 @@ $(function() { select_path(bool have_direct, OfferStream const &direct, bool have_bridge, OfferStream const &leg1, OfferStream const &leg2)ripple::CreateOfferprivatestatic step_account(OfferStream &stream, Taker const &taker)ripple::CreateOfferprivatestatic stepCounter_ripple::CreateOfferprivate - takerCross(Sandbox &sb, Sandbox &sbCancel, Amounts const &takerAmount)ripple::CreateOfferprivate - ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)ripple::Transactorstatic - Transactor(Transactor const &)=deleteripple::Transactorprotected - Transactor(ApplyContext &ctx)ripple::Transactorexplicitprotected - trapTransaction(uint256) constripple::Transactorprivate - view()ripple::Transactor - view() constripple::Transactor - ~Transactor()=defaultripple::Transactorprotectedvirtual + ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)ripple::Transactorstatic + Transactor(Transactor const &)=deleteripple::Transactorprotected + Transactor(ApplyContext &ctx)ripple::Transactorexplicitprotected + trapTransaction(uint256) constripple::Transactorprivate + view()ripple::Transactor + view() constripple::Transactor + ~Transactor()=defaultripple::Transactorprotectedvirtual