Skip to content

fix(tax): guard division-by-zero panic in community-tax split adjustment - #670

Merged
fragwuerdig merged 3 commits into
classic-terra:mainfrom
GeoffreySHD:fix/tax-split-div-zero
Oct 3, 2026
Merged

fragwuerdig merged 3 commits into
classic-terra:mainfrom
GeoffreySHD:fix/tax-split-div-zero

Conversation

@GeoffreySHD

Copy link
Copy Markdown
Contributor

Description (severity: chain-halt risk reachable via governance)

ProcessTaxSplits in x/tax/keeper/tax_split.go computes the community-tax adjustment as:

applyCommunityTax := communityTax.Mul(oracleSplitRate.Quo(communityTax.Mul(oracleSplitRate).Add(sdkmath.LegacyOneDec()).Sub(communityTax)))

The divisor is communityTax·(1 − oracleSplitRate). Both inputs are ordinary governance-settable parameters:

  • communityTax = 1.0 is accepted by the SDK's distribution param validation,
  • oracleSplitRate = 0 is accepted by validateOraceSplit (only rejects negatives and >1).

With that combination the denominator is zero and the LegacyDec division panics — inside ProcessTaxSplits, which runs within every taxable transaction (bank sends, market swaps, wasm calls through the tax handler wrappers). A single passed governance proposal setting those two values would make every taxable transaction panic → chain halt until an emergency upgrade corrects the parameter.

Reproduction (exact original expression, governing inputs): panics with division by zero.

Fix

Extract the computation into a pure, testable helper types.CommunityTaxAdjustment that returns the unadjusted communityTax whenever the numerator or denominator is non-positive. Behavior is identical for every previously-working parameter combination; only the panicking configuration changes (no community-tax share is taken — consistent with that configuration's semantics).

Testing

  • New subtests in x/tax/types/compute_test.go (main's existing ComputeTaxes tests are preserved) covering the hand-computed normal case, zero inputs, and the previously panicking configuration (NotPanics).
  • Full x/tax/... suite passes; golangci-lint (pinned v2.1.6) reports 0 issues on the canonical tree.

The community-tax adjustment in ProcessTaxSplits divides by
communityTax*(1-oracleSplitRate). With communityTax == 1.0 (100%, allowed
by the SDK's distribution param validation) and oracleSplitRate == 0
(allowed by validateOraceSplit), the denominator is zero and the LegacyDec
division panics — inside every taxable transaction (bank sends, market
swaps, wasm calls), halting the chain until the parameter is fixed via an
emergency upgrade. Both values are ordinary governance-settable params,
so a single passed proposal could brick all taxable activity.

Reproduced with the exact original expression on the governing inputs:
panics with "division by zero".

Extract the computation into types.CommunityTaxAdjustment (pure function,
no keeper state) with an explicit guard: when the numerator or divisor is
non-positive, return the unadjusted communityTax. Behavior is identical
for every previously-working parameter combination; only the panicking
configuration changes (no community-tax share of the distribution delta
is taken — consistent with that configuration's semantics).

Adds table + edge-case tests including the previously panicking inputs.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@GeoffreySHD
GeoffreySHD force-pushed the fix/tax-split-div-zero branch from fb90d48 to 2bb430a Compare September 9, 2026 17:40
The strangelove-ventures GitHub org was renamed/moved to amygdala-labs,
which killed the ghcr.io/strangelove-ventures/heighliner/osmosis image
path baked into interchaintest's built-in chain config. Both PFM tests
(TestTerraGaiaOsmoPFM, TestTerraPFM) now fail during chain setup with
"denied" when pulling the image, independent of the code under test.

Override the osmosis image repository per ChainSpec (the spec-level
Images list replaces the built-in one; ChainSpec.Version still applies
to Images[0]) to the same tags under the new amygdala-labs org. Verified
the replacement manifests are pullable.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@GeoffreySHD

Copy link
Copy Markdown
Contributor Author

Cherry-picked the osmosis heighliner image fix (0a6ee0b) — the earlier test-ibc-pfm red check was the repo-wide dead ghcr.io/strangelove-ventures image path (see #667), unrelated to this change.

@GeoffreySHD

Copy link
Copy Markdown
Contributor Author

Friendly bump — fully green (31/31), e2e matrix included. This closes a governance-reachable chain-halt: in ProcessTaxSplits, the community-tax adjustment divides by communityTax*(1-oracleSplit), which is zero when governance sets communityTax=1.0 and oracleSplit=0 (both individually valid) — every taxable transaction would panic. The PR reproduces the panic on the exact original expression and guards only the divisor, preserving bit-identical behavior for every working configuration. Heads-up for the MM2 PR (#664): the redirect block lands in the same function, so on rebase keep the guarded helper. Would appreciate a review from @StrathCole @fragwuerdig @hoank101.

GeoffreySHD added a commit to GeoffreySHD/core that referenced this pull request Sep 9, 2026
Complete writeup of the audit shipped across classic-terra#667/classic-terra#668/classic-terra#669/classic-terra#670 plus the
part-2 pass over oracle price-staleness windows and treasury distribution
params, and the posted MM2 (classic-terra#664) review.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@fragwuerdig
fragwuerdig self-requested a review September 23, 2026 11:21
Comment thread x/tax/types/compute.go Outdated
Comment thread x/tax/types/compute.go Outdated
@fragwuerdig fragwuerdig added the state machine breaking Something that will impact the state machine and consensus label Sep 28, 2026
Co-authored-by: StrathCole <7449529+StrathCole@users.noreply.github.com>
@fragwuerdig
fragwuerdig merged commit 9a5c97c into classic-terra:main Oct 3, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked state machine breaking Something that will impact the state machine and consensus

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants