Compare commits

...

4 Commits

Author SHA1 Message Date
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
22 changed files with 145 additions and 47 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>
template <class T>
inline bool
checkBounds(T const& value, T const& min, T const& max)
{
return value >= min && value <= max;
}
template <class T>
inline bool
checkMax(T const& value, T const& max)
{
return value <= max;
}
template <class T>
inline bool
checkSize(T const& value, std::size_t const max)
{
return value.size() <= max;
}
template <class T>
inline bool
checkSizeNonEmpty(T const& value, std::size_t const max)
{
return value.size() <= max && !value.empty();
}
// 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(xrpl::STAmount const& amount)
{
return xrpl::isXRP(amount) && amount > beast::kZero;
}
// Checks whether an amount (of any asset type) is strictly positive.
inline bool
isPositiveAmount(xrpl::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(xrpl::Currency const& currency)
{
return xrpl::badCurrency() == currency;
}

View File

@@ -28,11 +28,12 @@
#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 <libxrpl/tx/PreflightHelpers.h>
#include <memory>
#include <system_error>
#include <variant>
@@ -101,10 +102,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;
@@ -118,7 +119,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;
@@ -141,7 +142,7 @@ EscrowCreate::preflight(PreflightContext const& ctx)
}
else
{
if (amount <= beast::kZero)
if (!isPositiveXRPAmount(amount))
return temBAD_AMOUNT;
}

View File

@@ -27,6 +27,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <expected>
#include <optional>
#include <variant>
@@ -48,7 +50,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

@@ -16,6 +16,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
namespace xrpl {
bool
@@ -27,11 +29,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/LendingHelpers.h>
@@ -20,6 +19,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
namespace xrpl {
bool
@@ -31,11 +32,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))
@@ -43,7 +44,7 @@ LoanBrokerCoverWithdraw::preflight(PreflightContext const& ctx)
if (auto const destination = ctx.tx[~sfDestination])
{
if (*destination == beast::kZero)
if (isZeroId(*destination))
{
return temMALFORMED;
}

View File

@@ -18,6 +18,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
namespace xrpl {
bool
@@ -29,7 +31,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>
@@ -20,6 +19,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <memory>
#include <vector>
@@ -57,13 +58,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

@@ -16,6 +16,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
namespace xrpl {
bool
@@ -27,7 +29,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>
@@ -24,6 +23,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <algorithm>
#include <cstdint>
namespace xrpl {
@@ -43,7 +44,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,8 @@
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/transactors/lending/LoanManage.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <algorithm>
#include <bit>
#include <cstdint>
@@ -48,10 +50,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

@@ -28,6 +28,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <cstddef>
#include <cstdint>
#include <limits>
@@ -138,7 +140,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

@@ -36,6 +36,8 @@
#include <xrpl/tx/applySteps.h>
#include <xrpl/tx/paths/RippleCalc.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <algorithm>
#include <cstdint>
#include <limits>
@@ -143,7 +145,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 +185,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;

View File

@@ -22,6 +22,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <cstdint>
#include <optional>
@@ -42,15 +44,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>
@@ -22,6 +21,8 @@
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <memory>
namespace xrpl {
@@ -57,7 +58,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>
@@ -20,6 +19,8 @@
#include <xrpl/tx/Transactor.h>
#include <xrpl/tx/applySteps.h>
#include <libxrpl/tx/PreflightHelpers.h>
namespace xrpl {
TxConsequences
@@ -31,10 +32,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>
@@ -12,13 +11,15 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
namespace xrpl {
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,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <memory>
#include <utility>
@@ -39,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

@@ -22,6 +22,8 @@
#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <algorithm>
#include <variant>
@@ -44,7 +46,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 +69,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>
@@ -21,6 +20,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <cstdint>
#include <expected>
#include <memory>
@@ -87,7 +88,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

@@ -25,6 +25,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <cstdint>
#include <unordered_set>
@@ -98,7 +100,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>
@@ -19,12 +18,14 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/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

@@ -23,6 +23,8 @@
#include <xrpl/protocol/XRPAmount.h>
#include <xrpl/tx/Transactor.h>
#include <libxrpl/tx/PreflightHelpers.h>
#include <stdexcept>
namespace xrpl {
@@ -42,18 +44,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;
}