Skip to content

refactor SetHook preflight validation#725

Open
tequdev wants to merge 22 commits into
Xahau:devfrom
tequdev:refactor-sethook-preflight
Open

refactor SetHook preflight validation#725
tequdev wants to merge 22 commits into
Xahau:devfrom
tequdev:refactor-sethook-preflight

Conversation

@tequdev

@tequdev tequdev commented Mar 20, 2026

Copy link
Copy Markdown
Member

High Level Overview of Change

Context of Change

Type of Change

  • Refactor (non-breaking change that only restructures code)

RichardAH and others added 6 commits February 24, 2026 16:07
* Add AMM bid/create/deposit/swap/withdraw/vote invariants:
  - Deposit, Withdrawal invariants: `sqrt(asset1Balance * asset2Balance) >= LPTokens`.
  - Bid: `sqrt(asset1Balance * asset2Balance) > LPTokens` and the pool balances don't change.
  - Create: `sqrt(asset1Balance * assetBalance2) == LPTokens`.
  - Swap: `asset1BalanceAfter * asset2BalanceAfter >= asset1BalanceBefore * asset2BalanceBefore`
     and `LPTokens` don't change.
  - Vote: `LPTokens` and pool balances don't change.
  - All AMM and swap transactions: amounts and tokens are greater than zero, except on withdrawal if all tokens
    are withdrawn.
* Add AMM deposit and withdraw rounding to ensure AMM invariant:
  - On deposit, tokens out are rounded downward and deposit amount is rounded upward.
  - On withdrawal, tokens in are rounded upward and withdrawal amount is rounded downward.
* Add Order Book Offer invariant to verify consumed amounts. Consumed amounts are less than the offer.
* Fix Bid validation. `AuthAccount` can't have duplicate accounts or the submitter account.
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance. This was fixed for `AMMWithdraw` in `fixAMMv1_1` by adjusting the LPTokenBalance to be the same as the trustline balance. Since `AMMClawback` is also performing a withdrawal, we need to adjust LPTokenBalance as well in `AMMClawback.`

This change includes:
1. Refactored `verifyAndAdjustLPTokenBalance` function in `AMMUtils`, which both`AMMWithdraw` and `AMMClawback` call to adjust LPTokenBalance.
2. Added the unit test `testLastHolderLPTokenBalance` to test the scenario.
3. Modify the existing unit tests for `fixAMMClawbackRounding`.
@sentinel-ai-reviewer

Copy link
Copy Markdown

Sentinel Security Review

Priority File Issue Category
🟠 HIGH src/xrpld/app/hook/detail/HookAPI.cpp:3219 Missing Unexpected() wrapper: return pe_unknown_type_early (line 3219) returns... input_validation
🟠 HIGH src/xrpld/app/hook/detail/HookAPI.cpp:3324 Out-of-bounds read in STI_PATHSET parsing: after incrementing length by up to ... memory_corruption
🟡 MEDIUM src/xrpld/app/hook/detail/HookAPI.cpp:1567 Integer truncation: uint8_t length = data.size() at line 1567 truncates the ve... input_validation
🟡 MEDIUM src/xrpld/app/tx/detail/SetHook.cpp:63 Orphaned HookOnV2 field bypasses validateHookOn and its featureHookOnV2 amendmen... missing_validation
⚪ LOW src/xrpld/app/tx/detail/SetHook.cpp:284 getFieldVL(sfCreateCode) called at function entry without isFieldPresent guard. ... missing_validation
⚪ LOW src/xrpld/app/tx/detail/SetHook.cpp:365 getFieldU16(sfHookApiVersion) called without isFieldPresent guard inside the fun... missing_validation

Stats: 6 issues · 2 high · 2 medium · 2 low · reviewed 0 files

Available Commands

PR-Level (top-level PR comment)

Command Description
/audit Re-trigger a security audit
/config Show effective Sentinel configuration
/help Show available commands

Finding-Level (reply to a Sentinel inline comment)

Command Description
/fp Mark finding as false positive
/fix Generate a fix for this issue
/explain Explain the finding in detail
/validate Re-validate with additional context
/dismiss Dismiss this finding

Sentinel AI

bool
isHookOnFieldsPresent(STObject const& hookSetObj)
{
return hookSetObj.isFieldPresent(sfHookOn) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM · missing_validation

Orphaned HookOnV2 field bypasses validateHookOn and its featureHookOnV2 amendment gate. If only sfHookOnOutgoing is present (without sfHookOnIncoming), isHookOnFieldsPresent returns false, so validateHookOn is never called from validateHookSetFields. For hsoUPDATE/hsoINSTALL operations, no per-operation HookOn validation exists, so the orphan field reaches doApply unvalidated. Critically, the featureHookOnV2 amendment check inside validateHookOn is also bypassed, allowing sfHookOnOutgoing to be applied to a hook even before the HookOnV2 amendment is enabled.

Suggested fix: isHookOnFieldsPresent should also return true when EITHER sfHookOnOutgoing OR sfHookOnIncoming is present (not requiring both), so that validateHookOn can reject the incomplete pair. Alternatively, add an explicit check in validateHookSetFields: if exactly one of sfHookOnOutgoing/sfHookOnIncoming is present without sfHookOn, return false.

std::variant<bool, std::pair<uint64_t, uint64_t>>
validateWasmCode(SetHookCtx& ctx, STObject const& hookSetObj)
{
Blob hook = hookSetObj.getFieldVL(sfCreateCode);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW · missing_validation

getFieldVL(sfCreateCode) called at function entry without isFieldPresent guard. The function relies entirely on callers to ensure sfCreateCode is present before invocation. Currently the sole call site (hsoCREATE at line 640) does check isFieldPresent, but the function is not self-defensive. If any future caller omits the guard, getFieldVL throws, which would be caught as tefEXCEPTION in the try-catch at line 838-852, returning temMALFORMED. However, this masks the actual error.

Suggested fix: Add an isFieldPresent(sfCreateCode) check at the start of validateWasmCode, or document the precondition with an XRPL_VERIFY.

validateHookAPIVersion(SetHookCtx& ctx, STObject const& hookSetObj)
{
auto const apiVersion = hookSetObj.getFieldU16(sfHookApiVersion);
if (apiVersion != 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW · missing_validation

getFieldU16(sfHookApiVersion) called without isFieldPresent guard inside the function body. The caller validateHookSetFields checks isFieldPresent before calling, but validateHookAPIVersion itself is not self-defensive. If invoked without the guard, getFieldU16 throws on absent field.

Suggested fix: Add isFieldPresent(sfHookApiVersion) check inside validateHookAPIVersion or use getOptionalU16/~sfHookApiVersion pattern.

if (flag & STPathElement::typeIssuer) // issuer
length += 20;

int next_flag = *(upto + length);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH · memory_corruption

Out-of-bounds read in STI_PATHSET parsing: after incrementing length by up to 60 bytes inside the inner while loop (line 3317-3322 for account+currency+issuer), *(upto + length) is read at line 3324 without verifying upto + length < end. If the serialized pathset data is truncated, this reads memory beyond the input buffer boundary.

Suggested fix: Add bounds check if (upto + length >= end) return Unexpected(pe_unexpected_end); before the read at line 3324.

@Xahau Xahau deleted a comment from sentinel-ai-reviewer Bot Mar 20, 2026
@Xahau Xahau deleted a comment from sentinel-ai-reviewer Bot Mar 20, 2026
@codecov

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.3%. Comparing base (c55420b) to head (fe200e1).

Files with missing lines Patch % Lines
src/xrpld/app/tx/detail/SetHook.cpp 90.6% 11 Missing ⚠️
src/xrpld/app/hook/detail/HookAPI.cpp 93.3% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@          Coverage Diff          @@
##             dev    #725   +/-   ##
=====================================
  Coverage   77.3%   77.3%           
=====================================
  Files        837     837           
  Lines      78575   78584    +9     
  Branches   11551   11546    -5     
=====================================
+ Hits       60748   60758   +10     
+ Misses     17817   17816    -1     
  Partials      10      10           
Files with missing lines Coverage Δ
src/xrpld/app/hook/detail/HookAPI.cpp 88.3% <93.3%> (+<0.1%) ⬆️
src/xrpld/app/tx/detail/SetHook.cpp 82.0% <90.6%> (+0.4%) ⬆️

... and 7 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants