1#include <xrpl/tx/transactors/system/Batch.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/ledger/ApplyView.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/protocol/AccountID.h>
9#include <xrpl/protocol/Protocol.h>
10#include <xrpl/protocol/SField.h>
11#include <xrpl/protocol/STLedgerEntry.h>
12#include <xrpl/protocol/STObject.h>
13#include <xrpl/protocol/STTx.h>
14#include <xrpl/protocol/SystemParameters.h>
15#include <xrpl/protocol/TER.h>
16#include <xrpl/protocol/TxFlags.h>
17#include <xrpl/protocol/TxFormats.h>
18#include <xrpl/protocol/XRPAmount.h>
19#include <xrpl/tx/Transactor.h>
20#include <xrpl/tx/applySteps.h>
61 if (baseFee > maxAmount -
view.fees().base)
63 JLOG(
debugLog().error()) <<
"BatchTrace: Base fee overflow detected.";
79 JLOG(
debugLog().error()) <<
"BatchTrace: Raw Transactions array exceeds max entries.";
86 STTx const stx =
STTx{std::move(txn)};
91 JLOG(
debugLog().error()) <<
"BatchTrace: Inner Batch transaction found.";
98 if (txnFees > maxAmount - fee)
101 <<
"BatchTrace: XRPAmount overflow in txnFees calculation.";
118 JLOG(
debugLog().error()) <<
"BatchTrace: Batch Signers array exceeds max entries.";
123 for (
STObject const& signer : signers)
125 if (signer.isFieldPresent(sfTxnSignature))
129 else if (signer.isFieldPresent(sfSigners))
131 signerCount += signer.getFieldArray(sfSigners).size();
137 if (signerCount > 0 &&
view.fees().base > maxAmount / signerCount)
139 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in signerCount calculation.";
147 if (signerFees > maxAmount - txnFees)
149 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in signerFees calculation.";
152 if (txnFees + signerFees > maxAmount - batchBase)
154 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in total fee calculation.";
160 return signerFees + txnFees + batchBase;
208 if (
std::popcount(flags & (tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent)) != 1)
210 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
211 <<
"too many flags.";
216 if (rawTxns.size() <= 1)
218 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
219 <<
"txns array must have at least 2 entries.";
225 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
226 <<
"txns array exceeds 8 entries.";
233 auto checkSignatureFields =
234 [&parentBatchId, &j = ctx.
j](
236 if (sig.isFieldPresent(sfTxnSignature))
238 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
239 <<
"inner txn " << label <<
"cannot include TxnSignature. "
244 if (sig.isFieldPresent(sfSigners))
246 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
247 <<
"inner txn " << label <<
" cannot include Signers. "
252 if (!sig.getFieldVL(sfSigningPubKey).empty())
254 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
255 <<
"inner txn " << label <<
" SigningPubKey must be empty. "
264 STTx const stx =
STTx{std::move(rb)};
266 if (!uniqueHashes.
emplace(hash).second)
268 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
269 <<
"duplicate Txn found. "
274 auto const txType = stx.
getFieldU16(sfTransactionType);
275 if (txType == ttBATCH)
277 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
278 <<
"batch cannot have an inner batch txn. "
284 kDisabledTxTypes, [txType](
auto const& disabled) {
return txType == disabled; }))
291 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
292 <<
"inner txn must have the tfInnerBatchTxn flag. "
297 if (
auto const ret = checkSignatureFields(stx, hash))
304 auto const counterpartySignature = stx.
getFieldObject(sfCounterpartySignature);
306 checkSignatureFields(counterpartySignature, hash,
"counterparty signature "))
315 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
316 <<
"inner txn must have a fee of 0. "
322 if (
auto const preflightResult =
326 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
327 <<
"inner txn preflight failed: " <<
transHuman(preflightResult.ter)
336 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
337 <<
"inner txn must have exactly one of Sequence and "
346 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
347 <<
"inner txn must have either Sequence or "
354 if ((flags & (tfAllOrNothing | tfUntilFailure)) != 0u)
356 if (
auto const seq = stx.
getFieldU32(sfSequence); seq != 0)
358 if (!accountSeqTicket[innerAccount].insert(seq).second)
360 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
361 <<
"duplicate sequence found: "
369 if (
auto const ticket = stx.
getFieldU32(sfTicketSequence);
370 !accountSeqTicket[innerAccount].
insert(ticket).second)
372 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
373 <<
"duplicate ticket found: "
395 auto const innerAccount = rb.getAccountID(sfAccount);
399 if (innerAccount != outerAccount)
400 requiredSigners.
insert(innerAccount);
403 if (
auto const counterparty = rb.at(~sfCounterparty);
404 counterparty && counterparty != outerAccount)
405 requiredSigners.
insert(*counterparty);
417 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
418 <<
"signers array exceeds 8 entries.";
427 for (
auto const& signer : signers)
429 AccountID const signerAccount = signer.getAccountID(sfAccount);
430 if (signerAccount == outerAccount)
432 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
433 <<
"signer cannot be the outer account: " << signerAccount;
437 if (!batchSigners.
insert(signerAccount).second)
439 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
440 <<
"duplicate signer found: " << signerAccount;
446 if (requiredSigners.
erase(signerAccount) == 0)
448 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
449 <<
"no account signature for inner txn.";
459 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
460 <<
"invalid batch txn signature: " << sigResult.error();
465 if (!requiredSigners.
empty())
467 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
468 <<
"invalid batch signers.";
A generic endpoint for log messages.
UInt size() const
Number of values in array or object.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
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.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static constexpr auto kDisabledTxTypes
static NotTEC preflightSigValidated(PreflightContext const &ctx)
bool native() const noexcept
std::shared_ptr< STLedgerEntry const > const & const_ref
std::uint32_t getFieldU32(SField const &field) const
STArray const & getFieldArray(SField const &field) const
bool isFlag(std::uint32_t) 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
std::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.
constexpr FlagValue tfInnerBatchTxn
PreflightResult preflight(ServiceRegistry ®istry, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
beast::Journal debugLog()
Returns a debug journal.
constexpr std::size_t kMaxBatchTxCount
The maximum number of transactions that can be in a batch.
std::string transHuman(TER code)
TERSubset< CanCvtToNotTEC > NotTEC
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
constexpr XRPAmount kInitialXrp
Configure the native currency.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.
std::reference_wrapper< ServiceRegistry > registry