rippled
Loading...
Searching...
No Matches
applySteps.cpp
1#include <xrpld/app/tx/applySteps.h>
2#pragma push_macro("TRANSACTION")
3#undef TRANSACTION
4
5// Do nothing
6#define TRANSACTION(...)
7#define TRANSACTION_INCLUDE 1
8
9#include <xrpl/protocol/detail/transactions.macro>
10
11#undef TRANSACTION
12#pragma pop_macro("TRANSACTION")
13
14// DO NOT INCLUDE TRANSACTOR HEADER FILES HERE.
15// See the instructions at the top of transactions.macro instead.
16
17#include <xrpl/protocol/TxFormats.h>
18
19#include <stdexcept>
20
21namespace xrpl {
22
23namespace {
24
25struct UnknownTxnType : std::exception
26{
27 TxType txnType;
28 UnknownTxnType(TxType t) : txnType{t}
29 {
30 }
31};
32
33// Call a lambda with the concrete transaction type as a template parameter
34// throw an "UnknownTxnType" exception on error
35template <class F>
36auto
37with_txn_type(Rules const& rules, TxType txnType, F&& f)
38{
39 // These global updates really should have been for every Transaction
40 // step: preflight, preclaim, calculateBaseFee, and doApply. Unfortunately,
41 // they were only included in doApply (via Transactor::operator()). That may
42 // have been sufficient when the changes were only related to operations
43 // that mutated data, but some features will now change how they read data,
44 // so these need to be more global.
45 //
46 // To prevent unintentional side effects on existing checks, they will be
47 // set for every operation only once SingleAssetVault (or later
48 // LendingProtocol) are enabled.
49 //
50 // See also Transactor::operator().
51 //
52 std::optional<NumberSO> stNumberSO;
55 if (rules.enabled(featureSingleAssetVault) ||
56 rules.enabled(featureLendingProtocol))
57 {
58 // raii classes for the current ledger rules.
59 // fixUniversalNumber predates the rulesGuard and should be replaced.
60 stNumberSO.emplace(rules.enabled(fixUniversalNumber));
61 rulesGuard.emplace(rules);
62 }
63 else
64 {
65 // Without those features enabled, always use the old number rules.
66 mantissaScaleGuard.emplace(MantissaRange::small);
67 }
68
69 switch (txnType)
70 {
71#pragma push_macro("TRANSACTION")
72#undef TRANSACTION
73
74#define TRANSACTION(tag, value, name, ...) \
75 case tag: \
76 return f.template operator()<name>();
77
78#include <xrpl/protocol/detail/transactions.macro>
79
80#undef TRANSACTION
81#pragma pop_macro("TRANSACTION")
82 default:
83 throw UnknownTxnType(txnType);
84 }
85}
86} // namespace
87
88// Templates so preflight does the right thing with T::ConsequencesFactory.
89//
90// This could be done more easily using if constexpr, but Visual Studio
91// 2017 doesn't handle if constexpr correctly. So once we're no longer
92// building with Visual Studio 2017 we can consider replacing the four
93// templates with a single template function that uses if constexpr.
94//
95// For Transactor::Normal
96//
97
98// clang-format off
99// Current formatter for rippled is based on clang-10, which does not handle `requires` clauses
100template <class T>
101requires(T::ConsequencesFactory == Transactor::Normal)
102TxConsequences
104{
105 return TxConsequences(ctx.tx);
106};
107
108// For Transactor::Blocker
109template <class T>
110requires(T::ConsequencesFactory == Transactor::Blocker)
111TxConsequences
116
117// For Transactor::Custom
118template <class T>
119requires(T::ConsequencesFactory == Transactor::Custom)
120TxConsequences
122{
123 return T::makeTxConsequences(ctx);
124};
125// clang-format on
126
129{
130 try
131 {
132 return with_txn_type(ctx.rules, ctx.tx.getTxnType(), [&]<typename T>() {
133 auto const tec = Transactor::invokePreflight<T>(ctx);
134 return std::make_pair(
135 tec,
136 isTesSuccess(tec) ? consequences_helper<T>(ctx)
137 : TxConsequences{tec});
138 });
139 }
140 catch (UnknownTxnType const& e)
141 {
142 // Should never happen
143 // LCOV_EXCL_START
144 JLOG(ctx.j.fatal())
145 << "Unknown transaction type in preflight: " << e.txnType;
146 UNREACHABLE("xrpl::invoke_preflight : unknown transaction type");
147 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
148 // LCOV_EXCL_STOP
149 }
150}
151
152static TER
154{
155 try
156 {
157 // use name hiding to accomplish compile-time polymorphism of static
158 // class functions for Transactor and derived classes.
159 return with_txn_type(
160 ctx.view.rules(), ctx.tx.getTxnType(), [&]<typename T>() -> TER {
161 // preclaim functionality is divided into two sections:
162 // 1. Up to and including the signature check: returns NotTEC.
163 // All transaction checks before and including checkSign
164 // MUST return NotTEC, or something more restrictive.
165 // Allowing tec results in these steps risks theft or
166 // destruction of funds, as a fee will be charged before the
167 // signature is checked.
168 // 2. After the signature check: returns TER.
169
170 // If the transactor requires a valid account and the
171 // transaction doesn't list one, preflight will have already
172 // a flagged a failure.
173 auto const id = ctx.tx.getAccountID(sfAccount);
174
175 if (id != beast::zero)
176 {
177 if (NotTEC const preSigResult = [&]() -> NotTEC {
178 if (NotTEC const result =
179 T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
180 return result;
181
182 if (NotTEC const result =
183 T::checkPriorTxAndLastLedger(ctx))
184 return result;
185
186 if (NotTEC const result =
187 T::checkPermission(ctx.view, ctx.tx))
188 return result;
189
190 if (NotTEC const result = T::checkSign(ctx))
191 return result;
192
193 return tesSUCCESS;
194 }())
195 return preSigResult;
196
197 if (TER const result = T::checkFee(
198 ctx, calculateBaseFee(ctx.view, ctx.tx)))
199 return result;
200 }
201
202 return T::preclaim(ctx);
203 });
204 }
205 catch (UnknownTxnType const& e)
206 {
207 // Should never happen
208 // LCOV_EXCL_START
209 JLOG(ctx.j.fatal())
210 << "Unknown transaction type in preclaim: " << e.txnType;
211 UNREACHABLE("xrpl::invoke_preclaim : unknown transaction type");
212 return temUNKNOWN;
213 // LCOV_EXCL_STOP
214 }
215}
216
233static XRPAmount
234invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
235{
236 try
237 {
238 return with_txn_type(view.rules(), tx.getTxnType(), [&]<typename T>() {
239 return T::calculateBaseFee(view, tx);
240 });
241 }
242 catch (UnknownTxnType const& e)
243 {
244 // LCOV_EXCL_START
245 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
246 return XRPAmount{0};
247 // LCOV_EXCL_STOP
248 }
249}
250
251TxConsequences::TxConsequences(NotTEC pfResult)
252 : isBlocker_(false)
253 , fee_(beast::zero)
254 , potentialSpend_(beast::zero)
255 , seqProx_(SeqProxy::sequence(0))
256 , sequencesConsumed_(0)
257{
258 XRPL_ASSERT(
259 !isTesSuccess(pfResult),
260 "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
261}
262
264 : isBlocker_(false)
265 , fee_(
266 tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp()
267 : beast::zero)
268 , potentialSpend_(beast::zero)
269 , seqProx_(tx.getSeqProxy())
270 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
271{
272}
273
275 : TxConsequences(tx)
276{
277 isBlocker_ = (category == blocker);
278}
279
281 : TxConsequences(tx)
282{
284}
285
291
292static ApplyResult
294{
295 try
296 {
297 return with_txn_type(
298 ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
299 T p(ctx);
300 return p();
301 });
302 }
303 catch (UnknownTxnType const& e)
304 {
305 // Should never happen
306 // LCOV_EXCL_START
307 JLOG(ctx.journal.fatal())
308 << "Unknown transaction type in apply: " << e.txnType;
309 UNREACHABLE("xrpl::invoke_apply : unknown transaction type");
310 return {temUNKNOWN, false};
311 // LCOV_EXCL_STOP
312 }
313}
314
315PreflightResult
317 Application& app,
318 Rules const& rules,
319 STTx const& tx,
320 ApplyFlags flags,
322{
323 PreflightContext const pfCtx(app, tx, rules, flags, j);
324 try
325 {
326 return {pfCtx, invoke_preflight(pfCtx)};
327 }
328 catch (std::exception const& e)
329 {
330 JLOG(j.fatal()) << "apply (preflight): " << e.what();
331 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
332 }
333}
334
335PreflightResult
337 Application& app,
338 Rules const& rules,
339 uint256 const& parentBatchId,
340 STTx const& tx,
341 ApplyFlags flags,
343{
344 PreflightContext const pfCtx(app, tx, parentBatchId, rules, flags, j);
345 try
346 {
347 return {pfCtx, invoke_preflight(pfCtx)};
348 }
349 catch (std::exception const& e)
350 {
351 JLOG(j.fatal()) << "apply (preflight): " << e.what();
352 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
353 }
354}
355
356PreclaimResult
358 PreflightResult const& preflightResult,
359 Application& app,
360 OpenView const& view)
361{
363 if (preflightResult.rules != view.rules())
364 {
365 auto secondFlight = [&]() {
366 if (preflightResult.parentBatchId)
367 return preflight(
368 app,
369 view.rules(),
370 preflightResult.parentBatchId.value(),
371 preflightResult.tx,
372 preflightResult.flags,
373 preflightResult.j);
374
375 return preflight(
376 app,
377 view.rules(),
378 preflightResult.tx,
379 preflightResult.flags,
380 preflightResult.j);
381 }();
382
383 ctx.emplace(
384 app,
385 view,
386 secondFlight.ter,
387 secondFlight.tx,
388 secondFlight.flags,
389 secondFlight.parentBatchId,
390 secondFlight.j);
391 }
392 else
393 {
394 ctx.emplace(
395 app,
396 view,
397 preflightResult.ter,
398 preflightResult.tx,
399 preflightResult.flags,
400 preflightResult.parentBatchId,
401 preflightResult.j);
402 }
403
404 try
405 {
406 if (ctx->preflightResult != tesSUCCESS)
407 return {*ctx, ctx->preflightResult};
408 return {*ctx, invoke_preclaim(*ctx)};
409 }
410 catch (std::exception const& e)
411 {
412 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
413 return {*ctx, tefEXCEPTION};
414 }
415}
416
417XRPAmount
418calculateBaseFee(ReadView const& view, STTx const& tx)
419{
420 return invoke_calculateBaseFee(view, tx);
421}
422
423XRPAmount
424calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
425{
426 return Transactor::calculateBaseFee(view, tx);
427}
428
429ApplyResult
430doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view)
431{
432 if (preclaimResult.view.seq() != view.seq())
433 {
434 // Logic error from the caller. Don't have enough
435 // info to recover.
436 return {tefEXCEPTION, false};
437 }
438 try
439 {
440 if (!preclaimResult.likelyToClaimFee)
441 return {preclaimResult.ter, false};
442 ApplyContext ctx(
443 app,
444 view,
445 preclaimResult.parentBatchId,
446 preclaimResult.tx,
447 preclaimResult.ter,
448 calculateBaseFee(view, preclaimResult.tx),
449 preclaimResult.flags,
450 preclaimResult.j);
451 return invoke_apply(ctx);
452 }
453 catch (std::exception const& e)
454 {
455 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
456 return {tefEXCEPTION, false};
457 }
458}
459
460} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:41
Stream fatal() const
Definition Journal.h:333
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:46
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:131
A view into a ledger.
Definition ReadView.h:32
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:99
Rules controlling protocol behavior.
Definition Rules.h:19
TxType getTxnType() const
Definition STTx.h:187
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:37
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:39
TxConsequences(NotTEC pfResult)
std::uint32_t sequencesConsumed_
Number of sequences consumed.
Definition applySteps.h:62
Category
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:43
@ blocker
Affects the ability of subsequent transactions to claim a fee.
Definition applySteps.h:48
XRPAmount potentialSpend_
Does NOT include the fee.
Definition applySteps.h:58
bool isBlocker_
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:54
std::uint32_t sequencesConsumed() const
Sequences consumed.
Definition applySteps.h:115
XRPAmount const & potentialSpend() const
Potential Spend.
Definition applySteps.h:101
T emplace(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
static XRPAmount invoke_calculateBaseFee(ReadView const &view, STTx const &tx)
Calculates the base fee for a given transaction.
TxType
Transaction type identifiers.
Definition TxFormats.h:38
static ApplyResult invoke_apply(ApplyContext &ctx)
@ tefEXCEPTION
Definition TER.h:153
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
static std::pair< NotTEC, TxConsequences > invoke_preflight(PreflightContext const &ctx)
static TER invoke_preclaim(PreclaimContext const &ctx)
TERSubset< CanCvtToTER > TER
Definition TER.h:630
TxConsequences consequences_helper(PreflightContext const &ctx)
ApplyResult doApply(PreclaimResult const &preclaimResult, Application &app, OpenView &view)
Apply a prechecked transaction to an OpenView.
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
ApplyFlags
Definition ApplyView.h:11
@ temUNKNOWN
Definition TER.h:105
bool isTesSuccess(TER x) noexcept
Definition TER.h:659
PreclaimResult preclaim(PreflightResult const &preflightResult, Application &app, OpenView const &view)
Gate a transaction based on static ledger information.
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
@ tesSUCCESS
Definition TER.h:226
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
Describes the results of the preclaim check.
Definition applySteps.h:190
ReadView const & view
From the input - the ledger view.
Definition applySteps.h:193
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:197
TER const ter
Intermediate transaction result.
Definition applySteps.h:204
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:199
STTx const & tx
From the input - the transaction.
Definition applySteps.h:195
beast::Journal const j
From the input - the journal.
Definition applySteps.h:201
bool const likelyToClaimFee
Success flag - whether the transaction is likely to claim a fee.
Definition applySteps.h:208
State information when preflighting a tx.
Definition Transactor.h:16
beast::Journal const j
Definition Transactor.h:23
Describes the results of the preflight check.
Definition applySteps.h:144
beast::Journal const j
From the input - the journal.
Definition applySteps.h:157
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:155
Rules const rules
From the input - the rules.
Definition applySteps.h:151
NotTEC const ter
Intermediate transaction result.
Definition applySteps.h:160
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:149
STTx const & tx
From the input - the transaction.
Definition applySteps.h:147
T what(T... args)