refactor: Move reserve checks from preclaim to doApply where relevant#7607
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to standardize reserve enforcement for PaymentChannelCreate and OracleSet by shifting reserve checks away from preclaim() (which uses SLE pre-fee balance reads) into doApply() using preFeeBalance_, aligning these transactors with the dominant reserve-check pattern described in issue #6599.
Changes:
- Gate the existing
preclaim()reserve checks so they only run whenfeatureSponsoris not enabled. - Add
doApply()-time reserve checks (usingpreFeeBalance_) whenfeatureSponsoris enabled forPaymentChannelCreateandOracleSet.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/libxrpl/tx/transactors/payment_channel/PaymentChannelCreate.cpp | Gated preclaim reserve checks and added doApply reserve/funding checks under featureSponsor. |
| src/libxrpl/tx/transactors/oracle/OracleSet.cpp | Gated preclaim reserve checks and added doApply reserve checks under featureSponsor for both create/update paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (auto const ret = checkInsufficientReserve( | ||
| ctx_.view(), ctx_.tx, sle, STAmount{preFeeBalance_}, *sponsorSle, 1, 0, j_); | ||
| !isTesSuccess(ret)) | ||
| return ret; | ||
| if (auto const ret = checkInsufficientReserve( | ||
| ctx_.view(), | ||
| ctx_.tx, | ||
| sle, | ||
| STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()}, | ||
| *sponsorSle, | ||
| 1, | ||
| 0, | ||
| j_); | ||
| !isTesSuccess(ret)) | ||
| return tecUNFUNDED; |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## xrplf/sponsor #7607 +/- ##
===============================================
- Coverage 82.2% 82.1% -0.1%
===============================================
Files 1016 1016
Lines 78375 78209 -166
Branches 8996 9006 +10
===============================================
- Hits 64438 64243 -195
- Misses 13928 13957 +29
Partials 9 9
🚀 New features to boost your workflow:
|
|
This PR has conflicts, please resolve them in order for the PR to be reviewed. |
… mvadari/sponsor/preclaim
|
All conflicts have been resolved. Assigned reviewers can now start or resume their review. |
There was a problem hiding this comment.
Two correctness bugs flagged inline: a compile error (undeclared sponsorSle) in the OracleSet create branch, and a mutation-before-check ordering issue in the update branch. An unsigned underflow risk is also flagged in PaymentChannelCreate.
Review by Claude Sonnet 4.6 · Prompt: V15
| ctx_.view(), | ||
| ctx_.tx, | ||
| sle, | ||
| STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()}, |
There was a problem hiding this comment.
Unsigned underflow: subtraction can wrap if sfAmount > preFeeBalance_. Guard before subtracting:
if (ctx_.tx[sfAmount].xrp() > preFeeBalance_)
return tecUNFUNDED;
if (auto const ret = checkInsufficientReserve(
ctx_.view(),
ctx_.tx,
sle,
STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()},
{},
1,
0,
j_);
There was a problem hiding this comment.
Reserve sponsor SLE is never retrieved in PaymentChannelCreate::doApply — both checkInsufficientReserve calls silently ignore any sponsor, which can cause a valid sponsored transaction to fail with tecINSUFFICIENT_RESERVE. See inline.
Review by Claude Sonnet 4.6 · Prompt: V15
| if (ctx_.view().rules().enabled(featureSponsor)) | ||
| { | ||
| if (auto const ret = checkInsufficientReserve( | ||
| ctx_.view(), ctx_.tx, sle, STAmount{preFeeBalance_}, {}, 1, 0, j_); |
There was a problem hiding this comment.
Missing sponsor SLE — both checkInsufficientReserve calls pass {}, silently dropping any reserve sponsor. Mirror OracleSet::doApply: retrieve the sponsor SLE via getTxReserveSponsor and forward it to both calls.
auto const sponsorSle = getTxReserveSponsor(ctx_.view(), ctx_.tx);
if (!sponsorSle)
return sponsorSle.error();
if (auto const ret = checkInsufficientReserve(
ctx_.view(), ctx_.tx, sle, STAmount{preFeeBalance_}, *sponsorSle, 1, 0, j_);
!isTesSuccess(ret))
return ret;
if (auto const ret = checkInsufficientReserve(
ctx_.view(),
ctx_.tx,
sle,
STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()},
*sponsorSle,
1,
0,
j_);
!isTesSuccess(ret))
return tecUNFUNDED;
| auto const sponsorSle = getTxReserveSponsor(ctx_.view(), ctx_.tx); | ||
| if (!sponsorSle) | ||
| return sponsorSle.error(); | ||
| if (auto const ret = checkInsufficientReserve( |
There was a problem hiding this comment.
The mehod name checkInsufficientReserve is a little bit confusing. The reserve can either be sufficient or insufficient, and what the method is doing is determining which one it is. What do you think about calling it checkAccountReserve? Doesn't have to be part of this PR though.
There was a problem hiding this comment.
I would consider that out of scope of this PR but I'll take note of it
| ctx_.tx, | ||
| sle, | ||
| STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()}, | ||
| {}, |
There was a problem hiding this comment.
Second checkInsufficientReserve passes {} (no sponsor) but the first passes *sponsorSle — inconsistent reserve baseline for sponsored transactions may produce wrong tecUNFUNDED results.
if (auto const ret = checkInsufficientReserve(
ctx_.view(),
ctx_.tx,
sle,
STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()},
*sponsorSle,
1,
0,
j_);
!isTesSuccess(ret))
return tecUNFUNDED;
| auto const count = calculateOracleReserve(series.size()); | ||
| if (ctx_.view().rules().enabled(featureSponsor)) | ||
| { | ||
| if (auto const ret = checkInsufficientReserve( |
There was a problem hiding this comment.
I thought oracleSet is out of scope for reserve sponsor?
… mvadari/sponsor/preclaim
| ctx_.tx, | ||
| sle, | ||
| STAmount{preFeeBalance_ - ctx_.tx[sfAmount].xrp()}, | ||
| {}, |
There was a problem hiding this comment.
Second checkInsufficientReserve passes {} for sponsor, but the first call (line 143) uses *sponsorSle. If intentional, add a comment explaining why the sponsor is excluded from the funding check; if accidental, fix to match the original semantics:
*sponsorSle,
High Level Overview of Change
This PR moves reserve checks in
PaymentChannelCreateandOracleSetfrompreclaimtodoApply, and incorporatespreFeeBalance_into the calculations instead ofsfBalance.Context of Change
Having reserve checks in preclaim is an anti-pattern - see #6599 for a full explanation
API Impact
N/A