rippled
Loading...
Searching...
No Matches
Clawback.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2023 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/tx/detail/Clawback.h>
21
22#include <xrpl/ledger/View.h>
23#include <xrpl/protocol/Feature.h>
24#include <xrpl/protocol/Indexes.h>
25#include <xrpl/protocol/MPTAmount.h>
26#include <xrpl/protocol/Protocol.h>
27#include <xrpl/protocol/TxFlags.h>
28
29namespace ripple {
30
31template <ValidIssueType T>
32static NotTEC
34
35template <>
38{
39 if (ctx.tx.isFieldPresent(sfHolder))
40 return temMALFORMED;
41
42 AccountID const issuer = ctx.tx[sfAccount];
43 STAmount const clawAmount = ctx.tx[sfAmount];
44
45 // The issuer field is used for the token holder instead
46 AccountID const& holder = clawAmount.getIssuer();
47
48 if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::zero)
49 return temBAD_AMOUNT;
50
51 return tesSUCCESS;
52}
53
54template <>
57{
58 if (!ctx.rules.enabled(featureMPTokensV1))
59 return temDISABLED;
60
61 auto const mptHolder = ctx.tx[~sfHolder];
62 auto const clawAmount = ctx.tx[sfAmount];
63
64 if (!mptHolder)
65 return temMALFORMED;
66
67 // issuer is the same as holder
68 if (ctx.tx[sfAccount] == *mptHolder)
69 return temMALFORMED;
70
71 if (clawAmount.mpt() > MPTAmount{maxMPTokenAmount} ||
72 clawAmount <= beast::zero)
73 return temBAD_AMOUNT;
74
75 return tesSUCCESS;
76}
77
80{
81 if (!ctx.rules.enabled(featureClawback))
82 return temDISABLED;
83
84 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
85 return ret;
86
87 if (ctx.tx.getFlags() & tfClawbackMask)
88 return temINVALID_FLAG;
89
90 if (auto const ret = std::visit(
91 [&]<typename T>(T const&) { return preflightHelper<T>(ctx); },
92 ctx.tx[sfAmount].asset().value());
93 !isTesSuccess(ret))
94 return ret;
95
96 return preflight2(ctx);
97}
98
99template <ValidIssueType T>
100static TER
102 PreclaimContext const& ctx,
103 SLE const& sleIssuer,
104 AccountID const& issuer,
105 AccountID const& holder,
106 STAmount const& clawAmount);
107
108template <>
111 PreclaimContext const& ctx,
112 SLE const& sleIssuer,
113 AccountID const& issuer,
114 AccountID const& holder,
115 STAmount const& clawAmount)
116{
117 std::uint32_t const issuerFlagsIn = sleIssuer.getFieldU32(sfFlags);
118
119 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
120 // permission
121 if (!(issuerFlagsIn & lsfAllowTrustLineClawback) ||
122 (issuerFlagsIn & lsfNoFreeze))
123 return tecNO_PERMISSION;
124
125 auto const sleRippleState =
126 ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency()));
127 if (!sleRippleState)
128 return tecNO_LINE;
129
130 STAmount const balance = (*sleRippleState)[sfBalance];
131
132 // If balance is positive, issuer must have higher address than holder
133 if (balance > beast::zero && issuer < holder)
134 return tecNO_PERMISSION;
135
136 // If balance is negative, issuer must have lower address than holder
137 if (balance < beast::zero && issuer > holder)
138 return tecNO_PERMISSION;
139
140 // At this point, we know that issuer and holder accounts
141 // are correct and a trustline exists between them.
142 //
143 // Must now explicitly check the balance to make sure
144 // available balance is non-zero.
145 //
146 // We can't directly check the balance of trustline because
147 // the available balance of a trustline is prone to new changes (eg.
148 // XLS-34). So we must use `accountHolds`.
149 if (accountHolds(
150 ctx.view,
151 holder,
152 clawAmount.getCurrency(),
153 issuer,
155 ctx.j) <= beast::zero)
157
158 return tesSUCCESS;
159}
160
161template <>
164 PreclaimContext const& ctx,
165 SLE const& sleIssuer,
166 AccountID const& issuer,
167 AccountID const& holder,
168 STAmount const& clawAmount)
169{
170 auto const issuanceKey =
171 keylet::mptIssuance(clawAmount.get<MPTIssue>().getMptID());
172 auto const sleIssuance = ctx.view.read(issuanceKey);
173 if (!sleIssuance)
174 return tecOBJECT_NOT_FOUND;
175
176 if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback))
177 return tecNO_PERMISSION;
178
179 if (sleIssuance->getAccountID(sfIssuer) != issuer)
180 return tecNO_PERMISSION;
181
182 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
183 return tecOBJECT_NOT_FOUND;
184
185 if (accountHolds(
186 ctx.view,
187 holder,
188 clawAmount.get<MPTIssue>(),
191 ctx.j) <= beast::zero)
193
194 return tesSUCCESS;
195}
196
197TER
199{
200 AccountID const issuer = ctx.tx[sfAccount];
201 auto const clawAmount = ctx.tx[sfAmount];
202 AccountID const holder =
203 clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
204
205 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
206 auto const sleHolder = ctx.view.read(keylet::account(holder));
207 if (!sleIssuer || !sleHolder)
208 return terNO_ACCOUNT;
209
210 // Note the order of checks - when SAV is active, this check here will make
211 // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant.
212 if (ctx.view.rules().enabled(featureSingleAssetVault) &&
213 isPseudoAccount(sleHolder))
214 return tecPSEUDO_ACCOUNT;
215 else if (sleHolder->isFieldPresent(sfAMMID))
216 return tecAMM_ACCOUNT;
217
218 return std::visit(
219 [&]<typename T>(T const&) {
220 return preclaimHelper<T>(
221 ctx, *sleIssuer, issuer, holder, clawAmount);
222 },
223 ctx.tx[sfAmount].asset().value());
224}
225
226template <ValidIssueType T>
227static TER
229
230template <>
233{
234 AccountID const issuer = ctx.tx[sfAccount];
235 STAmount clawAmount = ctx.tx[sfAmount];
236 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
237
238 // Replace the `issuer` field with issuer's account
239 clawAmount.setIssuer(issuer);
240 if (holder == issuer)
241 return tecINTERNAL;
242
243 // Get the spendable balance. Must use `accountHolds`.
244 STAmount const spendableAmount = accountHolds(
245 ctx.view(),
246 holder,
247 clawAmount.getCurrency(),
248 clawAmount.getIssuer(),
250 ctx.journal);
251
252 return rippleCredit(
253 ctx.view(),
254 holder,
255 issuer,
256 std::min(spendableAmount, clawAmount),
257 true,
258 ctx.journal);
259}
260
261template <>
264{
265 AccountID const issuer = ctx.tx[sfAccount];
266 auto clawAmount = ctx.tx[sfAmount];
267 AccountID const holder = ctx.tx[sfHolder];
268
269 // Get the spendable balance. Must use `accountHolds`.
270 STAmount const spendableAmount = accountHolds(
271 ctx.view(),
272 holder,
273 clawAmount.get<MPTIssue>(),
276 ctx.journal);
277
278 return rippleCredit(
279 ctx.view(),
280 holder,
281 issuer,
282 std::min(spendableAmount, clawAmount),
283 /*checkIssuer*/ false,
284 ctx.journal);
285}
286
287TER
289{
290 return std::visit(
291 [&]<typename T>(T const&) { return applyHelper<T>(ctx_); },
292 ctx_.tx[sfAmount].asset().value());
293}
294
295} // namespace ripple
State information when applying a tx.
ApplyView & view()
beast::Journal const journal
static NotTEC preflight(PreflightContext const &ctx)
Definition Clawback.cpp:79
static TER preclaim(PreclaimContext const &ctx)
Definition Clawback.cpp:198
TER doApply() override
Definition Clawback.cpp:288
A currency issued by an account.
Definition Issue.h:33
AccountID const & getIssuer() const
Definition Issue.h:45
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:46
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
constexpr TIss const & get() const
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:588
Currency const & getCurrency() const
Definition STAmount.h:502
AccountID const & getIssuer() const
Definition STAmount.h:508
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:615
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
std::uint32_t getFlags() const
Definition STObject.cpp:537
ApplyContext & ctx_
Definition Transactor.h:141
T min(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:540
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:244
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ fhIGNORE_FREEZE
Definition View.h:77
bool isXRP(AccountID const &c)
Definition AccountID.h:90
static NotTEC preflightHelper(PreflightContext const &ctx)
constexpr std::uint32_t const tfClawbackMask
Definition TxFlags.h:241
@ lsfAllowTrustLineClawback
@ lsfMPTCanClawback
TER preclaimHelper< MPTIssue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:163
@ ahIGNORE_AUTH
Definition View.h:80
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition Clawback.cpp:37
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
TER applyHelper< Issue >(ApplyContext &ctx)
Definition Clawback.cpp:232
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:110
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Clawback.cpp:56
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
@ tecPSEUDO_ACCOUNT
Definition TER.h:362
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecINTERNAL
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:305
@ tecAMM_ACCOUNT
Definition TER.h:334
@ tecNO_LINE
Definition TER.h:301
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:2829
@ tesSUCCESS
Definition TER.h:244
static TER applyHelper(ApplyContext &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:384
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
static TER preclaimHelper(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
TER applyHelper< MPTIssue >(ApplyContext &ctx)
Definition Clawback.cpp:263
@ terNO_ACCOUNT
Definition TER.h:217
TERSubset< CanCvtToTER > TER
Definition TER.h:645
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1115
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temBAD_AMOUNT
Definition TER.h:89
@ temMALFORMED
Definition TER.h:87
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35
T visit(T... args)