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#include <xrpl/protocol/Feature.h>
23#include <xrpl/protocol/Indexes.h>
24#include <xrpl/protocol/MPTAmount.h>
25#include <xrpl/protocol/Protocol.h>
26#include <xrpl/protocol/TxFlags.h>
27
28namespace ripple {
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::zero)
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{maxMPTokenAmount} ||
71 clawAmount <= beast::zero)
72 return temBAD_AMOUNT;
73
74 return tesSUCCESS;
75}
76
79{
80 if (!ctx.rules.enabled(featureClawback))
81 return temDISABLED;
82
83 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
84 return ret;
85
86 if (ctx.tx.getFlags() & tfClawbackMask)
87 return temINVALID_FLAG;
88
89 if (auto const ret = std::visit(
90 [&]<typename T>(T const&) { return preflightHelper<T>(ctx); },
91 ctx.tx[sfAmount].asset().value());
92 !isTesSuccess(ret))
93 return ret;
94
95 return preflight2(ctx);
96}
97
98template <ValidIssueType T>
99static TER
101 PreclaimContext const& ctx,
102 SLE const& sleIssuer,
103 AccountID const& issuer,
104 AccountID const& holder,
105 STAmount const& clawAmount);
106
107template <>
110 PreclaimContext const& ctx,
111 SLE const& sleIssuer,
112 AccountID const& issuer,
113 AccountID const& holder,
114 STAmount const& clawAmount)
115{
116 std::uint32_t const issuerFlagsIn = sleIssuer.getFieldU32(sfFlags);
117
118 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
119 // permission
120 if (!(issuerFlagsIn & lsfAllowTrustLineClawback) ||
121 (issuerFlagsIn & lsfNoFreeze))
122 return tecNO_PERMISSION;
123
124 auto const sleRippleState =
125 ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency()));
126 if (!sleRippleState)
127 return tecNO_LINE;
128
129 STAmount const balance = (*sleRippleState)[sfBalance];
130
131 // If balance is positive, issuer must have higher address than holder
132 if (balance > beast::zero && issuer < holder)
133 return tecNO_PERMISSION;
134
135 // If balance is negative, issuer must have lower address than holder
136 if (balance < beast::zero && issuer > holder)
137 return tecNO_PERMISSION;
138
139 // At this point, we know that issuer and holder accounts
140 // are correct and a trustline exists between them.
141 //
142 // Must now explicitly check the balance to make sure
143 // available balance is non-zero.
144 //
145 // We can't directly check the balance of trustline because
146 // the available balance of a trustline is prone to new changes (eg.
147 // XLS-34). So we must use `accountHolds`.
148 if (accountHolds(
149 ctx.view,
150 holder,
151 clawAmount.getCurrency(),
152 issuer,
154 ctx.j) <= beast::zero)
156
157 return tesSUCCESS;
158}
159
160template <>
163 PreclaimContext const& ctx,
164 SLE const& sleIssuer,
165 AccountID const& issuer,
166 AccountID const& holder,
167 STAmount const& clawAmount)
168{
169 auto const issuanceKey =
170 keylet::mptIssuance(clawAmount.get<MPTIssue>().getMptID());
171 auto const sleIssuance = ctx.view.read(issuanceKey);
172 if (!sleIssuance)
173 return tecOBJECT_NOT_FOUND;
174
175 if (!((*sleIssuance)[sfFlags] & lsfMPTCanClawback))
176 return tecNO_PERMISSION;
177
178 if (sleIssuance->getAccountID(sfIssuer) != issuer)
179 return tecNO_PERMISSION;
180
181 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
182 return tecOBJECT_NOT_FOUND;
183
184 if (accountHolds(
185 ctx.view,
186 holder,
187 clawAmount.get<MPTIssue>(),
190 ctx.j) <= beast::zero)
192
193 return tesSUCCESS;
194}
195
196TER
198{
199 AccountID const issuer = ctx.tx[sfAccount];
200 auto const clawAmount = ctx.tx[sfAmount];
201 AccountID const holder =
202 clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
203
204 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
205 auto const sleHolder = ctx.view.read(keylet::account(holder));
206 if (!sleIssuer || !sleHolder)
207 return terNO_ACCOUNT;
208
209 if (sleHolder->isFieldPresent(sfAMMID))
210 return tecAMM_ACCOUNT;
211
212 return std::visit(
213 [&]<typename T>(T const&) {
214 return preclaimHelper<T>(
215 ctx, *sleIssuer, issuer, holder, clawAmount);
216 },
217 ctx.tx[sfAmount].asset().value());
218}
219
220template <ValidIssueType T>
221static TER
223
224template <>
227{
228 AccountID const issuer = ctx.tx[sfAccount];
229 STAmount clawAmount = ctx.tx[sfAmount];
230 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
231
232 // Replace the `issuer` field with issuer's account
233 clawAmount.setIssuer(issuer);
234 if (holder == issuer)
235 return tecINTERNAL;
236
237 // Get the spendable balance. Must use `accountHolds`.
238 STAmount const spendableAmount = accountHolds(
239 ctx.view(),
240 holder,
241 clawAmount.getCurrency(),
242 clawAmount.getIssuer(),
244 ctx.journal);
245
246 return rippleCredit(
247 ctx.view(),
248 holder,
249 issuer,
250 std::min(spendableAmount, clawAmount),
251 true,
252 ctx.journal);
253}
254
255template <>
258{
259 AccountID const issuer = ctx.tx[sfAccount];
260 auto clawAmount = ctx.tx[sfAmount];
261 AccountID const holder = ctx.tx[sfHolder];
262
263 // Get the spendable balance. Must use `accountHolds`.
264 STAmount const spendableAmount = accountHolds(
265 ctx.view(),
266 holder,
267 clawAmount.get<MPTIssue>(),
270 ctx.journal);
271
272 return rippleCredit(
273 ctx.view(),
274 holder,
275 issuer,
276 std::min(spendableAmount, clawAmount),
277 /*checkIssuer*/ false,
278 ctx.journal);
279}
280
281TER
283{
284 return std::visit(
285 [&]<typename T>(T const&) { return applyHelper<T>(ctx_); },
286 ctx_.tx[sfAmount].asset().value());
287}
288
289} // namespace ripple
State information when applying a tx.
Definition: ApplyContext.h:36
ApplyView & view()
Definition: ApplyContext.h:54
beast::Journal const journal
Definition: ApplyContext.h:51
static NotTEC preflight(PreflightContext const &ctx)
Definition: Clawback.cpp:78
static TER preclaim(PreclaimContext const &ctx)
Definition: Clawback.cpp:197
TER doApply() override
Definition: Clawback.cpp:282
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:88
T min(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition: Indexes.cpp:523
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:235
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:509
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ fhIGNORE_FREEZE
Definition: View.h:75
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
static NotTEC preflightHelper(PreflightContext const &ctx)
constexpr std::uint32_t const tfClawbackMask
Definition: TxFlags.h:200
@ lsfAllowTrustLineClawback
@ lsfMPTCanClawback
TER preclaimHelper< MPTIssue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition: Clawback.cpp:162
@ ahIGNORE_AUTH
Definition: View.h:78
bool isTesSuccess(TER x)
Definition: TER.h:656
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition: Clawback.cpp:36
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:81
TER applyHelper< Issue >(ApplyContext &ctx)
Definition: Clawback.cpp:226
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, AccountID const &issuer, AccountID const &holder, STAmount const &clawAmount)
Definition: Clawback.cpp:109
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition: Clawback.cpp:55
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:133
@ 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:308
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:257
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:2098
@ 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:53
ReadView const & view
Definition: Transactor.h:56
beast::Journal const j
Definition: Transactor.h:60
State information when preflighting a tx.
Definition: Transactor.h:32
T visit(T... args)