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