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(TxType txnType, F&& f)
38{
39 switch (txnType)
40 {
41#pragma push_macro("TRANSACTION")
42#undef TRANSACTION
43
44#define TRANSACTION(tag, value, name, ...) \
45 case tag: \
46 return f.template operator()<name>();
47
48#include <xrpl/protocol/detail/transactions.macro>
49
50#undef TRANSACTION
51#pragma pop_macro("TRANSACTION")
52 default:
53 throw UnknownTxnType(txnType);
54 }
55}
56} // namespace
57
58// Templates so preflight does the right thing with T::ConsequencesFactory.
59//
60// This could be done more easily using if constexpr, but Visual Studio
61// 2017 doesn't handle if constexpr correctly. So once we're no longer
62// building with Visual Studio 2017 we can consider replacing the four
63// templates with a single template function that uses if constexpr.
64//
65// For Transactor::Normal
66//
67
68// clang-format off
69// Current formatter for rippled is based on clang-10, which does not handle `requires` clauses
70template <class T>
71requires(T::ConsequencesFactory == Transactor::Normal)
72TxConsequences
74{
75 return TxConsequences(ctx.tx);
76};
77
78// For Transactor::Blocker
79template <class T>
80requires(T::ConsequencesFactory == Transactor::Blocker)
81TxConsequences
86
87// For Transactor::Custom
88template <class T>
89requires(T::ConsequencesFactory == Transactor::Custom)
90TxConsequences
92{
93 return T::makeTxConsequences(ctx);
94};
95// clang-format on
96
99{
100 try
101 {
102 return with_txn_type(ctx.tx.getTxnType(), [&]<typename T>() {
103 auto const tec = Transactor::invokePreflight<T>(ctx);
104 return std::make_pair(
105 tec,
106 isTesSuccess(tec) ? consequences_helper<T>(ctx)
107 : TxConsequences{tec});
108 });
109 }
110 catch (UnknownTxnType const& e)
111 {
112 // Should never happen
113 // LCOV_EXCL_START
114 JLOG(ctx.j.fatal())
115 << "Unknown transaction type in preflight: " << e.txnType;
116 UNREACHABLE("xrpl::invoke_preflight : unknown transaction type");
117 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
118 // LCOV_EXCL_STOP
119 }
120}
121
122static TER
124{
125 try
126 {
127 // use name hiding to accomplish compile-time polymorphism of static
128 // class functions for Transactor and derived classes.
129 return with_txn_type(ctx.tx.getTxnType(), [&]<typename T>() -> TER {
130 // preclaim functionality is divided into two sections:
131 // 1. Up to and including the signature check: returns NotTEC.
132 // All transaction checks before and including checkSign
133 // MUST return NotTEC, or something more restrictive.
134 // Allowing tec results in these steps risks theft or
135 // destruction of funds, as a fee will be charged before the
136 // signature is checked.
137 // 2. After the signature check: returns TER.
138
139 // If the transactor requires a valid account and the
140 // transaction doesn't list one, preflight will have already
141 // a flagged a failure.
142 auto const id = ctx.tx.getAccountID(sfAccount);
143
144 if (id != beast::zero)
145 {
146 if (NotTEC const preSigResult = [&]() -> NotTEC {
147 if (NotTEC const result =
148 T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
149 return result;
150
151 if (NotTEC const result =
152 T::checkPriorTxAndLastLedger(ctx))
153 return result;
154
155 if (NotTEC const result =
156 T::checkPermission(ctx.view, ctx.tx))
157 return result;
158
159 if (NotTEC const result = T::checkSign(ctx))
160 return result;
161
162 return tesSUCCESS;
163 }())
164 return preSigResult;
165
166 if (TER const result =
167 T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)))
168 return result;
169 }
170
171 return T::preclaim(ctx);
172 });
173 }
174 catch (UnknownTxnType const& e)
175 {
176 // Should never happen
177 // LCOV_EXCL_START
178 JLOG(ctx.j.fatal())
179 << "Unknown transaction type in preclaim: " << e.txnType;
180 UNREACHABLE("xrpl::invoke_preclaim : unknown transaction type");
181 return temUNKNOWN;
182 // LCOV_EXCL_STOP
183 }
184}
185
202static XRPAmount
203invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
204{
205 try
206 {
207 return with_txn_type(tx.getTxnType(), [&]<typename T>() {
208 return T::calculateBaseFee(view, tx);
209 });
210 }
211 catch (UnknownTxnType const& e)
212 {
213 // LCOV_EXCL_START
214 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
215 return XRPAmount{0};
216 // LCOV_EXCL_STOP
217 }
218}
219
220TxConsequences::TxConsequences(NotTEC pfResult)
221 : isBlocker_(false)
222 , fee_(beast::zero)
223 , potentialSpend_(beast::zero)
224 , seqProx_(SeqProxy::sequence(0))
225 , sequencesConsumed_(0)
226{
227 XRPL_ASSERT(
228 !isTesSuccess(pfResult),
229 "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
230}
231
233 : isBlocker_(false)
234 , fee_(
235 tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp()
236 : beast::zero)
237 , potentialSpend_(beast::zero)
238 , seqProx_(tx.getSeqProxy())
239 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
240{
241}
242
244 : TxConsequences(tx)
245{
246 isBlocker_ = (category == blocker);
247}
248
250 : TxConsequences(tx)
251{
253}
254
260
261static ApplyResult
263{
264 try
265 {
266 return with_txn_type(ctx.tx.getTxnType(), [&]<typename T>() {
267 T p(ctx);
268 return p();
269 });
270 }
271 catch (UnknownTxnType const& e)
272 {
273 // Should never happen
274 // LCOV_EXCL_START
275 JLOG(ctx.journal.fatal())
276 << "Unknown transaction type in apply: " << e.txnType;
277 UNREACHABLE("xrpl::invoke_apply : unknown transaction type");
278 return {temUNKNOWN, false};
279 // LCOV_EXCL_STOP
280 }
281}
282
283PreflightResult
285 Application& app,
286 Rules const& rules,
287 STTx const& tx,
288 ApplyFlags flags,
290{
291 PreflightContext const pfCtx(app, tx, rules, flags, j);
292 try
293 {
294 return {pfCtx, invoke_preflight(pfCtx)};
295 }
296 catch (std::exception const& e)
297 {
298 JLOG(j.fatal()) << "apply (preflight): " << e.what();
299 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
300 }
301}
302
303PreflightResult
305 Application& app,
306 Rules const& rules,
307 uint256 const& parentBatchId,
308 STTx const& tx,
309 ApplyFlags flags,
311{
312 PreflightContext const pfCtx(app, tx, parentBatchId, rules, flags, j);
313 try
314 {
315 return {pfCtx, invoke_preflight(pfCtx)};
316 }
317 catch (std::exception const& e)
318 {
319 JLOG(j.fatal()) << "apply (preflight): " << e.what();
320 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
321 }
322}
323
324PreclaimResult
326 PreflightResult const& preflightResult,
327 Application& app,
328 OpenView const& view)
329{
331 if (preflightResult.rules != view.rules())
332 {
333 auto secondFlight = [&]() {
334 if (preflightResult.parentBatchId)
335 return preflight(
336 app,
337 view.rules(),
338 preflightResult.parentBatchId.value(),
339 preflightResult.tx,
340 preflightResult.flags,
341 preflightResult.j);
342
343 return preflight(
344 app,
345 view.rules(),
346 preflightResult.tx,
347 preflightResult.flags,
348 preflightResult.j);
349 }();
350
351 ctx.emplace(
352 app,
353 view,
354 secondFlight.ter,
355 secondFlight.tx,
356 secondFlight.flags,
357 secondFlight.parentBatchId,
358 secondFlight.j);
359 }
360 else
361 {
362 ctx.emplace(
363 app,
364 view,
365 preflightResult.ter,
366 preflightResult.tx,
367 preflightResult.flags,
368 preflightResult.parentBatchId,
369 preflightResult.j);
370 }
371
372 try
373 {
374 if (ctx->preflightResult != tesSUCCESS)
375 return {*ctx, ctx->preflightResult};
376 return {*ctx, invoke_preclaim(*ctx)};
377 }
378 catch (std::exception const& e)
379 {
380 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
381 return {*ctx, tefEXCEPTION};
382 }
383}
384
385XRPAmount
386calculateBaseFee(ReadView const& view, STTx const& tx)
387{
388 return invoke_calculateBaseFee(view, tx);
389}
390
391XRPAmount
392calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
393{
394 return Transactor::calculateBaseFee(view, tx);
395}
396
397ApplyResult
398doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view)
399{
400 if (preclaimResult.view.seq() != view.seq())
401 {
402 // Logic error from the caller. Don't have enough
403 // info to recover.
404 return {tefEXCEPTION, false};
405 }
406 try
407 {
408 if (!preclaimResult.likelyToClaimFee)
409 return {preclaimResult.ter, false};
410 ApplyContext ctx(
411 app,
412 view,
413 preclaimResult.parentBatchId,
414 preclaimResult.tx,
415 preclaimResult.ter,
416 calculateBaseFee(view, preclaimResult.tx),
417 preclaimResult.flags,
418 preclaimResult.j);
419 return invoke_apply(ctx);
420 }
421 catch (std::exception const& e)
422 {
423 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
424 return {tefEXCEPTION, false};
425 }
426}
427
428} // 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
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
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
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)