Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(AMM): prevent orphaned objects, inconsistent ledger state: (updates XLS-30) #4626

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
da44bc8
Cleanup AMM account owner directory on AMM account deletion
gregtatcam Jul 17, 2023
5cead5b
Disallow check create to AMM
gregtatcam Jul 17, 2023
eaef4b6
Fix unconstrained entries in AuthAccount
gregtatcam Jul 17, 2023
b28101f
Allow SetTrust on AMM only for LP tokens and other changes
gregtatcam Jul 18, 2023
cbd7144
Add AMMDelete to handle amortized deletion and other changes to addre…
gregtatcam Jul 23, 2023
40317c5
Fix missing AMMDelete transactor files
gregtatcam Jul 24, 2023
9cf3fd7
Merge remote-tracking branch 'origin/develop' into amm-fixes
gregtatcam Jul 24, 2023
75af923
Update api changelog for AMM feature
gregtatcam Jul 24, 2023
cd88481
Maintain AMM trustlines count in AMM root account and other changes a…
gregtatcam Jul 28, 2023
9ddff74
Check no directory left after AMM trustlines are deleted and other mi…
gregtatcam Jul 29, 2023
2b1c8c6
Disallow clawback out of AMM account
gregtatcam Jul 31, 2023
c5e3c89
Disallow AMM create if issuer has clawback enabled and other changes
gregtatcam Aug 1, 2023
b63e696
Remove lsfAMM flag and use sfAMMID instead, plus minor refactoring
gregtatcam Aug 2, 2023
2efa0f8
Rall-back lsfAllowTrustLineClawback flag and other changes
gregtatcam Aug 4, 2023
fa0d9c4
Address auditor's feedback
gregtatcam Aug 7, 2023
7e2c454
Update tecAMM_ACCOUNT error message
gregtatcam Aug 11, 2023
c07bb67
Merge branch 'develop' into amm-fixes
manojsdoshi Aug 18, 2023
484e01f
Add unit-tests to verify CLOB/AMM offer and strand selection logic
gregtatcam Aug 7, 2023
cfa3201
Address reviewer's feedback
gregtatcam Aug 11, 2023
a34d29b
Add comments to stress that the selection tests would have to be upda…
gregtatcam Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/ripple/app/tx/impl/AMMCreate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ applyCreate(
TOTAL_TIME_SLOT_SECS;
auctionSlot.setFieldU32(sfExpiration, expiration);
auctionSlot.setFieldAmount(sfPrice, STAmount{lpTokens.issue(), 0});
sb.insert(ammSle);

// Add owner directory to link the root account and AMM object.
if (auto const page = sb.dirInsert(
Expand All @@ -294,6 +293,9 @@ applyCreate(
JLOG(j_.debug()) << "AMM Instance: failed to insert owner dir";
return {tecDIR_FULL, false};
}
else
ammSle->setFieldU64(sfOwnerNode, *page);
sb.insert(ammSle);

// Send LPT to LP.
auto res = accountSend(sb, *ammAccount, account_, lpTokens, ctx_.journal);
Expand Down
76 changes: 72 additions & 4 deletions src/ripple/app/tx/impl/AMMWithdraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <ripple/ledger/Sandbox.h>
#include <ripple/ledger/View.h>
#include <ripple/protocol/AMMCore.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/STAccount.h>
#include <ripple/protocol/TxFlags.h>

Expand Down Expand Up @@ -410,9 +409,78 @@ AMMWithdraw::deleteAccount(Sandbox& sb, AccountID const& ammAccountID)
if (!sleAMMRoot || !ammSle)
return tecINTERNAL;

// Note, the AMM trust lines are deleted since the balance
// goes to 0. It also means there are no linked
// ledger objects.
// Delete directory entry linking the root account and AMM object
Keylet const ownerDirKeylet{keylet::ownerDir(ammAccountID)};
if (!sb.dirRemove(
ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
{
JLOG(j_.fatal()) << "AMM Withdraw: failed to remove dir link";
return tecINTERNAL;
}

// Delete the trustlines
std::shared_ptr<SLE> sleDirNode{};
unsigned int uDirEntry{0};
uint256 dirEntry{beast::zero};
if (sb.exists(ownerDirKeylet) &&
dirFirst(sb, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing actionable, just a comment: the dirFirst/dirNext iterators are so ugly to use...

{
do
{
auto sleItem = sb.peek(keylet::child(dirEntry));
if (!sleItem)
{
// Directory node has an invalid index
JLOG(j_.fatal())
<< "AMM Withdraw: Directory node in ledger " << sb.seq()
<< " has index to object that is missing: "
<< to_string(dirEntry);
return tefBAD_LEDGER;
}

// Should only have the trustlines
if (auto const nodeType = safe_cast<LedgerEntryType>(
sleItem->getFieldU16(sfLedgerEntryType));
nodeType != LedgerEntryType::ltRIPPLE_STATE)
{
JLOG(j_.error())
<< "AMM Withdraw: deleting non-trustline " << nodeType;
return tecINTERNAL;
}

// Trustlines must have zero balance
if (sleItem->getFieldAmount(sfBalance) != beast::zero)
{
JLOG(j_.error()) << "AMM Withdraw: deleting trustline with "
"non-zero balance.";
return tecINTERNAL;
}

auto const& [lowAccountID, highAccountID] = std::minmax(
sleItem->getFieldAmount(sfHighLimit).getIssuer(), ammAccountID);
if (auto const ter = trustDelete(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suppose I create a trustline to the AMM, increasing my ownercount by 1.
Then the AMM is subsequently deleted, deleting my trustline object.
Where in this code is my ownercount returned to me? I don't believe trustDelete handles this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will update.

sb, sleItem, lowAccountID, highAccountID, ctx_.journal);
ter != tesSUCCESS)
{
JLOG(j_.error())
<< "AMM Withdraw: failed to delete the trustline.";
return ter;
}

// Update the iterator
assert(uDirEntry == 1);
if (uDirEntry != 1)
{
JLOG(j_.error())
<< "AMM Withdraw: iterator re-validation failed.";
return tefBAD_LEDGER;
}
uDirEntry = 0;

} while (
dirNext(sb, ownerDirKeylet.key, sleDirNode, uDirEntry, dirEntry));
}

sb.erase(ammSle);
sb.erase(sleAMMRoot);

Expand Down
4 changes: 4 additions & 0 deletions src/ripple/app/tx/impl/CreateCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ CreateCheck::preclaim(PreclaimContext const& ctx)
(flags & lsfDisallowIncomingCheck))
return tecNO_PERMISSION;

// AMM can not cash the check
if (flags & lsfAMM)
return tecNO_PERMISSION;

if ((flags & lsfRequireDestTag) && !ctx.tx.isFieldPresent(sfDestinationTag))
{
// The tag is basically account-specific information we don't
Expand Down
6 changes: 6 additions & 0 deletions src/ripple/protocol/impl/InnerObjectFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ InnerObjectFormats::InnerObjectFormats()
{sfPrice, soeREQUIRED},
{sfAuthAccounts, soeOPTIONAL},
});

add(sfAuthAccount.jsonName.c_str(),
sfAuthAccount.getCode(),
{
{sfAccount, soeREQUIRED},
});
}

InnerObjectFormats const&
Expand Down
3 changes: 2 additions & 1 deletion src/ripple/protocol/impl/LedgerFormats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ LedgerFormats::LedgerFormats()
{sfAuctionSlot, soeOPTIONAL},
{sfLPTokenBalance, soeREQUIRED},
{sfAsset, soeREQUIRED},
{sfAsset2, soeREQUIRED}
{sfAsset2, soeREQUIRED},
{sfOwnerNode, soeREQUIRED}
},
commonFields);

Expand Down
1 change: 0 additions & 1 deletion src/test/app/AMMExtended_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include <test/jtx/AMM.h>
#include <test/jtx/AMMTest.h>
#include <test/jtx/PathSet.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/amount.h>
#include <test/jtx/sendmax.h>

Expand Down
14 changes: 13 additions & 1 deletion src/test/app/AMM_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <test/jtx/AMMTest.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/amount.h>
#include <test/jtx/sendmax.h>

Expand Down Expand Up @@ -1770,9 +1769,16 @@ struct AMM_test : public jtx::AMMTest

// Withdraw all tokens.
testAMM([&](AMM& ammAlice, Env& env) {
env(trust(carol, STAmount{ammAlice.lptIssue(), 10000}));
env(trust(
carol,
STAmount{Issue{EUR.currency, ammAlice.ammAccount()}, 10000}));
env.close();
ammAlice.withdrawAll(alice);
BEAST_EXPECT(!ammAlice.ammExists());

BEAST_EXPECT(!env.le(keylet::ownerDir(ammAlice.ammAccount())));

// Can create AMM for the XRP/USD pair
AMM ammCarol(env, carol, XRP(10'000), USD(10'000));
BEAST_EXPECT(ammCarol.expectBalances(
Expand Down Expand Up @@ -2750,6 +2756,12 @@ struct AMM_test : public jtx::AMMTest
ter(tecNO_PERMISSION));
});

// Can't pay into AMM with checks.
testAMM([&](AMM& ammAlice, Env& env) {
env(check::create(env.master.id(), ammAlice.ammAccount(), XRP(100)),
ter(tecNO_PERMISSION));
});

// Pay amounts close to one side of the pool
testAMM(
[&](AMM& ammAlice, Env& env) {
Expand Down
1 change: 0 additions & 1 deletion src/test/app/CrossingLimits_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <ripple/beast/unit_test.h>
#include <ripple/protocol/Feature.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {
namespace test {
Expand Down
1 change: 0 additions & 1 deletion src/test/app/Escrow_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

#include <algorithm>
#include <iterator>
Expand Down
1 change: 0 additions & 1 deletion src/test/app/Flow_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/PathSet.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {
namespace test {
Expand Down
1 change: 0 additions & 1 deletion src/test/app/Freeze_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {

Expand Down
1 change: 0 additions & 1 deletion src/test/app/Offer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/PathSet.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/WSClient.h>

namespace ripple {
Expand Down
1 change: 0 additions & 1 deletion src/test/app/Path_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
#include <condition_variable>
#include <mutex>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/envconfig.h>
#include <thread>

Expand Down
1 change: 0 additions & 1 deletion src/test/app/PayChan_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

#include <chrono>

Expand Down
1 change: 0 additions & 1 deletion src/test/app/PayStrand_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#include "ripple/app/paths/AMMContext.h"
#include <test/jtx.h>
#include <test/jtx/PathSet.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {
namespace test {
Expand Down
1 change: 0 additions & 1 deletion src/test/app/TrustAndBalance_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <ripple/protocol/SField.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/WSClient.h>

namespace ripple {
Expand Down
1 change: 1 addition & 0 deletions src/test/jtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <test/jtx/Env.h>
#include <test/jtx/Env_ss.h>
#include <test/jtx/JTx.h>
#include <test/jtx/TestHelpers.h>
#include <test/jtx/account_txn_id.h>
#include <test/jtx/acctdelete.h>
#include <test/jtx/amount.h>
Expand Down
33 changes: 33 additions & 0 deletions src/test/jtx/TestHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <ripple/json/json_value.h>
#include <ripple/protocol/AccountID.h>
#include <ripple/protocol/Quality.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/jss.h>
#include <test/jtx/Env.h>

Expand Down Expand Up @@ -405,6 +406,38 @@ STPathElement
allpe(AccountID const& a, Issue const& iss);
/***************************************************************/

/* Check */
/***************************************************************/
namespace check {

/** Create a check. */
// clang-format off
template <typename A>
requires std::is_same_v<A, AccountID>
Json::Value
create(A const& account, A const& dest, STAmount const& sendMax)
{
Json::Value jv;
jv[sfAccount.jsonName] = to_string(account);
jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
jv[sfDestination.jsonName] = to_string(dest);
jv[sfTransactionType.jsonName] = jss::CheckCreate;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}
// clang-format on

inline Json::Value
create(
jtx::Account const& account,
jtx::Account const& dest,
STAmount const& sendMax)
{
return create(account.id(), dest.id(), sendMax);
}

} // namespace check

} // namespace jtx
} // namespace test
} // namespace ripple
Expand Down
7 changes: 0 additions & 7 deletions src/test/jtx/check.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ namespace jtx {
/** Check operations. */
namespace check {

/** Create a check. */
Json::Value
create(
jtx::Account const& account,
jtx::Account const& dest,
STAmount const& sendMax);

/** Cash a check requiring that a specific amount be delivered. */
Json::Value
cash(jtx::Account const& dest, uint256 const& checkId, STAmount const& amount);
Expand Down
16 changes: 0 additions & 16 deletions src/test/jtx/impl/check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,6 @@ namespace jtx {

namespace check {

// Create a check.
Json::Value
create(
jtx::Account const& account,
jtx::Account const& dest,
STAmount const& sendMax)
{
Json::Value jv;
jv[sfAccount.jsonName] = account.human();
jv[sfSendMax.jsonName] = sendMax.getJson(JsonOptions::none);
jv[sfDestination.jsonName] = dest.human();
jv[sfTransactionType.jsonName] = jss::CheckCreate;
jv[sfFlags.jsonName] = tfUniversal;
return jv;
}

// Cash a check requiring that a specific amount be delivered.
Json::Value
cash(jtx::Account const& dest, uint256 const& checkId, STAmount const& amount)
Expand Down
1 change: 0 additions & 1 deletion src/test/rpc/AccountOffers_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {
namespace test {
Expand Down
1 change: 0 additions & 1 deletion src/test/rpc/LedgerData_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <ripple/basics/StringUtilities.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {

Expand Down
1 change: 0 additions & 1 deletion src/test/rpc/NoRipple_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/TestHelpers.h>

namespace ripple {

Expand Down