mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-04 17:27:00 +00:00
Add MPT to ValidAMM invariant. Add ValidAMM invariant unit-tests.
This commit is contained in:
@@ -27,8 +27,9 @@ ValidAMM::visitEntry(
|
||||
}
|
||||
// AMM pool changed
|
||||
else if (
|
||||
(type == ltRIPPLE_STATE && ((after->getFlags() & lsfAMMNode) != 0u)) ||
|
||||
(type == ltACCOUNT_ROOT && after->isFieldPresent(sfAMMID)))
|
||||
(type == ltRIPPLE_STATE && after->isFlag(lsfAMMNode)) ||
|
||||
(type == ltACCOUNT_ROOT && after->isFieldPresent(sfAMMID)) ||
|
||||
(type == ltMPTOKEN && after->isFlag(lsfMPTAMM)))
|
||||
{
|
||||
ammPoolChanged_ = true;
|
||||
}
|
||||
@@ -68,9 +69,9 @@ ValidAMM::finalizeVote(bool enforce, beast::Journal const& j) const
|
||||
{
|
||||
// LPTokens and the pool can not change on vote
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMVote invariant failed: " << lptAMMBalanceBefore_.value_or(STAmount{})
|
||||
<< " " << lptAMMBalanceAfter_.value_or(STAmount{}) << " "
|
||||
<< ammPoolChanged_;
|
||||
JLOG(j.error()) << "Invariant failed: AMMVote failed, "
|
||||
<< lptAMMBalanceBefore_.value_or(STAmount{}) << " "
|
||||
<< lptAMMBalanceAfter_.value_or(STAmount{}) << " " << ammPoolChanged_;
|
||||
if (enforce)
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
@@ -86,7 +87,7 @@ ValidAMM::finalizeBid(bool enforce, beast::Journal const& j) const
|
||||
{
|
||||
// The pool can not change on bid
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMBid invariant failed: pool changed";
|
||||
JLOG(j.error()) << "Invariant failed: AMMBid failed, pool changed";
|
||||
if (enforce)
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
@@ -97,7 +98,7 @@ ValidAMM::finalizeBid(bool enforce, beast::Journal const& j) const
|
||||
(*lptAMMBalanceAfter_ > *lptAMMBalanceBefore_ || *lptAMMBalanceAfter_ <= beast::zero))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMBid invariant failed: " << *lptAMMBalanceBefore_ << " "
|
||||
JLOG(j.error()) << "Invariant failed: AMMBid failed, " << *lptAMMBalanceBefore_ << " "
|
||||
<< *lptAMMBalanceAfter_;
|
||||
if (enforce)
|
||||
return false;
|
||||
@@ -117,7 +118,7 @@ ValidAMM::finalizeCreate(
|
||||
if (!ammAccount_)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMCreate invariant failed: AMM object is not created";
|
||||
JLOG(j.error()) << "Invariant failed: AMMCreate failed, AMM object is not created";
|
||||
if (enforce)
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
@@ -138,8 +139,8 @@ ValidAMM::finalizeCreate(
|
||||
if (!validBalances(amount, amount2, *lptAMMBalanceAfter_, ZeroAllowed::No) ||
|
||||
ammLPTokens(amount, amount2, lptAMMBalanceAfter_->get<Issue>()) != *lptAMMBalanceAfter_)
|
||||
{
|
||||
JLOG(j.error()) << "AMMCreate invariant failed: " << amount << " " << amount2 << " "
|
||||
<< *lptAMMBalanceAfter_;
|
||||
JLOG(j.error()) << "Invariant failed: AMMCreate failed, " << amount << " " << amount2
|
||||
<< " " << *lptAMMBalanceAfter_;
|
||||
if (enforce)
|
||||
return false;
|
||||
}
|
||||
@@ -156,7 +157,7 @@ ValidAMM::finalizeDelete(bool enforce, TER res, beast::Journal const& j) const
|
||||
// LCOV_EXCL_START
|
||||
std::string const msg = (isTesSuccess(res)) ? "AMM object is not deleted on tesSUCCESS"
|
||||
: "AMM object is changed on tecINCOMPLETE";
|
||||
JLOG(j.error()) << "AMMDelete invariant failed: " << msg;
|
||||
JLOG(j.error()) << "Invariant failed: AMMDelete failed, " << msg;
|
||||
if (enforce)
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
@@ -171,7 +172,7 @@ ValidAMM::finalizeDEX(bool enforce, beast::Journal const& j) const
|
||||
if (ammAccount_)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMM swap invariant failed: AMM object changed";
|
||||
JLOG(j.error()) << "Invariant failed: AMM swap failed, AMM object changed";
|
||||
if (enforce)
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
@@ -204,10 +205,10 @@ ValidAMM::generalInvariant(
|
||||
};
|
||||
if (!nonNegativeBalances || (!strongInvariantCheck && !weakInvariantCheck()))
|
||||
{
|
||||
JLOG(j.error()) << "AMM " << tx.getTxnType()
|
||||
<< " invariant failed: " << tx.getHash(HashPrefix::transactionID) << " "
|
||||
<< ammPoolChanged_ << " " << amount << " " << amount2 << " "
|
||||
<< poolProductMean << " " << lptAMMBalanceAfter_->getText() << " "
|
||||
JLOG(j.error()) << "Invariant failed: AMM " << tx.getTxnType() << " failed, "
|
||||
<< tx.getHash(HashPrefix::transactionID) << " " << ammPoolChanged_ << " "
|
||||
<< amount << " " << amount2 << " " << poolProductMean << " "
|
||||
<< lptAMMBalanceAfter_->getText() << " "
|
||||
<< ((*lptAMMBalanceAfter_ == beast::zero)
|
||||
? Number{1}
|
||||
: ((*lptAMMBalanceAfter_ - poolProductMean) / poolProductMean));
|
||||
@@ -227,7 +228,7 @@ ValidAMM::finalizeDeposit(
|
||||
if (!ammAccount_)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMDeposit invariant failed: AMM object is deleted";
|
||||
JLOG(j.error()) << "Invariant failed: AMMDeposit failed, AMM object is deleted";
|
||||
if (enforce)
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
@@ -139,11 +139,7 @@ class Invariants_test : public beast::unit_test::suite
|
||||
for (TER const& terExpect : ters)
|
||||
{
|
||||
terActual = ac.checkInvariants(terActual, fee);
|
||||
if (!BEAST_EXPECTS(terExpect == terActual, std::to_string(TERtoInt(terActual))))
|
||||
{
|
||||
std::cout << terExpect << " " << terActual << " "
|
||||
<< std::to_string(TERtoInt(terActual)) << std::endl;
|
||||
}
|
||||
BEAST_EXPECTS(terExpect == terActual, std::to_string(TERtoInt(terActual)));
|
||||
auto const messages = sink.messages().str();
|
||||
|
||||
if (!isTesSuccess(terActual))
|
||||
@@ -157,10 +153,7 @@ class Invariants_test : public beast::unit_test::suite
|
||||
// std::cerr << messages << '\n';
|
||||
for (auto const& m : expect_logs)
|
||||
{
|
||||
if (!BEAST_EXPECTS(messages.find(m) != std::string::npos, m))
|
||||
{
|
||||
std::cout << messages << " " << m << std::endl;
|
||||
}
|
||||
BEAST_EXPECTS(messages.find(m) != std::string::npos, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4084,6 +4077,112 @@ class Invariants_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testAMM()
|
||||
{
|
||||
testcase << "AMM";
|
||||
using namespace jtx;
|
||||
|
||||
MPTID mptID{};
|
||||
uint256 ammID{};
|
||||
AccountID ammAccountID{};
|
||||
Account const gw{"gw"};
|
||||
Issue lptIssue{};
|
||||
PrettyAsset poolAsset{xrpIssue()};
|
||||
|
||||
auto deleteAMMAccount = [&](ApplyContext& ac, bool) {
|
||||
auto sle = ac.view().peek(keylet::account(ammAccountID));
|
||||
if (!sle)
|
||||
return false;
|
||||
ac.view().erase(sle);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto updateLPTokensBalance = [&](ApplyContext& ac, std::int64_t amount) {
|
||||
auto sle = ac.view().peek(keylet::amm(ammID));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setFieldAmount(sfLPTokenBalance, STAmount{lptIssue, amount});
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
};
|
||||
auto updateLPTokensBadAmount = [&](ApplyContext& ac, bool) {
|
||||
return updateLPTokensBalance(ac, -1);
|
||||
};
|
||||
auto updateLPTokensBadBalance = [&](ApplyContext& ac, bool) {
|
||||
return updateLPTokensBalance(ac, 200'000'000);
|
||||
};
|
||||
auto updateAMM = [&](ApplyContext& ac, bool) { return updateLPTokensBalance(ac, 10); };
|
||||
|
||||
auto updateAMMPool = [&](ApplyContext& ac, bool isMPT) {
|
||||
if (isMPT)
|
||||
{
|
||||
auto sle = ac.view().peek(keylet::mptoken(mptID, ammAccountID));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setFieldU64(sfMPTAmount, 1);
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
}
|
||||
auto sle = ac.view().peek(keylet::account(ammAccountID));
|
||||
if (!sle)
|
||||
return false;
|
||||
sle->setFieldAmount(sfBalance, XRP(1));
|
||||
ac.view().update(sle);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto test =
|
||||
[&](auto const txType, auto&& update, bool isMPT, TER error = tecINVARIANT_FAILED) {
|
||||
doInvariantCheck(
|
||||
{{"AMM"}},
|
||||
[&](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
return update(ac, isMPT);
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{txType, [&](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, error},
|
||||
[&](Account const& A1, Account const& A2, Env& env) {
|
||||
env.fund(XRP(1'000), gw);
|
||||
poolAsset = [&]() -> PrettyAsset {
|
||||
if (isMPT)
|
||||
{
|
||||
MPT const mpt = MPTTester({.env = env, .issuer = gw});
|
||||
mptID = mpt.issuanceID;
|
||||
return mpt;
|
||||
}
|
||||
return gw["USD"];
|
||||
}();
|
||||
AMM const amm(env, gw, XRP(100), poolAsset(100));
|
||||
ammAccountID = amm.ammAccount();
|
||||
ammID = amm.ammID();
|
||||
lptIssue = amm.lptIssue();
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
for (bool const isMPT : {false, true})
|
||||
{
|
||||
auto const error = isMPT ? TER(tecINVARIANT_FAILED) : TER(tefINVARIANT_FAILED);
|
||||
for (auto txType : {ttAMM_CREATE, ttAMM_DEPOSIT, ttAMM_CLAWBACK, ttAMM_WITHDRAW})
|
||||
{
|
||||
test(txType, deleteAMMAccount, isMPT, tefINVARIANT_FAILED);
|
||||
test(txType, updateLPTokensBadAmount, isMPT);
|
||||
test(txType, updateLPTokensBadBalance, isMPT);
|
||||
}
|
||||
for (auto txType : {ttAMM_BID, ttAMM_VOTE})
|
||||
{
|
||||
test(txType, updateAMMPool, isMPT, error);
|
||||
test(txType, updateLPTokensBadAmount, isMPT);
|
||||
test(txType, updateLPTokensBadBalance, isMPT);
|
||||
}
|
||||
for (auto txType : {ttAMM_DELETE, ttCHECK_CASH, ttOFFER_CREATE, ttPAYMENT})
|
||||
{
|
||||
test(txType, updateAMM, isMPT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -4110,6 +4209,7 @@ public:
|
||||
testValidLoanBroker();
|
||||
testVault();
|
||||
testMPT();
|
||||
testAMM();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user