20#include <xrpld/app/main/Application.h>
21#include <xrpld/app/misc/CredentialHelpers.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#include <xrpld/ledger/View.h>
29#include <xrpl/basics/Log.h>
30#include <xrpl/basics/contract.h>
31#include <xrpl/json/to_string.h>
32#include <xrpl/protocol/Feature.h>
33#include <xrpl/protocol/Indexes.h>
34#include <xrpl/protocol/Protocol.h>
35#include <xrpl/protocol/STAccount.h>
36#include <xrpl/protocol/UintTypes.h>
63 if (*txNID != nodeNID)
70 if (txID == beast::zero)
73 <<
"applyTransaction: transaction id may not be zero";
97 if (
id == beast::zero)
99 JLOG(ctx.
j.
warn()) <<
"preflight1: bad account id";
105 if (!fee.native() || fee.negative() || !
isLegalAmount(fee.xrp()))
107 JLOG(ctx.
j.
debug()) <<
"preflight1: invalid fee";
115 JLOG(ctx.
j.
debug()) <<
"preflight1: invalid signing key";
140 JLOG(ctx.
j.
debug()) <<
"preflight2: bad signature. " << sigValid.second;
154 : app(app_), tx(tx_), rules(rules_), flags(flags_), j(j_)
161 : ctx_(ctx), j_(ctx.journal), account_(ctx.tx.getAccountID(sfAccount))
180 return baseFee + (signerCount * baseFee);
196 if (!ctx.
tx[sfFee].native())
199 auto const feePaid = ctx.
tx[sfFee].xrp();
209 if (feePaid < feeDue)
212 <<
"Insufficient fee paid: " <<
to_string(feePaid) <<
"/"
218 if (feePaid == beast::zero)
226 auto const balance = (*sle)[sfBalance].xrp();
228 if (balance < feePaid)
231 <<
"Insufficient balance:" <<
" balance=" <<
to_string(balance)
234 if ((balance > beast::zero) && !ctx.
view.
open())
249 auto const feePaid =
ctx_.
tx[sfFee].xrp();
279 <<
"applyTransaction: delay: source account does not exist "
287 if (t_seqProx.
isSeq())
292 JLOG(j.
trace()) <<
"applyTransaction: has both a TicketSequence "
293 "and a non-zero Sequence number";
296 if (t_seqProx != a_seq)
298 if (a_seq < t_seqProx)
301 <<
"applyTransaction: has future sequence number "
302 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
306 JLOG(j.
trace()) <<
"applyTransaction: has past sequence number "
307 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
320 JLOG(j.
trace()) <<
"applyTransaction: has future ticket id "
321 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
329 <<
"applyTransaction: ticket already used or never created "
330 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
348 <<
"applyTransaction: delay: source account does not exist "
354 (sle->getFieldH256(sfAccountTxnID) !=
372 sleAccount,
"ripple::Transactor::consumeSeqProxy : non-null account");
379 sleAccount->setFieldU32(sfSequence, seqProx.
value() + 1);
399 JLOG(j.
fatal()) <<
"Ticket disappeared from ledger.";
406 JLOG(j.
fatal()) <<
"Unable to delete Ticket from owner.";
415 JLOG(j.
fatal()) <<
"Could not find Ticket owner account root.";
419 if (
auto ticketCount = (*sleAccount)[~sfTicketCount])
421 if (*ticketCount == 1)
422 sleAccount->makeFieldAbsent(sfTicketCount);
424 ticketCount = *ticketCount - 1;
428 JLOG(j.
fatal()) <<
"TicketCount field missing from account root.";
446 "ripple::Transactor::preCompute : nonzero account");
461 sle !=
nullptr ||
account_ == beast::zero,
462 "ripple::Transactor::apply : non-null SLE or zero account");
477 if (sle->isFieldPresent(sfAccountTxnID))
504 <<
"checkSingleSign: signing public key type is unknown";
520 if ((*sleAccount)[~sfRegularKey] == idSigner)
526 if (!isMasterDisabled && idAccount == idSigner)
532 if (isMasterDisabled && idAccount == idSigner)
541 if (idSigner == idAccount)
544 if (isMasterDisabled)
547 else if ((*sleAccount)[~sfRegularKey] == idSigner)
551 else if (sleAccount->isFieldPresent(sfRegularKey))
555 <<
"checkSingleSign: Not authorized to use account.";
563 <<
"checkSingleSign: Not authorized to use account.";
578 if (!sleAccountSigners)
581 <<
"applyTransaction: Invalid: Not a multi-signing account.";
588 sleAccountSigners->isFieldPresent(sfSignerListID),
589 "ripple::Transactor::checkMultiSign : has signer list ID");
591 sleAccountSigners->getFieldU32(sfSignerListID) == 0,
592 "ripple::Transactor::checkMultiSign : signer list ID is 0");
594 auto accountSigners =
597 return accountSigners.error();
609 auto iter = accountSigners->begin();
610 for (
auto const& txSigner : txSigners)
612 AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
615 while (iter->account < txSignerAcctID)
617 if (++iter == accountSigners->end())
620 <<
"applyTransaction: Invalid SigningAccount.Account.";
624 if (iter->account != txSignerAcctID)
628 <<
"applyTransaction: Invalid SigningAccount.Account.";
635 auto const spk = txSigner.getFieldVL(sfSigningPubKey);
640 <<
"checkMultiSign: signing public key type is unknown";
644 AccountID const signingAcctIDFromPubKey =
674 if (signingAcctIDFromPubKey == txSignerAcctID)
681 sleTxSignerRoot->getFieldU32(sfFlags);
686 <<
"applyTransaction: Signer:Account lsfDisableMaster.";
695 if (!sleTxSignerRoot)
697 JLOG(ctx.
j.
trace()) <<
"applyTransaction: Non-phantom signer "
698 "lacks account root.";
702 if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
705 <<
"applyTransaction: Account lacks RegularKey.";
708 if (signingAcctIDFromPubKey !=
709 sleTxSignerRoot->getAccountID(sfRegularKey))
712 <<
"applyTransaction: Account doesn't match RegularKey.";
717 weightSum += iter->weight;
721 if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
724 <<
"applyTransaction: Signers failed to meet quorum.";
742 for (
auto const& index : offers)
762 for (
auto const& index : offers)
779 for (
auto const& index : creds)
795 <<
"removeDeletedTrustLines: deleted trustlines exceed max "
796 << trustLines.
size();
800 for (
auto const& index : trustLines)
802 if (
auto const sleState = view.
peek({ltRIPPLE_STATE, index});
807 <<
"removeDeletedTrustLines: failed to delete AMM trustline";
825 auto const balance = txnAcct->getFieldAmount(sfBalance).xrp();
829 balance != beast::zero && (!
view().
open() || balance >= fee),
830 "ripple::Transactor::reset : valid balance");
843 txnAcct->setFieldAmount(sfBalance, balance - fee);
846 isTesSuccess(ter),
"ripple::Transactor::reset : result is tesSUCCESS");
859 JLOG(
j_.
debug()) <<
"Transaction trapped: " << txHash;
883 JLOG(
j_.
fatal()) <<
"Transaction serdes mismatch";
887 "ripple::Transactor::operator() : transaction serdes mismatch");
906 "ripple::Transactor::operator() : result is not temUNKNOWN");
909 stream <<
"preclaim result: " <<
transToken(result);
941 bool const doOffers =
944 bool const doNFTokenOffers = (result ==
tecEXPIRED);
945 bool const doCredentials = (result ==
tecEXPIRED);
946 if (doOffers || doLines || doNFTokenOffers || doCredentials)
953 &expiredNFTokenOffers,
955 &expiredCredentials](
964 "ripple::Transactor::operator()::visit : non-null SLE "
966 if (doOffers && before &&
after &&
967 (before->getType() == ltOFFER) &&
968 (before->getFieldAmount(sfTakerPays) ==
969 after->getFieldAmount(sfTakerPays)))
975 if (doLines && before &&
after &&
976 (before->getType() == ltRIPPLE_STATE))
982 if (doNFTokenOffers && before &&
after &&
983 (before->getType() == ltNFTOKEN_OFFER))
986 if (doCredentials && before &&
after &&
987 (before->getType() == ltCREDENTIAL))
995 auto const resetResult =
reset(fee);
997 result = resetResult.first;
999 fee = resetResult.second;
1032 auto const resetResult =
reset(fee);
1034 result = resetResult.first;
1036 fee = resetResult.second;
1058 if (fee < beast::zero)
1059 Throw<std::logic_error>(
"fee charged is negative!");
1065 if (!
view().
open() && fee != beast::zero)
1072 JLOG(
j_.
trace()) << (applied ?
"applied " :
"not applied ")
1075 return {result, applied};
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual beast::Journal journal(std::string const &name)=0
virtual const std::optional< uint256 > & trapTxID() const =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.
void discard()
Discard changes and start fresh.
void destroyXRP(XRPAmount const &fee)
std::size_t size()
Get the number of unapplied changes.
void apply(TER)
Apply the transaction result to the base.
TER checkInvariants(TER const result, XRPAmount const fee)
Applies all invariant checkers one by one.
Writeable view to a ledger, for applying a transaction.
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.
RAII class to set and restore the current transaction rules.
RAII class to set and restore the Number switchover.
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.
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.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
RAII class to set and restore the STAmount canonicalize switchover.
AccountID getAccountID(SField const &field) const
const STArray & getFieldArray(SField const &field) const
bool isEquivalent(const STBase &t) const override
std::uint32_t getFieldU32(SField const &field) const
void add(Serializer &s) const override
STAmount const & getFieldAmount(SField const &field) const
bool isFieldPresent(SField const &field) const
uint256 getFieldH256(SField const &field) const
SeqProxy getSeqProxy() const
Json::Value getJson(JsonOptions options) const override
Blob getSigningPubKey() const
uint256 getTransactionID() const
A type that represents either a sequence value or a ticket value.
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
constexpr bool isSeq() const
constexpr std::uint32_t value() const
constexpr bool isTicket() const
Slice slice() const noexcept
static Expected< std::vector< SignerEntry >, NotTEC > deserialize(STObject const &obj, beast::Journal journal, std::string_view annotation)
TER consumeSeqProxy(SLE::pointer const &sleAccount)
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(PreclaimContext const &ctx)
std::pair< TER, bool > operator()()
Process the transaction.
virtual void preCompute()
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
std::pair< TER, XRPAmount > reset(XRPAmount fee)
Reset the context, discarding any changes made and adjust the fee.
Transactor(Transactor const &)=delete
static NotTEC checkMultiSign(PreclaimContext const &ctx)
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet signers(AccountID const &account) noexcept
A SignerList.
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
static ticket_t const ticket
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
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.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
@ telNETWORK_ID_MAKES_TX_NON_CANONICAL
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.
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
std::size_t constexpr unfundedOfferRemoveLimit
The maximum number of unfunded offers to delete at once.
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.
std::size_t constexpr oversizeMetaDataCap
The maximum number of metadata entries allowed in one transaction.
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
AccountID calcAccountID(PublicKey const &pk)
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
static void removeUnfundedOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
@ 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)
static bool adjustOwnerCount(ApplyContext &ctx, int count)
std::string transToken(TER code)
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)
bool isTecClaimHardFail(TER ter, ApplyFlags flags)
Return true if the transaction can claim a fee (tec), and the ApplyFlags do not allow soft failures.
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
@ SigBad
Signature is bad. Didn't do local checks.
std::string to_string(base_uint< Bits, Tag > const &a)
static void removeDeletedTrustLines(ApplyView &view, std::vector< uint256 > const &trustLines, beast::Journal viewJ)
XRPAmount scaleFeeLoad(XRPAmount fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
std::uint16_t constexpr maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules, Config const &config)
Checks transaction signature and local checks.
static bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
TERSubset< CanCvtToNotTEC > NotTEC
Reflects the fee settings for a particular ledger.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.
PreflightContext(Application &app_, STTx const &tx_, Rules const &rules_, ApplyFlags flags_, beast::Journal j_)