rippled
Loading...
Searching...
No Matches
applySteps.cpp
1#include <xrpl/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/core/ServiceRegistry.h>
18#include <xrpl/protocol/TxFormats.h>
19
20#include <stdexcept>
21
22namespace xrpl {
23
24namespace {
25
26struct UnknownTxnType : std::exception
27{
28 TxType txnType;
29 UnknownTxnType(TxType t) : txnType{t}
30 {
31 }
32};
33
34// Call a lambda with the concrete transaction type as a template parameter
35// throw an "UnknownTxnType" exception on error
36template <class F>
37auto
38with_txn_type(Rules const& rules, TxType txnType, F&& f)
39{
40 // These global updates really should have been for every Transaction
41 // step: preflight, preclaim, calculateBaseFee, and doApply. Unfortunately,
42 // they were only included in doApply (via Transactor::operator()). That may
43 // have been sufficient when the changes were only related to operations
44 // that mutated data, but some features will now change how they read data,
45 // so these need to be more global.
46 //
47 // To prevent unintentional side effects on existing checks, they will be
48 // set for every operation only once SingleAssetVault (or later
49 // LendingProtocol) are enabled.
50 //
51 // See also Transactor::operator().
52 //
53 std::optional<NumberSO> stNumberSO;
56 if (rules.enabled(featureSingleAssetVault) || 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, isTesSuccess(tec) ? consequences_helper<T>(ctx) : TxConsequences{tec});
136 });
137 }
138 catch (UnknownTxnType const& e)
139 {
140 // Should never happen
141 // LCOV_EXCL_START
142 JLOG(ctx.j.fatal()) << "Unknown transaction type in preflight: " << e.txnType;
143 UNREACHABLE("xrpl::invoke_preflight : unknown transaction type");
144 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
145 // LCOV_EXCL_STOP
146 }
147}
148
149static TER
151{
152 try
153 {
154 // use name hiding to accomplish compile-time polymorphism of static
155 // class functions for Transactor and derived classes.
156 return with_txn_type(ctx.view.rules(), ctx.tx.getTxnType(), [&]<typename T>() -> TER {
157 // preclaim functionality is divided into two sections:
158 // 1. Up to and including the signature check: returns NotTEC.
159 // All transaction checks before and including checkSign
160 // MUST return NotTEC, or something more restrictive.
161 // Allowing tec results in these steps risks theft or
162 // destruction of funds, as a fee will be charged before the
163 // signature is checked.
164 // 2. After the signature check: returns TER.
165
166 // If the transactor requires a valid account and the
167 // transaction doesn't list one, preflight will have already
168 // a flagged a failure.
169 auto const id = ctx.tx.getAccountID(sfAccount);
170
171 if (id != beast::zero)
172 {
173 if (NotTEC const preSigResult = [&]() -> NotTEC {
174 if (NotTEC const result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
175 return result;
176
177 if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
178 return result;
179
180 if (NotTEC const result = T::checkPermission(ctx.view, ctx.tx))
181 return result;
182
183 if (NotTEC const result = T::checkSign(ctx))
184 return result;
185
186 return tesSUCCESS;
187 }())
188 return preSigResult;
189
190 if (TER const result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)))
191 return result;
192 }
193
194 return T::preclaim(ctx);
195 });
196 }
197 catch (UnknownTxnType const& e)
198 {
199 // Should never happen
200 // LCOV_EXCL_START
201 JLOG(ctx.j.fatal()) << "Unknown transaction type in preclaim: " << e.txnType;
202 UNREACHABLE("xrpl::invoke_preclaim : unknown transaction type");
203 return temUNKNOWN;
204 // LCOV_EXCL_STOP
205 }
206}
207
224static XRPAmount
225invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
226{
227 try
228 {
229 return with_txn_type(view.rules(), tx.getTxnType(), [&]<typename T>() {
230 return T::calculateBaseFee(view, tx);
231 });
232 }
233 catch (UnknownTxnType const& e)
234 {
235 // LCOV_EXCL_START
236 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
237 return XRPAmount{0};
238 // LCOV_EXCL_STOP
239 }
240}
241
242TxConsequences::TxConsequences(NotTEC pfResult)
243 : isBlocker_(false)
244 , fee_(beast::zero)
245 , potentialSpend_(beast::zero)
246 , seqProx_(SeqProxy::sequence(0))
247 , sequencesConsumed_(0)
248{
249 XRPL_ASSERT(
250 !isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
251}
252
254 : isBlocker_(false)
255 , fee_(tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() : beast::zero)
256 , potentialSpend_(beast::zero)
257 , seqProx_(tx.getSeqProxy())
258 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
259{
260}
261
263{
264 isBlocker_ = (category == blocker);
265}
266
271
276
277static ApplyResult
279{
280 try
281 {
282 return with_txn_type(ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
283 T p(ctx);
284 return p();
285 });
286 }
287 catch (UnknownTxnType const& e)
288 {
289 // Should never happen
290 // LCOV_EXCL_START
291 JLOG(ctx.journal.fatal()) << "Unknown transaction type in apply: " << e.txnType;
292 UNREACHABLE("xrpl::invoke_apply : unknown transaction type");
293 return {temUNKNOWN, false};
294 // LCOV_EXCL_STOP
295 }
296}
297
298PreflightResult
300 ServiceRegistry& registry,
301 Rules const& rules,
302 STTx const& tx,
303 ApplyFlags flags,
305{
306 PreflightContext const pfCtx(registry, tx, rules, flags, j);
307 try
308 {
309 return {pfCtx, invoke_preflight(pfCtx)};
310 }
311 catch (std::exception const& e)
312 {
313 JLOG(j.fatal()) << "apply (preflight): " << e.what();
314 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
315 }
316}
317
318PreflightResult
320 ServiceRegistry& registry,
321 Rules const& rules,
322 uint256 const& parentBatchId,
323 STTx const& tx,
324 ApplyFlags flags,
326{
327 PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
328 try
329 {
330 return {pfCtx, invoke_preflight(pfCtx)};
331 }
332 catch (std::exception const& e)
333 {
334 JLOG(j.fatal()) << "apply (preflight): " << e.what();
335 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
336 }
337}
338
339PreclaimResult
340preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view)
341{
343 if (preflightResult.rules != view.rules())
344 {
345 auto secondFlight = [&]() {
346 if (preflightResult.parentBatchId)
347 return preflight(
348 registry,
349 view.rules(),
350 preflightResult.parentBatchId.value(),
351 preflightResult.tx,
352 preflightResult.flags,
353 preflightResult.j);
354
355 return preflight(
356 registry,
357 view.rules(),
358 preflightResult.tx,
359 preflightResult.flags,
360 preflightResult.j);
361 }();
362
363 ctx.emplace(
364 registry,
365 view,
366 secondFlight.ter,
367 secondFlight.tx,
368 secondFlight.flags,
369 secondFlight.parentBatchId,
370 secondFlight.j);
371 }
372 else
373 {
374 ctx.emplace(
375 registry,
376 view,
377 preflightResult.ter,
378 preflightResult.tx,
379 preflightResult.flags,
380 preflightResult.parentBatchId,
381 preflightResult.j);
382 }
383
384 try
385 {
386 if (ctx->preflightResult != tesSUCCESS)
387 return {*ctx, ctx->preflightResult};
388 return {*ctx, invoke_preclaim(*ctx)};
389 }
390 catch (std::exception const& e)
391 {
392 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
393 return {*ctx, tefEXCEPTION};
394 }
395}
396
397XRPAmount
398calculateBaseFee(ReadView const& view, STTx const& tx)
399{
400 return invoke_calculateBaseFee(view, tx);
401}
402
403XRPAmount
404calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
405{
406 return Transactor::calculateBaseFee(view, tx);
407}
408
409ApplyResult
410doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view)
411{
412 if (preclaimResult.view.seq() != view.seq())
413 {
414 // Logic error from the caller. Don't have enough
415 // info to recover.
416 return {tefEXCEPTION, false};
417 }
418 try
419 {
420 if (!preclaimResult.likelyToClaimFee)
421 return {preclaimResult.ter, false};
422 ApplyContext ctx(
423 registry,
424 view,
425 preclaimResult.parentBatchId,
426 preclaimResult.tx,
427 preclaimResult.ter,
428 calculateBaseFee(view, preclaimResult.tx),
429 preclaimResult.flags,
430 preclaimResult.j);
431 return invoke_apply(ctx);
432 }
433 catch (std::exception const& e)
434 {
435 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
436 return {tefEXCEPTION, false};
437 }
438}
439
440} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:325
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:131
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:184
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:36
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:38
TxConsequences(NotTEC pfResult)
std::uint32_t sequencesConsumed_
Number of sequences consumed.
Definition applySteps.h:61
Category
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:42
@ blocker
Affects the ability of subsequent transactions to claim a fee.
Definition applySteps.h:47
XRPAmount potentialSpend_
Does NOT include the fee.
Definition applySteps.h:57
bool isBlocker_
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:53
std::uint32_t sequencesConsumed() const
Sequences consumed.
Definition applySteps.h:114
XRPAmount const & potentialSpend() const
Potential Spend.
Definition applySteps.h:100
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.
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:37
static ApplyResult invoke_apply(ApplyContext &ctx)
PreclaimResult preclaim(PreflightResult const &preflightResult, ServiceRegistry &registry, OpenView const &view)
Gate a transaction based on static ledger information.
@ tefEXCEPTION
Definition TER.h:152
static std::pair< NotTEC, TxConsequences > invoke_preflight(PreflightContext const &ctx)
static TER invoke_preclaim(PreclaimContext const &ctx)
TERSubset< CanCvtToTER > TER
Definition TER.h:622
TxConsequences consequences_helper(PreflightContext const &ctx)
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:651
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
ApplyResult doApply(PreclaimResult const &preclaimResult, ServiceRegistry &registry, OpenView &view)
Apply a prechecked transaction to an OpenView.
@ tesSUCCESS
Definition TER.h:225
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
Describes the results of the preclaim check.
Definition applySteps.h:187
ReadView const & view
From the input - the ledger view.
Definition applySteps.h:190
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:194
TER const ter
Intermediate transaction result.
Definition applySteps.h:201
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:196
STTx const & tx
From the input - the transaction.
Definition applySteps.h:192
beast::Journal const j
From the input - the journal.
Definition applySteps.h:198
bool const likelyToClaimFee
Success flag - whether the transaction is likely to claim a fee.
Definition applySteps.h:205
State information when preflighting a tx.
Definition Transactor.h:14
beast::Journal const j
Definition Transactor.h:21
Describes the results of the preflight check.
Definition applySteps.h:143
beast::Journal const j
From the input - the journal.
Definition applySteps.h:156
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:154
Rules const rules
From the input - the rules.
Definition applySteps.h:150
NotTEC const ter
Intermediate transaction result.
Definition applySteps.h:159
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:148
STTx const & tx
From the input - the transaction.
Definition applySteps.h:146
T what(T... args)