mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-03 16:56:48 +00:00
fix: Sponsor's MaxFee cap is bypassed in reset() path, allowing sponsee to drain entire pre-funded FeeAmount in a single tec-failing transaction
This commit is contained in:
@@ -1243,6 +1243,12 @@ Transactor::reset(XRPAmount fee)
|
||||
|
||||
auto const balance = payerSle->getFieldAmount(payer.balanceField).xrp();
|
||||
|
||||
if (payer.type == FeePayerType::SponsorPreFunded && payerSle->isFieldPresent(sfMaxFee))
|
||||
{
|
||||
auto const cap = payerSle->getFieldAmount(sfMaxFee).xrp();
|
||||
fee = std::min(fee, cap);
|
||||
}
|
||||
|
||||
// balance should have already been checked in checkFee / preFlight.
|
||||
XRPL_ASSERT(
|
||||
balance != beast::zero && (!view().open() || balance >= fee),
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/OpenView.h>
|
||||
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Asset.h>
|
||||
@@ -57,6 +58,7 @@
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/tx/apply.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
@@ -1695,6 +1697,42 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFee cap is enforced in reset() for tec-failing transactions.
|
||||
// On a closed ledger view (!view.open()), checkFee returns tecINSUFF_FEE when
|
||||
// fee > MaxFee (not terINSUF_FEE_B), triggering reset()
|
||||
{
|
||||
Env env{*this, testable_amendments()};
|
||||
Account const alice("alice");
|
||||
Account const carol("sponsor");
|
||||
|
||||
env.fund(XRP(10000), alice, carol);
|
||||
env.close();
|
||||
|
||||
// FeeAmount=1000 drops, MaxFee=10 drops
|
||||
env(sponsor::set_fee(carol, 0, drops(1000), drops(10)), sponsor::sponseeAcc(alice));
|
||||
env.close();
|
||||
|
||||
// Apply directly against the closed ledger view (open_ = false) so that
|
||||
// checkFee returns tecINSUFF_FEE and reset() is invoked.
|
||||
OpenView overlay(&*env.closed());
|
||||
|
||||
auto jt = env.jt(
|
||||
noop(alice),
|
||||
fee(drops(1000)),
|
||||
seq(env.seq(alice)),
|
||||
sponsor::as(carol, spfSponsorFee));
|
||||
|
||||
auto const result = xrpl::apply(env.app(), overlay, *jt.stx, tapNONE, env.journal);
|
||||
BEAST_EXPECT(result.ter == tecINSUFF_FEE);
|
||||
BEAST_EXPECT(result.applied);
|
||||
|
||||
// Only MaxFee (10 drops) must be deducted, not the full 1000 drops.
|
||||
auto const sle = overlay.read(keylet::sponsor(carol.id(), alice.id()));
|
||||
BEAST_EXPECT(sle);
|
||||
BEAST_EXPECT(sle->isFieldPresent(sfFeeAmount));
|
||||
BEAST_EXPECT(sle->getFieldAmount(sfFeeAmount) == drops(990)); // 1000 - MaxFee(10)
|
||||
}
|
||||
|
||||
// test lsfSponsorshipRequireSignForFee
|
||||
{
|
||||
Env env{*this, testable_amendments()};
|
||||
|
||||
Reference in New Issue
Block a user