xrpld
Loading...
Searching...
No Matches
EscrowFinish.cpp
1#include <xrpl/tx/transactors/escrow/EscrowFinish.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Slice.h>
5#include <xrpl/basics/chrono.h>
6#include <xrpl/conditions/Condition.h>
7#include <xrpl/conditions/Fulfillment.h>
8#include <xrpl/core/HashRouter.h>
9#include <xrpl/ledger/ApplyView.h>
10#include <xrpl/ledger/ReadView.h>
11#include <xrpl/ledger/View.h>
12#include <xrpl/ledger/helpers/AccountRootHelpers.h>
13#include <xrpl/ledger/helpers/CredentialHelpers.h>
14#include <xrpl/ledger/helpers/EscrowHelpers.h>
15#include <xrpl/ledger/helpers/MPTokenHelpers.h>
16#include <xrpl/ledger/helpers/RippleStateHelpers.h>
17#include <xrpl/ledger/helpers/TokenHelpers.h>
18#include <xrpl/protocol/AccountID.h>
19#include <xrpl/protocol/Concepts.h>
20#include <xrpl/protocol/Feature.h>
21#include <xrpl/protocol/Indexes.h>
22#include <xrpl/protocol/Issue.h>
23#include <xrpl/protocol/MPTIssue.h>
24#include <xrpl/protocol/Rate.h>
25#include <xrpl/protocol/SField.h>
26#include <xrpl/protocol/STAmount.h>
27#include <xrpl/protocol/STLedgerEntry.h>
28#include <xrpl/protocol/STTx.h>
29#include <xrpl/protocol/TER.h>
30#include <xrpl/protocol/XRPAmount.h>
31#include <xrpl/tx/Transactor.h>
32
33#include <system_error>
34#include <variant>
35
36namespace xrpl {
37
38// During an EscrowFinish, the transaction must specify both
39// a condition and a fulfillment. We track whether that
40// fulfillment matches and validates the condition.
43
44//------------------------------------------------------------------------------
45
46static bool
48{
49 using namespace xrpl::cryptoconditions;
50
52
53 auto condition = Condition::deserialize(c, ec);
54 if (!condition)
55 return false;
56
57 auto fulfillment = Fulfillment::deserialize(f, ec);
58 if (!fulfillment)
59 return false;
60
61 return validate(*fulfillment, *condition);
62}
63
64bool
66{
67 return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials);
68}
69
72{
73 auto const cb = ctx.tx[~sfCondition];
74 auto const fb = ctx.tx[~sfFulfillment];
75
76 // If you specify a condition, then you must also specify
77 // a fulfillment.
78 if (static_cast<bool>(cb) != static_cast<bool>(fb))
79 return temMALFORMED;
80
81 return tesSUCCESS;
82}
83
86{
87 auto const cb = ctx.tx[~sfCondition];
88 auto const fb = ctx.tx[~sfFulfillment];
89
90 if (cb && fb)
91 {
92 auto& router = ctx.registry.get().getHashRouter();
93
94 auto const id = ctx.tx.getTransactionID();
95 auto const flags = router.getFlags(id);
96
97 // If we haven't checked the condition, check it
98 // now. Whether it passes or not isn't important
99 // in preflight.
100 if (!any(flags & (kSfCfInvalid | kSfCfValid)))
101 {
102 if (checkCondition(*fb, *cb))
103 {
104 router.setFlags(id, kSfCfValid);
105 }
106 else
107 {
108 router.setFlags(id, kSfCfInvalid);
109 }
110 }
111 }
112
113 if (auto const err = credentials::checkFields(ctx.tx, ctx.j); !isTesSuccess(err))
114 return err;
115
116 return tesSUCCESS;
117}
118
121{
122 XRPAmount extraFee{0};
123
124 if (auto const fb = tx[~sfFulfillment])
125 {
126 extraFee += view.fees().base * (32 + (fb->size() / 16));
127 }
128
129 return Transactor::calculateBaseFee(view, tx) + extraFee;
130}
131
132template <ValidIssueType T>
133static TER
135 PreclaimContext const& ctx,
136 AccountID const& dest,
137 STAmount const& amount);
138
139template <>
142 PreclaimContext const& ctx,
143 AccountID const& dest,
144 STAmount const& amount)
145{
146 AccountID const& issuer = amount.getIssuer();
147 // If the issuer is the same as the account, return tesSUCCESS
148 if (issuer == dest)
149 return tesSUCCESS;
150
151 // If the issuer has requireAuth set, check if the destination is authorized
152 if (auto const ter = requireAuth(ctx.view, amount.get<Issue>(), dest); !isTesSuccess(ter))
153 return ter;
154
155 // If the issuer has deep frozen the destination, return tecFROZEN
156 if (isDeepFrozen(ctx.view, dest, amount.get<Issue>().currency, amount.getIssuer()))
157 return tecFROZEN;
158
159 return tesSUCCESS;
160}
161
162template <>
165 PreclaimContext const& ctx,
166 AccountID const& dest,
167 STAmount const& amount)
168{
169 AccountID const& issuer = amount.getIssuer();
170 // If the issuer is the same as the dest, return tesSUCCESS
171 if (issuer == dest)
172 return tesSUCCESS;
173
174 // If the mpt does not exist, return tecOBJECT_NOT_FOUND
175 auto const issuanceKey = keylet::mptokenIssuance(amount.get<MPTIssue>().getMptID());
176 auto const sleIssuance = ctx.view.read(issuanceKey);
177 if (!sleIssuance)
178 return tecOBJECT_NOT_FOUND;
179
180 // If the issuer has requireAuth set, check if the destination is
181 // authorized
182 auto const& mptIssue = amount.get<MPTIssue>();
183 if (auto const ter = requireAuth(ctx.view, mptIssue, dest, AuthType::WeakAuth);
184 !isTesSuccess(ter))
185 return ter;
186
187 // If the issuer has frozen the destination, return tecLOCKED
188 if (isFrozen(ctx.view, dest, mptIssue))
189 return tecLOCKED;
190
191 return tesSUCCESS;
192}
193
194TER
196{
197 if (ctx.view.rules().enabled(featureCredentials))
198 {
199 if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
200 !isTesSuccess(err))
201 return err;
202 }
203
204 if (ctx.view.rules().enabled(featureTokenEscrow))
205 {
206 auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]);
207 auto const slep = ctx.view.read(k);
208 if (!slep)
209 return tecNO_TARGET;
210
211 AccountID const dest = (*slep)[sfDestination];
212 STAmount const amount = (*slep)[sfAmount];
213
214 if (!isXRP(amount))
215 {
216 if (auto const ret = std::visit(
217 [&]<typename T>(T const&) {
218 return escrowFinishPreclaimHelper<T>(ctx, dest, amount);
219 },
220 amount.asset().value());
221 !isTesSuccess(ret))
222 return ret;
223 }
224 }
225 return tesSUCCESS;
226}
227
228TER
230{
231 auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
232 auto const slep = ctx_.view().peek(k);
233 if (!slep)
234 {
235 if (ctx_.view().rules().enabled(featureTokenEscrow))
236 return tecINTERNAL; // LCOV_EXCL_LINE
237
238 return tecNO_TARGET;
239 }
240
241 // If a cancel time is present, a finish operation should only succeed prior
242 // to that time.
243 auto const now = ctx_.view().header().parentCloseTime;
244
245 // Too soon: can't execute before the finish time
246 if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter]))
247 return tecNO_PERMISSION;
248
249 // Too late: can't execute after the cancel time
250 if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter]))
251 return tecNO_PERMISSION;
252
253 // Check cryptocondition fulfillment
254 {
255 auto const id = ctx_.tx.getTransactionID();
256 auto flags = ctx_.registry.get().getHashRouter().getFlags(id);
257
258 auto const cb = ctx_.tx[~sfCondition];
259
260 // It's unlikely that the results of the check will
261 // expire from the hash router, but if it happens,
262 // simply re-run the check.
263 if (cb && !any(flags & (kSfCfInvalid | kSfCfValid)))
264 {
265 // LCOV_EXCL_START
266 auto const fb = ctx_.tx[~sfFulfillment];
267
268 if (!fb)
269 return tecINTERNAL;
270
271 if (checkCondition(*fb, *cb))
272 {
273 flags = kSfCfValid;
274 }
275 else
276 {
277 flags = kSfCfInvalid;
278 }
279
280 ctx_.registry.get().getHashRouter().setFlags(id, flags);
281 // LCOV_EXCL_STOP
282 }
283
284 // If the check failed, then simply return an error
285 // and don't look at anything else.
286 if (any(flags & kSfCfInvalid))
288
289 // Check against condition in the ledger entry:
290 auto const cond = (*slep)[~sfCondition];
291
292 // If a condition wasn't specified during creation,
293 // one shouldn't be included now.
294 if (!cond && cb)
296
297 // If a condition was specified during creation of
298 // the suspended payment, the identical condition
299 // must be presented again. We don't check if the
300 // fulfillment matches the condition since we did
301 // that in preflight.
302 if (cond && (cond != cb))
304 }
305
306 // NOTE: Escrow payments cannot be used to fund accounts.
307 AccountID const destID = (*slep)[sfDestination];
308 auto const sled = ctx_.view().peek(keylet::account(destID));
309 if (!sled)
310 return tecNO_DST;
311
312 if (auto err =
313 verifyDepositPreauth(ctx_.tx, ctx_.view(), accountID_, destID, sled, ctx_.journal);
314 !isTesSuccess(err))
315 return err;
316
317 AccountID const account = (*slep)[sfAccount];
318
319 // Remove escrow from owner directory
320 {
321 auto const page = (*slep)[sfOwnerNode];
322 if (!ctx_.view().dirRemove(keylet::ownerDir(account), page, k.key, true))
323 {
324 // LCOV_EXCL_START
325 JLOG(j_.fatal()) << "Unable to delete Escrow from owner.";
326 return tefBAD_LEDGER;
327 // LCOV_EXCL_STOP
328 }
329 }
330
331 // Remove escrow from recipient's owner directory, if present.
332 if (auto const optPage = (*slep)[~sfDestinationNode])
333 {
334 if (!ctx_.view().dirRemove(keylet::ownerDir(destID), *optPage, k.key, true))
335 {
336 // LCOV_EXCL_START
337 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
338 return tefBAD_LEDGER;
339 // LCOV_EXCL_STOP
340 }
341 }
342
343 STAmount const amount = slep->getFieldAmount(sfAmount);
344 // Transfer amount to destination
345 if (isXRP(amount))
346 {
347 (*sled)[sfBalance] = (*sled)[sfBalance] + amount;
348 }
349 else
350 {
351 if (!ctx_.view().rules().enabled(featureTokenEscrow))
352 return temDISABLED; // LCOV_EXCL_LINE
353
354 Rate lockedRate = slep->isFieldPresent(sfTransferRate)
355 ? xrpl::Rate(slep->getFieldU32(sfTransferRate))
356 : kParityRate;
357 auto const issuer = amount.getIssuer();
358 bool const createAsset = destID == accountID_;
359 if (auto const ret = std::visit(
360 [&]<typename T>(T const&) {
362 ctx_.view(),
363 lockedRate,
364 sled,
366 amount,
367 issuer,
368 account,
369 destID,
370 createAsset,
371 j_);
372 },
373 amount.asset().value());
374 !isTesSuccess(ret))
375 return ret;
376
377 // Remove escrow from issuers owner directory, if present.
378 if (auto const optPage = (*slep)[~sfIssuerNode]; optPage)
379 {
380 if (!ctx_.view().dirRemove(keylet::ownerDir(issuer), *optPage, k.key, true))
381 {
382 // LCOV_EXCL_START
383 JLOG(j_.fatal()) << "Unable to delete Escrow from recipient.";
384 return tefBAD_LEDGER;
385 // LCOV_EXCL_STOP
386 }
387 }
388 }
389
390 ctx_.view().update(sled);
391
392 // Adjust source owner count
393 auto const sle = ctx_.view().peek(keylet::account(account));
394 adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal);
395 ctx_.view().update(sle);
396
397 // Remove escrow from ledger
398 ctx_.view().erase(slep);
399 return tesSUCCESS;
400}
401
402void
404{
405 // No transaction-specific invariants yet (future work).
406}
407
408bool
410 STTx const&,
411 TER,
412 XRPAmount,
413 ReadView const&,
414 beast::Journal const&)
415{
416 // No transaction-specific invariants yet (future work).
417 return true;
418}
419} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
static bool checkExtraFeatures(PreflightContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static TER preclaim(PreclaimContext const &ctx)
TER doApply() override
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static NotTEC preflight(PreflightContext const &ctx)
static NotTEC preflightSigValidated(PreflightContext const &ctx)
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:33
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer 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:171
constexpr TIss const & get() const
Asset const & asset() const
Definition STAmount.h:478
AccountID const & getIssuer() const
Definition STAmount.h:498
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:454
uint256 getTransactionID() const
Definition STTx.h:200
An immutable linear range of bytes.
Definition Slice.h:26
beast::Journal const j_
Definition Transactor.h:118
ApplyView & view()
Definition Transactor.h:136
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
AccountID const accountID_
Definition Transactor.h:120
XRPAmount preFeeBalance_
Definition Transactor.h:121
ApplyContext & ctx_
Definition Transactor.h:116
static std::unique_ptr< Condition > deserialize(Slice s, std::error_code &ec)
Load a condition from its binary form.
NotTEC checkFields(STTx const &tx, beast::Journal j)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
bool validate(Fulfillment const &f, Condition const &c, Slice m)
Verify if the given message satisfies the fulfillment.
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:521
Keylet escrow(AccountID const &src, std::uint32_t seq) noexcept
An escrow entry.
Definition Indexes.cpp:372
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:357
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TER escrowUnlockApplyHelper(ApplyView &view, Rate lockedRate, SLE::ref sleDest, STAmount const &xrpBalance, STAmount const &amount, AccountID const &issuer, AccountID const &sender, AccountID const &receiver, bool createAsset, beast::Journal journal)
bool isXRP(AccountID const &c)
Definition AccountID.h:70
static TER escrowFinishPreclaimHelper(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
@ tefBAD_LEDGER
Definition TER.h:160
constexpr HashRouterFlags kSfCfInvalid
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
TER escrowFinishPreclaimHelper< Issue >(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
constexpr HashRouterFlags kSfCfValid
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
TER escrowFinishPreclaimHelper< MPTIssue >(PreclaimContext const &ctx, AccountID const &dest, STAmount const &amount)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:554
Rate const kParityRate
A transfer rate signifying a 1:1 exchange.
void adjustOwnerCount(ApplyView &view, SLE::ref sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
HashRouterFlags
Definition HashRouter.h:14
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, std::uint8_t depth=0)
static bool checkCondition(Slice f, Slice c)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
@ temMALFORMED
Definition TER.h:73
@ temDISABLED
Definition TER.h:100
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
TERSubset< CanCvtToTER > TER
Definition TER.h:634
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
@ tecLOCKED
Definition TER.h:356
@ tecNO_TARGET
Definition TER.h:302
@ tecOBJECT_NOT_FOUND
Definition TER.h:324
@ tecINTERNAL
Definition TER.h:308
@ tecFROZEN
Definition TER.h:301
@ tecCRYPTOCONDITION_ERROR
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:303
@ tecNO_DST
Definition TER.h:288
@ tesSUCCESS
Definition TER.h:240
TER verifyDepositPreauth(STTx const &tx, ApplyView &view, AccountID const &src, AccountID const &dst, SLE::const_ref sleDst, beast::Journal j)
constexpr bool any(HashRouterFlags flags)
Definition HashRouter.h:63
uint256 key
Definition Keylet.h:20
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:18
beast::Journal const j
Definition Transactor.h:25
std::reference_wrapper< ServiceRegistry > registry
Definition Transactor.h:20
Represents a transfer rate.
Definition Rate.h:20
static std::unique_ptr< Fulfillment > deserialize(Slice s, std::error_code &ec)
Load a fulfillment from its binary form.
T visit(T... args)