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