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