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