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(tec, isTesSuccess(tec) ? consequences_helper<T>(ctx) : TxConsequences{tec});
135 });
136 }
137 catch (UnknownTxnType const& e)
138 {
139 // Should never happen
140 // LCOV_EXCL_START
141 JLOG(ctx.j.fatal()) << "Unknown transaction type in preflight: " << e.txnType;
142 UNREACHABLE("xrpl::invoke_preflight : unknown transaction type");
143 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
144 // LCOV_EXCL_STOP
145 }
146}
147
148static TER
150{
151 try
152 {
153 // use name hiding to accomplish compile-time polymorphism of static
154 // class functions for Transactor and derived classes.
155 return with_txn_type(ctx.view.rules(), ctx.tx.getTxnType(), [&]<typename T>() -> TER {
156 // preclaim functionality is divided into two sections:
157 // 1. Up to and including the signature check: returns NotTEC.
158 // All transaction checks before and including checkSign
159 // MUST return NotTEC, or something more restrictive.
160 // Allowing tec results in these steps risks theft or
161 // destruction of funds, as a fee will be charged before the
162 // signature is checked.
163 // 2. After the signature check: returns TER.
164
165 // If the transactor requires a valid account and the
166 // transaction doesn't list one, preflight will have already
167 // a flagged a failure.
168 auto const id = ctx.tx.getAccountID(sfAccount);
169
170 if (id != beast::zero)
171 {
172 if (NotTEC const preSigResult = [&]() -> NotTEC {
173 if (NotTEC const result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
174 return result;
175
176 if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
177 return result;
178
179 if (NotTEC const result = T::checkPermission(ctx.view, ctx.tx))
180 return result;
181
182 if (NotTEC const result = T::checkSign(ctx))
183 return result;
184
185 return tesSUCCESS;
186 }())
187 return preSigResult;
188
189 if (TER const result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)))
190 return result;
191 }
192
193 return T::preclaim(ctx);
194 });
195 }
196 catch (UnknownTxnType const& e)
197 {
198 // Should never happen
199 // LCOV_EXCL_START
200 JLOG(ctx.j.fatal()) << "Unknown transaction type in preclaim: " << e.txnType;
201 UNREACHABLE("xrpl::invoke_preclaim : unknown transaction type");
202 return temUNKNOWN;
203 // LCOV_EXCL_STOP
204 }
205}
206
223static XRPAmount
224invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
225{
226 try
227 {
228 return with_txn_type(
229 view.rules(), tx.getTxnType(), [&]<typename T>() { return T::calculateBaseFee(view, tx); });
230 }
231 catch (UnknownTxnType const& e)
232 {
233 // LCOV_EXCL_START
234 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
235 return XRPAmount{0};
236 // LCOV_EXCL_STOP
237 }
238}
239
240TxConsequences::TxConsequences(NotTEC pfResult)
241 : isBlocker_(false)
242 , fee_(beast::zero)
243 , potentialSpend_(beast::zero)
244 , seqProx_(SeqProxy::sequence(0))
245 , sequencesConsumed_(0)
246{
247 XRPL_ASSERT(!isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
248}
249
251 : isBlocker_(false)
252 , fee_(tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() : beast::zero)
253 , potentialSpend_(beast::zero)
254 , seqProx_(tx.getSeqProxy())
255 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
256{
257}
258
260{
261 isBlocker_ = (category == blocker);
262}
263
268
273
274static ApplyResult
276{
277 try
278 {
279 return with_txn_type(ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
280 T p(ctx);
281 return p();
282 });
283 }
284 catch (UnknownTxnType const& e)
285 {
286 // Should never happen
287 // LCOV_EXCL_START
288 JLOG(ctx.journal.fatal()) << "Unknown transaction type in apply: " << e.txnType;
289 UNREACHABLE("xrpl::invoke_apply : unknown transaction type");
290 return {temUNKNOWN, false};
291 // LCOV_EXCL_STOP
292 }
293}
294
295PreflightResult
296preflight(ServiceRegistry& registry, Rules const& rules, STTx const& tx, ApplyFlags flags, beast::Journal j)
297{
298 PreflightContext const pfCtx(registry, tx, rules, flags, j);
299 try
300 {
301 return {pfCtx, invoke_preflight(pfCtx)};
302 }
303 catch (std::exception const& e)
304 {
305 JLOG(j.fatal()) << "apply (preflight): " << e.what();
306 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
307 }
308}
309
310PreflightResult
312 ServiceRegistry& registry,
313 Rules const& rules,
314 uint256 const& parentBatchId,
315 STTx const& tx,
316 ApplyFlags flags,
318{
319 PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
320 try
321 {
322 return {pfCtx, invoke_preflight(pfCtx)};
323 }
324 catch (std::exception const& e)
325 {
326 JLOG(j.fatal()) << "apply (preflight): " << e.what();
327 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
328 }
329}
330
331PreclaimResult
332preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view)
333{
335 if (preflightResult.rules != view.rules())
336 {
337 auto secondFlight = [&]() {
338 if (preflightResult.parentBatchId)
339 return preflight(
340 registry,
341 view.rules(),
342 preflightResult.parentBatchId.value(),
343 preflightResult.tx,
344 preflightResult.flags,
345 preflightResult.j);
346
347 return preflight(registry, view.rules(), preflightResult.tx, preflightResult.flags, preflightResult.j);
348 }();
349
350 ctx.emplace(
351 registry,
352 view,
353 secondFlight.ter,
354 secondFlight.tx,
355 secondFlight.flags,
356 secondFlight.parentBatchId,
357 secondFlight.j);
358 }
359 else
360 {
361 ctx.emplace(
362 registry,
363 view,
364 preflightResult.ter,
365 preflightResult.tx,
366 preflightResult.flags,
367 preflightResult.parentBatchId,
368 preflightResult.j);
369 }
370
371 try
372 {
373 if (ctx->preflightResult != tesSUCCESS)
374 return {*ctx, ctx->preflightResult};
375 return {*ctx, invoke_preclaim(*ctx)};
376 }
377 catch (std::exception const& e)
378 {
379 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
380 return {*ctx, tefEXCEPTION};
381 }
382}
383
384XRPAmount
385calculateBaseFee(ReadView const& view, STTx const& tx)
386{
387 return invoke_calculateBaseFee(view, tx);
388}
389
390XRPAmount
391calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
392{
393 return Transactor::calculateBaseFee(view, tx);
394}
395
396ApplyResult
397doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view)
398{
399 if (preclaimResult.view.seq() != view.seq())
400 {
401 // Logic error from the caller. Don't have enough
402 // info to recover.
403 return {tefEXCEPTION, false};
404 }
405 try
406 {
407 if (!preclaimResult.likelyToClaimFee)
408 return {preclaimResult.ter, false};
409 ApplyContext ctx(
410 registry,
411 view,
412 preclaimResult.parentBatchId,
413 preclaimResult.tx,
414 preclaimResult.ter,
415 calculateBaseFee(view, preclaimResult.tx),
416 preclaimResult.flags,
417 preclaimResult.j);
418 return invoke_apply(ctx);
419 }
420 catch (std::exception const& e)
421 {
422 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
423 return {tefEXCEPTION, false};
424 }
425}
426
427} // 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
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: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.
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:614
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:643
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:52
ReadView const & view
Definition Transactor.h:55
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:14
beast::Journal const j
Definition Transactor.h:21
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)