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