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> 
   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> 
   48        JLOG(ctx.
j.
warn()) << 
"Pseudo transactions cannot contain the " 
   49                              "tfInnerBatchTxn flag.";
 
   72            if (*txNID != nodeNID)
 
   79    if (txID == beast::zero)
 
   82            << 
"applyTransaction: transaction id may not be zero";
 
   90            << 
": invalid flags.";
 
 
  106    if (
auto const spk = sigObject.
getFieldVL(sfSigningPubKey);
 
  109        JLOG(j.
debug()) << 
"preflightCheckSigningKey: invalid signing key";
 
 
  124        if (signature && !signature->empty())
 
  137        for (
auto const& signer : sigObject.
getFieldArray(sfSigners))
 
  139            if (signer.isFieldPresent(sfTxnSignature) &&
 
  140                !signer[sfTxnSignature].empty())
 
  148        Slice const signingPubKey = sigObject[sfSigningPubKey];
 
  149        if (!signingPubKey.
empty())
 
 
  175        if (!ctx.
rules.
enabled(featurePermissionDelegationV1_1))
 
  178        if (ctx.
tx[sfDelegate] == ctx.
tx[sfAccount])
 
  182    if (
auto const ret = 
preflight0(ctx, flagMask))
 
  186    if (
id == beast::zero)
 
  188        JLOG(ctx.
j.
warn()) << 
"preflight1: bad account id";
 
  194    if (!fee.native() || fee.negative() || !
isLegalAmount(fee.xrp()))
 
  196        JLOG(ctx.
j.
debug()) << 
"preflight1: invalid fee";
 
  219        "Inner batch transaction must have a parent batch ID.");
 
 
  238        JLOG(ctx.
j.
debug()) << 
"preflight2: bad signature. " << sigValid.second;
 
 
  250    , account_(ctx.tx.getAccountID(sfAccount))
 
 
  261    return !slice->empty() && slice->length() <= maxLength;
 
 
  279    auto const delegate = tx[~sfDelegate];
 
  284    auto const sle = 
view.
read(delegateKey);
 
 
  307    return baseFee + (signerCount * baseFee);
 
 
  326        "ripple::Transactor::calculateOwnerReserveFee : Owner reserve is " 
 
  344    if (!ctx.
tx[sfFee].native())
 
  347    auto const feePaid = ctx.
tx[sfFee].xrp();
 
  351        if (feePaid == beast::zero)
 
  354        JLOG(ctx.
j.
trace()) << 
"Batch: Fee must be zero.";
 
  367        if (feePaid < feeDue)
 
  370                << 
"Insufficient fee paid: " << 
to_string(feePaid) << 
"/" 
  376    if (feePaid == beast::zero)
 
  386    auto const balance = (*sle)[sfBalance].xrp();
 
  388    if (balance < feePaid)
 
  391            << 
"Insufficient balance:" << 
" balance=" << 
to_string(balance)
 
  394        if ((balance > beast::zero) && !ctx.
view.
open())
 
 
  409    auto const feePaid = 
ctx_.
tx[sfFee].xrp();
 
  419        delegatedSle->setFieldAmount(
 
  420            sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid);
 
 
  454            << 
"applyTransaction: delay: source account does not exist " 
  462    if (t_seqProx.
isSeq())
 
  467            JLOG(j.
trace()) << 
"applyTransaction: has both a TicketSequence " 
  468                               "and a non-zero Sequence number";
 
  471        if (t_seqProx != a_seq)
 
  473            if (a_seq < t_seqProx)
 
  476                    << 
"applyTransaction: has future sequence number " 
  477                    << 
"a_seq=" << a_seq << 
" t_seq=" << t_seqProx;
 
  481            JLOG(j.
trace()) << 
"applyTransaction: has past sequence number " 
  482                            << 
"a_seq=" << a_seq << 
" t_seq=" << t_seqProx;
 
  495            JLOG(j.
trace()) << 
"applyTransaction: has future ticket id " 
  496                            << 
"a_seq=" << a_seq << 
" t_seq=" << t_seqProx;
 
  504                << 
"applyTransaction: ticket already used or never created " 
  505                << 
"a_seq=" << a_seq << 
" t_seq=" << t_seqProx;
 
 
  523            << 
"applyTransaction: delay: source account does not exist " 
  529        (sle->getFieldH256(sfAccountTxnID) !=
 
 
  547        sleAccount, 
"ripple::Transactor::consumeSeqProxy : non-null account");
 
  554        sleAccount->setFieldU32(sfSequence, seqProx.
value() + 1);
 
 
  575        JLOG(j.
fatal()) << 
"Ticket disappeared from ledger.";
 
  584        JLOG(j.
fatal()) << 
"Unable to delete Ticket from owner.";
 
  595        JLOG(j.
fatal()) << 
"Could not find Ticket owner account root.";
 
  600    if (
auto ticketCount = (*sleAccount)[~sfTicketCount])
 
  602        if (*ticketCount == 1)
 
  603            sleAccount->makeFieldAbsent(sfTicketCount);
 
  605            ticketCount = *ticketCount - 1;
 
  610        JLOG(j.
fatal()) << 
"TicketCount field missing from account root.";
 
 
  629        "ripple::Transactor::preCompute : nonzero account");
 
 
  644        sle != 
nullptr || 
account_ == beast::zero,
 
  645        "ripple::Transactor::apply : non-null SLE or zero account");
 
  660        if (sle->isFieldPresent(sfAccountTxnID))
 
 
  677    auto const pkSigner = sigObject.
getFieldVL(sfSigningPubKey);
 
  682        if (sigObject.
isFieldPresent(sfTxnSignature) || !pkSigner.empty() ||
 
  690    if ((flags & 
tapDRY_RUN) && pkSigner.empty() &&
 
  707        !pkSigner.empty(), 
"ripple::Transactor::checkSign : non-empty signer");
 
  711        JLOG(j.
trace()) << 
"checkSign: signing public key type is unknown";
 
  716    auto const idSigner = pkSigner.empty()
 
 
  740    for (
auto const& signer : signers)
 
  742        auto const idAccount = signer.getAccountID(sfAccount);
 
  744        Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
 
  745        if (pkSigner.
empty())
 
  748                    ctx.
view, ctx.
flags, idAccount, signer, ctx.
j);
 
  766                if (idAccount != idSigner)
 
  773                    ctx.
view, idSigner, idAccount, sleAccount, ctx.
j);
 
 
  792    if ((*sleAccount)[~sfRegularKey] == idSigner)
 
  798    if (!isMasterDisabled && idAccount == idSigner)
 
  804    if (isMasterDisabled && idAccount == idSigner)
 
 
  825    if (!sleAccountSigners)
 
  828            << 
"applyTransaction: Invalid: Not a multi-signing account.";
 
  835        sleAccountSigners->isFieldPresent(sfSignerListID),
 
  836        "ripple::Transactor::checkMultiSign : has signer list ID");
 
  838        sleAccountSigners->getFieldU32(sfSignerListID) == 0,
 
  839        "ripple::Transactor::checkMultiSign : signer list ID is 0");
 
  841    auto accountSigners =
 
  844        return accountSigners.error();
 
  856    auto iter = accountSigners->begin();
 
  857    for (
auto const& txSigner : txSigners)
 
  859        AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
 
  862        while (iter->account < txSignerAcctID)
 
  864            if (++iter == accountSigners->end())
 
  867                    << 
"applyTransaction: Invalid SigningAccount.Account.";
 
  871        if (iter->account != txSignerAcctID)
 
  875                << 
"applyTransaction: Invalid SigningAccount.Account.";
 
  882        auto const spk = txSigner.getFieldVL(sfSigningPubKey);
 
  889                << 
"checkMultiSign: signing public key type is unknown";
 
  895            "ripple::Transactor::checkMultiSign : non-empty signer or " 
  897        AccountID const signingAcctIDFromPubKey = spk.empty()
 
  928        if (signingAcctIDFromPubKey == txSignerAcctID)
 
  935                    sleTxSignerRoot->getFieldU32(sfFlags);
 
  940                        << 
"applyTransaction: Signer:Account lsfDisableMaster.";
 
  949            if (!sleTxSignerRoot)
 
  951                JLOG(j.
trace()) << 
"applyTransaction: Non-phantom signer " 
  952                                   "lacks account root.";
 
  956            if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
 
  959                    << 
"applyTransaction: Account lacks RegularKey.";
 
  962            if (signingAcctIDFromPubKey !=
 
  963                sleTxSignerRoot->getAccountID(sfRegularKey))
 
  966                    << 
"applyTransaction: Account doesn't match RegularKey.";
 
  971        weightSum += iter->weight;
 
  975    if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
 
  977        JLOG(j.
trace()) << 
"applyTransaction: Signers failed to meet quorum.";
 
 
  995    for (
auto const& index : offers)
 
 
 1015    for (
auto const& index : offers)
 
 
 1032    for (
auto const& index : creds)
 
 
 1048            << 
"removeDeletedTrustLines: deleted trustlines exceed max " 
 1049            << trustLines.
size();
 
 1053    for (
auto const& index : trustLines)
 
 1055        if (
auto const sleState = view.
peek({ltRIPPLE_STATE, index});
 
 1060                << 
"removeDeletedTrustLines: failed to delete AMM trustline";
 
 
 1075    auto const txnAcct =
 
 1089    auto const balance = payerSle->getFieldAmount(sfBalance).xrp();
 
 1093        balance != beast::zero && (!
view().
open() || balance >= fee),
 
 1094        "ripple::Transactor::reset : valid balance");
 
 1108    payerSle->setFieldAmount(sfBalance, balance - fee);
 
 1111        isTesSuccess(ter), 
"ripple::Transactor::reset : result is tesSUCCESS");
 
 1116        if (payerSle != txnAcct)
 
 
 1128    JLOG(
j_.
debug()) << 
"Transaction trapped: " << txHash;
 
 
 1152            JLOG(
j_.
fatal()) << 
"Transaction serdes mismatch";
 
 1156                "ripple::Transactor::operator() : transaction serdes mismatch");
 
 1176        "ripple::Transactor::operator() : result is not temUNKNOWN");
 
 1179        stream << 
"preclaim result: " << 
transToken(result);
 
 1210        bool const doOffers =
 
 1213        bool const doNFTokenOffers = (result == 
tecEXPIRED);
 
 1214        bool const doCredentials = (result == 
tecEXPIRED);
 
 1215        if (doOffers || doLines || doNFTokenOffers || doCredentials)
 
 1222                        &expiredNFTokenOffers,
 
 1224                        &expiredCredentials](
 
 1233                        "ripple::Transactor::operator()::visit : non-null SLE " 
 1235                    if (doOffers && before && 
after &&
 
 1236                        (before->getType() == ltOFFER) &&
 
 1237                        (before->getFieldAmount(sfTakerPays) ==
 
 1238                         after->getFieldAmount(sfTakerPays)))
 
 1244                    if (doLines && before && 
after &&
 
 1245                        (before->getType() == ltRIPPLE_STATE))
 
 1251                    if (doNFTokenOffers && before && 
after &&
 
 1252                        (before->getType() == ltNFTOKEN_OFFER))
 
 1255                    if (doCredentials && before && 
after &&
 
 1256                        (before->getType() == ltCREDENTIAL))
 
 1264            auto const resetResult = 
reset(fee);
 
 1266                result = resetResult.first;
 
 1268            fee = resetResult.second;
 
 1301            auto const resetResult = 
reset(fee);
 
 1303                result = resetResult.first;
 
 1305            fee = resetResult.second;
 
 1328        if (fee < beast::zero)
 
 1329            Throw<std::logic_error>(
"fee charged is negative!");
 
 1335        if (!
view().
open() && fee != beast::zero)
 
 1347    JLOG(
j_.
trace()) << (applied ? 
"applied " : 
"not applied ")
 
 1350    return {result, applied, metadata};
 
 
A generic endpoint for log messages.
 
Stream trace() const
Severity stream access functions.
 
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)
 
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.
 
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.
 
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
 
virtual std::string getFullText() const
 
Blob getFieldVL(SField const &field) const
 
AccountID getAccountID(SField const &field) const
 
STArray const & getFieldArray(SField const &field) const
 
bool isFlag(std::uint32_t) const
 
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
 
bool isEquivalent(STBase const &t) const override
 
STBase const & peekAtField(SField const &field) const
 
std::uint32_t getFlags() const
 
uint256 getFieldH256(SField const &field) const
 
SeqProxy getSeqProxy() const
 
Json::Value getJson(JsonOptions options) const override
 
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)
 
An immutable linear range of bytes.
 
bool empty() const noexcept
Return true if the byte range is empty.
 
TER consumeSeqProxy(SLE::pointer const &sleAccount)
 
ApplyResult operator()()
Process the transaction.
 
static NotTEC preflightSigValidated(PreflightContext const &ctx)
 
static NotTEC checkPriorTxAndLastLedger(PreclaimContext const &ctx)
 
static NotTEC checkMultiSign(ReadView const &view, ApplyFlags flags, AccountID const &id, STObject const &sigObject, beast::Journal const j)
 
static TER checkFee(PreclaimContext const &ctx, XRPAmount baseFee)
 
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
 
static NotTEC checkSeqProxy(ReadView const &view, STTx const &tx, beast::Journal j)
 
static NotTEC checkSign(PreclaimContext const &ctx)
 
void trapTransaction(uint256) const
 
static XRPAmount minimumFee(Application &app, XRPAmount baseFee, Fees const &fees, ApplyFlags flags)
Compute the minimum fee required to process a transaction with a given baseFee based on the current s...
 
static NotTEC preflight1(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the account and fee fields.
 
static NotTEC checkSingleSign(ReadView const &view, AccountID const &idSigner, AccountID const &idAccount, std::shared_ptr< SLE const > sleAccount, beast::Journal const j)
 
static NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
 
static NotTEC checkBatchSign(PreclaimContext const &ctx)
 
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
 
virtual void preCompute()
 
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
 
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
 
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
 
std::pair< TER, XRPAmount > reset(XRPAmount fee)
Reset the context, discarding any changes made and adjust the fee.
 
Transactor(Transactor const &)=delete
 
static NotTEC checkPermission(ReadView const &view, STTx const &tx)
 
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
 
std::optional< NotTEC > preflightCheckSimulateKeys(ApplyFlags flags, STObject const &sigObject, beast::Journal j)
Checks the special signing key state needed for simulation.
 
NotTEC preflightCheckSigningKey(STObject const &sigObject, beast::Journal j)
Checks the validity of the transactor signing key.
 
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
 
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 to_short_string(base_uint< Bits, Tag > const &a)
 
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
 
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
 
@ telNETWORK_ID_MAKES_TX_NON_CANONICAL
 
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.
 
std::size_t constexpr unfundedOfferRemoveLimit
The maximum number of unfunded offers to delete at once.
 
std::size_t constexpr expiredOfferRemoveLimit
The maximum number of expired offers to delete at once.
 
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
 
std::size_t constexpr oversizeMetaDataCap
The maximum number of metadata entries allowed in one transaction.
 
AccountID calcAccountID(PublicKey const &pk)
 
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)
 
std::string transToken(TER code)
 
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)
 
bool isTesSuccess(TER x) noexcept
 
@ 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)
 
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
 
constexpr std::uint32_t tfUniversalMask
 
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.
 
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.
 
@ terNO_DELEGATE_PERMISSION
 
bool isTecClaim(TER x) noexcept
 
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
 
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
 
TERSubset< CanCvtToNotTEC > NotTEC
 
constexpr std::uint32_t tfInnerBatchTxn
 
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.
 
std::optional< uint256 const  > parentBatchId