Skip to content

fix(chainbase): guard divide-by-zero#6687

Closed
Little-Peony wants to merge 8 commits into
tronprotocol:developfrom
Little-Peony:cherry_pick_6620
Closed

fix(chainbase): guard divide-by-zero#6687
Little-Peony wants to merge 8 commits into
tronprotocol:developfrom
Little-Peony:cherry_pick_6620

Conversation

@Little-Peony
Copy link
Copy Markdown
Contributor

@Little-Peony Little-Peony commented Apr 16, 2026

What does this PR do?

Fix audit warnings in energy-related code.

Changes

  • TransactionTrace: guard newArea / newSize with a newSize == 0 check in resetAccountUsage. A zero window size means no valid time window exists, so usage should be 0 rather than dividing by zero. Also fixes a minor comment typo (pre consumedpreconsumed).

Test plan

  • Existing tests pass — no behaviour change for the normal (non-zero newSize) path

… loading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Little-Peony Little-Peony changed the title fix(api): add whitelist rate limiter class to prevent arbitrary class loading fix(http): replace Class.forName with whitelist in RateLimiterServlet Apr 16, 2026
@halibobo1205 halibobo1205 added topic:DB Database topic:api rpc/http related issue labels Apr 16, 2026
@halibobo1205 halibobo1205 added this to the GreatVoyage-v4.8.2 milestone Apr 16, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Little-Peony Little-Peony changed the title fix(http): replace Class.forName with whitelist in RateLimiterServlet fix(chainbase): fix divide-by-zero and volatile head in energy calculation Apr 16, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Sunny6889 Sunny6889 removed the topic:api rpc/http related issue label Apr 16, 2026
@Sunny6889 Sunny6889 changed the title fix(chainbase): fix divide-by-zero and volatile head in energy calculation fix(chainbase): fix divide-by-zero and volatile head Apr 16, 2026
// Calc new usage by fixed x-axes
long newUsage = max(0, newArea / newSize, dynamicPropertiesStore.disableJavaLangMath());
// A zero window size means no valid time window exists and thus zero usage
long newUsage = newSize == 0 ? 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.

The zero-division guard is well-reasoned — newSize can indeed be 0 when mergedSize == currentSize and size from the receipt is 0, even though getWindowSize() itself never returns 0. Great job tracing the actual root cause rather than just adding a blanket check! 👏

Since this is a subtle edge case that's easy to regress on, would it be worth adding a unit test in TransactionTraceTest that exercises the newSize == 0 path? This would also serve as documentation for future readers about when this condition can occur.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[SHOULD] Please add a regression test for the newSize == 0 path in TransactionTraceTest. This is a subtle edge case, and a test would both lock in the intended behavior (newUsage = 0 with window reset) and document when this condition is expected to occur.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Added two regression tests in TransactionTraceTest:

  • testResetAccountUsageZeroWindowSize: covers the resetAccountUsage path (V2 disabled) with mergedSize == currentSize and size == 0, which produces newSize == 0.
  • testResetAccountUsageV2ZeroWindowSize: same scenario via the resetAccountUsageV2 path (V2 enabled).

Both verify that energyUsage is reset to 0 without an ArithmeticException.

@lvs0075 lvs0075 requested review from halibobo1205 and lxcmyf and removed request for waynercheung April 17, 2026 06:06
Comment thread chainbase/src/main/java/org/tron/core/db/TransactionTrace.java Outdated
long currentUsage = accountCap.getEnergyUsage();
// Drop the pre consumed frozen energy
// Drop the preconsumed frozen energy
long newArea = currentUsage * currentSize
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[QUESTION] newArea still relies on raw long multiplications/subtractions here. I realize this is pre-existing code rather than introduced by this PR, but since this method is already being touched for numeric safety, do we want a follow-up to harden these arithmetic operations with the existing exact-helper pattern as well?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good observation. While there's a theoretical overflow risk in newArea = currentUsage * currentSize - (mergedUsage * mergedSize - usage * size), in practice this won't occur in java-tron's business context — both currentUsage (energy usage) and currentSize (window size, capped at WINDOW_SIZE_MS / BLOCK_PRODUCED_INTERVAL = 28800) are tightly bounded by the protocol's energy limits, so the product stays well within long range. But I agree it's worth noting as a pre-existing pattern; happy to track it in a
follow-up if you'd like.

Comment thread chainbase/src/main/java/org/tron/core/db/TransactionTrace.java Outdated
@waynercheung
Copy link
Copy Markdown
Collaborator

[DISCUSS] If newSize == 0 is truly reachable during block execution, then this change does more than prevent a local exception: it changes the behavior from an uncaught ArithmeticException during finalization to continuing with newUsage = 0. In that case, the PR should explicitly document the consensus/upgrade impact (for example, whether coordinated node upgrade or any activation mechanism is needed).

At the moment, though, the reachability of newSize == 0 is not obvious from the current call path, because the related window sizes are populated via getWindowSize() in VMActuator, which normally does not return 0. Please clarify the exact trigger path, or describe this as a defensive guard if it is only intended as protection against an unexpected zero window.

Comment thread chainbase/src/main/java/org/tron/core/db2/core/Chainbase.java Outdated
Little-Peony and others added 3 commits April 20, 2026 10:02
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ctionTrace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Sunny6889 Sunny6889 changed the title fix(chainbase): fix divide-by-zero and volatile head fix(chainbase): guard divide-by-zero Apr 20, 2026
@Sunny6889
Copy link
Copy Markdown
Contributor

[DISCUSS] If newSize == 0 is truly reachable during block execution, then this change does more than prevent a local exception: it changes the behavior from an uncaught ArithmeticException during finalization to continuing with newUsage = 0. In that case, the PR should explicitly document the consensus/upgrade impact (for example, whether coordinated node upgrade or any activation mechanism is needed).

At the moment, though, the reachability of newSize == 0 is not obvious from the current call path, because the related window sizes are populated via getWindowSize() in VMActuator, which normally does not return 0. Please clarify the exact trigger path, or describe this as a defensive guard if it is only intended as protection against an unexpected zero window.

The size parameter passed to resetAccountUsage is recorded from account.getWindowSize(ENERGY) in VMActuator (lines 583 and 751). AccountCapsule.getWindowSize(ENERGY) has an explicit fallback: when the stored proto field is 0, it returns WINDOW_SIZE_MS / BLOCK_PRODUCED_INTERVAL = 28800 — so it never returns 0. Likewise, currentSize is read from accountCap.getWindowSize(ENERGY) at the point resetAccountUsage is called, subject to the same fallback. Since neither size nor currentSize can be 0, newSize (= mergedSize == currentSize ? size : currentSize) cannot be 0 in any normal execution path.

This is therefore a defensive guard — the existing comment "A zero window size means no valid time window exists and thus zero usage" already expressed the intended behavior, but the zero check was missing. There is no reachable code path that produces newSize == 0, so no consensus impact and no coordinated upgrade is required.

@tronprotocol tronprotocol deleted a comment from Little-Peony Apr 20, 2026
long newSize = mergedSize == currentSize ? size : currentSize;
// Calc new usage by fixed x-axes
long newUsage = max(0, newArea / newSize, dynamicPropertiesStore.disableJavaLangMath());
// A zero window size means no valid time window exists and thus zero usage
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[NIT] New comment removes the algorithm context from 'fixed x-axes'

The original comment // Calc new usage by fixed x-axes explained the area-based energy accounting model (usage × window = area; new_usage = area / new_window). The replacement comment only explains the zero-guard case, so readers of the non-zero path lose the rationale for newArea / newSize.

The same applies to the equivalent line in resetAccountUsageV2 (line ~320).

Suggestion: Merge both meanings, e.g.

// Calculate new usage from area/window model; zero window means no valid time context and yields zero usage

@Little-Peony
Copy link
Copy Markdown
Contributor Author

Closed, as it is only theoretical guard fix.

@halibobo1205 halibobo1205 removed this from the GreatVoyage-v4.8.2 milestone Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:DB Database

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants