mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-03 00:36:48 +00:00
fix: Prevent stale AuthAccounts from persisting after tfTwoAssetIfEmpty re-initialization (#6996)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
This commit is contained in:
@@ -817,6 +817,9 @@ initializeFeeAuctionVote(
|
||||
{
|
||||
auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE
|
||||
}
|
||||
// Clear stale auth accounts from any previous auction slot holder.
|
||||
if (rules.enabled(fixCleanup3_2_0) && auctionSlot.isFieldPresent(sfAuthAccounts))
|
||||
auctionSlot.makeFieldAbsent(sfAuthAccounts);
|
||||
}
|
||||
|
||||
Expected<bool, TER>
|
||||
|
||||
@@ -23,6 +23,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>
|
||||
@@ -7080,6 +7081,95 @@ private:
|
||||
{all});
|
||||
}
|
||||
|
||||
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 < kMAX_DELETABLE_AMM_TRUST_LINES + 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) == kVOTE_WEIGHT_SCALE_FACTOR);
|
||||
}
|
||||
// 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
|
||||
{
|
||||
@@ -7150,6 +7240,8 @@ private:
|
||||
testWithdrawRounding(all);
|
||||
testWithdrawRounding(all - fixAMMv1_3);
|
||||
testFailedPseudoAccount();
|
||||
testStaleAuthAccountsAfterReinit(all);
|
||||
testStaleAuthAccountsAfterReinit(all - fixCleanup3_2_0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user