1#include <xrpld/app/tx/apply.h>
2#include <xrpld/app/tx/detail/Batch.h>
4#include <xrpl/basics/Log.h>
5#include <xrpl/ledger/Sandbox.h>
6#include <xrpl/ledger/View.h>
7#include <xrpl/protocol/Feature.h>
8#include <xrpl/protocol/Indexes.h>
9#include <xrpl/protocol/SystemParameters.h>
10#include <xrpl/protocol/TER.h>
11#include <xrpl/protocol/TxFlags.h>
46 JLOG(
debugLog().error()) <<
"BatchTrace: Base fee overflow detected.";
62 JLOG(
debugLog().error()) <<
"BatchTrace: Raw Transactions array exceeds max entries.";
69 STTx const stx =
STTx{std::move(txn)};
74 JLOG(
debugLog().error()) <<
"BatchTrace: Inner Batch transaction found.";
81 if (txnFees > maxAmount - fee)
83 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in txnFees calculation.";
100 JLOG(
debugLog().error()) <<
"BatchTrace: Batch Signers array exceeds max entries.";
105 for (
STObject const& signer : signers)
107 if (signer.isFieldPresent(sfTxnSignature))
109 else if (signer.isFieldPresent(sfSigners))
110 signerCount += signer.getFieldArray(sfSigners).size();
115 if (signerCount > 0 &&
view.
fees().
base > maxAmount / signerCount)
117 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in signerCount calculation.";
125 if (signerFees > maxAmount - txnFees)
127 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in signerFees calculation.";
130 if (txnFees + signerFees > maxAmount - batchBase)
132 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in total fee calculation.";
138 return signerFees + txnFees + batchBase;
188 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
189 <<
"too many flags.";
194 if (rawTxns.size() <= 1)
196 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
197 <<
"txns array must have at least 2 entries.";
203 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
204 <<
"txns array exceeds 8 entries.";
211 auto checkSignatureFields = [&parentBatchId, &j = ctx.
j](
213 if (sig.isFieldPresent(sfTxnSignature))
215 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
216 <<
"inner txn " << label <<
"cannot include TxnSignature. "
221 if (sig.isFieldPresent(sfSigners))
223 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
224 <<
"inner txn " << label <<
" cannot include Signers. "
229 if (!sig.getFieldVL(sfSigningPubKey).empty())
231 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
232 <<
"inner txn " << label <<
" SigningPubKey must be empty. "
241 STTx const stx =
STTx{std::move(rb)};
243 if (!uniqueHashes.
emplace(hash).second)
245 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
246 <<
"duplicate Txn found. "
251 auto const txType = stx.
getFieldU16(sfTransactionType);
252 if (txType == ttBATCH)
254 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
255 <<
"batch cannot have an inner batch txn. "
261 return txType == disabled;
269 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
270 <<
"inner txn must have the tfInnerBatchTxn flag. "
275 if (
auto const ret = checkSignatureFields(stx, hash))
282 auto const counterpartySignature = stx.
getFieldObject(sfCounterpartySignature);
283 if (
auto const ret = checkSignatureFields(counterpartySignature, hash,
"counterparty signature "))
290 if (
auto const fee = stx.
getFieldAmount(sfFee); !fee.native() || fee.xrp() != beast::zero)
292 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
293 <<
"inner txn must have a fee of 0. "
302 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
303 <<
"inner txn preflight failed: " <<
transHuman(preflightResult.ter) <<
" "
311 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
312 <<
"inner txn must have exactly one of Sequence and "
321 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
322 <<
"inner txn must have either Sequence or "
331 if (
auto const seq = stx.
getFieldU32(sfSequence); seq != 0)
333 if (!accountSeqTicket[innerAccount].insert(seq).second)
335 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
336 <<
"duplicate sequence found: "
344 if (
auto const ticket = stx.
getFieldU32(sfTicketSequence);
345 !accountSeqTicket[innerAccount].
insert(ticket).second)
347 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
348 <<
"duplicate ticket found: "
370 auto const innerAccount = rb.getAccountID(sfAccount);
374 if (innerAccount != outerAccount)
375 requiredSigners.
insert(innerAccount);
378 if (
auto const counterparty = rb.at(~sfCounterparty); counterparty && counterparty != outerAccount)
379 requiredSigners.
insert(*counterparty);
391 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
392 <<
"signers array exceeds 8 entries.";
401 for (
auto const& signer : signers)
403 AccountID const signerAccount = signer.getAccountID(sfAccount);
404 if (signerAccount == outerAccount)
406 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
407 <<
"signer cannot be the outer account: " << signerAccount;
411 if (!batchSigners.
insert(signerAccount).second)
413 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
414 <<
"duplicate signer found: " << signerAccount;
420 if (requiredSigners.
erase(signerAccount) == 0)
422 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
423 <<
"no account signature for inner txn.";
433 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
434 <<
"invalid batch txn signature: " << sigResult.error();
439 if (!requiredSigners.
empty())
441 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
442 <<
"invalid batch signers.";
static constexpr auto disabledTxTypes
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Calculates the total base fee for a batch transaction.
static NotTEC preflight(PreflightContext const &ctx)
Performs preflight validation checks for a Batch transaction.
static NotTEC checkSign(PreclaimContext const &ctx)
Checks the validity of signatures for a batch transaction.
TER doApply() override
Applies the outer batch transaction.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static NotTEC preflightSigValidated(PreflightContext const &ctx)
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
std::uint32_t getFieldU32(SField const &field) const
STArray const & getFieldArray(SField const &field) const
bool isFieldPresent(SField const &field) const
STObject getFieldObject(SField const &field) const
AccountID getAccountID(SField const &field) const
std::uint16_t getFieldU16(SField const &field) const
STAmount const & getFieldAmount(SField const &field) const
std::uint32_t getFlags() const
Expected< void, std::string > checkBatchSign(Rules const &rules) const
TxType getTxnType() const
uint256 getTransactionID() const
static NotTEC checkSign(PreclaimContext const &ctx)
static NotTEC checkBatchSign(PreclaimContext const &ctx)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
beast::Journal debugLog()
Returns a debug journal.
constexpr std::uint32_t tfInnerBatchTxn
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
std::string transHuman(TER code)
constexpr std::uint32_t tfAllOrNothing
std::size_t constexpr maxBatchTxCount
The maximum number of transactions that can be in a batch.
constexpr std::uint32_t tfOnlyOne
constexpr std::uint32_t const tfBatchMask
bool isTesSuccess(TER x) noexcept
constexpr XRPAmount INITIAL_XRP
Configure the native currency.
constexpr std::uint32_t tfUntilFailure
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
constexpr std::uint32_t tfIndependent
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.