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) || rules.enabled(featureLendingProtocol))
56 {
57 // raii classes for the current ledger rules.
58 // fixUniversalNumber predates the rulesGuard and should be replaced.
59 stNumberSO.emplace(rules.enabled(fixUniversalNumber));
60 rulesGuard.emplace(rules);
61 }
62 else
63 {
64 // Without those features enabled, always use the old number rules.
65 mantissaScaleGuard.emplace(MantissaRange::small);
66 }
67
68 switch (txnType)
69 {
70#pragma push_macro("TRANSACTION")
71#undef TRANSACTION
72
73#define TRANSACTION(tag, value, name, ...) \
74 case tag: \
75 return f.template operator()<name>();
76
77#include <xrpl/protocol/detail/transactions.macro>
78
79#undef TRANSACTION
80#pragma pop_macro("TRANSACTION")
81 default:
82 throw UnknownTxnType(txnType);
83 }
84}
85} // namespace
86
87// Templates so preflight does the right thing with T::ConsequencesFactory.
88//
89// This could be done more easily using if constexpr, but Visual Studio
90// 2017 doesn't handle if constexpr correctly. So once we're no longer
91// building with Visual Studio 2017 we can consider replacing the four
92// templates with a single template function that uses if constexpr.
93//
94// For Transactor::Normal
95//
96
97// clang-format off
98// Current formatter for rippled is based on clang-10, which does not handle `requires` clauses
99template <class T>
100requires(T::ConsequencesFactory == Transactor::Normal)
101TxConsequences
103{
104 return TxConsequences(ctx.tx);
105};
106
107// For Transactor::Blocker
108template <class T>
109requires(T::ConsequencesFactory == Transactor::Blocker)
110TxConsequences
115
116// For Transactor::Custom
117template <class T>
118requires(T::ConsequencesFactory == Transactor::Custom)
119TxConsequences
121{
122 return T::makeTxConsequences(ctx);
123};
124// clang-format on
125
128{
129 try
130 {
131 return with_txn_type(ctx.rules, ctx.tx.getTxnType(), [&]<typename T>() {
132 auto const tec = Transactor::invokePreflight<T>(ctx);
133 return std::make_pair(tec, isTesSuccess(tec) ? consequences_helper<T>(ctx) : TxConsequences{tec});
134 });
135 }
136 catch (UnknownTxnType const& e)
137 {
138 // Should never happen
139 // LCOV_EXCL_START
140 JLOG(ctx.j.fatal()) << "Unknown transaction type in preflight: " << e.txnType;
141 UNREACHABLE("xrpl::invoke_preflight : unknown transaction type");
142 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
143 // LCOV_EXCL_STOP
144 }
145}
146
147static TER
149{
150 try
151 {
152 // use name hiding to accomplish compile-time polymorphism of static
153 // class functions for Transactor and derived classes.
154 return with_txn_type(ctx.view.rules(), ctx.tx.getTxnType(), [&]<typename T>() -> TER {
155 // preclaim functionality is divided into two sections:
156 // 1. Up to and including the signature check: returns NotTEC.
157 // All transaction checks before and including checkSign
158 // MUST return NotTEC, or something more restrictive.
159 // Allowing tec results in these steps risks theft or
160 // destruction of funds, as a fee will be charged before the
161 // signature is checked.
162 // 2. After the signature check: returns TER.
163
164 // If the transactor requires a valid account and the
165 // transaction doesn't list one, preflight will have already
166 // a flagged a failure.
167 auto const id = ctx.tx.getAccountID(sfAccount);
168
169 if (id != beast::zero)
170 {
171 if (NotTEC const preSigResult = [&]() -> NotTEC {
172 if (NotTEC const result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
173 return result;
174
175 if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
176 return result;
177
178 if (NotTEC const result = T::checkPermission(ctx.view, ctx.tx))
179 return result;
180
181 if (NotTEC const result = T::checkSign(ctx))
182 return result;
183
184 return tesSUCCESS;
185 }())
186 return preSigResult;
187
188 if (TER const result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)))
189 return result;
190 }
191
192 return T::preclaim(ctx);
193 });
194 }
195 catch (UnknownTxnType const& e)
196 {
197 // Should never happen
198 // LCOV_EXCL_START
199 JLOG(ctx.j.fatal()) << "Unknown transaction type in preclaim: " << e.txnType;
200 UNREACHABLE("xrpl::invoke_preclaim : unknown transaction type");
201 return temUNKNOWN;
202 // LCOV_EXCL_STOP
203 }
204}
205
222static XRPAmount
223invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
224{
225 try
226 {
227 return with_txn_type(
228 view.rules(), tx.getTxnType(), [&]<typename T>() { return T::calculateBaseFee(view, tx); });
229 }
230 catch (UnknownTxnType const& e)
231 {
232 // LCOV_EXCL_START
233 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
234 return XRPAmount{0};
235 // LCOV_EXCL_STOP
236 }
237}
238
239TxConsequences::TxConsequences(NotTEC pfResult)
240 : isBlocker_(false)
241 , fee_(beast::zero)
242 , potentialSpend_(beast::zero)
243 , seqProx_(SeqProxy::sequence(0))
244 , sequencesConsumed_(0)
245{
246 XRPL_ASSERT(!isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
247}
248
250 : isBlocker_(false)
251 , fee_(tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() : beast::zero)
252 , potentialSpend_(beast::zero)
253 , seqProx_(tx.getSeqProxy())
254 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
255{
256}
257
259{
260 isBlocker_ = (category == blocker);
261}
262
267
272
273static ApplyResult
275{
276 try
277 {
278 return with_txn_type(ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
279 T p(ctx);
280 return p();
281 });
282 }
283 catch (UnknownTxnType const& e)
284 {
285 // Should never happen
286 // LCOV_EXCL_START
287 JLOG(ctx.journal.fatal()) << "Unknown transaction type in apply: " << e.txnType;
288 UNREACHABLE("xrpl::invoke_apply : unknown transaction type");
289 return {temUNKNOWN, false};
290 // LCOV_EXCL_STOP
291 }
292}
293
294PreflightResult
295preflight(Application& app, Rules const& rules, STTx const& tx, ApplyFlags flags, beast::Journal j)
296{
297 PreflightContext const pfCtx(app, tx, rules, flags, j);
298 try
299 {
300 return {pfCtx, invoke_preflight(pfCtx)};
301 }
302 catch (std::exception const& e)
303 {
304 JLOG(j.fatal()) << "apply (preflight): " << e.what();
305 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
306 }
307}
308
309PreflightResult
311 Application& app,
312 Rules const& rules,
313 uint256 const& parentBatchId,
314 STTx const& tx,
315 ApplyFlags flags,
317{
318 PreflightContext const pfCtx(app, tx, parentBatchId, rules, flags, j);
319 try
320 {
321 return {pfCtx, invoke_preflight(pfCtx)};
322 }
323 catch (std::exception const& e)
324 {
325 JLOG(j.fatal()) << "apply (preflight): " << e.what();
326 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
327 }
328}
329
330PreclaimResult
331preclaim(PreflightResult const& preflightResult, Application& app, OpenView const& view)
332{
334 if (preflightResult.rules != view.rules())
335 {
336 auto secondFlight = [&]() {
337 if (preflightResult.parentBatchId)
338 return preflight(
339 app,
340 view.rules(),
341 preflightResult.parentBatchId.value(),
342 preflightResult.tx,
343 preflightResult.flags,
344 preflightResult.j);
345
346 return preflight(app, view.rules(), preflightResult.tx, preflightResult.flags, preflightResult.j);
347 }();
348
349 ctx.emplace(
350 app,
351 view,
352 secondFlight.ter,
353 secondFlight.tx,
354 secondFlight.flags,
355 secondFlight.parentBatchId,
356 secondFlight.j);
357 }
358 else
359 {
360 ctx.emplace(
361 app,
362 view,
363 preflightResult.ter,
364 preflightResult.tx,
365 preflightResult.flags,
366 preflightResult.parentBatchId,
367 preflightResult.j);
368 }
369
370 try
371 {
372 if (ctx->preflightResult != tesSUCCESS)
373 return {*ctx, ctx->preflightResult};
374 return {*ctx, invoke_preclaim(*ctx)};
375 }
376 catch (std::exception const& e)
377 {
378 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
379 return {*ctx, tefEXCEPTION};
380 }
381}
382
383XRPAmount
384calculateBaseFee(ReadView const& view, STTx const& tx)
385{
386 return invoke_calculateBaseFee(view, tx);
387}
388
389XRPAmount
390calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
391{
392 return Transactor::calculateBaseFee(view, tx);
393}
394
395ApplyResult
396doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view)
397{
398 if (preclaimResult.view.seq() != view.seq())
399 {
400 // Logic error from the caller. Don't have enough
401 // info to recover.
402 return {tefEXCEPTION, false};
403 }
404 try
405 {
406 if (!preclaimResult.likelyToClaimFee)
407 return {preclaimResult.ter, false};
408 ApplyContext ctx(
409 app,
410 view,
411 preclaimResult.parentBatchId,
412 preclaimResult.tx,
413 preclaimResult.ter,
414 calculateBaseFee(view, preclaimResult.tx),
415 preclaimResult.flags,
416 preclaimResult.j);
417 return invoke_apply(ctx);
418 }
419 catch (std::exception const& e)
420 {
421 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
422 return {tefEXCEPTION, false};
423 }
424}
425
426} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:324
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:45
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:123
A view into a ledger.
Definition ReadView.h:31
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:97
Rules controlling protocol behavior.
Definition Rules.h:18
TxType getTxnType() const
Definition STTx.h:180
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:36
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:37
TxConsequences(NotTEC pfResult)
std::uint32_t sequencesConsumed_
Number of sequences consumed.
Definition applySteps.h:60
Category
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:41
@ blocker
Affects the ability of subsequent transactions to claim a fee.
Definition applySteps.h:46
XRPAmount potentialSpend_
Does NOT include the fee.
Definition applySteps.h:56
bool isBlocker_
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:52
std::uint32_t sequencesConsumed() const
Sequences consumed.
Definition applySteps.h:113
XRPAmount const & potentialSpend() const
Potential Spend.
Definition applySteps.h:99
T emplace(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
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:37
static ApplyResult invoke_apply(ApplyContext &ctx)
@ tefEXCEPTION
Definition TER.h:152
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:620
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:10
@ temUNKNOWN
Definition TER.h:104
bool isTesSuccess(TER x) noexcept
Definition TER.h:649
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:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
Describes the results of the preclaim check.
Definition applySteps.h:186
ReadView const & view
From the input - the ledger view.
Definition applySteps.h:189
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:193
TER const ter
Intermediate transaction result.
Definition applySteps.h:200
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:195
STTx const & tx
From the input - the transaction.
Definition applySteps.h:191
beast::Journal const j
From the input - the journal.
Definition applySteps.h:197
bool const likelyToClaimFee
Success flag - whether the transaction is likely to claim a fee.
Definition applySteps.h:204
State information when preflighting a tx.
Definition Transactor.h:15
beast::Journal const j
Definition Transactor.h:22
Describes the results of the preflight check.
Definition applySteps.h:142
beast::Journal const j
From the input - the journal.
Definition applySteps.h:155
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:153
Rules const rules
From the input - the rules.
Definition applySteps.h:149
NotTEC const ter
Intermediate transaction result.
Definition applySteps.h:158
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:147
STTx const & tx
From the input - the transaction.
Definition applySteps.h:145
T what(T... args)