xrpld
Loading...
Searching...
No Matches
applySteps.cpp
1#include <xrpl/tx/applySteps.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/beast/utility/Zero.h>
7#include <xrpl/beast/utility/instrumentation.h>
8#include <xrpl/ledger/ApplyView.h>
9#include <xrpl/ledger/OpenView.h>
10#include <xrpl/protocol/Rules.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/SeqProxy.h>
13#include <xrpl/protocol/TER.h>
14#include <xrpl/protocol/XRPAmount.h>
15#include <xrpl/tx/ApplyContext.h>
16#include <xrpl/tx/Transactor.h>
17
18#include <cstdint>
19#include <exception>
20#include <expected>
21#include <memory>
22#include <optional>
23#include <utility>
24#pragma push_macro("TRANSACTION")
25#undef TRANSACTION
26
27// Do nothing
28#define TRANSACTION(...)
29#define TRANSACTION_INCLUDE 1
30
31#include <xrpl/protocol/detail/transactions.macro>
32
33#undef TRANSACTION
34#pragma pop_macro("TRANSACTION")
35
36// DO NOT INCLUDE TRANSACTOR HEADER FILES HERE.
37// See the instructions at the top of transactions.macro instead.
38
39#include <xrpl/core/ServiceRegistry.h>
40#include <xrpl/protocol/TxFormats.h>
41
42namespace xrpl {
43
44namespace {
45
46struct UnknownTxnType : std::exception
47{
48 TxType txnType;
49 UnknownTxnType(TxType t) : txnType{t}
50 {
51 }
52};
53
54// Call a lambda with the concrete transaction type as a template parameter
55// throw an "UnknownTxnType" exception on error
56template <class F>
57auto
58withTxnType(Rules const& rules, TxType txnType, F&& f)
59{
60 // These global updates really should have been for every Transaction
61 // step: preflight, preclaim, calculateBaseFee, and doApply. Unfortunately,
62 // they were only included in doApply (via Transactor::operator()). That may
63 // have been sufficient when the changes were only related to operations
64 // that mutated data, but some features will now change how they read data,
65 // so these need to be more global.
66 //
67 // To prevent unintentional side effects on existing checks, they will be
68 // set for every operation only once at least one of the relevant amendments
69 // are enabled.
70 //
71 // See also Transactor::operator().
72 //
73 std::optional<CurrentTransactionRulesGuard> rulesGuard;
74 std::optional<NumberMantissaScaleGuard> mantissaScaleGuard;
75 createGuards(rules, rulesGuard, mantissaScaleGuard);
76
77 switch (txnType)
78 {
79#pragma push_macro("TRANSACTION")
80#undef TRANSACTION
81
82#define TRANSACTION(tag, value, name, ...) \
83 case tag: \
84 return f.template operator()<name>();
85
86#include <xrpl/protocol/detail/transactions.macro>
87
88#undef TRANSACTION
89#pragma pop_macro("TRANSACTION")
90 default:
91 throw UnknownTxnType(txnType);
92 }
93}
94} // namespace
95
96// Templates so preflight does the right thing with T::kConsequencesFactory.
97//
98// This could be done more easily using if constexpr, but Visual Studio
99// 2017 doesn't handle if constexpr correctly. So once we're no longer
100// building with Visual Studio 2017 we can consider replacing the four
101// templates with a single template function that uses if constexpr.
102//
103// For ConsequencesFactoryType::Normal
104//
105
106template <class T>
107 requires(T::kConsequencesFactory == Transactor::ConsequencesFactoryType::Normal)
110{
111 return TxConsequences(ctx.tx);
112};
113
114// For ConsequencesFactoryType::Blocker
115template <class T>
116 requires(T::kConsequencesFactory == Transactor::ConsequencesFactoryType::Blocker)
117TxConsequences
122
123// For ConsequencesFactoryType::Custom
124template <class T>
125 requires(T::kConsequencesFactory == Transactor::ConsequencesFactoryType::Custom)
126TxConsequences
128{
129 return T::makeTxConsequences(ctx);
130};
131
134{
135 try
136 {
137 return withTxnType(ctx.rules, ctx.tx.getTxnType(), [&]<typename T>() {
138 auto const tec = Transactor::invokePreflight<T>(ctx);
139 return std::make_pair(
140 tec, isTesSuccess(tec) ? consequencesHelper<T>(ctx) : TxConsequences{tec});
141 });
142 }
143 catch (UnknownTxnType const& e)
144 {
145 // Should never happen
146 // LCOV_EXCL_START
147 JLOG(ctx.j.fatal()) << "Unknown transaction type in preflight: " << e.txnType;
148 UNREACHABLE("xrpl::invokePreflight : unknown transaction type");
149 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
150 // LCOV_EXCL_STOP
151 }
152}
153
154static TER
156{
157 try
158 {
159 // use name hiding to accomplish compile-time polymorphism of static
160 // class functions for Transactor and derived classes.
161 return withTxnType(ctx.view.rules(), ctx.tx.getTxnType(), [&]<typename T>() -> TER {
162 // preclaim functionality is divided into two sections:
163 // 1. Up to and including the signature check: returns NotTEC.
164 // All transaction checks before and including checkSign
165 // MUST return NotTEC, or something more restrictive.
166 // Allowing tec results in these steps risks theft or
167 // destruction of funds, as a fee will be charged before the
168 // signature is checked.
169 // 2. After the signature check: returns TER.
170
171 // If the transactor requires a valid account and the
172 // transaction doesn't list one, preflight will have already
173 // a flagged a failure.
174 auto const id = ctx.tx.getAccountID(sfAccount);
175
176 if (id != beast::kZero)
177 {
178 if (NotTEC const preSigResult = [&]() -> NotTEC {
179 if (NotTEC const result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
180 return result;
181
182 if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
183 return result;
184
185 if (NotTEC const result = T::checkSponsor(ctx.view, ctx.tx))
186 return result;
187
188 if (NotTEC const result =
189 Transactor::invokeCheckPermission<T>(ctx.view, ctx.tx))
190 return result;
191
192 if (NotTEC const result = T::checkSign(ctx))
193 return result;
194
195 return tesSUCCESS;
196 }())
197 return preSigResult;
198
199 // We can't check the fee if we can't compute it, so reject.
200 auto const baseFee = calculateBaseFee(ctx.view, ctx.tx);
201 if (!baseFee)
202 return baseFee.error();
203
204 if (TER const result = T::checkFee(ctx, *baseFee))
205 return result;
206 }
207
208 return T::preclaim(ctx);
209 });
210 }
211 catch (UnknownTxnType const& e)
212 {
213 // Should never happen
214 // LCOV_EXCL_START
215 JLOG(ctx.j.fatal()) << "Unknown transaction type in preclaim: " << e.txnType;
216 UNREACHABLE("xrpl::invokePreclaim : unknown transaction type");
217 return temUNKNOWN;
218 // LCOV_EXCL_STOP
219 }
220}
221
237static std::expected<XRPAmount, TER>
238invokeCalculateBaseFee(ReadView const& view, STTx const& tx)
239{
240 try
241 {
242 return withTxnType(view.rules(), tx.getTxnType(), [&]<typename T>() {
243 return T::calculateBaseFee(view, tx);
244 });
245 }
246 catch (UnknownTxnType const&)
247 {
248 // LCOV_EXCL_START
249 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
251 // LCOV_EXCL_STOP
252 }
253 catch (std::exception const& e)
254 {
255 JLOG(debugLog().error()) << "calculateBaseFee: " << tx.getTransactionID()
256 << " threw an exception: " << e.what();
258 }
259 catch (...)
260 {
261 JLOG(debugLog().error()) << "calculateBaseFee: " << tx.getTransactionID()
262 << " threw an unknown exception";
264 }
265}
266
268 : isBlocker_(false)
269 , fee_(beast::kZero)
270 , potentialSpend_(beast::kZero)
271 , seqProx_(SeqProxy::rawSequence(0))
273{
274 XRPL_ASSERT(
275 !isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
276}
277
279 : isBlocker_(false)
280 , fee_(tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() : beast::kZero)
281 , potentialSpend_(beast::kZero)
282 , seqProx_(tx.getSeqProxy())
283 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
284{
285}
286
291
296
301
302static ApplyResult
304{
305 try
306 {
307 return withTxnType(ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
308 T p(ctx);
309 return p();
310 });
311 }
312 catch (UnknownTxnType const& e)
313 {
314 // Should never happen
315 // LCOV_EXCL_START
316 JLOG(ctx.journal.fatal()) << "Unknown transaction type in apply: " << e.txnType;
317 UNREACHABLE("xrpl::invokeApply : unknown transaction type");
318 return {temUNKNOWN, false};
319 // LCOV_EXCL_STOP
320 }
321}
322
323// Test-only factory — not part of the public API.
324// The returned Transactor holds a raw reference to ctx; the caller must ensure
325// the ApplyContext outlives the Transactor.
328{
329 return withTxnType(
330 ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() -> std::unique_ptr<Transactor> {
331 return std::make_unique<T>(ctx);
332 });
333}
334
335PreflightResult
337 ServiceRegistry& registry,
338 Rules const& rules,
339 STTx const& tx,
340 ApplyFlags flags,
342{
343 PreflightContext const pfCtx(registry, tx, rules, flags, j);
344
345 // XRPL_ASSERT_IF in the PreflightContext constructor only fires in debug
346 // builds; re-check the same invariant here so a release build can't
347 // silently skip a proposed transaction's signature-presence checks
348 // outside of a dry run.
349 if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
350 {
351 // LCOV_EXCL_START
352 JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
353 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
354 // LCOV_EXCL_STOP
355 }
356
357 try
358 {
359 return {pfCtx, invokePreflight(pfCtx)};
360 }
361 catch (std::exception const& e)
362 {
363 JLOG(j.fatal()) << "apply (preflight): " << e.what();
364 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
365 }
366}
367
368PreflightResult
370 ServiceRegistry& registry,
371 Rules const& rules,
372 UInt256 const& parentBatchId,
373 STTx const& tx,
374 ApplyFlags flags,
376{
377 PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
378
379 // See the comment in the other preflight() overload above.
380 if ((flags & TapProposal) != TapNone && (flags & TapDryRun) == TapNone)
381 {
382 // LCOV_EXCL_START
383 JLOG(j.fatal()) << "apply (preflight): TapProposal set without TapDryRun.";
384 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
385 // LCOV_EXCL_STOP
386 }
387
388 try
389 {
390 return {pfCtx, invokePreflight(pfCtx)};
391 }
392 catch (std::exception const& e)
393 {
394 JLOG(j.fatal()) << "apply (preflight): " << e.what();
395 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
396 }
397}
398
399NotTEC
400invokeCheckPermission(ReadView const& view, STTx const& tx)
401{
402 try
403 {
404 return withTxnType(view.rules(), tx.getTxnType(), [&]<typename T>() {
405 return Transactor::invokeCheckPermission<T>(view, tx);
406 });
407 }
408 // LCOV_EXCL_START
409 catch (UnknownTxnType const& e)
410 {
411 // Should never happen
412 JLOG(debugLog().fatal()) << "Unknown transaction type in invokeCheckPermission: "
413 << e.txnType;
414 UNREACHABLE("xrpl::invokeCheckPermission : unknown transaction type");
415 return temUNKNOWN;
416 }
417 // LCOV_EXCL_STOP
418}
419
420PreclaimResult
421preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view)
422{
424 if (preflightResult.rules != view.rules())
425 {
426 auto secondFlight = [&]() {
427 if (preflightResult.parentBatchId)
428 {
429 return preflight(
430 registry,
431 view.rules(),
432 preflightResult.parentBatchId.value(),
433 preflightResult.tx,
434 preflightResult.flags,
435 preflightResult.j);
436 }
437
438 return preflight(
439 registry,
440 view.rules(),
441 preflightResult.tx,
442 preflightResult.flags,
443 preflightResult.j);
444 }();
445
446 ctx.emplace(
447 registry,
448 view,
449 secondFlight.ter,
450 secondFlight.tx,
451 secondFlight.flags,
452 secondFlight.parentBatchId,
453 secondFlight.j);
454 }
455 else
456 {
457 ctx.emplace(
458 registry,
459 view,
460 preflightResult.ter,
461 preflightResult.tx,
462 preflightResult.flags,
463 preflightResult.parentBatchId,
464 preflightResult.j);
465 }
466
467 try
468 {
469 if (!isTesSuccess(ctx->preflightResult))
470 return {*ctx, ctx->preflightResult};
471 return {*ctx, invokePreclaim(*ctx)};
472 }
473 catch (std::exception const& e)
474 {
475 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
476 return {*ctx, tefEXCEPTION};
477 }
478}
479
480std::expected<XRPAmount, TER>
481calculateBaseFee(ReadView const& view, STTx const& tx)
482{
483 return invokeCalculateBaseFee(view, tx);
484}
485
486XRPAmount
487calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
488{
489 return Transactor::calculateBaseFee(view, tx);
490}
491
492ApplyResult
493doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view)
494{
495 if (preclaimResult.view.seq() != view.seq())
496 {
497 // Logic error from the caller. Don't have enough
498 // info to recover.
499 return {tefEXCEPTION, false};
500 }
501 try
502 {
503 if (!preclaimResult.likelyToClaimFee)
504 return {preclaimResult.ter, false};
505
506 // For any tx with a real account, preclaim already computed this fee
507 // successfully against this same view.
508 auto const baseFee = calculateBaseFee(view, preclaimResult.tx);
509 if (!baseFee)
510 {
511 // LCOV_EXCL_START
512 JLOG(preclaimResult.j.error())
513 << "apply: could not compute base fee: " << transToken(baseFee.error());
514 return {tefINTERNAL, false};
515 // LCOV_EXCL_STOP
516 }
517
518 ApplyContext ctx(
519 registry,
520 view,
521 preclaimResult.parentBatchId,
522 preclaimResult.tx,
523 preclaimResult.ter,
524 *baseFee,
525 preclaimResult.flags,
526 preclaimResult.j);
527 return invokeApply(ctx);
528 }
529 catch (std::exception const& e)
530 {
531 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
532 return {tefEXCEPTION, false};
533 }
534}
535
536} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream fatal() const
Definition Journal.h:368
Stream error() const
Definition Journal.h:362
State information when applying a tx.
STTx const & tx
beast::Journal const journal
ApplyView & view()
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:59
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:149
A view into a ledger.
Definition ReadView.h:41
virtual Rules const & rules() const =0
Returns the tx processing rules.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:115
Rules controlling protocol behavior.
Definition Rules.h:40
TxType getTxnType() const
Definition STTx.h:250
UInt256 getTransactionID() const
Definition STTx.h:262
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:37
Service registry for dependency injection.
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Class describing the consequences to the account of applying a transaction if the transaction consume...
Definition applySteps.h:53
TxConsequences(NotTEC pfResult)
std::uint32_t sequencesConsumed_
Number of sequences consumed.
Definition applySteps.h:92
Category
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:59
@ Blocker
Affects the ability of subsequent transactions to claim a fee.
Definition applySteps.h:68
XRPAmount potentialSpend_
Does NOT include the fee.
Definition applySteps.h:84
bool isBlocker_
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:76
std::uint32_t sequencesConsumed() const
Sequences consumed.
Definition applySteps.h:169
XRPAmount const & potentialSpend() const
Potential Spend.
Definition applySteps.h:151
SeqProxy seqProx_
SeqProxy of transaction.
Definition applySteps.h:88
XRPAmount fee_
Transaction fee.
Definition applySteps.h:80
T emplace(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
PreflightResult preflight(ServiceRegistry &registry, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
TxType
Transaction type identifiers.
Definition TxFormats.h:45
PreclaimResult preclaim(PreflightResult const &preflightResult, ServiceRegistry &registry, OpenView const &view)
Gate a transaction based on static ledger information.
std::expected< XRPAmount, TER > calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
beast::Journal debugLog()
Returns a debug journal.
Definition Log.cpp:399
static TER invokePreclaim(PreclaimContext const &ctx)
TxConsequences consequencesHelper(PreflightContext const &ctx)
@ tefINTERNAL
Definition TER.h:168
@ tefEXCEPTION
Definition TER.h:167
void createGuards(Rules const &rules, std::optional< CurrentTransactionRulesGuard > &rulesGuard, std::optional< NumberMantissaScaleGuard > &mantissaScaleGuard)
Definition Rules.cpp:93
std::string transToken(TER code)
Definition TER.cpp:257
static std::pair< NotTEC, TxConsequences > invokePreflight(PreflightContext const &ctx)
BaseUInt< 256 > UInt256
Definition base_uint.h:580
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
static std::expected< XRPAmount, TER > invokeCalculateBaseFee(ReadView const &view, STTx const &tx)
Calculates the base fee for a given transaction.
std::unique_ptr< Transactor > makeTransactor(ApplyContext &ctx)
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
NotTEC invokeCheckPermission(ReadView const &view, STTx const &tx)
Type-erased overload of Transactor::invokeCheckPermission.
ApplyFlags
Definition ApplyView.h:27
@ TapDryRun
Definition ApplyView.h:46
@ TapProposal
Definition ApplyView.h:53
@ TapNone
Definition ApplyView.h:28
@ temUNKNOWN
Definition TER.h:112
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
TERSubset< CanCvtToTER > TER
Definition TER.h:654
static ApplyResult invokeApply(ApplyContext &ctx)
ApplyResult doApply(PreclaimResult const &preclaimResult, ServiceRegistry &registry, OpenView &view)
Apply a prechecked transaction to an OpenView.
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
Describes the results of the preclaim check.
Definition applySteps.h:264
ReadView const & view
From the input - the ledger view.
Definition applySteps.h:269
TER const ter
Intermediate transaction result.
Definition applySteps.h:290
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:281
STTx const & tx
From the input - the transaction.
Definition applySteps.h:273
std::optional< UInt256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:277
beast::Journal const j
From the input - the journal.
Definition applySteps.h:285
bool const likelyToClaimFee
Success flag - whether the transaction is likely to claim a fee.
Definition applySteps.h:296
State information when preflighting a tx.
Definition Transactor.h:39
beast::Journal const j
Definition Transactor.h:46
Describes the results of the preflight check.
Definition applySteps.h:201
beast::Journal const j
From the input - the journal.
Definition applySteps.h:226
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:222
Rules const rules
From the input - the rules.
Definition applySteps.h:214
NotTEC const ter
Intermediate transaction result.
Definition applySteps.h:231
STTx const & tx
From the input - the transaction.
Definition applySteps.h:206
std::optional< UInt256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:210
T unexpected(T... args)
T what(T... args)