rippled
Loading...
Searching...
No Matches
Transactor.cpp
1#include <xrpl/basics/contract.h>
2#include <xrpl/core/NetworkIDService.h>
3#include <xrpl/json/to_string.h>
4#include <xrpl/ledger/View.h>
5#include <xrpl/ledger/helpers/AccountRootHelpers.h>
6#include <xrpl/ledger/helpers/CredentialHelpers.h>
7#include <xrpl/ledger/helpers/OfferHelpers.h>
8#include <xrpl/ledger/helpers/RippleStateHelpers.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/Protocol.h>
12#include <xrpl/protocol/SystemParameters.h>
13#include <xrpl/protocol/TxFlags.h>
14#include <xrpl/protocol/UintTypes.h>
15#include <xrpl/server/LoadFeeTrack.h>
16#include <xrpl/tx/SignerEntries.h>
17#include <xrpl/tx/Transactor.h>
18#include <xrpl/tx/apply.h>
19#include <xrpl/tx/transactors/delegate/DelegateUtils.h>
20#include <xrpl/tx/transactors/nft/NFTokenUtils.h>
21
22namespace xrpl {
23
27{
28 if (isPseudoTx(ctx.tx) && ctx.tx.isFlag(tfInnerBatchTxn))
29 {
30 JLOG(ctx.j.warn()) << "Pseudo transactions cannot contain the "
31 "tfInnerBatchTxn flag.";
32 return temINVALID_FLAG;
33 }
34
35 if (!isPseudoTx(ctx.tx) || ctx.tx.isFieldPresent(sfNetworkID))
36 {
37 uint32_t const nodeNID = ctx.registry.get().getNetworkIDService().getNetworkID();
38 std::optional<uint32_t> txNID = ctx.tx[~sfNetworkID];
39
40 if (nodeNID <= 1024)
41 {
42 // legacy networks have ids less than 1024, these networks cannot
43 // specify NetworkID in txn
44 if (txNID)
46 }
47 else
48 {
49 // new networks both require the field to be present and require it
50 // to match
51 if (!txNID)
53
54 if (*txNID != nodeNID)
55 return telWRONG_NETWORK;
56 }
57 }
58
59 auto const txID = ctx.tx.getTransactionID();
60
61 if (txID == beast::zero)
62 {
63 JLOG(ctx.j.warn()) << "applyTransaction: transaction id may not be zero";
64 return temINVALID;
65 }
66
67 if ((ctx.tx.getFlags() & flagMask) != 0u)
68 {
69 JLOG(ctx.j.debug()) << ctx.tx.peekAtField(sfTransactionType).getFullText()
70 << ": invalid flags.";
71 return temINVALID_FLAG;
72 }
73
74 return tesSUCCESS;
75}
76
77namespace detail {
78
85{
86 if (auto const spk = sigObject.getFieldVL(sfSigningPubKey);
87 !spk.empty() && !publicKeyType(makeSlice(spk)))
88 {
89 JLOG(j.debug()) << "preflightCheckSigningKey: invalid signing key";
90 return temBAD_SIGNATURE;
91 }
92 return tesSUCCESS;
93}
94
97{
98 if ((flags & tapDRY_RUN) != 0u) // simulation
99 {
100 std::optional<Slice> const signature = sigObject[~sfTxnSignature];
101 if (signature && !signature->empty())
102 {
103 // NOTE: This code should never be hit because it's checked in the
104 // `simulate` RPC
105 return temINVALID; // LCOV_EXCL_LINE
106 }
107
108 if (!sigObject.isFieldPresent(sfSigners))
109 {
110 // no signers, no signature - a valid simulation
111 return tesSUCCESS;
112 }
113
114 for (auto const& signer : sigObject.getFieldArray(sfSigners))
115 {
116 if (signer.isFieldPresent(sfTxnSignature) && !signer[sfTxnSignature].empty())
117 {
118 // NOTE: This code should never be hit because it's
119 // checked in the `simulate` RPC
120 return temINVALID; // LCOV_EXCL_LINE
121 }
122 }
123
124 Slice const signingPubKey = sigObject[sfSigningPubKey];
125 if (!signingPubKey.empty())
126 {
127 // trying to single-sign _and_ multi-sign a transaction
128 return temINVALID;
129 }
130 return tesSUCCESS;
131 }
132 return {};
133}
134
135} // namespace detail
136
138NotTEC
140{
141 if (ctx.tx.isFieldPresent(sfDelegate))
142 {
143 if (!ctx.rules.enabled(featurePermissionDelegationV1_1))
144 return temDISABLED;
145
146 if (ctx.tx[sfDelegate] == ctx.tx[sfAccount])
147 return temBAD_SIGNER;
148 }
149
150 if (auto const ret = preflight0(ctx, flagMask))
151 return ret;
152
153 auto const id = ctx.tx.getAccountID(sfAccount);
154 if (id == beast::zero)
155 {
156 JLOG(ctx.j.warn()) << "preflight1: bad account id";
157 return temBAD_SRC_ACCOUNT;
158 }
159
160 // No point in going any further if the transaction fee is malformed.
161 auto const fee = ctx.tx.getFieldAmount(sfFee);
162 if (!fee.native() || fee.negative() || !isLegalAmount(fee.xrp()))
163 {
164 JLOG(ctx.j.debug()) << "preflight1: invalid fee";
165 return temBAD_FEE;
166 }
167
168 if (auto const ret = detail::preflightCheckSigningKey(ctx.tx, ctx.j))
169 return ret;
170
171 // An AccountTxnID field constrains transaction ordering more than the
172 // Sequence field. Tickets, on the other hand, reduce ordering
173 // constraints. Because Tickets and AccountTxnID work against one
174 // another the combination is unsupported and treated as malformed.
175 //
176 // We return temINVALID for such transactions.
177 if (ctx.tx.getSeqProxy().isTicket() && ctx.tx.isFieldPresent(sfAccountTxnID))
178 return temINVALID;
179
180 if (ctx.tx.isFlag(tfInnerBatchTxn) && !ctx.rules.enabled(featureBatch))
181 return temINVALID_FLAG;
182
183 XRPL_ASSERT(
184 ctx.tx.isFlag(tfInnerBatchTxn) == ctx.parentBatchId.has_value() ||
185 !ctx.rules.enabled(featureBatch),
186 "Inner batch transaction must have a parent batch ID.");
187
188 return tesSUCCESS;
189}
190
192NotTEC
194{
195 if (auto const ret = detail::preflightCheckSimulateKeys(ctx.flags, ctx.tx, ctx.j))
196 {
197 // Skips following checks if the transaction is being simulated,
198 // regardless of success or failure
199 return *ret;
200 }
201
202 // It should be impossible for the InnerBatchTxn flag to be set without
203 // featureBatch being enabled
204 XRPL_ASSERT_PARTS(
205 !ctx.tx.isFlag(tfInnerBatchTxn) || ctx.rules.enabled(featureBatch),
206 "xrpl::Transactor::preflight2",
207 "InnerBatch flag only set if feature enabled");
208 // Skip signature check on batch inner transactions
209 if (ctx.tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatch))
210 return tesSUCCESS;
211 // Do not add any checks after this point that are relevant for
212 // batch inner transactions. They will be skipped.
213
214 auto const sigValid = checkValidity(ctx.registry.get().getHashRouter(), ctx.tx, ctx.rules);
215 if (sigValid.first == Validity::SigBad)
216 { // LCOV_EXCL_START
217 JLOG(ctx.j.debug()) << "preflight2: bad signature. " << sigValid.second;
218 return temINVALID;
219 // LCOV_EXCL_STOP
220 }
221
222 // Do not add any checks after this point that are relevant for
223 // batch inner transactions. They will be skipped.
224
225 return tesSUCCESS;
226}
227
228//------------------------------------------------------------------------------
229
231 : ctx_(ctx)
232 , sink_(ctx.journal, to_short_string(ctx.tx.getTransactionID()) + " ")
233 , j_(sink_)
234 , account_(ctx.tx.getAccountID(sfAccount))
235{
236}
237
238bool
240{
241 if (!slice)
242 return true;
243 return !slice->empty() && slice->length() <= maxLength;
244}
245
251
252NotTEC
257
258NotTEC
260{
261 auto const delegate = tx[~sfDelegate];
262 if (!delegate)
263 return tesSUCCESS;
264
265 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
266 auto const sle = view.read(delegateKey);
267
268 if (!sle)
270
271 return checkTxPermission(sle, tx);
272}
273
276{
277 // Returns the fee in fee units.
278
279 // The computation has two parts:
280 // * The base fee, which is the same for most transactions.
281 // * The additional cost of each multisignature on the transaction.
282 XRPAmount const baseFee = view.fees().base;
283
284 // Each signer adds one more baseFee to the minimum required fee
285 // for the transaction.
286 std::size_t const signerCount =
287 tx.isFieldPresent(sfSigners) ? tx.getFieldArray(sfSigners).size() : 0;
288
289 return baseFee + (signerCount * baseFee);
290}
291
292// Returns the fee in fee units, not scaled for load.
295{
296 // Assumption: One reserve increment is typically much greater than one base
297 // fee.
298 // This check is in an assert so that it will come to the attention of
299 // developers if that assumption is not correct. If the owner reserve is not
300 // significantly larger than the base fee (or even worse, smaller), we will
301 // need to rethink charging an owner reserve as a transaction fee.
302 // TODO: This function is static, and I don't want to add more parameters.
303 // When it is finally refactored to be in a context that has access to the
304 // Application, include "app().getOverlay().networkID() > 2 ||" in the
305 // condition.
306 XRPL_ASSERT(
307 view.fees().increment > view.fees().base * 100,
308 "xrpl::Transactor::calculateOwnerReserveFee : Owner reserve is "
309 "reasonable");
310 return view.fees().increment;
311}
312
315 ServiceRegistry& registry,
316 XRPAmount baseFee,
317 Fees const& fees,
318 ApplyFlags flags)
319{
320 return scaleFeeLoad(baseFee, registry.getFeeTrack(), fees, (flags & tapUNLIMITED) != 0u);
321}
322
323TER
325{
326 if (!ctx.tx[sfFee].native())
327 return temBAD_FEE;
328
329 auto const feePaid = ctx.tx[sfFee].xrp();
330
331 if ((ctx.flags & tapBATCH) != 0u)
332 {
333 if (feePaid == beast::zero)
334 return tesSUCCESS;
335
336 JLOG(ctx.j.trace()) << "Batch: Fee must be zero.";
337 return temBAD_FEE; // LCOV_EXCL_LINE
338 }
339
340 if (!isLegalAmount(feePaid) || feePaid < beast::zero)
341 return temBAD_FEE;
342
343 // Only check fee is sufficient when the ledger is open.
344 if (ctx.view.open())
345 {
346 auto const feeDue = minimumFee(ctx.registry, baseFee, ctx.view.fees(), ctx.flags);
347
348 if (feePaid < feeDue)
349 {
350 JLOG(ctx.j.trace()) << "Insufficient fee paid: " << to_string(feePaid) << "/"
351 << to_string(feeDue);
352 return telINSUF_FEE_P;
353 }
354 }
355
356 if (feePaid == beast::zero)
357 return tesSUCCESS;
358
359 auto const id = ctx.tx.getFeePayer();
360 auto const sle = ctx.view.read(keylet::account(id));
361 if (!sle)
362 return terNO_ACCOUNT;
363
364 auto const balance = (*sle)[sfBalance].xrp();
365
366 // NOTE: Because preclaim evaluates against a static readview, it
367 // does not reflect fee deductions from other transactions paid by
368 // the same account within the current ledger.
369 // As a result, if an account's balance is over-committed across multiple
370 // transactions, this check may pass optimistically.
371 // The fee shortfall will be handled by the Transactor::reset mechanism,
372 // which caps the fee to the remaining actual balance.
373 if (balance < feePaid)
374 {
375 JLOG(ctx.j.trace()) << "Insufficient balance:" << " balance=" << to_string(balance)
376 << " paid=" << to_string(feePaid);
377
378 if ((balance > beast::zero) && !ctx.view.open())
379 {
380 // Closed ledger, non-zero balance, less than fee
381 return tecINSUFF_FEE;
382 }
383
384 return terINSUF_FEE_B;
385 }
386
387 return tesSUCCESS;
388}
389
390TER
392{
393 auto const feePaid = ctx_.tx[sfFee].xrp();
394
395 auto const feePayer = ctx_.tx.getFeePayer();
396 auto const sle = view().peek(keylet::account(feePayer));
397 if (!sle)
398 return tefINTERNAL; // LCOV_EXCL_LINE
399
400 // Deduct the fee, so it's not available during the transaction.
401 // Will only write the account back if the transaction succeeds.
402 sle->setFieldAmount(sfBalance, sle->getFieldAmount(sfBalance) - feePaid);
403 if (feePayer != account_)
404 view().update(sle); // done in `apply()` for the account
405
406 // VFALCO Should we call view().rawDestroyXRP() here as well?
407 return tesSUCCESS;
408}
409
410NotTEC
412{
413 auto const id = tx.getAccountID(sfAccount);
414
415 auto const sle = view.read(keylet::account(id));
416
417 if (!sle)
418 {
419 JLOG(j.trace()) << "applyTransaction: delay: source account does not exist "
420 << toBase58(id);
421 return terNO_ACCOUNT;
422 }
423
424 SeqProxy const t_seqProx = tx.getSeqProxy();
425 SeqProxy const a_seq = SeqProxy::sequence((*sle)[sfSequence]);
426
427 if (t_seqProx.isSeq())
428 {
429 if (tx.isFieldPresent(sfTicketSequence))
430 {
431 JLOG(j.trace()) << "applyTransaction: has both a TicketSequence "
432 "and a non-zero Sequence number";
433 return temSEQ_AND_TICKET;
434 }
435 if (t_seqProx != a_seq)
436 {
437 if (a_seq < t_seqProx)
438 {
439 JLOG(j.trace()) << "applyTransaction: has future sequence number "
440 << "a_seq=" << a_seq << " t_seq=" << t_seqProx;
441 return terPRE_SEQ;
442 }
443 // It's an already-used sequence number.
444 JLOG(j.trace()) << "applyTransaction: has past sequence number "
445 << "a_seq=" << a_seq << " t_seq=" << t_seqProx;
446 return tefPAST_SEQ;
447 }
448 }
449 else if (t_seqProx.isTicket())
450 {
451 // Bypass the type comparison. Apples and oranges.
452 if (a_seq.value() <= t_seqProx.value())
453 {
454 // If the Ticket number is greater than or equal to the
455 // account sequence there's the possibility that the
456 // transaction to create the Ticket has not hit the ledger
457 // yet. Allow a retry.
458 JLOG(j.trace()) << "applyTransaction: has future ticket id "
459 << "a_seq=" << a_seq << " t_seq=" << t_seqProx;
460 return terPRE_TICKET;
461 }
462
463 // Transaction can never succeed if the Ticket is not in the ledger.
464 if (!view.exists(keylet::ticket(id, t_seqProx)))
465 {
466 JLOG(j.trace()) << "applyTransaction: ticket already used or never created "
467 << "a_seq=" << a_seq << " t_seq=" << t_seqProx;
468 return tefNO_TICKET;
469 }
470 }
471
472 return tesSUCCESS;
473}
474
475NotTEC
477{
478 auto const id = ctx.tx.getAccountID(sfAccount);
479
480 auto const sle = ctx.view.read(keylet::account(id));
481
482 if (!sle)
483 {
484 JLOG(ctx.j.trace()) << "applyTransaction: delay: source account does not exist "
485 << toBase58(id);
486 return terNO_ACCOUNT;
487 }
488
489 if (ctx.tx.isFieldPresent(sfAccountTxnID) &&
490 (sle->getFieldH256(sfAccountTxnID) != ctx.tx.getFieldH256(sfAccountTxnID)))
491 return tefWRONG_PRIOR;
492
493 if (ctx.tx.isFieldPresent(sfLastLedgerSequence) &&
494 (ctx.view.seq() > ctx.tx.getFieldU32(sfLastLedgerSequence)))
495 return tefMAX_LEDGER;
496
497 if (ctx.view.txExists(ctx.tx.getTransactionID()))
498 return tefALREADY;
499
500 return tesSUCCESS;
501}
502
503TER
505{
506 XRPL_ASSERT(sleAccount, "xrpl::Transactor::consumeSeqProxy : non-null account");
507 SeqProxy const seqProx = ctx_.tx.getSeqProxy();
508 if (seqProx.isSeq())
509 {
510 // Note that if this transaction is a TicketCreate, then
511 // the transaction will modify the account root sfSequence
512 // yet again.
513 sleAccount->setFieldU32(sfSequence, seqProx.value() + 1);
514 return tesSUCCESS;
515 }
516 return ticketDelete(view(), account_, getTicketIndex(account_, seqProx), j_);
517}
518
519// Remove a single Ticket from the ledger.
520TER
522 ApplyView& view,
523 AccountID const& account,
524 uint256 const& ticketIndex,
526{
527 // Delete the Ticket, adjust the account root ticket count, and
528 // reduce the owner count.
529 SLE::pointer const sleTicket = view.peek(keylet::ticket(ticketIndex));
530 if (!sleTicket)
531 {
532 // LCOV_EXCL_START
533 JLOG(j.fatal()) << "Ticket disappeared from ledger.";
534 return tefBAD_LEDGER;
535 // LCOV_EXCL_STOP
536 }
537
538 std::uint64_t const page{(*sleTicket)[sfOwnerNode]};
539 if (!view.dirRemove(keylet::ownerDir(account), page, ticketIndex, true))
540 {
541 // LCOV_EXCL_START
542 JLOG(j.fatal()) << "Unable to delete Ticket from owner.";
543 return tefBAD_LEDGER;
544 // LCOV_EXCL_STOP
545 }
546
547 // Update the account root's TicketCount. If the ticket count drops to
548 // zero remove the (optional) field.
549 auto sleAccount = view.peek(keylet::account(account));
550 if (!sleAccount)
551 {
552 // LCOV_EXCL_START
553 JLOG(j.fatal()) << "Could not find Ticket owner account root.";
554 return tefBAD_LEDGER;
555 // LCOV_EXCL_STOP
556 }
557
558 if (auto ticketCount = (*sleAccount)[~sfTicketCount])
559 {
560 if (*ticketCount == 1)
561 {
562 sleAccount->makeFieldAbsent(sfTicketCount);
563 }
564 else
565 {
566 ticketCount = *ticketCount - 1;
567 }
568 }
569 else
570 {
571 // LCOV_EXCL_START
572 JLOG(j.fatal()) << "TicketCount field missing from account root.";
573 return tefBAD_LEDGER;
574 // LCOV_EXCL_STOP
575 }
576
577 // Update the Ticket owner's reserve.
578 adjustOwnerCount(view, sleAccount, -1, j);
579
580 // Remove Ticket from ledger.
581 view.erase(sleTicket);
582 return tesSUCCESS;
583}
584
585// check stuff before you bother to lock the ledger
586void
588{
589 XRPL_ASSERT(account_ != beast::zero, "xrpl::Transactor::preCompute : nonzero account");
590}
591
592TER
594{
595 preCompute();
596
597 // If the transactor requires a valid account and the transaction doesn't
598 // list one, preflight will have already a flagged a failure.
599 auto const sle = view().peek(keylet::account(account_));
600
601 // sle must exist except for transactions
602 // that allow zero account.
603 XRPL_ASSERT(
604 sle != nullptr || account_ == beast::zero,
605 "xrpl::Transactor::apply : non-null SLE or zero account");
606
607 if (sle)
608 {
609 preFeeBalance_ = STAmount{(*sle)[sfBalance]}.xrp();
610
611 TER result = consumeSeqProxy(sle);
612 if (!isTesSuccess(result))
613 return result;
614
615 result = payFee();
616 if (!isTesSuccess(result))
617 return result;
618
619 if (sle->isFieldPresent(sfAccountTxnID))
620 sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
621
622 view().update(sle);
623 }
624
625 return doApply();
626}
627
628NotTEC
630 ReadView const& view,
631 ApplyFlags flags,
632 std::optional<uint256 const> const& parentBatchId,
633 AccountID const& idAccount,
634 STObject const& sigObject,
635 beast::Journal const j)
636{
637 {
638 auto const sle = view.read(keylet::account(idAccount));
639
640 if (view.rules().enabled(featureLendingProtocol) && isPseudoAccount(sle))
641 {
642 // Pseudo-accounts can't sign transactions. This check is gated on
643 // the Lending Protocol amendment because that's the project it was
644 // added under, and it doesn't justify another amendment
645 return tefBAD_AUTH;
646 }
647 }
648
649 auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey);
650 // Ignore signature check on batch inner transactions
651 if (parentBatchId && view.rules().enabled(featureBatch))
652 {
653 // Defensive Check: These values are also checked in Batch::preflight
654 if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() ||
655 sigObject.isFieldPresent(sfSigners))
656 {
657 return temINVALID_FLAG; // LCOV_EXCL_LINE
658 }
659 return tesSUCCESS;
660 }
661
662 if (((flags & tapDRY_RUN) != 0u) && pkSigner.empty() && !sigObject.isFieldPresent(sfSigners))
663 {
664 // simulate: skip signature validation when neither SigningPubKey nor
665 // Signers are provided
666 return tesSUCCESS;
667 }
668
669 // If the pk is empty and not simulate or simulate and signers,
670 // then we must be multi-signing.
671 if (sigObject.isFieldPresent(sfSigners))
672 {
673 return checkMultiSign(view, flags, idAccount, sigObject, j);
674 }
675
676 // Check Single Sign
677 XRPL_ASSERT(!pkSigner.empty(), "xrpl::Transactor::checkSign : non-empty signer");
678
679 if (!publicKeyType(makeSlice(pkSigner)))
680 {
681 JLOG(j.trace()) << "checkSign: signing public key type is unknown";
682 return tefBAD_AUTH; // FIXME: should be better error!
683 }
684
685 // Look up the account.
686 auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
687 auto const sleAccount = view.read(keylet::account(idAccount));
688 if (!sleAccount)
689 return terNO_ACCOUNT;
690
691 return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
692}
693
694NotTEC
696{
697 auto const idAccount = ctx.tx.isFieldPresent(sfDelegate) ? ctx.tx.getAccountID(sfDelegate)
698 : ctx.tx.getAccountID(sfAccount);
699 return checkSign(ctx.view, ctx.flags, ctx.parentBatchId, idAccount, ctx.tx, ctx.j);
700}
701
702NotTEC
704{
705 NotTEC ret = tesSUCCESS;
706 STArray const& signers{ctx.tx.getFieldArray(sfBatchSigners)};
707 for (auto const& signer : signers)
708 {
709 auto const idAccount = signer.getAccountID(sfAccount);
710
711 Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
712 if (pkSigner.empty())
713 {
714 if (ret = checkMultiSign(ctx.view, ctx.flags, idAccount, signer, ctx.j);
715 !isTesSuccess(ret))
716 return ret;
717 }
718 else
719 {
720 // LCOV_EXCL_START
721 if (!publicKeyType(makeSlice(pkSigner)))
722 return tefBAD_AUTH;
723 // LCOV_EXCL_STOP
724
725 auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
726 auto const sleAccount = ctx.view.read(keylet::account(idAccount));
727
728 // A batch can include transactions from an un-created account ONLY
729 // when the account master key is the signer
730 if (!sleAccount)
731 {
732 if (idAccount != idSigner)
733 return tefBAD_AUTH;
734
735 return tesSUCCESS;
736 }
737
738 if (ret = checkSingleSign(ctx.view, idSigner, idAccount, sleAccount, ctx.j);
739 !isTesSuccess(ret))
740 return ret;
741 }
742 }
743 return ret;
744}
745
746NotTEC
748 ReadView const& view,
749 AccountID const& idSigner,
750 AccountID const& idAccount,
752 beast::Journal const j)
753{
754 bool const isMasterDisabled = sleAccount->isFlag(lsfDisableMaster);
755
756 // Signed with regular key.
757 if ((*sleAccount)[~sfRegularKey] == idSigner)
758 {
759 return tesSUCCESS;
760 }
761
762 // Signed with enabled master key.
763 if (!isMasterDisabled && idAccount == idSigner)
764 {
765 return tesSUCCESS;
766 }
767
768 // Signed with disabled master key.
769 if (isMasterDisabled && idAccount == idSigner)
770 {
771 return tefMASTER_DISABLED;
772 }
773
774 // Signed with any other key.
775 return tefBAD_AUTH;
776}
777
778NotTEC
780 ReadView const& view,
781 ApplyFlags flags,
782 AccountID const& id,
783 STObject const& sigObject,
784 beast::Journal const j)
785{
786 // Get id's SignerList and Quorum.
787 std::shared_ptr<STLedgerEntry const> const sleAccountSigners = view.read(keylet::signers(id));
788 // If the signer list doesn't exist the account is not multi-signing.
789 if (!sleAccountSigners)
790 {
791 JLOG(j.trace()) << "applyTransaction: Invalid: Not a multi-signing account.";
793 }
794
795 // We have plans to support multiple SignerLists in the future. The
796 // presence and defaulted value of the SignerListID field will enable that.
797 XRPL_ASSERT(
798 sleAccountSigners->isFieldPresent(sfSignerListID),
799 "xrpl::Transactor::checkMultiSign : has signer list ID");
800 XRPL_ASSERT(
801 sleAccountSigners->getFieldU32(sfSignerListID) == 0,
802 "xrpl::Transactor::checkMultiSign : signer list ID is 0");
803
804 auto accountSigners = SignerEntries::deserialize(*sleAccountSigners, j, "ledger");
805 if (!accountSigners)
806 return accountSigners.error();
807
808 // Get the array of transaction signers.
809 STArray const& txSigners(sigObject.getFieldArray(sfSigners));
810
811 // Walk the accountSigners performing a variety of checks and see if
812 // the quorum is met.
813
814 // Both the multiSigners and accountSigners are sorted by account. So
815 // matching multi-signers to account signers should be a simple
816 // linear walk. *All* signers must be valid or the transaction fails.
817 std::uint32_t weightSum = 0;
818 auto iter = accountSigners->begin();
819 for (auto const& txSigner : txSigners)
820 {
821 AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
822
823 // Attempt to match the SignerEntry with a Signer;
824 while (iter->account < txSignerAcctID)
825 {
826 if (++iter == accountSigners->end())
827 {
828 JLOG(j.trace()) << "applyTransaction: Invalid SigningAccount.Account.";
829 return tefBAD_SIGNATURE;
830 }
831 }
832 if (iter->account != txSignerAcctID)
833 {
834 // The SigningAccount is not in the SignerEntries.
835 JLOG(j.trace()) << "applyTransaction: Invalid SigningAccount.Account.";
836 return tefBAD_SIGNATURE;
837 }
838
839 // We found the SigningAccount in the list of valid signers. Now we
840 // need to compute the accountID that is associated with the signer's
841 // public key.
842 auto const spk = txSigner.getFieldVL(sfSigningPubKey);
843
844 // spk being non-empty in non-simulate is checked in
845 // STTx::checkMultiSign
846 if (!spk.empty() && !publicKeyType(makeSlice(spk)))
847 {
848 JLOG(j.trace()) << "checkMultiSign: signing public key type is unknown";
849 return tefBAD_SIGNATURE;
850 }
851
852 XRPL_ASSERT(
853 (flags & tapDRY_RUN) || !spk.empty(),
854 "xrpl::Transactor::checkMultiSign : non-empty signer or "
855 "simulation");
856 AccountID const signingAcctIDFromPubKey =
857 spk.empty() ? txSignerAcctID : calcAccountID(PublicKey(makeSlice(spk)));
858
859 // Verify that the signingAcctID and the signingAcctIDFromPubKey
860 // belong together. Here are the rules:
861 //
862 // 1. "Phantom account": an account that is not in the ledger
863 // A. If signingAcctID == signingAcctIDFromPubKey and the
864 // signingAcctID is not in the ledger then we have a phantom
865 // account.
866 // B. Phantom accounts are always allowed as multi-signers.
867 //
868 // 2. "Master Key"
869 // A. signingAcctID == signingAcctIDFromPubKey, and signingAcctID
870 // is in the ledger.
871 // B. If the signingAcctID in the ledger does not have the
872 // asfDisableMaster flag set, then the signature is allowed.
873 //
874 // 3. "Regular Key"
875 // A. signingAcctID != signingAcctIDFromPubKey, and signingAcctID
876 // is in the ledger.
877 // B. If signingAcctIDFromPubKey == signingAcctID.RegularKey (from
878 // ledger) then the signature is allowed.
879 //
880 // No other signatures are allowed. (January 2015)
881
882 // In any of these cases we need to know whether the account is in
883 // the ledger. Determine that now.
884 auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
885
886 if (signingAcctIDFromPubKey == txSignerAcctID)
887 {
888 // Either Phantom or Master. Phantoms automatically pass.
889 if (sleTxSignerRoot)
890 {
891 // Master Key. Account may not have asfDisableMaster set.
892 std::uint32_t const signerAccountFlags = sleTxSignerRoot->getFieldU32(sfFlags);
893
894 if ((signerAccountFlags & lsfDisableMaster) != 0u)
895 {
896 JLOG(j.trace()) << "applyTransaction: Signer:Account lsfDisableMaster.";
897 return tefMASTER_DISABLED;
898 }
899 }
900 }
901 else
902 {
903 // May be a Regular Key. Let's find out.
904 // Public key must hash to the account's regular key.
905 if (!sleTxSignerRoot)
906 {
907 JLOG(j.trace()) << "applyTransaction: Non-phantom signer "
908 "lacks account root.";
909 return tefBAD_SIGNATURE;
910 }
911
912 if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
913 {
914 JLOG(j.trace()) << "applyTransaction: Account lacks RegularKey.";
915 return tefBAD_SIGNATURE;
916 }
917 if (signingAcctIDFromPubKey != sleTxSignerRoot->getAccountID(sfRegularKey))
918 {
919 JLOG(j.trace()) << "applyTransaction: Account doesn't match RegularKey.";
920 return tefBAD_SIGNATURE;
921 }
922 }
923 // The signer is legitimate. Add their weight toward the quorum.
924 weightSum += iter->weight;
925 }
926
927 // Cannot perform transaction if quorum is not met.
928 if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
929 {
930 JLOG(j.trace()) << "applyTransaction: Signers failed to meet quorum.";
931 return tefBAD_QUORUM;
932 }
933
934 // Met the quorum. Continue.
935 return tesSUCCESS;
936}
937
938//------------------------------------------------------------------------------
939
940static void
942{
943 int removed = 0;
944
945 for (auto const& index : offers)
946 {
947 if (auto const sleOffer = view.peek(keylet::offer(index)))
948 {
949 // offer is unfunded
950 offerDelete(view, sleOffer, viewJ);
951 if (++removed == unfundedOfferRemoveLimit)
952 return;
953 }
954 }
955}
956
957static void
959 ApplyView& view,
960 std::vector<uint256> const& offers,
961 beast::Journal viewJ)
962{
963 std::size_t removed = 0;
964
965 for (auto const& index : offers)
966 {
967 if (auto const offer = view.peek(keylet::nftoffer(index)))
968 {
969 nft::deleteTokenOffer(view, offer);
970 if (++removed == expiredOfferRemoveLimit)
971 return;
972 }
973 }
974}
975
976static void
978{
979 for (auto const& index : creds)
980 {
981 if (auto const sle = view.peek(keylet::credential(index)))
982 credentials::deleteSLE(view, sle, viewJ);
983 }
984}
985
986static void
988 ApplyView& view,
989 std::vector<uint256> const& trustLines,
990 beast::Journal viewJ)
991{
992 if (trustLines.size() > maxDeletableAMMTrustLines)
993 {
994 JLOG(viewJ.error()) << "removeDeletedTrustLines: deleted trustlines exceed max "
995 << trustLines.size();
996 return;
997 }
998
999 for (auto const& index : trustLines)
1000 {
1001 if (auto const sleState = view.peek({ltRIPPLE_STATE, index});
1002 !isTesSuccess(deleteAMMTrustLine(view, sleState, std::nullopt, viewJ)))
1003 {
1004 JLOG(viewJ.error()) << "removeDeletedTrustLines: failed to delete AMM trustline";
1005 }
1006 }
1007}
1008
1016{
1017 ctx_.discard();
1018
1019 auto const txnAcct = view().peek(keylet::account(ctx_.tx.getAccountID(sfAccount)));
1020
1021 // The account should never be missing from the ledger. But if it
1022 // is missing then we can't very well charge it a fee, can we?
1023 if (!txnAcct)
1024 return {tefINTERNAL, beast::zero};
1025
1026 auto const payerSle = view().peek(keylet::account(ctx_.tx.getFeePayer()));
1027 if (!payerSle)
1028 return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE
1029
1030 auto const balance = payerSle->getFieldAmount(sfBalance).xrp();
1031
1032 // balance should have already been checked in checkFee / preFlight.
1033 XRPL_ASSERT(
1034 balance != beast::zero && (!view().open() || balance >= fee),
1035 "xrpl::Transactor::reset : valid balance");
1036
1037 // We retry/reject the transaction if the account balance is zero or
1038 // we're applying against an open ledger and the balance is less than
1039 // the fee
1040 if (fee > balance)
1041 fee = balance;
1042
1043 // Since we reset the context, we need to charge the fee and update
1044 // the account's sequence number (or consume the Ticket) again.
1045 //
1046 // If for some reason we are unable to consume the ticket or sequence
1047 // then the ledger is corrupted. Rather than make things worse we
1048 // reject the transaction.
1049 payerSle->setFieldAmount(sfBalance, balance - fee);
1050 TER const ter{consumeSeqProxy(txnAcct)};
1051 XRPL_ASSERT(isTesSuccess(ter), "xrpl::Transactor::reset : result is tesSUCCESS");
1052
1053 if (isTesSuccess(ter))
1054 {
1055 view().update(txnAcct);
1056 if (payerSle != txnAcct)
1057 view().update(payerSle);
1058 }
1059
1060 return {ter, fee};
1061}
1062
1063// The sole purpose of this function is to provide a convenient, named
1064// location to set a breakpoint, to be used when replaying transactions.
1065void
1067{
1068 JLOG(j_.debug()) << "Transaction trapped: " << txHash;
1069}
1070
1071//------------------------------------------------------------------------------
1074{
1075 JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID();
1076
1077 // These global updates really should have been for every Transaction
1078 // step: preflight, preclaim, and doApply. And even calculateBaseFee. See
1079 // with_txn_type().
1080 //
1081 // raii classes for the current ledger rules.
1082 // fixUniversalNumber predate the rulesGuard and should be replaced.
1083 NumberSO const stNumberSO{view().rules().enabled(fixUniversalNumber)};
1084 CurrentTransactionRulesGuard const currentTransactionRulesGuard(view().rules());
1085
1086#ifdef DEBUG
1087 {
1088 Serializer ser;
1089 ctx_.tx.add(ser);
1090 SerialIter sit(ser.slice());
1091 STTx const s2(sit);
1092
1093 if (!s2.isEquivalent(ctx_.tx))
1094 {
1095 // LCOV_EXCL_START
1096 JLOG(j_.fatal()) << "Transaction serdes mismatch";
1098 JLOG(j_.fatal()) << s2.getJson(JsonOptions::none);
1099 UNREACHABLE("xrpl::Transactor::operator() : transaction serdes mismatch");
1100 // LCOV_EXCL_STOP
1101 }
1102 }
1103#endif
1104
1105 if (auto const& trap = ctx_.registry.get().getTrapTxID();
1106 trap && *trap == ctx_.tx.getTransactionID())
1107 {
1108 trapTransaction(*trap);
1109 }
1110
1111 auto result = ctx_.preclaimResult;
1112 if (isTesSuccess(result))
1113 result = apply();
1114
1115 // No transaction can return temUNKNOWN from apply,
1116 // and it can't be passed in from a preclaim.
1117 XRPL_ASSERT(result != temUNKNOWN, "xrpl::Transactor::operator() : result is not temUNKNOWN");
1118
1119 if (auto stream = j_.trace())
1120 stream << "preclaim result: " << transToken(result);
1121
1122 bool applied = isTesSuccess(result);
1123 auto fee = ctx_.tx.getFieldAmount(sfFee).xrp();
1124
1126 result = tecOVERSIZE;
1127
1128 if (isTecClaim(result) && ((view().flags() & tapFAIL_HARD) != 0u))
1129 {
1130 // If the tapFAIL_HARD flag is set, a tec result
1131 // must not do anything
1132 ctx_.discard();
1133 applied = false;
1134 }
1135 else if (
1136 (result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) ||
1137 (result == tecEXPIRED) || (isTecClaimHardFail(result, view().flags())))
1138 {
1139 JLOG(j_.trace()) << "reapplying because of " << transToken(result);
1140
1141 // FIXME: This mechanism for doing work while returning a `tec` is
1142 // awkward and very limiting. A more general purpose approach
1143 // should be used, making it possible to do more useful work
1144 // when transactions fail with a `tec` code.
1145 std::vector<uint256> removedOffers;
1146 std::vector<uint256> removedTrustLines;
1147 std::vector<uint256> expiredNFTokenOffers;
1148 std::vector<uint256> expiredCredentials;
1149
1150 bool const doOffers = ((result == tecOVERSIZE) || (result == tecKILLED));
1151 bool const doLines = (result == tecINCOMPLETE);
1152 bool const doNFTokenOffers = (result == tecEXPIRED);
1153 bool const doCredentials = (result == tecEXPIRED);
1154 if (doOffers || doLines || doNFTokenOffers || doCredentials)
1155 {
1156 ctx_.visit([doOffers,
1157 &removedOffers,
1158 doLines,
1159 &removedTrustLines,
1160 doNFTokenOffers,
1161 &expiredNFTokenOffers,
1162 doCredentials,
1163 &expiredCredentials](
1164 uint256 const& index,
1165 bool isDelete,
1166 std::shared_ptr<SLE const> const& before,
1168 if (isDelete)
1169 {
1170 XRPL_ASSERT(
1171 before && after,
1172 "xrpl::Transactor::operator()::visit : non-null SLE "
1173 "inputs");
1174 if (doOffers && before && after && (before->getType() == ltOFFER) &&
1175 (before->getFieldAmount(sfTakerPays) == after->getFieldAmount(sfTakerPays)))
1176 {
1177 // Removal of offer found or made unfunded
1178 removedOffers.push_back(index);
1179 }
1180
1181 if (doLines && before && after && (before->getType() == ltRIPPLE_STATE))
1182 {
1183 // Removal of obsolete AMM trust line
1184 removedTrustLines.push_back(index);
1185 }
1186
1187 if (doNFTokenOffers && before && after &&
1188 (before->getType() == ltNFTOKEN_OFFER))
1189 expiredNFTokenOffers.push_back(index);
1190
1191 if (doCredentials && before && after && (before->getType() == ltCREDENTIAL))
1192 expiredCredentials.push_back(index);
1193 }
1194 });
1195 }
1196
1197 // Reset the context, potentially adjusting the fee.
1198 {
1199 auto const resetResult = reset(fee);
1200 if (!isTesSuccess(resetResult.first))
1201 result = resetResult.first;
1202
1203 fee = resetResult.second;
1204 }
1205
1206 // If necessary, remove any offers found unfunded during processing
1207 if ((result == tecOVERSIZE) || (result == tecKILLED))
1208 {
1209 removeUnfundedOffers(view(), removedOffers, ctx_.registry.get().getJournal("View"));
1210 }
1211
1212 if (result == tecEXPIRED)
1213 {
1215 view(), expiredNFTokenOffers, ctx_.registry.get().getJournal("View"));
1216 }
1217
1218 if (result == tecINCOMPLETE)
1219 {
1221 view(), removedTrustLines, ctx_.registry.get().getJournal("View"));
1222 }
1223
1224 if (result == tecEXPIRED)
1225 {
1227 view(), expiredCredentials, ctx_.registry.get().getJournal("View"));
1228 }
1229
1230 applied = isTecClaim(result);
1231 }
1232
1233 if (applied)
1234 {
1235 // Check invariants: if `tecINVARIANT_FAILED` is not returned, we can
1236 // proceed to apply the tx
1237 result = ctx_.checkInvariants(result, fee);
1238
1239 if (result == tecINVARIANT_FAILED)
1240 {
1241 // if invariants checking failed again, reset the context and
1242 // attempt to only claim a fee.
1243 auto const resetResult = reset(fee);
1244 if (!isTesSuccess(resetResult.first))
1245 result = resetResult.first;
1246
1247 fee = resetResult.second;
1248
1249 // Check invariants again to ensure the fee claiming doesn't
1250 // violate invariants.
1251 if (isTesSuccess(result) || isTecClaim(result))
1252 result = ctx_.checkInvariants(result, fee);
1253 }
1254
1255 // We ran through the invariant checker, which can, in some cases,
1256 // return a tef error code. Don't apply the transaction in that case.
1257 if (!isTecClaim(result) && !isTesSuccess(result))
1258 applied = false;
1259 }
1260
1261 std::optional<TxMeta> metadata;
1262 if (applied)
1263 {
1264 // Transaction succeeded fully or (retries are not allowed and the
1265 // transaction could claim a fee)
1266
1267 // The transactor and invariant checkers guarantee that this will
1268 // *never* trigger but if it, somehow, happens, don't allow a tx
1269 // that charges a negative fee.
1270 if (fee < beast::zero)
1271 Throw<std::logic_error>("fee charged is negative!");
1272
1273 // Charge whatever fee they specified. The fee has already been
1274 // deducted from the balance of the account that issued the
1275 // transaction. We just need to account for it in the ledger
1276 // header.
1277 if (!view().open() && fee != beast::zero)
1278 ctx_.destroyXRP(fee);
1279
1280 // Once we call apply, we will no longer be able to look at view()
1281 metadata = ctx_.apply(result);
1282 }
1283
1284 if ((ctx_.flags() & tapDRY_RUN) != 0u)
1285 {
1286 applied = false;
1287 }
1288
1289 JLOG(j_.trace()) << (applied ? "applied " : "not applied ") << transToken(result);
1290
1291 return {result, applied, metadata};
1292}
1293
1294} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:325
Stream error() const
Definition Journal.h:319
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
State information when applying a tx.
std::size_t size()
Get the number of unapplied changes.
STTx const & tx
void destroyXRP(XRPAmount const &fee)
ApplyFlags const & flags() const
void discard()
Discard changes and start fresh.
std::reference_wrapper< ServiceRegistry > registry
std::optional< TxMeta > apply(TER)
Apply the transaction result to the base.
TER checkInvariants(TER const result, XRPAmount const fee)
Applies all invariant checkers one by one.
TER const preclaimResult
void visit(std::function< void(uint256 const &key, bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after)> const &func)
Visit unapplied changes.
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:116
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
RAII class to set and restore the current transaction rules.
Definition Rules.h:89
RAII class to set and restore the Number switchover.
Definition IOUAmount.h:193
A public key.
Definition PublicKey.h:42
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual bool txExists(key_type const &key) const =0
Returns true if a tx exists in the tx map.
virtual bool open() const =0
Returns true if this reflects an open ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition ReadView.h:97
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:120
XRPAmount xrp() const
Definition STAmount.cpp:261
size_type size() const
Definition STArray.h:225
virtual std::string getFullText() const
Definition STBase.cpp:65
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:641
bool isEquivalent(STBase const &t) const override
Definition STObject.cpp:342
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:593
STArray const & getFieldArray(SField const &field) const
Definition STObject.cpp:680
void add(Serializer &s) const override
Definition STObject.cpp:119
bool isFlag(std::uint32_t) const
Definition STObject.cpp:503
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:456
uint256 getFieldH256(SField const &field) const
Definition STObject.cpp:623
STBase const & peekAtField(SField const &field) const
Definition STObject.cpp:401
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:635
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:649
std::uint32_t getFlags() const
Definition STObject.cpp:509
Json::Value getJson(JsonOptions options) const override
Definition STTx.cpp:317
SeqProxy getSeqProxy() const
Definition STTx.cpp:194
AccountID getFeePayer() const
Definition STTx.cpp:217
uint256 getTransactionID() const
Definition STTx.h:200
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:36
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:56
constexpr bool isTicket() const
Definition SeqProxy.h:74
constexpr std::uint32_t value() const
Definition SeqProxy.h:62
constexpr bool isSeq() const
Definition SeqProxy.h:68
Slice slice() const noexcept
Definition Serializer.h:44
Service registry for dependency injection.
virtual LoadFeeTrack & getFeeTrack()=0
static Expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string_view annotation)
An immutable linear range of bytes.
Definition Slice.h:26
bool empty() const noexcept
Return true if the byte range is empty.
Definition Slice.h:50
static NotTEC preflight1(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the account and fee fields.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
TER consumeSeqProxy(SLE::pointer const &sleAccount)
AccountID const account_
Definition Transactor.h:116
void trapTransaction(uint256) const
static TER checkFee(PreclaimContext const &ctx, XRPAmount baseFee)
static XRPAmount minimumFee(ServiceRegistry &registry, XRPAmount baseFee, Fees const &fees, ApplyFlags flags)
Compute the minimum fee required to process a transaction with a given baseFee based on the current s...
static NotTEC checkSign(PreclaimContext const &ctx)
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
ApplyResult operator()()
Process the transaction.
static NotTEC checkPermission(ReadView const &view, STTx const &tx)
static NotTEC preflightSigValidated(PreflightContext const &ctx)
static NotTEC checkBatchSign(PreclaimContext const &ctx)
static NotTEC checkSeqProxy(ReadView const &view, STTx const &tx, beast::Journal j)
beast::Journal const j_
Definition Transactor.h:114
virtual TER doApply()=0
static NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
ApplyView & view()
Definition Transactor.h:132
static NotTEC checkSingleSign(ReadView const &view, AccountID const &idSigner, AccountID const &idAccount, std::shared_ptr< SLE const > sleAccount, beast::Journal const j)
Transactor(Transactor const &)=delete
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
static NotTEC checkPriorTxAndLastLedger(PreclaimContext const &ctx)
XRPAmount preFeeBalance_
Definition Transactor.h:117
static NotTEC checkMultiSign(ReadView const &view, ApplyFlags flags, AccountID const &id, STObject const &sigObject, beast::Journal const j)
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
virtual void preCompute()
ApplyContext & ctx_
Definition Transactor.h:112
std::pair< TER, XRPAmount > reset(XRPAmount fee)
Reset the context, discarding any changes made and adjust the fee.
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
T empty(T... args)
T is_same_v
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
std::optional< NotTEC > preflightCheckSimulateKeys(ApplyFlags flags, STObject const &sigObject, beast::Journal j)
Checks the special signing key state needed for simulation.
NotTEC preflightCheckSigningKey(STObject const &sigObject, beast::Journal j)
Checks the validity of the transactor signing key.
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:295
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:386
static ticket_t const ticket
Definition Indexes.h:148
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:336
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition Indexes.cpp:243
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:418
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:498
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telWRONG_NETWORK
Definition TER.h:45
@ telNETWORK_ID_MAKES_TX_NON_CANONICAL
Definition TER.h:47
@ telINSUF_FEE_P
Definition TER.h:37
@ telREQUIRES_NETWORK_ID
Definition TER.h:46
@ terPRE_SEQ
Definition TER.h:201
@ terINSUF_FEE_B
Definition TER.h:196
@ terNO_DELEGATE_PERMISSION
Definition TER.h:210
@ terNO_ACCOUNT
Definition TER.h:197
@ terPRE_TICKET
Definition TER.h:206
static void removeExpiredNFTokenOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
constexpr FlagValue tfInnerBatchTxn
Definition TxFlags.h:41
bool isLegalAmount(XRPAmount const &amount)
Returns true if the amount does not exceed the initial XRP in existence.
@ SigBad
Signature is bad. Didn't do local checks.
std::size_t constexpr expiredOfferRemoveLimit
The maximum number of expired offers to delete at once.
Definition Protocol.h:31
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
static void removeExpiredCredentials(ApplyView &view, std::vector< uint256 > const &creds, beast::Journal viewJ)
bool isTecClaimHardFail(TER ter, ApplyFlags flags)
Return true if the transaction can claim a fee (tec), and the ApplyFlags do not allow soft failures.
Definition applySteps.h:28
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules)
Checks transaction signature and local checks.
Definition apply.cpp:21
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:138
@ tefBAD_QUORUM
Definition TER.h:160
@ tefMAX_LEDGER
Definition TER.h:158
@ tefMASTER_DISABLED
Definition TER.h:157
@ tefALREADY
Definition TER.h:147
@ tefBAD_LEDGER
Definition TER.h:150
@ tefWRONG_PRIOR
Definition TER.h:156
@ tefNO_TICKET
Definition TER.h:165
@ tefBAD_SIGNATURE
Definition TER.h:159
@ tefINTERNAL
Definition TER.h:153
@ tefBAD_AUTH
Definition TER.h:149
@ tefPAST_SEQ
Definition TER.h:155
@ tefNOT_MULTI_SIGNING
Definition TER.h:161
std::uint16_t constexpr maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
Definition Protocol.h:276
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
static void removeUnfundedOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
std::string transToken(TER code)
Definition TER.cpp:243
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
Returns true if and only if sleAcct is a pseudo-account or specific pseudo-accounts in pseudoFieldFil...
std::size_t constexpr oversizeMetaDataCap
The maximum number of metadata entries allowed in one transaction.
Definition Protocol.h:34
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
@ open
We haven't closed our ledger yet, but others might have.
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:523
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
AccountID calcAccountID(PublicKey const &pk)
static void removeDeletedTrustLines(ApplyView &view, std::vector< uint256 > const &trustLines, beast::Journal viewJ)
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
ApplyFlags
Definition ApplyView.h:10
@ tapDRY_RUN
Definition ApplyView.h:29
@ tapFAIL_HARD
Definition ApplyView.h:15
@ tapUNLIMITED
Definition ApplyView.h:22
@ tapBATCH
Definition ApplyView.h:25
@ temBAD_FEE
Definition TER.h:72
@ temINVALID
Definition TER.h:90
@ temINVALID_FLAG
Definition TER.h:91
@ temBAD_SRC_ACCOUNT
Definition TER.h:86
@ temSEQ_AND_TICKET
Definition TER.h:106
@ temDISABLED
Definition TER.h:94
@ temUNKNOWN
Definition TER.h:104
@ temBAD_SIGNATURE
Definition TER.h:85
@ temBAD_SIGNER
Definition TER.h:95
XRPAmount scaleFeeLoad(XRPAmount fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
std::size_t constexpr unfundedOfferRemoveLimit
The maximum number of unfunded offers to delete at once.
Definition Protocol.h:28
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid and flags.
@ tecINSUFF_FEE
Definition TER.h:283
@ tecINCOMPLETE
Definition TER.h:316
@ tecINVARIANT_FAILED
Definition TER.h:294
@ tecEXPIRED
Definition TER.h:295
@ tecKILLED
Definition TER.h:297
@ tecOVERSIZE
Definition TER.h:292
bool isTecClaim(TER x) noexcept
Definition TER.h:658
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:215
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:582
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:809
constexpr FlagValue tfUniversalMask
Definition TxFlags.h:43
@ tesSUCCESS
Definition TER.h:225
std::string to_short_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:609
T push_back(T... args)
T size(T... args)
Reflects the fee settings for a particular ledger.
XRPAmount increment
Additional XRP reserve required per owned ledger object.
XRPAmount base
Cost of a reference transaction in drops.
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
std::reference_wrapper< ServiceRegistry > registry
Definition Transactor.h:59
beast::Journal const j
Definition Transactor.h:65
std::optional< uint256 const > const parentBatchId
Definition Transactor.h:64
State information when preflighting a tx.
Definition Transactor.h:14
beast::Journal const j
Definition Transactor.h:21
std::optional< uint256 const > parentBatchId
Definition Transactor.h:20
std::reference_wrapper< ServiceRegistry > registry
Definition Transactor.h:16