Merge branch 'develop' into tapanito/unit-tests

This commit is contained in:
Vito Tumas
2026-05-19 15:56:02 +02:00
committed by GitHub
868 changed files with 13479 additions and 10887 deletions

View File

@@ -25,6 +25,7 @@
#include <xrpl/basics/Number.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/safe_cast.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ApplyView.h>
@@ -555,11 +556,11 @@ private:
{
// bad preflight1
json::Value jv = json::ObjectValue;
json::Value jv = json::ValueType::Object;
jv[jss::Account] = alice_.human();
jv[jss::TransactionType] = jss::AMMDeposit;
jv[jss::Asset] = STIssue(sfAsset, XRP).getJson(JsonOptions::KNone);
jv[jss::Asset2] = STIssue(sfAsset, USD).getJson(JsonOptions::KNone);
jv[jss::Asset] = STIssue(sfAsset, XRP).getJson(JsonOptions::Values::None);
jv[jss::Asset2] = STIssue(sfAsset, USD).getJson(JsonOptions::Values::None);
jv[jss::Fee] = "-1";
env(jv, Ter(temBAD_FEE));
}
@@ -570,12 +571,12 @@ private:
alice_, IOUAmount{-1}, std::nullopt, std::nullopt, Ter(temBAD_AMM_TOKENS));
{
json::Value jv = json::ObjectValue;
json::Value jv = json::ValueType::Object;
jv[jss::Account] = alice_.human();
jv[jss::TransactionType] = jss::AMMDeposit;
jv[jss::Asset] = STIssue(sfAsset, XRP).getJson(JsonOptions::KNone);
jv[jss::Asset2] = STIssue(sfAsset, USD).getJson(JsonOptions::KNone);
jv[jss::LPTokenOut] = USD(100).value().getJson(JsonOptions::KNone);
jv[jss::Asset] = STIssue(sfAsset, XRP).getJson(JsonOptions::Values::None);
jv[jss::Asset2] = STIssue(sfAsset, USD).getJson(JsonOptions::Values::None);
jv[jss::LPTokenOut] = USD(100).value().getJson(JsonOptions::Values::None);
jv[jss::Flags] = tfLPToken;
env(jv, Ter(temBAD_AMM_TOKENS));
}
@@ -2733,18 +2734,17 @@ private:
// 1st Interval after close, price for 0th interval.
env(ammAlice.bid({.account = bob_}));
env.close(seconds(kAUCTION_SLOT_INTERVAL_DURATION + 1));
env.close(seconds(kAuctionSlotIntervalDuration + 1));
BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 1, IOUAmount{1'155, -1}));
// 10th Interval after close, price for 1st interval.
env(ammAlice.bid({.account = carol_}));
env.close(seconds((10 * kAUCTION_SLOT_INTERVAL_DURATION) + 1));
env.close(seconds((10 * kAuctionSlotIntervalDuration) + 1));
BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 10, IOUAmount{121'275, -3}));
// 20th Interval (expired) after close, price for 10th interval.
env(ammAlice.bid({.account = bob_}));
env.close(
seconds((kAUCTION_SLOT_TIME_INTERVALS * kAUCTION_SLOT_INTERVAL_DURATION) + 1));
env.close(seconds((kAuctionSlotTimeIntervals * kAuctionSlotIntervalDuration) + 1));
BEAST_EXPECT(ammAlice.expectAuctionSlot(0, std::nullopt, IOUAmount{127'33875, -5}));
// 0 Interval.
@@ -2971,7 +2971,7 @@ private:
ammTokens));
}
// Auction slot expired, no discounted fee
env.close(seconds(kTOTAL_TIME_SLOT_SECS + 1));
env.close(seconds(kTotalTimeSlotSecs + 1));
// clock is parent's based
env.close();
if (!features[fixAMMv1_1])
@@ -3060,7 +3060,7 @@ private:
testAMM(
[&](AMM& ammAlice, Env& env) {
// Bid a tiny amount
auto const tiny = Number{STAmount::kMIN_VALUE, STAmount::kMIN_OFFSET};
auto const tiny = Number{STAmount::kMinValue, STAmount::kMinOffset};
env(ammAlice.bid({.account = alice_, .bidMin = IOUAmount{tiny}}));
// Auction slot purchase price is equal to the tiny amount
// since the minSlotPrice is 0 with no trading fee.
@@ -3070,7 +3070,7 @@ private:
// Bid the tiny amount
env(ammAlice.bid({
.account = alice_,
.bidMin = IOUAmount{STAmount::kMIN_VALUE, STAmount::kMIN_OFFSET},
.bidMin = IOUAmount{STAmount::kMinValue, STAmount::kMinOffset},
}));
// Pay slightly higher price
BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}}));
@@ -3228,9 +3228,9 @@ private:
testAMM([&](AMM& ammAlice, Env& env) {
auto const baseFee = env.current()->fees().base;
env(escrow::create(carol_, ammAlice.ammAccount(), XRP(1)),
escrow::kCONDITION(escrow::kCB1),
escrow::kFINISH_TIME(env.now() + 1s),
escrow::kCANCEL_TIME(env.now() + 2s),
escrow::kCondition(escrow::kCb1),
escrow::kFinishTime(env.now() + 1s),
escrow::kCancelTime(env.now() + 2s),
Fee(baseFee * 150),
Ter(tecNO_PERMISSION));
});
@@ -5102,7 +5102,7 @@ private:
all);
fund(env, gw_, {alice_}, XRP(20'000), {USD(10'000)});
AMM amm(env, gw_, XRP(10'000), USD(10'000));
for (auto i = 0; i < kMAX_DELETABLE_AMM_TRUST_LINES + 10; ++i)
for (auto i = 0; i < kMaxDeletableAmmTrustLines + 10; ++i)
{
Account const a{std::to_string(i)};
env.fund(XRP(1'000), a);
@@ -5160,7 +5160,7 @@ private:
all);
fund(env, gw_, {alice_}, XRP(20'000), {USD(10'000)});
AMM amm(env, gw_, XRP(10'000), USD(10'000));
for (auto i = 0; i < (kMAX_DELETABLE_AMM_TRUST_LINES * 2) + 10; ++i)
for (auto i = 0; i < (kMaxDeletableAmmTrustLines * 2) + 10; ++i)
{
Account const a{std::to_string(i)};
env.fund(XRP(1'000), a);
@@ -5218,7 +5218,8 @@ private:
fail();
}
amm.deposit(carol_, 1'000);
auto affected = env.meta()->getJson(JsonOptions::KNone)[sfAffectedNodes.fieldName];
auto affected =
env.meta()->getJson(JsonOptions::Values::None)[sfAffectedNodes.fieldName];
try
{
bool found = false;
@@ -6729,9 +6730,9 @@ private:
AccountID const accountId = xrpl::pseudoAccountAddress(*env.current(), keylet.key);
env(pay(env.master.id(), accountId, XRP(1000)),
Seq(kAUTOFILL),
Fee(kAUTOFILL),
Sig(kAUTOFILL));
Seq(kAutofill),
Fee(kAutofill),
Sig(kAutofill));
}
AMM const ammAlice(
@@ -7271,6 +7272,95 @@ private:
BEAST_EXPECT(env.balance(trader, shareIssue) == STAmount(shareIssue, std::uint64_t{0}));
}
void
testStaleAuthAccountsAfterReinit(FeatureBitset features)
{
testcase("Test AuthAccounts reset after empty pool reinitialization");
using namespace jtx;
Env env(
*this,
envconfig([](std::unique_ptr<Config> cfg) {
cfg->FEES.reference_fee = XRPAmount(1);
return cfg;
}),
features);
Account const dan("dan");
Account const ed("ed");
fund(env, gw_, {alice_, carol_, bob_, dan, ed}, XRP(50'000), {USD(50'000)});
AMM amm(env, alice_, XRP(10'000), USD(10'000));
// Create excess trustlines to prevent AMM auto-deletion on withdrawal.
for (auto i = 0; i < kMaxDeletableAmmTrustLines + 10; ++i)
{
Account const a{std::to_string(i)};
env.fund(XRP(1'000), a);
env(trust(a, STAmount{amm.lptIssue(), 10'000}));
env.close();
}
// Carol deposits so she has LP tokens to bid.
amm.deposit(carol_, 1'000'000);
// Carol wins the auction slot, authorizing bob and dan.
env(amm.bid({
.account = carol_,
.bidMin = 100,
.authAccounts = {bob_, dan},
}));
env.close();
BEAST_EXPECT(amm.expectAuctionSlot({bob_.id(), dan.id()}));
// Withdraw all — AMM enters empty state but is not deleted because
// excess trustlines prevent auto-deletion.
amm.withdrawAll(alice_);
amm.withdrawAll(carol_);
BEAST_EXPECT(amm.ammExists());
// Pre-conditions before re-init: AMM is empty and stale sfAuthAccounts
// from carol's bid are still present.
BEAST_EXPECT(amm.getLPTokensBalance() == IOUAmount{0});
BEAST_EXPECT(amm.expectAuctionSlot({bob_.id(), dan.id()}));
// Ed re-initializes the AMM via tfTwoAssetIfEmpty with fee=500.
amm.deposit(
ed,
std::nullopt,
XRP(10'000),
USD(10'000),
std::nullopt,
tfTwoAssetIfEmpty,
std::nullopt,
std::nullopt,
500);
auto const ammSle = env.current()->read(keylet::amm(amm[0], amm[1]));
BEAST_EXPECT(ammSle && ammSle->isFieldPresent(sfAuctionSlot));
auto const& slot = safeDowncast<STObject const&>(ammSle->peekAtField(sfAuctionSlot));
// sfDiscountedFee = 500 / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION = 50,
// sfPrice = 0 (reset on init), time interval = 0 (freshly issued slot).
BEAST_EXPECT(amm.expectAuctionSlot(50, 0, IOUAmount{0}));
// sfAccount must be the re-initializing depositor, not the previous
// slot holder (carol).
BEAST_EXPECT(slot[sfAccount] == ed.id());
// sfTradingFee on the AMM SLE must reflect ed's deposit fee.
BEAST_EXPECT(ammSle->getFieldU16(sfTradingFee) == 500);
// sfVoteSlots must be reset to a single entry for ed.
auto const& votes = ammSle->getFieldArray(sfVoteSlots);
BEAST_EXPECT(votes.size() == 1);
if (!votes.empty())
{
BEAST_EXPECT(votes[0].getAccountID(sfAccount) == ed.id());
BEAST_EXPECT(votes[0].getFieldU16(sfTradingFee) == 500);
BEAST_EXPECT(votes[0].getFieldU32(sfVoteWeight) == kVoteWeightScaleFactor);
}
// sfAuthAccounts behaviour depends on the fix.
if (features[fixCleanup3_2_0])
{
BEAST_EXPECT(!slot.isFieldPresent(sfAuthAccounts));
}
else
{
BEAST_EXPECT(
slot.isFieldPresent(sfAuthAccounts) && !slot.getFieldArray(sfAuthAccounts).empty());
}
}
void
run() override
{
@@ -7343,6 +7433,8 @@ private:
testWithdrawRounding(all);
testWithdrawRounding(all - fixAMMv1_3);
testFailedPseudoAccount();
testStaleAuthAccountsAfterReinit(all);
testStaleAuthAccountsAfterReinit(all - fixCleanup3_2_0);
}
};