rippled
Loading...
Searching...
No Matches
Clawback.cpp
1#include <xrpl/ledger/View.h>
2#include <xrpl/ledger/helpers/AccountRootHelpers.h>
3#include <xrpl/ledger/helpers/TokenHelpers.h>
4#include <xrpl/protocol/Feature.h>
5#include <xrpl/protocol/Indexes.h>
6#include <xrpl/protocol/MPTAmount.h>
7#include <xrpl/protocol/Protocol.h>
8#include <xrpl/protocol/TxFlags.h>
9#include <xrpl/tx/transactors/token/Clawback.h>
10
11namespace xrpl {
12
13template <ValidIssueType T>
14static NotTEC
16
17template <>
20{
21 if (ctx.tx.isFieldPresent(sfHolder))
22 return temMALFORMED;
23
24 AccountID const issuer = ctx.tx[sfAccount];
25 STAmount const clawAmount = ctx.tx[sfAmount];
26
27 // The issuer field is used for the token holder instead
28 AccountID const& holder = clawAmount.getIssuer();
29
30 if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::zero)
31 return temBAD_AMOUNT;
32
33 return tesSUCCESS;
34}
35
36template <>
39{
40 if (!ctx.rules.enabled(featureMPTokensV1))
41 return temDISABLED;
42
43 auto const mptHolder = ctx.tx[~sfHolder];
44 auto const clawAmount = ctx.tx[sfAmount];
45
46 if (!mptHolder)
47 return temMALFORMED;
48
49 // issuer is the same as holder
50 if (ctx.tx[sfAccount] == *mptHolder)
51 return temMALFORMED;
52
53 if (clawAmount.mpt() > MPTAmount{maxMPTokenAmount} || clawAmount <= beast::zero)
54 return temBAD_AMOUNT;
55
56 return tesSUCCESS;
57}
58
61{
62 if (auto const ret = std::visit(
63 [&]<typename T>(T const&) { return preflightHelper<T>(ctx); },
64 ctx.tx[sfAmount].asset().value());
65 !isTesSuccess(ret))
66 return ret;
67
68 return tesSUCCESS;
69}
70
71template <ValidIssueType T>
72static TER
74 PreclaimContext const& ctx,
75 SLE const& sleIssuer,
76 AccountID const& issuer,
77 AccountID const& holder,
78 STAmount const& clawAmount);
79
80template <>
83 PreclaimContext const& ctx,
84 SLE const& sleIssuer,
85 AccountID const& issuer,
86 AccountID const& holder,
87 STAmount const& clawAmount)
88{
89 std::uint32_t const issuerFlagsIn = sleIssuer.getFieldU32(sfFlags);
90
91 // If AllowTrustLineClawback is not set or NoFreeze is set, return no
92 // permission
93 if (((issuerFlagsIn & lsfAllowTrustLineClawback) == 0u) ||
94 ((issuerFlagsIn & lsfNoFreeze) != 0u))
95 return tecNO_PERMISSION;
96
97 auto const sleRippleState =
98 ctx.view.read(keylet::line(holder, issuer, clawAmount.getCurrency()));
99 if (!sleRippleState)
100 return tecNO_LINE;
101
102 STAmount const balance = (*sleRippleState)[sfBalance];
103
104 // If balance is positive, issuer must have higher address than holder
105 if (balance > beast::zero && issuer < holder)
106 return tecNO_PERMISSION;
107
108 // If balance is negative, issuer must have lower address than holder
109 if (balance < beast::zero && issuer > holder)
110 return tecNO_PERMISSION;
111
112 // At this point, we know that issuer and holder accounts
113 // are correct and a trustline exists between them.
114 //
115 // Must now explicitly check the balance to make sure
116 // available balance is non-zero.
117 //
118 // We can't directly check the balance of trustline because
119 // the available balance of a trustline is prone to new changes (eg.
120 // XLS-34). So we must use `accountHolds`.
121 if (accountHolds(ctx.view, holder, clawAmount.getCurrency(), issuer, fhIGNORE_FREEZE, ctx.j) <=
122 beast::zero)
124
125 return tesSUCCESS;
126}
127
128template <>
131 PreclaimContext const& ctx,
132 SLE const& sleIssuer,
133 AccountID const& issuer,
134 AccountID const& holder,
135 STAmount const& clawAmount)
136{
137 auto const issuanceKey = keylet::mptIssuance(clawAmount.get<MPTIssue>().getMptID());
138 auto const sleIssuance = ctx.view.read(issuanceKey);
139 if (!sleIssuance)
140 return tecOBJECT_NOT_FOUND;
141
142 if (((*sleIssuance)[sfFlags] & lsfMPTCanClawback) == 0u)
143 return tecNO_PERMISSION;
144
145 if (sleIssuance->getAccountID(sfIssuer) != issuer)
146 return tecNO_PERMISSION;
147
148 if (!ctx.view.exists(keylet::mptoken(issuanceKey.key, holder)))
149 return tecOBJECT_NOT_FOUND;
150
151 if (accountHolds(
152 ctx.view, holder, clawAmount.get<MPTIssue>(), fhIGNORE_FREEZE, ahIGNORE_AUTH, ctx.j) <=
153 beast::zero)
155
156 return tesSUCCESS;
157}
158
159TER
161{
162 AccountID const issuer = ctx.tx[sfAccount];
163 auto const clawAmount = ctx.tx[sfAmount];
164 AccountID const holder = clawAmount.holds<Issue>() ? clawAmount.getIssuer() : ctx.tx[sfHolder];
165
166 auto const sleIssuer = ctx.view.read(keylet::account(issuer));
167 auto const sleHolder = ctx.view.read(keylet::account(holder));
168 if (!sleIssuer || !sleHolder)
169 return terNO_ACCOUNT;
170
171 // Note the order of checks - when SAV is active, this check here will make
172 // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant.
173 if (ctx.view.rules().enabled(featureSingleAssetVault) && isPseudoAccount(sleHolder))
174 {
175 return tecPSEUDO_ACCOUNT;
176 }
177 if (sleHolder->isFieldPresent(sfAMMID))
178 {
179 return tecAMM_ACCOUNT;
180 }
181
182 return std::visit(
183 [&]<typename T>(T const&) {
184 return preclaimHelper<T>(ctx, *sleIssuer, issuer, holder, clawAmount);
185 },
186 ctx.tx[sfAmount].asset().value());
187}
188
189template <ValidIssueType T>
190static TER
192
193template <>
196{
197 AccountID const issuer = ctx.tx[sfAccount];
198 STAmount clawAmount = ctx.tx[sfAmount];
199 AccountID const holder = clawAmount.getIssuer(); // cannot be reference
200
201 // Replace the `issuer` field with issuer's account
202 clawAmount.setIssuer(issuer);
203 if (holder == issuer)
204 return tecINTERNAL; // LCOV_EXCL_LINE
205
206 // Get the spendable balance. Must use `accountHolds`.
207 STAmount const spendableAmount = accountHolds(
208 ctx.view(),
209 holder,
210 clawAmount.getCurrency(),
211 clawAmount.getIssuer(),
213 ctx.journal);
214
215 return rippleCredit(
216 ctx.view(), holder, issuer, std::min(spendableAmount, clawAmount), true, ctx.journal);
217}
218
219template <>
222{
223 AccountID const issuer = ctx.tx[sfAccount];
224 auto clawAmount = ctx.tx[sfAmount];
225 AccountID const holder = ctx.tx[sfHolder];
226
227 // Get the spendable balance. Must use `accountHolds`.
228 STAmount const spendableAmount = accountHolds(
229 ctx.view(),
230 holder,
231 clawAmount.get<MPTIssue>(),
234 ctx.journal);
235
236 return rippleCredit(
237 ctx.view(),
238 holder,
239 issuer,
240 std::min(spendableAmount, clawAmount),
241 /*checkIssuer*/ false,
242 ctx.journal);
243}
244
245TER
247{
248 return std::visit(
249 [&]<typename T>(T const&) { return applyHelper<T>(ctx_); },
250 ctx_.tx[sfAmount].asset().value());
251}
252
253} // namespace xrpl
State information when applying a tx.
STTx const & tx
beast::Journal const journal
ApplyView & view()
TER doApply() override
Definition Clawback.cpp:246
static NotTEC preflight(PreflightContext const &ctx)
Definition Clawback.cpp:60
static TER preclaim(PreclaimContext const &ctx)
Definition Clawback.cpp:160
A currency issued by an account.
Definition Issue.h:13
AccountID const & getIssuer() const
Definition Issue.h:25
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:26
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:120
constexpr TIss const & get() const
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:572
Currency const & getCurrency() const
Definition STAmount.h:476
AccountID const & getIssuer() const
Definition STAmount.h:482
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:593
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:456
ApplyContext & ctx_
Definition Transactor.h:112
T min(T... args)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:474
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:220
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:486
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_ACCOUNT
Definition TER.h:197
@ fhIGNORE_FREEZE
bool isXRP(AccountID const &c)
Definition AccountID.h:70
static TER applyHelper(ApplyContext &ctx)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
static NotTEC preflightHelper(PreflightContext const &ctx)
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
TERSubset< CanCvtToTER > TER
Definition TER.h:622
@ ahIGNORE_AUTH
TER preclaimHelper< Issue >(PreclaimContext const &ctx, SLE const &sleIssuer, STAmount const &clawAmount)
NotTEC preflightHelper< MPTIssue >(PreflightContext const &ctx)
Definition Clawback.cpp:38
@ temMALFORMED
Definition TER.h:67
@ temDISABLED
Definition TER.h:94
@ temBAD_AMOUNT
Definition TER.h:69
NotTEC preflightHelper< Issue >(PreflightContext const &ctx)
Definition Clawback.cpp:19
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
TER preclaimHelper< MPTIssue >(PreclaimContext const &ctx, SLE const &sleIssuer, STAmount const &clawAmount)
@ tecPSEUDO_ACCOUNT
Definition TER.h:343
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecINTERNAL
Definition TER.h:291
@ tecAMM_ACCOUNT
Definition TER.h:315
@ tecINSUFFICIENT_FUNDS
Definition TER.h:306
@ tecNO_LINE
Definition TER.h:282
@ tecNO_PERMISSION
Definition TER.h:286
TER applyHelper< MPTIssue >(ApplyContext &ctx)
Definition Clawback.cpp:221
TER applyHelper< Issue >(ApplyContext &ctx)
Definition Clawback.cpp:195
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:582
@ tesSUCCESS
Definition TER.h:225
static TER preclaimHelper(PreclaimContext const &ctx, SLE const &sleIssuer, STAmount const &clawAmount)
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.
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
beast::Journal const j
Definition Transactor.h:65
State information when preflighting a tx.
Definition Transactor.h:14
T visit(T... args)