Skip to content

fix(pool): enforce amount validation in recover_stake_ok and state guard in recover_stake - #2509

Open
getasewtilahun wants to merge 1 commit into
ton-blockchain:masterfrom
getasewtilahun:fix/nominator-pool-recover-stake-validation
Open

fix(pool): enforce amount validation in recover_stake_ok and state guard in recover_stake#2509
getasewtilahun wants to merge 1 commit into
ton-blockchain:masterfrom
getasewtilahun:fix/nominator-pool-recover-stake-validation

Conversation

@getasewtilahun

Copy link
Copy Markdown

Overview

This PR fixes a state tracking issue in pool.fc where the contract's internal accounting can desynchronize from the Elector's state during stake recovery.

Note for Core Maintainers / Triage Team: A full technical vulnerability analysis, state-transition proofs, and detailed execution trace have been submitted via the official TON Bug Bounty Bot (@ton_bugs_bot). Please reference that submission for the complete report.

Problem Statement

In pool.fc, op::recover_stake_ok() unconditionally resets contract state (state = 0, stake_amount_sent = 0) upon receiving any op::recover_stake_ok message from the Elector.

However, the Elector maintains credits (which include surplus stake returns and complaint rewards) separately from past_elections (frozen stake). If recover_stake is processed when the Elector holds a pre-unfreeze credit balance while principal remains frozen in past_elections, the pool receives a recover_stake_ok message carrying only the partial credit.

Because pool.fc currently lacks an amount check on msg_value, processing this partial response prematurely zeros stake_amount_sent and resets state = 0. The subsequent legitimate return of the main stake is then misclassified as validation yield rather than principal return, corrupting reward distribution logic.

Summary of Changes

  1. op::recover_stake_ok Amount Verification:

    • Updated the handler to check if (msg_value >= stake_amount_sent) before clearing stake_amount_sent and resetting state = 0.
    • If msg_value < stake_amount_sent, the incoming coins (partial credit) are accepted into pool balance, but state and stake_amount_sent remain active until the full stake is recovered.
  2. op::recover_stake State Guard:

    • Added throw_unless(77, state == 0) to op::recover_stake to ensure recovery requests can only be forwarded to the Elector when the pool is in an idle state.

Impact & Severity

  • State Integrity: Ensures stake_amount_sent accurately tracks outstanding frozen principal at all times.
  • Accounting Accuracy: Prevents misclassifying returned principal as yield under non-standard recovery sequences.

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.

1 participant