Skip to content

Conversation

@ximinez
Copy link
Collaborator

@ximinez ximinez commented Jan 21, 2026

High Level Overview of Change

Introduces a member function to Number for scale computations, so we can be sure it's done the same everywhere.

Context of Change

#6217

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)

@ximinez ximinez requested a review from Tapanito January 21, 2026 01:05
@ximinez ximinez requested a review from a team as a code owner January 21, 2026 01:05
- Requires a template for STAmount and Asset.
- Update tests and computeMinScale from #6217 to use scale.
- Convert a few other places to use "scale" correctly.
@ximinez ximinez force-pushed the ximinez/number-scale branch from 3bff525 to 7ab9709 Compare January 21, 2026 01:06
@ximinez ximinez mentioned this pull request Jan 21, 2026
13 tasks
@Tapanito
Copy link
Collaborator

Tapanito commented Jan 21, 2026

I don’t believe this approach addresses the underlying issue. After tracing the execution and considering the edge cases, there are three main reasons why this fix falls short.

In InvariantCheck.cpp::3441, the rounding logic effectively does nothing because of how STAmount handles normalization.

Trace Example:

  • minScale: -12
  • destinationDelta: 4999.5099009901 (Mantissa: ...0100000, Exp: -15)
  • vaultPseudoDeltaAssets: -4999.50990099 (Mantissa: ...0000000, Exp: -15)

When we call roundToAsset, the following occurs:

STAmount const ret{asset, value}; 
// Value converts from (Exp: -15) to (Exp: -12)
// Example: 4999509900990000000, -15 -> 4999509900990000, -12

if (ret.integral()) return ret;

return roundToScale(ret, scale); 
// We are passing an Exp -12 value to be rounded to scale -12.
// No rounding occurs, but normalization returns the original Exp -15 representation.

Even though we passed a minScale of -12, the returned values still have an exponent of -15. The internal representation is fighting the rounding logic.

As you rightfully pointed out in my PR, we cannot blindly round to the smallest scale:

  • Input 1: 1.000000 (Scale 0)
  • Input 2: 1.234567 (Scale 6)
  • Result: Both round to 1.

This creates an artificial equality that shouldn't exist. Rounding to the lowest common scale is too destructive for invariant checks.

Instead of trying to force the exponents to match via rounding, we should implement an epsilon difference tolerance. This acknowledges that while the numbers are represented differently in memory, they are "equal enough" within a defined precision threshold.

@Tapanito
Copy link
Collaborator

@ximinez, I added the unit test to my branch, then locally merged by branch into yours causing the invariant violation unit-test to fail.

@ximinez
Copy link
Collaborator Author

ximinez commented Jan 21, 2026

I don’t believe this approach addresses the underlying issue. After tracing the execution and considering the edge cases, there are three main reasons why this fix falls short.

In InvariantCheck.cpp::3441, the rounding logic effectively does nothing because of how STAmount handles normalization.

Trace Example:

* **minScale:** `-12`

* **destinationDelta:** `4999.5099009901` (Mantissa: `...0100000`, Exp: `-15`)

* **vaultPseudoDeltaAssets:** `-4999.50990099` (Mantissa: `...0000000`, Exp: `-15`)

Ah! You're taking the scale of the deltas. You need to take the scale of the original values - the before and afters that were used to compute the deltas. Those tell you how many digits of the resulting delta are actually valid for comparison.

The problem is that in the current implementation, you don't know all of the relevant values until well after you've computed all the deltas.

Right now, deltas_ is std::unordered_map<uint256, Number> deltas_ = {};. You probably need to have it be std::unordered_map<uint256, DeltaInfo> deltas_ = {}; Where DeltaInfo is a struct containing the Number and an int representing the scale of the original values.

… into ximinez/number-scale

* XRPLF/tapanito/lending-vault-invariant:
  flyby change removing unused includes
  addreses review comments
  adds invariant test
… into ximinez/number-scale

* XRPLF/tapanito/lending-vault-invariant:
  refactors vault invariant to use relative distance
  Limit reply size on `TMGetObjectByHash` queries (6110)
  ci: remove 'master' branch as a trigger (6234)
  Improve ledger_entry lookups for fee, amendments, NUNL, and hashes (5644)
@Tapanito Tapanito self-requested a review January 22, 2026 12:08
@Tapanito
Copy link
Collaborator

Hi @ximinez , VaultClawback unit tests are failing with on some instances.

2026-01-22T12:05:08.6691439Z xrpld: /__w/rippled/rippled/build/modules/xrpl.libxrpl.basics/xrpl/basics/Number.h:764: std::pair<T, int> xrpl::Number::normalizeToRange(T, T) const [T = unsigned long]: Assertion `("xrpl::Number::normalizeToRange" " : " "Number is non-negative for unsigned range.") && (!negative)' failed.

if (sign != 0)
deltas_[key] = balanceDelta * sign;
{
balanceDelta.delta *= sign;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we're initializing the DeltaInfo with a scale that is outside of a valid range, we should add an assertion here to ensure that scale was initialized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since we're initializing the DeltaInfo with a scale that is outside of a valid range, we should add an assertion here to ensure that scale was initialized.

I might change scale to optional.

ximinez and others added 2 commits January 22, 2026 18:27
@Tapanito Tapanito self-requested a review January 26, 2026 18:59
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.

3 participants