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