rippled
Loading...
Searching...
No Matches
Clawback.cpp
1#include <xrpld/app/tx/detail/Clawback.h>
2
3#include <xrpl/ledger/View.h>
4#include <xrpl/protocol/Feature.h>
5#include <xrpl/protocol/Indexes.h>
6#include <xrpl/protocol/MPTAmount.h>
7#include <xrpl/protocol/Protocol.h>
8#include <xrpl/protocol/TxFlags.h>
9
10namespace xrpl {
11
12template <ValidIssueType T>
13static NotTEC
15
16template <>
19{
20 if (ctx.tx.isFieldPresent(sfHolder))
21 return temMALFORMED;
22
23 AccountID const issuer = ctx.tx[sfAccount];
24 STAmount const clawAmount = ctx.tx[sfAmount];
25
26 // The issuer field is used for the token holder instead
27 AccountID const& holder = clawAmount.getIssuer();
28
29 if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::zero)
30 return temBAD_AMOUNT;
31
32 return tesSUCCESS;
33}
34
35template <>
38{
39 if (!ctx.rules.enabled(featureMPTokensV1))
40 return temDISABLED;
41
42 auto const mptHolder = ctx.tx[~sfHolder];
43 auto const clawAmount = ctx.tx[sfAmount];
44
45 if (!mptHolder)
46 return temMALFORMED;
47
48 // issuer is the same as holder
49 if (ctx.tx[sfAccount] == *mptHolder)
50 return temMALFORMED;
51
52 if (clawAmount.mpt() > MPTAmount{maxMPTokenAmount} || clawAmount <= beast::zero)
53 return temBAD_AMOUNT;
54
55 return tesSUCCESS;
56}
57
63
66{
67 if (auto const ret =
68 std::visit([&]<typename T>(T const&) { return preflightHelper<T>(ctx); }, ctx.tx[sfAmount].asset().value());
69 !isTesSuccess(ret))
70 return ret;
71
72 return tesSUCCESS;
73}
74
75template <ValidIssueType T>
76static TER
78 PreclaimContext const& ctx,
79 SLE const& sleIssuer,
80 AccountID const& issuer,
81 AccountID const& holder,
82 STAmount const& clawAmount);
83
84template <>
87 PreclaimContext const& ctx,
88 SLE const& sleIssuer,
89 AccountID const& issuer,
90 AccountID const& holder,
91 STAmount const& clawAmount)
92{
93 std::uint32_t const issuerFlagsIn = sleIssuer.getFieldU32(sfFlags);
94
95 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
96 // permission
97 if (!(issuerFlagsIn & lsfAllowTrustLineClawback) || (issuerFlagsIn & lsfNoFreeze))
98 return tecNO_PERMISSION;
99
100 auto const sleRippleState = ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency()));
101 if (!sleRippleState)
102 return tecNO_LINE;
103
104 STAmount const balance = (*sleRippleState)[sfBalance];
105
106 // If balance is positive, issuer must have higher address than holder
107 if (balance > beast::zero && issuer < holder)
108 return tecNO_PERMISSION;
109
110 // If balance is negative, issuer must have lower address than holder
111 if (balance < beast::zero && issuer > holder)
112 return tecNO_PERMISSION;
113
114 // At this point, we know that issuer and holder accounts
115 // are correct and a trustline exists between them.
116 //
117 // Must now explicitly check the balance to make sure
118 // available balance is non-zero.
119 //
120 // We can't directly check the balance of trustline because
121 // the available balance of a trustline is prone to new changes (eg.
122 // XLS-34). So we must use `accountHolds`.
123 if (accountHolds(ctx.view, holder, clawAmount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j) <= beast::zero)
125
126 return tesSUCCESS;
127}
128
129template <>
132 PreclaimContext const& ctx,
133 SLE const& sleIssuer,
134 AccountID const& issuer,
135 AccountID const& holder,
136 STAmount const& clawAmount)
137{
138 auto const issuanceKey = keylet::mptIssuance(clawAmount.get<MPTIssue>().getMptID());
139 auto const sleIssuance = ctx.view.read(issuanceKey);
140 if (!sleIssuance)
141 return tecOBJECT_NOT_FOUND;
142
143 if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback))
144 return tecNO_PERMISSION;
145
146 if (sleIssuance->getAccountID(sfIssuer) != issuer)
147 return tecNO_PERMISSION;
148
149 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
150 return tecOBJECT_NOT_FOUND;
151
152 if (accountHolds(ctx.view, holder, clawAmount.get<MPTIssue>(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) <=
153 beast::zero)
155
156 return tesSUCCESS;
157}
158
159TER
161{
162 AccountID const issuer = ctx.tx[sfAccount];
163 auto const clawAmount = ctx.tx[sfAmount];
164 AccountID const holder = clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
165
166 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
167 auto const sleHolder = ctx.view.read(keylet::account(holder));
168 if (!sleIssuer || !sleHolder)
169 return terNO_ACCOUNT;
170
171 // Note the order of checks - when SAV is active, this check here will make
172 // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant.
173 if (ctx.view.rules().enabled(featureSingleAssetVault) && isPseudoAccount(sleHolder))
174 return tecPSEUDO_ACCOUNT;
175 else if (sleHolder->isFieldPresent(sfAMMID))
176 return tecAMM_ACCOUNT;
177
178 return std::visit(
179 [&]<typename T>(T const&) { return preclaimHelper<T>(ctx, *sleIssuer, issuer, holder, clawAmount); },
180 ctx.tx[sfAmount].asset().value());
181}
182
183template <ValidIssueType T>
184static TER
186
187template <>
190{
191 AccountID const issuer = ctx.tx[sfAccount];
192 STAmount clawAmount = ctx.tx[sfAmount];
193 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
194
195 // Replace the `issuer` field with issuer's account
196 clawAmount.setIssuer(issuer);
197 if (holder == issuer)
198 return tecINTERNAL; // LCOV_EXCL_LINE
199
200 // Get the spendable balance. Must use `accountHolds`.
201 STAmount const spendableAmount = accountHolds(
202 ctx.view(), holder, clawAmount.getCurrency(), clawAmount.getIssuer(), fhIGNORE_FREEZE, ctx.journal);
203
204 return rippleCredit(ctx.view(), holder, issuer, std::min(spendableAmount, clawAmount), true, ctx.journal);
205}
206
207template <>
210{
211 AccountID const issuer = ctx.tx[sfAccount];
212 auto clawAmount = ctx.tx[sfAmount];
213 AccountID const holder = ctx.tx[sfHolder];
214
215 // Get the spendable balance. Must use `accountHolds`.
216 STAmount const spendableAmount =
217 accountHolds(ctx.view(), holder, clawAmount.get<MPTIssue>(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.journal);
218
219 return rippleCredit(
220 ctx.view(),
221 holder,
222 issuer,
223 std::min(spendableAmount, clawAmount),
224 /*checkIssuer*/ false,
225 ctx.journal);
226}
227
228TER
230{
231 return std::visit([&]<typename T>(T const&) { return applyHelper<T>(ctx_); }, ctx_.tx[sfAmount].asset().value());
232}
233
234} // namespace xrpl
State information when applying a tx.
STTx const & tx
beast::Journal const journal
ApplyView & view()
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Clawback.cpp:59
TER doApply() override
Definition Clawback.cpp:229
static NotTEC preflight(PreflightContext const &ctx)
Definition Clawback.cpp:65
static TER preclaim(PreclaimContext const &ctx)
Definition Clawback.cpp:160
A currency issued by an account.
Definition Issue.h:14
AccountID const & getIssuer() const
Definition Issue.h:26
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:27
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:118
constexpr TIss const & get() const
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:555
Currency const & getCurrency() const
Definition STAmount.h:461
AccountID const & getIssuer() const
Definition STAmount.h:467
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:576
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
ApplyContext & ctx_
Definition Transactor.h:109
T min(T... args)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:462
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:214
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:474
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
@ terNO_ACCOUNT
Definition TER.h:198
constexpr std::uint32_t const tfClawbackMask
Definition TxFlags.h:222
@ fhIGNORE_FREEZE
Definition View.h:59
bool isXRP(AccountID const &c)
Definition AccountID.h:71
static TER applyHelper(ApplyContext &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
Definition View.cpp:392
static NotTEC preflightHelper(PreflightContext const &ctx)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Definition View.cpp:1020
static TER preclaimHelper(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
TERSubset< CanCvtToTER > TER
Definition TER.h:621
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:86
@ ahIGNORE_AUTH
Definition View.h:62
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Clawback.cpp:37
@ temMALFORMED
Definition TER.h:68
@ temDISABLED
Definition TER.h:95
@ temBAD_AMOUNT
Definition TER.h:70
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition Clawback.cpp:18
bool isTesSuccess(TER x) noexcept
Definition TER.h:650
@ tecPSEUDO_ACCOUNT
Definition TER.h:344
@ tecOBJECT_NOT_FOUND
Definition TER.h:308
@ tecINTERNAL
Definition TER.h:292
@ tecAMM_ACCOUNT
Definition TER.h:316
@ tecINSUFFICIENT_FUNDS
Definition TER.h:307
@ tecNO_LINE
Definition TER.h:283
@ tecNO_PERMISSION
Definition TER.h:287
TER applyHelper< MPTIssue >(ApplyContext &ctx)
Definition Clawback.cpp:209
@ lsfAllowTrustLineClawback
@ lsfNoFreeze
@ lsfMPTCanClawback
TER applyHelper< Issue >(ApplyContext &ctx)
Definition Clawback.cpp:189
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:581
TER preclaimHelper< MPTIssue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:131
@ tesSUCCESS
Definition TER.h:226
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
Definition View.cpp:3083
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:54
ReadView const & view
Definition Transactor.h:57
beast::Journal const j
Definition Transactor.h:62
State information when preflighting a tx.
Definition Transactor.h:16
T visit(T... args)