1#include <xrpld/app/main/Application.h>
2#include <xrpld/app/misc/DelegateUtils.h>
3#include <xrpld/app/misc/LoadFeeTrack.h>
4#include <xrpld/app/tx/apply.h>
5#include <xrpld/app/tx/detail/NFTokenUtils.h>
6#include <xrpld/app/tx/detail/SignerEntries.h>
7#include <xrpld/app/tx/detail/Transactor.h>
8#include <xrpld/core/Config.h>
10#include <xrpl/basics/Log.h>
11#include <xrpl/basics/contract.h>
12#include <xrpl/json/to_string.h>
13#include <xrpl/ledger/CredentialHelpers.h>
14#include <xrpl/ledger/View.h>
15#include <xrpl/protocol/Feature.h>
16#include <xrpl/protocol/Indexes.h>
17#include <xrpl/protocol/Protocol.h>
18#include <xrpl/protocol/TxFlags.h>
19#include <xrpl/protocol/UintTypes.h>
29 JLOG(ctx.
j.
warn()) <<
"Pseudo transactions cannot contain the "
30 "tfInnerBatchTxn flag.";
53 if (*txNID != nodeNID)
60 if (txID == beast::zero)
62 JLOG(ctx.
j.
warn()) <<
"applyTransaction: transaction id may not be zero";
86 JLOG(j.
debug()) <<
"preflightCheckSigningKey: invalid signing key";
98 if (signature && !signature->empty())
111 for (
auto const& signer : sigObject.
getFieldArray(sfSigners))
113 if (signer.isFieldPresent(sfTxnSignature) && !signer[sfTxnSignature].empty())
121 Slice const signingPubKey = sigObject[sfSigningPubKey];
122 if (!signingPubKey.
empty())
140 if (!ctx.
rules.
enabled(featurePermissionDelegationV1_1))
143 if (ctx.
tx[sfDelegate] == ctx.
tx[sfAccount])
147 if (
auto const ret =
preflight0(ctx, flagMask))
151 if (
id == beast::zero)
153 JLOG(ctx.
j.
warn()) <<
"preflight1: bad account id";
159 if (!fee.native() || fee.negative() || !
isLegalAmount(fee.xrp()))
161 JLOG(ctx.
j.
debug()) <<
"preflight1: invalid fee";
182 "Inner batch transaction must have a parent batch ID.");
200 "xrpl::Transactor::preflight2",
201 "InnerBatch flag only set if feature enabled");
211 JLOG(ctx.
j.
debug()) <<
"preflight2: bad signature. " << sigValid.second;
228 , account_(ctx.tx.getAccountID(sfAccount))
237 return !slice->empty() && slice->length() <= maxLength;
255 auto const delegate = tx[~sfDelegate];
260 auto const sle =
view.
read(delegateKey);
282 return baseFee + (signerCount * baseFee);
301 "xrpl::Transactor::calculateOwnerReserveFee : Owner reserve is "
315 if (!ctx.
tx[sfFee].native())
318 auto const feePaid = ctx.
tx[sfFee].xrp();
322 if (feePaid == beast::zero)
325 JLOG(ctx.
j.
trace()) <<
"Batch: Fee must be zero.";
337 if (feePaid < feeDue)
344 if (feePaid == beast::zero)
353 auto const balance = (*sle)[sfBalance].xrp();
355 if (balance < feePaid)
357 JLOG(ctx.
j.
trace()) <<
"Insufficient balance:" <<
" balance=" <<
to_string(balance)
360 if ((balance > beast::zero) && !ctx.
view.
open())
375 auto const feePaid =
ctx_.
tx[sfFee].xrp();
385 delegatedSle->setFieldAmount(sfBalance, delegatedSle->getFieldAmount(sfBalance) - feePaid);
415 JLOG(j.
trace()) <<
"applyTransaction: delay: source account does not exist " <<
toBase58(
id);
422 if (t_seqProx.
isSeq())
426 JLOG(j.
trace()) <<
"applyTransaction: has both a TicketSequence "
427 "and a non-zero Sequence number";
430 if (t_seqProx != a_seq)
432 if (a_seq < t_seqProx)
434 JLOG(j.
trace()) <<
"applyTransaction: has future sequence number "
435 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
439 JLOG(j.
trace()) <<
"applyTransaction: has past sequence number "
440 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
453 JLOG(j.
trace()) <<
"applyTransaction: has future ticket id "
454 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
461 JLOG(j.
trace()) <<
"applyTransaction: ticket already used or never created "
462 <<
"a_seq=" << a_seq <<
" t_seq=" << t_seqProx;
479 JLOG(ctx.
j.
trace()) <<
"applyTransaction: delay: source account does not exist " <<
toBase58(
id);
484 (sle->getFieldH256(sfAccountTxnID) != ctx.
tx.
getFieldH256(sfAccountTxnID)))
499 XRPL_ASSERT(sleAccount,
"xrpl::Transactor::consumeSeqProxy : non-null account");
506 sleAccount->setFieldU32(sfSequence, seqProx.
value() + 1);
522 JLOG(j.
fatal()) <<
"Ticket disappeared from ledger.";
531 JLOG(j.
fatal()) <<
"Unable to delete Ticket from owner.";
542 JLOG(j.
fatal()) <<
"Could not find Ticket owner account root.";
547 if (
auto ticketCount = (*sleAccount)[~sfTicketCount])
549 if (*ticketCount == 1)
550 sleAccount->makeFieldAbsent(sfTicketCount);
552 ticketCount = *ticketCount - 1;
557 JLOG(j.
fatal()) <<
"TicketCount field missing from account root.";
574 XRPL_ASSERT(
account_ != beast::zero,
"xrpl::Transactor::preCompute : nonzero account");
588 XRPL_ASSERT(sle !=
nullptr ||
account_ == beast::zero,
"xrpl::Transactor::apply : non-null SLE or zero account");
603 if (sle->isFieldPresent(sfAccountTxnID))
631 auto const pkSigner = sigObject.
getFieldVL(sfSigningPubKey);
658 XRPL_ASSERT(!pkSigner.empty(),
"xrpl::Transactor::checkSign : non-empty signer");
662 JLOG(j.
trace()) <<
"checkSign: signing public key type is unknown";
678 auto const idAccount =
688 for (
auto const& signer : signers)
690 auto const idAccount = signer.getAccountID(sfAccount);
692 Blob const& pkSigner = signer.getFieldVL(sfSigningPubKey);
693 if (pkSigner.
empty())
712 if (idAccount != idSigner)
736 if ((*sleAccount)[~sfRegularKey] == idSigner)
742 if (!isMasterDisabled && idAccount == idSigner)
748 if (isMasterDisabled && idAccount == idSigner)
768 if (!sleAccountSigners)
770 JLOG(j.
trace()) <<
"applyTransaction: Invalid: Not a multi-signing account.";
777 sleAccountSigners->isFieldPresent(sfSignerListID),
"xrpl::Transactor::checkMultiSign : has signer list ID");
779 sleAccountSigners->getFieldU32(sfSignerListID) == 0,
"xrpl::Transactor::checkMultiSign : signer list ID is 0");
783 return accountSigners.error();
795 auto iter = accountSigners->begin();
796 for (
auto const& txSigner : txSigners)
798 AccountID const txSignerAcctID = txSigner.getAccountID(sfAccount);
801 while (iter->account < txSignerAcctID)
803 if (++iter == accountSigners->end())
805 JLOG(j.
trace()) <<
"applyTransaction: Invalid SigningAccount.Account.";
809 if (iter->account != txSignerAcctID)
812 JLOG(j.
trace()) <<
"applyTransaction: Invalid SigningAccount.Account.";
819 auto const spk = txSigner.getFieldVL(sfSigningPubKey);
825 JLOG(j.
trace()) <<
"checkMultiSign: signing public key type is unknown";
831 "xrpl::Transactor::checkMultiSign : non-empty signer or "
833 AccountID const signingAcctIDFromPubKey =
863 if (signingAcctIDFromPubKey == txSignerAcctID)
869 std::uint32_t const signerAccountFlags = sleTxSignerRoot->getFieldU32(sfFlags);
873 JLOG(j.
trace()) <<
"applyTransaction: Signer:Account lsfDisableMaster.";
882 if (!sleTxSignerRoot)
884 JLOG(j.
trace()) <<
"applyTransaction: Non-phantom signer "
885 "lacks account root.";
889 if (!sleTxSignerRoot->isFieldPresent(sfRegularKey))
891 JLOG(j.
trace()) <<
"applyTransaction: Account lacks RegularKey.";
894 if (signingAcctIDFromPubKey != sleTxSignerRoot->getAccountID(sfRegularKey))
896 JLOG(j.
trace()) <<
"applyTransaction: Account doesn't match RegularKey.";
901 weightSum += iter->weight;
905 if (weightSum < sleAccountSigners->getFieldU32(sfSignerQuorum))
907 JLOG(j.
trace()) <<
"applyTransaction: Signers failed to meet quorum.";
922 for (
auto const& index : offers)
939 for (
auto const& index : offers)
953 for (
auto const& index : creds)
965 JLOG(viewJ.
error()) <<
"removeDeletedTrustLines: deleted trustlines exceed max " << trustLines.
size();
969 for (
auto const& index : trustLines)
971 if (
auto const sleState = view.
peek({ltRIPPLE_STATE, index});
974 JLOG(viewJ.
error()) <<
"removeDeletedTrustLines: failed to delete AMM trustline";
996 auto const payerSle =
1001 auto const balance = payerSle->getFieldAmount(sfBalance).xrp();
1005 balance != beast::zero && (!
view().
open() || balance >= fee),
"xrpl::Transactor::reset : valid balance");
1019 payerSle->setFieldAmount(sfBalance, balance - fee);
1021 XRPL_ASSERT(
isTesSuccess(ter),
"xrpl::Transactor::reset : result is tesSUCCESS");
1026 if (payerSle != txnAcct)
1038 JLOG(
j_.
debug()) <<
"Transaction trapped: " << txHash;
1066 JLOG(
j_.
fatal()) <<
"Transaction serdes mismatch";
1069 UNREACHABLE(
"xrpl::Transactor::operator() : transaction serdes mismatch");
1086 XRPL_ASSERT(result !=
temUNKNOWN,
"xrpl::Transactor::operator() : result is not temUNKNOWN");
1089 stream <<
"preclaim result: " <<
transToken(result);
1121 bool const doNFTokenOffers = (result ==
tecEXPIRED);
1122 bool const doCredentials = (result ==
tecEXPIRED);
1123 if (doOffers || doLines || doNFTokenOffers || doCredentials)
1130 &expiredNFTokenOffers,
1132 &expiredCredentials](
1141 "xrpl::Transactor::operator()::visit : non-null SLE "
1143 if (doOffers && before &&
after && (before->getType() == ltOFFER) &&
1144 (before->getFieldAmount(sfTakerPays) ==
after->getFieldAmount(sfTakerPays)))
1150 if (doLines && before &&
after && (before->getType() == ltRIPPLE_STATE))
1156 if (doNFTokenOffers && before &&
after && (before->getType() == ltNFTOKEN_OFFER))
1159 if (doCredentials && before &&
after && (before->getType() == ltCREDENTIAL))
1167 auto const resetResult =
reset(fee);
1169 result = resetResult.first;
1171 fee = resetResult.second;
1200 auto const resetResult =
reset(fee);
1202 result = resetResult.first;
1204 fee = resetResult.second;
1227 if (fee < beast::zero)
1228 Throw<std::logic_error>(
"fee charged is negative!");
1234 if (!
view().
open() && fee != beast::zero)
1246 JLOG(
j_.
trace()) << (applied ?
"applied " :
"not applied ") <<
transToken(result);
1248 return {result, applied, metadata};
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
virtual HashRouter & getHashRouter()=0
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual beast::Journal journal(std::string const &name)=0
virtual std::optional< uint256 > const & trapTxID() const =0
State information when applying a tx.
std::size_t size()
Get the number of unapplied changes.
void destroyXRP(XRPAmount const &fee)
ApplyFlags const & flags() const
void discard()
Discard changes and start fresh.
std::optional< TxMeta > apply(TER)
Apply the transaction result to the base.
TER checkInvariants(TER const result, XRPAmount const fee)
Applies all invariant checkers one by one.
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.
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 void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
RAII class to set and restore the current transaction rules.
RAII class to set and restore the Number switchover.
virtual Rules const & rules() const =0
Returns the tx processing rules.
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.
virtual bool txExists(key_type const &key) const =0
Returns true if a tx exists in the tx map.
virtual bool open() const =0
Returns true if this reflects an open ledger.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
virtual std::string getFullText() const
Blob getFieldVL(SField const &field) const
bool isEquivalent(STBase const &t) const override
std::uint32_t getFieldU32(SField const &field) const
STArray const & getFieldArray(SField const &field) const
void add(Serializer &s) const override
bool isFlag(std::uint32_t) const
bool isFieldPresent(SField const &field) const
uint256 getFieldH256(SField const &field) const
STBase const & peekAtField(SField const &field) const
AccountID getAccountID(SField const &field) const
STAmount const & getFieldAmount(SField const &field) const
std::uint32_t getFlags() const
Json::Value getJson(JsonOptions options) const override
SeqProxy getSeqProxy() 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 isTicket() const
constexpr std::uint32_t value() const
constexpr bool isSeq() 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.
static NotTEC preflight1(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the account and fee fields.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
TER consumeSeqProxy(SLE::pointer const &sleAccount)
void trapTransaction(uint256) const
static TER checkFee(PreclaimContext const &ctx, XRPAmount baseFee)
static NotTEC checkSign(PreclaimContext const &ctx)
static XRPAmount calculateOwnerReserveFee(ReadView const &view, STTx const &tx)
ApplyResult operator()()
Process the transaction.
static NotTEC checkPermission(ReadView const &view, STTx const &tx)
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 preflightSigValidated(PreflightContext const &ctx)
static NotTEC checkBatchSign(PreclaimContext const &ctx)
static NotTEC checkSeqProxy(ReadView const &view, STTx const &tx, beast::Journal j)
static NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
static NotTEC checkSingleSign(ReadView const &view, AccountID const &idSigner, AccountID const &idAccount, std::shared_ptr< SLE const > sleAccount, beast::Journal const j)
Transactor(Transactor const &)=delete
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
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 bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
virtual void preCompute()
std::pair< TER, XRPAmount > reset(XRPAmount fee)
Reset the context, discarding any changes made and adjust the fee.
static TER ticketDelete(ApplyView &view, AccountID const &account, uint256 const &ticketIndex, beast::Journal j)
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
NotTEC preflightCheckSigningKey(STObject const &sigObject, beast::Journal j)
Checks the validity of the transactor signing key.
std::optional< NotTEC > preflightCheckSimulateKeys(ApplyFlags flags, STObject const &sigObject, beast::Journal j)
Checks the special signing key state needed for simulation.
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 ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
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.
@ telNETWORK_ID_MAKES_TX_NON_CANONICAL
@ terNO_DELEGATE_PERMISSION
static void removeExpiredNFTokenOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
bool isLegalAmount(XRPAmount const &amount)
Returns true if the amount does not exceed the initial XRP in existence.
@ SigBad
Signature is bad. Didn't do local checks.
std::size_t constexpr expiredOfferRemoveLimit
The maximum number of expired offers to delete at once.
constexpr std::uint32_t tfInnerBatchTxn
std::string to_string(base_uint< Bits, Tag > const &a)
static void removeExpiredCredentials(ApplyView &view, std::vector< uint256 > const &creds, beast::Journal viewJ)
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)
std::uint16_t constexpr maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
static void removeUnfundedOffers(ApplyView &view, std::vector< uint256 > const &offers, beast::Journal viewJ)
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
std::string transToken(TER code)
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
bool isPseudoAccount(std::shared_ptr< SLE const > sleAcct, std::set< SField const * > const &pseudoFieldFilter={})
std::size_t constexpr oversizeMetaDataCap
The maximum number of metadata entries allowed in one transaction.
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
@ open
We haven't closed our ledger yet, but others might have.
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
AccountID calcAccountID(PublicKey const &pk)
static void removeDeletedTrustLines(ApplyView &view, std::vector< uint256 > const &trustLines, beast::Journal viewJ)
std::pair< Validity, std::string > checkValidity(HashRouter &router, STTx const &tx, Rules const &rules, Config const &config)
Checks transaction signature and local checks.
TER offerDelete(ApplyView &view, std::shared_ptr< SLE > const &sle, beast::Journal j)
Delete an offer.
XRPAmount scaleFeeLoad(XRPAmount fee, LoadFeeTrack const &feeTrack, Fees const &fees, bool bUnlimited)
bool isTesSuccess(TER x) noexcept
std::size_t constexpr unfundedOfferRemoveLimit
The maximum number of unfunded offers to delete at once.
NotTEC preflight0(PreflightContext const &ctx, std::uint32_t flagMask)
Performs early sanity checks on the txid.
bool isTecClaim(TER x) noexcept
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)
TERSubset< CanCvtToNotTEC > NotTEC
bool isPseudoTx(STObject const &tx)
Check whether a transaction is a pseudo-transaction.
std::string to_short_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t tfUniversalMask
Reflects the fee settings for a particular ledger.
State information when determining if a tx is likely to claim a fee.
std::optional< uint256 const > const parentBatchId
State information when preflighting a tx.
std::optional< uint256 const > parentBatchId