Compare commits

...

15 Commits

Author SHA1 Message Date
Mayukha Vadari
6a7d98621d refactor: Wire PreflightHelpers into 5 more transactors
Straight De Morgan swaps at 7 preflight/preclaim sites that were
manually duplicating logic covered by the helpers introduced earlier
in this PR:

- OfferCreate: isZeroId(*domainID); isPositiveAmount(saTakerPays/Gets)
- AMMClawback / AMMWithdraw / AMMDeposit: isPositiveAmount on optional
  LP/claw amounts
- Payment (preclaim): checkSize on outer path set and each inner path
- XChainCreateBridge / BridgeModify: isPositiveXRPAmount on
  MinAccountCreateAmount

Each swap is semantics-preserving. The reward/fee amounts in
XChainBridge and SponsorshipSet accept zero or negative values and are
therefore intentionally left as-is.
2026-09-11 13:11:46 -04:00
Mayukha Vadari
4203d2f522 docs: Trim PreflightHelpers pointer to just the reference 2026-09-11 12:27:19 -04:00
Mayukha Vadari
1745983b63 docs: Point transactor authors at PreflightHelpers 2026-09-11 12:22:07 -04:00
Mayukha Vadari
0442f89919 Merge remote-tracking branch 'upstream/develop' into mvadari/check-bounds 2026-09-11 10:35:30 -04:00
Mayukha Vadari
c590f1cd0e Merge remote-tracking branch 'upstream/develop' into mvadari/check-bounds 2026-09-04 16:07:08 -04:00
Mayukha Vadari
28a4d394be Merge branch 'develop' into mvadari/check-bounds 2026-09-01 17:58:58 -04:00
Mayukha Vadari
315775dc21 Revert isPositive -> isNonPositive rename
isNonPositiveXRPAmount(amount) = !isXRP(amount) || amount <= 0, which
reads misleadingly (returns true for non-XRP amounts entirely, not
just non-positive XRP ones). Restore isPositiveXRPAmount/
isPositiveAmount and the negation at call sites.
2026-09-01 17:56:58 -04:00
Mayukha Vadari
6763158124 refactor: Rename checkSizeNonEmpty to checkSizeAndNonEmpty for clarity 2026-09-01 17:33:00 -04:00
Mayukha Vadari
2561b1b565 refactor: Wire checkBounds/checkSize/checkSizeNonEmpty into existing preflight checks
Restore these three helpers (dropped in the previous commit as
apparently unused) and use them at the 10 existing preflight call
sites that were manually duplicating the exact same size/bounds
logic: NFTokenCancelOffer, CredentialCreate/Accept/Delete, OracleSet,
DelegateSet, Batch, AMMBid, TicketCreate, and SignerListSet.

Each swap is a pure De Morgan's-law rewrite of the existing condition,
so preflight's accept/reject decision is unchanged. checkMax remains
removed - no call site anywhere matches its shape.
2026-09-01 17:20:52 -04:00
Mayukha Vadari
d9ad0a312c refactor: Address review feedback on PreflightHelpers
- Remove checkBounds/checkMax/checkSize/checkSizeNonEmpty (unused)
- Rename isPositive{,XRP}Amount to isNonPositive{,XRP}Amount and drop
  the negation at every call site, since all call sites negated them
- Move the header to include/xrpl/tx/helpers/ and wrap it in
  namespace xrpl, matching the ledger/helpers convention
2026-09-01 15:46:56 -04:00
Mayukha Vadari
b1eda783d0 Merge branch 'develop' into mvadari/check-bounds 2026-07-27 11:14:52 -04:00
Mayukha Vadari
99f7b65da7 Merge branch 'develop' into mvadari/check-bounds 2026-07-15 23:02:21 -04:00
Mayukha Vadari
efd6275569 rename functions 2026-07-15 23:01:36 -04:00
Mayukha Vadari
c64aaccf7b fix clang-tidy 2026-07-15 16:31:23 -04:00
Mayukha Vadari
ce57c882ce refactor: Introduce preflight helper functions 2026-07-15 12:34:00 -04:00
38 changed files with 165 additions and 69 deletions

View File

@@ -0,0 +1,66 @@
#pragma once
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/UintTypes.h>
#include <cstddef>
namespace xrpl {
// Checks whether value is within [min, max], inclusive.
template <class T>
inline bool
checkBounds(T const& value, T const& min, T const& max)
{
return value >= min && value <= max;
}
// Checks whether a container's size is at most max.
template <class T>
inline bool
checkSize(T const& value, std::size_t const max)
{
return value.size() <= max;
}
// Checks whether a container is non-empty and its size is at most max.
template <class T>
inline bool
checkSizeAndNonEmpty(T const& value, std::size_t const max)
{
return !value.empty() && value.size() <= max;
}
// Checks whether a hash-like identifier field (e.g. a uint256 object ID) is
// unset/zero.
template <class T>
inline bool
isZeroId(T const& id)
{
return id == beast::kZero;
}
// Checks whether an amount is a strictly positive XRP amount.
inline bool
isPositiveXRPAmount(STAmount const& amount)
{
return isXRP(amount) && amount > beast::kZero;
}
// Checks whether an amount (of any asset type) is strictly positive.
inline bool
isPositiveAmount(STAmount const& amount)
{
return amount > beast::kZero;
}
// Checks whether a currency code is the reserved "bad"/XRP currency code,
// i.e. not a valid IOU currency.
inline bool
isBadCurrency(Currency const& currency)
{
return badCurrency() == currency;
}
} // namespace xrpl

View File

@@ -3,3 +3,7 @@
See the repo-level [AGENTS.md](../../../AGENTS.md) for general build/test/style guidance.
Any change to transaction-processing behavior must be gated behind an amendment. New amendments (and fixes, i.e. `fix*` amendments) are added to [`include/xrpl/protocol/detail/features.macro`](../../../include/xrpl/protocol/detail/features.macro), as an `XRPL_FEATURE(...)` or `XRPL_FIX(...)` entry added to the top of the list (the list is kept in reverse chronological order). Once the pre-amendment code path for a retired amendment is removed, move its entry to `XRPL_RETIRE_FEATURE(...)`/`XRPL_RETIRE_FIX(...)` instead of deleting it.
## Preflight helpers
When writing transactor preflight logic, prefer the helpers in [`include/xrpl/tx/helpers/PreflightHelpers.h`](../../../include/xrpl/tx/helpers/PreflightHelpers.h) over ad-hoc checks. If the same check pattern shows up in more than one transactor, add a new helper there rather than inlining it.

View File

@@ -24,6 +24,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/SignerEntries.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
#include <cstddef>
@@ -247,7 +248,7 @@ SignerListSet::validateQuorumAndSignerEntries(
// Reject if there are too many or too few entries in the list.
{
std::size_t const signerCount = signers.size();
if (signerCount < STTx::kMinMultiSigners || signerCount > STTx::kMaxMultiSigners)
if (!checkBounds(signerCount, STTx::kMinMultiSigners, STTx::kMaxMultiSigners))
{
JLOG(j.trace()) << "Too many or too few signers in signer list.";
return temMALFORMED;

View File

@@ -35,6 +35,7 @@
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/SignerEntries.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <xrpl/tx/paths/Flow.h>
#include <xrpl/tx/paths/detail/Steps.h>
@@ -1369,8 +1370,8 @@ XChainCreateBridge::preflight(PreflightContext const& ctx)
}
if (minAccountCreate &&
((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
!isXRP(bridgeSpec.lockingChainIssue()) || !isXRP(bridgeSpec.issuingChainIssue())))
(!isPositiveXRPAmount(*minAccountCreate) || !isXRP(bridgeSpec.lockingChainIssue()) ||
!isXRP(bridgeSpec.issuingChainIssue())))
{
return temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT;
}
@@ -1540,8 +1541,8 @@ BridgeModify::preflight(PreflightContext const& ctx)
}
if (minAccountCreate &&
((!isXRP(*minAccountCreate) || minAccountCreate->signum() <= 0) ||
!isXRP(bridgeSpec.lockingChainIssue()) || !isXRP(bridgeSpec.issuingChainIssue())))
(!isPositiveXRPAmount(*minAccountCreate) || !isXRP(bridgeSpec.lockingChainIssue()) ||
!isXRP(bridgeSpec.issuingChainIssue())))
{
return temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT;
}

View File

@@ -18,6 +18,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
namespace xrpl {
@@ -41,7 +42,7 @@ CredentialAccept::preflight(PreflightContext const& ctx)
}
auto const credType = ctx.tx[sfCredentialType];
if (credType.empty() || (credType.size() > kMaxCredentialTypeLength))
if (!checkSizeAndNonEmpty(credType, kMaxCredentialTypeLength))
{
JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType.";
return temMALFORMED;

View File

@@ -20,6 +20,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <chrono>
#include <cstdint>
@@ -62,14 +63,14 @@ CredentialCreate::preflight(PreflightContext const& ctx)
}
auto const uri = tx[~sfURI];
if (uri && (uri->empty() || (uri->size() > kMaxCredentialUriLength)))
if (uri && !checkSizeAndNonEmpty(*uri, kMaxCredentialUriLength))
{
JLOG(j.trace()) << "Malformed transaction: invalid size of URI.";
return temMALFORMED;
}
auto const credType = tx[sfCredentialType];
if (credType.empty() || (credType.size() > kMaxCredentialTypeLength))
if (!checkSizeAndNonEmpty(credType, kMaxCredentialTypeLength))
{
JLOG(j.trace()) << "Malformed transaction: invalid size of CredentialType.";
return temMALFORMED;

View File

@@ -14,6 +14,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
namespace xrpl {
@@ -50,7 +51,7 @@ CredentialDelete::preflight(PreflightContext const& ctx)
}
auto const credType = ctx.tx[sfCredentialType];
if (credType.empty() || (credType.size() > kMaxCredentialTypeLength))
if (!checkSizeAndNonEmpty(credType, kMaxCredentialTypeLength))
{
JLOG(ctx.j.trace()) << "Malformed transaction: invalid size of CredentialType.";
return temMALFORMED;

View File

@@ -14,6 +14,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
#include <memory>
@@ -25,7 +26,7 @@ NotTEC
DelegateSet::preflight(PreflightContext const& ctx)
{
auto const& permissions = ctx.tx.getFieldArray(sfPermissions);
if (permissions.size() > kPermissionMaxSize)
if (!checkSize(permissions, kPermissionMaxSize))
return temARRAY_TOO_LARGE;
// can not authorize self

View File

@@ -22,6 +22,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
#include <chrono>
@@ -76,7 +77,7 @@ AMMBid::preflight(PreflightContext const& ctx)
if (ctx.tx.isFieldPresent(sfAuthAccounts))
{
auto const authAccounts = ctx.tx.getFieldArray(sfAuthAccounts);
if (authAccounts.size() > kAuctionSlotMaxAuthAccounts)
if (!checkSize(authAccounts, kAuctionSlotMaxAuthAccounts))
{
JLOG(ctx.j.debug()) << "AMM Bid: Invalid number of AuthAccounts.";
return temMALFORMED;

View File

@@ -24,6 +24,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <xrpl/tx/transactors/dex/AMMWithdraw.h>
#include <cstdint>
@@ -91,7 +92,7 @@ AMMClawback::preflight(PreflightContext const& ctx)
return temBAD_AMOUNT;
}
if (clawAmount && *clawAmount <= beast::kZero)
if (clawAmount && !isPositiveAmount(*clawAmount))
return temBAD_AMOUNT;
return tesSUCCESS;

View File

@@ -23,6 +23,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <bit>
#include <cstdint>
@@ -123,7 +124,7 @@ AMMDeposit::preflight(PreflightContext const& ctx)
return temBAD_AMM_TOKENS;
}
if (lpTokens && *lpTokens <= beast::kZero)
if (lpTokens && !isPositiveAmount(*lpTokens))
{
JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens";
return temBAD_AMM_TOKENS;

View File

@@ -29,6 +29,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
#include <bit>
@@ -129,7 +130,7 @@ AMMWithdraw::preflight(PreflightContext const& ctx)
return temBAD_AMM_TOKENS;
}
if (lpTokens && *lpTokens <= beast::kZero)
if (lpTokens && !isPositiveAmount(*lpTokens))
{
JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
return temBAD_AMM_TOKENS;

View File

@@ -42,6 +42,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <xrpl/tx/paths/Flow.h>
#include <xrpl/tx/paths/detail/Steps.h>
@@ -100,7 +101,7 @@ OfferCreate::preflight(PreflightContext const& ctx)
// A zero DomainID is invalid for a PermissionedDomain ledger entry because
// keylet::permissionedDomain(uint256) uses the DomainID as the ledger key.
if (auto const domainID = tx[~sfDomainID];
ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero)
ctx.rules.enabled(fixCleanup3_2_0) && domainID && isZeroId(*domainID))
return temMALFORMED;
bool const bImmediateOrCancel(tx.isFlag(tfImmediateOrCancel));
@@ -137,7 +138,7 @@ OfferCreate::preflight(PreflightContext const& ctx)
JLOG(j.debug()) << "Malformed offer: redundant (XRP for XRP)";
return temBAD_OFFER;
}
if (saTakerPays <= beast::kZero || saTakerGets <= beast::kZero)
if (!isPositiveAmount(saTakerPays) || !isPositiveAmount(saTakerGets))
{
JLOG(j.debug()) << "Malformed offer: bad amount";
return temBAD_OFFER;

View File

@@ -28,10 +28,10 @@
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <memory>
#include <system_error>
@@ -106,10 +106,10 @@ NotTEC
escrowCreatePreflightHelper<Issue>(PreflightContext const& ctx)
{
STAmount const amount = ctx.tx[sfAmount];
if (amount.native() || amount <= beast::kZero)
if (amount.native() || !isPositiveAmount(amount))
return temBAD_AMOUNT;
if (badCurrency() == amount.get<Issue>().currency)
if (isBadCurrency(amount.get<Issue>().currency))
return temBAD_CURRENCY;
return tesSUCCESS;
@@ -123,7 +123,7 @@ escrowCreatePreflightHelper<MPTIssue>(PreflightContext const& ctx)
return temDISABLED;
auto const amount = ctx.tx[sfAmount];
if (amount.native() || amount.mpt() > MPTAmount{kMaxMpTokenAmount} || amount <= beast::kZero)
if (amount.native() || amount.mpt() > MPTAmount{kMaxMpTokenAmount} || !isPositiveAmount(amount))
return temBAD_AMOUNT;
return tesSUCCESS;
@@ -146,7 +146,7 @@ EscrowCreate::preflight(PreflightContext const& ctx)
}
else
{
if (amount <= beast::kZero)
if (!isPositiveXRPAmount(amount))
return temBAD_AMOUNT;
}

View File

@@ -26,6 +26,7 @@
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <expected>
#include <optional>
@@ -48,7 +49,7 @@ LoanBrokerCoverClawback::preflight(PreflightContext const& ctx)
if (!brokerID && !amount)
return temINVALID;
if (brokerID && *brokerID == beast::kZero)
if (brokerID && isZeroId(*brokerID))
return temINVALID;
if (amount)

View File

@@ -15,6 +15,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
@@ -27,11 +28,11 @@ LoanBrokerCoverDeposit::checkExtraFeatures(PreflightContext const& ctx)
NotTEC
LoanBrokerCoverDeposit::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanBrokerID] == beast::kZero)
if (isZeroId(ctx.tx[sfLoanBrokerID]))
return temINVALID;
auto const dstAmount = ctx.tx[sfAmount];
if (dstAmount <= beast::kZero)
if (!isPositiveAmount(dstAmount))
return temBAD_AMOUNT;
if (!isLegalNet(dstAmount))

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
@@ -20,6 +19,7 @@
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
@@ -36,11 +36,11 @@ LoanBrokerCoverWithdraw::checkExtraFeatures(PreflightContext const& ctx)
NotTEC
LoanBrokerCoverWithdraw::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanBrokerID] == beast::kZero)
if (isZeroId(ctx.tx[sfLoanBrokerID]))
return temINVALID;
auto const dstAmount = ctx.tx[sfAmount];
if (dstAmount <= beast::kZero)
if (!isPositiveAmount(dstAmount))
return temBAD_AMOUNT;
if (!isLegalNet(dstAmount))
@@ -48,7 +48,7 @@ LoanBrokerCoverWithdraw::preflight(PreflightContext const& ctx)
if (auto const destination = ctx.tx[~sfDestination])
{
if (*destination == beast::kZero)
if (isZeroId(*destination))
{
return temMALFORMED;
}

View File

@@ -16,6 +16,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
@@ -28,7 +29,7 @@ LoanBrokerDelete::checkExtraFeatures(PreflightContext const& ctx)
NotTEC
LoanBrokerDelete::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanBrokerID] == beast::kZero)
if (isZeroId(ctx.tx[sfLoanBrokerID]))
return temINVALID;
return tesSUCCESS;

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
@@ -21,6 +20,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <memory>
#include <vector>
@@ -59,13 +59,13 @@ LoanBrokerSet::preflight(PreflightContext const& ctx)
tx.isFieldPresent(sfCoverRateLiquidation))
return temINVALID;
if (tx[sfLoanBrokerID] == beast::kZero)
if (isZeroId(tx[sfLoanBrokerID]))
return temINVALID;
}
if (auto const vaultID = tx.at(~sfVaultID))
{
if (*vaultID == beast::kZero)
if (isZeroId(*vaultID))
return temINVALID;
}

View File

@@ -15,6 +15,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
@@ -27,7 +28,7 @@ LoanDelete::checkExtraFeatures(PreflightContext const& ctx)
NotTEC
LoanDelete::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanID] == beast::kZero)
if (isZeroId(ctx.tx[sfLoanID]))
return temINVALID;
return tesSUCCESS;

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
@@ -23,6 +22,7 @@
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
#include <cstdint>
@@ -43,7 +43,7 @@ LoanManage::getFlagsMask(PreflightContext const& ctx)
NotTEC
LoanManage::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanID] == beast::kZero)
if (isZeroId(ctx.tx[sfLoanID]))
return temINVALID;
// Flags are mutually exclusive

View File

@@ -25,6 +25,7 @@
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <xrpl/tx/transactors/lending/LoanManage.h>
#include <algorithm>
@@ -78,10 +79,10 @@ LoanPay::getFlagsMask(PreflightContext const& ctx)
NotTEC
LoanPay::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanID] == beast::kZero)
if (isZeroId(ctx.tx[sfLoanID]))
return temINVALID;
if (ctx.tx[sfAmount] <= beast::kZero)
if (!isPositiveAmount(ctx.tx[sfAmount]))
return temBAD_AMOUNT;
// The loan payment flags are all mutually exclusive. If more than one is

View File

@@ -29,6 +29,7 @@
#include <xrpl/protocol/Units.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstddef>
#include <cstdint>
@@ -146,7 +147,7 @@ LoanSet::preflight(PreflightContext const& ctx)
return *ret;
}
if (auto const brokerID = ctx.tx[~sfLoanBrokerID]; brokerID && *brokerID == beast::kZero)
if (auto const brokerID = ctx.tx[~sfLoanBrokerID]; brokerID && isZeroId(*brokerID))
return temINVALID;
return tesSUCCESS;

View File

@@ -15,6 +15,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
namespace xrpl {
@@ -24,7 +25,7 @@ NFTokenCancelOffer::preflight(PreflightContext const& ctx)
{
auto const& offerIds = ctx.tx[sfNFTokenOffers];
if (offerIds.empty() || (offerIds.size() > kMaxTokenOfferCancelCount))
if (!checkSizeAndNonEmpty(offerIds, kMaxTokenOfferCancelCount))
return temMALFORMED;
// Zero offer IDs cannot be passed as ledger entry keys.

View File

@@ -20,6 +20,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <chrono>
#include <cstddef>
@@ -46,7 +47,7 @@ OracleSet::preflight(PreflightContext const& ctx)
auto const& dataSeries = ctx.tx.getFieldArray(sfPriceDataSeries);
if (dataSeries.empty())
return temARRAY_EMPTY;
if (dataSeries.size() > kMaxOracleDataSeries)
if (!checkSize(dataSeries, kMaxOracleDataSeries))
return temARRAY_TOO_LARGE;
auto isInvalidLength = [&](auto const& sField, std::size_t length) {

View File

@@ -34,6 +34,7 @@
#include <xrpl/protocol/jss.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <xrpl/tx/paths/RippleCalc.h>
#include <algorithm>
@@ -143,7 +144,7 @@ Payment::preflight(PreflightContext const& ctx)
// A zero DomainID is invalid for a PermissionedDomain ledger entry because
// keylet::permissionedDomain(uint256) uses the DomainID as the ledger key.
if (auto const domainID = tx[~sfDomainID];
ctx.rules.enabled(fixCleanup3_2_0) && domainID && *domainID == beast::kZero)
ctx.rules.enabled(fixCleanup3_2_0) && domainID && isZeroId(*domainID))
return temMALFORMED;
bool const partialPaymentAllowed = tx.isFlag(tfPartialPayment);
@@ -183,13 +184,13 @@ Payment::preflight(PreflightContext const& ctx)
<< "Payment destination account not specified.";
return temDST_NEEDED;
}
if (hasMax && maxSourceAmount <= beast::kZero)
if (hasMax && !isPositiveAmount(maxSourceAmount))
{
JLOG(j.trace()) << "Malformed transaction: bad max amount: "
<< maxSourceAmount.getFullText();
return temBAD_AMOUNT;
}
if (dstAmount <= beast::kZero)
if (!isPositiveAmount(dstAmount))
{
JLOG(j.trace()) << "Malformed transaction: bad dst amount: " << dstAmount.getFullText();
return temBAD_AMOUNT;
@@ -439,8 +440,8 @@ Payment::preclaim(PreclaimContext const& ctx)
{
STPathSet const& paths = ctx.tx.getFieldPathSet(sfPaths);
if (paths.size() > kMaxPathSize || std::ranges::any_of(paths, [](STPath const& path) {
return path.size() > kMaxPathLength;
if (!checkSize(paths, kMaxPathSize) || std::ranges::any_of(paths, [](STPath const& path) {
return !checkSize(path, kMaxPathLength);
}))
{
// Open view: the soft tel (unchanged). Inner batch txns are claimed

View File

@@ -21,6 +21,7 @@
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
#include <optional>
@@ -42,15 +43,15 @@ PaymentChannelClaim::getFlagsMask(PreflightContext const&)
NotTEC
PaymentChannelClaim::preflight(PreflightContext const& ctx)
{
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfChannel] == beast::kZero)
if (ctx.rules.enabled(fixCleanup3_2_0) && isZeroId(ctx.tx[sfChannel]))
return temMALFORMED;
auto const bal = ctx.tx[~sfBalance];
if (bal && (!isXRP(*bal) || *bal <= beast::kZero))
if (bal && !isPositiveXRPAmount(*bal))
return temBAD_AMOUNT;
auto const amt = ctx.tx[~sfAmount];
if (amt && (!isXRP(*amt) || *amt <= beast::kZero))
if (amt && !isPositiveXRPAmount(*amt))
return temBAD_AMOUNT;
if (bal && amt && *bal > *amt)

View File

@@ -1,7 +1,6 @@
#include <xrpl/tx/transactors/payment_channel/PaymentChannelCreate.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/View.h>
@@ -21,6 +20,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <memory>
@@ -57,7 +57,7 @@ PaymentChannelCreate::makeTxConsequences(PreflightContext const& ctx)
NotTEC
PaymentChannelCreate::preflight(PreflightContext const& ctx)
{
if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
if (!isPositiveXRPAmount(ctx.tx[sfAmount]))
return temBAD_AMOUNT;
if (ctx.tx[sfAccount] == ctx.tx[sfDestination])

View File

@@ -1,7 +1,6 @@
#include <xrpl/tx/transactors/payment_channel/PaymentChannelFund.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
@@ -19,6 +18,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
@@ -31,10 +31,10 @@ PaymentChannelFund::makeTxConsequences(PreflightContext const& ctx)
NotTEC
PaymentChannelFund::preflight(PreflightContext const& ctx)
{
if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx[sfChannel] == beast::kZero)
if (ctx.rules.enabled(fixCleanup3_2_0) && isZeroId(ctx.tx[sfChannel]))
return temMALFORMED;
if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::kZero))
if (!isPositiveXRPAmount(ctx.tx[sfAmount]))
return temBAD_AMOUNT;
return tesSUCCESS;

View File

@@ -1,7 +1,6 @@
#include <xrpl/tx/transactors/permissioned_domain/PermissionedDomainDelete.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/protocol/Indexes.h>
@@ -11,6 +10,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
@@ -18,7 +18,7 @@ NotTEC
PermissionedDomainDelete::preflight(PreflightContext const& ctx)
{
auto const domain = ctx.tx.getFieldH256(sfDomainID);
if (domain == beast::kZero)
if (isZeroId(domain))
return temMALFORMED;
return tesSUCCESS;

View File

@@ -1,6 +1,5 @@
#include <xrpl/tx/transactors/permissioned_domain/PermissionedDomainSet.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
@@ -17,6 +16,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <memory>
#include <utility>
@@ -40,7 +40,7 @@ PermissionedDomainSet::preflight(PreflightContext const& ctx)
return err;
auto const domain = ctx.tx.at(~sfDomainID);
if (domain && *domain == beast::kZero)
if (domain && isZeroId(*domain))
return temMALFORMED;
return tesSUCCESS;

View File

@@ -21,6 +21,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
#include <bit>
@@ -236,7 +237,7 @@ Batch::preflight(PreflightContext const& ctx)
}
if (ctx.tx.isFieldPresent(sfBatchSigners) &&
ctx.tx.getFieldArray(sfBatchSigners).size() > kMaxBatchSigners)
!checkSize(ctx.tx.getFieldArray(sfBatchSigners), kMaxBatchSigners))
{
JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:"
<< "signers array exceeds " << kMaxBatchSigners << " entries.";

View File

@@ -15,6 +15,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
#include <memory>
@@ -32,7 +33,7 @@ NotTEC
TicketCreate::preflight(PreflightContext const& ctx)
{
if (std::uint32_t const count = ctx.tx[sfTicketCount];
count < kMinValidCount || count > kMaxValidCount)
!checkBounds(count, kMinValidCount, kMaxValidCount))
return temINVALID_COUNT;
return tesSUCCESS;

View File

@@ -21,6 +21,7 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <algorithm>
#include <variant>
@@ -44,7 +45,7 @@ preflightHelper<Issue>(PreflightContext const& ctx)
// The issuer field is used for the token holder instead
AccountID const& holder = clawAmount.getIssuer();
if (issuer == holder || isXRP(clawAmount) || clawAmount <= beast::kZero)
if (issuer == holder || isXRP(clawAmount) || !isPositiveAmount(clawAmount))
return temBAD_AMOUNT;
return tesSUCCESS;
@@ -67,7 +68,7 @@ preflightHelper<MPTIssue>(PreflightContext const& ctx)
if (ctx.tx[sfAccount] == *mptHolder)
return temMALFORMED;
if (clawAmount.mpt() > MPTAmount{kMaxMpTokenAmount} || clawAmount <= beast::kZero)
if (clawAmount.mpt() > MPTAmount{kMaxMpTokenAmount} || !isPositiveAmount(clawAmount))
return temBAD_AMOUNT;
return tesSUCCESS;

View File

@@ -1,7 +1,6 @@
#include <xrpl/tx/transactors/token/MPTokenIssuanceCreate.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
@@ -20,6 +19,7 @@
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
#include <expected>
@@ -93,7 +93,7 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx)
if (auto const domain = ctx.tx[~sfDomainID])
{
if (*domain == beast::kZero)
if (isZeroId(*domain))
return temMALFORMED;
// Domain present implies that MPTokenIssuance is not public

View File

@@ -24,6 +24,7 @@
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <cstdint>
#include <unordered_set>
@@ -98,7 +99,7 @@ TrustSet::preflight(PreflightContext const& ctx)
return temBAD_LIMIT;
}
if (badCurrency() == saLimitAmount.get<Issue>().currency)
if (isBadCurrency(saLimitAmount.get<Issue>().currency))
{
JLOG(j.trace()) << "Malformed transaction: specifies XRP as IOU";
return temBAD_CURRENCY;

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -18,13 +17,14 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
namespace xrpl {
NotTEC
VaultDelete::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfVaultID] == beast::kZero)
if (isZeroId(ctx.tx[sfVaultID]))
{
JLOG(ctx.j.debug()) << "VaultDelete: zero/empty vault ID.";
return temMALFORMED;

View File

@@ -25,6 +25,7 @@
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/helpers/PreflightHelpers.h>
#include <stdexcept>
@@ -52,18 +53,18 @@ shouldWaiveWithdrawal(ReadView const& view, AccountID const& account, SLE::const
NotTEC
VaultWithdraw::preflight(PreflightContext const& ctx)
{
if (ctx.tx[sfVaultID] == beast::kZero)
if (isZeroId(ctx.tx[sfVaultID]))
{
JLOG(ctx.j.debug()) << "VaultWithdraw: zero/empty vault ID.";
return temMALFORMED;
}
if (ctx.tx[sfAmount] <= beast::kZero)
if (!isPositiveAmount(ctx.tx[sfAmount]))
return temBAD_AMOUNT;
if (auto const destination = ctx.tx[~sfDestination])
{
if (*destination == beast::kZero)
if (isZeroId(*destination))
{
return temMALFORMED;
}