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
83
86{
87 if (auto const ret = std::visit(
88 [&]<typename T>(T const&) { return preflightHelper<T>(ctx); },
89 ctx.tx[sfAmount].asset().value());
90 !isTesSuccess(ret))
91 return ret;
92
93 return tesSUCCESS;
94}
95
96template <ValidIssueType T>
97static TER
99 PreclaimContext const& ctx,
100 SLE const& sleIssuer,
101 AccountID const& issuer,
102 AccountID const& holder,
103 STAmount const& clawAmount);
104
105template <>
108 PreclaimContext const& ctx,
109 SLE const& sleIssuer,
110 AccountID const& issuer,
111 AccountID const& holder,
112 STAmount const& clawAmount)
113{
114 std::uint32_t const issuerFlagsIn = sleIssuer.getFieldU32(sfFlags);
115
116 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
117 // permission
118 if (!(issuerFlagsIn & lsfAllowTrustLineClawback) ||
119 (issuerFlagsIn & lsfNoFreeze))
120 return tecNO_PERMISSION;
121
122 auto const sleRippleState =
123 ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency()));
124 if (!sleRippleState)
125 return tecNO_LINE;
126
127 STAmount const balance = (*sleRippleState)[sfBalance];
128
129 // If balance is positive, issuer must have higher address than holder
130 if (balance > beast::zero && issuer < holder)
131 return tecNO_PERMISSION;
132
133 // If balance is negative, issuer must have lower address than holder
134 if (balance < beast::zero && issuer > holder)
135 return tecNO_PERMISSION;
136
137 // At this point, we know that issuer and holder accounts
138 // are correct and a trustline exists between them.
139 //
140 // Must now explicitly check the balance to make sure
141 // available balance is non-zero.
142 //
143 // We can't directly check the balance of trustline because
144 // the available balance of a trustline is prone to new changes (eg.
145 // XLS-34). So we must use `accountHolds`.
146 if (accountHolds(
147 ctx.view,
148 holder,
149 clawAmount.getCurrency(),
150 issuer,
152 ctx.j) <= beast::zero)
154
155 return tesSUCCESS;
156}
157
158template <>
161 PreclaimContext const& ctx,
162 SLE const& sleIssuer,
163 AccountID const& issuer,
164 AccountID const& holder,
165 STAmount const& clawAmount)
166{
167 auto const issuanceKey =
168 keylet::mptIssuance(clawAmount.get<MPTIssue>().getMptID());
169 auto const sleIssuance = ctx.view.read(issuanceKey);
170 if (!sleIssuance)
171 return tecOBJECT_NOT_FOUND;
172
173 if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback))
174 return tecNO_PERMISSION;
175
176 if (sleIssuance->getAccountID(sfIssuer) != issuer)
177 return tecNO_PERMISSION;
178
179 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
180 return tecOBJECT_NOT_FOUND;
181
182 if (accountHolds(
183 ctx.view,
184 holder,
185 clawAmount.get<MPTIssue>(),
188 ctx.j) <= beast::zero)
190
191 return tesSUCCESS;
192}
193
194TER
196{
197 AccountID const issuer = ctx.tx[sfAccount];
198 auto const clawAmount = ctx.tx[sfAmount];
199 AccountID const holder =
200 clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
201
202 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
203 auto const sleHolder = ctx.view.read(keylet::account(holder));
204 if (!sleIssuer || !sleHolder)
205 return terNO_ACCOUNT;
206
207 // Note the order of checks - when SAV is active, this check here will make
208 // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant.
209 if (ctx.view.rules().enabled(featureSingleAssetVault) &&
210 isPseudoAccount(sleHolder))
211 return tecPSEUDO_ACCOUNT;
212 else if (sleHolder->isFieldPresent(sfAMMID))
213 return tecAMM_ACCOUNT;
214
215 return std::visit(
216 [&]<typename T>(T const&) {
217 return preclaimHelper<T>(
218 ctx, *sleIssuer, issuer, holder, clawAmount);
219 },
220 ctx.tx[sfAmount].asset().value());
221}
222
223template <ValidIssueType T>
224static TER
226
227template <>
230{
231 AccountID const issuer = ctx.tx[sfAccount];
232 STAmount clawAmount = ctx.tx[sfAmount];
233 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
234
235 // Replace the `issuer` field with issuer's account
236 clawAmount.setIssuer(issuer);
237 if (holder == issuer)
238 return tecINTERNAL; // LCOV_EXCL_LINE
239
240 // Get the spendable balance. Must use `accountHolds`.
241 STAmount const spendableAmount = accountHolds(
242 ctx.view(),
243 holder,
244 clawAmount.getCurrency(),
245 clawAmount.getIssuer(),
247 ctx.journal);
248
249 return rippleCredit(
250 ctx.view(),
251 holder,
252 issuer,
253 std::min(spendableAmount, clawAmount),
254 true,
255 ctx.journal);
256}
257
258template <>
261{
262 AccountID const issuer = ctx.tx[sfAccount];
263 auto clawAmount = ctx.tx[sfAmount];
264 AccountID const holder = ctx.tx[sfHolder];
265
266 // Get the spendable balance. Must use `accountHolds`.
267 STAmount const spendableAmount = accountHolds(
268 ctx.view(),
269 holder,
270 clawAmount.get<MPTIssue>(),
273 ctx.journal);
274
275 return rippleCredit(
276 ctx.view(),
277 holder,
278 issuer,
279 std::min(spendableAmount, clawAmount),
280 /*checkIssuer*/ false,
281 ctx.journal);
282}
283
284TER
286{
287 return std::visit(
288 [&]<typename T>(T const&) { return applyHelper<T>(ctx_); },
289 ctx_.tx[sfAmount].asset().value());
290}
291
292} // namespace ripple
State information when applying a tx.
ApplyView & view()
beast::Journal const journal
static NotTEC preflight(PreflightContext const &ctx)
Definition Clawback.cpp:85
static TER preclaim(PreclaimContext const &ctx)
Definition Clawback.cpp:195
TER doApply() override
Definition Clawback.cpp:285
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition Clawback.cpp:79
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
ApplyContext & ctx_
Definition Transactor.h:143
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:160
@ ahIGNORE_AUTH
Definition View.h:80
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition Clawback.cpp:37
TER applyHelper< Issue >(ApplyContext &ctx)
Definition Clawback.cpp:229
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition Clawback.cpp:107
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Clawback.cpp:56
@ tecPSEUDO_ACCOUNT
Definition TER.h:363
@ tecOBJECT_NOT_FOUND
Definition TER.h:327
@ tecINSUFFICIENT_FUNDS
Definition TER.h:326
@ tecINTERNAL
Definition TER.h:311
@ tecNO_PERMISSION
Definition TER.h:306
@ tecAMM_ACCOUNT
Definition TER.h:335
@ tecNO_LINE
Definition TER.h:302
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:2856
@ tesSUCCESS
Definition TER.h:245
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:678
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:260
@ terNO_ACCOUNT
Definition TER.h:217
TERSubset< CanCvtToTER > TER
Definition TER.h:649
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct)
Definition View.cpp:1118
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:609
@ temBAD_AMOUNT
Definition TER.h:89
@ temMALFORMED
Definition TER.h:87
@ 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)