xrpld
Loading...
Searching...
No Matches
Clawback.cpp
1#include <xrpl/tx/transactors/token/Clawback.h>
2
3#include <xrpl/beast/utility/Zero.h>
4#include <xrpl/core/ServiceRegistry.h>
5#include <xrpl/ledger/helpers/AccountRootHelpers.h>
6#include <xrpl/ledger/helpers/TokenHelpers.h>
7#include <xrpl/protocol/AccountID.h>
8#include <xrpl/protocol/Concepts.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/Issue.h>
12#include <xrpl/protocol/LedgerFormats.h>
13#include <xrpl/protocol/MPTAmount.h>
14#include <xrpl/protocol/MPTIssue.h>
15#include <xrpl/protocol/Protocol.h>
16#include <xrpl/protocol/SField.h>
17#include <xrpl/protocol/STAmount.h>
18#include <xrpl/protocol/STLedgerEntry.h>
19#include <xrpl/protocol/STTx.h>
20#include <xrpl/protocol/TER.h>
21#include <xrpl/protocol/XRPAmount.h>
22#include <xrpl/tx/ApplyContext.h>
23#include <xrpl/tx/Transactor.h>
24
25#include <algorithm>
26#include <variant>
27
28namespace xrpl {
29
30template <ValidIssueType T>
31static NotTEC
33
34template <>
37{
38 if (ctx.tx.isFieldPresent(sfHolder))
39 return temMALFORMED;
40
41 AccountID const issuer = ctx.tx[sfAccount];
42 STAmount const clawAmount = ctx.tx[sfAmount];
43
44 // The issuer field is used for the token holder instead
45 AccountID const& holder = clawAmount.getIssuer();
46
47 if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::kZero)
48 return temBAD_AMOUNT;
49
50 return tesSUCCESS;
51}
52
53template <>
56{
57 if (!ctx.rules.enabled(featureMPTokensV1))
58 return temDISABLED;
59
60 auto const mptHolder = ctx.tx[~sfHolder];
61 auto const clawAmount = ctx.tx[sfAmount];
62
63 if (!mptHolder)
64 return temMALFORMED;
65
66 // issuer is the same as holder
67 if (ctx.tx[sfAccount] == *mptHolder)
68 return temMALFORMED;
69
70 if (clawAmount.mpt() > MPTAmount{kMaxMpTokenAmount} || clawAmount <= beast::kZero)
71 return temBAD_AMOUNT;
72
73 return tesSUCCESS;
74}
75
78{
79 if (auto const ret = std::visit(
80 [&]<typename T>(T const&) { return preflightHelper<T>(ctx); },
81 ctx.tx[sfAmount].asset().value());
82 !isTesSuccess(ret))
83 return ret;
84
85 return tesSUCCESS;
86}
87
88template <ValidIssueType T>
89static TER
91 PreclaimContext const& ctx,
92 SLE const& sleIssuer,
93 AccountID const& issuer,
94 AccountID const& holder,
95 STAmount const& clawAmount);
96
97template <>
100 PreclaimContext const& ctx,
101 SLE const& sleIssuer,
102 AccountID const& issuer,
103 AccountID const& holder,
104 STAmount const& clawAmount)
105{
106 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
107 // permission
108 if (!sleIssuer.isFlag(lsfAllowTrustLineClawback) || sleIssuer.isFlag(lsfNoFreeze))
109 return tecNO_PERMISSION;
110
111 auto const sleRippleState =
112 ctx.view.read(keylet::trustLine(holder, issuer, clawAmount.get<Issue>().currency));
113 if (!sleRippleState)
114 return tecNO_LINE;
115
116 STAmount const balance = (*sleRippleState)[sfBalance];
117
118 // If balance is positive, issuer must have higher address than holder
119 if (balance > beast::kZero && issuer < holder)
120 return tecNO_PERMISSION;
121
122 // If balance is negative, issuer must have lower address than holder
123 if (balance < beast::kZero && issuer > holder)
124 return tecNO_PERMISSION;
125
126 // At this point, we know that issuer and holder accounts
127 // are correct and a trustline exists between them.
128 //
129 // Must now explicitly check the balance to make sure
130 // available balance is non-zero.
131 //
132 // We can't directly check the balance of trustline because
133 // the available balance of a trustline is prone to new changes (eg.
134 // XLS-34). So we must use `accountHolds`.
135 if (accountHolds(
136 ctx.view,
137 holder,
138 clawAmount.get<Issue>().currency,
139 issuer,
141 ctx.j) <= beast::kZero)
143
144 return tesSUCCESS;
145}
146
147template <>
150 PreclaimContext const& ctx,
151 SLE const& sleIssuer,
152 AccountID const& issuer,
153 AccountID const& holder,
154 STAmount const& clawAmount)
155{
156 auto const issuanceKey = keylet::mptokenIssuance(clawAmount.get<MPTIssue>().getMptID());
157 auto const sleIssuance = ctx.view.read(issuanceKey);
158 if (!sleIssuance)
159 return tecOBJECT_NOT_FOUND;
160
161 if (!sleIssuance->isFlag(lsfMPTCanClawback))
162 return tecNO_PERMISSION;
163
164 if (sleIssuance->getAccountID(sfIssuer) != issuer)
165 return tecNO_PERMISSION;
166
167 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
168 return tecOBJECT_NOT_FOUND;
169
170 if (accountHolds(
171 ctx.view,
172 holder,
173 clawAmount.get<MPTIssue>(),
176 ctx.j) <= beast::kZero)
178
179 return tesSUCCESS;
180}
181
182TER
184{
185 AccountID const issuer = ctx.tx[sfAccount];
186 auto const clawAmount = ctx.tx[sfAmount];
187 AccountID const holder = clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
188
189 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
190 auto const sleHolder = ctx.view.read(keylet::account(holder));
191 if (!sleIssuer || !sleHolder)
192 return terNO_ACCOUNT;
193
194 // Note the order of checks - when SAV is active, this check here will make
195 // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant.
196 if (ctx.view.rules().enabled(featureSingleAssetVault) && isPseudoAccount(sleHolder))
197 {
198 return tecPSEUDO_ACCOUNT;
199 }
200 if (sleHolder->isFieldPresent(sfAMMID))
201 {
202 return tecAMM_ACCOUNT;
203 }
204
205 return std::visit(
206 [&]<typename T>(T const&) {
207 return preclaimHelper<T>(ctx, *sleIssuer, issuer, holder, clawAmount);
208 },
209 ctx.tx[sfAmount].asset().value());
210}
211
212template <ValidIssueType T>
213static TER
215
216template <>
219{
220 AccountID const issuer = ctx.tx[sfAccount];
221 STAmount clawAmount = ctx.tx[sfAmount];
222 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
223
224 // Replace the `issuer` field with issuer's account
225 clawAmount.get<Issue>().account = issuer;
226 if (holder == issuer)
227 return tecINTERNAL; // LCOV_EXCL_LINE
228
229 // Get the spendable balance. Must use `accountHolds`.
230 STAmount const spendableAmount = accountHolds(
231 ctx.view(),
232 holder,
233 clawAmount.get<Issue>().currency,
234 clawAmount.getIssuer(),
236 ctx.journal);
237
238 return directSendNoFee(
239 ctx.view(), holder, issuer, std::min(spendableAmount, clawAmount), true, ctx.journal);
240}
241
242template <>
245{
246 AccountID const issuer = ctx.tx[sfAccount];
247 auto clawAmount = ctx.tx[sfAmount];
248 AccountID const holder = ctx.tx[sfHolder];
249
250 // Get the spendable balance. Must use `accountHolds`.
251 STAmount const spendableAmount = accountHolds(
252 ctx.view(),
253 holder,
254 clawAmount.get<MPTIssue>(),
257 ctx.journal);
258
259 return directSendNoFee(
260 ctx.view(),
261 holder,
262 issuer,
263 std::min(spendableAmount, clawAmount),
264 /*checkIssuer*/ false,
265 ctx.journal);
266}
267
268TER
270{
271 return std::visit(
272 [&]<typename T>(T const&) { return applyHelper<T>(ctx_); },
273 ctx_.tx[sfAmount].asset().value());
274}
275
276void
278{
279 // No transaction-specific invariants yet (future work).
280}
281
282bool
284{
285 // No transaction-specific invariants yet (future work).
286 return true;
287}
288
289} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
State information when applying a tx.
STTx const & tx
beast::Journal const journal
ApplyView & view()
TER doApply() override
Definition Clawback.cpp:269
static NotTEC preflight(PreflightContext const &ctx)
Definition Clawback.cpp:77
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
Definition Clawback.cpp:277
static TER preclaim(PreclaimContext const &ctx)
Definition Clawback.cpp:183
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
Definition Clawback.cpp:283
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID const & getIssuer() const
Definition Issue.h:25
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:33
A view into a ledger.
Definition ReadView.h:31
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 SLE::const_pointer 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:171
constexpr TIss const & get() const
AccountID const & getIssuer() const
Definition STAmount.h:498
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFlag(std::uint32_t) const
Definition STObject.cpp:501
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:454
ApplyContext & ctx_
Definition Transactor.h:116
T min(T... args)
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:521
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:533
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:241
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_ACCOUNT
Definition TER.h:209
bool isXRP(AccountID const &c)
Definition AccountID.h:70
static TER applyHelper(ApplyContext &ctx)
static NotTEC preflightHelper(PreflightContext const &ctx)
STLedgerEntry SLE
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, STAmount const &clawAmount)
TER directSendNoFee(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static directSendNoFeeIOU if saAmount represents Issue.
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Clawback.cpp:55
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
@ temMALFORMED
Definition TER.h:73
@ temDISABLED
Definition TER.h:100
@ temBAD_AMOUNT
Definition TER.h:75
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition Clawback.cpp:36
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
TERSubset< CanCvtToTER > TER
Definition TER.h:634
TER preclaimHelper< MPTIssue >(PreclaimContext const &ctx, SLE const &sleIssuer, STAmount const &clawAmount)
@ tecPSEUDO_ACCOUNT
Definition TER.h:360
@ tecOBJECT_NOT_FOUND
Definition TER.h:324
@ tecINTERNAL
Definition TER.h:308
@ tecAMM_ACCOUNT
Definition TER.h:332
@ tecINSUFFICIENT_FUNDS
Definition TER.h:323
@ tecNO_LINE
Definition TER.h:299
@ tecNO_PERMISSION
Definition TER.h:303
TER applyHelper< MPTIssue >(ApplyContext &ctx)
Definition Clawback.cpp:244
bool isPseudoAccount(SLE::const_pointer sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
TER applyHelper< Issue >(ApplyContext &ctx)
Definition Clawback.cpp:218
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
@ tesSUCCESS
Definition TER.h:240
static TER preclaimHelper(PreclaimContext const &ctx, SLE const &sleIssuer, STAmount const &clawAmount)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:18
T visit(T... args)