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 if (sleHolder->isFieldPresent(sfAMMID))
211 return tecAMM_ACCOUNT;
212
213 return std::visit(
214 [&]<typename T>(T const&) {
215 return preclaimHelper<T>(
216 ctx, *sleIssuer, issuer, holder, clawAmount);
217 },
218 ctx.tx[sfAmount].asset().value());
219}
220
221template <ValidIssueType T>
222static TER
224
225template <>
228{
229 AccountID const issuer = ctx.tx[sfAccount];
230 STAmount clawAmount = ctx.tx[sfAmount];
231 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
232
233 // Replace the `issuer` field with issuer's account
234 clawAmount.setIssuer(issuer);
235 if (holder == issuer)
236 return tecINTERNAL;
237
238 // Get the spendable balance. Must use `accountHolds`.
239 STAmount const spendableAmount = accountHolds(
240 ctx.view(),
241 holder,
242 clawAmount.getCurrency(),
243 clawAmount.getIssuer(),
245 ctx.journal);
246
247 return rippleCredit(
248 ctx.view(),
249 holder,
250 issuer,
251 std::min(spendableAmount, clawAmount),
252 true,
253 ctx.journal);
254}
255
256template <>
259{
260 AccountID const issuer = ctx.tx[sfAccount];
261 auto clawAmount = ctx.tx[sfAmount];
262 AccountID const holder = ctx.tx[sfHolder];
263
264 // Get the spendable balance. Must use `accountHolds`.
265 STAmount const spendableAmount = accountHolds(
266 ctx.view(),
267 holder,
268 clawAmount.get<MPTIssue>(),
271 ctx.journal);
272
273 return rippleCredit(
274 ctx.view(),
275 holder,
276 issuer,
277 std::min(spendableAmount, clawAmount),
278 /*checkIssuer*/ false,
279 ctx.journal);
280}
281
282TER
284{
285 return std::visit(
286 [&]<typename T>(T const&) { return applyHelper<T>(ctx_); },
287 ctx_.tx[sfAmount].asset().value());
288}
289
290} // namespace ripple
State information when applying a tx.
Definition: ApplyContext.h:37
ApplyView & view()
Definition: ApplyContext.h:55
beast::Journal const journal
Definition: ApplyContext.h:52
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:283
A currency issued by an account.
Definition: Issue.h:36
AccountID const & getIssuer() const
Definition: Issue.h:48
MPTID const & getMptID() const
Definition: MPTIssue.cpp:52
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.
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:569
Currency const & getCurrency() const
Definition: STAmount.h:493
AccountID const & getIssuer() const
Definition: STAmount.h:499
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:90
T min(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:532
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:236
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:518
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:176
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ fhIGNORE_FREEZE
Definition: View.h:76
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
static NotTEC preflightHelper(PreflightContext const &ctx)
constexpr std::uint32_t const tfClawbackMask
Definition: TxFlags.h:209
@ 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:79
bool isTesSuccess(TER x)
Definition: TER.h:656
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:83
TER applyHelper< Issue >(ApplyContext &ctx)
Definition: Clawback.cpp:227
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:144
@ tecOBJECT_NOT_FOUND
Definition: TER.h:313
@ tecINSUFFICIENT_FUNDS
Definition: TER.h:312
@ tecINTERNAL
Definition: TER.h:297
@ tecNO_PERMISSION
Definition: TER.h:292
@ tecAMM_ACCOUNT
Definition: TER.h:321
@ tecNO_LINE
Definition: TER.h:288
@ tesSUCCESS
Definition: TER.h:242
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:309
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:258
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:2099
@ terNO_ACCOUNT
Definition: TER.h:217
TERSubset< CanCvtToTER > TER
Definition: TER.h:627
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:587
@ 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:55
ReadView const & view
Definition: Transactor.h:58
beast::Journal const j
Definition: Transactor.h:62
State information when preflighting a tx.
Definition: Transactor.h:34
T visit(T... args)