Skip to content

Commit

Permalink
Change getFieldAmount() and setFieldAmount() to take return/take STEi…
Browse files Browse the repository at this point in the history
…therAmount. Explicitly get STAmount for all fields that don't support MPT. Add MPT to field SO template and restrict most amount fields to not support MPT.
  • Loading branch information
gregtatcam committed Jul 10, 2024
1 parent 26a2290 commit 15b1418
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 0 deletions.
6 changes: 6 additions & 0 deletions include/xrpl/protocol/MPTIssue.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ badMPT()
return mpt;
}

inline bool
isXRP(uint192 const&)
{
return false;
}

} // namespace ripple

#endif // RIPPLE_PROTOCOL_MPTISSUE_H_INCLUDED
202 changes: 202 additions & 0 deletions src/xrpld/app/tx/detail/Clawback.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#define RIPPLE_TX_CLAWBACK_H_INCLUDED

#include <xrpld/app/tx/detail/Transactor.h>
#include <xrpld/ledger/View.h>
#include <xrpl/protocol/TxFlags.h>

namespace ripple {

Expand All @@ -36,13 +38,213 @@ class Clawback : public Transactor
static NotTEC
preflight(PreflightContext const& ctx);

template <ValidAmountType T>
static NotTEC
preflightHelper(PreflightContext const& ctx);

static TER
preclaim(PreclaimContext const& ctx);

template <ValidAmountType T>
static TER
preclaimHelper(PreclaimContext const& ctx);

TER
doApply() override;

template <ValidAmountType T>
TER
doApplyHelper();
};

template <ValidAmountType T>
NotTEC
Clawback::preflightHelper(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureClawback))
return temDISABLED;

auto const mptHolder = ctx.tx[~sfMPTokenHolder];
auto const clawAmount = get<T>(ctx.tx.getFieldAmount(sfAmount));
bool constexpr isMPT = std::is_same_v<T, STMPTAmount>;
if ((mptHolder || isMPT) && !ctx.rules.enabled(featureMPTokensV1))
return temDISABLED;

if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;

if (!mptHolder && isMPT)
return temMALFORMED;

if (mptHolder && !isMPT)
return temMALFORMED;

if (ctx.tx.getFlags() & tfClawbackMask)
return temINVALID_FLAG;

AccountID const issuer = ctx.tx[sfAccount];

// The issuer field is used for the token holder if asset is IOU
AccountID const& holder = isMPT ? *mptHolder : clawAmount.getIssuer();

if constexpr (isMPT)
{
if (issuer == holder)
return temMALFORMED;

if (clawAmount > MPTAmount{maxMPTokenAmount} ||
clawAmount <= beast::zero)
return temBAD_AMOUNT;
}
else
{
if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::zero)
return temBAD_AMOUNT;
}

return preflight2(ctx);
}

template <ValidAmountType T>
TER
Clawback::preclaimHelper(PreclaimContext const& ctx)
{
AccountID const issuer = ctx.tx[sfAccount];
auto const clawAmount = get<T>(ctx.tx.getFieldAmount(sfAmount));
bool constexpr isMPT = std::is_same_v<T, STMPTAmount>;
AccountID const& holder =
isMPT ? ctx.tx[sfMPTokenHolder] : clawAmount.getIssuer();

auto const sleIssuer = ctx.view.read(keylet::account(issuer));
auto const sleHolder = ctx.view.read(keylet::account(holder));
if (!sleIssuer || !sleHolder)
return terNO_ACCOUNT;

if (sleHolder->isFieldPresent(sfAMMID))
return tecAMM_ACCOUNT;

if constexpr (isMPT)
{
auto const& mptIssue = clawAmount.issue();
auto const issuanceKey = keylet::mptIssuance(mptIssue.mpt());
auto const sleIssuance = ctx.view.read(issuanceKey);
if (!sleIssuance)
return tecOBJECT_NOT_FOUND;

if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback))
return tecNO_PERMISSION;

if (sleIssuance->getAccountID(sfIssuer) != issuer)
return tecNO_PERMISSION;

if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
return tecOBJECT_NOT_FOUND;

if (accountHolds(
ctx.view,
holder,
mptIssue,
fhIGNORE_FREEZE,
ahIGNORE_AUTH,
ctx.j) <= beast::zero)
return tecINSUFFICIENT_FUNDS;
}
else
{
std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags);

// If AllowTrustLineClawback is not set or NoFreeze is set, return no
// permission
if (!(issuerFlagsIn & lsfAllowTrustLineClawback) ||
(issuerFlagsIn & lsfNoFreeze))
return tecNO_PERMISSION;

auto const& currency = clawAmount.getCurrency();
auto const sleRippleState =
ctx.view.read(keylet::line(holder, issuer, currency));
if (!sleRippleState)
return tecNO_LINE;

STAmount const& balance = get<STAmount>((*sleRippleState)[sfBalance]);

// If balance is positive, issuer must have higher address than holder
if (balance > beast::zero && issuer < holder)
return tecNO_PERMISSION;

// If balance is negative, issuer must have lower address than holder
if (balance < beast::zero && issuer > holder)
return tecNO_PERMISSION;

// At this point, we know that issuer and holder accounts
// are correct and a trustline exists between them.
//
// Must now explicitly check the balance to make sure
// available balance is non-zero.
//
// We can't directly check the balance of trustline because
// the available balance of a trustline is prone to new changes (eg.
// XLS-34). So we must use `accountHolds`.
if (accountHolds(
ctx.view, holder, currency, issuer, fhIGNORE_FREEZE, ctx.j) <=
beast::zero)
return tecINSUFFICIENT_FUNDS;
}

return tesSUCCESS;
}

template <ValidAmountType T>
TER
Clawback::doApplyHelper()
{
AccountID const& issuer = account_;
auto clawAmount = get<T>(ctx_.tx.getFieldAmount(sfAmount));
bool constexpr isMPT = std::is_same_v<T, STMPTAmount>;
AccountID const holder = isMPT
? ctx_.tx[sfMPTokenHolder]
: clawAmount.getIssuer(); // cannot be reference because clawAmount is
// modified below

if constexpr (isMPT)
{
// Get the spendable balance. Must use `accountHolds`.
STMPTAmount const spendableAmount = accountHolds(
view(),
holder,
clawAmount.issue(),
fhIGNORE_FREEZE,
ahIGNORE_AUTH,
j_);

return rippleCredit(
view(), holder, issuer, std::min(spendableAmount, clawAmount), j_);
}
else
{
// Replace the `issuer` field with issuer's account if asset is IOU
clawAmount.setIssuer(issuer);
if (holder == issuer)
return tecINTERNAL;

// Get the spendable balance. Must use `accountHolds`.
STAmount const spendableAmount = accountHolds(
view(),
holder,
clawAmount.getCurrency(),
clawAmount.getIssuer(),
fhIGNORE_FREEZE,
j_);

return rippleCredit(
view(),
holder,
issuer,
std::min(spendableAmount, clawAmount),
true,
j_);
}
}

} // namespace ripple

#endif

0 comments on commit 15b1418

Please sign in to comment.