xrpld
Loading...
Searching...
No Matches
apply.cpp
1#include <xrpl/tx/apply.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/beast/utility/instrumentation.h>
7#include <xrpl/core/HashRouter.h>
8#include <xrpl/core/ServiceRegistry.h>
9#include <xrpl/ledger/ApplyView.h>
10#include <xrpl/ledger/OpenView.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Rules.h>
13#include <xrpl/protocol/SField.h>
14#include <xrpl/protocol/STObject.h>
15#include <xrpl/protocol/STTx.h>
16#include <xrpl/protocol/TER.h>
17#include <xrpl/protocol/TxFlags.h>
18#include <xrpl/protocol/TxFormats.h>
19#include <xrpl/tx/applySteps.h>
20
21#include <exception>
22#include <string>
23#include <utility>
24
25namespace xrpl {
26
27// This file owns HashRouterFlags::PRIVATE1-4 and PRIVATE7-8 in HashRouter.h.
28// These are the first four; the other two are below.
29constexpr HashRouterFlags kSfSigbad = HashRouterFlags::PRIVATE1; // Signature is bad
30constexpr HashRouterFlags kSfSiggood = HashRouterFlags::PRIVATE2; // Signature is good
31constexpr HashRouterFlags kSfLocalbad = HashRouterFlags::PRIVATE3; // Local checks failed
32constexpr HashRouterFlags kSfLocalgood = HashRouterFlags::PRIVATE4; // Local checks passed
33
34// Before fixCleanup3_4_0, a signature in an alternate role field, such as
35// sfSponsorSignature, covered the same bytes as the top level signature. Which
36// bytes a role signature must cover therefore depends on whether the fix is
37// enabled, but the four flags above record only the verdict, not the rules that
38// produced it. A verdict reached under one prefix would otherwise be reused
39// under the other.
40//
41// The two flags below hold the verdict for the pre-fix prefixes, so the pre-fix
42// and post-fix verdicts occupy separate slots and neither is ever read in the
43// other's era. Nothing is cleared when the amendment activates: setFlags only
44// sets bits, so a stale pre-fix verdict simply stops being read and ages out
45// with the rest of the routing table.
46//
47// This is not one switchover at a single instant. The era is chosen per call
48// from the rules passed in, and callers do not agree on the rules: relay and
49// submit verify against the validated rules, which lag the open ledger rules
50// that preflight2 verifies against. At the amendment's flag ledger the same
51// transaction can therefore be checked under both prefixes, on the same node,
52// at the same time.
53//
54// Remove these two flags, and oldPrefixSig below, when Cleanup3_4_0 is retired
55// in features.macro.
58
59//------------------------------------------------------------------------------
60
62checkValidity(HashRouter& router, STTx const& tx, Rules const& rules)
63{
64 auto const id = tx.getTransactionID();
65 auto const flags = router.getFlags(id);
66
67 // Batch inner transactions are never independently valid: they are applied
68 // within their batch, not through checkValidity. Reaching here means one was
69 // relayed or submitted on its own, so mark it bad regardless of the
70 // amendment (like PeerImp and NetworkOPs).
71 if (tx.isFlag(tfInnerBatchTxn))
72 {
73 router.setFlags(id, kSfSigbad);
74 return {Validity::SigBad, "Batch inner transactions are never considered validly signed."};
75 }
76
77 // Pick the cache slot for this call's era; see kSfSiggoodOldPrefix above.
78 // Only a transaction that carries a role signature, and only while the fix
79 // is disabled, uses the separate slot. Every other transaction, and every
80 // transaction once the fix is enabled, uses the ordinary flags and verifies
81 // exactly once, so there is no steady state cost.
82 //
83 // Both directions matter. A good verdict from before the fix must not let a
84 // signature moved between roles survive the amendment, and a bad verdict
85 // from before the fix must not condemn a transaction that the new prefixes
86 // accept.
87 //
88 // Whether a transaction carries a role signature is fixed for its ID: the
89 // fields are kNotSigning, so they are excluded from the signed bytes, but
90 // they are still covered by the transaction ID. Repeat calls for one ID
91 // therefore always agree on which slot pair to use.
92 bool const oldPrefixSig = !rules.enabled(fixCleanup3_4_0) &&
93 (tx.isFieldPresent(sfSponsorSignature) || tx.isFieldPresent(sfCounterpartySignature));
94 auto const sigbadFlag = oldPrefixSig ? kSfSigbadOldPrefix : kSfSigbad;
95 auto const siggoodFlag = oldPrefixSig ? kSfSiggoodOldPrefix : kSfSiggood;
96
97 if (any(flags & sigbadFlag))
98 {
99 // Signature is known bad
100 return {Validity::SigBad, "Transaction has bad signature."};
101 }
102
103 if (!any(flags & siggoodFlag))
104 {
105 auto const sigVerify = tx.checkSign(rules);
106 if (!sigVerify)
107 {
108 router.setFlags(id, sigbadFlag);
109 return {Validity::SigBad, sigVerify.error()};
110 }
111 router.setFlags(id, siggoodFlag);
112 }
113
114 // Signature is now known good
115 if (any(flags & kSfLocalbad))
116 {
117 // ...but the local checks
118 // are known bad.
119 return {Validity::SigGoodOnly, "Local checks failed."};
120 }
121
122 if (any(flags & kSfLocalgood))
123 {
124 // ...and the local checks
125 // are known good.
126 return {Validity::Valid, ""};
127 }
128
129 // Do the local checks
130 std::string reason;
131 if (!passesLocalChecks(tx, reason))
132 {
133 router.setFlags(id, kSfLocalbad);
134 return {Validity::SigGoodOnly, reason};
135 }
136 router.setFlags(id, kSfLocalgood);
137 return {Validity::Valid, ""};
138}
139
140void
141forceValidity(HashRouter& router, UInt256 const& txid, Validity validity)
142{
143 // Callers reach here when they deliberately skip signature verification,
144 // such as a cluster peer that trusts its neighbor's checks, or a
145 // configuration that turns signature checks off. Nothing was verified, so
146 // there is no prefix era to record. Mark both of checkValidity's signature
147 // slots good: otherwise the forced verdict is ignored for a role-signature
148 // transaction until fixCleanup3_4_0 is enabled, and the signature the
149 // caller meant to skip gets verified after all. Marking both cannot leak a
150 // verdict across eras, because no verdict was reached, and this is the only
151 // place the distinction can be recorded: kSfSiggood alone does not say
152 // whether checkValidity verified a post-fix signature or a caller forced
153 // the result. An already cached bad verdict still wins, since checkValidity
154 // tests its bad flag first. Drop kSfSiggoodOldPrefix when Cleanup3_4_0 is
155 // retired.
157 switch (validity)
158 {
159 case Validity::Valid:
160 flags |= kSfLocalgood;
161 [[fallthrough]];
164 [[fallthrough]];
165 case Validity::SigBad:
166 // would be silly to call directly
167 break;
168 }
169 if (any(flags))
170 router.setFlags(txid, flags);
171}
172
173template <typename PreflightChecks>
174ApplyResult
175apply(ServiceRegistry& registry, OpenView& view, PreflightChecks&& preflightChecks)
176{
177 return doApply(preclaim(preflightChecks(), registry, view), registry, view);
178}
179
180ApplyResult
181apply(ServiceRegistry& registry, OpenView& view, STTx const& tx, ApplyFlags flags, beast::Journal j)
182{
183 return apply(
184 registry, view, [&]() mutable { return preflight(registry, view.rules(), tx, flags, j); });
185}
186
187ApplyResult
189 ServiceRegistry& registry,
190 OpenView& view,
191 UInt256 const& parentBatchId,
192 STTx const& tx,
193 ApplyFlags flags,
195{
196 return apply(registry, view, [&]() mutable {
197 return preflight(registry, view.rules(), parentBatchId, tx, flags, j);
198 });
199}
200
201static bool
203 ServiceRegistry& registry,
204 OpenView& batchView,
205 STTx const& batchTxn,
207{
208 XRPL_ASSERT(
209 batchTxn.getTxnType() == ttBATCH && !batchTxn.getFieldArray(sfRawTransactions).empty(),
210 "Batch transaction missing sfRawTransactions");
211
212 auto const parentBatchId = batchTxn.getTransactionID();
213 auto const mode = batchTxn.getFlags();
214
215 auto applyOneTransaction = [&registry, &j, &parentBatchId, &batchView](STTx const& tx) {
216 OpenView perTxBatchView(kBatchView, batchView);
217
218 auto const ret = apply(registry, perTxBatchView, parentBatchId, tx, TapBatch, j);
219 XRPL_ASSERT(
220 ret.applied == (isTesSuccess(ret.ter) || isTecClaim(ret.ter)),
221 "Inner transaction should not be applied");
222
223 JLOG(j.debug()) << "BatchTrace[" << parentBatchId << "]: " << tx.getTransactionID() << " "
224 << (ret.applied ? "applied" : "failure") << ": " << transToken(ret.ter);
225
226 // If the transaction should be applied push its changes to the
227 // whole-batch view.
228 // NOTE: each inner tx is individually capped at kOversizeMetaDataCap;
229 // there is no aggregate cap here. Bounded by kMaxBatchTxCount * cap,
230 // which standalone txns can already produce in one ledger.
231 if (ret.applied && (isTesSuccess(ret.ter) || isTecClaim(ret.ter)))
232 perTxBatchView.apply(batchView);
233
234 return ret;
235 };
236
237 int applied = 0;
238
239 for (auto const& stx : batchTxn.getBatchTransactions())
240 {
241 auto const result = applyOneTransaction(*stx);
242 XRPL_ASSERT(
243 result.applied == (isTesSuccess(result.ter) || isTecClaim(result.ter)),
244 "Outer Batch failure, inner transaction should not be applied");
245
246 if (result.applied)
247 ++applied;
248
249 if (!isTesSuccess(result.ter))
250 {
251 if ((mode & tfAllOrNothing) != 0u)
252 return false;
253
254 if ((mode & tfUntilFailure) != 0u)
255 break;
256 }
257 else if ((mode & tfOnlyOne) != 0u)
258 {
259 break;
260 }
261 }
262
263 return applied != 0;
264}
265
268 ServiceRegistry& registry,
269 OpenView& view,
270 STTx const& txn,
271 bool retryAssured,
272 ApplyFlags flags,
274{
275 // Returns false if the transaction has need not be retried.
276 if (retryAssured)
277 flags = flags | TapRetry;
278
279 JLOG(j.debug()) << "TXN " << txn.getTransactionID() << (retryAssured ? "/retry" : "/final");
280
281 try
282 {
283 auto const result = apply(registry, view, txn, flags, j);
284
285 if (result.applied)
286 {
287 JLOG(j.debug()) << "Transaction applied: " << transToken(result.ter);
288
289 // The batch transaction was just applied; now we need to apply
290 // its inner transactions as necessary.
291 if (isTesSuccess(result.ter) && txn.getTxnType() == ttBATCH)
292 {
293 OpenView wholeBatchView(kBatchView, view);
294
295 if (applyBatchTransactions(registry, wholeBatchView, txn, j))
296 wholeBatchView.apply(view);
297 }
298
300 }
301
302 if (isTefFailure(result.ter) || isTemMalformed(result.ter) || isTelLocal(result.ter))
303 {
304 // failure
305 JLOG(j.debug()) << "Transaction failure: " << transHuman(result.ter);
307 }
308
309 JLOG(j.debug()) << "Transaction retry: " << transHuman(result.ter);
311 }
312 catch (std::exception const& ex)
313 {
314 JLOG(j.warn()) << "Throws: " << ex.what();
316 }
317}
318
319} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream debug() const
Definition Journal.h:344
Stream warn() const
Definition Journal.h:356
Routing table for objects identified by hash.
Definition HashRouter.h:89
bool setFlags(UInt256 const &key, HashRouterFlags flags)
Set the flags on a hash.
HashRouterFlags getFlags(UInt256 const &key)
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:59
void apply(TxsRawView &to) const
Apply changes.
Definition OpenView.cpp:127
Rules const & rules() const override
Returns the tx processing rules.
Definition OpenView.cpp:149
Rules controlling protocol behavior.
Definition Rules.h:40
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:182
bool empty() const
Definition STArray.h:254
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:688
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
std::uint32_t getFlags() const
Definition STObject.cpp:517
std::expected< void, std::string > checkSign(Rules const &rules) const
Check the signature.
Definition STTx.cpp:270
TxType getTxnType() const
Definition STTx.h:250
UInt256 getTransactionID() const
Definition STTx.h:262
std::vector< std::shared_ptr< STTx const > > const & getBatchTransactions() const
The inner transactions of a Batch, built and validated at construction.
Definition STTx.cpp:648
Service registry for dependency injection.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr HashRouterFlags kSfSigbad
Definition apply.cpp:29
constexpr FlagValue tfInnerBatchTxn
Definition TxFlags.h:44
PreflightResult preflight(ServiceRegistry &registry, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
PreclaimResult preclaim(PreflightResult const &preflightResult, ServiceRegistry &registry, OpenView const &view)
Gate a transaction based on static ledger information.
Validity
Describes the pre-processing validity of a transaction.
Definition apply.h:24
@ SigBad
Signature is bad.
Definition apply.h:28
@ Valid
Signature and local checks are good / passed.
Definition apply.h:36
@ SigGoodOnly
Signature is good, but local checks fail.
Definition apply.h:32
ApplyResult apply(ServiceRegistry &registry, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition apply.cpp:181
constexpr struct xrpl::BatchViewT kBatchView
ApplyTransactionResult applyTransaction(ServiceRegistry &registry, OpenView &view, STTx const &tx, bool retryAssured, ApplyFlags flags, beast::Journal journal)
Transaction application helper.
Definition apply.cpp:267
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules)
Checks transaction signature and local checks.
Definition apply.cpp:62
constexpr HashRouterFlags kSfSiggood
Definition apply.cpp:30
ApplyTransactionResult
Enum class for return value from applyTransaction.
Definition apply.h:124
@ Success
Applied to this ledger.
Definition apply.h:128
@ Retry
Should be retried in this ledger.
Definition apply.h:136
@ Fail
Should not be retried in this ledger.
Definition apply.h:132
std::string transHuman(TER code)
Definition TER.cpp:266
std::string transToken(TER code)
Definition TER.cpp:257
bool passesLocalChecks(STTx const &tx, std::string &)
Definition STTx.cpp:850
void forceValidity(HashRouter &router, UInt256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
Definition apply.cpp:141
BaseUInt< 256 > UInt256
Definition base_uint.h:580
constexpr HashRouterFlags kSfSiggoodOldPrefix
Definition apply.cpp:57
bool isTefFailure(TER x) noexcept
Definition TER.h:671
HashRouterFlags
Definition HashRouter.h:20
static bool applyBatchTransactions(ServiceRegistry &registry, OpenView &batchView, STTx const &batchTxn, beast::Journal j)
Definition apply.cpp:202
ApplyFlags
Definition ApplyView.h:27
@ TapRetry
Definition ApplyView.h:36
@ TapBatch
Definition ApplyView.h:42
bool isTelLocal(TER x) noexcept
Definition TER.h:659
constexpr HashRouterFlags kSfSigbadOldPrefix
Definition apply.cpp:56
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
bool isTecClaim(TER x) noexcept
Definition TER.h:690
ApplyResult doApply(PreclaimResult const &preclaimResult, ServiceRegistry &registry, OpenView &view)
Apply a prechecked transaction to an OpenView.
constexpr HashRouterFlags kSfLocalgood
Definition apply.cpp:32
bool isTemMalformed(TER x) noexcept
Definition TER.h:665
constexpr bool any(HashRouterFlags flags)
Definition HashRouter.h:74
constexpr HashRouterFlags kSfLocalbad
Definition apply.cpp:31
T what(T... args)