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 PreclaimContext const& ctx,
664 AccountID const& idAccount,
665 STObject const& sigObject)
666{
667 auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey);
668 // Ignore signature check on batch inner transactions
669 if (sigObject.isFlag(tfInnerBatchTxn) &&
670 ctx.view.rules().enabled(featureBatch))
671 {
672 // Defensive Check: These values are also checked in Batch::preflight
673 if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() ||
674 sigObject.isFieldPresent(sfSigners))
675 {
676 return temINVALID_FLAG; // LCOV_EXCL_LINE
677 }
678 return tesSUCCESS;
679 }
680
681 if ((ctx.flags & tapDRY_RUN) && pkSigner.empty() &&
682 !sigObject.isFieldPresent(sfSigners))
683 {
684 // simulate: skip signature validation when neither SigningPubKey nor
685 // Signers are provided
686 return tesSUCCESS;
687 }
688
689 // If the pk is empty and not simulate or simulate and signers,
690 // then we must be multi-signing.
691 if (ctx.tx.isFieldPresent(sfSigners))
692 {
693 return checkMultiSign(ctx, idAccount, sigObject);
694 }
695
696 // Check Single Sign
697 XRPL_ASSERT(
698 !pkSigner.empty(), "ripple::Transactor::checkSign : non-empty signer");
699
700 if (!publicKeyType(makeSlice(pkSigner)))
701 {
702 JLOG(ctx.j.trace()) << "checkSign: signing public key type is unknown";
703 return tefBAD_AUTH; // FIXME: should be better error!
704 }
705
706 // Look up the account.
707 auto const idSigner = pkSigner.empty()
708 ? idAccount
709 : calcAccountID(PublicKey(makeSlice(pkSigner)));
710 auto const sleAccount = ctx.view.read(keylet::account(idAccount));
711 if (!sleAccount)
712 return terNO_ACCOUNT;
713
714 return checkSingleSign(ctx, idSigner, idAccount, sleAccount);
715}
716
717NotTEC
719{
720 auto const idAccount = ctx.tx.isFieldPresent(sfDelegate)
721 ? ctx.tx.getAccountID(sfDelegate)
722 : ctx.tx.getAccountID(sfAccount);
723 return checkSign(ctx, idAccount, ctx.tx);
724}
725
726NotTEC
728{
729 NotTEC ret = tesSUCCESS;
730 STArray const& signers{ctx.tx.getFieldArray(sfBatchSigners)};
731 for (auto const& signer : signers)
732 {
733 auto const idAccount = signer.getAccountID(sfAccount);
734
735 Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
736 if (pkSigner.empty())
737 {
738 if (ret = checkMultiSign(ctx, idAccount, signer);
739 !isTesSuccess(ret))
740 return ret;
741 }
742 else
743 {
744 // LCOV_EXCL_START
745 if (!publicKeyType(makeSlice(pkSigner)))
746 return tefBAD_AUTH;
747 // LCOV_EXCL_STOP
748
749 auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
750 auto const sleAccount = ctx.view.read(keylet::account(idAccount));
751
752 // A batch can include transactions from an un-created account ONLY
753 // when the account master key is the signer
754 if (!sleAccount)
755 {
756 if (idAccount != idSigner)
757 return tefBAD_AUTH;
758
759 return tesSUCCESS;
760 }
761
762 if (ret = checkSingleSign(ctx, idSigner, idAccount, sleAccount);
763 !isTesSuccess(ret))
764 return ret;
765 }
766 }
767 return ret;
768}
769
770NotTEC
772 PreclaimContext const& ctx,
773 AccountID const& idSigner,
774 AccountID const& idAccount,
776{
777 bool const isMasterDisabled = sleAccount->isFlag(lsfDisableMaster);
778
779 if (ctx.view.rules().enabled(fixMasterKeyAsRegularKey))
780 {
781 // Signed with regular key.
782 if ((*sleAccount)[~sfRegularKey] == idSigner)
783 {
784 return tesSUCCESS;
785 }
786
787 // Signed with enabled mater key.
788 if (!isMasterDisabled && idAccount == idSigner)
789 {
790 return tesSUCCESS;
791 }
792
793 // Signed with disabled master key.
794 if (isMasterDisabled && idAccount == idSigner)
795 {
796 return tefMASTER_DISABLED;
797 }
798
799 // Signed with any other key.
800 return tefBAD_AUTH;
801 }
802
803 if (idSigner == idAccount)
804 {
805 // Signing with the master key. Continue if it is not disabled.
806 if (isMasterDisabled)
807 return tefMASTER_DISABLED;
808 }
809 else if ((*sleAccount)[~sfRegularKey] == idSigner)
810 {
811 // Signing with the regular key. Continue.
812 }
813 else if (sleAccount->isFieldPresent(sfRegularKey))
814 {
815 // Signing key does not match master or regular key.
816 JLOG(ctx.j.trace())
817 << "checkSingleSign: Not authorized to use account.";
818 return tefBAD_AUTH;
819 }
820 else
821 {
822 // No regular key on account and signing key does not match master key.
823 // FIXME: Why differentiate this case from tefBAD_AUTH?
824 JLOG(ctx.j.trace())
825 << "checkSingleSign: Not authorized to use account.";
826 return tefBAD_AUTH_MASTER;
827 }
828
829 return tesSUCCESS;
830}
831
832NotTEC
834 PreclaimContext const& ctx,
835 AccountID const& id,
836 STObject const& sigObject)
837{
838 // Get id's SignerList and Quorum.
839 std::shared_ptr<STLedgerEntry const> sleAccountSigners =
840 ctx.view.read(keylet::signers(id));
841 // If the signer list doesn't exist the account is not multi-signing.
842 if (!sleAccountSigners)
843 {
844 JLOG(ctx.j.trace())
845 << "applyTransaction: Invalid: Not a multi-signing account.";
847 }
848
849 // We have plans to support multiple SignerLists in the future. The
850 // presence and defaulted value of the SignerListID field will enable that.
851 XRPL_ASSERT(
852 sleAccountSigners->isFieldPresent(sfSignerListID),
853 "ripple::Transactor::checkMultiSign : has signer list ID");
854 XRPL_ASSERT(
855 sleAccountSigners->getFieldU32(sfSignerListID) == 0,
856 "ripple::Transactor::checkMultiSign : signer list ID is 0");
857
858 auto accountSigners =
859 SignerEntries::deserialize(*sleAccountSigners, ctx.j, "ledger");
860 if (!accountSigners)
861 return accountSigners.error();
862
863 // Get the array of transaction signers.
864 STArray const& txSigners(sigObject.getFieldArray(sfSigners));
865
866 // Walk the accountSigners performing a variety of checks and see if
867 // the quorum is met.
868
869 // Both the multiSigners and accountSigners are sorted by account. So
870 // matching multi-signers to account signers should be a simple
871 // linear walk. *All* signers must be valid or the transaction fails.
872 std::uint32_t weightSum = 0;
873 auto iter = accountSigners->begin();
874 for (auto const& txSigner : txSigners)
875 {
876 AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
877
878 // Attempt to match the SignerEntry with a Signer;
879 while (iter->account < txSignerAcctID)
880 {
881 if (++iter == accountSigners->end())
882 {
883 JLOG(ctx.j.trace())
884 << "applyTransaction: Invalid SigningAccount.Account.";
885 return tefBAD_SIGNATURE;
886 }
887 }
888 if (iter->account != txSignerAcctID)
889 {
890 // The SigningAccount is not in the SignerEntries.
891 JLOG(ctx.j.trace())
892 << "applyTransaction: Invalid SigningAccount.Account.";
893 return tefBAD_SIGNATURE;
894 }
895
896 // We found the SigningAccount in the list of valid signers. Now we
897 // need to compute the accountID that is associated with the signer's
898 // public key.
899 auto const spk = txSigner.getFieldVL(sfSigningPubKey);
900
901 // spk being non-empty in non-simulate is checked in
902 // STTx::checkMultiSign
903 if (!spk.empty() && !publicKeyType(makeSlice(spk)))
904 {
905 JLOG(ctx.j.trace())
906 << "checkMultiSign: signing public key type is unknown";
907 return tefBAD_SIGNATURE;
908 }
909
910 XRPL_ASSERT(
911 (ctx.flags & tapDRY_RUN) || !spk.empty(),
912 "ripple::Transactor::checkMultiSign : non-empty signer or "
913 "simulation");
914 AccountID const signingAcctIDFromPubKey = spk.empty()
915 ? txSignerAcctID
917
918 // Verify that the signingAcctID and the signingAcctIDFromPubKey
919 // belong together. Here are the rules:
920 //
921 // 1. "Phantom account": an account that is not in the ledger
922 // A. If signingAcctID == signingAcctIDFromPubKey and the
923 // signingAcctID is not in the ledger then we have a phantom
924 // account.
925 // B. Phantom accounts are always allowed as multi-signers.
926 //
927 // 2. "Master Key"
928 // A. signingAcctID == signingAcctIDFromPubKey, and signingAcctID
929 // is in the ledger.
930 // B. If the signingAcctID in the ledger does not have the
931 // asfDisableMaster flag set, then the signature is allowed.
932 //
933 // 3. "Regular Key"
934 // A. signingAcctID != signingAcctIDFromPubKey, and signingAcctID
935 // is in the ledger.
936 // B. If signingAcctIDFromPubKey == signingAcctID.RegularKey (from
937 // ledger) then the signature is allowed.
938 //
939 // No other signatures are allowed. (January 2015)
940
941 // In any of these cases we need to know whether the account is in
942 // the ledger. Determine that now.
943 auto const sleTxSignerRoot =
944 ctx.view.read(keylet::account(txSignerAcctID));
945
946 if (signingAcctIDFromPubKey == txSignerAcctID)
947 {
948 // Either Phantom or Master. Phantoms automatically pass.
949 if (sleTxSignerRoot)
950 {
951 // Master Key. Account may not have asfDisableMaster set.
952 std::uint32_t const signerAccountFlags =
953 sleTxSignerRoot->getFieldU32(sfFlags);
954
955 if (signerAccountFlags & lsfDisableMaster)
956 {
957 JLOG(ctx.j.trace())
958 << "applyTransaction: Signer:Account lsfDisableMaster.";
959 return tefMASTER_DISABLED;
960 }
961 }
962 }
963 else
964 {
965 // May be a Regular Key. Let's find out.
966 // Public key must hash to the account's regular key.
967 if (!sleTxSignerRoot)
968 {
969 JLOG(ctx.j.trace()) << "applyTransaction: Non-phantom signer "
970 "lacks account root.";
971 return tefBAD_SIGNATURE;
972 }
973
974 if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
975 {
976 JLOG(ctx.j.trace())
977 << "applyTransaction: Account lacks RegularKey.";
978 return tefBAD_SIGNATURE;
979 }
980 if (signingAcctIDFromPubKey !=
981 sleTxSignerRoot->getAccountID(sfRegularKey))
982 {
983 JLOG(ctx.j.trace())
984 << "applyTransaction: Account doesn't match RegularKey.";
985 return tefBAD_SIGNATURE;
986 }
987 }
988 // The signer is legitimate. Add their weight toward the quorum.
989 weightSum += iter->weight;
990 }
991
992 // Cannot perform transaction if quorum is not met.
993 if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
994 {
995 JLOG(ctx.j.trace())
996 << "applyTransaction: Signers failed to meet quorum.";
997 return tefBAD_QUORUM;
998 }
999
1000 // Met the quorum. Continue.
1001 return tesSUCCESS;
1002}
1003
1004//------------------------------------------------------------------------------
1005
1006static void
1008 ApplyView& view,
1009 std::vector<uint256> const& offers,
1010 beast::Journal viewJ)
1011{
1012 int removed = 0;
1013
1014 for (auto const& index : offers)
1015 {
1016 if (auto const sleOffer = view.peek(keylet::offer(index)))
1017 {
1018 // offer is unfunded
1019 offerDelete(view, sleOffer, viewJ);
1020 if (++removed == unfundedOfferRemoveLimit)
1021 return;
1022 }
1023 }
1024}
1025
1026static void
1028 ApplyView& view,
1029 std::vector<uint256> const& offers,
1030 beast::Journal viewJ)
1031{
1032 std::size_t removed = 0;
1033
1034 for (auto const& index : offers)
1035 {
1036 if (auto const offer = view.peek(keylet::nftoffer(index)))
1037 {
1038 nft::deleteTokenOffer(view, offer);
1039 if (++removed == expiredOfferRemoveLimit)
1040 return;
1041 }
1042 }
1043}
1044
1045static void
1047 ApplyView& view,
1048 std::vector<uint256> const& creds,
1049 beast::Journal viewJ)
1050{
1051 for (auto const& index : creds)
1052 {
1053 if (auto const sle = view.peek(keylet::credential(index)))
1054 credentials::deleteSLE(view, sle, viewJ);
1055 }
1056}
1057
1058static void
1060 ApplyView& view,
1061 std::vector<uint256> const& trustLines,
1062 beast::Journal viewJ)
1063{
1064 if (trustLines.size() > maxDeletableAMMTrustLines)
1065 {
1066 JLOG(viewJ.error())
1067 << "removeDeletedTrustLines: deleted trustlines exceed max "
1068 << trustLines.size();
1069 return;
1070 }
1071
1072 for (auto const& index : trustLines)
1073 {
1074 if (auto const sleState = view.peek({ltRIPPLE_STATE, index});
1075 deleteAMMTrustLine(view, sleState, std::nullopt, viewJ) !=
1076 tesSUCCESS)
1077 {
1078 JLOG(viewJ.error())
1079 << "removeDeletedTrustLines: failed to delete AMM trustline";
1080 }
1081 }
1082}
1083
1091{
1092 ctx_.discard();
1093
1094 auto const txnAcct =
1096
1097 // The account should never be missing from the ledger. But if it
1098 // is missing then we can't very well charge it a fee, can we?
1099 if (!txnAcct)
1100 return {tefINTERNAL, beast::zero};
1101
1102 auto const payerSle = ctx_.tx.isFieldPresent(sfDelegate)
1103 ? view().peek(keylet::account(ctx_.tx.getAccountID(sfDelegate)))
1104 : txnAcct;
1105 if (!payerSle)
1106 return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE
1107
1108 auto const balance = payerSle->getFieldAmount(sfBalance).xrp();
1109
1110 // balance should have already been checked in checkFee / preFlight.
1111 XRPL_ASSERT(
1112 balance != beast::zero && (!view().open() || balance >= fee),
1113 "ripple::Transactor::reset : valid balance");
1114
1115 // We retry/reject the transaction if the account balance is zero or
1116 // we're applying against an open ledger and the balance is less than
1117 // the fee
1118 if (fee > balance)
1119 fee = balance;
1120
1121 // Since we reset the context, we need to charge the fee and update
1122 // the account's sequence number (or consume the Ticket) again.
1123 //
1124 // If for some reason we are unable to consume the ticket or sequence
1125 // then the ledger is corrupted. Rather than make things worse we
1126 // reject the transaction.
1127 payerSle->setFieldAmount(sfBalance, balance - fee);
1128 TER const ter{consumeSeqProxy(txnAcct)};
1129 XRPL_ASSERT(
1130 isTesSuccess(ter), "ripple::Transactor::reset : result is tesSUCCESS");
1131
1132 if (isTesSuccess(ter))
1133 {
1134 view().update(txnAcct);
1135 if (payerSle != txnAcct)
1136 view().update(payerSle);
1137 }
1138
1139 return {ter, fee};
1140}
1141
1142// The sole purpose of this function is to provide a convenient, named
1143// location to set a breakpoint, to be used when replaying transactions.
1144void
1146{
1147 JLOG(j_.debug()) << "Transaction trapped: " << txHash;
1148}
1149
1150//------------------------------------------------------------------------------
1153{
1154 JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID();
1155
1156 // raii classes for the current ledger rules. fixSTAmountCanonicalize and
1157 // fixSTAmountCanonicalize predate the rulesGuard and should be replaced.
1158 STAmountSO stAmountSO{view().rules().enabled(fixSTAmountCanonicalize)};
1159 NumberSO stNumberSO{view().rules().enabled(fixUniversalNumber)};
1160 CurrentTransactionRulesGuard currentTransctionRulesGuard(view().rules());
1161
1162#ifdef DEBUG
1163 {
1164 Serializer ser;
1165 ctx_.tx.add(ser);
1166 SerialIter sit(ser.slice());
1167 STTx s2(sit);
1168
1169 if (!s2.isEquivalent(ctx_.tx))
1170 {
1171 JLOG(j_.fatal()) << "Transaction serdes mismatch";
1173 JLOG(j_.fatal()) << s2.getJson(JsonOptions::none);
1174 UNREACHABLE(
1175 "ripple::Transactor::operator() : transaction serdes mismatch");
1176 }
1177 }
1178#endif
1179
1180 if (auto const& trap = ctx_.app.trapTxID();
1181 trap && *trap == ctx_.tx.getTransactionID())
1182 {
1183 trapTransaction(*trap);
1184 }
1185
1186 auto result = ctx_.preclaimResult;
1187 if (result == tesSUCCESS)
1188 result = apply();
1189
1190 // No transaction can return temUNKNOWN from apply,
1191 // and it can't be passed in from a preclaim.
1192 XRPL_ASSERT(
1193 result != temUNKNOWN,
1194 "ripple::Transactor::operator() : result is not temUNKNOWN");
1195
1196 if (auto stream = j_.trace())
1197 stream << "preclaim result: " << transToken(result);
1198
1199 bool applied = isTesSuccess(result);
1200 auto fee = ctx_.tx.getFieldAmount(sfFee).xrp();
1201
1203 result = tecOVERSIZE;
1204
1205 if (isTecClaim(result) && (view().flags() & tapFAIL_HARD))
1206 {
1207 // If the tapFAIL_HARD flag is set, a tec result
1208 // must not do anything
1209 ctx_.discard();
1210 applied = false;
1211 }
1212 else if (
1213 (result == tecOVERSIZE) || (result == tecKILLED) ||
1214 (result == tecINCOMPLETE) || (result == tecEXPIRED) ||
1215 (isTecClaimHardFail(result, view().flags())))
1216 {
1217 JLOG(j_.trace()) << "reapplying because of " << transToken(result);
1218
1219 // FIXME: This mechanism for doing work while returning a `tec` is
1220 // awkward and very limiting. A more general purpose approach
1221 // should be used, making it possible to do more useful work
1222 // when transactions fail with a `tec` code.
1223 std::vector<uint256> removedOffers;
1224 std::vector<uint256> removedTrustLines;
1225 std::vector<uint256> expiredNFTokenOffers;
1226 std::vector<uint256> expiredCredentials;
1227
1228 bool const doOffers =
1229 ((result == tecOVERSIZE) || (result == tecKILLED));
1230 bool const doLines = (result == tecINCOMPLETE);
1231 bool const doNFTokenOffers = (result == tecEXPIRED);
1232 bool const doCredentials = (result == tecEXPIRED);
1233 if (doOffers || doLines || doNFTokenOffers || doCredentials)
1234 {
1235 ctx_.visit([doOffers,
1236 &removedOffers,
1237 doLines,
1238 &removedTrustLines,
1239 doNFTokenOffers,
1240 &expiredNFTokenOffers,
1241 doCredentials,
1242 &expiredCredentials](
1243 uint256 const& index,
1244 bool isDelete,
1245 std::shared_ptr<SLE const> const& before,
1247 if (isDelete)
1248 {
1249 XRPL_ASSERT(
1250 before && after,
1251 "ripple::Transactor::operator()::visit : non-null SLE "
1252 "inputs");
1253 if (doOffers && before && after &&
1254 (before->getType() == ltOFFER) &&
1255 (before->getFieldAmount(sfTakerPays) ==
1256 after->getFieldAmount(sfTakerPays)))
1257 {
1258 // Removal of offer found or made unfunded
1259 removedOffers.push_back(index);
1260 }
1261
1262 if (doLines && before && after &&
1263 (before->getType() == ltRIPPLE_STATE))
1264 {
1265 // Removal of obsolete AMM trust line
1266 removedTrustLines.push_back(index);
1267 }
1268
1269 if (doNFTokenOffers && before && after &&
1270 (before->getType() == ltNFTOKEN_OFFER))
1271 expiredNFTokenOffers.push_back(index);
1272
1273 if (doCredentials && before && after &&
1274 (before->getType() == ltCREDENTIAL))
1275 expiredCredentials.push_back(index);
1276 }
1277 });
1278 }
1279
1280 // Reset the context, potentially adjusting the fee.
1281 {
1282 auto const resetResult = reset(fee);
1283 if (!isTesSuccess(resetResult.first))
1284 result = resetResult.first;
1285
1286 fee = resetResult.second;
1287 }
1288
1289 // If necessary, remove any offers found unfunded during processing
1290 if ((result == tecOVERSIZE) || (result == tecKILLED))
1292 view(), removedOffers, ctx_.app.journal("View"));
1293
1294 if (result == tecEXPIRED)
1296 view(), expiredNFTokenOffers, ctx_.app.journal("View"));
1297
1298 if (result == tecINCOMPLETE)
1300 view(), removedTrustLines, ctx_.app.journal("View"));
1301
1302 if (result == tecEXPIRED)
1304 view(), expiredCredentials, ctx_.app.journal("View"));
1305
1306 applied = isTecClaim(result);
1307 }
1308
1309 if (applied)
1310 {
1311 // Check invariants: if `tecINVARIANT_FAILED` is not returned, we can
1312 // proceed to apply the tx
1313 result = ctx_.checkInvariants(result, fee);
1314
1315 if (result == tecINVARIANT_FAILED)
1316 {
1317 // if invariants checking failed again, reset the context and
1318 // attempt to only claim a fee.
1319 auto const resetResult = reset(fee);
1320 if (!isTesSuccess(resetResult.first))
1321 result = resetResult.first;
1322
1323 fee = resetResult.second;
1324
1325 // Check invariants again to ensure the fee claiming doesn't
1326 // violate invariants.
1327 if (isTesSuccess(result) || isTecClaim(result))
1328 result = ctx_.checkInvariants(result, fee);
1329 }
1330
1331 // We ran through the invariant checker, which can, in some cases,
1332 // return a tef error code. Don't apply the transaction in that case.
1333 if (!isTecClaim(result) && !isTesSuccess(result))
1334 applied = false;
1335 }
1336
1337 std::optional<TxMeta> metadata;
1338 if (applied)
1339 {
1340 // Transaction succeeded fully or (retries are not allowed and the
1341 // transaction could claim a fee)
1342
1343 // The transactor and invariant checkers guarantee that this will
1344 // *never* trigger but if it, somehow, happens, don't allow a tx
1345 // that charges a negative fee.
1346 if (fee < beast::zero)
1347 Throw<std::logic_error>("fee charged is negative!");
1348
1349 // Charge whatever fee they specified. The fee has already been
1350 // deducted from the balance of the account that issued the
1351 // transaction. We just need to account for it in the ledger
1352 // header.
1353 if (!view().open() && fee != beast::zero)
1354 ctx_.destroyXRP(fee);
1355
1356 // Once we call apply, we will no longer be able to look at view()
1357 metadata = ctx_.apply(result);
1358 }
1359
1360 if (ctx_.flags() & tapDRY_RUN)
1361 {
1362 applied = false;
1363 }
1364
1365 JLOG(j_.trace()) << (applied ? "applied " : "not applied ")
1366 << transToken(result);
1367
1368 return {result, applied, metadata};
1369}
1370
1371} // 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
static NotTEC checkMultiSign(PreclaimContext const &ctx, AccountID const &id, STObject const &sigObject)
TER consumeSeqProxy(SLE::pointer const &sleAccount)
ApplyResult operator()()
Process the transaction.
static NotTEC preflightSigValidated(PreflightContext const &ctx)
static NotTEC checkPriorTxAndLastLedger(PreclaimContext const &ctx)
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 preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
ApplyView & view()
Definition Transactor.h:163
static NotTEC checkSingleSign(PreclaimContext const &ctx, AccountID const &idSigner, AccountID const &idAccount, std::shared_ptr< SLE const > sleAccount)
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:1029
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:3239
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:2780
@ 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:1634
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