mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-23 05:02:30 +00:00
Compare commits
3 Commits
dependabot
...
tapanito/i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
311719daeb | ||
|
|
d08e7b4495 | ||
|
|
90f9a3f318 |
@@ -16,6 +16,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FIX (InvariantOverwrite, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -227,6 +227,7 @@ public:
|
||||
class NoXRPTrustLines
|
||||
{
|
||||
bool xrpTrustLine_ = false;
|
||||
bool xrpTrustLineLegacy_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
@@ -246,6 +247,7 @@ public:
|
||||
class NoDeepFreezeTrustLinesWithoutFreeze
|
||||
{
|
||||
bool deepFreezeWithoutFreeze_ = false;
|
||||
bool deepFreezeWithoutFreezeLegacy_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
@@ -281,6 +283,7 @@ public:
|
||||
class NoZeroEscrow
|
||||
{
|
||||
bool bad_ = false;
|
||||
bool badLegacy_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
@@ -306,14 +306,25 @@ NoZeroEscrow::visitEntry(
|
||||
};
|
||||
|
||||
if (before && before->getType() == ltESCROW)
|
||||
bad_ |= isBad((*before)[sfAmount]);
|
||||
{
|
||||
auto const b = isBad((*before)[sfAmount]);
|
||||
bad_ |= b;
|
||||
badLegacy_ |= b;
|
||||
}
|
||||
|
||||
if (after && after->getType() == ltESCROW)
|
||||
bad_ |= isBad((*after)[sfAmount]);
|
||||
{
|
||||
auto const b = isBad((*after)[sfAmount]);
|
||||
bad_ |= b;
|
||||
badLegacy_ |= b;
|
||||
}
|
||||
|
||||
auto checkAmount = [this](std::int64_t amount) {
|
||||
if (amount > maxMPTokenAmount || amount < 0)
|
||||
{
|
||||
bad_ = true;
|
||||
badLegacy_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
if (after && after->getType() == ltMPTOKEN_ISSUANCE)
|
||||
@@ -323,7 +334,8 @@ NoZeroEscrow::visitEntry(
|
||||
if (auto const locked = (*after)[~sfLockedAmount])
|
||||
{
|
||||
checkAmount(*locked);
|
||||
bad_ = outstanding < *locked;
|
||||
bad_ |= outstanding < *locked;
|
||||
badLegacy_ = outstanding < *locked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +358,9 @@ NoZeroEscrow::finalize(
|
||||
ReadView const& rv,
|
||||
beast::Journal const& j) const
|
||||
{
|
||||
if (bad_)
|
||||
bool const effectiveBad = rv.rules().enabled(fixInvariantOverwrite) ? bad_ : badLegacy_;
|
||||
|
||||
if (effectiveBad)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: escrow specifies invalid amount";
|
||||
return false;
|
||||
@@ -598,8 +612,10 @@ NoXRPTrustLines::visitEntry(
|
||||
// checking the issue directly here instead of
|
||||
// relying on .native() just in case native somehow
|
||||
// were systematically incorrect
|
||||
xrpTrustLine_ = after->getFieldAmount(sfLowLimit).issue() == xrpIssue() ||
|
||||
bool const isXrp = after->getFieldAmount(sfLowLimit).issue() == xrpIssue() ||
|
||||
after->getFieldAmount(sfHighLimit).issue() == xrpIssue();
|
||||
xrpTrustLine_ |= isXrp;
|
||||
xrpTrustLineLegacy_ = isXrp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,10 +624,13 @@ NoXRPTrustLines::finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
ReadView const& rv,
|
||||
beast::Journal const& j) const
|
||||
{
|
||||
if (!xrpTrustLine_)
|
||||
bool const bad =
|
||||
rv.rules().enabled(fixInvariantOverwrite) ? xrpTrustLine_ : xrpTrustLineLegacy_;
|
||||
|
||||
if (!bad)
|
||||
return true;
|
||||
|
||||
JLOG(j.fatal()) << "Invariant failed: an XRP trust line was created";
|
||||
@@ -635,7 +654,9 @@ NoDeepFreezeTrustLinesWithoutFreeze::visitEntry(
|
||||
bool const highFreeze = uFlags & lsfHighFreeze;
|
||||
bool const highDeepFreeze = uFlags & lsfHighDeepFreeze;
|
||||
|
||||
deepFreezeWithoutFreeze_ = (lowDeepFreeze && !lowFreeze) || (highDeepFreeze && !highFreeze);
|
||||
bool const bad = (lowDeepFreeze && !lowFreeze) || (highDeepFreeze && !highFreeze);
|
||||
deepFreezeWithoutFreeze_ |= bad;
|
||||
deepFreezeWithoutFreezeLegacy_ = bad;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,10 +665,13 @@ NoDeepFreezeTrustLinesWithoutFreeze::finalize(
|
||||
STTx const&,
|
||||
TER const,
|
||||
XRPAmount const,
|
||||
ReadView const&,
|
||||
ReadView const& rv,
|
||||
beast::Journal const& j) const
|
||||
{
|
||||
if (!deepFreezeWithoutFreeze_)
|
||||
bool const bad = rv.rules().enabled(fixInvariantOverwrite) ? deepFreezeWithoutFreeze_
|
||||
: deepFreezeWithoutFreezeLegacy_;
|
||||
|
||||
if (!bad)
|
||||
return true;
|
||||
|
||||
JLOG(j.fatal()) << "Invariant failed: a trust line with deep freeze flag "
|
||||
|
||||
@@ -41,7 +41,7 @@ class Invariants_test : public beast::unit_test::suite
|
||||
defaultAmendments()
|
||||
{
|
||||
return xrpl::test::jtx::testable_amendments() | featureInvariantsV1_1 |
|
||||
featureSingleAssetVault;
|
||||
featureSingleAssetVault | fixInvariantOverwrite;
|
||||
}
|
||||
|
||||
/** Run a specific test case to put the ledger into a state that will be
|
||||
@@ -498,6 +498,26 @@ class Invariants_test : public beast::unit_test::suite
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Regression: two trust lines where the first is bad (XRP) and the
|
||||
// second is valid. Plain assignment would overwrite the earlier true.
|
||||
doInvariantCheck(
|
||||
{{"an XRP trust line was created"}},
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
// First: bad XRP trust line
|
||||
auto const sleBad =
|
||||
std::make_shared<SLE>(keylet::line(A1, A2, xrpIssue().currency));
|
||||
ac.view().insert(sleBad);
|
||||
|
||||
// Second: valid USD trust line — must NOT clear the flag
|
||||
Account const A3{"A3"};
|
||||
auto const sleGood =
|
||||
std::make_shared<SLE>(keylet::line(A1, A3, A1["USD"].currency));
|
||||
sleGood->setFieldAmount(sfLowLimit, A1["USD"](0));
|
||||
sleGood->setFieldAmount(sfHighLimit, A1["USD"](0));
|
||||
ac.view().insert(sleGood);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -576,6 +596,31 @@ class Invariants_test : public beast::unit_test::suite
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Regression: two trust lines where the first has deep freeze without
|
||||
// freeze, and the second is valid. Plain assignment would overwrite
|
||||
// the earlier true.
|
||||
doInvariantCheck(
|
||||
{{"a trust line with deep freeze flag without normal freeze was "
|
||||
"created"}},
|
||||
[](Account const& A1, Account const& A2, ApplyContext& ac) {
|
||||
// First: bad — lowDeepFreeze without lowFreeze
|
||||
auto const sleBad = std::make_shared<SLE>(keylet::line(A1, A2, A1["USD"].currency));
|
||||
sleBad->setFieldAmount(sfLowLimit, A1["USD"](0));
|
||||
sleBad->setFieldAmount(sfHighLimit, A1["USD"](0));
|
||||
sleBad->setFieldU32(sfFlags, lsfLowDeepFreeze);
|
||||
ac.view().insert(sleBad);
|
||||
|
||||
// Second: valid — no deep freeze flags at all
|
||||
Account const A3{"A3"};
|
||||
auto const sleGood =
|
||||
std::make_shared<SLE>(keylet::line(A1, A3, A1["EUR"].currency));
|
||||
sleGood->setFieldAmount(sfLowLimit, A1["EUR"](0));
|
||||
sleGood->setFieldAmount(sfHighLimit, A1["EUR"](0));
|
||||
sleGood->setFieldU32(sfFlags, 0u);
|
||||
ac.view().insert(sleGood);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -931,6 +976,25 @@ class Invariants_test : public beast::unit_test::suite
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT OutstandingAmount exceeds max, but locked <= outstanding
|
||||
// (regression: plain assignment would overwrite earlier bad_ = true)
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
[](Account const& A1, Account const&, ApplyContext& ac) {
|
||||
auto const sle = ac.view().peek(keylet::account(A1.id()));
|
||||
if (!sle)
|
||||
return false;
|
||||
|
||||
MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
|
||||
auto sleNew = std::make_shared<SLE>(keylet::mptIssuance(mpt.getMptID()));
|
||||
// outstanding exceeds maxMPTokenAmount -> checkAmount sets bad_
|
||||
sleNew->setFieldU64(sfOutstandingAmount, maxMPTokenAmount + 1);
|
||||
// locked is valid and <= outstanding -> must NOT clear bad_
|
||||
sleNew->setFieldU64(sfLockedAmount, 10);
|
||||
ac.view().insert(sleNew);
|
||||
return true;
|
||||
});
|
||||
|
||||
// MPT MPTAmount < 0
|
||||
doInvariantCheck(
|
||||
{{"escrow specifies invalid amount"}},
|
||||
|
||||
Reference in New Issue
Block a user