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#include <xrpld/ledger/View.h>
22
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.
Definition: ApplyContext.h:37
ApplyView & view()
Definition: ApplyContext.h:78
beast::Journal const journal
Definition: ApplyContext.h:75
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:36
AccountID const & getIssuer() const
Definition: Issue.h:48
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:140
T min(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:533
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:237
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:519
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ fhIGNORE_FREEZE
Definition: View.h:78
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
static NotTEC preflightHelper(PreflightContext const &ctx)
constexpr std::uint32_t const tfClawbackMask
Definition: TxFlags.h:211
@ 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:81
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.
Definition: Transactor.cpp:91
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.
Definition: Transactor.cpp:160
@ 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
@ 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:387
bool isTesSuccess(TER x) noexcept
Definition: TER.h:672
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
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:2623
@ terNO_ACCOUNT
Definition: TER.h:217
TERSubset< CanCvtToTER > TER
Definition: TER.h:643
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition: View.cpp:1128
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:603
@ 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:79
ReadView const & view
Definition: Transactor.h:82
beast::Journal const j
Definition: Transactor.h:87
State information when preflighting a tx.
Definition: Transactor.h:34
T visit(T... args)