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 // LCOV_EXCL_START
575 JLOG(j.fatal()) << "Ticket disappeared from ledger.";
576 return tefBAD_LEDGER;
577 // LCOV_EXCL_STOP
578 }
579
580 std::uint64_t const page{(*sleTicket)[sfOwnerNode]};
581 if (!view.dirRemove(keylet::ownerDir(account), page, ticketIndex, true))
582 {
583 // LCOV_EXCL_START
584 JLOG(j.fatal()) << "Unable to delete Ticket from owner.";
585 return tefBAD_LEDGER;
586 // LCOV_EXCL_STOP
587 }
588
589 // Update the account root's TicketCount. If the ticket count drops to
590 // zero remove the (optional) field.
591 auto sleAccount = view.peek(keylet::account(account));
592 if (!sleAccount)
593 {
594 // LCOV_EXCL_START
595 JLOG(j.fatal()) << "Could not find Ticket owner account root.";
596 return tefBAD_LEDGER;
597 // LCOV_EXCL_STOP
598 }
599
600 if (auto ticketCount = (*sleAccount)[~sfTicketCount])
601 {
602 if (*ticketCount == 1)
603 sleAccount->makeFieldAbsent(sfTicketCount);
604 else
605 ticketCount = *ticketCount - 1;
606 }
607 else
608 {
609 // LCOV_EXCL_START
610 JLOG(j.fatal()) << "TicketCount field missing from account root.";
611 return tefBAD_LEDGER;
612 // LCOV_EXCL_STOP
613 }
614
615 // Update the Ticket owner's reserve.
616 adjustOwnerCount(view, sleAccount, -1, j);
617
618 // Remove Ticket from ledger.
619 view.erase(sleTicket);
620 return tesSUCCESS;
621}
622
623// check stuff before you bother to lock the ledger
624void
626{
627 XRPL_ASSERT(
628 account_ != beast::zero,
629 "ripple::Transactor::preCompute : nonzero account");
630}
631
632TER
634{
635 preCompute();
636
637 // If the transactor requires a valid account and the transaction doesn't
638 // list one, preflight will have already a flagged a failure.
639 auto const sle = view().peek(keylet::account(account_));
640
641 // sle must exist except for transactions
642 // that allow zero account.
643 XRPL_ASSERT(
644 sle != nullptr || account_ == beast::zero,
645 "ripple::Transactor::apply : non-null SLE or zero account");
646
647 if (sle)
648 {
649 mPriorBalance = STAmount{(*sle)[sfBalance]}.xrp();
651
652 TER result = consumeSeqProxy(sle);
653 if (result != tesSUCCESS)
654 return result;
655
656 result = payFee();
657 if (result != tesSUCCESS)
658 return result;
659
660 if (sle->isFieldPresent(sfAccountTxnID))
661 sle->setFieldH256(sfAccountTxnID, ctx_.tx.getTransactionID());
662
663 view().update(sle);
664 }
665
666 return doApply();
667}
668
669NotTEC
671 ReadView const& view,
672 ApplyFlags flags,
673 AccountID const& idAccount,
674 STObject const& sigObject,
675 beast::Journal const j)
676{
677 auto const pkSigner = sigObject.getFieldVL(sfSigningPubKey);
678 // Ignore signature check on batch inner transactions
679 if (sigObject.isFlag(tfInnerBatchTxn) && view.rules().enabled(featureBatch))
680 {
681 // Defensive Check: These values are also checked in Batch::preflight
682 if (sigObject.isFieldPresent(sfTxnSignature) || !pkSigner.empty() ||
683 sigObject.isFieldPresent(sfSigners))
684 {
685 return temINVALID_FLAG; // LCOV_EXCL_LINE
686 }
687 return tesSUCCESS;
688 }
689
690 if ((flags & tapDRY_RUN) && pkSigner.empty() &&
691 !sigObject.isFieldPresent(sfSigners))
692 {
693 // simulate: skip signature validation when neither SigningPubKey nor
694 // Signers are provided
695 return tesSUCCESS;
696 }
697
698 // If the pk is empty and not simulate or simulate and signers,
699 // then we must be multi-signing.
700 if (sigObject.isFieldPresent(sfSigners))
701 {
702 return checkMultiSign(view, flags, idAccount, sigObject, j);
703 }
704
705 // Check Single Sign
706 XRPL_ASSERT(
707 !pkSigner.empty(), "ripple::Transactor::checkSign : non-empty signer");
708
709 if (!publicKeyType(makeSlice(pkSigner)))
710 {
711 JLOG(j.trace()) << "checkSign: signing public key type is unknown";
712 return tefBAD_AUTH; // FIXME: should be better error!
713 }
714
715 // Look up the account.
716 auto const idSigner = pkSigner.empty()
717 ? idAccount
718 : calcAccountID(PublicKey(makeSlice(pkSigner)));
719 auto const sleAccount = view.read(keylet::account(idAccount));
720 if (!sleAccount)
721 return terNO_ACCOUNT;
722
723 return checkSingleSign(view, idSigner, idAccount, sleAccount, j);
724}
725
726NotTEC
728{
729 auto const idAccount = ctx.tx.isFieldPresent(sfDelegate)
730 ? ctx.tx.getAccountID(sfDelegate)
731 : ctx.tx.getAccountID(sfAccount);
732 return checkSign(ctx.view, ctx.flags, idAccount, ctx.tx, ctx.j);
733}
734
735NotTEC
737{
738 NotTEC ret = tesSUCCESS;
739 STArray const& signers{ctx.tx.getFieldArray(sfBatchSigners)};
740 for (auto const& signer : signers)
741 {
742 auto const idAccount = signer.getAccountID(sfAccount);
743
744 Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
745 if (pkSigner.empty())
746 {
747 if (ret = checkMultiSign(
748 ctx.view, ctx.flags, idAccount, signer, ctx.j);
749 !isTesSuccess(ret))
750 return ret;
751 }
752 else
753 {
754 // LCOV_EXCL_START
755 if (!publicKeyType(makeSlice(pkSigner)))
756 return tefBAD_AUTH;
757 // LCOV_EXCL_STOP
758
759 auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner)));
760 auto const sleAccount = ctx.view.read(keylet::account(idAccount));
761
762 // A batch can include transactions from an un-created account ONLY
763 // when the account master key is the signer
764 if (!sleAccount)
765 {
766 if (idAccount != idSigner)
767 return tefBAD_AUTH;
768
769 return tesSUCCESS;
770 }
771
772 if (ret = checkSingleSign(
773 ctx.view, idSigner, idAccount, sleAccount, ctx.j);
774 !isTesSuccess(ret))
775 return ret;
776 }
777 }
778 return ret;
779}
780
781NotTEC
783 ReadView const& view,
784 AccountID const& idSigner,
785 AccountID const& idAccount,
787 beast::Journal const j)
788{
789 bool const isMasterDisabled = sleAccount->isFlag(lsfDisableMaster);
790
791 if (view.rules().enabled(fixMasterKeyAsRegularKey))
792 {
793 // Signed with regular key.
794 if ((*sleAccount)[~sfRegularKey] == idSigner)
795 {
796 return tesSUCCESS;
797 }
798
799 // Signed with enabled mater key.
800 if (!isMasterDisabled && idAccount == idSigner)
801 {
802 return tesSUCCESS;
803 }
804
805 // Signed with disabled master key.
806 if (isMasterDisabled && idAccount == idSigner)
807 {
808 return tefMASTER_DISABLED;
809 }
810
811 // Signed with any other key.
812 return tefBAD_AUTH;
813 }
814
815 if (idSigner == idAccount)
816 {
817 // Signing with the master key. Continue if it is not disabled.
818 if (isMasterDisabled)
819 return tefMASTER_DISABLED;
820 }
821 else if ((*sleAccount)[~sfRegularKey] == idSigner)
822 {
823 // Signing with the regular key. Continue.
824 }
825 else if (sleAccount->isFieldPresent(sfRegularKey))
826 {
827 // Signing key does not match master or regular key.
828 JLOG(j.trace()) << "checkSingleSign: Not authorized to use account.";
829 return tefBAD_AUTH;
830 }
831 else
832 {
833 // No regular key on account and signing key does not match master key.
834 // FIXME: Why differentiate this case from tefBAD_AUTH?
835 JLOG(j.trace()) << "checkSingleSign: Not authorized to use account.";
836 return tefBAD_AUTH_MASTER;
837 }
838
839 return tesSUCCESS;
840}
841
842NotTEC
844 ReadView const& view,
845 ApplyFlags flags,
846 AccountID const& id,
847 STObject const& sigObject,
848 beast::Journal const j)
849{
850 // Get id's SignerList and Quorum.
851 std::shared_ptr<STLedgerEntry const> sleAccountSigners =
853 // If the signer list doesn't exist the account is not multi-signing.
854 if (!sleAccountSigners)
855 {
856 JLOG(j.trace())
857 << "applyTransaction: Invalid: Not a multi-signing account.";
859 }
860
861 // We have plans to support multiple SignerLists in the future. The
862 // presence and defaulted value of the SignerListID field will enable that.
863 XRPL_ASSERT(
864 sleAccountSigners->isFieldPresent(sfSignerListID),
865 "ripple::Transactor::checkMultiSign : has signer list ID");
866 XRPL_ASSERT(
867 sleAccountSigners->getFieldU32(sfSignerListID) == 0,
868 "ripple::Transactor::checkMultiSign : signer list ID is 0");
869
870 auto accountSigners =
871 SignerEntries::deserialize(*sleAccountSigners, j, "ledger");
872 if (!accountSigners)
873 return accountSigners.error();
874
875 // Get the array of transaction signers.
876 STArray const& txSigners(sigObject.getFieldArray(sfSigners));
877
878 // Walk the accountSigners performing a variety of checks and see if
879 // the quorum is met.
880
881 // Both the multiSigners and accountSigners are sorted by account. So
882 // matching multi-signers to account signers should be a simple
883 // linear walk. *All* signers must be valid or the transaction fails.
884 std::uint32_t weightSum = 0;
885 auto iter = accountSigners->begin();
886 for (auto const& txSigner : txSigners)
887 {
888 AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
889
890 // Attempt to match the SignerEntry with a Signer;
891 while (iter->account < txSignerAcctID)
892 {
893 if (++iter == accountSigners->end())
894 {
895 JLOG(j.trace())
896 << "applyTransaction: Invalid SigningAccount.Account.";
897 return tefBAD_SIGNATURE;
898 }
899 }
900 if (iter->account != txSignerAcctID)
901 {
902 // The SigningAccount is not in the SignerEntries.
903 JLOG(j.trace())
904 << "applyTransaction: Invalid SigningAccount.Account.";
905 return tefBAD_SIGNATURE;
906 }
907
908 // We found the SigningAccount in the list of valid signers. Now we
909 // need to compute the accountID that is associated with the signer's
910 // public key.
911 auto const spk = txSigner.getFieldVL(sfSigningPubKey);
912
913 // spk being non-empty in non-simulate is checked in
914 // STTx::checkMultiSign
915 if (!spk.empty() && !publicKeyType(makeSlice(spk)))
916 {
917 JLOG(j.trace())
918 << "checkMultiSign: signing public key type is unknown";
919 return tefBAD_SIGNATURE;
920 }
921
922 XRPL_ASSERT(
923 (flags & tapDRY_RUN) || !spk.empty(),
924 "ripple::Transactor::checkMultiSign : non-empty signer or "
925 "simulation");
926 AccountID const signingAcctIDFromPubKey = spk.empty()
927 ? txSignerAcctID
929
930 // Verify that the signingAcctID and the signingAcctIDFromPubKey
931 // belong together. Here are the rules:
932 //
933 // 1. "Phantom account": an account that is not in the ledger
934 // A. If signingAcctID == signingAcctIDFromPubKey and the
935 // signingAcctID is not in the ledger then we have a phantom
936 // account.
937 // B. Phantom accounts are always allowed as multi-signers.
938 //
939 // 2. "Master Key"
940 // A. signingAcctID == signingAcctIDFromPubKey, and signingAcctID
941 // is in the ledger.
942 // B. If the signingAcctID in the ledger does not have the
943 // asfDisableMaster flag set, then the signature is allowed.
944 //
945 // 3. "Regular Key"
946 // A. signingAcctID != signingAcctIDFromPubKey, and signingAcctID
947 // is in the ledger.
948 // B. If signingAcctIDFromPubKey == signingAcctID.RegularKey (from
949 // ledger) then the signature is allowed.
950 //
951 // No other signatures are allowed. (January 2015)
952
953 // In any of these cases we need to know whether the account is in
954 // the ledger. Determine that now.
955 auto const sleTxSignerRoot = view.read(keylet::account(txSignerAcctID));
956
957 if (signingAcctIDFromPubKey == txSignerAcctID)
958 {
959 // Either Phantom or Master. Phantoms automatically pass.
960 if (sleTxSignerRoot)
961 {
962 // Master Key. Account may not have asfDisableMaster set.
963 std::uint32_t const signerAccountFlags =
964 sleTxSignerRoot->getFieldU32(sfFlags);
965
966 if (signerAccountFlags & lsfDisableMaster)
967 {
968 JLOG(j.trace())
969 << "applyTransaction: Signer:Account lsfDisableMaster.";
970 return tefMASTER_DISABLED;
971 }
972 }
973 }
974 else
975 {
976 // May be a Regular Key. Let's find out.
977 // Public key must hash to the account's regular key.
978 if (!sleTxSignerRoot)
979 {
980 JLOG(j.trace()) << "applyTransaction: Non-phantom signer "
981 "lacks account root.";
982 return tefBAD_SIGNATURE;
983 }
984
985 if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
986 {
987 JLOG(j.trace())
988 << "applyTransaction: Account lacks RegularKey.";
989 return tefBAD_SIGNATURE;
990 }
991 if (signingAcctIDFromPubKey !=
992 sleTxSignerRoot->getAccountID(sfRegularKey))
993 {
994 JLOG(j.trace())
995 << "applyTransaction: Account doesn't match RegularKey.";
996 return tefBAD_SIGNATURE;
997 }
998 }
999 // The signer is legitimate. Add their weight toward the quorum.
1000 weightSum += iter->weight;
1001 }
1002
1003 // Cannot perform transaction if quorum is not met.
1004 if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
1005 {
1006 JLOG(j.trace()) << "applyTransaction: Signers failed to meet quorum.";
1007 return tefBAD_QUORUM;
1008 }
1009
1010 // Met the quorum. Continue.
1011 return tesSUCCESS;
1012}
1013
1014//------------------------------------------------------------------------------
1015
1016static void
1018 ApplyView& view,
1019 std::vector<uint256> const& offers,
1020 beast::Journal viewJ)
1021{
1022 int removed = 0;
1023
1024 for (auto const& index : offers)
1025 {
1026 if (auto const sleOffer = view.peek(keylet::offer(index)))
1027 {
1028 // offer is unfunded
1029 offerDelete(view, sleOffer, viewJ);
1030 if (++removed == unfundedOfferRemoveLimit)
1031 return;
1032 }
1033 }
1034}
1035
1036static void
1038 ApplyView& view,
1039 std::vector<uint256> const& offers,
1040 beast::Journal viewJ)
1041{
1042 std::size_t removed = 0;
1043
1044 for (auto const& index : offers)
1045 {
1046 if (auto const offer = view.peek(keylet::nftoffer(index)))
1047 {
1048 nft::deleteTokenOffer(view, offer);
1049 if (++removed == expiredOfferRemoveLimit)
1050 return;
1051 }
1052 }
1053}
1054
1055static void
1057 ApplyView& view,
1058 std::vector<uint256> const& creds,
1059 beast::Journal viewJ)
1060{
1061 for (auto const& index : creds)
1062 {
1063 if (auto const sle = view.peek(keylet::credential(index)))
1064 credentials::deleteSLE(view, sle, viewJ);
1065 }
1066}
1067
1068static void
1070 ApplyView& view,
1071 std::vector<uint256> const& trustLines,
1072 beast::Journal viewJ)
1073{
1074 if (trustLines.size() > maxDeletableAMMTrustLines)
1075 {
1076 JLOG(viewJ.error())
1077 << "removeDeletedTrustLines: deleted trustlines exceed max "
1078 << trustLines.size();
1079 return;
1080 }
1081
1082 for (auto const& index : trustLines)
1083 {
1084 if (auto const sleState = view.peek({ltRIPPLE_STATE, index});
1085 deleteAMMTrustLine(view, sleState, std::nullopt, viewJ) !=
1086 tesSUCCESS)
1087 {
1088 JLOG(viewJ.error())
1089 << "removeDeletedTrustLines: failed to delete AMM trustline";
1090 }
1091 }
1092}
1093
1101{
1102 ctx_.discard();
1103
1104 auto const txnAcct =
1106
1107 // The account should never be missing from the ledger. But if it
1108 // is missing then we can't very well charge it a fee, can we?
1109 if (!txnAcct)
1110 return {tefINTERNAL, beast::zero};
1111
1112 auto const payerSle = ctx_.tx.isFieldPresent(sfDelegate)
1113 ? view().peek(keylet::account(ctx_.tx.getAccountID(sfDelegate)))
1114 : txnAcct;
1115 if (!payerSle)
1116 return {tefINTERNAL, beast::zero}; // LCOV_EXCL_LINE
1117
1118 auto const balance = payerSle->getFieldAmount(sfBalance).xrp();
1119
1120 // balance should have already been checked in checkFee / preFlight.
1121 XRPL_ASSERT(
1122 balance != beast::zero && (!view().open() || balance >= fee),
1123 "ripple::Transactor::reset : valid balance");
1124
1125 // We retry/reject the transaction if the account balance is zero or
1126 // we're applying against an open ledger and the balance is less than
1127 // the fee
1128 if (fee > balance)
1129 fee = balance;
1130
1131 // Since we reset the context, we need to charge the fee and update
1132 // the account's sequence number (or consume the Ticket) again.
1133 //
1134 // If for some reason we are unable to consume the ticket or sequence
1135 // then the ledger is corrupted. Rather than make things worse we
1136 // reject the transaction.
1137 payerSle->setFieldAmount(sfBalance, balance - fee);
1138 TER const ter{consumeSeqProxy(txnAcct)};
1139 XRPL_ASSERT(
1140 isTesSuccess(ter), "ripple::Transactor::reset : result is tesSUCCESS");
1141
1142 if (isTesSuccess(ter))
1143 {
1144 view().update(txnAcct);
1145 if (payerSle != txnAcct)
1146 view().update(payerSle);
1147 }
1148
1149 return {ter, fee};
1150}
1151
1152// The sole purpose of this function is to provide a convenient, named
1153// location to set a breakpoint, to be used when replaying transactions.
1154void
1156{
1157 JLOG(j_.debug()) << "Transaction trapped: " << txHash;
1158}
1159
1160//------------------------------------------------------------------------------
1163{
1164 JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID();
1165
1166 // raii classes for the current ledger rules.
1167 // fixUniversalNumber predate the rulesGuard and should be replaced.
1168 NumberSO stNumberSO{view().rules().enabled(fixUniversalNumber)};
1169 CurrentTransactionRulesGuard currentTransctionRulesGuard(view().rules());
1170
1171#ifdef DEBUG
1172 {
1173 Serializer ser;
1174 ctx_.tx.add(ser);
1175 SerialIter sit(ser.slice());
1176 STTx s2(sit);
1177
1178 if (!s2.isEquivalent(ctx_.tx))
1179 {
1180 // LCOV_EXCL_START
1181 JLOG(j_.fatal()) << "Transaction serdes mismatch";
1183 JLOG(j_.fatal()) << s2.getJson(JsonOptions::none);
1184 UNREACHABLE(
1185 "ripple::Transactor::operator() : transaction serdes mismatch");
1186 // LCOV_EXCL_STOP
1187 }
1188 }
1189#endif
1190
1191 if (auto const& trap = ctx_.app.trapTxID();
1192 trap && *trap == ctx_.tx.getTransactionID())
1193 {
1194 trapTransaction(*trap);
1195 }
1196
1197 auto result = ctx_.preclaimResult;
1198 if (result == tesSUCCESS)
1199 result = apply();
1200
1201 // No transaction can return temUNKNOWN from apply,
1202 // and it can't be passed in from a preclaim.
1203 XRPL_ASSERT(
1204 result != temUNKNOWN,
1205 "ripple::Transactor::operator() : result is not temUNKNOWN");
1206
1207 if (auto stream = j_.trace())
1208 stream << "preclaim result: " << transToken(result);
1209
1210 bool applied = isTesSuccess(result);
1211 auto fee = ctx_.tx.getFieldAmount(sfFee).xrp();
1212
1214 result = tecOVERSIZE;
1215
1216 if (isTecClaim(result) && (view().flags() & tapFAIL_HARD))
1217 {
1218 // If the tapFAIL_HARD flag is set, a tec result
1219 // must not do anything
1220 ctx_.discard();
1221 applied = false;
1222 }
1223 else if (
1224 (result == tecOVERSIZE) || (result == tecKILLED) ||
1225 (result == tecINCOMPLETE) || (result == tecEXPIRED) ||
1226 (isTecClaimHardFail(result, view().flags())))
1227 {
1228 JLOG(j_.trace()) << "reapplying because of " << transToken(result);
1229
1230 // FIXME: This mechanism for doing work while returning a `tec` is
1231 // awkward and very limiting. A more general purpose approach
1232 // should be used, making it possible to do more useful work
1233 // when transactions fail with a `tec` code.
1234 std::vector<uint256> removedOffers;
1235 std::vector<uint256> removedTrustLines;
1236 std::vector<uint256> expiredNFTokenOffers;
1237 std::vector<uint256> expiredCredentials;
1238
1239 bool const doOffers =
1240 ((result == tecOVERSIZE) || (result == tecKILLED));
1241 bool const doLines = (result == tecINCOMPLETE);
1242 bool const doNFTokenOffers = (result == tecEXPIRED);
1243 bool const doCredentials = (result == tecEXPIRED);
1244 if (doOffers || doLines || doNFTokenOffers || doCredentials)
1245 {
1246 ctx_.visit([doOffers,
1247 &removedOffers,
1248 doLines,
1249 &removedTrustLines,
1250 doNFTokenOffers,
1251 &expiredNFTokenOffers,
1252 doCredentials,
1253 &expiredCredentials](
1254 uint256 const& index,
1255 bool isDelete,
1256 std::shared_ptr<SLE const> const& before,
1258 if (isDelete)
1259 {
1260 XRPL_ASSERT(
1261 before && after,
1262 "ripple::Transactor::operator()::visit : non-null SLE "
1263 "inputs");
1264 if (doOffers && before && after &&
1265 (before->getType() == ltOFFER) &&
1266 (before->getFieldAmount(sfTakerPays) ==
1267 after->getFieldAmount(sfTakerPays)))
1268 {
1269 // Removal of offer found or made unfunded
1270 removedOffers.push_back(index);
1271 }
1272
1273 if (doLines && before && after &&
1274 (before->getType() == ltRIPPLE_STATE))
1275 {
1276 // Removal of obsolete AMM trust line
1277 removedTrustLines.push_back(index);
1278 }
1279
1280 if (doNFTokenOffers && before && after &&
1281 (before->getType() == ltNFTOKEN_OFFER))
1282 expiredNFTokenOffers.push_back(index);
1283
1284 if (doCredentials && before && after &&
1285 (before->getType() == ltCREDENTIAL))
1286 expiredCredentials.push_back(index);
1287 }
1288 });
1289 }
1290
1291 // Reset the context, potentially adjusting the fee.
1292 {
1293 auto const resetResult = reset(fee);
1294 if (!isTesSuccess(resetResult.first))
1295 result = resetResult.first;
1296
1297 fee = resetResult.second;
1298 }
1299
1300 // If necessary, remove any offers found unfunded during processing
1301 if ((result == tecOVERSIZE) || (result == tecKILLED))
1303 view(), removedOffers, ctx_.app.journal("View"));
1304
1305 if (result == tecEXPIRED)
1307 view(), expiredNFTokenOffers, ctx_.app.journal("View"));
1308
1309 if (result == tecINCOMPLETE)
1311 view(), removedTrustLines, ctx_.app.journal("View"));
1312
1313 if (result == tecEXPIRED)
1315 view(), expiredCredentials, ctx_.app.journal("View"));
1316
1317 applied = isTecClaim(result);
1318 }
1319
1320 if (applied)
1321 {
1322 // Check invariants: if `tecINVARIANT_FAILED` is not returned, we can
1323 // proceed to apply the tx
1324 result = ctx_.checkInvariants(result, fee);
1325
1326 if (result == tecINVARIANT_FAILED)
1327 {
1328 // if invariants checking failed again, reset the context and
1329 // attempt to only claim a fee.
1330 auto const resetResult = reset(fee);
1331 if (!isTesSuccess(resetResult.first))
1332 result = resetResult.first;
1333
1334 fee = resetResult.second;
1335
1336 // Check invariants again to ensure the fee claiming doesn't
1337 // violate invariants.
1338 if (isTesSuccess(result) || isTecClaim(result))
1339 result = ctx_.checkInvariants(result, fee);
1340 }
1341
1342 // We ran through the invariant checker, which can, in some cases,
1343 // return a tef error code. Don't apply the transaction in that case.
1344 if (!isTecClaim(result) && !isTesSuccess(result))
1345 applied = false;
1346 }
1347
1348 std::optional<TxMeta> metadata;
1349 if (applied)
1350 {
1351 // Transaction succeeded fully or (retries are not allowed and the
1352 // transaction could claim a fee)
1353
1354 // The transactor and invariant checkers guarantee that this will
1355 // *never* trigger but if it, somehow, happens, don't allow a tx
1356 // that charges a negative fee.
1357 if (fee < beast::zero)
1358 Throw<std::logic_error>("fee charged is negative!");
1359
1360 // Charge whatever fee they specified. The fee has already been
1361 // deducted from the balance of the account that issued the
1362 // transaction. We just need to account for it in the ledger
1363 // header.
1364 if (!view().open() && fee != beast::zero)
1365 ctx_.destroyXRP(fee);
1366
1367 // Once we call apply, we will no longer be able to look at view()
1368 metadata = ctx_.apply(result);
1369 }
1370
1371 if (ctx_.flags() & tapDRY_RUN)
1372 {
1373 applied = false;
1374 }
1375
1376 JLOG(j_.trace()) << (applied ? "applied " : "not applied ")
1377 << transToken(result);
1378
1379 return {result, applied, metadata};
1380}
1381
1382} // 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:62
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
XRPAmount xrp() const
Definition STAmount.cpp:283
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:702
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:338
uint256 getTransactionID() const
Definition STTx.h:249
A type that represents either a sequence value or a ticket value.
Definition SeqProxy.h:56
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition SeqProxy.h:76
constexpr bool isSeq() const
Definition SeqProxy.h:88
constexpr std::uint32_t value() const
Definition SeqProxy.h:82
constexpr bool isTicket() const
Definition SeqProxy.h:94
Slice slice() const noexcept
Definition Serializer.h:66
static Expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string_view annotation)
An immutable linear range of bytes.
Definition Slice.h:46
bool empty() const noexcept
Return true if the byte range is empty.
Definition Slice.h:70
TER consumeSeqProxy(SLE::pointer const &sleAccount)
ApplyResult operator()()
Process the transaction.
static NotTEC preflightSigValidated(PreflightContext const &ctx)
static NotTEC checkPriorTxAndLastLedger(PreclaimContext const &ctx)
static NotTEC checkMultiSign(ReadView const &view, ApplyFlags flags, AccountID const &id, STObject const &sigObject, beast::Journal const j)
static TER checkFee(PreclaimContext const &ctx, XRPAmount baseFee)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
static NotTEC checkSeqProxy(ReadView const &view, STTx const &tx, beast::Journal j)
static NotTEC checkSign(PreclaimContext const &ctx)
void trapTransaction(uint256) const
static XRPAmount minimumFee(Application &app, XRPAmount baseFee, Fees const &fees, ApplyFlags flags)
Compute the minimum fee required to process a transaction with a given baseFee based on the current s...
static NotTEC preflight1(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the account and fee fields.
AccountID const account_
Definition Transactor.h:147
static TER checkPermission(ReadView const &view, STTx const &tx)
static NotTEC checkSingleSign(ReadView const &view, AccountID const &idSigner, AccountID const &idAccount, std::shared_ptr< SLE const > sleAccount, beast::Journal const j)
static NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
ApplyView & view()
Definition Transactor.h:163
static NotTEC checkBatchSign(PreclaimContext const &ctx)
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
beast::Journal const j_
Definition Transactor.h:145
XRPAmount mPriorBalance
Definition Transactor.h:148
virtual void preCompute()
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
XRPAmount mSourceBalance
Definition Transactor.h:149
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:143
virtual TER doApply()=0
std::pair< TER, XRPAmount > reset(XRPAmount fee)
Reset the context, discarding any changes made and adjust the fee.
Transactor(Transactor const &)=delete
T empty(T... args)
T is_same_v
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
std::optional< NotTEC > preflightCheckSimulateKeys(ApplyFlags flags, STObject const &sigObject, beast::Journal j)
Checks the special signing key state needed for simulation.
NotTEC preflightCheckSigningKey(STObject const &sigObject, beast::Journal j)
Checks the validity of the transactor signing key.
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:465
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition Indexes.cpp:553
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:374
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:330
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:427
static ticket_t const ticket
Definition Indexes.h:171
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Definition Indexes.cpp:274
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string to_short_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:637
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
@ telWRONG_NETWORK
Definition TER.h:65
@ telINSUF_FEE_P
Definition TER.h:57
@ telREQUIRES_NETWORK_ID
Definition TER.h:66
@ telNETWORK_ID_MAKES_TX_NON_CANONICAL
Definition TER.h:67
bool isLegalAmount(XRPAmount const &amount)
Returns true if the amount does not exceed the initial XRP in existence.
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
Definition STTx.cpp:870
std::size_t constexpr unfundedOfferRemoveLimit
The maximum number of unfunded offers to delete at once.
Definition Protocol.h:47
std::size_t constexpr expiredOfferRemoveLimit
The maximum number of expired offers to delete at once.
Definition Protocol.h:50
@ lsfDisableMaster
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:1032
std::size_t constexpr oversizeMetaDataCap
The maximum number of metadata entries allowed in one transaction.
Definition Protocol.h:53
AccountID calcAccountID(PublicKey const &pk)
static void removeUnfundedOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
@ tefNOT_MULTI_SIGNING
Definition TER.h:181
@ tefPAST_SEQ
Definition TER.h:175
@ tefNO_TICKET
Definition TER.h:185
@ tefBAD_AUTH_MASTER
Definition TER.h:182
@ tefMAX_LEDGER
Definition TER.h:178
@ tefWRONG_PRIOR
Definition TER.h:176
@ tefBAD_AUTH
Definition TER.h:169
@ tefBAD_QUORUM
Definition TER.h:180
@ tefBAD_SIGNATURE
Definition TER.h:179
@ tefBAD_LEDGER
Definition TER.h:170
@ tefALREADY
Definition TER.h:167
@ tefMASTER_DISABLED
Definition TER.h:177
@ tefINTERNAL
Definition TER.h:173
@ open
We haven't closed our ledger yet, but others might have.
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
static void removeExpiredCredentials(ApplyView &view, std::vector< uint256 > const &creds, beast::Journal viewJ)
std::string transToken(TER code)
Definition TER.cpp:264
static void removeExpiredNFTokenOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition Slice.h:244
TER checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
@ tecNO_DELEGATE_PERMISSION
Definition TER.h:364
@ tecINSUFF_FEE
Definition TER.h:302
@ tecINCOMPLETE
Definition TER.h:335
@ tecKILLED
Definition TER.h:316
@ tecINVARIANT_FAILED
Definition TER.h:313
@ tecOVERSIZE
Definition TER.h:311
@ tecEXPIRED
Definition TER.h:314
bool isTecClaimHardFail(TER ter, ApplyFlags flags)
Return true if the transaction can claim a fee (tec), and the ApplyFlags do not allow soft failures.
Definition applySteps.h:48
@ tesSUCCESS
Definition TER.h:244
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:156
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
@ SigBad
Signature is bad. Didn't do local checks.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
static void removeDeletedTrustLines(ApplyView &view, std::vector< uint256 > const &trustLines, beast::Journal viewJ)
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:3266
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:2806
@ 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:150
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:1647
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