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