xrpld
Loading...
Searching...
No Matches
applySteps.cpp
1#include <xrpl/tx/applySteps.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/beast/utility/Zero.h>
7#include <xrpl/beast/utility/instrumentation.h>
8#include <xrpl/ledger/ApplyView.h>
9#include <xrpl/ledger/OpenView.h>
10#include <xrpl/protocol/Rules.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/SeqProxy.h>
13#include <xrpl/protocol/TER.h>
14#include <xrpl/protocol/XRPAmount.h>
15#include <xrpl/tx/ApplyContext.h>
16#include <xrpl/tx/Transactor.h>
17
18#include <cstdint>
19#include <exception>
20#include <memory>
21#include <optional>
22#include <utility>
23#pragma push_macro("TRANSACTION")
24#undef TRANSACTION
25
26// Do nothing
27#define TRANSACTION(...)
28#define TRANSACTION_INCLUDE 1
29
30#include <xrpl/protocol/detail/transactions.macro>
31
32#undef TRANSACTION
33#pragma pop_macro("TRANSACTION")
34
35// DO NOT INCLUDE TRANSACTOR HEADER FILES HERE.
36// See the instructions at the top of transactions.macro instead.
37
38#include <xrpl/core/ServiceRegistry.h>
39#include <xrpl/protocol/TxFormats.h>
40
41namespace xrpl {
42
43namespace {
44
45struct UnknownTxnType : std::exception
46{
47 TxType txnType;
48 UnknownTxnType(TxType t) : txnType{t}
49 {
50 }
51};
52
53// Call a lambda with the concrete transaction type as a template parameter
54// throw an "UnknownTxnType" exception on error
55template <class F>
56auto
57withTxnType(Rules const& rules, TxType txnType, F&& f)
58{
59 // These global updates really should have been for every Transaction
60 // step: preflight, preclaim, calculateBaseFee, and doApply. Unfortunately,
61 // they were only included in doApply (via Transactor::operator()). That may
62 // have been sufficient when the changes were only related to operations
63 // that mutated data, but some features will now change how they read data,
64 // so these need to be more global.
65 //
66 // To prevent unintentional side effects on existing checks, they will be
67 // set for every operation only once at least one of the relevant amendments
68 // are enabled.
69 //
70 // See also Transactor::operator().
71 //
72 std::optional<CurrentTransactionRulesGuard> rulesGuard;
73 std::optional<NumberMantissaScaleGuard> mantissaScaleGuard;
74 createGuards(rules, rulesGuard, mantissaScaleGuard);
75
76 switch (txnType)
77 {
78#pragma push_macro("TRANSACTION")
79#undef TRANSACTION
80
81#define TRANSACTION(tag, value, name, ...) \
82 case tag: \
83 return f.template operator()<name>();
84
85#include <xrpl/protocol/detail/transactions.macro>
86
87#undef TRANSACTION
88#pragma pop_macro("TRANSACTION")
89 default:
90 throw UnknownTxnType(txnType);
91 }
92}
93} // namespace
94
95// Templates so preflight does the right thing with T::kConsequencesFactory.
96//
97// This could be done more easily using if constexpr, but Visual Studio
98// 2017 doesn't handle if constexpr correctly. So once we're no longer
99// building with Visual Studio 2017 we can consider replacing the four
100// templates with a single template function that uses if constexpr.
101//
102// For ConsequencesFactoryType::Normal
103//
104
105template <class T>
106 requires(T::kConsequencesFactory == Transactor::ConsequencesFactoryType::Normal)
109{
110 return TxConsequences(ctx.tx);
111};
112
113// For ConsequencesFactoryType::Blocker
114template <class T>
115 requires(T::kConsequencesFactory == Transactor::ConsequencesFactoryType::Blocker)
116TxConsequences
117consequencesHelper(PreflightContext const& ctx)
118{
119 return TxConsequences(ctx.tx, TxConsequences::Category::Blocker);
120};
121
122// For ConsequencesFactoryType::Custom
123template <class T>
124 requires(T::kConsequencesFactory == Transactor::ConsequencesFactoryType::Custom)
127{
128 return T::makeTxConsequences(ctx);
129};
130
131static std::pair<NotTEC, TxConsequences>
133{
134 try
135 {
136 return withTxnType(ctx.rules, ctx.tx.getTxnType(), [&]<typename T>() {
137 auto const tec = Transactor::invokePreflight<T>(ctx);
138 return std::make_pair(
139 tec, isTesSuccess(tec) ? consequencesHelper<T>(ctx) : TxConsequences{tec});
140 });
141 }
142 catch (UnknownTxnType const& e)
143 {
144 // Should never happen
145 // LCOV_EXCL_START
146 JLOG(ctx.j.fatal()) << "Unknown transaction type in preflight: " << e.txnType;
147 UNREACHABLE("xrpl::invokePreflight : unknown transaction type");
148 return {temUNKNOWN, TxConsequences{temUNKNOWN}};
149 // LCOV_EXCL_STOP
150 }
151}
152
153static TER
155{
156 try
157 {
158 // use name hiding to accomplish compile-time polymorphism of static
159 // class functions for Transactor and derived classes.
160 return withTxnType(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::kZero)
176 {
177 if (NotTEC const preSigResult = [&]() -> NotTEC {
178 if (NotTEC const result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j))
179 return result;
180
181 if (NotTEC const result = T::checkPriorTxAndLastLedger(ctx))
182 return result;
183
184 if (NotTEC const result = T::checkSponsor(ctx.view, ctx.tx))
185 return result;
186
187 if (NotTEC const result =
188 Transactor::invokeCheckPermission<T>(ctx.view, ctx.tx))
189 return result;
190
191 if (NotTEC const result = T::checkSign(ctx))
192 return result;
193
194 return tesSUCCESS;
195 }())
196 return preSigResult;
197
198 if (TER const result = T::checkFee(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()) << "Unknown transaction type in preclaim: " << e.txnType;
210 UNREACHABLE("xrpl::invokePreclaim : unknown transaction type");
211 return temUNKNOWN;
212 // LCOV_EXCL_STOP
213 }
214}
215
232static XRPAmount
233invokeCalculateBaseFee(ReadView const& view, STTx const& tx)
234{
235 try
236 {
237 return withTxnType(view.rules(), tx.getTxnType(), [&]<typename T>() {
238 return T::calculateBaseFee(view, tx);
239 });
240 }
241 catch (UnknownTxnType const& e)
242 {
243 // LCOV_EXCL_START
244 UNREACHABLE("xrpl::invoke_calculateBaseFee : unknown transaction type");
245 return XRPAmount{0};
246 // LCOV_EXCL_STOP
247 }
248}
249
251 : isBlocker_(false)
252 , fee_(beast::kZero)
253 , potentialSpend_(beast::kZero)
254 , seqProx_(SeqProxy::rawSequence(0))
256{
257 XRPL_ASSERT(
258 !isTesSuccess(pfResult), "xrpl::TxConsequences::TxConsequences : is not tesSUCCESS");
259}
260
262 : isBlocker_(false)
263 , fee_(tx[sfFee].native() && !tx[sfFee].negative() ? tx[sfFee].xrp() : beast::kZero)
264 , potentialSpend_(beast::kZero)
265 , seqProx_(tx.getSeqProxy())
266 , sequencesConsumed_(tx.getSeqProxy().isSeq() ? 1 : 0)
267{
268}
269
274
279
284
285static ApplyResult
287{
288 try
289 {
290 return withTxnType(ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() {
291 T p(ctx);
292 return p();
293 });
294 }
295 catch (UnknownTxnType const& e)
296 {
297 // Should never happen
298 // LCOV_EXCL_START
299 JLOG(ctx.journal.fatal()) << "Unknown transaction type in apply: " << e.txnType;
300 UNREACHABLE("xrpl::invokeApply : unknown transaction type");
301 return {temUNKNOWN, false};
302 // LCOV_EXCL_STOP
303 }
304}
305
306// Test-only factory — not part of the public API.
307// The returned Transactor holds a raw reference to ctx; the caller must ensure
308// the ApplyContext outlives the Transactor.
311{
312 return withTxnType(
313 ctx.view().rules(), ctx.tx.getTxnType(), [&]<typename T>() -> std::unique_ptr<Transactor> {
314 return std::make_unique<T>(ctx);
315 });
316}
317
318PreflightResult
320 ServiceRegistry& registry,
321 Rules const& rules,
322 STTx const& tx,
323 ApplyFlags flags,
325{
326 PreflightContext const pfCtx(registry, tx, rules, flags, j);
327 try
328 {
329 return {pfCtx, invokePreflight(pfCtx)};
330 }
331 catch (std::exception const& e)
332 {
333 JLOG(j.fatal()) << "apply (preflight): " << e.what();
334 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
335 }
336}
337
338PreflightResult
340 ServiceRegistry& registry,
341 Rules const& rules,
342 uint256 const& parentBatchId,
343 STTx const& tx,
344 ApplyFlags flags,
346{
347 PreflightContext const pfCtx(registry, tx, parentBatchId, rules, flags, j);
348 try
349 {
350 return {pfCtx, invokePreflight(pfCtx)};
351 }
352 catch (std::exception const& e)
353 {
354 JLOG(j.fatal()) << "apply (preflight): " << e.what();
355 return {pfCtx, {tefEXCEPTION, TxConsequences{tx}}};
356 }
357}
358
359PreclaimResult
360preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view)
361{
363 if (preflightResult.rules != view.rules())
364 {
365 auto secondFlight = [&]() {
366 if (preflightResult.parentBatchId)
367 {
368 return preflight(
369 registry,
370 view.rules(),
371 preflightResult.parentBatchId.value(),
372 preflightResult.tx,
373 preflightResult.flags,
374 preflightResult.j);
375 }
376
377 return preflight(
378 registry,
379 view.rules(),
380 preflightResult.tx,
381 preflightResult.flags,
382 preflightResult.j);
383 }();
384
385 ctx.emplace(
386 registry,
387 view,
388 secondFlight.ter,
389 secondFlight.tx,
390 secondFlight.flags,
391 secondFlight.parentBatchId,
392 secondFlight.j);
393 }
394 else
395 {
396 ctx.emplace(
397 registry,
398 view,
399 preflightResult.ter,
400 preflightResult.tx,
401 preflightResult.flags,
402 preflightResult.parentBatchId,
403 preflightResult.j);
404 }
405
406 try
407 {
408 if (!isTesSuccess(ctx->preflightResult))
409 return {*ctx, ctx->preflightResult};
410 return {*ctx, invokePreclaim(*ctx)};
411 }
412 catch (std::exception const& e)
413 {
414 JLOG(ctx->j.fatal()) << "apply (preclaim): " << e.what();
415 return {*ctx, tefEXCEPTION};
416 }
417}
418
419XRPAmount
420calculateBaseFee(ReadView const& view, STTx const& tx)
421{
422 return invokeCalculateBaseFee(view, tx);
423}
424
425XRPAmount
426calculateDefaultBaseFee(ReadView const& view, STTx const& tx)
427{
428 return Transactor::calculateBaseFee(view, tx);
429}
430
431ApplyResult
432doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view)
433{
434 if (preclaimResult.view.seq() != view.seq())
435 {
436 // Logic error from the caller. Don't have enough
437 // info to recover.
438 return {tefEXCEPTION, false};
439 }
440 try
441 {
442 if (!preclaimResult.likelyToClaimFee)
443 return {preclaimResult.ter, false};
444 ApplyContext ctx(
445 registry,
446 view,
447 preclaimResult.parentBatchId,
448 preclaimResult.tx,
449 preclaimResult.ter,
450 calculateBaseFee(view, preclaimResult.tx),
451 preclaimResult.flags,
452 preclaimResult.j);
453 return invokeApply(ctx);
454 }
455 catch (std::exception const& e)
456 {
457 JLOG(preclaimResult.j.fatal()) << "apply: " << e.what();
458 return {tefEXCEPTION, false};
459 }
460}
461
462} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream fatal() const
Definition Journal.h:368
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:59
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:148
A view into a ledger.
Definition ReadView.h:41
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:115
Rules controlling protocol behavior.
Definition Rules.h:40
TxType getTxnType() const
Definition STTx.h:226
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:37
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:52
TxConsequences(NotTEC pfResult)
std::uint32_t sequencesConsumed_
Number of sequences consumed.
Definition applySteps.h:91
Category
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:58
@ Blocker
Affects the ability of subsequent transactions to claim a fee.
Definition applySteps.h:67
XRPAmount potentialSpend_
Does NOT include the fee.
Definition applySteps.h:83
bool isBlocker_
Describes how the transaction affects subsequent transactions.
Definition applySteps.h:75
std::uint32_t sequencesConsumed() const
Sequences consumed.
Definition applySteps.h:168
XRPAmount const & potentialSpend() const
Potential Spend.
Definition applySteps.h:150
SeqProxy seqProx_
SeqProxy of transaction.
Definition applySteps.h:87
XRPAmount fee_
Transaction fee.
Definition applySteps.h:79
T emplace(T... args)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:54
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
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:45
PreclaimResult preclaim(PreflightResult const &preflightResult, ServiceRegistry &registry, OpenView const &view)
Gate a transaction based on static ledger information.
static TER invokePreclaim(PreclaimContext const &ctx)
@ tefEXCEPTION
Definition TER.h:164
void createGuards(Rules const &rules, std::optional< CurrentTransactionRulesGuard > &rulesGuard, std::optional< NumberMantissaScaleGuard > &mantissaScaleGuard)
Definition Rules.cpp:91
static std::pair< NotTEC, TxConsequences > invokePreflight(PreflightContext const &ctx)
static XRPAmount invokeCalculateBaseFee(ReadView const &view, STTx const &tx)
Calculates the base fee for a given transaction.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
std::unique_ptr< Transactor > makeTransactor(ApplyContext &ctx)
XRPAmount calculateDefaultBaseFee(ReadView const &view, STTx const &tx)
Return the minimum fee that an "ordinary" transaction would pay.
TxConsequences consequencesHelper(PreflightContext const &ctx)
ApplyFlags
Definition ApplyView.h:27
@ temUNKNOWN
Definition TER.h:112
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
static ApplyResult invokeApply(ApplyContext &ctx)
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.
BaseUInt< 256 > uint256
Definition base_uint.h:580
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
Describes the results of the preclaim check.
Definition applySteps.h:263
ReadView const & view
From the input - the ledger view.
Definition applySteps.h:268
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:276
TER const ter
Intermediate transaction result.
Definition applySteps.h:289
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:280
STTx const & tx
From the input - the transaction.
Definition applySteps.h:272
beast::Journal const j
From the input - the journal.
Definition applySteps.h:284
bool const likelyToClaimFee
Success flag - whether the transaction is likely to claim a fee.
Definition applySteps.h:295
State information when preflighting a tx.
Definition Transactor.h:38
Describes the results of the preflight check.
Definition applySteps.h:200
beast::Journal const j
From the input - the journal.
Definition applySteps.h:225
ApplyFlags const flags
From the input - the flags.
Definition applySteps.h:221
Rules const rules
From the input - the rules.
Definition applySteps.h:213
NotTEC const ter
Intermediate transaction result.
Definition applySteps.h:230
std::optional< uint256 const > const parentBatchId
From the input - the batch identifier, if part of a batch.
Definition applySteps.h:209
STTx const & tx
From the input - the transaction.
Definition applySteps.h:205
T what(T... args)