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