mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Add a LoanBroker invariant to compare CoverAvailable to balance
- Ensures that LoanBroker.CoverAvailable is never less than the pseudo-account balance for the Vault asset.
This commit is contained in:
@@ -564,7 +564,7 @@ STAmount::clear()
|
|||||||
{
|
{
|
||||||
// The -100 is used to allow 0 to sort less than a small positive values
|
// The -100 is used to allow 0 to sort less than a small positive values
|
||||||
// which have a negative exponent.
|
// which have a negative exponent.
|
||||||
mOffset = native() ? 0 : -100;
|
mOffset = integral() ? 0 : -100;
|
||||||
mValue = 0;
|
mValue = 0;
|
||||||
mIsNegative = false;
|
mIsNegative = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ STAmount::isDefault() const
|
|||||||
void
|
void
|
||||||
STAmount::canonicalize()
|
STAmount::canonicalize()
|
||||||
{
|
{
|
||||||
if (native() || mAsset.holds<MPTIssue>())
|
if (integral())
|
||||||
{
|
{
|
||||||
// native and MPT currency amounts should always have an offset of zero
|
// native and MPT currency amounts should always have an offset of zero
|
||||||
// log(2^64,10) ~ 19.2
|
// log(2^64,10) ~ 19.2
|
||||||
@@ -859,8 +859,10 @@ STAmount::canonicalize()
|
|||||||
};
|
};
|
||||||
if (native())
|
if (native())
|
||||||
set(XRPAmount{num});
|
set(XRPAmount{num});
|
||||||
else
|
else if (mAsset.holds<MPTIssue>())
|
||||||
set(MPTAmount{num});
|
set(MPTAmount{num});
|
||||||
|
else
|
||||||
|
Throw<std::runtime_error>("Unknown integral asset type");
|
||||||
mOffset = 0;
|
mOffset = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -2342,9 +2342,30 @@ ValidLoanBroker::visitEntry(
|
|||||||
std::shared_ptr<SLE const> const& before,
|
std::shared_ptr<SLE const> const& before,
|
||||||
std::shared_ptr<SLE const> const& after)
|
std::shared_ptr<SLE const> const& after)
|
||||||
{
|
{
|
||||||
if (after && after->getType() == ltLOAN_BROKER)
|
if (after)
|
||||||
{
|
{
|
||||||
brokers_.emplace_back(before, after);
|
if (after->getType() == ltLOAN_BROKER)
|
||||||
|
{
|
||||||
|
auto& broker = brokers_[after->key()];
|
||||||
|
broker.brokerBefore = before;
|
||||||
|
broker.brokerAfter = after;
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
after->getType() == ltACCOUNT_ROOT &&
|
||||||
|
after->isFieldPresent(sfLoanBrokerID))
|
||||||
|
{
|
||||||
|
auto const& loanBrokerID = after->at(sfLoanBrokerID);
|
||||||
|
// create an entry if one doesn't already exist
|
||||||
|
auto& broker = brokers_[loanBrokerID];
|
||||||
|
}
|
||||||
|
else if (after->getType() == ltRIPPLE_STATE)
|
||||||
|
{
|
||||||
|
lines_.emplace_back(after);
|
||||||
|
}
|
||||||
|
else if (after->getType() == ltMPTOKEN)
|
||||||
|
{
|
||||||
|
mpts_.emplace_back(after);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2403,8 +2424,51 @@ ValidLoanBroker::finalize(
|
|||||||
// Loan Brokers will not exist on ledger if the Lending Protocol amendment
|
// Loan Brokers will not exist on ledger if the Lending Protocol amendment
|
||||||
// is not enabled, so there's no need to check it.
|
// is not enabled, so there's no need to check it.
|
||||||
|
|
||||||
for (auto const& [before, after] : brokers_)
|
for (auto const& line : lines_)
|
||||||
{
|
{
|
||||||
|
for (auto const& field : {&sfLowLimit, &sfHighLimit})
|
||||||
|
{
|
||||||
|
auto const account =
|
||||||
|
view.read(keylet::account(line->at(*field).getIssuer()));
|
||||||
|
// This Invariant doesn't know about the rules for Trust Lines, so
|
||||||
|
// if the account is missing, don't treat it as an error. This
|
||||||
|
// loop is only concerned with finding Broker pseudo-accounts
|
||||||
|
if (account && account->isFieldPresent(sfLoanBrokerID))
|
||||||
|
{
|
||||||
|
auto const& loanBrokerID = account->at(sfLoanBrokerID);
|
||||||
|
// create an entry if one doesn't already exist
|
||||||
|
auto& broker = brokers_[loanBrokerID];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto const& mpt : mpts_)
|
||||||
|
{
|
||||||
|
auto const account = view.read(keylet::account(mpt->at(sfAccount)));
|
||||||
|
// This Invariant doesn't know about the rules for MPTokens, so
|
||||||
|
// if the account is missing, don't treat is as an error. This
|
||||||
|
// loop is only concerned with finding Broker pseudo-accounts
|
||||||
|
if (account && account->isFieldPresent(sfLoanBrokerID))
|
||||||
|
{
|
||||||
|
auto const& loanBrokerID = account->at(sfLoanBrokerID);
|
||||||
|
// create an entry if one doesn't already exist
|
||||||
|
auto& broker = brokers_[loanBrokerID];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& [brokerID, broker] : brokers_)
|
||||||
|
{
|
||||||
|
auto const& after = broker.brokerAfter
|
||||||
|
? broker.brokerAfter
|
||||||
|
: view.read(keylet::loanbroker(brokerID));
|
||||||
|
|
||||||
|
if (!after)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: Loan Broker missing";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const& before = broker.brokerBefore;
|
||||||
|
|
||||||
// https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants
|
// https://github.com/Tapanito/XRPL-Standards/blob/xls-66-lending-protocol/XLS-0066d-lending-protocol/README.md#3123-invariants
|
||||||
// If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most
|
// If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most
|
||||||
// one node (the root), which will only hold entries for `RippleState`
|
// one node (the root), which will only hold entries for `RippleState`
|
||||||
@@ -2438,6 +2502,26 @@ ValidLoanBroker::finalize(
|
|||||||
<< "Invariant failed: Loan Broker cover available is negative";
|
<< "Invariant failed: Loan Broker cover available is negative";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
auto const vault = view.read(keylet::vault(after->at(sfVaultID)));
|
||||||
|
if (!vault)
|
||||||
|
{
|
||||||
|
JLOG(j.fatal())
|
||||||
|
<< "Invariant failed: Loan Broker vault ID is invalid";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto const& vaultAsset = vault->at(sfAsset);
|
||||||
|
if (after->at(sfCoverAvailable) < accountHolds(
|
||||||
|
view,
|
||||||
|
after->at(sfAccount),
|
||||||
|
vaultAsset,
|
||||||
|
FreezeHandling::fhIGNORE_FREEZE,
|
||||||
|
AuthHandling::ahIGNORE_AUTH,
|
||||||
|
j))
|
||||||
|
{
|
||||||
|
JLOG(j.fatal()) << "Invariant failed: Loan Broker cover available "
|
||||||
|
"is less than pseudo-account asset balance";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -763,9 +763,25 @@ public:
|
|||||||
*/
|
*/
|
||||||
class ValidLoanBroker
|
class ValidLoanBroker
|
||||||
{
|
{
|
||||||
// Pair is <before, after>. After is used for most of the checks, except
|
// Not all of these elements will necessarily be populated. Remaining items
|
||||||
|
// will be looked up as needed.
|
||||||
|
struct BrokerInfo
|
||||||
|
{
|
||||||
|
SLE::const_pointer brokerBefore = nullptr;
|
||||||
|
// After is used for most of the checks, except
|
||||||
// those that check changed values.
|
// those that check changed values.
|
||||||
std::vector<std::pair<SLE::const_pointer, SLE::const_pointer>> brokers_;
|
SLE::const_pointer brokerAfter = nullptr;
|
||||||
|
};
|
||||||
|
// Collect all the LoanBrokers found directly or indirectly through
|
||||||
|
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
|
||||||
|
// LoanBroker object if brokerBefore and brokerAfter are nullptr
|
||||||
|
std::map<uint256, BrokerInfo> brokers_;
|
||||||
|
// Collect all the modified trust lines. Their high and low accounts will be
|
||||||
|
// loaded to look for LoanBroker pseudo-accounts.
|
||||||
|
std::vector<SLE::const_pointer> lines_;
|
||||||
|
// Collect all the modified MPTokens. Their accounts will be loaded to look
|
||||||
|
// for LoanBroker pseudo-accounts.
|
||||||
|
std::vector<SLE::const_pointer> mpts_;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
goodZeroDirectory(
|
goodZeroDirectory(
|
||||||
|
|||||||
Reference in New Issue
Block a user