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