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/beast/utility/instrumentation.h>
7#include <xrpl/core/ServiceRegistry.h>
8#include <xrpl/ledger/ApplyView.h>
9#include <xrpl/ledger/ReadView.h>
10#include <xrpl/ledger/helpers/SponsorHelpers.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/Feature.h>
13#include <xrpl/protocol/Protocol.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/STAccount.h>
16#include <xrpl/protocol/STLedgerEntry.h>
17#include <xrpl/protocol/STObject.h>
18#include <xrpl/protocol/STTx.h>
19#include <xrpl/protocol/TER.h>
20#include <xrpl/protocol/TxFlags.h>
21#include <xrpl/protocol/TxFormats.h>
22#include <xrpl/protocol/XRPAmount.h>
23#include <xrpl/tx/Transactor.h>
24#include <xrpl/tx/applySteps.h>
53std::optional<XRPAmount>
62 if (baseFee > maxAmount -
view.fees().base)
64 JLOG(
debugLog().error()) <<
"BatchTrace: Base fee overflow detected.";
80 <<
"BatchTrace: base fee of inner transaction " << stx->getTransactionID()
81 <<
" could not be computed: " <<
transToken(fee.error());
86 if (txnFees > maxAmount - *fee)
88 UNREACHABLE(
"XRPAmount overflow in txnFees calculation");
89 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in txnFees calculation.";
105 JLOG(
debugLog().error()) <<
"BatchTrace: Batch Signers array exceeds max entries.";
110 for (
STObject const& signer : signers)
112 if (signer.isFieldPresent(sfTxnSignature))
116 else if (signer.isFieldPresent(sfSigners))
118 auto const& nestedSigners = signer.getFieldArray(sfSigners);
123 <<
"BatchTrace: Nested Signers array exceeds max entries.";
127 signerCount += nestedSigners.size();
133 if (signerCount > 0 &&
view.fees().base > maxAmount / signerCount)
135 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in signerCount calculation.";
143 if (signerFees > maxAmount - txnFees)
145 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in signerFees calculation.";
148 XRPAmount const innerFees = txnFees + signerFees;
149 if (innerFees > maxAmount - batchBase)
151 JLOG(
debugLog().error()) <<
"BatchTrace: XRPAmount overflow in total fee calculation.";
157 return innerFees + batchBase;
167 return view.fees().base;
223 if (
std::popcount(flags & (tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent)) != 1)
225 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
226 <<
"too many flags.";
234 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
235 <<
"spfSponsorReserve is not allowed on outer Batch.";
241 if (rawTxns.size() <= 1)
243 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
244 <<
"txns array must have at least 2 entries.";
251 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]:"
259 auto checkSignatureFields =
260 [&parentBatchId, &j = ctx.
j](
262 if (sig.isFieldPresent(sfTxnSignature))
264 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
265 <<
"inner txn " << label <<
"cannot include TxnSignature. "
270 if (sig.isFieldPresent(sfSigners))
272 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
273 <<
"inner txn " << label <<
" cannot include Signers. "
278 if (!sig.getFieldVL(sfSigningPubKey).empty())
280 JLOG(j.debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
281 <<
"inner txn " << label <<
" SigningPubKey must be empty. "
290 STTx const& stx = *stxPtr;
292 if (!uniqueHashes.
emplace(hash).second)
294 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
295 <<
"duplicate Txn found. "
300 auto const txType = stx.
getFieldU16(sfTransactionType);
303 bool const rejectedPreV12 = !ctx.
rules.
enabled(featureLendingProtocolV1_2) &&
305 kDisabledTxTypes, [txType](
auto const& disabled) {
return txType == disabled; });
311 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
312 <<
"inner txn must have the tfInnerBatchTxn flag. "
317 if (
auto const ret = checkSignatureFields(stx, hash))
324 auto const counterpartySignature = stx.
getFieldObject(sfCounterpartySignature);
326 checkSignatureFields(counterpartySignature, hash,
"counterparty signature "))
333 auto const sponsorSignature = stx.
getFieldObject(sfSponsorSignature);
334 if (
auto const ret = checkSignatureFields(sponsorSignature, hash,
"sponsor signature "))
343 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
344 <<
"inner txn must have a fee of 0. "
366 if (
auto const preflightResult =
370 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
371 <<
"inner txn preflight failed: " <<
transHuman(preflightResult.ter)
380 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
381 <<
"inner txn must have exactly one of Sequence and "
390 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
391 <<
"inner txn must have either Sequence or "
398 if ((flags & (tfAllOrNothing | tfUntilFailure)) != 0u)
400 if (
auto const seq = stx.
getFieldU32(sfSequence); seq != 0)
402 if (!accountSeqTicket[innerAccount].insert(seq).second)
404 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
405 <<
"duplicate sequence found: "
413 if (
auto const ticket = stx.
getFieldU32(sfTicketSequence);
414 !accountSeqTicket[innerAccount].
insert(ticket).second)
416 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
417 <<
"duplicate ticket found: "
432 ctx.
tx.
getTxnType() == ttBATCH,
"xrpl::Batch::preflightSigValidated : batch transaction");
449 STTx const& rb = *stxPtr;
456 if (authorizer != outerAccount)
460 if (
auto const counterparty = rb[~sfCounterparty];
461 counterparty && counterparty != outerAccount)
462 requiredSigners.
push_back(*counterparty);
464 if (
auto const sponsor = rb.
at(~sfSponsor);
465 sponsor && rb.
isFieldPresent(sfSponsorSignature) && sponsor != outerAccount)
470 requiredSigners.
erase(dupes.begin(), dupes.end());
483 for (
auto const& signer : signers)
485 AccountID const signerAccount = signer.getAccountID(sfAccount);
486 if (signerAccount == outerAccount)
488 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
489 <<
"signer cannot be the outer account: " << signerAccount;
493 if (lastBatchSigner == signerAccount)
495 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
496 <<
"duplicate signer found: " << signerAccount;
500 if (lastBatchSigner > signerAccount)
502 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
503 <<
"unsorted signers array: " << signerAccount;
506 lastBatchSigner = signerAccount;
508 if (numReqSignersMatched >= requiredSigners.
size() ||
509 requiredSigners[numReqSignersMatched] != signerAccount)
511 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
512 <<
"missing signer or extra signer provided: " << signerAccount;
515 ++numReqSignersMatched;
521 if (numReqSignersMatched != requiredSigners.
size())
523 JLOG(ctx.
j.
debug()) <<
"BatchTrace[" << parentBatchId <<
"]: "
524 <<
"invalid batch signers.";
535 for (
auto const& signer : signers)
541 auto const idAccount = signer.getAccountID(sfAccount);
A generic endpoint for log messages.
UInt size() const
Number of values in array or object.
static TER preclaim(PreclaimContext const &ctx)
static std::optional< XRPAmount > calculateBaseFeeImpl(ReadView const &view, STTx const &tx)
Calculates the total base fee for a batch transaction.
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
static NotTEC preflight(PreflightContext const &ctx)
Performs preflight validation checks for a Batch transaction.
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
static NotTEC checkBatchSign(PreclaimContext const &ctx)
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
Inner transaction types that preflight rejects before LendingProtocolV1_2.
static NotTEC preflightSigValidated(PreflightContext const &ctx)
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
bool native() const noexcept
std::shared_ptr< STLedgerEntry const > const & ConstRef
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
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
static constexpr std::size_t kMaxMultiSigners
TxType getTxnType() const
UInt256 getTransactionID() const
AccountID getInitiator() const
The account responsible for the authorization: the delegate when sfDelegate is present,...
std::vector< std::shared_ptr< STTx const > > const & getBatchTransactions() const
The inner transactions of a Batch, built and validated at construction.
static NotTEC checkSign(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
constexpr std::size_t kMaxBatchSigners
The maximum number of batch signers.
PreflightResult preflight(ServiceRegistry ®istry, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
std::expected< XRPAmount, TER > calculateBaseFee(ReadView const &view, STTx const &tx)
Compute only the expected base fee for a transaction.
beast::Journal debugLog()
Returns a debug journal.
std::string transHuman(TER code)
std::string transToken(TER code)
bool isReserveSponsored(STTx const &tx)
Whether the transaction's reserve is sponsored (sfSponsor present + spfSponsorReserve set).
TERSubset< CanCvtToNotTEC > NotTEC
bool isFeeSponsored(STTx const &tx)
Whether the transaction's fee is sponsored (sfSponsor present + spfSponsorFee set).
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
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::reference_wrapper< ServiceRegistry > registry