-
Notifications
You must be signed in to change notification settings - Fork 13
Description
f_reward Inflated by Consolidated Amounts (Post-Electra Validators)
Summary
Validators that are targets of consolidations (post-Electra) have their f_reward values inflated by exactly the consolidated amount (~32 ETH) due to incorrect timing when subtracting ConsolidatedAmounts from the epoch reward calculation.
Problem Description
Symptoms
- Validators with
effective_balance > 32 ETHshowf_reward >> f_max_reward - Typically
f_rewardis inflated by exactly 32 ETH (32,000,000,000 gwei) - Only affects validators with
withdrawal_prefix = 0x02(compounding validators) - Started appearing post-Electra (epoch >= 411389)
Root Cause
File: pkg/spec/metrics/standard.go:32
Function: EpochReward()
func (p StateMetricsBase) EpochReward(valIdx phase0.ValidatorIndex) int64 {
consolidatedAmount, ok := p.CurrentState.ConsolidatedAmounts[valIdx] // ← BUG
if !ok {
consolidatedAmount = 0
}
depositedAmount, ok := p.CurrentState.DepositedAmounts[valIdx]
if !ok {
depositedAmount = 0
}
depositedAmount += p.NextState.Deposits[valIdx]
reward := int64(p.NextState.Balances[valIdx]) - int64(p.CurrentState.Balances[valIdx])
reward += int64(p.NextState.Withdrawals[valIdx])
reward -= int64(depositedAmount)
reward -= int64(consolidatedAmount) // ← Subtracts 0 when should subtract 32 ETH
return reward
}The bug: Uses CurrentState.ConsolidatedAmounts instead of NextState.ConsolidatedAmounts
Why This Happens
When validator B is the target of a consolidation from validator A:
Before consolidation (CurrentState - epoch N):
Balances[B]= 32 ETHConsolidatedAmounts[B]= 0 (doesn't exist in CurrentState yet)
After consolidation (NextState - epoch N+1):
Balances[B]= 64 ETH (received 32 ETH from validator A)ConsolidatedAmounts[B]= 32 ETH (tracked in NextState)
Incorrect calculation (current code):
reward = NextState.Balances[B] - CurrentState.Balances[B] - CurrentState.ConsolidatedAmounts[B]
reward = 64e9 - 32e9 - 0
reward = 32,000,000,000 gwei ← WRONG (32 ETH inflation)
Correct calculation (should be):
reward = NextState.Balances[B] - CurrentState.Balances[B] - NextState.ConsolidatedAmounts[B]
reward = 64e9 - 32e9 - 32e9
reward = 0 gwei ← CORRECT
Impact
Severity: CRITICAL
Affected Data
- Table:
t_validator_rewards_summary - Column:
f_reward - Affected validators: All validators that are targets of consolidations
- Timeframe: Post-Electra upgrade (epoch >= 411389, ~December 2024)
Cascading Effects
Corrupted f_reward values propagate to:
t_pool_summary.aggregated_rewards(summed across pool)- Bindeth API APR calculations
- Dashboard displays showing impossible APR values
- Analytics and historical data
Example from Production
Validator receives consolidation at epoch X:
- Expected:
f_max_reward = 108,000 gwei,f_reward ≈ 0-108,000 gwei - Actual:
f_max_reward = 108,000 gwei,f_reward = 32,108,000,000 gwei
This creates the impossible scenario where f_reward > f_max_reward * 297,000, breaking all downstream calculations.
Proposed Solution
Fix (1 line change)
Change line 32 in pkg/spec/metrics/standard.go from:
consolidatedAmount, ok := p.CurrentState.ConsolidatedAmounts[valIdx]To:
consolidatedAmount, ok := p.NextState.ConsolidatedAmounts[valIdx]Rationale
The consolidated amount is applied to the balance in NextState, so it should be tracked in NextState.ConsolidatedAmounts. This aligns with how Deposits are handled (line 42):
depositedAmount += p.NextState.Deposits[valIdx] // Uses NextState, not CurrentStateTesting
Before Fix
Run goteth on a range containing consolidations (epoch >= 411389) and verify:
SELECT COUNT(*) FROM t_validator_rewards_summary
WHERE f_reward > f_max_reward * 2
AND f_epoch >= 411389;
-- Should return > 0 (bug exists)After Fix
Re-run same epoch range and verify:
SELECT COUNT(*) FROM t_validator_rewards_summary
WHERE f_reward > f_max_reward * 2
AND f_epoch >= 411389
AND f_withdrawal_prefix = 2;
-- Should return 0 (bug fixed)Edge Cases to Test
- Validator receives multiple consolidations in sequence
- Validator is both source and target of different consolidations
- Validator with
effective_balance > 64 ETH(multiple consolidations)
Migration Plan
Phase 1: Fix and Deploy
- Apply fix to
pkg/spec/metrics/standard.go - Test on development backfill (epochs 411389 - current)
- Verify no
f_reward > f_max_rewardanomalies for consolidated validators
Phase 2: Data Cleanup
-
Identify all corrupted epochs:
SELECT DISTINCT f_epoch FROM t_validator_rewards_summary WHERE f_reward > f_max_reward * 2 AND f_epoch >= 411389 ORDER BY f_epoch;
-
Delete corrupted data:
DELETE FROM t_validator_rewards_summary WHERE f_epoch IN (...); DELETE FROM t_pool_summary WHERE f_epoch IN (...);
-
Re-index those epochs with fixed goteth version
Phase 3: Validation
- Verify APR calculations are reasonable
- Check pool summaries for affected pools
- Monitor dashboard displays
Related Issues
- Related to Electra upgrade and consolidation feature (EIP-7251)
- May interact with Issue UInt64 Underflow in t_pool_summary.aggregated_rewards #214 (UInt64 underflow) when pool aggregations include these inflated values
Related Code
- Consolidation processing:
pkg/spec/metrics/state_electra.go:586-615 - State setup:
pkg/spec/state.go:118 - Reward calculation:
pkg/spec/metrics/state_altair.go:305(assignsRewardfield)
References
- Ethereum Consolidation Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-process_pending_consolidations
- EIP-7251 (Increase MAX_EFFECTIVE_BALANCE): https://eips.ethereum.org/EIPS/eip-7251
Additional Notes
This bug specifically affects target validators in consolidations. Source validators (the ones being consolidated away) are correctly handled as they are exited and their rewards stop being calculated.
The bug is deterministic and reproducible for any consolidation event, making it straightforward to test and verify the fix.