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