Improve / add freeze checking helper functions

- Add an override of isDeepFrozen that can take an Asset, and thus an
  MPTIssue. The MPT version just calls isFrozen, since they're
  equivalent for MPTs.
- Add wrappers checkFrozen and checkDeepFrozen that return the
  appropriate TER code, so the Asset type doesn't have to be checked
  at every #*%@ing caller.
- Convert the Loan* transactors to use these functions.
This commit is contained in:
Ed Hennis
2025-07-15 15:16:02 -04:00
parent 3ef72d28bd
commit 5199c5e073
6 changed files with 136 additions and 50 deletions

View File

@@ -89,15 +89,11 @@ LoanBrokerCoverDeposit::preclaim(PreclaimContext const& ctx)
auto const pseudoAccountID = sleBroker->at(sfAccount);
// Cannot transfer a frozen Asset
if (isFrozen(ctx.view, account, vaultAsset))
return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
if (vaultAsset.holds<Issue>())
{
auto const issue = vaultAsset.get<Issue>();
if (isDeepFrozen(
ctx.view, pseudoAccountID, issue.currency, issue.account))
return tecFROZEN;
}
if (auto const ret = checkFrozen(ctx.view, account, vaultAsset))
return ret;
// Pseudo-account cannot receive if asset is deep frozen
if (auto const ret = checkDeepFrozen(ctx.view, pseudoAccountID, vaultAsset))
return ret;
if (accountHolds(
ctx.view,

View File

@@ -90,18 +90,11 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
auto const pseudoAccountID = sleBroker->at(sfAccount);
// Cannot transfer a frozen Asset
/*
if (isFrozen(ctx.view, account, vaultAsset))
return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
*/
if (isFrozen(ctx.view, pseudoAccountID, vaultAsset))
return vaultAsset.holds<Issue>() ? tecFROZEN : tecLOCKED;
if (vaultAsset.holds<Issue>())
{
auto const issue = vaultAsset.get<Issue>();
if (isDeepFrozen(ctx.view, account, issue.currency, issue.account))
return tecFROZEN;
}
if (auto const ret = checkFrozen(ctx.view, pseudoAccountID, vaultAsset))
return ret;
// Account cannot receive if asset is deep frozen
if (auto const ret = checkDeepFrozen(ctx.view, account, vaultAsset))
return ret;
auto const coverAvail = sleBroker->at(sfCoverAvailable);
// Cover Rate is in 1/10 bips units

View File

@@ -131,19 +131,16 @@ LoanDraw::preclaim(PreclaimContext const& ctx)
return tecINSUFFICIENT_FUNDS;
}
if (isFrozen(ctx.view, brokerPseudoAccount, asset))
if (auto const ret = checkFrozen(ctx.view, brokerPseudoAccount, asset))
{
JLOG(ctx.j.warn()) << "Loan Broker pseudo-account is frozen.";
return asset.holds<Issue>() ? tecFROZEN : tecLOCKED;
return ret;
}
if (asset.holds<Issue>())
if (auto const ret = checkDeepFrozen(ctx.view, account, asset))
{
auto const issue = asset.get<Issue>();
if (isDeepFrozen(ctx.view, account, issue.currency, issue.account))
{
JLOG(ctx.j.warn()) << "Borrower account is frozen.";
return tecFROZEN;
}
JLOG(ctx.j.warn())
<< "Borrower account cannot receive funds (deep frozen).";
return ret;
}
if (hasExpired(ctx.view, loanSle->at(sfNextPaymentDueDate)))

View File

@@ -131,19 +131,16 @@ LoanPay::preclaim(PreclaimContext const& ctx)
return tecWRONG_ASSET;
}
if (isFrozen(ctx.view, brokerPseudoAccount, asset))
if (auto const ret = checkFrozen(ctx.view, account, asset))
{
JLOG(ctx.j.warn()) << "Loan Broker pseudo-account is frozen.";
return asset.holds<Issue>() ? tecFROZEN : tecLOCKED;
JLOG(ctx.j.warn()) << "Borrower account is frozen.";
return ret;
}
if (asset.holds<Issue>())
if (auto const ret = checkDeepFrozen(ctx.view, brokerPseudoAccount, asset))
{
auto const issue = asset.get<Issue>();
if (isDeepFrozen(ctx.view, account, issue.currency, issue.account))
{
JLOG(ctx.j.warn()) << "Borrower account is frozen.";
return tecFROZEN;
}
JLOG(ctx.j.warn()) << "Loan Broker pseudo-account can not receive "
"funds (deep frozen).";
return ret;
}
return tesSUCCESS;

View File

@@ -204,19 +204,25 @@ LoanSet::preclaim(PreclaimContext const& ctx)
if (auto const ter = canAddHolding(ctx.view, asset))
return ter;
if (isFrozen(ctx.view, brokerOwner, asset) ||
isFrozen(ctx.view, brokerPseudo, asset))
if (auto const ret = checkFrozen(ctx.view, brokerOwner, asset))
{
JLOG(ctx.j.warn()) << "One of the affected accounts is frozen.";
return asset.holds<Issue>() ? tecFROZEN : tecLOCKED;
JLOG(ctx.j.warn()) << "Broker owner account is frozen.";
return ret;
}
if (asset.holds<Issue>())
if (auto const ret = checkFrozen(ctx.view, brokerPseudo, asset))
{
auto const issue = asset.get<Issue>();
if (isDeepFrozen(ctx.view, borrower, issue.currency, issue.account))
return tecFROZEN;
if (isDeepFrozen(ctx.view, brokerPseudo, issue.currency, issue.account))
return tecFROZEN;
JLOG(ctx.j.warn()) << "Broker pseudo-account account is frozen.";
return ret;
}
if (auto const ret = checkDeepFrozen(ctx.view, borrower, asset))
{
JLOG(ctx.j.warn()) << "Borrower account is deep frozen.";
return ret;
}
if (auto const ret = checkDeepFrozen(ctx.view, brokerPseudo, asset))
{
JLOG(ctx.j.warn()) << "Broker pseudo-account account is deep frozen.";
return ret;
}
auto const principalRequested = tx[sfPrincipalRequested];

View File

@@ -175,6 +175,29 @@ isFrozen(
asset.value());
}
[[nodiscard]] inline TER
checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
{
return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue)
{
return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
{
return std::visit(
[&](auto const& issue) { return checkFrozen(view, account, issue); },
asset.value());
}
[[nodiscard]] bool
isAnyFrozen(
ReadView const& view,
@@ -220,6 +243,80 @@ isDeepFrozen(
Currency const& currency,
AccountID const& issuer);
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Issue const& issue,
int = 0 /*ignored*/)
{
return isDeepFrozen(view, account, issue.currency, issue.account);
}
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue,
int depth = 0)
{
// Unlike IOUs, frozen / locked MPTs are not allowed to send or receive
// funds, so checking "deep frozen" is the same as checking "frozen".
return isFrozen(view, account, mptIssue, depth);
}
/**
* isFrozen check is recursive for MPT shares in a vault, descending to
* assets in the vault, up to maxAssetCheckDepth recursion depth. This is
* purely defensive, as we currently do not allow such vaults to be created.
*/
[[nodiscard]] inline bool
isDeepFrozen(
ReadView const& view,
AccountID const& account,
Asset const& asset,
int depth = 0)
{
return std::visit(
[&](auto const& issue) {
return isDeepFrozen(view, account, issue, depth);
},
asset.value());
}
[[nodiscard]] inline TER
checkDeepFrozen(
ReadView const& view,
AccountID const& account,
Issue const& issue)
{
return isDeepFrozen(view, account, issue) ? (TER)tecFROZEN
: (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkDeepFrozen(
ReadView const& view,
AccountID const& account,
MPTIssue const& mptIssue)
{
return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED
: (TER)tesSUCCESS;
}
[[nodiscard]] inline TER
checkDeepFrozen(
ReadView const& view,
AccountID const& account,
Asset const& asset)
{
return std::visit(
[&](auto const& issue) {
return checkDeepFrozen(view, account, issue);
},
asset.value());
}
[[nodiscard]] bool
isLPTokenFrozen(
ReadView const& view,