Skip to content

feat(common): overflow-safe time-weighted average rate (#2)#45

Merged
elizabetheonoja-art merged 1 commit into
Utility-Protocol:mainfrom
real-venus:fix/weighted-rate-precision
Jun 25, 2026
Merged

feat(common): overflow-safe time-weighted average rate (#2)#45
elizabetheonoja-art merged 1 commit into
Utility-Protocol:mainfrom
real-venus:fix/weighted-rate-precision

Conversation

@real-venus

Copy link
Copy Markdown
Contributor

feat(common): overflow-safe, precision-preserving time-weighted average rate

Closes #2

(TariffOracle::calculate_flow_for_period) does this with:

  • saturating_mul for rate_i × duration_i — on overflow it silently clamps
    to a wrong (capped) value instead of failing, and

  • integer division Σ / total — which truncates, losing precision.

  • mul_full(a, b) -> (hi, lo) — exact 128×128 → 256-bit multiply.

  • add_256 — 256-bit accumulation with overflow detection.

  • div_256_by_128(hi, lo, d) — exact restoring long division (quotient +
    remainder); None if the quotient exceeds u128.

  • round_half_upROUND_HALF_UP via the remainder (overflow-safe 2r ≥ d).

  • mul_div_floor / mul_div_rounda*b/d with no intermediate overflow.

  • weighted_average(&[(rate, duration)]) — accumulates the numerator in full
    256-bit precision and divides exactly → zero precision loss, no intermediate
    overflow
    . Returns None (never silently wrong) on the unreachable
    > 2²⁵⁶ numerator / > u128 quotient cases.

  • interval_product_fits_u128 — optional per-interval pre-validation
    (blueprint step 3).

Files

  • contracts/common/src/weighted_rate.rs (new)
  • contracts/common/src/lib.rs (register module)
  • contracts/docs/specs/tariff-precision.md (new — strategy & overflow guarantees)

…col#2)

Time-weighted tariff averaging computes sum(rate_i * duration_i) /
sum(duration_i). A naive u128 implementation can overflow the product/sum
and truncates on division; the tariff calculator used saturating_mul
(silently clamps to a wrong value) plus integer division (precision loss).

Add contracts/common/src/weighted_rate.rs (pure no_std, no deps):
- mul_full: exact 128x128 -> 256-bit multiply
- div_256_by_128: exact restoring long division (quotient + remainder)
- mul_div_floor / mul_div_round (ROUND_HALF_UP)
- weighted_average(&[(rate, duration)]): accumulates the numerator in full
  256-bit precision and divides exactly -> zero precision loss, no
  intermediate overflow (returns None instead of silently wrong on the
  unreachable >2^256 numerator / >u128 quotient cases)
- interval_product_fits_u128: optional per-interval pre-validation

Tests: mul_full vs wrapping_mul + known high products, half-up rounding,
zero-divisor/quotient-overflow None, constant/two-window/duration-weighted
averages, 18-decimal 30-day scale, beyond-u128 numerator, and a 2000-iter
deterministic property sweep over rate in [1,1e18], duration in [60,2.59e6],
intervals in [1,2880] asserting exact equality with a native reference.

docs/specs/tariff-precision.md documents the strategy, overflow guarantees,
and why full-width integers beat a rounding Decimal128.

Note: the production consumer (TariffOracle::calculate_flow_for_period in
utility_contracts) does not compile today (pre-existing SDK-23 errors), so
the verified algorithm lands in the common crate for it to delegate to once
that crate builds again.
@elizabetheonoja-art elizabetheonoja-art merged commit 0c25405 into Utility-Protocol:main Jun 25, 2026
2 checks passed
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.

Temporal Tariff State Calculator Integer Precision Loss in Time-Weighted Rate Averaging

2 participants