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