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